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.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

View File

@@ -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)

View File

@@ -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))}>"
output.append(item.type.name.lower())
return f"{Type.LIST.name.lower()}<{'|'.join(set(output))}>"

View File

@@ -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):

View File

@@ -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:

View File

@@ -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 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
return Evaluator.oneOf(
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(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

View File

@@ -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):

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)