Add preprocessor skip option, command line options and help, options for all functions, and enhance parentheses removal.

This commit is contained in:
Sei Lisa 2014-07-28 02:13:08 +02:00
parent ae94e80c34
commit eab0bec84b
4 changed files with 99 additions and 27 deletions

View file

@ -31,6 +31,7 @@ class optimizer(object):
"""If the statement is a constant or an identifier, remove it as it does """If the statement is a constant or an identifier, remove it as it does
nothing. nothing.
""" """
# Ideally this should consider side effect analysis of the whole thing.
if code[0] in (CONSTANT, 'IDENT', 'FIELD'): if code[0] in (CONSTANT, 'IDENT', 'FIELD'):
code[:] = [S[';'], None] code[:] = [S[';'], None]
else: else:
@ -86,7 +87,10 @@ class optimizer(object):
if code0 == '()': if code0 == '()':
self.FoldTree(code[2]) self.FoldTree(code[2])
if code[2][0] == CONSTANT: if code[2][0] in (CONSTANT, 'VECTOR', 'ROTATION', 'LIST',
'IDENT', 'FIELD', 'V++', 'V--', 'FUNCTION', 'PRINT'):
# Child is an unary postfix expression; parentheses can be
# removed safely.
code[:] = code[2] code[:] = code[2]
return return
@ -154,8 +158,11 @@ class optimizer(object):
code[:] = [S['*'], code[1], code[2], [CONSTANT, 'integer', 1<<(code[3][2] & 31)]] code[:] = [S['*'], code[1], code[2], [CONSTANT, 'integer', 1<<(code[3][2] & 31)]]
else: else:
pass # TODO: Eliminate redundancy (x+0, x*1, x*-1, v+ZERO_VECTOR, perhaps x-1=~-x, etc.) pass # TODO: Eliminate redundancy (x+0, x*1, x*-1, v+ZERO_VECTOR, perhaps x-1=~-x, etc.)
# Include != to ^ and || to | and maybe && to &
# Note some cases e.g. x*0 can't be optimized away without side-effect analysis. # Note some cases e.g. x*0 can't be optimized away without side-effect analysis.
# But some cases like %1 can be turned into *0 to save bytes. # But some cases like %1 can be turned into *0 to save bytes.
# Turn also % (power of 2) into & mask (oops, nope, negative doesn't work)
# Maybe turn != -1 into ~ in if()'s.
return return
if code0 in self.assign_ops: if code0 in self.assign_ops:
@ -345,10 +352,16 @@ class optimizer(object):
raise Exception('Internal error: This should not happen, node = ' + code0) # pragma: no cover raise Exception('Internal error: This should not happen, node = ' + code0) # pragma: no cover
def optimize(self, symtab, functions): def optimize(self, symtab, functions, options = ('optimize',)):
"""Optimize the symbolic table symtab in place. Requires a table of """Optimize the symbolic table symtab in place. Requires a table of
predefined functions for folding constants. predefined functions for folding constants.
""" """
if 'optimize' not in options:
return
# TODO: Add option to handle local jumps properly.
self.functions = functions self.functions = functions
self.symtab = symtab self.symtab = symtab

View file

@ -262,12 +262,13 @@ class outscript(object):
first = False first = False
return ret + ')\n' + self.OutCode(code) return ret + ')\n' + self.OutCode(code)
def output(self, symtab): def output(self, symtab, options = ('optimizesigns',)):
# Build a sorted list of dict entries # Build a sorted list of dict entries
order = [] order = []
self.symtab = symtab self.symtab = symtab
self.optsigns = True # Optimize signs
self.optsigns = 'optimizesigns' in options
for i in symtab: for i in symtab:
item = [] item = []
@ -284,9 +285,6 @@ class outscript(object):
for name in order[0]: for name in order[0]:
sym = symtab[0][name] sym = symtab[0][name]
#DEBUG
#print name, repr(sym)
ret += self.dent() ret += self.dent()
if sym[1] == 'State': if sym[1] == 'State':
if name == 'default': if name == 'default':

View file

