From 1e634180d64385597d6ea5d56436e7a81ced55c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Mon, 8 Jul 2019 14:10:10 +0200 Subject: [PATCH] Create evaluator for function call --- smnp/ast/node/identifier.py | 4 +- smnp/ast/node/invocation.py | 2 +- smnp/library/model.py | 6 +-- smnp/library/signature.py | 4 +- smnp/runtime/evaluator.py | 4 +- smnp/runtime/evaluators/expression.py | 40 ++++++++++----- smnp/runtime/evaluators/function.py | 73 ++++++++++++++------------- smnp/runtime/evaluators/list.py | 4 +- smnp/token/tokenizers/string.py | 2 +- smnp/type/model.py | 4 +- 10 files changed, 81 insertions(+), 62 deletions(-) diff --git a/smnp/ast/node/identifier.py b/smnp/ast/node/identifier.py index f85ec7c..0edd0ab 100644 --- a/smnp/ast/node/identifier.py +++ b/smnp/ast/node/identifier.py @@ -1,7 +1,7 @@ from smnp.ast.node.access import AccessNode from smnp.ast.node.assignment import AssignmentNode from smnp.ast.node.expression import ExpressionNode -from smnp.ast.node.invocation import FunctionCall, ArgumentsListNode +from smnp.ast.node.invocation import FunctionCallNode, ArgumentsListNode from smnp.ast.parser import Parser from smnp.token.type import TokenType @@ -37,7 +37,7 @@ class IdentifierNode(AccessNode): @staticmethod def _functionCallParser(): def createNode(name, arguments): - node = FunctionCall(name.pos) + node = FunctionCallNode(name.pos) node.name = name node.arguments = arguments return node diff --git a/smnp/ast/node/invocation.py b/smnp/ast/node/invocation.py index 4dfe708..f9ab2f3 100644 --- a/smnp/ast/node/invocation.py +++ b/smnp/ast/node/invocation.py @@ -14,7 +14,7 @@ class ArgumentsListNode(Node): Parser.doAssert(ExpressionNode.parse, "expression"))(input) -class FunctionCall(AccessNode): +class FunctionCallNode(AccessNode): def __init__(self, pos): super().__init__(pos) diff --git a/smnp/library/model.py b/smnp/library/model.py index 89a6b7f..7a05d4c 100644 --- a/smnp/library/model.py +++ b/smnp/library/model.py @@ -55,7 +55,7 @@ def types(args): if arg.type == Type.LIST: output.append(listTypes(arg.value, [])) else: - output.append(arg.type.name) + output.append(arg.type.name.lower()) return f"({', '.join(output)})" @@ -64,5 +64,5 @@ def listTypes(l, output=[]): if item.type == Type.LIST: output.append(listTypes(item.value, [])) else: - output.append(item.type.name) - return f"LIST<{'|'.join(set(output))}>" \ No newline at end of file + output.append(item.type.name.lower()) + return f"{Type.LIST.name.lower()}<{'|'.join(set(output))}>" \ No newline at end of file diff --git a/smnp/library/signature.py b/smnp/library/signature.py index 77833b3..7c44aae 100644 --- a/smnp/library/signature.py +++ b/smnp/library/signature.py @@ -92,14 +92,14 @@ def allTypes(): def ofTypes(*types): def check(value): return value.type in types - return Matcher(None, check, f"<{'|'.join([t.name for t in types])}>") + return Matcher(None, check, f"<{', '.join([t.name.lower() for t in types])}>") def listOf(*types): def check(value): return len([item for item in value.value if not item.type in types]) == 0 - return Matcher(Type.LIST, check, f"{Type.LIST.name}<{'|'.join([t.name for t in types])}>") + return Matcher(Type.LIST, check, f"{Type.LIST.name.lower()}<{', '.join([t.name.lower() for t in types])}>") def listMatches(*pattern): diff --git a/smnp/runtime/evaluator.py b/smnp/runtime/evaluator.py index 9081d18..8578d82 100644 --- a/smnp/runtime/evaluator.py +++ b/smnp/runtime/evaluator.py @@ -64,11 +64,11 @@ class EvaluationResult(): def evaluate(node, environment): from smnp.runtime.evaluators.program import ProgramEvaluator - from smnp.runtime.evaluators.expression import evaluateExpression + from smnp.runtime.evaluators.expression import expressionEvaluator result = Evaluator.oneOf( Evaluator.forNodes(ProgramEvaluator.evaluate, Program), - evaluateExpression + expressionEvaluator() )(node, environment) if not result.result: diff --git a/smnp/runtime/evaluators/expression.py b/smnp/runtime/evaluators/expression.py index 9df4d86..76bdab9 100644 --- a/smnp/runtime/evaluators/expression.py +++ b/smnp/runtime/evaluators/expression.py @@ -1,22 +1,36 @@ from smnp.ast.node.identifier import IdentifierNode from smnp.ast.node.integer import IntegerLiteralNode +from smnp.ast.node.invocation import FunctionCallNode from smnp.ast.node.list import ListNode from smnp.ast.node.note import NoteLiteralNode from smnp.ast.node.string import StringLiteralNode +from smnp.error.runtime import RuntimeException from smnp.runtime.evaluator import Evaluator +from smnp.type.model import Type -def evaluateExpression(node, environment): - from smnp.runtime.evaluators.string import StringEvaluator - from smnp.runtime.evaluators.integer import IntegerEvaluator - from smnp.runtime.evaluators.note import NoteEvaluator - from smnp.runtime.evaluators.identifier import IdentifierEvaluator - from smnp.runtime.evaluators.list import ListEvaluator +def expressionEvaluator(doAssert=False): + def evaluateExpression(node, environment): + from smnp.runtime.evaluators.string import StringEvaluator + from smnp.runtime.evaluators.integer import IntegerEvaluator + from smnp.runtime.evaluators.note import NoteEvaluator + from smnp.runtime.evaluators.identifier import IdentifierEvaluator + from smnp.runtime.evaluators.list import ListEvaluator + from smnp.runtime.evaluators.function import FunctionCallEvaluator + + result = Evaluator.oneOf( + Evaluator.forNodes(StringEvaluator.evaluate, StringLiteralNode), + Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteralNode), + Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteralNode), + Evaluator.forNodes(IdentifierEvaluator.evaluate, IdentifierNode), + Evaluator.forNodes(ListEvaluator.evaluate, ListNode), + Evaluator.forNodes(FunctionCallEvaluator.evaluate, FunctionCallNode) + )(node, environment) + + if doAssert and result.result and result.value.type == Type.VOID: + raise RuntimeException(f"Expected expression", node.pos) + + return result + + return evaluateExpression - return Evaluator.oneOf( - Evaluator.forNodes(StringEvaluator.evaluate, StringLiteralNode), - Evaluator.forNodes(IntegerEvaluator.evaluate, IntegerLiteralNode), - Evaluator.forNodes(NoteEvaluator.evaluate, NoteLiteralNode), - Evaluator.forNodes(IdentifierEvaluator.evaluate, IdentifierNode), - Evaluator.forNodes(ListEvaluator.evaluate, ListNode) - )(node, environment) \ No newline at end of file diff --git a/smnp/runtime/evaluators/function.py b/smnp/runtime/evaluators/function.py index 1009351..01d5741 100644 --- a/smnp/runtime/evaluators/function.py +++ b/smnp/runtime/evaluators/function.py @@ -1,40 +1,45 @@ -from smnp.ast.node.identifier import IdentifierNode -from smnp.ast.node.program import Program -from smnp.error.base import SmnpException -from smnp.error.runtime import RuntimeException -from smnp.runtime.evaluators.list import evaluateList -from smnp.runtime.tools import flatListNode +from smnp.runtime.evaluator import Evaluator +from smnp.runtime.evaluators.expression import expressionEvaluator +from smnp.runtime.evaluators.iterable import abstractIterableEvaluator -def evaluateFunctionDefinition(definition, environment): - name = definition.name - params = list([p for p in flatListNode(definition.parameters)]) - body = definition.body +class FunctionCallEvaluator(Evaluator): - if not isinstance(definition.parent, Program): - raise RuntimeException(f"Functions can be defined only on the top level of script", name.pos) - - for p in params: - if not isinstance(p, IdentifierNode): - raise RuntimeException("Parameter of function definition must be an identifier", p.pos, ) - - if name.identifier in environment.customFunctions or name.identifier in environment.functions: - raise RuntimeException(f"Function '{name.identifier}' already exists", name.pos) - - environment.customFunctions[name.identifier] = { - 'params': params, - 'body': flatListNode(body) - } - - -def evaluateFunctionCall(functionCall, environment): - try: - functionName = functionCall.identifier.identifier - arguments = evaluateList(functionCall.arguments, environment).value - return environment.invokeFunction(functionName, arguments) - except SmnpException as e: - e.pos = functionCall.pos - raise e + @classmethod + def evaluator(cls, node, environment): + name = node.name.value + arguments = abstractIterableEvaluator(expressionEvaluator(True))(node.arguments, environment) + return environment.invokeFunction(name, arguments) +# +# def evaluateFunctionDefinition(definition, environment): +# name = definition.name +# params = list([p for p in flatListNode(definition.parameters)]) +# body = definition.body +# +# if not isinstance(definition.parent, Program): +# raise RuntimeException(f"Functions can be defined only on the top level of script", name.pos) +# +# for p in params: +# if not isinstance(p, IdentifierNode): +# raise RuntimeException("Parameter of function definition must be an identifier", p.pos, ) +# +# if name.identifier in environment.customFunctions or name.identifier in environment.functions: +# raise RuntimeException(f"Function '{name.identifier}' already exists", name.pos) +# +# environment.customFunctions[name.identifier] = { +# 'params': params, +# 'body': flatListNode(body) +# } +# +# +# def evaluateFunctionCall(functionCall, environment): +# try: +# functionName = functionCall.identifier.identifier +# arguments = evaluateList(functionCall.arguments, environment).value +# return environment.invokeFunction(functionName, arguments) +# except SmnpException as e: +# e.pos = functionCall.pos +# raise e # def evaluateFunctionCall(functionCall, environment): diff --git a/smnp/runtime/evaluators/list.py b/smnp/runtime/evaluators/list.py index f675b6e..cf9fb87 100644 --- a/smnp/runtime/evaluators/list.py +++ b/smnp/runtime/evaluators/list.py @@ -1,5 +1,5 @@ from smnp.runtime.evaluator import Evaluator -from smnp.runtime.evaluators.expression import evaluateExpression +from smnp.runtime.evaluators.expression import expressionEvaluator from smnp.runtime.evaluators.iterable import abstractIterableEvaluator from smnp.type.model import Type from smnp.type.value import Value @@ -9,5 +9,5 @@ class ListEvaluator(Evaluator): @classmethod def evaluator(cls, node, environment): - list = abstractIterableEvaluator(evaluateExpression)(node, environment) + list = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(node, environment) return Value(Type.LIST, list) diff --git a/smnp/token/tokenizers/string.py b/smnp/token/tokenizers/string.py index a07a98e..4135036 100644 --- a/smnp/token/tokenizers/string.py +++ b/smnp/token/tokenizers/string.py @@ -13,5 +13,5 @@ def stringTokenizer(input, current, line): char = input[current + consumedChars] value += char consumedChars += 1 - return (consumedChars, Token(TokenType.STRING, value, (line, current))) + return (consumedChars, Token(TokenType.STRING, value[1:len(value)-1], (line, current))) return (0, None) diff --git a/smnp/type/model.py b/smnp/type/model.py index 73a2122..c54949e 100644 --- a/smnp/type/model.py +++ b/smnp/type/model.py @@ -7,7 +7,7 @@ from smnp.note.model import Note class Type(Enum): INTEGER = (int, lambda x: str(x)) STRING = (str, lambda x: x) - LIST = (list, lambda x: f"({', '.join([e.stringify() for e in x])})") + LIST = (list, lambda x: f"[{', '.join([e.stringify() for e in x])}]") PERCENT = (float, lambda x: f"{int(x * 100)}%") NOTE = (Note, lambda x: x.note.name) VOID = (type(None), lambda x: _failStringify(Type.VOID)) @@ -17,7 +17,7 @@ class Type(Enum): def _failStringify(t): - raise RuntimeException(f"Not able to interpret {t.name}'") + raise RuntimeException(f"Not able to interpret {t.name}'", None)