Remove the symbol table's parent pointer

Instead of a tree of symbol tables, we keep a running stack of active symbol tables while parsing. The only case in which this causes problems is forward reference resolution for jump labels, which is solved by storing a copy of the stack at the point the jump was found.
This commit is contained in:
Sei Lisa 2018-12-29 17:55:07 +01:00
parent 76f483fc11
commit 660dcff65b

View file

@ -244,17 +244,20 @@ class parser(object):
def PushScope(self): def PushScope(self):
"""Create a new symbol table / scope level""" """Create a new symbol table / scope level"""
self.symtab.append({-1:self.scopeindex}) # Add parent pointer self.scopeindex = len(self.symtab)
self.scopeindex = len(self.symtab)-1 self.symtab.append({}) # Add new symbol table
self.scopestack.append(self.scopeindex)
def PopScope(self): def PopScope(self):
"""Return to the previous scope level""" """Return to the previous scope level"""
self.scopeindex = self.symtab[self.scopeindex][-1] # -1 is a dict key, not an index assert self.scopeindex == self.scopestack[-1]
assert self.scopeindex is not None, 'Unexpected internal error' self.scopestack.pop()
self.scopeindex = self.scopestack[-1]
assert len(self.scopestack) > 0
def AddSymbol(self, kind, scope, name, **values): def AddSymbol(self, kind, scope, name, **values):
values['Kind'] = kind values['Kind'] = kind
if kind in 'vl': if kind in ('v', 'l'):
values['Scope'] = scope values['Scope'] = scope
self.symtab[scope][name] = values self.symtab[scope][name] = values
@ -278,33 +281,33 @@ class parser(object):
gives an Name Not Defined error. gives an Name Not Defined error.
""" """
scopelevel = self.scopeindex scopelevel = len(self.scopestack)
while scopelevel is not None: while scopelevel:
symtab = self.symtab[scopelevel] scopelevel -= 1
if symbol in symtab and (not MustBeLabel or symtab[symbol]['Kind'] == 'l'): symtab = self.symtab[self.scopestack[scopelevel]]
if symbol in symtab and (not MustBeLabel
or symtab[symbol]['Kind'] == 'l'):
return symtab[symbol] return symtab[symbol]
scopelevel = symtab[-1] # -1 is a dict key, not an index
return None return None
# No labels or states allowed here (but functions are) # No labels or states allowed here (but functions are)
def FindSymbolFull(self, symbol, scopelevel = None): def FindSymbolFull(self, symbol, globalonly=False):
"""Returns the symbol table entry for the given symbol.""" """Returns the symbol table entry for the given symbol."""
if scopelevel is None: scopelevel = 1 if globalonly else len(self.scopestack)
# unspecified scope level means to look in the current scope while scopelevel: # Loop over all scopes in the stack
scopelevel = self.scopeindex scopelevel -= 1
while scopelevel: # Loop over all local scopes symtab = self.symtab[self.scopestack[scopelevel]]
symtab = self.symtab[scopelevel]
if symbol in symtab: if symbol in symtab:
# This can't happen, as functions can't be local # This can't happen, as functions can't be local
#if len(symtab[symbol]) > 3: #if len(symtab[symbol]) > 3:
# return (symtab[symbol][1], symtab[symbol][3]) # return (symtab[symbol][1], symtab[symbol][3])
return symtab[symbol] return symtab[symbol]
scopelevel = symtab[-1]
try: try:
return self.symtab[0][symbol] # Quick guess return self.symtab[0][symbol] # Quick guess
except KeyError: except KeyError:
if self.disallowglobalvars and symbol not in self.symtab[0] \ if (self.disallowglobalvars and symbol not in self.symtab[0]
or symbol not in self.globals: or symbol not in self.globals
):
return None # Disallow forwards in global var mode return None # Disallow forwards in global var mode
return self.globals[symbol] return self.globals[symbol]
@ -947,7 +950,7 @@ class parser(object):
self.NextToken() self.NextToken()
# Functions are looked up in the global scope only. # Functions are looked up in the global scope only.
sym = self.FindSymbolFull(val, 0) sym = self.FindSymbolFull(val, globalonly=True)
if sym is None: if sym is None:
self.errorpos = savepos self.errorpos = savepos
raise EParseUndefined(self) raise EParseUndefined(self)
@ -1325,7 +1328,7 @@ list lazy_list_set(list L, integer i, list v)
if typ not in self.TypeToExtractionFunction: if typ not in self.TypeToExtractionFunction:
raise EParseNoConversion(self) raise EParseNoConversion(self)
fn = self.TypeToExtractionFunction[typ] fn = self.TypeToExtractionFunction[typ]
sym = self.FindSymbolFull(fn, 0) sym = self.FindSymbolFull(fn, globalonly=True)
assert sym is not None assert sym is not None
fnparamtypes = sym['ParamTypes'] fnparamtypes = sym['ParamTypes']
subparamtypes = [x.t for x in expr.ch] subparamtypes = [x.t for x in expr.ch]
@ -1747,8 +1750,8 @@ list lazy_list_set(list L, integer i, list v)
if not sym or sym['Kind'] != 'l': if not sym or sym['Kind'] != 'l':
# It might still be a forward reference, so we add it to the # It might still be a forward reference, so we add it to the
# list of things to look up when done # list of things to look up when done
self.jump_lookups.append((name, self.scopeindex, self.errorpos, self.jump_lookups.append((name, self.scopestack[:],
jumpnode)) self.errorpos, jumpnode))
else: else:
jumpnode.scope = sym['Scope'] jumpnode.scope = sym['Scope']
sym['ref'] += 1 sym['ref'] += 1
@ -2568,9 +2571,12 @@ list lazy_list_set(list L, integer i, list v)
self.Parse_states() self.Parse_states()
self.expect('EOF') self.expect('EOF')
assert len(self.scopestack) == 1 and self.scopestack[0] == 0
# Check the pending jump targets to assign them the scope of the label. # Check the pending jump targets to assign them the scope of the label.
for tgt in self.jump_lookups: for tgt in self.jump_lookups:
self.scopeindex = tgt[1] self.scopestack = tgt[1]
self.scopeindex = self.scopestack[-1]
sym = self.FindSymbolPartial(tgt[0], MustBeLabel = True) sym = self.FindSymbolPartial(tgt[0], MustBeLabel = True)
if sym is None: if sym is None:
self.errorpos = tgt[2] self.errorpos = tgt[2]
@ -2579,6 +2585,7 @@ list lazy_list_set(list L, integer i, list v)
sym['ref'] += 1 sym['ref'] += 1
del self.jump_lookups # Finished with it. del self.jump_lookups # Finished with it.
self.scopestack = [0]
def Parse_single_expression(self): def Parse_single_expression(self):
"""Parse the script as an expression, Used by lslcalc. """Parse the script as an expression, Used by lslcalc.
@ -2825,13 +2832,14 @@ list lazy_list_set(list L, integer i, list v)
# Symbol table: # Symbol table:
# This is a list of all local and global symbol tables. # This is a list of all local and global symbol tables.
# The first element (0) is the global scope. Each symbol table is a # The first element (0) is the global scope. Each symbol table is a
# dictionary. Element -1 of the dictionary is the parent index. The # dictionary of symbols, whose elements are in turn dictionaries of
# rest of entries are dictionaries. Each has a 'Kind', which can be # attributes. Each has a 'Kind', which can be:
# 'v' for variable, 'f' for function, 'l' for label, 's' for state, # 'v' for variable, 'f' for function, 'l' for label, 's' for state,
# or 'e' for event. Some have a 'Loc' indicating the location (index) # or 'e' for event. Some have a 'Loc' indicating the location (index)
# of the definition in the tree root. # of the definition in the tree root.
# Variables have 'Scope' and 'Type' (a string). # Variables have 'Scope' and 'Type' (a string).
# Global variables also have 'Loc'. # Global variables also have 'Loc'.
# Variables that are parameters also have 'Param'.
# Functions have 'Type' (return type, a string) and 'ParamTypes' (a list of strings). # Functions have 'Type' (return type, a string) and 'ParamTypes' (a list of strings).
# User-defined functions also have 'Loc' and 'ParamNames' (a list of strings). # User-defined functions also have 'Loc' and 'ParamNames' (a list of strings).
# Labels only have 'Scope'. # Labels only have 'Scope'.
@ -2841,9 +2849,13 @@ list lazy_list_set(list L, integer i, list v)
# Incorporate the library into the initial symbol table. # Incorporate the library into the initial symbol table.
self.symtab = [self.funclibrary.copy()] self.symtab = [self.funclibrary.copy()]
self.symtab[0][-1] = None
# Current scope index
self.scopeindex = 0 self.scopeindex = 0
# Stack of scopes in which to look for a symbol as we parse
self.scopestack = [0]
if self.prettify: if self.prettify:
# Add the constants as symbol table variables... # Add the constants as symbol table variables...
for i in self.constants: for i in self.constants:
@ -2915,6 +2927,7 @@ list lazy_list_set(list L, integer i, list v)
# No longer needed. The data is already in self.symtab[0]. # No longer needed. The data is already in self.symtab[0].
del self.globals del self.globals
del self.scopestack
treesymtab = self.tree, self.symtab treesymtab = self.tree, self.symtab
del self.tree del self.tree