Fix leaking scope of function to outer scope
This commit is contained in:
@@ -81,7 +81,7 @@ class Environment():
|
|||||||
if function.name == name:
|
if function.name == name:
|
||||||
signatureCheckresult = function.signature.check(args)
|
signatureCheckresult = function.signature.check(args)
|
||||||
if signatureCheckresult[0]:
|
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.scopes[-1].update({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) })
|
||||||
self.callStack.append(CallStackItem(name))
|
self.callStack.append(CallStackItem(name))
|
||||||
result = Type.void()
|
result = Type.void()
|
||||||
@@ -90,7 +90,7 @@ class Environment():
|
|||||||
except Return as r:
|
except Return as r:
|
||||||
result = r.value
|
result = r.value
|
||||||
self.callStack.pop(-1)
|
self.callStack.pop(-1)
|
||||||
self.scopes.pop(-1)
|
self.popScope(mergeVariables=False)
|
||||||
return (True, result)
|
return (True, result)
|
||||||
raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}")
|
raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}")
|
||||||
return (False, None)
|
return (False, None)
|
||||||
@@ -140,6 +140,17 @@ class Environment():
|
|||||||
else:
|
else:
|
||||||
return scope
|
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):
|
def scopesToString(self):
|
||||||
return "Scopes:\n" + ("\n".join([ f" [{i}]: {scope}" for i, scope in enumerate(self.scopes) ]))
|
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):
|
def evaluator(cls, node, environment):
|
||||||
target = node.left.value
|
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
|
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)
|
environment.scopes[-1][target] = value
|
||||||
if scopeOfExistingVariable is None:
|
|
||||||
environment.scopes[-1][target] = value
|
|
||||||
else:
|
|
||||||
scopeOfExistingVariable[target] = value
|
|
||||||
|
|
||||||
return 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
|
@classmethod
|
||||||
def evaluator(cls, node, environment):
|
def evaluator(cls, node, environment):
|
||||||
environment.scopes.append({})
|
environment.appendScope()
|
||||||
|
|
||||||
for child in node.children:
|
for child in node.children:
|
||||||
evaluate(child, environment)
|
evaluate(child, environment)
|
||||||
|
|
||||||
environment.scopes.pop(-1)
|
environment.popScope()
|
||||||
|
|
||||||
#
|
#
|
||||||
# def evaluateBlock(block, environment):
|
# 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 []
|
parameters = [ identifier.value for identifier in node.parameters ] if type(node.parameters) != NoneNode() else []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
environment.scopes.append({})
|
environment.appendScope()
|
||||||
|
|
||||||
output = {
|
output = {
|
||||||
Type.INTEGER: cls.numberEvaluator,
|
Type.INTEGER: cls.numberEvaluator,
|
||||||
@@ -22,7 +22,7 @@ class LoopEvaluator(Evaluator):
|
|||||||
Type.MAP: cls.mapEvaluator
|
Type.MAP: cls.mapEvaluator
|
||||||
}[iterator.type](node, environment, iterator, parameters, node.filter)
|
}[iterator.type](node, environment, iterator, parameters, node.filter)
|
||||||
|
|
||||||
environment.scopes.pop(-1)
|
environment.popScope()
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise RuntimeException(f"The {iterator.type.name.lower()} type cannot stand as an iterator for loop statement", node.left.pos)
|
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