Fix leaking scope of function to outer scope
This commit is contained in:
@@ -81,7 +81,7 @@ class Environment():
|
||||
if function.name == name:
|
||||
signatureCheckresult = function.signature.check(args)
|
||||
if signatureCheckresult[0]:
|
||||
self.scopes.append(function.defaultArgs)
|
||||
self.appendScope(function.defaultArgs)
|
||||
self.scopes[-1].update({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) })
|
||||
self.callStack.append(CallStackItem(name))
|
||||
result = Type.void()
|
||||
@@ -90,7 +90,7 @@ class Environment():
|
||||
except Return as r:
|
||||
result = r.value
|
||||
self.callStack.pop(-1)
|
||||
self.scopes.pop(-1)
|
||||
self.popScope(mergeVariables=False)
|
||||
return (True, result)
|
||||
raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}")
|
||||
return (False, None)
|
||||
@@ -140,6 +140,17 @@ class Environment():
|
||||
else:
|
||||
return scope
|
||||
|
||||
def appendScope(self, variables=None):
|
||||
if variables is None:
|
||||
variables = {}
|
||||
|
||||
self.scopes.append(variables)
|
||||
|
||||
def popScope(self, mergeVariables=True):
|
||||
lastScope = self.scopes.pop(-1)
|
||||
if mergeVariables:
|
||||
self.scopes[-1].update(lastScope)
|
||||
|
||||
def scopesToString(self):
|
||||
return "Scopes:\n" + ("\n".join([ f" [{i}]: {scope}" for i, scope in enumerate(self.scopes) ]))
|
||||
|
||||
|
||||
@@ -8,11 +8,7 @@ class AssignmentEvaluator(Evaluator):
|
||||
def evaluator(cls, node, environment):
|
||||
target = node.left.value
|
||||
value = expressionEvaluator(doAssert=True)(node.right, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
scopeOfExistingVariable = environment.findVariableScope(target)
|
||||
if scopeOfExistingVariable is None:
|
||||
environment.scopes[-1][target] = value
|
||||
else:
|
||||
scopeOfExistingVariable[target] = value
|
||||
environment.scopes[-1][target] = value
|
||||
|
||||
return value
|
||||
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
from smnp.ast.node.identifier import Identifier
|
||||
from smnp.runtime.evaluator import evaluate, Evaluator, EvaluationResult
|
||||
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||
from smnp.type.model import Type
|
||||
|
||||
|
||||
class AsteriskEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
def evaluator(cls, node, environment):
|
||||
iterator = expressionEvaluator(doAssert=True)(node.iterator, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
return Evaluator.oneOf(
|
||||
cls._numberIteratorAsteriskEvaluator(iterator),
|
||||
cls._listIteratorAsteriskEvaluator(iterator),
|
||||
cls._mapIteratorAsteriskEvaluator(iterator)
|
||||
)(node, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
|
||||
@classmethod
|
||||
def _numberIteratorAsteriskEvaluator(cls, evaluatedIterator):
|
||||
def evaluator(node, environment):
|
||||
if evaluatedIterator.type == Type.INTEGER:
|
||||
results = []
|
||||
automaticVariable = cls._automaticNamedVariable(node.iterator, environment, "_")
|
||||
for i in range(evaluatedIterator.value):
|
||||
environment.scopes[-1][automaticVariable] = Type.integer(i + 1)
|
||||
result = evaluate(node.statement, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
if result is None or result.type == Type.VOID:
|
||||
results = None
|
||||
|
||||
if results is not None:
|
||||
results.append(result)
|
||||
|
||||
del environment.scopes[-1][automaticVariable]
|
||||
|
||||
return EvaluationResult.OK(Type.list(results).decompose() if results is not None else Type.void())
|
||||
|
||||
return EvaluationResult.FAIL()
|
||||
|
||||
return evaluator
|
||||
|
||||
@classmethod
|
||||
def _automaticNamedVariable(cls, iteratorNode, environment, prefix=''):
|
||||
if type(iteratorNode) == Identifier:
|
||||
return cls._automaticVariableName(environment, prefix, iteratorNode.value, False)
|
||||
else:
|
||||
return cls._automaticVariableName(environment, prefix, '', True)
|
||||
|
||||
@classmethod
|
||||
def _automaticVariableName(cls, environment, prefix='', suffix='', startWithNumber=False):
|
||||
number = 1 if startWithNumber else ''
|
||||
variableName = lambda x: f"{prefix}{x}{suffix}"
|
||||
while environment.findVariableScope(variableName(number)) is not None:
|
||||
if number == '':
|
||||
number = 1
|
||||
else:
|
||||
number += 1
|
||||
|
||||
return variableName(number)
|
||||
|
||||
@classmethod
|
||||
def _listIteratorAsteriskEvaluator(cls, evaluatedIterator):
|
||||
def evaluator(node, environment):
|
||||
if evaluatedIterator.type == Type.LIST:
|
||||
results = []
|
||||
automaticVariableKey = cls._automaticNamedVariable(node.iterator, environment, "_")
|
||||
automaticVariableValue = cls._automaticNamedVariable(node.iterator, environment, "__")
|
||||
for i, v in enumerate(evaluatedIterator.value):
|
||||
environment.scopes[-1][automaticVariableKey] = Type.integer(i + 1)
|
||||
environment.scopes[-1][automaticVariableValue] = v
|
||||
result = evaluate(node.statement, environment).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
if result is not None and result.type != Type.VOID:
|
||||
results.append(result)
|
||||
|
||||
del environment.scopes[-1][automaticVariableKey]
|
||||
del environment.scopes[-1][automaticVariableValue]
|
||||
|
||||
return EvaluationResult.OK(Type.list(results).decompose())
|
||||
|
||||
return EvaluationResult.FAIL()
|
||||
|
||||
return evaluator
|
||||
|
||||
@classmethod
|
||||
def _mapIteratorAsteriskEvaluator(cls, evaluatedIterator):
|
||||
def evaluator(node, environment):
|
||||
if evaluatedIterator.type == Type.MAP:
|
||||
results = []
|
||||
automaticVariableKey = cls._automaticNamedVariable(node.iterator, environment, "_")
|
||||
automaticVariableValue = cls._automaticNamedVariable(node.iterator, environment, "__")
|
||||
for k, v in evaluatedIterator.value.items():
|
||||
environment.scopes[-1][automaticVariableKey] = k
|
||||
environment.scopes[-1][automaticVariableValue] = v
|
||||
result = evaluate(node.statement, environment).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
if result is not None and result.type != Type.VOID:
|
||||
results.append(result)
|
||||
|
||||
del environment.scopes[-1][automaticVariableKey]
|
||||
del environment.scopes[-1][automaticVariableValue]
|
||||
|
||||
return EvaluationResult.OK(Type.list(results).decompose())
|
||||
|
||||
return EvaluationResult.FAIL()
|
||||
|
||||
return evaluator
|
||||
@@ -5,12 +5,12 @@ class BlockEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
def evaluator(cls, node, environment):
|
||||
environment.scopes.append({})
|
||||
environment.appendScope()
|
||||
|
||||
for child in node.children:
|
||||
evaluate(child, environment)
|
||||
|
||||
environment.scopes.pop(-1)
|
||||
environment.popScope()
|
||||
|
||||
#
|
||||
# def evaluateBlock(block, environment):
|
||||
|
||||
@@ -13,7 +13,7 @@ class LoopEvaluator(Evaluator):
|
||||
parameters = [ identifier.value for identifier in node.parameters ] if type(node.parameters) != NoneNode() else []
|
||||
|
||||
try:
|
||||
environment.scopes.append({})
|
||||
environment.appendScope()
|
||||
|
||||
output = {
|
||||
Type.INTEGER: cls.numberEvaluator,
|
||||
@@ -22,7 +22,7 @@ class LoopEvaluator(Evaluator):
|
||||
Type.MAP: cls.mapEvaluator
|
||||
}[iterator.type](node, environment, iterator, parameters, node.filter)
|
||||
|
||||
environment.scopes.pop(-1)
|
||||
environment.popScope()
|
||||
except KeyError:
|
||||
raise RuntimeException(f"The {iterator.type.name.lower()} type cannot stand as an iterator for loop statement", node.left.pos)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user