mirror of
https://github.com/irmen/prog8.git
synced 2024-11-26 11:49:22 +00:00
ast refactor
This commit is contained in:
parent
2f6ef28c80
commit
1ab253b4a1
@ -217,7 +217,7 @@ class CodeGenerator:
|
|||||||
"\t\tclc\t\t\t; clear carry flag",
|
"\t\tclc\t\t\t; clear carry flag",
|
||||||
"\t\tclv\t\t\t; clear overflow flag",
|
"\t\tclv\t\t\t; clear overflow flag",
|
||||||
]
|
]
|
||||||
statements.insert(index+1, ParseResult.InlineAsm(asmlines, 0))
|
statements.insert(index+1, ParseResult.InlineAsm(asmlines, stmt.sourceref))
|
||||||
break
|
break
|
||||||
block.statements = statements
|
block.statements = statements
|
||||||
# generate
|
# generate
|
||||||
@ -374,21 +374,21 @@ class CodeGenerator:
|
|||||||
elif isinstance(stmt, ParseResult.AssignmentStmt):
|
elif isinstance(stmt, ParseResult.AssignmentStmt):
|
||||||
self.generate_assignment(stmt)
|
self.generate_assignment(stmt)
|
||||||
elif isinstance(stmt, ParseResult.Label):
|
elif isinstance(stmt, ParseResult.Label):
|
||||||
self.p("\n{:s}\t\t\t\t; src l. {:d}".format(stmt.name, stmt.lineno))
|
self.p("\n{:s}\t\t\t\t; {:s}".format(stmt.name, stmt.lineref))
|
||||||
elif isinstance(stmt, (ParseResult.InplaceIncrStmt, ParseResult.InplaceDecrStmt)):
|
elif isinstance(stmt, (ParseResult.InplaceIncrStmt, ParseResult.InplaceDecrStmt)):
|
||||||
self.generate_incr_or_decr(stmt)
|
self.generate_incr_or_decr(stmt)
|
||||||
elif isinstance(stmt, ParseResult.CallStmt):
|
elif isinstance(stmt, ParseResult.CallStmt):
|
||||||
self.generate_call(stmt)
|
self.generate_call(stmt)
|
||||||
elif isinstance(stmt, ParseResult.InlineAsm):
|
elif isinstance(stmt, ParseResult.InlineAsm):
|
||||||
self.p("\t\t; inline asm, src l. {:d}".format(stmt.lineno))
|
self.p("\t\t; inline asm, " + stmt.lineref)
|
||||||
for line in stmt.asmlines:
|
for line in stmt.asmlines:
|
||||||
self.p(line)
|
self.p(line)
|
||||||
self.p("\t\t; end inline asm, src l. {:d}".format(stmt.lineno))
|
self.p("\t\t; end inline asm, " + stmt.lineref)
|
||||||
elif isinstance(stmt, ParseResult.Comment):
|
elif isinstance(stmt, ParseResult.Comment):
|
||||||
self.p(stmt.text)
|
self.p(stmt.text)
|
||||||
elif isinstance(stmt, ParseResult.BreakpointStmt):
|
elif isinstance(stmt, ParseResult.BreakpointStmt):
|
||||||
# put a marker in the source so that we can generate a list of breakpoints later
|
# put a marker in the source so that we can generate a list of breakpoints later
|
||||||
self.p("\t\tnop\t; {:s} src l. {:d}".format(self.BREAKPOINT_COMMENT_SIGNATURE, stmt.lineno))
|
self.p("\t\tnop\t; {:s} {:s}".format(self.BREAKPOINT_COMMENT_SIGNATURE, stmt.lineref))
|
||||||
else:
|
else:
|
||||||
raise CodeError("unknown statement " + repr(stmt))
|
raise CodeError("unknown statement " + repr(stmt))
|
||||||
self.previous_stmt_was_assignment = isinstance(stmt, ParseResult.AssignmentStmt)
|
self.previous_stmt_was_assignment = isinstance(stmt, ParseResult.AssignmentStmt)
|
||||||
@ -569,7 +569,7 @@ class CodeGenerator:
|
|||||||
raise CodeError("cannot in/decrement " + str(stmt.what))
|
raise CodeError("cannot in/decrement " + str(stmt.what))
|
||||||
|
|
||||||
def generate_call(self, stmt: ParseResult.CallStmt) -> None:
|
def generate_call(self, stmt: ParseResult.CallStmt) -> None:
|
||||||
self.p("\t\t\t\t\t; src l. {:d}".format(stmt.lineno))
|
self.p("\t\t\t\t\t; " + stmt.lineref)
|
||||||
if stmt.condition:
|
if stmt.condition:
|
||||||
assert stmt.is_goto
|
assert stmt.is_goto
|
||||||
if stmt.condition.lvalue:
|
if stmt.condition.lvalue:
|
||||||
@ -699,7 +699,7 @@ class CodeGenerator:
|
|||||||
self.p("+\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A
|
self.p("+\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A
|
||||||
else:
|
else:
|
||||||
raise CodeError("conditions cannot yet use other types than byte or word", # @todo comparisons of other types
|
raise CodeError("conditions cannot yet use other types than byte or word", # @todo comparisons of other types
|
||||||
cv.datatype, str(cv), self.cur_block.sourceref.file, stmt.lineno)
|
cv.datatype, str(cv), stmt.sourceref)
|
||||||
|
|
||||||
def branch_emitter_reg(targetstr: str, is_goto: bool, target_indirect: bool) -> None:
|
def branch_emitter_reg(targetstr: str, is_goto: bool, target_indirect: bool) -> None:
|
||||||
assert is_goto and not stmt.condition.comparison_op
|
assert is_goto and not stmt.condition.comparison_op
|
||||||
@ -786,7 +786,7 @@ class CodeGenerator:
|
|||||||
self.p("+\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A
|
self.p("+\t\tlda " + Parser.to_hex(Zeropage.SCRATCH_B1)) # restore A
|
||||||
else:
|
else:
|
||||||
raise CodeError("conditions cannot yet use other types than byte or word", # @todo comparisons of other types
|
raise CodeError("conditions cannot yet use other types than byte or word", # @todo comparisons of other types
|
||||||
cv.datatype, str(cv), self.cur_block.sourceref.file, stmt.lineno)
|
cv.datatype, str(cv), stmt.sourceref)
|
||||||
else:
|
else:
|
||||||
raise CodeError("weird indirect type", str(cv))
|
raise CodeError("weird indirect type", str(cv))
|
||||||
|
|
||||||
@ -954,8 +954,8 @@ class CodeGenerator:
|
|||||||
with self.preserving_registers(preserve_regs, loads_a_within=params_load_a()):
|
with self.preserving_registers(preserve_regs, loads_a_within=params_load_a()):
|
||||||
generate_param_assignments()
|
generate_param_assignments()
|
||||||
if targetstr in REGISTER_WORDS:
|
if targetstr in REGISTER_WORDS:
|
||||||
print("warning: {:s}:{:d}: indirect register pair call is quite inefficient, use a jump table in memory instead?"
|
print("warning: {}: indirect register pair call is quite inefficient, use a jump table in memory instead?"
|
||||||
.format(self.cur_block.sourceref.file, stmt.lineno))
|
.format(stmt.sourceref))
|
||||||
if stmt.preserve_regs:
|
if stmt.preserve_regs:
|
||||||
# cannot use zp scratch because it may be used by the register backup. This is very inefficient code!
|
# cannot use zp scratch because it may be used by the register backup. This is very inefficient code!
|
||||||
self.p("\t\tjsr il65_lib.jsr_indirect_nozpuse_"+targetstr)
|
self.p("\t\tjsr il65_lib.jsr_indirect_nozpuse_"+targetstr)
|
||||||
@ -995,7 +995,7 @@ class CodeGenerator:
|
|||||||
# for instance: value += 3
|
# for instance: value += 3
|
||||||
lvalue = stmt.leftvalues[0]
|
lvalue = stmt.leftvalues[0]
|
||||||
rvalue = stmt.right
|
rvalue = stmt.right
|
||||||
self.p("\t\t\t\t\t; src l. {:d}".format(stmt.lineno))
|
self.p("\t\t\t\t\t; " + stmt.lineref)
|
||||||
if isinstance(lvalue, ParseResult.RegisterValue):
|
if isinstance(lvalue, ParseResult.RegisterValue):
|
||||||
if isinstance(rvalue, ParseResult.IntegerValue):
|
if isinstance(rvalue, ParseResult.IntegerValue):
|
||||||
self._generate_aug_reg_int(lvalue, stmt.operator, rvalue)
|
self._generate_aug_reg_int(lvalue, stmt.operator, rvalue)
|
||||||
@ -1405,14 +1405,14 @@ class CodeGenerator:
|
|||||||
if isinstance(iv.value, ParseResult.MemMappedValue):
|
if isinstance(iv.value, ParseResult.MemMappedValue):
|
||||||
return iv.value
|
return iv.value
|
||||||
elif iv.value.constant and isinstance(iv.value, ParseResult.IntegerValue):
|
elif iv.value.constant and isinstance(iv.value, ParseResult.IntegerValue):
|
||||||
return ParseResult.MemMappedValue(iv.value.value, iv.datatype, 1, iv.name)
|
return ParseResult.MemMappedValue(iv.value.value, iv.datatype, 1, stmt.sourceref, iv.name)
|
||||||
else:
|
else:
|
||||||
raise CodeError("cannot yet generate code for assignment: non-constant and non-memmapped indirect") # XXX
|
raise CodeError("cannot yet generate code for assignment: non-constant and non-memmapped indirect") # XXX
|
||||||
|
|
||||||
rvalue = stmt.right
|
rvalue = stmt.right
|
||||||
if isinstance(rvalue, ParseResult.IndirectValue):
|
if isinstance(rvalue, ParseResult.IndirectValue):
|
||||||
rvalue = unwrap_indirect(rvalue)
|
rvalue = unwrap_indirect(rvalue)
|
||||||
self.p("\t\t\t\t\t; src l. {:d}".format(stmt.lineno))
|
self.p("\t\t\t\t\t; " + stmt.lineref)
|
||||||
if isinstance(rvalue, ParseResult.IntegerValue):
|
if isinstance(rvalue, ParseResult.IntegerValue):
|
||||||
for lv in stmt.leftvalues:
|
for lv in stmt.leftvalues:
|
||||||
if isinstance(lv, ParseResult.RegisterValue):
|
if isinstance(lv, ParseResult.RegisterValue):
|
||||||
|
257
il65/parse.py
257
il65/parse.py
@ -45,11 +45,44 @@ class ParseResult:
|
|||||||
for sub in block.symbols.iter_subroutines(True):
|
for sub in block.symbols.iter_subroutines(True):
|
||||||
yield sub.sub_block
|
yield sub.sub_block
|
||||||
|
|
||||||
class Block:
|
def add_block(self, block: 'ParseResult.Block', position: Optional[int]=None) -> None:
|
||||||
|
if position is not None:
|
||||||
|
self.blocks.insert(position, block)
|
||||||
|
else:
|
||||||
|
self.blocks.append(block)
|
||||||
|
|
||||||
|
def merge(self, parsed: 'ParseResult') -> None:
|
||||||
|
existing_blocknames = set(block.name for block in self.blocks)
|
||||||
|
other_blocknames = set(block.name for block in parsed.blocks)
|
||||||
|
overlap = existing_blocknames & other_blocknames
|
||||||
|
if overlap != {"<header>"}:
|
||||||
|
raise SymbolError("double block names: {}".format(overlap))
|
||||||
|
for block in parsed.blocks:
|
||||||
|
if block.name != "<header>":
|
||||||
|
self.blocks.append(block)
|
||||||
|
|
||||||
|
def find_block(self, name: str) -> 'Block':
|
||||||
|
for block in self.blocks:
|
||||||
|
if block.name == name:
|
||||||
|
return block
|
||||||
|
raise KeyError("block not found: " + name)
|
||||||
|
|
||||||
|
def sub_used_by(self, sub: SubroutineDef, sourceref: SourceRef) -> None:
|
||||||
|
self.subroutine_usage[(sub.blockname, sub.name)].add(str(sourceref))
|
||||||
|
|
||||||
|
class _AstNode:
|
||||||
|
def __init__(self, sourceref: SourceRef) -> None:
|
||||||
|
self.sourceref = sourceref.copy()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lineref(self) -> str:
|
||||||
|
return "src l. " + str(self.sourceref.line)
|
||||||
|
|
||||||
|
class Block(_AstNode):
|
||||||
_unnamed_block_labels = {} # type: Dict[ParseResult.Block, str]
|
_unnamed_block_labels = {} # type: Dict[ParseResult.Block, str]
|
||||||
|
|
||||||
def __init__(self, name: str, sourceref: SourceRef, parent_scope: SymbolTable) -> None:
|
def __init__(self, name: str, sourceref: SourceRef, parent_scope: SymbolTable) -> None:
|
||||||
self.sourceref = sourceref.copy()
|
super().__init__(sourceref)
|
||||||
self.address = 0
|
self.address = 0
|
||||||
self.name = name
|
self.name = name
|
||||||
self.statements = [] # type: List[ParseResult._AstNode]
|
self.statements = [] # type: List[ParseResult._AstNode]
|
||||||
@ -89,8 +122,9 @@ class ParseResult:
|
|||||||
for stmt in sub.sub_block.statements:
|
for stmt in sub.sub_block.statements:
|
||||||
yield sub.sub_block, sub, stmt
|
yield sub.sub_block, sub, stmt
|
||||||
|
|
||||||
class Value:
|
class Value(_AstNode):
|
||||||
def __init__(self, datatype: DataType, name: str=None, constant: bool=False) -> None:
|
def __init__(self, datatype: DataType, sourceref: SourceRef, name: str=None, constant: bool=False) -> None:
|
||||||
|
super().__init__(sourceref)
|
||||||
self.datatype = datatype
|
self.datatype = datatype
|
||||||
self.name = name
|
self.name = name
|
||||||
self.constant = constant
|
self.constant = constant
|
||||||
@ -102,9 +136,9 @@ class ParseResult:
|
|||||||
|
|
||||||
class IndirectValue(Value):
|
class IndirectValue(Value):
|
||||||
# only constant integers, memmapped and register values are wrapped in this.
|
# only constant integers, memmapped and register values are wrapped in this.
|
||||||
def __init__(self, value: 'ParseResult.Value', type_modifier: DataType) -> None:
|
def __init__(self, value: 'ParseResult.Value', type_modifier: DataType, sourceref: SourceRef) -> None:
|
||||||
assert type_modifier
|
assert type_modifier
|
||||||
super().__init__(type_modifier, value.name, False)
|
super().__init__(type_modifier, sourceref, value.name, False)
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -144,7 +178,7 @@ class ParseResult:
|
|||||||
return False, "incompatible value for indirect assignment (need byte, word, float or string)"
|
return False, "incompatible value for indirect assignment (need byte, word, float or string)"
|
||||||
|
|
||||||
class IntegerValue(Value):
|
class IntegerValue(Value):
|
||||||
def __init__(self, value: Optional[int], *, datatype: DataType=None, name: str=None) -> None:
|
def __init__(self, value: Optional[int], sourceref: SourceRef, *, datatype: DataType=None, name: str=None) -> None:
|
||||||
if type(value) is int:
|
if type(value) is int:
|
||||||
if datatype is None:
|
if datatype is None:
|
||||||
if 0 <= value < 0x100:
|
if 0 <= value < 0x100:
|
||||||
@ -157,12 +191,12 @@ class ParseResult:
|
|||||||
faultreason = check_value_in_range(datatype, "", 1, value)
|
faultreason = check_value_in_range(datatype, "", 1, value)
|
||||||
if faultreason:
|
if faultreason:
|
||||||
raise OverflowError(faultreason)
|
raise OverflowError(faultreason)
|
||||||
super().__init__(datatype, name, True)
|
super().__init__(datatype, sourceref, name, True)
|
||||||
self.value = value
|
self.value = value
|
||||||
elif value is None:
|
elif value is None:
|
||||||
if not name:
|
if not name:
|
||||||
raise ValueError("when integer value is not given, the name symbol should be speicified")
|
raise ValueError("when integer value is not given, the name symbol should be speicified")
|
||||||
super().__init__(datatype, name, True)
|
super().__init__(datatype, sourceref, name, True)
|
||||||
self.value = None
|
self.value = None
|
||||||
else:
|
else:
|
||||||
raise TypeError("invalid data type")
|
raise TypeError("invalid data type")
|
||||||
@ -182,9 +216,9 @@ class ParseResult:
|
|||||||
return "<IntegerValue {} name={}>".format(self.value, self.name)
|
return "<IntegerValue {} name={}>".format(self.value, self.name)
|
||||||
|
|
||||||
class FloatValue(Value):
|
class FloatValue(Value):
|
||||||
def __init__(self, value: float, name: str=None) -> None:
|
def __init__(self, value: float, sourceref: SourceRef, name: str=None) -> None:
|
||||||
if type(value) is float:
|
if type(value) is float:
|
||||||
super().__init__(DataType.FLOAT, name, True)
|
super().__init__(DataType.FLOAT, sourceref, name, True)
|
||||||
self.value = value
|
self.value = value
|
||||||
else:
|
else:
|
||||||
raise TypeError("invalid data type")
|
raise TypeError("invalid data type")
|
||||||
@ -204,8 +238,8 @@ class ParseResult:
|
|||||||
return "<FloatValue {} name={}>".format(self.value, self.name)
|
return "<FloatValue {} name={}>".format(self.value, self.name)
|
||||||
|
|
||||||
class StringValue(Value):
|
class StringValue(Value):
|
||||||
def __init__(self, value: str, name: str=None, constant: bool=False) -> None:
|
def __init__(self, value: str, sourceref: SourceRef, name: str=None, constant: bool=False) -> None:
|
||||||
super().__init__(DataType.STRING, name, constant)
|
super().__init__(DataType.STRING, sourceref, name, constant)
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
@ -223,10 +257,10 @@ class ParseResult:
|
|||||||
return "<StringValue {!r:s} name={} constant={}>".format(self.value, self.name, self.constant)
|
return "<StringValue {!r:s} name={} constant={}>".format(self.value, self.name, self.constant)
|
||||||
|
|
||||||
class RegisterValue(Value):
|
class RegisterValue(Value):
|
||||||
def __init__(self, register: str, datatype: DataType, name: str=None) -> None:
|
def __init__(self, register: str, datatype: DataType, sourceref: SourceRef, name: str=None) -> None:
|
||||||
assert datatype in (DataType.BYTE, DataType.WORD)
|
assert datatype in (DataType.BYTE, DataType.WORD)
|
||||||
assert register in REGISTER_SYMBOLS
|
assert register in REGISTER_SYMBOLS
|
||||||
super().__init__(datatype, name, False)
|
super().__init__(datatype, sourceref, name, False)
|
||||||
self.register = register
|
self.register = register
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
@ -292,8 +326,9 @@ class ParseResult:
|
|||||||
return False, "incompatible value for register assignment"
|
return False, "incompatible value for register assignment"
|
||||||
|
|
||||||
class MemMappedValue(Value):
|
class MemMappedValue(Value):
|
||||||
def __init__(self, address: Optional[int], datatype: DataType, length: int, name: str=None, constant: bool=False) -> None:
|
def __init__(self, address: Optional[int], datatype: DataType, length: int,
|
||||||
super().__init__(datatype, name, constant)
|
sourceref: SourceRef, name: str=None, constant: bool=False) -> None:
|
||||||
|
super().__init__(datatype, sourceref, name, constant)
|
||||||
self.address = address
|
self.address = address
|
||||||
self.length = length
|
self.length = length
|
||||||
assert address is None or type(address) is int
|
assert address is None or type(address) is int
|
||||||
@ -353,23 +388,19 @@ class ParseResult:
|
|||||||
return False, "(unsigned) word required"
|
return False, "(unsigned) word required"
|
||||||
return False, "incompatible value for assignment"
|
return False, "incompatible value for assignment"
|
||||||
|
|
||||||
class _AstNode: # @todo merge Value with this?
|
|
||||||
def __init__(self, lineno: int) -> None:
|
|
||||||
self.lineno = lineno
|
|
||||||
|
|
||||||
class Comment(_AstNode):
|
class Comment(_AstNode):
|
||||||
def __init__(self, text: str, lineno: int) -> None:
|
def __init__(self, text: str, sourceref: SourceRef) -> None:
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
self.text = text
|
self.text = text
|
||||||
|
|
||||||
class Label(_AstNode):
|
class Label(_AstNode):
|
||||||
def __init__(self, name: str, lineno: int) -> None:
|
def __init__(self, name: str, sourceref: SourceRef) -> None:
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
class AssignmentStmt(_AstNode):
|
class AssignmentStmt(_AstNode):
|
||||||
def __init__(self, leftvalues: List['ParseResult.Value'], right: 'ParseResult.Value', lineno: int) -> None:
|
def __init__(self, leftvalues: List['ParseResult.Value'], right: 'ParseResult.Value', sourceref: SourceRef) -> None:
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
self.leftvalues = leftvalues
|
self.leftvalues = leftvalues
|
||||||
self.right = right
|
self.right = right
|
||||||
|
|
||||||
@ -395,10 +426,10 @@ class ParseResult:
|
|||||||
self.right.name = stringvar_name
|
self.right.name = stringvar_name
|
||||||
self._immediate_string_vars[self.right.value] = (cur_block.name, stringvar_name)
|
self._immediate_string_vars[self.right.value] = (cur_block.name, stringvar_name)
|
||||||
|
|
||||||
def remove_identity_lvalues(self, filename: str, lineno: int) -> None:
|
def remove_identity_lvalues(self) -> None:
|
||||||
for lv in self.leftvalues:
|
for lv in self.leftvalues:
|
||||||
if lv == self.right:
|
if lv == self.right:
|
||||||
print("{:s}:{:d}: removed identity assignment".format(filename, lineno))
|
print("{}: removed identity assignment".format(self.sourceref))
|
||||||
remaining_leftvalues = [lv for lv in self.leftvalues if lv != self.right]
|
remaining_leftvalues = [lv for lv in self.leftvalues if lv != self.right]
|
||||||
self.leftvalues = remaining_leftvalues
|
self.leftvalues = remaining_leftvalues
|
||||||
|
|
||||||
@ -409,45 +440,45 @@ class ParseResult:
|
|||||||
SUPPORTED_OPERATORS = {"+=", "-=", "&=", "|=", "^=", ">>=", "<<="}
|
SUPPORTED_OPERATORS = {"+=", "-=", "&=", "|=", "^=", ">>=", "<<="}
|
||||||
# full set: {"+=", "-=", "*=", "/=", "%=", "//=", "**=", "&=", "|=", "^=", ">>=", "<<="}
|
# full set: {"+=", "-=", "*=", "/=", "%=", "//=", "**=", "&=", "|=", "^=", ">>=", "<<="}
|
||||||
|
|
||||||
def __init__(self, left: 'ParseResult.Value', operator: str, right: 'ParseResult.Value', lineno: int) -> None:
|
def __init__(self, left: 'ParseResult.Value', operator: str, right: 'ParseResult.Value', sourceref: SourceRef) -> None:
|
||||||
assert operator in self.SUPPORTED_OPERATORS
|
assert operator in self.SUPPORTED_OPERATORS
|
||||||
super().__init__([left], right, lineno)
|
super().__init__([left], right, sourceref)
|
||||||
self.operator = operator
|
self.operator = operator
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "<AugAssign {:s} {:s} {:s}>".format(str(self.leftvalues[0]), self.operator, str(self.right))
|
return "<AugAssign {:s} {:s} {:s}>".format(str(self.leftvalues[0]), self.operator, str(self.right))
|
||||||
|
|
||||||
class ReturnStmt(_AstNode):
|
class ReturnStmt(_AstNode):
|
||||||
def __init__(self, lineno: int, a: Optional['ParseResult.Value']=None,
|
def __init__(self, sourceref: SourceRef, a: Optional['ParseResult.Value']=None,
|
||||||
x: Optional['ParseResult.Value']=None,
|
x: Optional['ParseResult.Value']=None,
|
||||||
y: Optional['ParseResult.Value']=None) -> None:
|
y: Optional['ParseResult.Value']=None) -> None:
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
self.a = a
|
self.a = a
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
|
|
||||||
class InplaceIncrStmt(_AstNode):
|
class InplaceIncrStmt(_AstNode):
|
||||||
def __init__(self, what: 'ParseResult.Value', howmuch: Union[int, float], lineno: int) -> None:
|
def __init__(self, what: 'ParseResult.Value', howmuch: Union[int, float], sourceref: SourceRef) -> None:
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
assert howmuch > 0
|
assert howmuch > 0
|
||||||
self.what = what
|
self.what = what
|
||||||
self.howmuch = howmuch
|
self.howmuch = howmuch
|
||||||
|
|
||||||
class InplaceDecrStmt(_AstNode):
|
class InplaceDecrStmt(_AstNode):
|
||||||
def __init__(self, what: 'ParseResult.Value', howmuch: Union[int, float], lineno: int) -> None:
|
def __init__(self, what: 'ParseResult.Value', howmuch: Union[int, float], sourceref: SourceRef) -> None:
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
assert howmuch > 0
|
assert howmuch > 0
|
||||||
self.what = what
|
self.what = what
|
||||||
self.howmuch = howmuch
|
self.howmuch = howmuch
|
||||||
|
|
||||||
class CallStmt(_AstNode):
|
class CallStmt(_AstNode):
|
||||||
def __init__(self, lineno: int, target: Optional['ParseResult.Value']=None, *,
|
def __init__(self, sourceref: SourceRef, target: Optional['ParseResult.Value']=None, *,
|
||||||
address: Optional[int]=None, arguments: List[Tuple[str, Any]]=None,
|
address: Optional[int]=None, arguments: List[Tuple[str, Any]]=None,
|
||||||
outputs: List[Tuple[str, 'ParseResult.Value']]=None, is_goto: bool=False,
|
outputs: List[Tuple[str, 'ParseResult.Value']]=None, is_goto: bool=False,
|
||||||
preserve_regs: bool=True, condition: 'ParseResult.IfCondition'=None) -> None:
|
preserve_regs: bool=True, condition: 'ParseResult.IfCondition'=None) -> None:
|
||||||
if not is_goto:
|
if not is_goto:
|
||||||
assert condition is None
|
assert condition is None
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
self.target = target
|
self.target = target
|
||||||
self.address = address
|
self.address = address
|
||||||
self.arguments = arguments
|
self.arguments = arguments
|
||||||
@ -470,14 +501,13 @@ class ParseResult:
|
|||||||
if value.startswith("'") and value.endswith("'"):
|
if value.startswith("'") and value.endswith("'"):
|
||||||
parser.print_warning("possible problematic string to byte conversion (use a .text var instead?)")
|
parser.print_warning("possible problematic string to byte conversion (use a .text var instead?)")
|
||||||
if not assignment.is_identity():
|
if not assignment.is_identity():
|
||||||
assignment.lineno = self.lineno
|
assignment.sourceref = self.sourceref.copy() # @todo why set this?
|
||||||
self.desugared_call_arguments.append(assignment)
|
self.desugared_call_arguments.append(assignment)
|
||||||
if all(not isinstance(v, ParseResult.RegisterValue) for r, v in self.outputvars or []):
|
if all(not isinstance(v, ParseResult.RegisterValue) for r, v in self.outputvars or []):
|
||||||
# if none of the output variables are registers, we can simply generate the assignments without issues
|
# if none of the output variables are registers, we can simply generate the assignments without issues
|
||||||
for register, value in self.outputvars or []:
|
for register, value in self.outputvars or []:
|
||||||
rvalue = parser.parse_expression(register)
|
rvalue = parser.parse_expression(register)
|
||||||
assignment = ParseResult.AssignmentStmt([value], rvalue, self.lineno)
|
assignment = ParseResult.AssignmentStmt([value], rvalue, self.sourceref)
|
||||||
assignment.lineno = self.lineno
|
|
||||||
self.desugared_output_assignments.append(assignment)
|
self.desugared_output_assignments.append(assignment)
|
||||||
else:
|
else:
|
||||||
result_reg_mapping = [(register, value.register, value) for register, value in self.outputvars or []
|
result_reg_mapping = [(register, value.register, value) for register, value in self.outputvars or []
|
||||||
@ -492,13 +522,12 @@ class ParseResult:
|
|||||||
# note: do not remove the identity assignment here or the output register handling generates buggy code
|
# note: do not remove the identity assignment here or the output register handling generates buggy code
|
||||||
for register, value in self.outputvars or []:
|
for register, value in self.outputvars or []:
|
||||||
rvalue = parser.parse_expression(register)
|
rvalue = parser.parse_expression(register)
|
||||||
assignment = ParseResult.AssignmentStmt([value], rvalue, self.lineno)
|
assignment = ParseResult.AssignmentStmt([value], rvalue, self.sourceref)
|
||||||
assignment.lineno = self.lineno
|
|
||||||
self.desugared_output_assignments.append(assignment)
|
self.desugared_output_assignments.append(assignment)
|
||||||
|
|
||||||
class InlineAsm(_AstNode):
|
class InlineAsm(_AstNode):
|
||||||
def __init__(self, asmlines: List[str], lineno: int) -> None:
|
def __init__(self, asmlines: List[str], sourceref: SourceRef) -> None:
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
self.asmlines = asmlines
|
self.asmlines = asmlines
|
||||||
|
|
||||||
class IfCondition(_AstNode):
|
class IfCondition(_AstNode):
|
||||||
@ -511,12 +540,12 @@ class ParseResult:
|
|||||||
IF_STATUSES = {"cc", "cs", "vc", "vs", "eq", "ne", "true", "not", "zero", "pos", "neg", "lt", "gt", "le", "ge"}
|
IF_STATUSES = {"cc", "cs", "vc", "vs", "eq", "ne", "true", "not", "zero", "pos", "neg", "lt", "gt", "le", "ge"}
|
||||||
|
|
||||||
def __init__(self, ifstatus: str, leftvalue: Optional['ParseResult.Value'],
|
def __init__(self, ifstatus: str, leftvalue: Optional['ParseResult.Value'],
|
||||||
operator: str, rightvalue: Optional['ParseResult.Value'], lineno: int) -> None:
|
operator: str, rightvalue: Optional['ParseResult.Value'], sourceref: SourceRef) -> None:
|
||||||
assert ifstatus in self.IF_STATUSES
|
assert ifstatus in self.IF_STATUSES
|
||||||
assert operator in (None, "") or operator in self.SWAPPED_OPERATOR
|
assert operator in (None, "") or operator in self.SWAPPED_OPERATOR
|
||||||
if operator:
|
if operator:
|
||||||
assert ifstatus in ("true", "not", "zero")
|
assert ifstatus in ("true", "not", "zero")
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
self.ifstatus = ifstatus
|
self.ifstatus = ifstatus
|
||||||
self.lvalue = leftvalue
|
self.lvalue = leftvalue
|
||||||
self.comparison_op = operator
|
self.comparison_op = operator
|
||||||
@ -539,33 +568,8 @@ class ParseResult:
|
|||||||
return self.lvalue, self.comparison_op, self.rvalue
|
return self.lvalue, self.comparison_op, self.rvalue
|
||||||
|
|
||||||
class BreakpointStmt(_AstNode):
|
class BreakpointStmt(_AstNode):
|
||||||
def __init__(self, lineno: int) -> None:
|
def __init__(self, sourceref: SourceRef) -> None:
|
||||||
super().__init__(lineno)
|
super().__init__(sourceref)
|
||||||
|
|
||||||
def add_block(self, block: 'ParseResult.Block', position: Optional[int]=None) -> None:
|
|
||||||
if position is not None:
|
|
||||||
self.blocks.insert(position, block)
|
|
||||||
else:
|
|
||||||
self.blocks.append(block)
|
|
||||||
|
|
||||||
def merge(self, parsed: 'ParseResult') -> None:
|
|
||||||
existing_blocknames = set(block.name for block in self.blocks)
|
|
||||||
other_blocknames = set(block.name for block in parsed.blocks)
|
|
||||||
overlap = existing_blocknames & other_blocknames
|
|
||||||
if overlap != {"<header>"}:
|
|
||||||
raise SymbolError("double block names: {}".format(overlap))
|
|
||||||
for block in parsed.blocks:
|
|
||||||
if block.name != "<header>":
|
|
||||||
self.blocks.append(block)
|
|
||||||
|
|
||||||
def find_block(self, name: str) -> Block:
|
|
||||||
for block in self.blocks:
|
|
||||||
if block.name == name:
|
|
||||||
return block
|
|
||||||
raise KeyError("block not found: " + name)
|
|
||||||
|
|
||||||
def sub_used_by(self, sub: SubroutineDef, sourceref: SourceRef) -> None:
|
|
||||||
self.subroutine_usage[(sub.blockname, sub.name)].add(str(sourceref))
|
|
||||||
|
|
||||||
|
|
||||||
class Parser:
|
class Parser:
|
||||||
@ -659,7 +663,7 @@ class Parser:
|
|||||||
while True:
|
while True:
|
||||||
line = self.next_line().lstrip()
|
line = self.next_line().lstrip()
|
||||||
if line.startswith(';'):
|
if line.startswith(';'):
|
||||||
self.cur_block.statements.append(ParseResult.Comment(line, self.sourceref.line))
|
self.cur_block.statements.append(ParseResult.Comment(line, self.sourceref))
|
||||||
continue
|
continue
|
||||||
self.prev_line()
|
self.prev_line()
|
||||||
break
|
break
|
||||||
@ -730,16 +734,13 @@ class Parser:
|
|||||||
def desugar_immediate_strings(stmt: ParseResult._AstNode) -> None:
|
def desugar_immediate_strings(stmt: ParseResult._AstNode) -> None:
|
||||||
if isinstance(stmt, ParseResult.CallStmt):
|
if isinstance(stmt, ParseResult.CallStmt):
|
||||||
for s in stmt.desugared_call_arguments:
|
for s in stmt.desugared_call_arguments:
|
||||||
self.sourceref.line = s.lineno
|
self.sourceref = s.sourceref.copy()
|
||||||
self.sourceref.column = 0
|
|
||||||
s.desugar_immediate_string(self)
|
s.desugar_immediate_string(self)
|
||||||
for s in stmt.desugared_output_assignments:
|
for s in stmt.desugared_output_assignments:
|
||||||
self.sourceref.line = s.lineno
|
self.sourceref = s.sourceref.copy()
|
||||||
self.sourceref.column = 0
|
|
||||||
s.desugar_immediate_string(self)
|
s.desugar_immediate_string(self)
|
||||||
if isinstance(stmt, ParseResult.AssignmentStmt):
|
if isinstance(stmt, ParseResult.AssignmentStmt):
|
||||||
self.sourceref.line = stmt.lineno
|
self.sourceref = stmt.sourceref.copy()
|
||||||
self.sourceref.column = 0
|
|
||||||
stmt.desugar_immediate_string(self)
|
stmt.desugar_immediate_string(self)
|
||||||
|
|
||||||
for block in self.result.blocks:
|
for block in self.result.blocks:
|
||||||
@ -748,7 +749,7 @@ class Parser:
|
|||||||
self.sourceref.column = 0
|
self.sourceref.column = 0
|
||||||
for block, sub, stmt in block.all_statements():
|
for block, sub, stmt in block.all_statements():
|
||||||
if isinstance(stmt, ParseResult.CallStmt):
|
if isinstance(stmt, ParseResult.CallStmt):
|
||||||
self.sourceref.line = stmt.lineno
|
self.sourceref = stmt.sourceref.copy()
|
||||||
stmt.desugar_call_arguments_and_outputs(self)
|
stmt.desugar_call_arguments_and_outputs(self)
|
||||||
desugar_immediate_strings(stmt)
|
desugar_immediate_strings(stmt)
|
||||||
|
|
||||||
@ -1032,7 +1033,7 @@ class Parser:
|
|||||||
self.prev_line()
|
self.prev_line()
|
||||||
self.cur_block.statements.append(self.parse_asm())
|
self.cur_block.statements.append(self.parse_asm())
|
||||||
elif line == "breakpoint":
|
elif line == "breakpoint":
|
||||||
self.cur_block.statements.append(ParseResult.BreakpointStmt(self.sourceref.line))
|
self.cur_block.statements.append(ParseResult.BreakpointStmt(self.sourceref))
|
||||||
self.print_warning("breakpoint defined")
|
self.print_warning("breakpoint defined")
|
||||||
elif unstripped_line.startswith((" ", "\t")):
|
elif unstripped_line.startswith((" ", "\t")):
|
||||||
if is_zp_block:
|
if is_zp_block:
|
||||||
@ -1054,7 +1055,7 @@ class Parser:
|
|||||||
if labelname in self.cur_block.symbols:
|
if labelname in self.cur_block.symbols:
|
||||||
raise self.PError("symbol already defined")
|
raise self.PError("symbol already defined")
|
||||||
self.cur_block.symbols.define_label(labelname, self.sourceref)
|
self.cur_block.symbols.define_label(labelname, self.sourceref)
|
||||||
self.cur_block.statements.append(ParseResult.Label(labelname, self.sourceref.line))
|
self.cur_block.statements.append(ParseResult.Label(labelname, self.sourceref))
|
||||||
if len(label_line) > 1:
|
if len(label_line) > 1:
|
||||||
rest = label_line[1]
|
rest = label_line[1]
|
||||||
self.cur_block.statements.append(self.parse_statement(rest))
|
self.cur_block.statements.append(self.parse_statement(rest))
|
||||||
@ -1227,8 +1228,8 @@ class Parser:
|
|||||||
if isinstance(what, ParseResult.IntegerValue):
|
if isinstance(what, ParseResult.IntegerValue):
|
||||||
raise self.PError("cannot in/decrement a constant value")
|
raise self.PError("cannot in/decrement a constant value")
|
||||||
if incr:
|
if incr:
|
||||||
return ParseResult.InplaceIncrStmt(what, 1, self.sourceref.line)
|
return ParseResult.InplaceIncrStmt(what, 1, self.sourceref)
|
||||||
return ParseResult.InplaceDecrStmt(what, 1, self.sourceref.line)
|
return ParseResult.InplaceDecrStmt(what, 1, self.sourceref)
|
||||||
else:
|
else:
|
||||||
# perhaps it is an augmented assignment statement
|
# perhaps it is an augmented assignment statement
|
||||||
match = re.fullmatch(r"(?P<left>\S+)\s*(?P<assignment>\+=|-=|\*=|/=|%=|//=|\*\*=|&=|\|=|\^=|>>=|<<=)\s*(?P<right>\S.*)", line)
|
match = re.fullmatch(r"(?P<left>\S+)\s*(?P<assignment>\+=|-=|\*=|/=|%=|//=|\*\*=|&=|\|=|\^=|>>=|<<=)\s*(?P<right>\S.*)", line)
|
||||||
@ -1317,10 +1318,10 @@ class Parser:
|
|||||||
assert len(newsymbol.parameters) == 1
|
assert len(newsymbol.parameters) == 1
|
||||||
arguments = [(newsymbol.parameters[0][1], arguments[0][1])]
|
arguments = [(newsymbol.parameters[0][1], arguments[0][1])]
|
||||||
if is_goto:
|
if is_goto:
|
||||||
return ParseResult.CallStmt(self.sourceref.line, target, address=address,
|
return ParseResult.CallStmt(self.sourceref, target, address=address,
|
||||||
arguments=arguments, outputs=outputvars, is_goto=True, condition=condition)
|
arguments=arguments, outputs=outputvars, is_goto=True, condition=condition)
|
||||||
else:
|
else:
|
||||||
return ParseResult.CallStmt(self.sourceref.line, target, address=address,
|
return ParseResult.CallStmt(self.sourceref, target, address=address,
|
||||||
arguments=arguments, outputs=outputvars, preserve_regs=preserve_regs)
|
arguments=arguments, outputs=outputvars, preserve_regs=preserve_regs)
|
||||||
else:
|
else:
|
||||||
raise TypeError("target should be a Value", target)
|
raise TypeError("target should be a Value", target)
|
||||||
@ -1348,8 +1349,8 @@ class Parser:
|
|||||||
if isinstance(r_value, ParseResult.FloatValue):
|
if isinstance(r_value, ParseResult.FloatValue):
|
||||||
truncated, value = self.coerce_value(self.sourceref, lv.datatype, r_value.value)
|
truncated, value = self.coerce_value(self.sourceref, lv.datatype, r_value.value)
|
||||||
if truncated:
|
if truncated:
|
||||||
r_value = ParseResult.IntegerValue(int(value), datatype=lv.datatype, name=r_value.name)
|
r_value = ParseResult.IntegerValue(int(value), self.sourceref, datatype=lv.datatype, name=r_value.name)
|
||||||
return ParseResult.AssignmentStmt(l_values, r_value, self.sourceref.line)
|
return ParseResult.AssignmentStmt(l_values, r_value, self.sourceref)
|
||||||
|
|
||||||
def parse_augmented_assignment(self, leftstr: str, operator: str, rightstr: str) \
|
def parse_augmented_assignment(self, leftstr: str, operator: str, rightstr: str) \
|
||||||
-> Union[ParseResult.AssignmentStmt, ParseResult.InplaceDecrStmt, ParseResult.InplaceIncrStmt]:
|
-> Union[ParseResult.AssignmentStmt, ParseResult.InplaceDecrStmt, ParseResult.InplaceIncrStmt]:
|
||||||
@ -1365,23 +1366,23 @@ class Parser:
|
|||||||
if isinstance(r_value, ParseResult.FloatValue):
|
if isinstance(r_value, ParseResult.FloatValue):
|
||||||
truncated, value = self.coerce_value(self.sourceref, l_value.datatype, r_value.value)
|
truncated, value = self.coerce_value(self.sourceref, l_value.datatype, r_value.value)
|
||||||
if truncated:
|
if truncated:
|
||||||
r_value = ParseResult.IntegerValue(int(value), datatype=l_value.datatype, name=r_value.name)
|
r_value = ParseResult.IntegerValue(int(value), self.sourceref, datatype=l_value.datatype, name=r_value.name)
|
||||||
if r_value.constant and operator in ("+=", "-="):
|
if r_value.constant and operator in ("+=", "-="):
|
||||||
if operator == "+=":
|
if operator == "+=":
|
||||||
if r_value.value > 0: # type: ignore
|
if r_value.value > 0: # type: ignore
|
||||||
return ParseResult.InplaceIncrStmt(l_value, r_value.value, self.sourceref.line) # type: ignore
|
return ParseResult.InplaceIncrStmt(l_value, r_value.value, self.sourceref) # type: ignore
|
||||||
elif r_value.value < 0: # type: ignore
|
elif r_value.value < 0: # type: ignore
|
||||||
return ParseResult.InplaceDecrStmt(l_value, -r_value.value, self.sourceref.line) # type: ignore
|
return ParseResult.InplaceDecrStmt(l_value, -r_value.value, self.sourceref) # type: ignore
|
||||||
else:
|
else:
|
||||||
self.print_warning("incr with zero, ignored")
|
self.print_warning("incr with zero, ignored")
|
||||||
else:
|
else:
|
||||||
if r_value.value > 0: # type: ignore
|
if r_value.value > 0: # type: ignore
|
||||||
return ParseResult.InplaceDecrStmt(l_value, r_value.value, self.sourceref.line) # type: ignore
|
return ParseResult.InplaceDecrStmt(l_value, r_value.value, self.sourceref) # type: ignore
|
||||||
elif r_value.value < 0: # type: ignore
|
elif r_value.value < 0: # type: ignore
|
||||||
return ParseResult.InplaceIncrStmt(l_value, -r_value.value, self.sourceref.line) # type: ignore
|
return ParseResult.InplaceIncrStmt(l_value, -r_value.value, self.sourceref) # type: ignore
|
||||||
else:
|
else:
|
||||||
self.print_warning("decr with zero, ignored")
|
self.print_warning("decr with zero, ignored")
|
||||||
return ParseResult.AugmentedAssignmentStmt(l_value, operator, r_value, self.sourceref.line)
|
return ParseResult.AugmentedAssignmentStmt(l_value, operator, r_value, self.sourceref)
|
||||||
|
|
||||||
def parse_return(self, line: str) -> ParseResult.ReturnStmt:
|
def parse_return(self, line: str) -> ParseResult.ReturnStmt:
|
||||||
parts = line.split(maxsplit=1)
|
parts = line.split(maxsplit=1)
|
||||||
@ -1392,7 +1393,7 @@ class Parser:
|
|||||||
if len(parts) > 1:
|
if len(parts) > 1:
|
||||||
values = parts[1].split(",")
|
values = parts[1].split(",")
|
||||||
if len(values) == 0:
|
if len(values) == 0:
|
||||||
return ParseResult.ReturnStmt(self.sourceref.line)
|
return ParseResult.ReturnStmt(self.sourceref)
|
||||||
else:
|
else:
|
||||||
a = self.parse_expression(values[0]) if values[0] else None
|
a = self.parse_expression(values[0]) if values[0] else None
|
||||||
if len(values) > 1:
|
if len(values) > 1:
|
||||||
@ -1401,11 +1402,10 @@ class Parser:
|
|||||||
y = self.parse_expression(values[2]) if values[2] else None
|
y = self.parse_expression(values[2]) if values[2] else None
|
||||||
if len(values) > 3:
|
if len(values) > 3:
|
||||||
raise self.PError("too many returnvalues")
|
raise self.PError("too many returnvalues")
|
||||||
return ParseResult.ReturnStmt(self.sourceref.line, a, x, y)
|
return ParseResult.ReturnStmt(self.sourceref, a, x, y)
|
||||||
|
|
||||||
def parse_asm(self) -> ParseResult.InlineAsm:
|
def parse_asm(self) -> ParseResult.InlineAsm:
|
||||||
line = self.next_line()
|
line = self.next_line()
|
||||||
lineno = self.sourceref.line
|
|
||||||
aline = line.split()
|
aline = line.split()
|
||||||
if not len(aline) == 2 or aline[0] != "asm" or aline[1] != "{":
|
if not len(aline) == 2 or aline[0] != "asm" or aline[1] != "{":
|
||||||
raise self.PError("invalid asm start")
|
raise self.PError("invalid asm start")
|
||||||
@ -1413,7 +1413,7 @@ class Parser:
|
|||||||
while True:
|
while True:
|
||||||
line = self.next_line()
|
line = self.next_line()
|
||||||
if line.strip() == "}":
|
if line.strip() == "}":
|
||||||
return ParseResult.InlineAsm(asmlines, lineno)
|
return ParseResult.InlineAsm(asmlines, self.sourceref)
|
||||||
# asm can refer to other symbols as well, track subroutine usage
|
# asm can refer to other symbols as well, track subroutine usage
|
||||||
splits = line.split(maxsplit=1)
|
splits = line.split(maxsplit=1)
|
||||||
if len(splits) == 2:
|
if len(splits) == 2:
|
||||||
@ -1454,7 +1454,7 @@ class Parser:
|
|||||||
lines = ['{:s}\t.binclude "{:s}"'.format(scopename, filename)]
|
lines = ['{:s}\t.binclude "{:s}"'.format(scopename, filename)]
|
||||||
else:
|
else:
|
||||||
raise self.PError("invalid asminclude statement")
|
raise self.PError("invalid asminclude statement")
|
||||||
return ParseResult.InlineAsm(lines, self.sourceref.line)
|
return ParseResult.InlineAsm(lines, self.sourceref)
|
||||||
elif aline[0] == "asmbinary":
|
elif aline[0] == "asmbinary":
|
||||||
if len(aline) == 4:
|
if len(aline) == 4:
|
||||||
offset = parse_expr_as_int(aline[2], None, None, self.sourceref)
|
offset = parse_expr_as_int(aline[2], None, None, self.sourceref)
|
||||||
@ -1467,7 +1467,7 @@ class Parser:
|
|||||||
lines = ['\t.binary "{:s}"'.format(filename)]
|
lines = ['\t.binary "{:s}"'.format(filename)]
|
||||||
else:
|
else:
|
||||||
raise self.PError("invalid asmbinary statement")
|
raise self.PError("invalid asmbinary statement")
|
||||||
return ParseResult.InlineAsm(lines, self.sourceref.line)
|
return ParseResult.InlineAsm(lines, self.sourceref)
|
||||||
else:
|
else:
|
||||||
raise self.PError("invalid statement")
|
raise self.PError("invalid statement")
|
||||||
|
|
||||||
@ -1484,34 +1484,34 @@ class Parser:
|
|||||||
if isinstance(expression, ParseResult.StringValue):
|
if isinstance(expression, ParseResult.StringValue):
|
||||||
return expression
|
return expression
|
||||||
elif isinstance(expression, ParseResult.MemMappedValue):
|
elif isinstance(expression, ParseResult.MemMappedValue):
|
||||||
return ParseResult.IntegerValue(expression.address, datatype=DataType.WORD, name=expression.name)
|
return ParseResult.IntegerValue(expression.address, self.sourceref, datatype=DataType.WORD, name=expression.name)
|
||||||
else:
|
else:
|
||||||
raise self.PError("cannot take the address of this type")
|
raise self.PError("cannot take the address of this type")
|
||||||
elif text[0] in "-.0123456789$%~":
|
elif text[0] in "-.0123456789$%~":
|
||||||
number = parse_expr_as_number(text, self.cur_block.symbols, self.ppsymbols, self.sourceref)
|
number = parse_expr_as_number(text, self.cur_block.symbols, self.ppsymbols, self.sourceref)
|
||||||
try:
|
try:
|
||||||
if type(number) is int:
|
if type(number) is int:
|
||||||
return ParseResult.IntegerValue(int(number))
|
return ParseResult.IntegerValue(int(number), self.sourceref)
|
||||||
elif type(number) is float:
|
elif type(number) is float:
|
||||||
return ParseResult.FloatValue(number)
|
return ParseResult.FloatValue(number, self.sourceref)
|
||||||
else:
|
else:
|
||||||
raise TypeError("invalid number type")
|
raise TypeError("invalid number type")
|
||||||
except (ValueError, OverflowError) as ex:
|
except (ValueError, OverflowError) as ex:
|
||||||
raise self.PError(str(ex))
|
raise self.PError(str(ex))
|
||||||
elif text in REGISTER_WORDS:
|
elif text in REGISTER_WORDS:
|
||||||
return ParseResult.RegisterValue(text, DataType.WORD)
|
return ParseResult.RegisterValue(text, DataType.WORD, self.sourceref)
|
||||||
elif text in REGISTER_BYTES | REGISTER_SBITS:
|
elif text in REGISTER_BYTES | REGISTER_SBITS:
|
||||||
return ParseResult.RegisterValue(text, DataType.BYTE)
|
return ParseResult.RegisterValue(text, DataType.BYTE, self.sourceref)
|
||||||
elif (text.startswith("'") and text.endswith("'")) or (text.startswith('"') and text.endswith('"')):
|
elif (text.startswith("'") and text.endswith("'")) or (text.startswith('"') and text.endswith('"')):
|
||||||
strvalue = parse_expr_as_string(text, self.cur_block.symbols, self.ppsymbols, self.sourceref)
|
strvalue = parse_expr_as_string(text, self.cur_block.symbols, self.ppsymbols, self.sourceref)
|
||||||
if len(strvalue) == 1:
|
if len(strvalue) == 1:
|
||||||
petscii_code = char_to_bytevalue(strvalue)
|
petscii_code = char_to_bytevalue(strvalue)
|
||||||
return ParseResult.IntegerValue(petscii_code)
|
return ParseResult.IntegerValue(petscii_code, self.sourceref)
|
||||||
return ParseResult.StringValue(strvalue)
|
return ParseResult.StringValue(strvalue, self.sourceref)
|
||||||
elif text == "true":
|
elif text == "true":
|
||||||
return ParseResult.IntegerValue(1)
|
return ParseResult.IntegerValue(1, self.sourceref)
|
||||||
elif text == "false":
|
elif text == "false":
|
||||||
return ParseResult.IntegerValue(0)
|
return ParseResult.IntegerValue(0, self.sourceref)
|
||||||
elif self.is_identifier(text):
|
elif self.is_identifier(text):
|
||||||
symblock, sym = self.lookup_with_ppsymbols(text)
|
symblock, sym = self.lookup_with_ppsymbols(text)
|
||||||
if isinstance(sym, (VariableDef, ConstantDef)):
|
if isinstance(sym, (VariableDef, ConstantDef)):
|
||||||
@ -1521,21 +1521,22 @@ class Parser:
|
|||||||
else:
|
else:
|
||||||
symbolname = "{:s}.{:s}".format(sym.blockname, sym.name)
|
symbolname = "{:s}.{:s}".format(sym.blockname, sym.name)
|
||||||
if isinstance(sym, VariableDef) and sym.register:
|
if isinstance(sym, VariableDef) and sym.register:
|
||||||
return ParseResult.RegisterValue(sym.register, sym.type, name=symbolname)
|
return ParseResult.RegisterValue(sym.register, sym.type, self.sourceref, name=symbolname)
|
||||||
elif sym.type in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
elif sym.type in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
if isinstance(sym, ConstantDef):
|
if isinstance(sym, ConstantDef):
|
||||||
if sym.type == DataType.FLOAT:
|
if sym.type == DataType.FLOAT:
|
||||||
return ParseResult.FloatValue(sym.value, sym.name) # type: ignore
|
return ParseResult.FloatValue(sym.value, self.sourceref, sym.name) # type: ignore
|
||||||
elif sym.type in (DataType.BYTE, DataType.WORD):
|
elif sym.type in (DataType.BYTE, DataType.WORD):
|
||||||
return ParseResult.IntegerValue(sym.value, datatype=sym.type, name=sym.name) # type: ignore
|
return ParseResult.IntegerValue(sym.value, self.sourceref, datatype=sym.type, name=sym.name) # type: ignore
|
||||||
elif sym.type in STRING_DATATYPES:
|
elif sym.type in STRING_DATATYPES:
|
||||||
return ParseResult.StringValue(sym.value, sym.name, True) # type: ignore
|
return ParseResult.StringValue(sym.value, self.sourceref, sym.name, True) # type: ignore
|
||||||
else:
|
else:
|
||||||
raise TypeError("invalid const type", sym.type)
|
raise TypeError("invalid const type", sym.type)
|
||||||
else:
|
else:
|
||||||
return ParseResult.MemMappedValue(sym.address, sym.type, sym.length, name=symbolname, constant=constant)
|
return ParseResult.MemMappedValue(sym.address, sym.type, sym.length,
|
||||||
|
self.sourceref, name=symbolname, constant=constant)
|
||||||
elif sym.type in STRING_DATATYPES:
|
elif sym.type in STRING_DATATYPES:
|
||||||
return ParseResult.StringValue(sym.value, name=symbolname, constant=constant) # type: ignore
|
return ParseResult.StringValue(sym.value, self.sourceref, name=symbolname, constant=constant) # type: ignore
|
||||||
elif sym.type == DataType.MATRIX:
|
elif sym.type == DataType.MATRIX:
|
||||||
raise self.PError("cannot manipulate matrix directly, use one of the matrix procedures")
|
raise self.PError("cannot manipulate matrix directly, use one of the matrix procedures")
|
||||||
elif sym.type == DataType.BYTEARRAY or sym.type == DataType.WORDARRAY:
|
elif sym.type == DataType.BYTEARRAY or sym.type == DataType.WORDARRAY:
|
||||||
@ -1544,11 +1545,11 @@ class Parser:
|
|||||||
raise self.PError("invalid symbol type")
|
raise self.PError("invalid symbol type")
|
||||||
elif isinstance(sym, LabelDef):
|
elif isinstance(sym, LabelDef):
|
||||||
name = sym.name if symblock is self.cur_block else sym.blockname + '.' + sym.name
|
name = sym.name if symblock is self.cur_block else sym.blockname + '.' + sym.name
|
||||||
return ParseResult.MemMappedValue(None, DataType.WORD, 1, name, True)
|
return ParseResult.MemMappedValue(None, DataType.WORD, 1, self.sourceref, name, True)
|
||||||
elif isinstance(sym, SubroutineDef):
|
elif isinstance(sym, SubroutineDef):
|
||||||
self.result.sub_used_by(sym, self.sourceref)
|
self.result.sub_used_by(sym, self.sourceref)
|
||||||
name = sym.name if symblock is self.cur_block else sym.blockname + '.' + sym.name
|
name = sym.name if symblock is self.cur_block else sym.blockname + '.' + sym.name
|
||||||
return ParseResult.MemMappedValue(sym.address, DataType.WORD, 1, name, True)
|
return ParseResult.MemMappedValue(sym.address, DataType.WORD, 1, self.sourceref, name, True)
|
||||||
else:
|
else:
|
||||||
raise self.PError("invalid symbol type")
|
raise self.PError("invalid symbol type")
|
||||||
elif text.startswith('[') and text.endswith(']'):
|
elif text.startswith('[') and text.endswith(']'):
|
||||||
@ -1581,7 +1582,7 @@ class Parser:
|
|||||||
raise self.PError("invalid type modifier for the value's datatype, must be " + expr.datatype.name)
|
raise self.PError("invalid type modifier for the value's datatype, must be " + expr.datatype.name)
|
||||||
else:
|
else:
|
||||||
raise self.PError("use variable directly instead of using indirect addressing")
|
raise self.PError("use variable directly instead of using indirect addressing")
|
||||||
return indirect, ParseResult.IndirectValue(expr, type_modifier)
|
return indirect, ParseResult.IndirectValue(expr, type_modifier, self.sourceref)
|
||||||
|
|
||||||
def is_identifier(self, name: str) -> bool:
|
def is_identifier(self, name: str) -> bool:
|
||||||
if name.isidentifier():
|
if name.isidentifier():
|
||||||
@ -1687,9 +1688,9 @@ class Parser:
|
|||||||
rightv = None
|
rightv = None
|
||||||
if leftv == rightv:
|
if leftv == rightv:
|
||||||
raise self.PError("left and right values in comparison are identical")
|
raise self.PError("left and right values in comparison are identical")
|
||||||
result = ParseResult.IfCondition(ifstatus, leftv, operator, rightv, self.sourceref.line)
|
result = ParseResult.IfCondition(ifstatus, leftv, operator, rightv, self.sourceref)
|
||||||
else:
|
else:
|
||||||
result = ParseResult.IfCondition(ifstatus, None, "", None, self.sourceref.line)
|
result = ParseResult.IfCondition(ifstatus, None, "", None, self.sourceref)
|
||||||
if result.make_if_true():
|
if result.make_if_true():
|
||||||
self.print_warning("if_not condition inverted to if")
|
self.print_warning("if_not condition inverted to if")
|
||||||
return result
|
return result
|
||||||
@ -1746,7 +1747,7 @@ class Optimizer:
|
|||||||
cond.comparison_op, cond.rvalue = "", None
|
cond.comparison_op, cond.rvalue = "", None
|
||||||
simplified = True
|
simplified = True
|
||||||
if simplified:
|
if simplified:
|
||||||
print("{:s}:{:d}: simplified comparison with zero".format(block.sourceref.file, stmt.lineno))
|
print("{}: simplified comparison with zero".format(stmt.sourceref))
|
||||||
|
|
||||||
def combine_assignments_into_multi(self, block: ParseResult.Block) -> None:
|
def combine_assignments_into_multi(self, block: ParseResult.Block) -> None:
|
||||||
# fold multiple consecutive assignments with the same rvalue into one multi-assignment
|
# fold multiple consecutive assignments with the same rvalue into one multi-assignment
|
||||||
@ -1756,7 +1757,7 @@ class Optimizer:
|
|||||||
if isinstance(stmt, ParseResult.AssignmentStmt) and not isinstance(stmt, ParseResult.AugmentedAssignmentStmt):
|
if isinstance(stmt, ParseResult.AssignmentStmt) and not isinstance(stmt, ParseResult.AugmentedAssignmentStmt):
|
||||||
if multi_assign_statement and multi_assign_statement.right == stmt.right:
|
if multi_assign_statement and multi_assign_statement.right == stmt.right:
|
||||||
multi_assign_statement.leftvalues.extend(stmt.leftvalues)
|
multi_assign_statement.leftvalues.extend(stmt.leftvalues)
|
||||||
print("{:s}:{:d}: joined with previous line into multi-assign statement".format(block.sourceref.file, stmt.lineno))
|
print("{}: joined with previous line into multi-assign statement".format(stmt.sourceref))
|
||||||
else:
|
else:
|
||||||
if multi_assign_statement:
|
if multi_assign_statement:
|
||||||
statements.append(multi_assign_statement)
|
statements.append(multi_assign_statement)
|
||||||
@ -1777,7 +1778,7 @@ class Optimizer:
|
|||||||
# remove duplicates
|
# remove duplicates
|
||||||
lvalues = list(set(stmt.leftvalues))
|
lvalues = list(set(stmt.leftvalues))
|
||||||
if len(lvalues) != len(stmt.leftvalues):
|
if len(lvalues) != len(stmt.leftvalues):
|
||||||
print("{:s}:{:d}: removed duplicate assignment targets".format(block.sourceref.file, stmt.lineno))
|
print("{}: removed duplicate assignment targets".format(stmt.sourceref))
|
||||||
# change order: first registers, then zp addresses, then non-zp addresses, then the rest (if any)
|
# change order: first registers, then zp addresses, then non-zp addresses, then the rest (if any)
|
||||||
stmt.leftvalues = list(sorted(lvalues, key=_value_sortkey))
|
stmt.leftvalues = list(sorted(lvalues, key=_value_sortkey))
|
||||||
|
|
||||||
@ -1785,9 +1786,9 @@ class Optimizer:
|
|||||||
have_removed_stmts = False
|
have_removed_stmts = False
|
||||||
for index, stmt in enumerate(list(block.statements)):
|
for index, stmt in enumerate(list(block.statements)):
|
||||||
if isinstance(stmt, ParseResult.AssignmentStmt):
|
if isinstance(stmt, ParseResult.AssignmentStmt):
|
||||||
stmt.remove_identity_lvalues(block.sourceref.file, stmt.lineno)
|
stmt.remove_identity_lvalues()
|
||||||
if not stmt.leftvalues:
|
if not stmt.leftvalues:
|
||||||
print("{:s}:{:d}: removed identity assignment statement".format(block.sourceref.file, stmt.lineno))
|
print("{}: removed identity assignment statement".format(stmt.sourceref))
|
||||||
have_removed_stmts = True
|
have_removed_stmts = True
|
||||||
block.statements[index] = None
|
block.statements[index] = None
|
||||||
if have_removed_stmts:
|
if have_removed_stmts:
|
||||||
|
@ -42,7 +42,7 @@ class PreprocessingParser(Parser):
|
|||||||
return self.result
|
return self.result
|
||||||
|
|
||||||
def parse_asminclude(self, line: str) -> ParseResult.InlineAsm:
|
def parse_asminclude(self, line: str) -> ParseResult.InlineAsm:
|
||||||
return ParseResult.InlineAsm([], self.sourceref.line)
|
return ParseResult.InlineAsm([], self.sourceref)
|
||||||
|
|
||||||
def parse_statement(self, line: str) -> ParseResult._AstNode:
|
def parse_statement(self, line: str) -> ParseResult._AstNode:
|
||||||
return None # type: ignore
|
return None # type: ignore
|
||||||
|
6
todo.ill
6
todo.ill
@ -1,6 +1,6 @@
|
|||||||
output prg,basic
|
output prg,basic
|
||||||
|
|
||||||
;reg_preserve off ; @todo global option off/on default off?
|
;reg_preserve off ; @todo global option off/on default off? NOT AN OPTION -> change the default to OFF
|
||||||
|
|
||||||
import "c64lib"
|
import "c64lib"
|
||||||
|
|
||||||
@ -10,8 +10,8 @@ start
|
|||||||
X = $22
|
X = $22
|
||||||
Y = $33
|
Y = $33
|
||||||
|
|
||||||
c64scr.clear_screen !(81, 5, $04)
|
c64scr.clear_screen !(81, 5, $04) ; @todo new syntax to specify registers to save (! = all three A,X,Y)
|
||||||
;c64scr.clear_screen !A (81, 5, $04)
|
;c64scr.clear_screen !A (81, 5, $04) ; @todo new syntax to specify registers to save (only A)
|
||||||
;c64scr.clear_screen !AX (81, 5, $04)
|
;c64scr.clear_screen !AX (81, 5, $04)
|
||||||
;c64scr.clear_screen !AXY (81, 5, $04)
|
;c64scr.clear_screen !AXY (81, 5, $04)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user