Create evaluator for function call

This commit is contained in:
Bartłomiej Pluta
2019-07-08 14:10:10 +02:00
parent 6e42ac0f91
commit 1e634180d6
10 changed files with 81 additions and 62 deletions

View File

@@ -1,7 +1,7 @@
from smnp.ast.node.access import AccessNode from smnp.ast.node.access import AccessNode
from smnp.ast.node.assignment import AssignmentNode from smnp.ast.node.assignment import AssignmentNode
from smnp.ast.node.expression import ExpressionNode 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.ast.parser import Parser
from smnp.token.type import TokenType from smnp.token.type import TokenType
@@ -37,7 +37,7 @@ class IdentifierNode(AccessNode):
@staticmethod @staticmethod
def _functionCallParser(): def _functionCallParser():
def createNode(name, arguments): def createNode(name, arguments):
node = FunctionCall(name.pos) node = FunctionCallNode(name.pos)
node.name = name node.name = name
node.arguments = arguments node.arguments = arguments
return node return node

View File

@@ -14,7 +14,7 @@ class ArgumentsListNode(Node):
Parser.doAssert(ExpressionNode.parse, "expression"))(input) Parser.doAssert(ExpressionNode.parse, "expression"))(input)
class FunctionCall(AccessNode): class FunctionCallNode(AccessNode):
def __init__(self, pos): def __init__(self, pos):
super().__init__(pos) super().__init__(pos)

View File

@@ -55,7 +55,7 @@ def types(args):
if arg.type == Type.LIST: if arg.type == Type.LIST:
output.append(listTypes(arg.value, [])) output.append(listTypes(arg.value, []))
else: else:
output.append(arg.type.name) output.append(arg.type.name.lower())
return f"({', '.join(output)})" return f"({', '.join(output)})"
@@ -64,5 +64,5 @@ def listTypes(l, output=[]):
if item.type == Type.LIST: if item.type == Type.LIST:
output.append(listTypes(item.value, [])) output.append(listTypes(item.value, []))
else: else:
output.append(item.type.name) output.append(item.type.name.lower())
return f"LIST<{'|'.join(set(output))}>" return f"{Type.LIST.name.lower()}<{'|'.join(set(output))}>"

View File

@@ -92,14 +92,14 @@ def allTypes():
def ofTypes(*types): def ofTypes(*types):
def check(value): def check(value):
return value.type in types 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 listOf(*types):
def check(value): def check(value):
return len([item for item in value.value if not item.type in types]) == 0 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): def listMatches(*pattern):

View File

@@ -64,11 +64,11 @@ class EvaluationResult():
def evaluate(node, environment): def evaluate(node, environment):
from smnp.runtime.evaluators.program import ProgramEvaluator from smnp.runtime.evaluators.program import ProgramEvaluator
from smnp.runtime.evaluators.expression import evaluateExpression from smnp.runtime.evaluators.expression import expressionEvaluator
result = Evaluator.oneOf( result = Evaluator.oneOf(
Evaluator.forNodes(ProgramEvaluator.evaluate, Program), Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
evaluateExpression expressionEvaluator()
)(node, environment) )(node, environment)
if not result.result: if not result.result:

View File

@@ -1,22 +1,36 @@
from smnp.ast.node.identifier import IdentifierNode from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.integer import IntegerLiteralNode 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.list import ListNode
from smnp.ast.node.note import NoteLiteralNode from smnp.ast.node.note import NoteLiteralNode
from smnp.ast.node.string import StringLiteralNode from smnp.ast.node.string import StringLiteralNode
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator from smnp.runtime.evaluator import Evaluator
from smnp.type.model import Type
def evaluateExpression(node, environment): def expressionEvaluator(doAssert=False):
from smnp.runtime.evaluators.string import StringEvaluator def evaluateExpression(node, environment):
from smnp.runtime.evaluators.integer import IntegerEvaluator from smnp.runtime.evaluators.string import StringEvaluator
from smnp.runtime.evaluators.note import NoteEvaluator from smnp.runtime.evaluators.integer import IntegerEvaluator
from smnp.runtime.evaluators.identifier import IdentifierEvaluator from smnp.runtime.evaluators.note import NoteEvaluator
from smnp.runtime.evaluators.list import ListEvaluator 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)

