Add "lazy lists" assignment support (mylist[index] = value).

Add support for LAMBDA (empty) tree nodes while on it, that allow us to define private stuff at the top without caring about Loc.
This commit is contained in:
Sei Lisa 2015-03-03 17:59:51 +01:00
parent 7e521780d6
commit b73805e0ce
4 changed files with 75 additions and 19 deletions

View file

@ -1015,9 +1015,10 @@ class foldconst(object):
node['SEF'] = True node['SEF'] = True
return return
if nt in ('JUMP', '@', 'V++', 'V--', '--V', '++V'): if nt in ('JUMP', '@', 'V++', 'V--', '--V', '++V', 'LAMBDA'):
# These all have side effects, as in, can't be eliminated as # Except LAMBDA, these all have side effects, as in, can't be
# statements. # eliminated as statements.
# LAMBDA can't be eliminated without scrolling Loc's.
return return
assert False, 'Internal error: This should not happen, node type = ' \ assert False, 'Internal error: This should not happen, node type = ' \

View file

@ -366,6 +366,9 @@ class outscript(object):
if nt == 'EXPR': if nt == 'EXPR':
return self.dent() + self.OutExpr(child[0]) + ';\n' return self.dent() + self.OutExpr(child[0]) + ';\n'
if nt == 'LAMBDA':
return ''
assert False, "Internal error: node type not handled: " + nt # pragma: no cover assert False, "Internal error: node type not handled: " + nt # pragma: no cover
def output(self, treesymtab, options = ('optsigns','optfloats')): def output(self, treesymtab, options = ('optsigns','optfloats')):

View file

@ -544,12 +544,14 @@ class parser(object):
rotation_literal: '<' expression ',' expression ',' expression rotation_literal: '<' expression ',' expression ',' expression
',' expression '>' ',' expression '>'
list_literal: '[' optional_expression_list ']' list_literal: '[' optional_expression_list ']'
assignment: lvalue '=' expression | lvalue '+=' expression assignment: xlvalue '=' expression | lvalue '+=' expression
| lvalue '-=' expression | lvalue '*=' expression | lvalue '-=' expression | lvalue '*=' expression
| lvalue '/=' expression | lvalue '%=' expression | lvalue '/=' expression | lvalue '%=' expression
%EXTENDED RULES: | lvalue '|=' expression %if extendedassignment
| lvalue '|=' expression | lvalue '&=' expression | lvalue '&=' expression %if extendedassignment
| lvalue '<<=' expression | lvalue '>>=' expression | lvalue '<<=' expression %if extendedassignment
| lvalue '>>=' expression %if extendedassignment
xlvalue: lvalue | IDENT '[' expression ']' %if lazylists
lvalue: IDENT | IDENT '.' IDENT lvalue: IDENT | IDENT '.' IDENT
""" """
tok0 = self.tok[0] tok0 = self.tok[0]
@ -677,7 +679,21 @@ class parser(object):
raise EParseTypeMismatch(self) raise EParseTypeMismatch(self)
return {'nt':'V++' if tok0 == '++' else 'V--', 't':lvalue['t'], 'ch':[lvalue]} return {'nt':'V++' if tok0 == '++' else 'V--', 't':lvalue['t'], 'ch':[lvalue]}
if AllowAssignment and (tok0 in self.assignment_toks if AllowAssignment and (tok0 in self.assignment_toks
or self.extendedassignment and tok0 in self.extassignment_toks): or self.extendedassignment and tok0 in self.extassignment_toks
or self.lazylists and tok0 == '['):
if tok0 == '[':
if lvalue['nt'] != 'IDENT':
raise EParseSyntax(self)
if lvalue['t'] != 'list':
raise EParseTypeMismatch(self)
self.NextToken()
idxexpr = self.Parse_expression()
if idxexpr['t'] != 'integer':
raise EParseTypeMismatch(self)
self.expect(']')
self.NextToken()
self.expect('=')
self.NextToken() self.NextToken()
expr = self.Parse_expression() expr = self.Parse_expression()
rtyp = expr['t'] rtyp = expr['t']
@ -690,6 +706,37 @@ class parser(object):
if tok0 != '*=' or typ == 'float': if tok0 != '*=' or typ == 'float':
expr = self.autocastcheck(expr, typ) expr = self.autocastcheck(expr, typ)
rtyp = typ rtyp = typ
# Lazy list handler
if tok0 == '[':
# Define aux function if it doesn't exist
# (leaves users room for writing their own replacement, e.g.
# one that fills with something other than zeros)
if 'lazy_list_set' not in self.symtab[0]:
self.PushScope()
paramscope = self.scopeindex
params = (['list', 'integer', 'list'],
['L', 'i', 'v'])
self.AddSymbol('f', 0, 'lazy_list_set', Loc=self.usedspots,
Type='list', ParamTypes=params[0], ParamNames=params[1])
self.AddSymbol('v', paramscope, 'L', Type='list')
self.AddSymbol('v', paramscope, 'i', Type='integer')
self.AddSymbol('v', paramscope, 'v', Type='list')
self.PushScope()
localscope = self.scopeindex
self.AddSymbol('v', localscope, 'ins', Type='integer',
Local=True)
# Add body (apologies for the wall of text)
self.tree[self.usedspots] = {'ch': [{'ch': [{'scope':localscope, 'ch': [{'ch': [{'scope':paramscope, 'nt': 'IDENT', 't': 'list', 'name': 'L'}, {'scope':paramscope, 'nt': 'IDENT', 't': 'list', 'name': 'v'}], 'nt': '!=', 't': 'integer'}], 'nt': 'DECL', 't': 'integer', 'name': 'ins'}, {'ch': [{'ch': [{'ch': [{'scope':localscope, 'nt': 'IDENT', 't': 'integer', 'name': 'ins'}], 'nt': '++V', 't': 'integer'}, {'scope':paramscope, 'nt': 'IDENT', 't': 'integer', 'name': 'i'}], 'nt': '<', 't': 'integer'}, {'ch': [{'ch': [{'scope':paramscope, 'nt': 'IDENT', 't': 'list', 'name': 'L'}, {'ch': [{'scope':paramscope, 'nt': 'IDENT', 't': 'list', 'name': 'L'}, {'ch': [{'scope':localscope, 'nt': 'IDENT', 't': 'integer', 'name': 'ins'}, {'scope':localscope, 'nt': 'IDENT', 't': 'integer', 'name': 'ins'}], 'nt': '^', 't': 'integer'}], 'nt': '+', 't': 'list'}], 'nt': '=', 't': 'list'}], 'nt': 'EXPR', 't': 'list'}], 'nt': 'WHILE', 't': None}, {'ch': [{'ch': [{'scope':paramscope, 'nt': 'IDENT', 't': 'list', 'name': 'L'}, {'scope':paramscope, 'nt': 'IDENT', 't': 'list', 'name': 'v'}, {'ch': [{'scope':localscope, 'nt': 'IDENT', 't': 'integer', 'name': 'ins'}, {'scope':paramscope, 'nt': 'IDENT', 't': 'integer', 'name': 'i'}], 'nt': '=', 't': 'integer'}, {'scope':localscope, 'nt': 'IDENT', 't': 'integer', 'name': 'ins'}], 'nt': 'FNCALL', 't': 'list', 'name': 'llListReplaceList'}], 'nt': 'RETURN', 't': None, 'LIR': True}], 'nt': '{}', 't': None, 'LIR': True}], 't': 'list', 'pnames': ['L', 'i', 'v'], 'scope': 0, 'pscope': 1, 'nt': 'FNDEF', 'ptypes': ['list', 'integer', 'list'], 'name': 'lazy_list_set'}
self.usedspots += 1
self.PopScope()
self.PopScope()
return {'nt':'=', 't':'list', 'ch':[lvalue, {
'nt':'FNCALL', 't':'list', 'name':'lazy_list_set',
'ch':[lvalue.copy(), idxexpr,
{'nt':'LIST','t':'list', 'ch':[expr]}]
}]}
# Lots of drama for checking types. This is pretty much like # Lots of drama for checking types. This is pretty much like
# addition, subtraction, multiply, divide, etc. all in one go. # addition, subtraction, multiply, divide, etc. all in one go.
if tok0 == '=': if tok0 == '=':
@ -1825,8 +1872,8 @@ class parser(object):
# TODO: Enable switch statements. # TODO: Enable switch statements.
#self.enableswitch = 'enableswitch' in options #self.enableswitch = 'enableswitch' in options
# TODO: Enable brackets for list elements e.g. (float)mylist[3], or mylist[5]=4 # Allow brackets for assignment of list elements e.g. mylist[5]=4
#self.lazylists = 'lazylists' in options self.lazylists = 'lazylists' in options
# TODO: Enable break/continue # TODO: Enable break/continue
#self.breakcont = 'breakcont' in options #self.breakcont = 'breakcont' in options
@ -1889,7 +1936,9 @@ class parser(object):
self.pos = 0 self.pos = 0
self.tok = self.GetToken() self.tok = self.GetToken()
self.tree = [] # Reserve spots at the beginning for functions we add
self.tree = [{'nt':'LAMBDA','t':None}]
self.usedspots = 0
# Start the parsing proper # Start the parsing proper
self.Parse_script() self.Parse_script()

