Create evaluators for optional arguments in function and method definitions

This commit is contained in:
Bartłomiej Pluta
2019-07-13 23:52:15 +02:00
parent 69bac69946
commit 460deb4981
6 changed files with 41 additions and 18 deletions

View File

@@ -40,7 +40,8 @@ class Environment():
if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu if method.typeSignature.check([object])[0] and method.name == name: #Todo sprawdzic sygnature typu
signatureCheckresult = method.signature.check(args) signatureCheckresult = method.signature.check(args)
if signatureCheckresult[0]: 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.scopes[-1][method.alias] = object
self.callStack.append(CallStackItem(name)) self.callStack.append(CallStackItem(name))
result = Type.void() result = Type.void()
@@ -80,7 +81,8 @@ 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({ 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)) self.callStack.append(CallStackItem(name))
result = Type.void() result = Type.void()
try: try:
@@ -93,11 +95,11 @@ class Environment():
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)
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: if len([fun for fun in self.functions + self.customFunctions if fun.name == name]) > 0:
raise RuntimeException(f"Cannot redeclare function '{name}'", None) 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: # TODO:
# There is still problem with checking existing of generic types, like lists: # There is still problem with checking existing of generic types, like lists:
@@ -108,14 +110,14 @@ class Environment():
# function foo() { return 2 } # function foo() { return 2 }
# } # }
# Then calling [1, 2, 3, 4].foo() will produce 1, when the second method is more suitable # 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: 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) 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: 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) 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): def findVariable(self, name, type=None, pos=None):
for scope in reversed(self.scopes): for scope in reversed(self.scopes):
@@ -175,18 +177,20 @@ class CallStackItem:
class CustomFunction: class CustomFunction:
def __init__(self, name, signature, arguments, body): def __init__(self, name, signature, arguments, body, defaultArgs):
self.name = name self.name = name
self.signature = signature self.signature = signature
self.arguments = arguments self.arguments = arguments
self.body = body self.body = body
self.defaultArgs = defaultArgs
class CustomMethod: 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.typeSignature = typeSignature
self.alias = alias self.alias = alias
self.name = name self.name = name
self.signature = signature self.signature = signature
self.arguments = arguments self.arguments = arguments
self.body = body self.body = body
self.defaultArgs = defaultArgs

View File

@@ -1,14 +1,13 @@
import sys import sys
from smnp.error.base import SmnpException from smnp.error.base import SmnpException
from smnp.library.loader import loadStandardLibrary
from smnp.program.interpreter import Interpreter from smnp.program.interpreter import Interpreter
def main(): def main():
try: try:
stdLibraryEnv = loadStandardLibrary() #stdLibraryEnv = loadStandardLibrary()
Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=True, execute=True, baseEnvironment=stdLibraryEnv) Interpreter.interpretFile(sys.argv[1], printTokens=False, printAst=False, execute=True, baseEnvironment=None)
#draft() #draft()
#tokens = tokenize(['function a(b...) { x+y}']) #tokens = tokenize(['function a(b...) { x+y}'])
#FunctionDefinitionParser(tokens).node.print() #FunctionDefinitionParser(tokens).node.print()

View File

@@ -1,7 +1,8 @@
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.function.signature import signature from smnp.function.signature import signature
from smnp.runtime.evaluator import Evaluator 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.model import Type
from smnp.type.signature.matcher.type import ofType from smnp.type.signature.matcher.type import ofType
@@ -34,6 +35,7 @@ class ExtendEvaluator(Evaluator):
name = node.name.value name = node.name.value
signature = argumentsNodeToMethodSignature(node.arguments) signature = argumentsNodeToMethodSignature(node.arguments)
arguments = [arg.variable.value for arg in node.arguments] arguments = [arg.variable.value for arg in node.arguments]
defaultArguments = evaluateDefaultArguments(node.arguments, environment)
body = node.body body = node.body
environment.addCustomMethod(type, variable, name, signature, arguments, body) environment.addCustomMethod(type, variable, name, signature, arguments, body, defaultArguments)

View File

@@ -3,7 +3,7 @@ from smnp.runtime.evaluator import Evaluator, evaluate
from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.runtime.tools.error import updatePos 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 from smnp.type.model import Type
@@ -27,8 +27,9 @@ class FunctionDefinitionEvaluator(Evaluator):
name = node.name.value name = node.name.value
signature = argumentsNodeToMethodSignature(node.arguments) signature = argumentsNodeToMethodSignature(node.arguments)
arguments = [ arg.variable.value for arg in node.arguments ] arguments = [ arg.variable.value for arg in node.arguments ]
defaultArguments = evaluateDefaultArguments(node.arguments, environment)
body = node.body body = node.body
environment.addCustomFunction(name, signature, arguments, body) environment.addCustomFunction(name, signature, arguments, body, defaultArguments)
except RuntimeException as e: except RuntimeException as e:
raise updatePos(e, node) raise updatePos(e, node)

View File

@@ -2,7 +2,8 @@ from smnp.ast.node import type as ast
from smnp.ast.node.none import NoneNode from smnp.ast.node.none import NoneNode
from smnp.ast.node.type import TypesList from smnp.ast.node.type import TypesList
from smnp.error.runtime import RuntimeException 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.runtime.tools.error import updatePos
from smnp.type.model import Type from smnp.type.model import Type
from smnp.type.signature.matcher.list import listOfMatchers 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 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): def argumentsNodeToMethodSignature(node):
try: try:
sign = [] sign = []
vararg = None vararg = None
argumentsCount = len(node.children) argumentsCount = len(node.children)
checkPositionOfOptionalArguments(node)
for i, child in enumerate(node.children): for i, child in enumerate(node.children):
matchers = { matchers = {
ast.Type: (lambda c: c.type, typeMatcher), 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) raise RuntimeException("Vararg must be the last argument in signature", child.pos)
vararg = evaluatedMatcher vararg = evaluatedMatcher
else: else:
if type(child.optionalValue) != NoneNode:
evaluatedMatcher = optional(evaluatedMatcher)
sign.append(evaluatedMatcher) sign.append(evaluatedMatcher)
@@ -35,6 +44,14 @@ def argumentsNodeToMethodSignature(node):
raise updatePos(e, 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): def multipleTypeMatcher(typeNode):
subSignature = [] subSignature = []