mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 01:29:28 +00:00
coerce values put back mostly
This commit is contained in:
parent
f06721a4ce
commit
dba4dd780a
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user