def test_regression_parser(self): """Test the error cases. There are too many to make a test of each.""" sys.stderr.write('\nRunning parser error tests: ') parser = lslparse.parser(lslloadlib.LoadLibrary()) self.assertRaises(lslparse.EParseSyntax, parser.parse, 'f(){integer i;i>>=i;}') self.assertRaises(lslparse.EParseCantChangeState, parser.parse, 'f(){if(1)state default;else;}default{timer(){}}') self.assertRaises(lslparse.EParseCantChangeState, parser.parse, 'f(){if(1);else state default;}default{timer(){}}') self.assertRaises( lslparse.EParseCantChangeState, parser.parse, 'f(){if(1)if(1)state default;else;else;}default{timer(){}}') # Test behaviour of void functions self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){<llDie(),0,0>;}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){[<llDie(),0,0>];}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){key a=llDie();}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){key a;a=llDie();}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){do;while(llDie());}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){for(;llDie(););}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){while(llDie());}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){if(llDie());}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){if(llDie());else;}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){[llDie()];}}', ('optimize', )) parser.parse('default{timer(){[llDie()];}}') parser.parse('default{timer(){llDie();}}') parser.parse('default{timer(){(llDie());}}') parser.parse('default{timer(){for(llDie();1;llDie());}}', ('optimize', )) # 'return <void expr>' works in the same situations as state changes self.assertRaises(lslparse.EParseReturnShouldBeEmpty, parser.parse, 'default{timer(){return llDie();}}') self.assertRaises(lslparse.EParseReturnShouldBeEmpty, parser.parse, 'default{timer(){if(1)return llDie();else;}}') self.assertRaises(lslparse.EParseReturnShouldBeEmpty, parser.parse, 'default{timer(){if(1);else return llDie();}}') self.assertRaises(lslparse.EParseReturnShouldBeEmpty, parser.parse, 'default{timer(){return 1;}}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'default{timer(){if(1)return 1;}}')
def test_coverage_parser(self): """Cover the error cases. There are too many to make a test of each.""" parser = lslparse.parser( lslloadlib.LoadLibrary( builtins='unit_tests/builtins-coverage-2.txt', fndata='unit_tests/builtins-coverage-2.txt')) self.assertRaises(lslparse.EParseNoConversion, parser.parse, 'f(){list L;(integer)L[0];}', ('lazylists', )) parser = lslparse.parser(lslloadlib.LoadLibrary()) sys.stderr.write('\nRunning parser exception coverage tests: ') # Parse_unary_postfix_expression self.assertRaises(lslparse.EParseUEOF, parser.parse, u'f(){key x=') self.assertRaises(lslparse.EParseUndefined, parser.parse, 'f(){g();}') self.assertRaises(lslparse.EParseUndefined, parser.parse, 'integer g;f(){g();}') self.assertRaises(lslparse.EParseUndefined, parser.parse, 'f(){f=0;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){integer V; V[1] = 0;}', ('lazylists', )) self.assertRaises(lslparse.EParseFunctionMismatch, parser.parse, 'f(){list V; V[1,1] = 0;}', ('lazylists', )) self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){list V; V[""] = 0;}', ('lazylists', )) self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){list V; V[1] = llDie();}', ('lazylists', )) self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){string s;s++;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){string s;++s;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){string s;s=llDie();}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){string s;s+=(key)"";}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){string s;s-=s;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){string s;s*=2;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){vector v;v%=1.0;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){string s;s>>=s;}', ('extendedassignment', )) # Parse_unary_expression self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){-"";}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){!"";}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){~"";}') self.assertRaises(lslparse.EParseUndefined, parser.parse, 'f(){++f;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){(key)1;}') self.assertRaises(lslparse.EParseFunctionMismatch, parser.parse, 'f(){list L;(integer)L[""];}', ('lazylists', )) # Parse_factor self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){""*2;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){<1,1,1>%2;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){<1,1,1>/<1,1,1>;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){<1,1,1>/"";}') # Parse_term self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){llDie()+1;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){""-1;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){[]+llDie();}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){(key)""+(key)"";}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){""+(key)"";}') # Parse_shift self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){"">>1;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){1<<"";}') # Parse_inequality self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){""<"";}') # Parse_comparison self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){llDie()==3;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){""==3;}') # Parse_bitbool_factor self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){""&3;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){3&"";}') # Parse_bitxor_term self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){""^3;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){3^"";}') # Parse_bitbool_term self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){""|3;}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){3|"";}') # Parse_expression self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){3||"";}') self.assertRaises(lslparse.EParseTypeMismatch, parser.parse, 'f(){""&&3;}') # Parse_optional_expression_list self.assertRaises(lslparse.EParseFunctionMismatch, parser.parse, 'f(){llSay(0);}') self.assertRaises(lslparse.EParseAlreadyDefined, parser.parse, 'f(){@x;@x;}') self.assertRaises(lslparse.EParseAlreadyDefined, parser.parse, 'f(){integer x;integer x;}') self.assertRaises(lslparse.EParseAlreadyDefined, parser.parse, 'f(integer x, integer x){}') self.assertRaises(lslparse.EParseAlreadyDefined, parser.parse, 'default{timer(){}timer(){}}') self.assertRaises(lslparse.EParseSyntax, parser.parse, 'default{timer(){state state;}}') self.assertRaises(lslparse.EParseUndefined, parser.parse, 'default{timer(){state undefined;}}') self.assertRaises(lslparse.EParseSyntax, parser.parse, 'default{timer(){switch(1){case 1;}}}', ('enableswitch', )) self.assertRaises(lslparse.EParseSyntax, parser.parse, 'default{timer(){switch(1){default;}}}', ('enableswitch', )) self.assertRaises(lslparse.EParseInvalidBrkContArg, parser.parse, 'default{timer(){while(1){break 0;}}}', ('breakcont', )) self.assertRaises(lslparse.EParseInvalidBrkContArg, parser.parse, 'default{timer(){while(1){break 2;}}}', ('breakcont', )) self.assertRaises(lslparse.EParseInvalidBrkContArg, parser.parse, 'default{timer(){while(1){continue 0;}}}', ('breakcont', )) self.assertRaises(lslparse.EParseInvalidBrkContArg, parser.parse, 'default{timer(){while(1){continue 2;}}}', ('breakcont', )) self.assertRaises(lslparse.EParseSyntax, parser.parse, 'integer T=-TRUE;default{timer(){}}') self.assertRaises(lslparse.EParseSyntax, parser.parse, 'list L=[[]];default{timer(){}}') self.assertRaises(lslparse.EParseSyntax, parser.parse, 'default{timer(integer i){}}') self.assertRaises( lslparse.EParseSyntax, parser.parse, 'i = 0;', ) self.assertRaises(lslparse.EParseSyntax, parser.parse, 'default{timer(){}}state{timer(){}}') self.assertRaises(lslparse.EParseUndefined, parser.parse, 'default{timer(){jump undefined;}}') # BuildTempGlobalsTable coverage self.assertRaises(lslparse.EParseSyntax, parser.parse, ';') self.assertRaises(lslparse.EParseSyntax, parser.parse, 'f(;') self.assertRaises(lslparse.EParseSyntax, parser.parse, 'f();') self.assertRaises(lslparse.EParseSyntax, parser.parse, 'integer f=') self.assertRaises(lslparse.EParseUEOF, parser.parse, 'integer /*') self.assertRaises(lslparse.EParseSyntax, parser.parse, 'default{timer(){}}state e;')
def main(argv): """Main executable.""" import os if hasattr(sys, "frozen") and sys.frozen in ("windows_exe", "console_exe"): lslopt.lslcommon.DataPath = (os.path.dirname(os.path.abspath(sys.executable)) + os.sep) else: # If it's good to append the basename to it, it's good to append the # auxiliary files' names to it, which should be located where this file is. lslopt.lslcommon.DataPath = __file__[:-len(os.path.basename(__file__))] # Default options options = {'extendedglobalexpr', 'extendedtypecast', 'extendedassignment', 'allowkeyconcat', 'allowmultistrings', 'processpre', 'warntabs', 'optimize', 'optsigns', 'optfloats', 'constfold', 'dcr', 'errmissingdefault', 'listlength', 'listadd'} assert not (options - validoptions), (u"Default options not present in" u" validoptions: '%s'" % (b"', '".join(options - validoptions)).decode('utf8')) try: opts, args = getopt.gnu_getopt(argv[1:], 'hO:o:p:P:HTyb:L:A:', ('optimizer-options=', 'help', 'version', 'output=', 'header', 'timestamp', 'python-exceptions', 'prettify', 'bom', 'emap', 'preproc=', 'precmd=', 'prearg=', 'prenodef', 'preshow', 'avid=', 'avname=', 'assetid=', 'shortname=', 'builtins=' 'libdata=', 'postarg=')) except getopt.GetoptError as e: Usage(argv[0]) werr(u"\nError: %s\n" % str(e).decode('utf8', 'replace')) return 1 outfile = '-' avid = '00000000-0000-0000-0000-000000000000' avname = '' shortname = '' assetid = '00000000-0000-0000-0000-000000000000' preproc_command = 'cpp' preproc_user_preargs = [] preproc_user_postargs = [] preproc = 'none' predefines = True script_header = '' script_timestamp = '' preshow = False raise_exception = False prettify = False bom = False emap = False builtins = None libdata = None for opt, arg in opts: if opt in ('-O', '--optimizer-options'): optchanges = arg.lower().split(',') for chg in optchanges: if not chg: continue if chg in ('clear', '+clear'): options = set() continue if chg == '-clear': # ignore continue chgfix = chg if chgfix[0] not in ('+', '-'): chgfix = '+' + chgfix if chgfix[1:] not in validoptions: Usage(argv[0], 'optimizer-options') werr(u"\nError: Unrecognized" u" optimizer option: %s\n" % chg.decode('utf8')) return 1 if chgfix[0] == '-': options.discard(chgfix[1:]) else: options.add(chgfix[1:]) del chgfix elif opt in ('-h', '--help'): Usage(argv[0]) return 0 elif opt == '--version': wout(u'LSL PyOptimizer version %s\n' % str2u(VERSION)) return 0 elif opt in ('-o', '--output'): outfile = arg elif opt in ('-b', '--builtins'): builtins = arg elif opt in ('-L', '--libdata'): libdata = arg elif opt in ('-y', '--python-exceptions'): raise_exception = True elif opt in ('-p', '--preproc'): preproc = arg.lower() supported = ('ext', 'mcpp', 'gcpp', 'none') if preproc not in supported: Usage(argv[0]) werr(u"\nUnknown --preproc option: '%s'." u" Only '%s' supported.\n" % (preproc, str2u("', '".join(supported)))) return 1 if preproc == 'gcpp': preproc_command = 'cpp' elif preproc == 'mcpp': preproc_command = 'mcpp' elif opt == '--precmd': preproc_command = arg elif opt in ('-P', '--prearg'): preproc_user_preargs.append(arg) elif opt == '--prenodef': predefines = False elif opt == '--preshow': preshow = True elif opt in ('-H', '--header'): script_header = True elif opt in ('-T', '--timestamp'): script_timestamp = True elif opt == '--avid': avid = arg elif opt == '--avname': avname = arg elif opt == '--assetid': assetid = arg elif opt == '--shortname': shortname = arg elif opt == '--prettify': prettify = True elif opt == '--bom': bom = True elif opt == '--emap': emap = True del opts if prettify: options &= {'rsrclimit'} options.add('prettify') rsrclimit = False try: if 'rsrclimit' in options: rsrclimit = True import resource resource.setrlimit(resource.RLIMIT_CPU, (5, 8)) resource.setrlimit(resource.RLIMIT_STACK, (0x60000, 0x80000)) resource.setrlimit(resource.RLIMIT_DATA, (9001000, 12001000)) resource.setrlimit(resource.RLIMIT_AS, (61001000, 81001000)) if 'lso' in options: lslopt.lslcommon.LSO = True options.remove('lso') if 'expr' in options: lslopt.lslcommon.IsCalc = True options.remove('expr') if 'help' in options: Usage(argv[0], 'optimizer-options') return 0 fname = args[0] if args else None if fname is None: Usage(argv[0]) werr(u"\nError: Input file not specified. Use -" u" if you want to use stdin.\n") return 1 del args script = '' if fname == '-': script = sys.stdin.read() else: try: f = open(fname, 'r') except IOError as e: if e.errno == 2: werr(u"Error: File not found: %s\n" % str2u(fname)) return 2 raise try: script = f.read() finally: f.close() del f if script_header: script_header = ScriptHeader(script, avname) if script_timestamp: import time tmp = time.time() script_timestamp = time.strftime( b'// Generated on %Y-%m-%dT%H:%M:%S.{0:06d}Z\n' .format(int(tmp % 1 * 1000000)), time.gmtime(tmp)) del tmp if shortname == '': shortname = os.path.basename(fname) # Build preprocessor command line preproc_cmdline = [preproc_command] + preproc_user_preargs if preproc == 'gcpp': preproc_cmdline += [ '-undef', '-x', 'c', '-std=c99', '-nostdinc', '-trigraphs', '-dN', '-fno-extended-identifiers', ] elif preproc == 'mcpp': preproc_cmdline += [ '-e', 'UTF-8', '-I-', '-N', '-2', '-3', '-j', '-V199901L', ] if predefines: preproc_cmdline += [ '-Dinteger(...)=((integer)(__VA_ARGS__))', '-Dfloat(...)=((float)(__VA_ARGS__))', '-Dstring(...)=((string)(__VA_ARGS__))', '-Dkey(...)=((key)(__VA_ARGS__))', '-Drotation(...)=((rotation)(__VA_ARGS__))', '-Dquaternion(...)=((quaternion)(__VA_ARGS__))', '-Dvector(...)=((vector)(__VA_ARGS__))', '-Dlist(...)=((list)(__VA_ARGS__))', ] preproc_cmdline.append('-D__AGENTKEY__="%s"' % avid) preproc_cmdline.append('-D__AGENTID__="%s"' % avid) preproc_cmdline.append('-D__AGENTIDRAW__=' + avid) preproc_cmdline.append('-D__AGENTNAME__="%s"' % avname) preproc_cmdline.append('-D__ASSETID__=' + assetid) preproc_cmdline.append('-D__SHORTFILE__="%s"' % shortname) preproc_cmdline.append('-D__OPTIMIZER__=LSL PyOptimizer') preproc_cmdline.append('-D__OPTIMIZER_VERSION__=' + VERSION) # Append user arguments at the end to allow them to override defaults preproc_cmdline += preproc_user_postargs if type(script) is unicode: script = script.encode('utf8') else: try: # Try converting the script to Unicode, to report any encoding # errors with accurate line information. At this point we don't # need the result. UniConvScript(script, options, fname if fname != '-' else '<stdin>', emap).to_unicode() except EParse as e: # We don't call ReportError to prevent problems due to # displaying invalid UTF-8 werr(e.args[0] + u"\n") return 1 if preproc != 'none': # At this point, for the external preprocessor to work we need the # script as a byte array, not as unicode, but it should be UTF-8. script = PreparePreproc(script.decode('utf8')).encode('utf8') if preproc == 'mcpp': # As a special treatment for mcpp, we force it to output its # macros so we can read if USE_xxx are defined. With GCC that # is achieved with -dN, but mcpp has no command line option. script += b'\n#pragma MCPP put_defines\n' # Invoke the external preprocessor import subprocess p = subprocess.Popen(preproc_cmdline, stdin=subprocess.PIPE, stdout=subprocess.PIPE) script = p.communicate(input=script)[0] status = p.wait() if status: return status del p, status # This method is very imperfect, in several senses. However, since # it's applied to the output of the preprocessor, all of the # concerns should be addressed: # - \s includes \n, but \n should not be allowed. # - Comments preceding the directive should not cause problems. # e.g.: /* test */ #directive # - #directive within a comment or string should be ignored. for x in re.findall(br'(?:(?<=\n)|^)\s*#\s*define\s+(' br'USE_SWITCHES' br'|USE_LAZY_LISTS' br')(?:$|[^A-Za-z0-9_])', script, re.S): if x == b'USE_SWITCHES': options.add('enableswitch') elif x == b'USE_LAZY_LISTS': options.add('lazylists') if not preshow: if emap: options.add('emap') lib = lslopt.lslloadlib.LoadLibrary(builtins, libdata) p = parser(lib) try: ts = p.parse(script, options, fname if fname != '-' else '<stdin>') except EParse as e: ReportError(script, e) return 1 del p, script opt = optimizer(lib) ts = opt.optimize(ts, options) del opt outs = outscript() script = script_header + script_timestamp + outs.output(ts, options) del outs, ts del script_header, script_timestamp if bom: if not script.startswith(b'\xEF\xBB\xBF'): script = b'\xEF\xBB\xBF' + script if outfile == '-': sys.stdout.write(script) else: outf = open(outfile, 'w') try: outf.write(script) finally: outf.close() return 0 except Exception as e: if rsrclimit: # Raise the soft limits to hopefully prevent double exceptions resource.setrlimit(resource.RLIMIT_CPU, (8, 8)) resource.setrlimit(resource.RLIMIT_STACK, (0x80000, 0x80000)) resource.setrlimit(resource.RLIMIT_DATA, (12001000, 12001000)) resource.setrlimit(resource.RLIMIT_AS, (81001000, 81001000)) if raise_exception: raise werr(e.__class__.__name__ + ': ' + str(e) + '\n') return 1