Fix leaking scope of function to outer scope

This commit is contained in:
Bartłomiej Pluta
2019-07-16 10:18:00 +02:00
parent 6e9e252b86
commit ea28ab6235
5 changed files with 18 additions and 115 deletions

View File

@@ -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) ]))

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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)