@ -249,6 +249,19 @@ class parser(object):
self.pos += 1 self.pos += 1
# Process comments # Process comments
if c == '#' and self.skippreproc:
# Preprocessor directives act like single line comments.
# Most are not supposed to reach us but cpp also generates
# as output lines like: # 123 "file.lsl"
self.ceof()
while self.script[self.pos] != '\n':
self.pos += 1
self.ceof() # A single-line comment at EOF is not unexpected EOF.
self.pos += 1
self.ceof()
continue
if c == '/': if c == '/':
if self.script[self.pos:self.pos+1] == '/': if self.script[self.pos:self.pos+1] == '/':
self.pos += 1 self.pos += 1
@ -1666,7 +1679,7 @@ class parser(object):
self.NextToken() self.NextToken()
def parse(self, script, options = frozenset()): def parse(self, script, options = ()):
"""Parse the given stream with the given options. """Parse the given stream with the given options.
This function also builds the temporary globals table. This function also builds the temporary globals table.
@ -1693,7 +1706,8 @@ class parser(object):
# Allow C style string composition of strings: "blah" "blah" = "blahblah" # Allow C style string composition of strings: "blah" "blah" = "blahblah"
self.allowmultistrings = 'allowmultistrings' in options self.allowmultistrings = 'allowmultistrings' in options
# TODO: Add option to skip preprocessor directives (specifically #line). # Skip preprocessor directives (specifically #line).
self.skippreproc = 'skippreproc' in options
# TODO: Allow pure C-style string parsing. This is low-priority. # TODO: Allow pure C-style string parsing. This is low-priority.
#self.allowcescapes = 'allowcescapes' in options #self.allowcescapes = 'allowcescapes' in options

83
main.py
View file

@ -6,27 +6,74 @@ from lslopt.lsloptimizer import optimizer
import sys import sys
def main(): def main():
if len(sys.argv) > 1: if len(sys.argv) < 2:
p = parser() sys.stderr.write(r'''LSL optimizer v0.1
try:
p.parsefile(sys.argv[1]) Usage: %s [-o option[,option[,...]]] filename
funcs = p.functions
symtab = p.symtab Options (* means not implemented):
except EParse as e: extendedglobalexpr Enables arbitrary expressions in globals (as opposed to
print e.message dull simple expressions allowed by regular LSL). Needs
the optimizer to run for the result to be compilable.
extendedtypecast Allows extended typecast syntax e.g. (string)(integer)a
is valid with this option.
extendedassignment Enables &=, |=, ^=, <<=, >>= assignment operators.
explicitcast Add explicit casts where they are implicit. This option
is useless with 'optimize' and 'optsigns', and is of
basically no use in general.
allowkeyconcat Allow string + key and key + string (both return string)
allowmultistrings Allow C-like string juxtaposition, e.g. "ab" "cd" means
"abcd", no concatenation involved. Very useful when used
with a preprocessor (although the optimizer would
optimize concatenated strings if they are parenthesized
correctly, see note at the footer).
skippreproc Skip preprocessor directives in the source as if they
were comments. Not useful unless the script is output
by cpp, which inserts directives like: # 123 "filename"
optimize Runs the optimizer.
optsigns Optimize signs and float as int.
* allowcescapes Enables use of \r, \b, \xNN, \NNN, etc.
* enableswitch Enables Firestorm-compatible switch statements
(not recommended)
* lazylists Enables Firestorm-compatible list syntax, e.g.
mylist[3] = 5; v = (float)mylist[2]; (needs to know the
type to work). Works better with extendedtypecast.
Note that the optimizer doesn't reorder expressions to fold constants. This
means that e.g. a + 3 + 5 is not optimized to a + 8; however a + (3 + 5) is.
''' % sys.argv[0])
return 1
if sys.argv[1] == '-o':
if len(sys.argv) < 4:
sys.stderr.write('Command line: Not enough parameters\n')
return 1 return 1
del p options = sys.argv[2].split(',')
fname = sys.argv[3]
else:
options = ()
fname = sys.argv[1]
opt = optimizer() p = parser()
opt.optimize(symtab, funcs) try:
del opt p.parsefile(fname, options)
funcs = p.functions
symtab = p.symtab
except EParse as e:
print e.message
return 1
del p
outs = outscript() opt = optimizer()
script = outs.output(symtab) opt.optimize(symtab, funcs, options)
del outs del opt
del symtab
sys.stdout.write(script) outs = outscript()
return 0 script = outs.output(symtab, options)
del outs
del symtab
sys.stdout.write(script)
return 0
ret = main() ret = main()
if ret: if ret: