Add call stack to fix 'return' statement issue

This commit is contained in:
Bartłomiej Pluta
2019-07-10 12:18:13 +02:00
parent 48638b832b
commit d10df10282
5 changed files with 40 additions and 8 deletions

View File

@@ -11,6 +11,7 @@ class Environment():
self.methods = methods self.methods = methods
self.customFunctions = [] self.customFunctions = []
self.customMethods = [] self.customMethods = []
self.callStack = []
def invokeMethod(self, object, name, args): def invokeMethod(self, object, name, args):
builtinMethodResult = self._invokeBuiltinMethod(object, name, args) builtinMethodResult = self._invokeBuiltinMethod(object, name, args)
@@ -26,7 +27,9 @@ class Environment():
def _invokeBuiltinMethod(self, object, name, args): def _invokeBuiltinMethod(self, object, name, args):
for method in self.methods: for method in self.methods:
if method.name == name: if method.name == name:
self.callStack.append(CallStackItem(name))
ret = method.call(self, [object, *args]) ret = method.call(self, [object, *args])
self.callStack.pop(-1)
if ret is not None: if ret is not None:
return (True, ret) return (True, ret)
@@ -39,7 +42,9 @@ class Environment():
if signatureCheckresult[0]: if signatureCheckresult[0]:
self.scopes.append({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))}) self.scopes.append({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))})
self.scopes[-1][method.alias] = object self.scopes[-1][method.alias] = object
self.callStack.append(CallStackItem(name))
result = BodyEvaluator.evaluate(method.body, self).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult result = BodyEvaluator.evaluate(method.body, self).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
self.callStack.pop(-1)
self.scopes.pop(-1) self.scopes.pop(-1)
return (True, result) return (True, result)
raise IllegalFunctionInvocationException(f"{method.name}{method.signature.string}", raise IllegalFunctionInvocationException(f"{method.name}{method.signature.string}",
@@ -60,7 +65,9 @@ class Environment():
def _invokeBuiltinFunction(self, name, args): def _invokeBuiltinFunction(self, name, args):
for function in self.functions: for function in self.functions:
if function.name == name: if function.name == name:
self.callStack.append(CallStackItem(name))
ret = function.call(self, args) ret = function.call(self, args)
self.callStack.pop(-1)
if ret is not None: if ret is not None:
return (True, ret) return (True, ret)
@@ -72,7 +79,9 @@ class Environment():
signatureCheckresult = function.signature.check(args) signatureCheckresult = function.signature.check(args)
if signatureCheckresult[0]: if signatureCheckresult[0]:
self.scopes.append({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) }) self.scopes.append({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) })
self.callStack.append(CallStackItem(name))
result = BodyEvaluator.evaluate(function.body, self).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult result = BodyEvaluator.evaluate(function.body, self).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
self.callStack.pop(-1)
self.scopes.pop(-1) self.scopes.pop(-1)
return (True, result) return (True, result)
raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{types(args)}") raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{types(args)}")
@@ -138,6 +147,9 @@ class Environment():
def customMethodsToString(self): def customMethodsToString(self):
return "Custom Methods:\n" + ("\n".join([ f" {function.name}(...)" for function in self.customMethods ])) return "Custom Methods:\n" + ("\n".join([ f" {function.name}(...)" for function in self.customMethods ]))
def callStackToString(self):
return "Call Stack:\n" + ("\n".join([ f" [{i}]: {function.function}(...)" for i, function in reversed(list(enumerate(self.callStack))) ]))
def extend(self, environment): def extend(self, environment):
self.scopes[0].update(environment.scopes[0]) self.scopes[0].update(environment.scopes[0])
self.customFunctions.extend(environment.customFunctions) self.customFunctions.extend(environment.customFunctions)
@@ -145,12 +157,18 @@ class Environment():
def __str__(self): def __str__(self):
return f"{self.scopesToString()}\n{self.functionsToString()}\n{self.methodsToString()}\n{self.customFunctionsToString()}\n{self.customMethodsToString()}" return f"{self.scopesToString()}\n{self.functionsToString()}\n{self.methodsToString()}\n{self.customFunctionsToString()}\n{self.customMethodsToString()}\n{self.callStackToString()}"
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
class CallStackItem:
def __init__(self, function):
self.function = function
self.value = None
class CustomFunction: class CustomFunction:
def __init__(self, name, signature, arguments, body): def __init__(self, name, signature, arguments, body):
self.name = name self.name = name

