mirror of
https://github.com/irmen/prog8.git
synced 2024-11-29 17:50:35 +00:00
symbol table fixes
This commit is contained in:
parent
614f90fc35
commit
e6804b2bf9
@ -31,12 +31,12 @@ class PlyParser:
|
|||||||
self.check_directives(module)
|
self.check_directives(module)
|
||||||
self.process_imports(module)
|
self.process_imports(module)
|
||||||
self.create_multiassigns(module)
|
self.create_multiassigns(module)
|
||||||
|
self.check_and_merge_zeropages(module)
|
||||||
self.process_all_expressions(module)
|
self.process_all_expressions(module)
|
||||||
if not self.parsing_import:
|
if not self.parsing_import:
|
||||||
# these shall only be done on the main module after all imports have been done:
|
# these shall only be done on the main module after all imports have been done:
|
||||||
self.apply_directive_options(module)
|
self.apply_directive_options(module)
|
||||||
self.determine_subroutine_usage(module)
|
self.determine_subroutine_usage(module)
|
||||||
self.check_and_merge_zeropages(module)
|
|
||||||
except ParseError as x:
|
except ParseError as x:
|
||||||
self.handle_parse_error(x)
|
self.handle_parse_error(x)
|
||||||
if self.parse_errors:
|
if self.parse_errors:
|
||||||
@ -60,6 +60,7 @@ class PlyParser:
|
|||||||
zeropage.scope.add_node(node, 0)
|
zeropage.scope.add_node(node, 0)
|
||||||
elif isinstance(node, VarDef):
|
elif isinstance(node, VarDef):
|
||||||
zeropage.scope.add_node(node)
|
zeropage.scope.add_node(node)
|
||||||
|
print("ADDED ZP VAR", node) # XXX
|
||||||
else:
|
else:
|
||||||
raise ParseError("only variables and directives allowed in zeropage block", node.sourceref)
|
raise ParseError("only variables and directives allowed in zeropage block", node.sourceref)
|
||||||
else:
|
else:
|
||||||
@ -72,7 +73,11 @@ class PlyParser:
|
|||||||
@no_type_check
|
@no_type_check
|
||||||
def process_all_expressions(self, module: Module) -> None:
|
def process_all_expressions(self, module: Module) -> None:
|
||||||
# process/simplify all expressions (constant folding etc)
|
# process/simplify all expressions (constant folding etc)
|
||||||
|
encountered_blocks = set()
|
||||||
for block, parent in module.all_scopes():
|
for block, parent in module.all_scopes():
|
||||||
|
if block.name in encountered_blocks:
|
||||||
|
raise ValueError("block names not unique:", block.name)
|
||||||
|
encountered_blocks.add(block.name)
|
||||||
for node in block.nodes:
|
for node in block.nodes:
|
||||||
try:
|
try:
|
||||||
node.process_expressions(block.scope)
|
node.process_expressions(block.scope)
|
||||||
@ -266,7 +271,7 @@ class PlyParser:
|
|||||||
if len(splits) == 2:
|
if len(splits) == 2:
|
||||||
for match in re.finditer(r"(?P<symbol>[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]):
|
for match in re.finditer(r"(?P<symbol>[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]):
|
||||||
name = match.group("symbol")
|
name = match.group("symbol")
|
||||||
if name[0] == '$':
|
if name[0] == '$' or "." not in name:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
symbol = parent_scope[name]
|
symbol = parent_scope[name]
|
||||||
@ -274,7 +279,8 @@ class PlyParser:
|
|||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if isinstance(symbol, Subroutine):
|
if isinstance(symbol, Subroutine):
|
||||||
usages[(parent_scope.name, symbol.name)].add(str(asmnode.sourceref))
|
namespace, name = name.rsplit(".", maxsplit=2)
|
||||||
|
usages[(namespace, name)].add(str(asmnode.sourceref))
|
||||||
|
|
||||||
def check_directives(self, module: Module) -> None:
|
def check_directives(self, module: Module) -> None:
|
||||||
for node, parent in module.all_scopes():
|
for node, parent in module.all_scopes():
|
||||||
@ -329,7 +335,7 @@ class PlyParser:
|
|||||||
# append the imported module's contents (blocks) at the end of the current module
|
# append the imported module's contents (blocks) at the end of the current module
|
||||||
for imported_module in imported:
|
for imported_module in imported:
|
||||||
for block in imported_module.scope.filter_nodes(Block):
|
for block in imported_module.scope.filter_nodes(Block):
|
||||||
module.scope.nodes.append(block)
|
module.scope.add_node(block)
|
||||||
|
|
||||||
def import_file(self, filename: str) -> Tuple[Module, int]:
|
def import_file(self, filename: str) -> Tuple[Module, int]:
|
||||||
sub_parser = PlyParser(parsing_import=True)
|
sub_parser = PlyParser(parsing_import=True)
|
||||||
|
@ -127,6 +127,7 @@ class AssemblyGenerator:
|
|||||||
self.p("{:s}\t.proc\n".format(zpblock.label))
|
self.p("{:s}\t.proc\n".format(zpblock.label))
|
||||||
self.generate_block_init(zpblock)
|
self.generate_block_init(zpblock)
|
||||||
self.generate_block_vars(zpblock)
|
self.generate_block_vars(zpblock)
|
||||||
|
# there's no code in the zero page block.
|
||||||
self.p("\v.pend\n")
|
self.p("\v.pend\n")
|
||||||
for block in sorted(self.module.scope.filter_nodes(Block), key=lambda b: b.address or 0):
|
for block in sorted(self.module.scope.filter_nodes(Block), key=lambda b: b.address or 0):
|
||||||
if block.name == "ZP":
|
if block.name == "ZP":
|
||||||
@ -149,8 +150,8 @@ class AssemblyGenerator:
|
|||||||
self.p("\v{:s} = {:s}".format(subdef.name, to_hex(subdef.address)))
|
self.p("\v{:s} = {:s}".format(subdef.name, to_hex(subdef.address)))
|
||||||
self.p("; end external subroutines\n")
|
self.p("; end external subroutines\n")
|
||||||
for stmt in block.scope.nodes:
|
for stmt in block.scope.nodes:
|
||||||
if isinstance(stmt, (VarDef, Directive)):
|
if isinstance(stmt, (VarDef, Directive, Subroutine)):
|
||||||
continue # should have been handled already
|
continue # should have been handled already or will be later
|
||||||
self.generate_statement(stmt)
|
self.generate_statement(stmt)
|
||||||
if block.name == "main" and isinstance(stmt, Label) and stmt.name == "start":
|
if block.name == "main" and isinstance(stmt, Label) and stmt.name == "start":
|
||||||
# make sure the main.start routine clears the decimal and carry flags as first steps
|
# make sure the main.start routine clears the decimal and carry flags as first steps
|
||||||
@ -215,12 +216,13 @@ class AssemblyGenerator:
|
|||||||
# generate the block initializer
|
# generate the block initializer
|
||||||
# @todo add a block initializer subroutine that can contain custom reset/init code? (static initializer)
|
# @todo add a block initializer subroutine that can contain custom reset/init code? (static initializer)
|
||||||
self.p("_il65_init_block\v; (re)set vars to initial values")
|
self.p("_il65_init_block\v; (re)set vars to initial values")
|
||||||
# @todo optimize init order (sort on value first to avoid needless register loads, etc)
|
|
||||||
self.p("\vlda #0\n\vldx #0")
|
self.p("\vlda #0\n\vldx #0")
|
||||||
float_inits = {}
|
float_inits = {}
|
||||||
string_inits = []
|
string_inits = []
|
||||||
prev_value = 0
|
prev_value = 0
|
||||||
for variable in [vd for vd in block.scope.filter_nodes(VarDef) if vd.vartype == VarType.VAR]:
|
vardefs = [vd for vd in block.scope.filter_nodes(VarDef) if vd.vartype == VarType.VAR]
|
||||||
|
# @todo optimize init order (sort on value first to avoid needless register loads, etc)
|
||||||
|
for variable in vardefs:
|
||||||
vname = variable.name
|
vname = variable.name
|
||||||
vvalue = variable.value
|
vvalue = variable.value
|
||||||
if variable.datatype == DataType.BYTE:
|
if variable.datatype == DataType.BYTE:
|
||||||
|
@ -53,6 +53,8 @@ start = "start"
|
|||||||
@attr.s(cmp=False, slots=True, frozen=False)
|
@attr.s(cmp=False, slots=True, frozen=False)
|
||||||
class AstNode:
|
class AstNode:
|
||||||
sourceref = attr.ib(type=SourceRef)
|
sourceref = attr.ib(type=SourceRef)
|
||||||
|
# when evaluating an expression, does it have to be a constant value?:
|
||||||
|
processed_expr_must_be_constant = attr.ib(type=bool, init=False, default=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def lineref(self) -> str:
|
def lineref(self) -> str:
|
||||||
@ -410,8 +412,8 @@ class VarDef(AstNode):
|
|||||||
raise ParseError("constant value assignment is missing",
|
raise ParseError("constant value assignment is missing",
|
||||||
attr.evolve(self.sourceref, column=self.sourceref.column+len(self.name)))
|
attr.evolve(self.sourceref, column=self.sourceref.column+len(self.name)))
|
||||||
# if the value is an expression, mark it as a *constant* expression here
|
# if the value is an expression, mark it as a *constant* expression here
|
||||||
if isinstance(self.value, Expression):
|
if isinstance(self.value, AstNode):
|
||||||
self.value.processed_must_be_constant = True
|
self.value.processed_expr_must_be_constant = True
|
||||||
elif self.value is None and self.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
elif self.value is None and self.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
|
||||||
self.value = 0
|
self.value = 0
|
||||||
# note: value coercion is done later, when all expressions are evaluated
|
# note: value coercion is done later, when all expressions are evaluated
|
||||||
@ -423,7 +425,7 @@ class VarDef(AstNode):
|
|||||||
try:
|
try:
|
||||||
_, self.value = coerce_value(self.datatype, self.value, self.sourceref)
|
_, self.value = coerce_value(self.datatype, self.value, self.sourceref)
|
||||||
except (TypeError, OverflowError) as x:
|
except (TypeError, OverflowError) as x:
|
||||||
raise ParseError(str(x), self.sourceref) from None
|
raise ParseError("processed expression vor vardef is not a constant value: " + str(x), self.sourceref) from None
|
||||||
|
|
||||||
|
|
||||||
@attr.s(cmp=False, slots=True, repr=False)
|
@attr.s(cmp=False, slots=True, repr=False)
|
||||||
@ -548,7 +550,6 @@ class Expression(AstNode):
|
|||||||
operator = attr.ib(type=str)
|
operator = attr.ib(type=str)
|
||||||
right = attr.ib()
|
right = attr.ib()
|
||||||
unary = attr.ib(type=bool, default=False)
|
unary = attr.ib(type=bool, default=False)
|
||||||
processed_must_be_constant = attr.ib(type=bool, init=False, default=False) # does the expression have to be a constant value?
|
|
||||||
|
|
||||||
def __attrs_post_init__(self):
|
def __attrs_post_init__(self):
|
||||||
assert self.operator not in ("++", "--"), "incr/decr should not be an expression"
|
assert self.operator not in ("++", "--"), "incr/decr should not be an expression"
|
||||||
@ -586,8 +587,8 @@ class Expression(AstNode):
|
|||||||
|
|
||||||
def process_expression(value: Any, scope: Scope, sourceref: SourceRef) -> Any:
|
def process_expression(value: Any, scope: Scope, sourceref: SourceRef) -> Any:
|
||||||
# process/simplify all expressions (constant folding etc)
|
# process/simplify all expressions (constant folding etc)
|
||||||
if isinstance(value, Expression):
|
if isinstance(value, AstNode):
|
||||||
must_be_constant = value.processed_must_be_constant
|
must_be_constant = value.processed_expr_must_be_constant
|
||||||
else:
|
else:
|
||||||
must_be_constant = False
|
must_be_constant = False
|
||||||
if must_be_constant:
|
if must_be_constant:
|
||||||
@ -631,7 +632,6 @@ def process_constant_expression(expr: Any, sourceref: SourceRef, symbolscope: Sc
|
|||||||
raise ParseError(str(x), expr.sourceref) from None
|
raise ParseError(str(x), expr.sourceref) from None
|
||||||
elif isinstance(expr, SubCall):
|
elif isinstance(expr, SubCall):
|
||||||
if isinstance(expr.target, CallTarget):
|
if isinstance(expr.target, CallTarget):
|
||||||
print("CALLTARGET", expr.target.address_of, expr.target.target) # XXX
|
|
||||||
target = expr.target.target
|
target = expr.target.target
|
||||||
if isinstance(target, SymbolName): # 'function(1,2,3)'
|
if isinstance(target, SymbolName): # 'function(1,2,3)'
|
||||||
funcname = target.name
|
funcname = target.name
|
||||||
|
@ -125,10 +125,10 @@
|
|||||||
; taking the address of things from the ZP will work even when it is a var
|
; taking the address of things from the ZP will work even when it is a var
|
||||||
; because zp-vars get assigned a specific address (from a pool). Also, it's a byte.
|
; because zp-vars get assigned a specific address (from a pool). Also, it's a byte.
|
||||||
|
|
||||||
var .word initword0a = &ZP.zpmem1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP)
|
var .word initword0a = &ZP.zpmem1
|
||||||
var .word initword0 = &ZP.zpvar1
|
var .word initword0 = &ZP.zpvar1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP)
|
||||||
var initbytea0 = &ZP.zpmem1
|
var initbytea0 = &ZP.zpmem1
|
||||||
var .word initworda1 = &ZP.zpvar1
|
var .word initworda1 = &ZP.zpvar1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP)
|
||||||
|
|
||||||
|
|
||||||
; (constant) expressions
|
; (constant) expressions
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
; floating point tests
|
; floating point tests
|
||||||
|
|
||||||
%output prg, basic
|
%output basic
|
||||||
|
|
||||||
%import c64lib
|
%import c64lib
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
%zp restore , clobber ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
%zp restore , clobber ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||||
|
|
||||||
|
|
||||||
~ main $0a00
|
~ main
|
||||||
{
|
{
|
||||||
; this is the main block with the start routine.
|
; this is the main block with the start routine.
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ somelabel1:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
~ fidget $1000
|
~ fidget $0c00
|
||||||
{
|
{
|
||||||
|
|
||||||
subroutine:
|
subroutine:
|
||||||
|
Loading…
Reference in New Issue
Block a user