Implement function overriding syntax extension, fixing a bug on the way.

The funcoverride option allows defining multiple functions with the same name, each overriding the former. That's for compatibility with Firestorm, whose optimizer does that.

While on it, fix a bug where defining a function whose name matches a library function was not reporting an error, and rename self.functions to self.funclibrary for clarity. It also brings consistency with other parts of the code and with the code documentation.
This commit is contained in:
Sei Lisa 2015-03-27 00:26:56 +01:00
parent 3839863a21
commit 921955f321
2 changed files with 39 additions and 15 deletions

View file

@ -271,7 +271,7 @@ class parser(object):
try: try:
return self.symtab[0][symbol] # Quick guess return self.symtab[0][symbol] # Quick guess
except KeyError: except KeyError:
if self.globalmode and symbol not in self.symtab[0] and symbol not in self.functions \ if self.globalmode 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]
@ -1965,6 +1965,7 @@ list lazy_list_set(list L, integer i, list v)
func_def: optional_type IDENT '(' optional_param_list ')' code_block func_def: optional_type IDENT '(' optional_param_list ')' code_block
optional_type: LAMBDA | TYPE optional_type: LAMBDA | TYPE
""" """
assert self.scopeindex == 0
while self.tok[0] in ('TYPE','IDENT'): while self.tok[0] in ('TYPE','IDENT'):
typ = None typ = None
if self.tok[0] == 'TYPE': if self.tok[0] == 'TYPE':
@ -1973,9 +1974,26 @@ list lazy_list_set(list L, integer i, list v)
self.expect('IDENT') self.expect('IDENT')
name = self.tok[1] name = self.tok[1]
if name in self.symtab[self.scopeindex]:
raise EParseAlreadyDefined(self)
self.NextToken() self.NextToken()
if name in self.symtab[0]:
# Duplicate identifier. That's an exception unless function
# override is in effect.
report = True
if self.funcoverride:
# Is it a function definition, and is the entry in the
# symbol table a function definition itself? And is it
# a user-defined function?
if self.tok[0] == '(' \
and self.symtab[0][name]['Kind'] == 'f' \
and 'Loc' in self.symtab[0][name]:
# Override it.
report = False
# Delete the previous definition.
self.tree[self.symtab[0][name]['Loc']] = \
{'nt':'LAMBDA', 't':None}
del self.symtab[0][name]
if report:
raise EParseAlreadyDefined(self)
if self.tok[0] in ('=', ';'): if self.tok[0] in ('=', ';'):
# This is a variable definition # This is a variable definition
@ -2134,7 +2152,7 @@ list lazy_list_set(list L, integer i, list v)
states: state [state [...]] states: state [state [...]]
state: (DEFAULT | STATE IDENT) balanced_braces_or_anything_else state: (DEFAULT | STATE IDENT) balanced_braces_or_anything_else
""" """
ret = self.functions.copy() # The library functions go here too. ret = self.funclibrary.copy() # The library functions go here too.
# If there's a syntax error, that's not our business. We just return # If there's a syntax error, that's not our business. We just return
# what we have so far. Doing a proper parse will determine the exact # what we have so far. Doing a proper parse will determine the exact
@ -2301,6 +2319,10 @@ list lazy_list_set(list L, integer i, list v)
# Shrink names. Activates duplabels automatically. # Shrink names. Activates duplabels automatically.
self.shrinknames = 'shrinknames' in options self.shrinknames = 'shrinknames' in options
# Allow a duplicate function definition to override the former,
# rather than reporting a duplicate identifier error.
self.funcoverride = 'funcoverride' in options
# 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
@ -2309,13 +2331,15 @@ list lazy_list_set(list L, integer i, list v)
# '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. # or 'e' for event.
# Variables have 'Scope', 'Type', 'Loc' (if global), 'Local' (if local). # Variables have 'Scope', 'Type', 'Loc' (if global), 'Local' (if local).
# Functions have 'Type' and 'ParamTypes'; UDFs also have 'Loc' and'ParamNames'. # Functions have 'Type' and 'ParamTypes'; UDFs also have 'Loc' and 'ParamNames'.
# Labels only have 'Scope'. # Labels only have 'Scope'.
# States only have 'Loc'. # States only have 'Loc'.
# Events have 'ParamTypes' and 'ParamNames'. # Events have 'ParamTypes' and 'ParamNames'.
# Other modules may add information if they need. # Other modules may add information if they need.
self.symtab = [{-1: None},] # Incorporate the library into the initial symbol table.
self.symtab = [self.funclibrary.copy()]
self.symtab[0][-1] = None
self.scopeindex = 0 self.scopeindex = 0
# This is a small hack to prevent circular definitions in globals when # This is a small hack to prevent circular definitions in globals when
@ -2363,9 +2387,6 @@ 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
# Insert library functions into symbol table
self.symtab[0].update(self.functions)
treesymtab = self.tree, self.symtab treesymtab = self.tree, self.symtab
del self.tree del self.tree
del self.symtab del self.symtab
@ -2387,7 +2408,7 @@ list lazy_list_set(list L, integer i, list v)
self.events = {} self.events = {}
self.constants = {} self.constants = {}
self.functions = {} self.funclibrary = {}
# Library read code # Library read code
@ -2449,12 +2470,12 @@ list lazy_list_set(list L, integer i, list v)
# Library functions go to the functions table. If # Library functions go to the functions table. If
# they are implemented in lslfuncs.*, they get a # they are implemented in lslfuncs.*, they get a
# reference to the implementation; otherwise None. # reference to the implementation; otherwise None.
if name in self.functions: if name in self.funclibrary:
warning('Function already defined in bultins.txt, overwriting: ' + name) warning('Function already defined in bultins.txt, overwriting: ' + name)
fn = getattr(lslfuncs, name, None) fn = getattr(lslfuncs, name, None)
self.functions[name] = {'Kind':'f', 'Type':typ, 'ParamTypes':args} self.funclibrary[name] = {'Kind':'f', 'Type':typ, 'ParamTypes':args}
if fn is not None: if fn is not None:
self.functions[name]['Fn'] = fn self.funclibrary[name]['Fn'] = fn
elif match.group(4): elif match.group(4):
# constant # constant
name = match.group(5) name = match.group(5)
@ -2544,7 +2565,7 @@ list lazy_list_set(list L, integer i, list v)
if line == '': if line == '':
break break
line = line.strip() line = line.strip()
if line and line[0] != '#' and line in self.functions: if line and line[0] != '#' and line in self.funclibrary:
self.functions[line]['SEF'] = True self.funclibrary[line]['SEF'] = True
finally: finally:
f.close() f.close()

View file

@ -234,6 +234,9 @@ Optimizer options (+ means active by default, - means inactive by default):
Like lazylists, it's implemented for compatibility with Like lazylists, it's implemented for compatibility with
Firestorm, but not recommended. Note that the operand to Firestorm, but not recommended. Note that the operand to
switch() may be evaluated more than once. switch() may be evaluated more than once.
funcoverride - Allow duplicate function definitions to override the
previous definition. For compatibility with Firestorm's
optimizer.
Optimization options Optimization options