View File

@@ -2,8 +2,9 @@ from smnp.error.base import SmnpException
class RuntimeException(SmnpException): class RuntimeException(SmnpException):
def __init__(self, msg, pos): def __init__(self, msg, pos, environment=None):
super().__init__(msg, pos) super().__init__(msg, pos)
self.environment = environment
def _title(self): def _title(self):
return "Runtime Error" return "Runtime Error"

View File

@@ -18,6 +18,9 @@ def _function(env, parameter):
elif parameter.value == "methods": elif parameter.value == "methods":
print(env.methodsToString()) print(env.methodsToString())
return return
elif parameter.value == "callstack":
print(env.callStackToString())
return
raise IllegalArgumentException(f"Unknown parameter '{parameter.value}'") raise IllegalArgumentException(f"Unknown parameter '{parameter.value}'")

View File

@@ -3,6 +3,7 @@ from smnp.ast.node.extend import ExtendNode
from smnp.ast.node.function import FunctionDefinitionNode from smnp.ast.node.function import FunctionDefinitionNode
from smnp.ast.node.imports import ImportNode from smnp.ast.node.imports import ImportNode
from smnp.ast.node.program import Program from smnp.ast.node.program import Program
from smnp.ast.node.ret import ReturnNode
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.type.model import Type from smnp.type.model import Type
@@ -73,12 +74,14 @@ def evaluate(node, environment):
from smnp.runtime.evaluators.extend import ExtendEvaluator from smnp.runtime.evaluators.extend import ExtendEvaluator
from smnp.runtime.evaluators.block import BlockEvaluator from smnp.runtime.evaluators.block import BlockEvaluator
from smnp.runtime.evaluators.imports import ImportEvaluator from smnp.runtime.evaluators.imports import ImportEvaluator
from smnp.runtime.evaluators.function import ReturnEvaluator
result = Evaluator.oneOf( result = Evaluator.oneOf(
Evaluator.forNodes(ProgramEvaluator.evaluate, Program), Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode), Evaluator.forNodes(ImportEvaluator.evaluate, ImportNode),
Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode), Evaluator.forNodes(FunctionDefinitionEvaluator.evaluate, FunctionDefinitionNode),
Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode), Evaluator.forNodes(ExtendEvaluator.evaluate, ExtendNode),
Evaluator.forNodes(BlockEvaluator.evaluate, BlockNode), Evaluator.forNodes(BlockEvaluator.evaluate, BlockNode),
Evaluator.forNodes(ReturnEvaluator.evaluate, ReturnNode),
expressionEvaluator() expressionEvaluator()
)(node, environment) )(node, environment)

View File

@@ -1,4 +1,3 @@
from smnp.ast.node.ret import ReturnNode
from smnp.error.runtime import RuntimeException from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator, evaluate from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
@@ -38,9 +37,17 @@ class BodyEvaluator(Evaluator):
@classmethod @classmethod
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
for child in node.children: for child in node.children:
if type(child) == ReturnNode: evaluate(child, environment)
x = expressionEvaluator(doAssert=True)(child.value, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult if environment.callStack[-1].value is not None:
return x return environment.callStack[-1].value
else:
evaluate(child, environment)
class ReturnEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
if len(environment.callStack) > 0:
returnValue = expressionEvaluator(doAssert=True)(node.value, environment)
environment.callStack[-1].value = returnValue.value
else:
raise RuntimeException("Cannot use 'return' statement outside a function or method", node.pos, environment)