mirror of
https://github.com/Sei-Lisa/LSL-PyOptimizer
synced 2025-07-01 15:48:21 +00:00
Fix void expressions in FOR loops.
As an enhancement over LSL, we trigger a Type Mismatch when there are void expressions in list constructors, because in LSL, while accepted, they trigger an ugly runtime exception. This works fine, but then expression lists, where this are checked, are not exclusive of list constructor; they are used in other places. One of these places is the initializer and the iterator of FOR loops. As a consequence, we didn't allow void functions in the FOR initializer or iterator. Fix by adding another possible value to the parameter 'expected_types' in Parse_expression_list. False means don't allow void either (what Null did before); Null now means allow anything. All callers to Parse_expression_list are changed accordingly. Added corresponding regression test.
This commit is contained in:
parent
e60457f00e
commit
37483a72cb
2 changed files with 22 additions and 17 deletions
|
@ -872,7 +872,7 @@ class parser(object):
|
|||
|
||||
if tok0 == '[':
|
||||
self.NextToken()
|
||||
val = self.Parse_optional_expression_list()
|
||||
val = self.Parse_optional_expression_list(False)
|
||||
self.expect(']')
|
||||
self.NextToken()
|
||||
return {'nt':'LIST', 't':'list', 'ch':val}
|
||||
|
@ -926,7 +926,7 @@ class parser(object):
|
|||
self.NextToken()
|
||||
if typ != 'list':
|
||||
raise EParseTypeMismatch(self)
|
||||
idxexpr = self.Parse_optional_expression_list()
|
||||
idxexpr = self.Parse_optional_expression_list(False)
|
||||
self.expect(']')
|
||||
self.NextToken()
|
||||
if self.tok[0] != '=' or not AllowAssignment:
|
||||
|
@ -1464,24 +1464,28 @@ list lazy_list_set(list L, integer i, list v)
|
|||
optional_expression_list: LAMBDA | expression_list
|
||||
expression_list: expression | expression_list ',' expression
|
||||
"""
|
||||
# This is a maze of which we get out with a dirty hack.
|
||||
# optional_expression_list is used by FOR statements (closed by ';' or ')'),
|
||||
# list constants (closed by ']') and function arguments (closed by ')').
|
||||
# If it's not the right token, we'll err anyway, in Parse_expression or
|
||||
# upon return.
|
||||
# Recursive descendent parsers are nice, but not exempt of problems.
|
||||
# We need to accept empty lists. This is a maze of which we get out
|
||||
# with a dirty hack. Rather than attempt to parse as an expression and
|
||||
# backtrack in case of error, we check the next token to see if it
|
||||
# is one that closes the expression list.
|
||||
# optional_expression_list is used by FOR loops (closed by ';' or ')'),
|
||||
# list constants and lazy lists (closed by ']') and function arguments
|
||||
# (closed by ')'). If it's not the right token, we'll err anyway upon
|
||||
# return.
|
||||
ret = []
|
||||
idx = 0
|
||||
if self.tok[0] not in (']', ')', ';'):
|
||||
while True:
|
||||
expr = self.Parse_expression()
|
||||
if expected_types is not None:
|
||||
if False is not expected_types is not None:
|
||||
if idx >= len(expected_types):
|
||||
raise EParseFunctionMismatch(self)
|
||||
try:
|
||||
expr = self.autocastcheck(expr, expected_types[idx]);
|
||||
except EParseTypeMismatch:
|
||||
raise EParseFunctionMismatch(self)
|
||||
else:
|
||||
elif expected_types is False: # don't accept void expressions
|
||||
if expr['t'] not in self.types:
|
||||
raise EParseTypeMismatch(self)
|
||||
idx += 1
|
||||
|
@ -1489,7 +1493,7 @@ list lazy_list_set(list L, integer i, list v)
|
|||
if self.tok[0] != ',':
|
||||
break
|
||||
self.NextToken()
|
||||
if expected_types is not None and idx != len(expected_types):
|
||||
if False is not expected_types is not None and idx != len(expected_types):
|
||||
raise EParseFunctionMismatch(self)
|
||||
return ret
|
||||
|
||||
|
|
|
@ -228,13 +228,14 @@ class Test02_Parser(UnitTestCase):
|
|||
self.parser.parse('default{timer(){vector v;v.x=0;}}')
|
||||
|
||||
# Check for exceptions only
|
||||
p = self.parser.parse('default{timer(){jump x;while(1)@x;}}')
|
||||
p = self.parser.parse('default{timer(){jump x;do@x;while(1);}}')
|
||||
p = self.parser.parse('default{timer(){jump x;for(;1;)@x;}}')
|
||||
p = self.parser.parse('default{timer(){jump x;while(1)@x;}}', ('breakcont',))
|
||||
p = self.parser.parse('default{timer(){jump x;do@x;while(1);}}', ('breakcont',))
|
||||
p = self.parser.parse('default{timer(){jump x;for(;1;)@x;}}', ('breakcont',))
|
||||
self.outscript.output(p)
|
||||
self.parser.parse('default{timer(){jump x;while(1)@x;}}')
|
||||
self.parser.parse('default{timer(){jump x;do@x;while(1);}}')
|
||||
self.parser.parse('default{timer(){jump x;for(;1;)@x;}}')
|
||||
self.parser.parse('default{timer(){jump x;while(1)@x;}}', ('breakcont',))
|
||||
self.parser.parse('default{timer(){jump x;do@x;while(1);}}', ('breakcont',))
|
||||
self.parser.parse('default{timer(){jump x;for(;1;)@x;}}', ('breakcont',))
|
||||
|
||||
self.parser.parse('default{timer(){for(llDie();1;llDie());}}')
|
||||
|
||||
self.assertRaises(EParseUndefined, self.parser.parse,
|
||||
'default{timer(){jump x;while(1){@x;}}}')
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue