Introduce 'c' (constant) as symbol kind

This is preparatory work for addressing issue #30. It also fixes a bug where --prettify made the constants writable.
This commit is contained in:
Sei Lisa 2024-05-25 20:48:30 +02:00
parent c924f3ae5c
commit 5841c2c17a
2 changed files with 70 additions and 13 deletions

View file

@ -1009,7 +1009,7 @@ class parser(object):
return nr(nt='FNCALL', t=sym['Type'], name=name, ch=args)
sym = self.FindSymbolFull(val)
if sym is None or sym['Kind'] != 'v':
if sym is None or sym['Kind'] not in {'v', 'c'}:
self.errorpos = savepos
raise EParseUndefined(self)
@ -1052,9 +1052,8 @@ class parser(object):
self.AddSymbol('f', 0, 'lazy_list_set', Loc=self.usedspots,
Type='list', ParamTypes=params[0], ParamNames=params[1],
Inline=False)
self.AddSymbol('v', paramscope, 'L', Type='list')
self.AddSymbol('v', paramscope, 'i', Type='integer')
self.AddSymbol('v', paramscope, 'v', Type='list')
for typ, name in zip(*params):
self.AddSymbol('v', paramscope, name, Type=typ)
#self.PushScope() # no locals
# Add body (apologies for the wall of text)
@ -1187,6 +1186,8 @@ list lazy_list_set(list L, integer i, list v)
)])
if tok0 == '.':
if sym['Kind'] == 'c':
raise EParseSyntax(self)
self.NextToken()
self.expect('IDENT')
self.ValidateField(typ, self.tok[1])
@ -1196,6 +1197,8 @@ list lazy_list_set(list L, integer i, list v)
typ = 'float'
if tok0 in ('++', '--'):
if sym['Kind'] == 'c':
raise EParseSyntax(self)
self.NextToken()
if lvalue.t not in ('integer', 'float'):
raise EParseTypeMismatch(self)
@ -1204,6 +1207,8 @@ list lazy_list_set(list L, integer i, list v)
if AllowAssignment and (tok0 in self.assignment_toks
or self.extendedassignment
and tok0 in self.extassignment_toks):
if sym['Kind'] == 'c':
raise EParseSyntax(self)
self.NextToken()
expr = self.Parse_expression()
rtyp = expr.t
@ -1765,6 +1770,8 @@ list lazy_list_set(list L, integer i, list v)
self.NextToken()
self.expect('IDENT')
name = self.tok[1]
if name in self.symtab[0] and self.symtab[0][name]['Kind'] == 'c':
raise EParseSyntax(self)
if name in self.symtab[self.scopeindex]:
raise EParseAlreadyDefined(self)
# shrinknames *needs* all labels renamed, so they are out of the way
@ -2258,6 +2265,8 @@ list lazy_list_set(list L, integer i, list v)
self.NextToken()
self.expect('IDENT')
name = self.tok[1]
if name in self.symtab[0] and self.symtab[0][name]['Kind'] == 'c':
raise EParseSyntax(self)
if name in self.symtab[self.scopeindex]:
raise EParseAlreadyDefined(self)
self.NextToken()
@ -2354,8 +2363,8 @@ list lazy_list_set(list L, integer i, list v)
sym = self.FindSymbolPartial(tok[1])
# The parser accepts library function names here as valid variables
# (it chokes at RAIL in Mono, and at runtime in LSO for some types)
if sym is None or sym['Kind'] != 'v' and (sym['Kind'] != 'f'
or 'ParamNames' in sym): # only UDFs have ParamNames
if sym is None or sym['Kind'] not in {'v', 'c'} and (sym['Kind'] !=
'f' or 'ParamNames' in sym): # only UDFs have ParamNames
raise EParseUndefined(self)
typ = sym['Type']
if ForbidList and lslcommon.LSO and typ == 'key':
@ -2429,6 +2438,10 @@ list lazy_list_set(list L, integer i, list v)
self.expect('IDENT')
name = self.tok[1]
if (name in self.symtab[0]
and self.symtab[0][name]['Kind'] == 'c'
):
raise EParseSyntax(self)
if name in self.symtab[self.scopeindex]:
raise EParseAlreadyDefined(self)
@ -2928,14 +2941,16 @@ list lazy_list_set(list L, integer i, list v)
# The first element (0) is the global scope. Each symbol table is a
# dictionary of symbols, whose elements are in turn dictionaries of
# attributes. Each has a 'Kind', which can be:
# 'v' for variable, 'f' for function, 'l' for label, 's' for state,
# or 'e' for event. Some have a 'Loc' indicating the location (index)
# of the definition in the tree root.
# Variables have 'Scope' and 'Type' (a string).
# 'v' for variable, 'c' for constant, 'f' for function, 'l' for label,
# 's' for state, or 'e' for event. Some have a 'Loc' indicating the
# location (index) of the definition in the tree's root.
# Variables and constants have 'Scope' and 'Type' (a string).
# Global variables also have 'Loc'.
# Variables that are parameters also have 'Param'.
# Functions have 'Type' (return type, a string) and 'ParamTypes' (a list of strings).
# User-defined functions also have 'Loc' and 'ParamNames' (a list of strings).
# Functions have 'Type' (return type, a string) and 'ParamTypes' (a
# list of strings).
# User-defined functions also have 'Loc' and 'ParamNames' (a list of
# strings).
# Labels only have 'Scope'.
# States only have 'Loc'.
# Events have 'ParamTypes' and 'ParamNames', just like UDFs.
@ -2953,7 +2968,7 @@ list lazy_list_set(list L, integer i, list v)
if self.prettify:
# Add the constants as symbol table variables...
for i in self.constants:
self.symtab[0][i] = {'Kind':'v', 'Scope':0,
self.symtab[0][i] = {'Kind':'c', 'Scope':0,
'Type':lslcommon.PythonType2LSL[type(self.constants[i])]}
# ... and remove them as constants.
self.constants = {}

View file

@ -418,6 +418,48 @@ class UnitTestRegression(UnitTestCase):
self.assertRaises(lslparse.EParseTypeMismatch, parser.parse,
'default{timer(){if(1)return 1;}}')
# Constants with 'c' in symbol table (introduced to solve issue #30)
# Some errors are not the expected ones. Most (all?) should be syntax.
parser.parse('integer a=LOOP;default{timer(){llOwnerSay(NAK+EOF);}}')
parser.parse('integer a=LOOP;default{timer(){llOwnerSay(NAK+EOF);}}',
('prettify',))
parser.parse('default{timer(){LOOP;}}')
parser.parse('default{timer(){LOOP;}}', ('prettify',))
self.assertRaises(lslparse.EParseSyntax, parser.parse,
'default{touch(integer LOOP){}}', ('prettify',))
self.assertRaises(lslparse.EParseSyntax, parser.parse,
'default{timer(){ZERO_VECTOR.x;}}', ('prettify',))
self.assertRaises(lslparse.EParseSyntax, parser.parse,
'default{timer(){LOOP.x;}}', ('prettify',))
self.assertRaises(lslparse.EParseSyntax, parser.parse,
'default{timer(){LOOP=1;}}', ('prettify',))
self.assertRaises(lslparse.EParseSyntax, parser.parse,
'default{timer(){LOOP++;}}', ('prettify',))
# should raise EParseSyntax instead
self.assertRaises(lslparse.EParseUndefined, parser.parse,
'default{timer(){++LOOP;}}', ('prettify',))
# should raise EParseSyntax instead
self.assertRaises(lslparse.EParseAlreadyDefined, parser.parse,
'integer LOOP=0;', ('prettify',))
# should raise EParseSyntax instead
self.assertRaises(lslparse.EParseAlreadyDefined, parser.parse,
'integer LOOP(){}', ('prettify',))
self.assertRaises(lslparse.EParseSyntax, parser.parse,
'default{timer(){integer LOOP=1;}}', ('prettify',))
self.assertRaises(lslparse.EParseSyntax, parser.parse,
'default{timer(){integer LOOP;}}', ('prettify',))
# should raise EParseSyntax instead
self.assertRaises(lslparse.EParseAlreadyDefined, parser.parse,
'default{timer(){}}state LOOP{timer(){}}', ('prettify',))
# should raise EParseSyntax instead
self.assertRaises(lslparse.EParseUndefined, parser.parse,
'default{timer(){state LOOP;}}', ('prettify',))
self.assertRaises(lslparse.EParseSyntax, parser.parse,
'default{timer(){@LOOP;}}', ('prettify',))
self.assertRaises(lslparse.EParseSyntax, parser.parse,
'default{timer(){integer LOOP;}}', ('prettify',))
class UnitTestCoverage(UnitTestCase):
def test_coverage_misc(self):
"""Miscellaneous tests that can't be computed or are too difficult