symbol table fixes

This commit is contained in:
Irmen de Jong 2018-01-12 23:25:00 +01:00
parent 614f90fc35
commit e6804b2bf9
6 changed files with 29 additions and 21 deletions

View File

@ -31,12 +31,12 @@ class PlyParser:
self.check_directives(module)
self.process_imports(module)
self.create_multiassigns(module)
self.check_and_merge_zeropages(module)
self.process_all_expressions(module)
if not self.parsing_import:
# these shall only be done on the main module after all imports have been done:
self.apply_directive_options(module)
self.determine_subroutine_usage(module)
self.check_and_merge_zeropages(module)
except ParseError as x:
self.handle_parse_error(x)
if self.parse_errors:
@ -60,6 +60,7 @@ class PlyParser:
zeropage.scope.add_node(node, 0)
elif isinstance(node, VarDef):
zeropage.scope.add_node(node)
print("ADDED ZP VAR", node) # XXX
else:
raise ParseError("only variables and directives allowed in zeropage block", node.sourceref)
else:
@ -72,7 +73,11 @@ class PlyParser:
@no_type_check
def process_all_expressions(self, module: Module) -> None:
# process/simplify all expressions (constant folding etc)
encountered_blocks = set()
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:
try:
node.process_expressions(block.scope)
@ -266,7 +271,7 @@ class PlyParser:
if len(splits) == 2:
for match in re.finditer(r"(?P<symbol>[a-zA-Z_$][a-zA-Z0-9_\.]+)", splits[1]):
name = match.group("symbol")
if name[0] == '$':
if name[0] == '$' or "." not in name:
continue
try:
symbol = parent_scope[name]
@ -274,7 +279,8 @@ class PlyParser:
pass
else:
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:
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
for imported_module in imported:
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]:
sub_parser = PlyParser(parsing_import=True)

View File

@ -127,6 +127,7 @@ class AssemblyGenerator:
self.p("{:s}\t.proc\n".format(zpblock.label))
self.generate_block_init(zpblock)
self.generate_block_vars(zpblock)
# there's no code in the zero page block.
self.p("\v.pend\n")
for block in sorted(self.module.scope.filter_nodes(Block), key=lambda b: b.address or 0):
if block.name == "ZP":
@ -149,8 +150,8 @@ class AssemblyGenerator:
self.p("\v{:s} = {:s}".format(subdef.name, to_hex(subdef.address)))
self.p("; end external subroutines\n")
for stmt in block.scope.nodes:
if isinstance(stmt, (VarDef, Directive)):
continue # should have been handled already
if isinstance(stmt, (VarDef, Directive, Subroutine)):
continue # should have been handled already or will be later
self.generate_statement(stmt)
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
@ -215,12 +216,13 @@ class AssemblyGenerator:
# generate the block 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")
# @todo optimize init order (sort on value first to avoid needless register loads, etc)
self.p("\vlda #0\n\vldx #0")
float_inits = {}
string_inits = []
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
vvalue = variable.value
if variable.datatype == DataType.BYTE:

View File

@ -53,6 +53,8 @@ start = "start"
@attr.s(cmp=False, slots=True, frozen=False)
class AstNode:
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
def lineref(self) -> str:
@ -410,8 +412,8 @@ class VarDef(AstNode):
raise ParseError("constant value assignment is missing",
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 isinstance(self.value, Expression):
self.value.processed_must_be_constant = True
if isinstance(self.value, AstNode):
self.value.processed_expr_must_be_constant = True
elif self.value is None and self.datatype in (DataType.BYTE, DataType.WORD, DataType.FLOAT):
self.value = 0
# note: value coercion is done later, when all expressions are evaluated
@ -423,7 +425,7 @@ class VarDef(AstNode):
try:
_, self.value = coerce_value(self.datatype, self.value, self.sourceref)
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)
@ -548,7 +550,6 @@ class Expression(AstNode):
operator = attr.ib(type=str)
right = attr.ib()
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):
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:
# process/simplify all expressions (constant folding etc)
if isinstance(value, Expression):
must_be_constant = value.processed_must_be_constant
if isinstance(value, AstNode):
must_be_constant = value.processed_expr_must_be_constant
else:
must_be_constant = False
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
elif isinstance(expr, SubCall):
if isinstance(expr.target, CallTarget):
print("CALLTARGET", expr.target.address_of, expr.target.target) # XXX
target = expr.target.target
if isinstance(target, SymbolName): # 'function(1,2,3)'
funcname = target.name

View File

@ -125,10 +125,10 @@
; 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.
var .word initword0a = &ZP.zpmem1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP)
var .word initword0 = &ZP.zpvar1
var .word initword0a = &ZP.zpmem1
var .word initword0 = &ZP.zpvar1 ; @todo should work, reference this symbols' generated address (@todo generate address for ZP)
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

View File

@ -1,6 +1,6 @@
; floating point tests
%output prg, basic
%output basic
%import c64lib

View File

@ -8,7 +8,7 @@
%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.
@ -178,7 +178,7 @@ somelabel1:
}
~ fidget $1000
~ fidget $0c00
{
subroutine: