From 921955f3214b4fe9e1cd5c8df9d44c9d212ee0bf Mon Sep 17 00:00:00 2001 From: Sei Lisa Date: Fri, 27 Mar 2015 00:26:56 +0100 Subject: [PATCH] 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. --- lslopt/lslparse.py | 51 ++++++++++++++++++++++++++++++++-------------- main.py | 3 +++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/lslopt/lslparse.py b/lslopt/lslparse.py index fa2053d..3137f81 100644 --- a/lslopt/lslparse.py +++ b/lslopt/lslparse.py @@ -271,7 +271,7 @@ class parser(object): try: return self.symtab[0][symbol] # Quick guess 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: return None # Disallow forwards in global var mode 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 optional_type: LAMBDA | TYPE """ + assert self.scopeindex == 0 while self.tok[0] in ('TYPE','IDENT'): typ = None if self.tok[0] == 'TYPE': @@ -1973,9 +1974,26 @@ list lazy_list_set(list L, integer i, list v) self.expect('IDENT') name = self.tok[1] - if name in self.symtab[self.scopeindex]: - raise EParseAlreadyDefined(self) 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 ('=', ';'): # This is a variable definition @@ -2134,7 +2152,7 @@ list lazy_list_set(list L, integer i, list v) states: state [state [...]] 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 # 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. 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: # This is a list of all local and global symbol tables. # 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, # or 'e' for event. # 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'. # States only have 'Loc'. # Events have 'ParamTypes' and 'ParamNames'. # 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 # 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]. del self.globals - # Insert library functions into symbol table - self.symtab[0].update(self.functions) - treesymtab = self.tree, self.symtab del self.tree del self.symtab @@ -2387,7 +2408,7 @@ list lazy_list_set(list L, integer i, list v) self.events = {} self.constants = {} - self.functions = {} + self.funclibrary = {} # 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 # they are implemented in lslfuncs.*, they get a # 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) 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: - self.functions[name]['Fn'] = fn + self.funclibrary[name]['Fn'] = fn elif match.group(4): # constant name = match.group(5) @@ -2544,7 +2565,7 @@ list lazy_list_set(list L, integer i, list v) if line == '': break line = line.strip() - if line and line[0] != '#' and line in self.functions: - self.functions[line]['SEF'] = True + if line and line[0] != '#' and line in self.funclibrary: + self.funclibrary[line]['SEF'] = True finally: f.close() diff --git a/main.py b/main.py index b364890..d3a6fdf 100755 --- a/main.py +++ b/main.py @@ -234,6 +234,9 @@ Optimizer options (+ means active by default, - means inactive by default): Like lazylists, it's implemented for compatibility with Firestorm, but not recommended. Note that the operand to 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