mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-01 23:58:20 +00:00
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:
parent
7e521780d6
commit
b73805e0ce
4 changed files with 75 additions and 19 deletions
|
@ -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 = ' \
|
||||||
|
|
|
@ -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')):
|
||||||
|
|
|
@ -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
19
main.py
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue