diff --git a/il65/compile.py b/il65/compile.py index 992a22894..c834f1af4 100644 --- a/il65/compile.py +++ b/il65/compile.py @@ -42,8 +42,8 @@ class PlyParser: self.determine_subroutine_usage(module) self.all_parents_connected(module) self.semantic_check(module) - self.allocate_zeropage_vars(module) self.coerce_values(module) + self.allocate_zeropage_vars(module) except ParseError as x: self.handle_parse_error(x) if self.parse_errors: @@ -73,9 +73,9 @@ class PlyParser: def coerce_values(self, module: Module) -> None: for node in module.all_nodes(): try: - if isinstance(node, Assignment): - pass # @todo coerce assignment - elif isinstance(node, AugAssignment): + # note: not processing regular assignments, because they can contain multiple targets of different datatype. + # this has to be dealt with anyway later, so we don't bother dealing with it here for just a special case. + if isinstance(node, AugAssignment): if node.right.is_compile_constant(): _, node.right = coerce_constant_value(datatype_of(node.left, node.my_scope()), node.right, node.right.sourceref) elif isinstance(node, Goto): @@ -165,6 +165,9 @@ class PlyParser: if node.operator in ("/=", "//="): if isinstance(node.right, LiteralValue) and node.right.value == 0: raise ParseError("division by zero", node.right.sourceref) + elif isinstance(node, VarDef): + if node.value is not None and not node.value.is_compile_constant(): + raise ParseError("variable initialization value should be a compile-time constant", node.value.sourceref) previous_stmt = node def check_subroutine_arguments(self, call: SubCall, subdef: Subroutine) -> None: @@ -223,7 +226,6 @@ class PlyParser: @no_type_check def create_multiassigns(self, module: Module) -> None: # create multi-assign statements from nested assignments (A=B=C=5), - # @todo optimize TargetRegisters down to single Register if it's just one register. def reduce_right(assign: Assignment) -> Assignment: if isinstance(assign.right, Assignment): right = reduce_right(assign.right) diff --git a/il65/emit/generate.py b/il65/emit/generate.py index 4482a22a2..e8d91e8c5 100644 --- a/il65/emit/generate.py +++ b/il65/emit/generate.py @@ -11,7 +11,7 @@ from typing import TextIO, Callable, no_type_check from ..plylex import print_bold from ..plyparse import Module, Scope, ProgramFormat, Block, Directive, VarDef, Label, Subroutine, AstNode, ZpOptions, \ InlineAssembly, Return, Register, Goto, SubCall, Assignment, AugAssignment, IncrDecr, AssignmentTargets -from . import CodeError, to_hex +from . import CodeError, to_hex, to_mflpt5 from .variables import generate_block_init, generate_block_vars from .assignment import generate_assignment, generate_aug_assignment from .calls import generate_goto, generate_subcall @@ -174,6 +174,11 @@ class AssemblyGenerator: self.cur_block = cur_block out("") out("; -- end block subroutines") + if block.scope.float_const_values: + # generate additional float constants that are used in floating point expressions + out("\n; -- float constants") + for name, value in block.scope.float_const_values.items(): + out("{:s}\t\t.byte ${:02x}, ${:02x}, ${:02x}, ${:02x}, ${:02x}\t; {}".format(name, *to_mflpt5(value), value)) out("\n\v.pend\n") @no_type_check diff --git a/il65/emit/incrdecr.py b/il65/emit/incrdecr.py index 59fd626ef..9d2822009 100644 --- a/il65/emit/incrdecr.py +++ b/il65/emit/incrdecr.py @@ -193,13 +193,12 @@ def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope) -> None: out("\vjsr c64flt.float_add_one") else: out("\vjsr c64flt.float_sub_one") - else: - # XXX for the float += otherfloat cases - print("FLOAT INCR/DECR BY", stmt.howmuch) # XXX + elif stmt.howmuch != 0: + float_name = scope.define_float_constant(stmt.howmuch) with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True): - # XXX out("\vlda #<" + stmt.value.name) + out("\vlda #<" + float_name) out("\vsta c64.SCRATCH_ZPWORD1") - # XXX out("\vlda #>" + stmt.value.name) + out("\vlda #>" + float_name) out("\vsta c64.SCRATCH_ZPWORD1+1") out("\vldx #<" + what_str) out("\vldy #>" + what_str) diff --git a/il65/emit/variables.py b/il65/emit/variables.py index 7efc63d45..8501f014e 100644 --- a/il65/emit/variables.py +++ b/il65/emit/variables.py @@ -67,7 +67,7 @@ def generate_block_init(out: Callable, block: Block) -> None: out("\vsta {:s}".format(bytevar.name)) for wordvar in sorted(vars_by_datatype[DataType.WORD], key=lambda vd: vd.value): if isinstance(wordvar.value, AddressOf): - raise CodeError("can't yet use addressof here", wordvar.sourceref) # XXX + raise CodeError("addressof is not a compile-time constant value", wordvar.sourceref) assert isinstance(wordvar.value, LiteralValue) and type(wordvar.value.value) is int v_hi, v_lo = divmod(wordvar.value.value, 256) if v_hi != prev_value_a: @@ -131,7 +131,6 @@ def generate_block_vars(out: Callable, block: Block, zeropage: bool=False) -> No _generate_string_var(out, vardef) else: raise CodeError("invalid const type", vardef) - # @todo float constants that are used in expressions out("; memory mapped variables") for vardef in vars_by_vartype.get(VarType.MEMORY, []): # create a definition for variables at a specific place in memory (memory-mapped) diff --git a/il65/optimize.py b/il65/optimize.py index 4e60869ee..9edf82f53 100644 --- a/il65/optimize.py +++ b/il65/optimize.py @@ -207,7 +207,7 @@ class Optimizer: self.optimizations_performed = True self.num_warnings += 1 print_warning("{}: removed statement that has no effect".format(assignment.sourceref)) - if isinstance(assignment, AugAssignment): + elif isinstance(assignment, AugAssignment): if isinstance(assignment.right, LiteralValue) and isinstance(assignment.right.value, (int, float)): if assignment.right.value == 0: if assignment.operator in ("+=", "-=", "|=", "<<=", ">>=", "^="): @@ -538,10 +538,12 @@ def _process_dynamic_expression(expr: Expression, sourceref: SourceRef) -> Expre left_sourceref = expr.left.sourceref if isinstance(expr.left, AstNode) else sourceref expr.left = _process_dynamic_expression(expr.left, left_sourceref) expr.left.parent = expr - try: - return _process_constant_expression(expr, sourceref) - except ExpressionEvaluationError: - return expr + if expr.is_compile_constant(): + try: + return _process_constant_expression(expr, sourceref) + except ExpressionEvaluationError: + pass + return expr else: left_sourceref = expr.left.sourceref if isinstance(expr.left, AstNode) else sourceref expr.left = _process_dynamic_expression(expr.left, left_sourceref) @@ -549,10 +551,12 @@ def _process_dynamic_expression(expr: Expression, sourceref: SourceRef) -> Expre right_sourceref = expr.right.sourceref if isinstance(expr.right, AstNode) else sourceref expr.right = _process_dynamic_expression(expr.right, right_sourceref) expr.right.parent = expr - try: - return _process_constant_expression(expr, sourceref) - except ExpressionEvaluationError: - return expr + if expr.is_compile_constant(): + try: + return _process_constant_expression(expr, sourceref) + except ExpressionEvaluationError: + pass + return expr else: raise ParseError("expression required, not {}".format(expr.__class__.__name__), expr.sourceref) diff --git a/il65/plyparse.py b/il65/plyparse.py index 31c27079b..d37038817 100644 --- a/il65/plyparse.py +++ b/il65/plyparse.py @@ -123,6 +123,7 @@ class Scope(AstNode): nodes = attr.ib(type=list, init=True) # requires nodes in __init__ symbols = attr.ib(init=False) name = attr.ib(init=False) # will be set by enclosing block, or subroutine etc. + float_const_values = attr.ib(type=dict, default=attr.Factory(dict), init=False) # floatingpoint number -> float const name _save_registers = attr.ib(type=bool, default=None, init=False) @property @@ -177,6 +178,13 @@ class Scope(AstNode): f = BuiltinFunction(name=name, func=func, sourceref=self.sourceref) self.add_node(f) + def define_float_constant(self, value: float) -> str: + if value in self.float_const_values: + return self.float_const_values[value] + name = "il65_float_const_" + str(1 + len(self.float_const_values)) + self.float_const_values[name] = value + return name + def lookup(self, name: str) -> AstNode: assert isinstance(name, str) if '.' in name: diff --git a/todo.ill b/todo.ill index 5eb8b2ec1..b8ef9237d 100644 --- a/todo.ill +++ b/todo.ill @@ -1,59 +1,25 @@ +%import c64lib ~ main { - var .float flt - var .word vmemaddr1 = &flt +; var .float flt +; var bytevar = 22 + 23 ; @todo constant-fold before semantic check +; var .float initfloat1 = -1.234e-14 ; @todo constant-fold before semantic check / fix float parse? +; var .float initfloat2 = -555.666 ; @todo constant-fold before semantic check / fix float parse? +; var .text guess = '?' * 80 ; @todo constant-fold before semantic check + const .word border = $d020 start: + ; @todo float incrdecr/augassign +; flt += 0.1 +; flt += 1.1 +; flt += 10.1 +; flt += 100.1 +; flt += 1000.1 +; flt *= 2.34 - Y-=5 - Y-=8 - Y-- - - ;flt+=2 ; @todo implement on float - ;flt+=2 - ;flt+=2 - ;flt+=2 - ;flt+=2 - - X=0 - X+=5 - X=X+22 - X=33+X - X-=3 - X=X-4 - X=X-5 - X=8*X - X=24|X - X=X^66 - X+=250 - X=5+2+3+4 - X+=5+2+3+4 - X-=100 - X-=50 - X-=5 - - Y=Y - X=X - A=A - - X++ - X-- - X+=1 - X+=1 ; @todo? (optimize) join with previous - X+=0 ; is removed. - A+=0 ; is removed. - Y+=0 ; is removed. - X+=1 ; @todo? (optimize) join with previous - X+=1 ; @todo? (optimize) join with previous - - ;@todo float incrdecr/augassign - ;flt += 0.1 - ;flt += 1.1 - ;flt += 10.1 - ;flt += 100.1 - ;flt += 1000.1 - ;flt *= 2.34 + [border] ++; @todo suport incr/decr on deref constant + [$d020] ++ ; @todo suport incr/decr on deref memory return 44 } diff --git a/todo2.ill b/todo2.ill index c363e6483..58a27f9cd 100644 --- a/todo2.ill +++ b/todo2.ill @@ -38,11 +38,11 @@ start: %breakpoint abc,def XY+=255 - XY+=256 - XY+=257 + XY+=254 + XY+=253 XY-=255 - XY-=256 - XY-=257 + XY-=254 + XY-=253 v3t++ v3t+=1 @@ -57,7 +57,7 @@ start: ; v3t+=2.23424 ; @todo store as constant float with generated name, replace value node ; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node ; v3t+=2.23411 ; @todo store as constant float with generated name, replace value node - v3t=2.23424 * v3t ; @todo store as constant float with generated name, replace value node + ;v3t=2.23424 * v3t ; @todo store as constant float with generated name, replace value node XY*=2 XY*=3 X=3 ; @todo optimize consecutive assignments @@ -78,7 +78,7 @@ start: XY=XY/0 ; @todo zerodiv (during expression to code generation) XY=XY//0 ; @todo zerodiv (during expression to code generation) XY*=2.23424 ; @todo store as constant float with generated name, replace value node - XY*=2.23424 * v3t ; @todo store as constant float with generated name, replace value node + ;XY*=2.23424 * v3t ; @todo store as constant float with generated name, replace value node ;v3t*=2.23424 * v3t ; @todo store as constant float with generated name, replace value node ; A++ ; X--