diff --git a/smnp/runtime/evaluators/expression.py b/smnp/runtime/evaluators/expression.py index e0758fa..2418f70 100644 --- a/smnp/runtime/evaluators/expression.py +++ b/smnp/runtime/evaluators/expression.py @@ -50,3 +50,15 @@ def expressionEvaluator(doAssert=False): return evaluateExpression + + +def expressionEvaluatorWithMatcher(matcher, exceptionProvider, doAssert=True): + def evaluate(node, environment): + value = expressionEvaluator(doAssert=doAssert)(node, environment).value + + if not matcher.match(value): + raise exceptionProvider(value) + + return value + + return evaluate \ No newline at end of file diff --git a/smnp/runtime/evaluators/extend.py b/smnp/runtime/evaluators/extend.py index 81fb45d..7f8a546 100644 --- a/smnp/runtime/evaluators/extend.py +++ b/smnp/runtime/evaluators/extend.py @@ -1,8 +1,7 @@ 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, \ - evaluateDefaultArguments +from smnp.runtime.tools.signature import argumentsNodeToMethodSignature, listSpecifier, mapSpecifier from smnp.type.model import Type from smnp.type.signature.matcher.type import ofType @@ -33,9 +32,8 @@ class ExtendEvaluator(Evaluator): @classmethod def _evaluateMethodDefinition(cls, node, environment, type, variable): name = node.name.value - signature = argumentsNodeToMethodSignature(node.arguments) + defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment) 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, defaultArguments) diff --git a/smnp/runtime/evaluators/function.py b/smnp/runtime/evaluators/function.py index 39fc792..6b59ca1 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, evaluateDefaultArguments +from smnp.runtime.tools.signature import argumentsNodeToMethodSignature from smnp.type.model import Type @@ -25,9 +25,8 @@ class FunctionDefinitionEvaluator(Evaluator): def evaluator(cls, node, environment): try: name = node.name.value - signature = argumentsNodeToMethodSignature(node.arguments) + defaultArguments, signature = argumentsNodeToMethodSignature(node.arguments, environment) arguments = [ arg.variable.value for arg in node.arguments ] - defaultArguments = evaluateDefaultArguments(node.arguments, environment) body = node.body environment.addCustomFunction(name, signature, arguments, body, defaultArguments) except RuntimeException as e: diff --git a/smnp/runtime/tools/signature.py b/smnp/runtime/tools/signature.py index f6d8248..3cef1b5 100644 --- a/smnp/runtime/tools/signature.py +++ b/smnp/runtime/tools/signature.py @@ -3,7 +3,7 @@ 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, optional -from smnp.runtime.evaluators.expression import expressionEvaluator +from smnp.runtime.evaluators.expression import expressionEvaluator, expressionEvaluatorWithMatcher from smnp.runtime.tools.error import updatePos from smnp.type.model import Type from smnp.type.signature.matcher.list import listOfMatchers @@ -13,15 +13,17 @@ 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, environment): try: sign = [] vararg = None argumentsCount = len(node.children) checkPositionOfOptionalArguments(node) + defaultArgs = {} for i, child in enumerate(node.children): matchers = { ast.Type: (lambda c: c.type, typeMatcher), @@ -29,17 +31,22 @@ def argumentsNodeToMethodSignature(node): TypesList: (lambda c: c, multipleTypeMatcher) } evaluatedMatcher = matchers[type(child.type)][1](matchers[type(child.type)][0](child)) + if child.vararg: if i != argumentsCount - 1: raise RuntimeException("Vararg must be the last argument in signature", child.pos) vararg = evaluatedMatcher else: if type(child.optionalValue) != NoneNode: + defaultArgs[child.variable.value] = expressionEvaluatorWithMatcher( + evaluatedMatcher, + exceptionProvider=lambda value: RuntimeException( + f"Value '{value.stringify()}' doesn't match declared type: {evaluatedMatcher.string}", child.optionalValue.pos) + )(child.optionalValue, environment) evaluatedMatcher = optional(evaluatedMatcher) sign.append(evaluatedMatcher) - - return varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign) + return defaultArgs, (varargSignature(vararg, *sign, wrapVarargInValue=True) if vararg is not None else signature(*sign)) except RuntimeException as e: raise updatePos(e, node)