19
main.py
View file

@ -42,14 +42,14 @@ Options (+ means active by default, - means inactive by default):
directives like: # 123 "filename". directives like: # 123 "filename".
optimize + Runs the optimizer. optimize + Runs the optimizer.
optsigns + Optimize signs in float and integer constants. optsigns + Optimize signs in float and integer constants.
optfloats + Optimize a float when it is an integral value. optfloats + Optimize floats that represent an integral value.
constfold + Fold constant expressions to their values. constfold + Fold constant expressions to their values, and simplify
foldtabs - Tabs can't be copy-pasted, so they aren't optimized by some expressions.
default. But with support from the viewer, they can be foldtabs - Tabs can't be copy-pasted, so expressions that produce
folded too and make it to the uploaded source. This tabs (like llUnescapeURL("%09") aren't optimized by
option overrides that check, enabling optimization of default. This option overrides that check, enabling
strings with tabs. The resulting source isn't guaranteed optimization of strings with tabs. The resulting source
to be copy-paste-able to a different script, though. isn't guaranteed to be copy-paste-able to the viewer.
duplabels - Normally, a duplicate label within a function is allowed duplabels - Normally, a duplicate label within a function is allowed
by the syntax by using {} blocks; however, the server by the syntax by using {} blocks; however, the server
will just refuse to save the script (under Mono) or do will just refuse to save the script (under Mono) or do
@ -61,6 +61,9 @@ Options (+ means active by default, - means inactive by default):
process, it turns the script into unreadable gibberish, process, it turns the script into unreadable gibberish,
hard to debug, but this gets big savings for complex hard to debug, but this gets big savings for complex
scripts. scripts.
lazylists - Support syntax like mylist[index] = 5; rather than using
llListReplaceList. Not recommended, as it adds a new
function.
''' % sys.argv[0]) ''' % sys.argv[0])
return 1 return 1