From 0855b8ad1d7369791b29cfb41d84812e0833a7b6 Mon Sep 17 00:00:00 2001 From: Sei Lisa Date: Sat, 22 Dec 2018 15:44:14 +0100 Subject: [PATCH] Add 'listto' to fndata.txt; remove more magic names from the code Rather than using a hardcoded table of list-to-type extraction function, add a 'ListTo' attribute to the function data. No error is raised if more than one function exists to convert to the same type. This change is of questionable usefulness, but it should soothe those allergic to magic names/numbers. I cringed a bit myself. While on it, change the syntax error that was raised when the corresponding conversion function did not exist, to a tailor-made error. --- fndata.txt | 11 ++++++ lslopt/lslloadlib.py | 80 ++++++++++++++++++++++++-------------------- lslopt/lslparse.py | 24 ++++++++----- 3 files changed, 70 insertions(+), 45 deletions(-) diff --git a/fndata.txt b/fndata.txt index a4eb2a0..1ae18e3 100644 --- a/fndata.txt +++ b/fndata.txt @@ -75,6 +75,10 @@ # max: # Like min, but for the maximum value. # +# listto: +# Identifies the function as a list-extracting function, for use with +# lazy lists. It needs an LSL type as a parameter. +# # return [if ]: # Self-explanatory. There can be several; they are evaluated in order. # can be a parameter or a LSL constant. is explaned @@ -927,30 +931,37 @@ string llList2CSV(list src) float llList2Float(list src, integer index) - SEF +- listto float integer llList2Integer(list src, integer index) - SEF +- listto integer string llList2Json(string type, list values) - SEF key llList2Key(list src, integer index) - SEF +- listto key list llList2List(list src, integer start, integer end) - SEF +- listto list list llList2ListStrided(list src, integer start, integer end, integer stride) - SEF rotation llList2Rot(list src, integer index) - SEF +- listto rotation string llList2String(list src, integer index) - SEF +- listto string vector llList2Vector(list src, integer index) - SEF +- listto vector integer llListFindList(list src, list test) - SEF diff --git a/lslopt/lslloadlib.py b/lslopt/lslloadlib.py index 3efb269..d91e068 100644 --- a/lslopt/lslloadlib.py +++ b/lslopt/lslloadlib.py @@ -235,7 +235,9 @@ def LoadLibrary(builtins = None, fndata = None): r'|\[(?:[^]"]|"(?:\\.|[^"])*")*\]))' # lists r'(?:\s+if\s+(.*\S))?' r'|(unstable|stop|strlen|detect|touch|grab)' - r'|(min|max|delay)\s+([-0-9.]+))\s*$', re.I) + r'|(min|max|delay)\s+([-0-9.]+)' + r'|listto\s+(integer|float|string|key|vector|rotation|list)' + r')\s*$', re.I) # TODO: "quaternion" doesn't compare equal to "rotation" even if they are # equivalent. Canonicalize it before comparison, to avoid false @@ -382,44 +384,50 @@ def LoadLibrary(builtins = None, fndata = None): functions[curr_fn]['uns'] = True else: functions[curr_fn][flag] = True - elif match_flag.group(5).lower() in ('min', 'max'): - minmax = match_flag.group(5).lower() - value = match_flag.group(6) - typ = functions[curr_fn]['Type'] - if typ == 'integer': - good = parse_int_re.search(value) + elif match_flag.group(5): + if match_flag.group(5).lower() in ('min', 'max'): + minmax = match_flag.group(5).lower() + value = match_flag.group(6) + typ = functions[curr_fn]['Type'] + if typ == 'integer': + good = parse_int_re.search(value) + if good: + value = lslfuncs.S32(int(good.group(1), 0)) + elif typ == 'float': + good = parse_fp_re.search(value) + if good: + value = lslfuncs.F32(float(good.group(1))) + else: + good = False if good: - value = lslfuncs.S32(int(good.group(1), 0)) - elif typ == 'float': - good = parse_fp_re.search(value) - if good: - value = lslfuncs.F32(float(good.group(1))) - else: - good = False - if good: - functions[curr_fn][minmax] = value - else: - warning(u"Type mismatch or value error in %s" - u" line %d: %s" - % (ufndata, linenum, uline)) - continue - else: # delay - value = parse_fp_re.search(match_flag.group(6)) - if not value: - warning(u"Invalid delay value in %s" - u" line %d: %s" - % (ufndata, linenum, uline)) - continue + functions[curr_fn][minmax] = value + else: + warning(u"Type mismatch or value error in %s" + u" line %d: %s" + % (ufndata, linenum, uline)) + continue + else: # delay + value = parse_fp_re.search(match_flag.group(6)) + if not value: + warning(u"Invalid delay value in %s" + u" line %d: %s" + % (ufndata, linenum, uline)) + continue - value = float(value.group(1)) # no need to F32 - if value != 0 and 'SEF' in functions[curr_fn]: - warning(u"Side-effect-free function" - u" %s contradicts delay, in %s" - u" line %d" - % (ucurr_fn, ufndata, linenum)) - continue + value = float(value.group(1)) # no need to F32 + if value != 0 and 'SEF' in functions[curr_fn]: + warning(u"Side-effect-free function" + u" %s contradicts delay, in %s" + u" line %d" + % (ucurr_fn, ufndata, linenum)) + continue - functions[curr_fn]['delay'] = value + functions[curr_fn]['delay'] = value + elif match_flag.group(7): + functions[curr_fn]['ListTo'] = match_flag.group(7) + continue + else: + pass else: warning(u"Syntax error in %s line %d, skipping: %s" % (ufndata, linenum, uline)) diff --git a/lslopt/lslparse.py b/lslopt/lslparse.py index ece5526..86ac786 100644 --- a/lslopt/lslparse.py +++ b/lslopt/lslparse.py @@ -200,6 +200,11 @@ class EParseInvalidLabelOpt(EParse): u" child of a 'for', 'if', 'while' or 'do'. Disable optimization" u" or rewrite the code in some other way.") +class EParseNoConversion(EParse): + def __init__(self, parser): + super(EParseNoConversion, self).__init__(parser, + u"There's no conversion function in the library for this type") + class EInternal(Exception): """This exception is a construct to allow a different function to cause an immediate return of EOF from parser.GetToken(). @@ -225,9 +230,7 @@ class parser(object): unicode:'STRING_VALUE', Key:'KEY_VALUE', Vector:'VECTOR_VALUE', Quaternion:'ROTATION_VALUE', list:'LIST_VALUE'} - TypeToExtractionFunction = {'integer':'llList2Integer', - 'float':'llList2Float', 'string':'llList2String', 'key':'llList2Key', - 'vector':'llList2Vector', 'rotation':'llList2Rot', 'list':'llList2List'} + TypeToExtractionFunction = {} # Utility function def GenerateLabel(self): @@ -1316,14 +1319,11 @@ list lazy_list_set(list L, integer i, list v) expr = self.Parse_unary_postfix_expression(AllowAssignment = False) basetype = expr.t if self.lazylists and basetype is None and expr.nt == 'SUBIDX': + if typ not in self.TypeToExtractionFunction: + raise EParseNoConversion(self) fn = self.TypeToExtractionFunction[typ] sym = self.FindSymbolFull(fn, 0) - if sym is None: - # in the unlikely event that the underlying function is not - # defined in builtins.txt, throw a syntax error (making a - # new exception just for this seems overkill, and throwing - # an unknown identifier error would be confusing) - raise EParseSyntax(self) + assert sym is not None fnparamtypes = sym['ParamTypes'] subparamtypes = [x.t for x in expr.ch] if fnparamtypes != subparamtypes: @@ -2713,6 +2713,12 @@ list lazy_list_set(list L, integer i, list v) self.constants = lib[1] self.funclibrary = lib[2] + self.TypeToExtractionFunction.clear() + for name in self.funclibrary: + fn = self.funclibrary[name] + if 'ListTo' in fn: + self.TypeToExtractionFunction[fn['ListTo']] = name + self.filename = filename if type(script) is unicode: