From ea28ab6235fd065aceb51864331b02712031b32a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Tue, 16 Jul 2019 10:18:00 +0200 Subject: [PATCH] Fix leaking scope of function to outer scope --- smnp/environment/environment.py | 15 +++- smnp/runtime/evaluators/assignment.py | 6 +- smnp/runtime/evaluators/asterisk.py | 104 -------------------------- smnp/runtime/evaluators/block.py | 4 +- smnp/runtime/evaluators/loop.py | 4 +- 5 files changed, 18 insertions(+), 115 deletions(-) delete mode 100644 smnp/runtime/evaluators/asterisk.py diff --git a/smnp/environment/environment.py b/smnp/environment/environment.py index 3a34531..786f15d 100644 --- a/smnp/environment/environment.py +++ b/smnp/environment/environment.py @@ -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) ])) diff --git a/smnp/runtime/evaluators/assignment.py b/smnp/runtime/evaluators/assignment.py index f9e9234..1edfe28 100644 --- a/smnp/runtime/evaluators/assignment.py +++ b/smnp/runtime/evaluators/assignment.py @@ -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 diff --git a/smnp/runtime/evaluators/asterisk.py b/smnp/runtime/evaluators/asterisk.py deleted file mode 100644 index 2d4223e..0000000 --- a/smnp/runtime/evaluators/asterisk.py +++ /dev/null @@ -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 \ No newline at end of file diff --git a/smnp/runtime/evaluators/block.py b/smnp/runtime/evaluators/block.py index c4f4c6e..af0b99b 100644 --- a/smnp/runtime/evaluators/block.py +++ b/smnp/runtime/evaluators/block.py @@ -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): diff --git a/smnp/runtime/evaluators/loop.py b/smnp/runtime/evaluators/loop.py index eafb343..c386db8 100644 --- a/smnp/runtime/evaluators/loop.py +++ b/smnp/runtime/evaluators/loop.py @@ -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)