diff --git a/il65/compile.py b/il65/compile.py index c0b916eaa..992a22894 100644 --- a/il65/compile.py +++ b/il65/compile.py @@ -43,6 +43,7 @@ class PlyParser: self.all_parents_connected(module) self.semantic_check(module) self.allocate_zeropage_vars(module) + self.coerce_values(module) except ParseError as x: self.handle_parse_error(x) if self.parse_errors: @@ -69,6 +70,31 @@ class PlyParser: raise ParseError("last statement in a block/subroutine must be a return or goto, " "(or %noreturn directive to silence this error)", last_stmt.sourceref) + 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): + 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): + if node.condition is not None and node.condition.is_compile_constant(): + _, node.nodes[1] = coerce_constant_value(DataType.WORD, node.nodes[1], node.nodes[1].sourceref) # type: ignore + elif isinstance(node, Return): + if node.value_A is not None and node.value_A.is_compile_constant(): + _, node.nodes[0] = coerce_constant_value(DataType.BYTE, node.nodes[0], node.nodes[0].sourceref) # type: ignore + if node.value_X is not None and node.value_X.is_compile_constant(): + _, node.nodes[1] = coerce_constant_value(DataType.BYTE, node.nodes[1], node.nodes[1].sourceref) # type: ignore + if node.value_Y is not None and node.value_Y.is_compile_constant(): + _, node.nodes[2] = coerce_constant_value(DataType.BYTE, node.nodes[2], node.nodes[2].sourceref) # type: ignore + elif isinstance(node, VarDef): + if node.value is not None: + if node.value.is_compile_constant(): + _, node.value = coerce_constant_value(datatype_of(node, node.my_scope()), node.value, node.value.sourceref) + except OverflowError as x: + raise ParseError(str(x), node.sourceref) + def all_parents_connected(self, module: Module) -> None: # check that all parents are connected in all nodes def check(node: AstNode, expected_parent: AstNode) -> None: diff --git a/il65/emit/assignment.py b/il65/emit/assignment.py index ef3b8d19e..85b8dd1eb 100644 --- a/il65/emit/assignment.py +++ b/il65/emit/assignment.py @@ -30,7 +30,7 @@ def generate_aug_assignment(out: Callable, stmt: AugAssignment, scope: Scope) -> if 0 <= rvalue.value <= 255: _generate_aug_reg_constant_int(out, lvalue, stmt.operator, rvalue.value, "", scope) else: - raise CodeError("assignment value must be 0..255", rvalue) + raise CodeError("aug. assignment value must be 0..255", rvalue) else: raise CodeError("constant integer literal or variable required for now", rvalue) # XXX elif isinstance(rvalue, SymbolName): @@ -39,7 +39,7 @@ def generate_aug_assignment(out: Callable, stmt: AugAssignment, scope: Scope) -> if 0 <= symdef.value.const_value() <= 255: # type: ignore _generate_aug_reg_constant_int(out, lvalue, stmt.operator, 0, symdef.name, scope) else: - raise CodeError("assignment value must be 0..255", rvalue) + raise CodeError("aug. assignment value must be 0..255", rvalue) else: raise CodeError("constant integer literal or variable required for now", rvalue) # XXX elif isinstance(rvalue, Register): @@ -47,9 +47,9 @@ def generate_aug_assignment(out: Callable, stmt: AugAssignment, scope: Scope) -> _generate_aug_reg_reg(out, lvalue, stmt.operator, rvalue, scope) else: # @todo Register += symbolname / dereference , _generate_aug_reg_mem? - raise CodeError("invalid rvalue for augmented assignment on register", rvalue) + raise CodeError("invalid rvalue for aug. assignment on register", rvalue) else: - raise CodeError("augmented assignment only implemented for registers for now", stmt.sourceref) # XXX + raise CodeError("aug. assignment only implemented for registers for now", stmt.sourceref) # XXX def _generate_aug_reg_constant_int(out: Callable, lvalue: Register, operator: str, rvalue: int, rname: str, scope: Scope) -> None: diff --git a/il65/plyparse.py b/il65/plyparse.py index 7e0e1ed73..31c27079b 100644 --- a/il65/plyparse.py +++ b/il65/plyparse.py @@ -733,15 +733,15 @@ class Return(AstNode): # one, two or three subnodes: value_A, value_X, value_Y (all three Expression) @property def value_A(self) -> Optional[Expression]: - return self.nodes[0] if self.nodes else None # type: ignore + return self.nodes[0] if len(self.nodes) >= 1 else None # type: ignore @property def value_X(self) -> Optional[Expression]: - return self.nodes[0] if self.nodes else None # type: ignore + return self.nodes[1] if len(self.nodes) >= 2 else None # type: ignore @property def value_Y(self) -> Optional[Expression]: - return self.nodes[0] if self.nodes else None # type: ignore + return self.nodes[2] if len(self.nodes) >= 3 else None # type: ignore @attr.s(cmp=False, slots=True, repr=False) @@ -824,35 +824,42 @@ class AugAssignment(AstNode): def right(self) -> Expression: return self.nodes[1] # type: ignore + @right.setter + def right(self, rvalue: Expression) -> None: + assert isinstance(rvalue, Expression) + self.nodes[1] = rvalue -def datatype_of(assignmenttarget: AstNode, scope: Scope) -> DataType: + +def datatype_of(targetnode: AstNode, scope: Scope) -> DataType: # tries to determine the DataType of an assignment target node - if isinstance(assignmenttarget, (VarDef, Dereference, Register)): - return assignmenttarget.datatype - elif isinstance(assignmenttarget, SymbolName): - symdef = scope.lookup(assignmenttarget.name) + if isinstance(targetnode, VarDef): + return DataType.WORD if targetnode.vartype == VarType.MEMORY else targetnode.datatype + elif isinstance(targetnode, (Dereference, Register)): + return targetnode.datatype + elif isinstance(targetnode, SymbolName): + symdef = scope.lookup(targetnode.name) if isinstance(symdef, VarDef): return symdef.datatype - elif isinstance(assignmenttarget, TargetRegisters): - if len(assignmenttarget.nodes) == 1: - return datatype_of(assignmenttarget.nodes[0], scope) - raise TypeError("cannot determine datatype", assignmenttarget) + elif isinstance(targetnode, TargetRegisters): + if len(targetnode.nodes) == 1: + return datatype_of(targetnode.nodes[0], scope) + raise TypeError("cannot determine datatype", targetnode) -def coerce_constant_value(datatype: DataType, value: AstNode, - sourceref: SourceRef=None) -> Tuple[bool, AstNode]: +def coerce_constant_value(datatype: DataType, value: Expression, + sourceref: SourceRef=None) -> Tuple[bool, Expression]: # if we're a BYTE type, and the value is a single character, convert it to the numeric value - assert isinstance(value, AstNode) + assert isinstance(value, Expression) - def verify_bounds(value: Union[int, float, str]) -> None: + def verify_bounds(pvalue: Union[int, float, str]) -> None: # if the value is out of bounds, raise an overflow exception - if isinstance(value, (int, float)): - if datatype == DataType.BYTE and not (0 <= value <= 0xff): # type: ignore - raise OverflowError("value out of range for byte: " + str(value)) - if datatype == DataType.WORD and not (0 <= value <= 0xffff): # type: ignore - raise OverflowError("value out of range for word: " + str(value)) - if datatype == DataType.FLOAT and not (FLOAT_MAX_NEGATIVE <= value <= FLOAT_MAX_POSITIVE): # type: ignore - raise OverflowError("value out of range for float: " + str(value)) + if isinstance(pvalue, (int, float)): + if datatype == DataType.BYTE and not (0 <= pvalue <= 0xff): # type: ignore + raise OverflowError("value out of range for byte: " + str(pvalue)) + if datatype == DataType.WORD and not (0 <= pvalue <= 0xffff): # type: ignore + raise OverflowError("value out of range for word: " + str(pvalue)) + if datatype == DataType.FLOAT and not (FLOAT_MAX_NEGATIVE <= pvalue <= FLOAT_MAX_POSITIVE): # type: ignore + raise OverflowError("value out of range for float: " + str(pvalue)) if isinstance(value, LiteralValue): if type(value.value) is str and len(value.value) == 1 and (datatype.isnumeric() or datatype.isarray()): @@ -924,7 +931,6 @@ def p_start(p): scope.name = "<" + p.lexer.source_filename + " global scope>" p[0] = Module(name=p.lexer.source_filename, sourceref=SourceRef(lexer.source_filename, 1, 1)) p[0].nodes.append(scope) - print("CREATED ROOT SCOPE AND MODULE", p[0]) def p_module(p):