coerce values put back mostly

This commit is contained in:
Irmen de Jong 2018-02-01 01:56:36 +01:00
parent f06721a4ce
commit dba4dd780a
3 changed files with 60 additions and 28 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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):