View File

@@ -1,40 +1,45 @@
from smnp.ast.node.identifier import IdentifierNode from smnp.runtime.evaluator import Evaluator
from smnp.ast.node.program import Program from smnp.runtime.evaluators.expression import expressionEvaluator
from smnp.error.base import SmnpException from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluators.list import evaluateList
from smnp.runtime.tools import flatListNode
def evaluateFunctionDefinition(definition, environment): class FunctionCallEvaluator(Evaluator):
name = definition.name
params = list([p for p in flatListNode(definition.parameters)])
body = definition.body
if not isinstance(definition.parent, Program): @classmethod
raise RuntimeException(f"Functions can be defined only on the top level of script", name.pos) def evaluator(cls, node, environment):
name = node.name.value
for p in params: arguments = abstractIterableEvaluator(expressionEvaluator(True))(node.arguments, environment)
if not isinstance(p, IdentifierNode): return environment.invokeFunction(name, arguments)
raise RuntimeException("Parameter of function definition must be an identifier", p.pos, ) #
# def evaluateFunctionDefinition(definition, environment):
if name.identifier in environment.customFunctions or name.identifier in environment.functions: # name = definition.name
raise RuntimeException(f"Function '{name.identifier}' already exists", name.pos) # params = list([p for p in flatListNode(definition.parameters)])
# body = definition.body
environment.customFunctions[name.identifier] = { #
'params': params, # if not isinstance(definition.parent, Program):
'body': flatListNode(body) # 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):
def evaluateFunctionCall(functionCall, environment): # raise RuntimeException("Parameter of function definition must be an identifier", p.pos, )
try: #
functionName = functionCall.identifier.identifier # if name.identifier in environment.customFunctions or name.identifier in environment.functions:
arguments = evaluateList(functionCall.arguments, environment).value # raise RuntimeException(f"Function '{name.identifier}' already exists", name.pos)
return environment.invokeFunction(functionName, arguments) #
except SmnpException as e: # environment.customFunctions[name.identifier] = {
e.pos = functionCall.pos # 'params': params,
raise e # '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): # def evaluateFunctionCall(functionCall, environment):

View File

@@ -1,5 +1,5 @@
from smnp.runtime.evaluator import Evaluator 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.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.type.model import Type from smnp.type.model import Type
from smnp.type.value import Value from smnp.type.value import Value
@@ -9,5 +9,5 @@ class ListEvaluator(Evaluator):
@classmethod @classmethod
def evaluator(cls, node, environment): def evaluator(cls, node, environment):
list = abstractIterableEvaluator(evaluateExpression)(node, environment) list = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(node, environment)
return Value(Type.LIST, list) return Value(Type.LIST, list)

View File

@@ -13,5 +13,5 @@ def stringTokenizer(input, current, line):
char = input[current + consumedChars] char = input[current + consumedChars]
value += char value += char
consumedChars += 1 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) return (0, None)

View File

@@ -7,7 +7,7 @@ from smnp.note.model import Note
class Type(Enum): class Type(Enum):
INTEGER = (int, lambda x: str(x)) INTEGER = (int, lambda x: str(x))
STRING = (str, lambda x: 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)}%") PERCENT = (float, lambda x: f"{int(x * 100)}%")
NOTE = (Note, lambda x: x.note.name) NOTE = (Note, lambda x: x.note.name)
VOID = (type(None), lambda x: _failStringify(Type.VOID)) VOID = (type(None), lambda x: _failStringify(Type.VOID))
@@ -17,7 +17,7 @@ class Type(Enum):
def _failStringify(t): def _failStringify(t):
raise RuntimeException(f"Not able to interpret {t.name}'") raise RuntimeException(f"Not able to interpret {t.name}'", None)