Create evaluator for function call
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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))}>"
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user