From 460deb49819b759fea238d7c43f2929884111f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Sat, 13 Jul 2019 23:52:15 +0200 Subject: [PATCH] Create evaluators for optional arguments in function and method definitions --- smnp/environment/environment.py | 22 +++++++++++++--------- smnp/main.py | 5 ++--- smnp/runtime/evaluators/extend.py | 6 ++++-- smnp/runtime/evaluators/function.py | 5 +++-- smnp/runtime/tools/signature.py | 19 ++++++++++++++++++- smnp/type/signature/matcher/type.py | 2 +- 6 files changed, 41 insertions(+), 18 deletions(-) diff --git a/smnp/environment/environment.py b/smnp/environment/environment.py index fff3799..3a34531 100644 --- a/smnp/environment/environment.py +++ b/smnp/environment/environment.py @@ -40,7 +40,8 @@ class Environment(): if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu signatureCheckresult = method.signature.check(args) if signatureCheckresult[0]: - self.scopes.append({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))}) + self.scopes.append(method.defaultArgs) + self.scopes[-1].update({argName: argValue for argName, argValue in zip(method.arguments, list(signatureCheckresult[1:]))}) self.scopes[-1][method.alias] = object self.callStack.append(CallStackItem(name)) result = Type.void() @@ -80,7 +81,8 @@ class Environment(): if function.name == name: signatureCheckresult = function.signature.check(args) if signatureCheckresult[0]: - self.scopes.append({ argName: argValue for argName, argValue in zip(function.arguments, list(signatureCheckresult[1:])) }) + self.scopes.append(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() try: @@ -93,11 +95,11 @@ class Environment(): raise IllegalFunctionInvocationException(f"{function.name}{function.signature.string}", f"{name}{argsTypesToString(args)}") return (False, None) - def addCustomFunction(self, name, signature, arguments, body): + def addCustomFunction(self, name, signature, arguments, body, defaultArguments): if len([fun for fun in self.functions + self.customFunctions if fun.name == name]) > 0: raise RuntimeException(f"Cannot redeclare function '{name}'", None) - self.customFunctions.append(CustomFunction(name, signature, arguments, body)) + self.customFunctions.append(CustomFunction(name, signature, arguments, body, defaultArguments)) # TODO: # There is still problem with checking existing of generic types, like lists: @@ -108,14 +110,14 @@ class Environment(): # function foo() { return 2 } # } # Then calling [1, 2, 3, 4].foo() will produce 1, when the second method is more suitable - def addCustomMethod(self, typeSignature, alias, name, signature, arguments, body): + def addCustomMethod(self, typeSignature, alias, name, signature, arguments, body, defaultArguments): if len([m for m in self.methods if m.name == name and m.signature.matchers[0] == typeSignature.matchers[0]]) > 0: raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None) if len([m for m in self.customMethods if m.name == name and m.typeSignature.matchers[0] == typeSignature.matchers[0]]) > 0: raise RuntimeException(f"Cannot redeclare method '{name}' for type '{typeSignature.matchers[0]}'", None) - self.customMethods.append(CustomMethod(typeSignature, alias, name, signature, arguments, body)) + self.customMethods.append(CustomMethod(typeSignature, alias, name, signature, arguments, body, defaultArguments)) def findVariable(self, name, type=None, pos=None): for scope in reversed(self.scopes): @@ -175,18 +177,20 @@ class CallStackItem: class CustomFunction: - def __init__(self, name, signature, arguments, body): + def __init__(self, name, signature, arguments, body, defaultArgs): self.name = name self.signature = signature self.arguments = arguments self.body = body + self.defaultArgs = defaultArgs class CustomMethod: - def __init__(self, typeSignature, alias, name, signature, arguments, body): + def __init__(self, typeSignature, alias, name, signature, arguments, body, defaultArgs): self.typeSignature = typeSignature self.alias = alias self.name = name self.signature = signature self.arguments = arguments - self.body = body \ No newline at end of file + self.body = body + self.defaultArgs = defaultArgs \ No newline at end of file diff --git a/smnp/main.py b/smnp/main.py index e68357a..2bfbec8 100644 --- a/smnp/main.py +++ b/smnp/main.py @@ -1,14 +1,13 @@ import sys from smnp.error.base import SmnpException -from smnp.library.loader import loadStandardLibrary from smnp.program.interpreter import Interpreter def main(): try: - stdLibraryEnv = loadStandardLibrary() - Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=True, execute=True, baseEnvironment=stdLibraryEnv) + #stdLibraryEnv = loadStandardLibrary() + Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=False, execute=True, baseEnvironment=None) #draft() #tokens = tokenize(['function a(b...) { x+y}']) #FunctionDefinitionParser(tokens).node.print() diff --git a/smnp/runtime/evaluators/extend.py b/smnp/runtime/evaluators/extend.py index 546487c..81fb45d 100644 --- a/smnp/runtime/evaluators/extend.py +++ b/smnp/runtime/evaluators/extend.py @@ -1,7 +1,8 @@ from smnp.ast.node.none import NoneNode from smnp.function.signature import signature from smnp.runtime.evaluator import Evaluator -from smnp.runtime.tools.signature import argumentsNodeToMethodSignature, listSpecifier, mapSpecifier +from smnp.runtime.tools.signature import argumentsNodeToMethodSignature, listSpecifier, mapSpecifier, \ + evaluateDefaultArguments from smnp.type.model import Type from smnp.type.signature.matcher.type import ofType @@ -34,6 +35,7 @@ class ExtendEvaluator(Evaluator): name = node.name.value signature = argumentsNodeToMethodSignature(node.arguments) arguments = [arg.variable.value for arg in node.arguments] + defaultArguments = evaluateDefaultArguments(node.arguments, environment) body = node.body - environment.addCustomMethod(type, variable, name, signature, arguments, body) + environment.addCustomMethod(type, variable, name, signature, arguments, body, defaultArguments) diff --git a/smnp/runtime/evaluators/function.py b/smnp/runtime/evaluators/function.py index 378598a..39fc792 100644 --- a/smnp/runtime/evaluators/function.py +++ b/smnp/runtime/evaluators/function.py @@ -3,7 +3,7 @@ from smnp.runtime.evaluator import Evaluator, evaluate from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.iterable import abstractIterableEvaluator from smnp.runtime.tools.error import updatePos -from smnp.runtime.tools.signature import argumentsNodeToMethodSignature +from smnp.runtime.tools.signature import argumentsNodeToMethodSignature, evaluateDefaultArguments from smnp.type.model import Type @@ -27,8 +27,9 @@ class FunctionDefinitionEvaluator(Evaluator): name = node.name.value signature = argumentsNodeToMethodSignature(node.arguments) arguments = [ arg.variable.value for arg in node.arguments ] + defaultArguments = evaluateDefaultArguments(node.arguments, environment) body = node.body - environment.addCustomFunction(name, signature, arguments, body) + environment.addCustomFunction(name, signature, arguments, body, defaultArguments) except RuntimeException as e: raise updatePos(e, node) diff --git a/smnp/runtime/tools/signature.py b/smnp/runtime/tools/signature.py index baf8d35..f6d8248 100644 --- a/smnp/runtime/tools/signature.py +++ b/smnp/runtime/tools/signature.py @@ -2,7 +2,8 @@ from smnp.ast.node import type as ast from smnp.ast.node.none import NoneNode from smnp.ast.node.type import TypesList from smnp.error.runtime import RuntimeException -from smnp.function.signature import varargSignature, signature +from smnp.function.signature import varargSignature, signature, optional +from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.tools.error import updatePos from smnp.type.model import Type from smnp.type.signature.matcher.list import listOfMatchers @@ -10,11 +11,17 @@ from smnp.type.signature.matcher.map import mapOfMatchers from smnp.type.signature.matcher.type import allTypes, oneOf, ofType +def evaluateDefaultArguments(node, environment): + defaultValues = { arg.variable.value: expressionEvaluator(doAssert=True)(arg.optionalValue, environment).value for arg in node.children if type(arg.optionalValue) != NoneNode } + return defaultValues + + def argumentsNodeToMethodSignature(node): try: sign = [] vararg = None argumentsCount = len(node.children) + checkPositionOfOptionalArguments(node) for i, child in enumerate(node.children): matchers = { ast.Type: (lambda c: c.type, typeMatcher), @@ -27,6 +34,8 @@ def argumentsNodeToMethodSignature(node): raise RuntimeException("Vararg must be the last argument in signature", child.pos) vararg = evaluatedMatcher else: + if type(child.optionalValue) != NoneNode: + evaluatedMatcher = optional(evaluatedMatcher) sign.append(evaluatedMatcher) @@ -35,6 +44,14 @@ def argumentsNodeToMethodSignature(node): raise updatePos(e, node) +def checkPositionOfOptionalArguments(node): + firstOptional = next((i for i, v in enumerate(node.children) if type(v.optionalValue) != NoneNode), None) #next(filter(lambda arg: type(arg.optionalValue) != NoneNode, node.children), None) + if firstOptional is not None: + regularAfterOptional = next((i for i, v in enumerate(node.children[firstOptional:]) if type(v.optionalValue) == NoneNode), None) + if regularAfterOptional is not None: + raise RuntimeException(f"Optional arguments should be declared at the end of the arguments list", node.children[regularAfterOptional].pos) + + def multipleTypeMatcher(typeNode): subSignature = [] diff --git a/smnp/type/signature/matcher/type.py b/smnp/type/signature/matcher/type.py index 8416a74..f374186 100644 --- a/smnp/type/signature/matcher/type.py +++ b/smnp/type/signature/matcher/type.py @@ -25,4 +25,4 @@ def oneOf(*matchers): def check(value): return any(matcher.match(value) for matcher in matchers) - return Matcher(None, check, f"<{', '.join(m.string for m in matchers)}>") \ No newline at end of file + return Matcher(None, check, f"<{', '.join(m.string for m in matchers)}>")