Create evaluators for literals, list and identifier nodes

This commit is contained in:
Bartłomiej Pluta
2019-07-08 13:39:07 +02:00
parent fd9f240ce5
commit 6e42ac0f91
10 changed files with 166 additions and 53 deletions

View File

@@ -16,10 +16,8 @@ def main():
ast = parse(tokens)
ast.print()
sys.exit(0)
env = createEnvironment()
evaluate(ast, env)

View File

@@ -1,2 +1,78 @@
def evaluate(input, environment):
pass
from smnp.ast.node.program import Program
from smnp.error.runtime import RuntimeException
from smnp.type.model import Type
from smnp.type.value import Value
class Evaluator:
@classmethod
def evaluator(cls, node, environment):
pass
@classmethod
def evaluate(cls, node, environment):
result = cls.evaluator(node, environment)
if result is None:
return EvaluationResult.OK(Value(Type.VOID, None))
return EvaluationResult.OK(result)
@staticmethod
def forNodes(evaluator, *nodes):
def nodeEvaluator(node, environment):
if type(node) in nodes:
return evaluator(node, environment)
return EvaluationResult.FAIL()
return nodeEvaluator
@staticmethod
def oneOf(*evaluators):
def combinedEvaluator(node, environment):
for evaluator in evaluators:
result = evaluator(node, environment)
if result.result:
return result
return EvaluationResult.FAIL()
return combinedEvaluator
class EvaluationResult():
def __init__(self, result, value):
if result and value is None:
raise RuntimeError("Value musn't be None if result is set to True for EvaluationResult")
if type(value) == EvaluationResult:
raise RuntimeError(f"Nested EvaluationResult detected. Trying to create EvaluationResult with value = {value}")
self.result = result
self.value = value
@staticmethod
def FAIL():
return EvaluationResult(False, None)
@staticmethod
def OK(value):
return EvaluationResult(True, value)
def __str__(self):
return f"{'OK' if self.result else 'FAILED'}[{self.value}]"
def evaluate(node, environment):
from smnp.runtime.evaluators.program import ProgramEvaluator
from smnp.runtime.evaluators.expression import evaluateExpression
result = Evaluator.oneOf(
Evaluator.forNodes(ProgramEvaluator.evaluate, Program),
evaluateExpression
)(node, environment)
if not result.result:
raise RuntimeException("Cannot evaluate program", node.pos)
return result

View File

@@ -0,0 +1,22 @@
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.integer import IntegerLiteralNode
from smnp.ast.node.list import ListNode
from smnp.ast.node.note import NoteLiteralNode
from smnp.ast.node.string import StringLiteralNode
from smnp.runtime.evaluator import Evaluator
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
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,10 +1,13 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
def evaluateIdentifier(identifier, environment):
try:
value = environment.findVariable(identifier.identifier)
return value
except RuntimeException as e:
e.pos = identifier.pos
raise e
class IdentifierEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
try:
return environment.findVariable(node.value)
except RuntimeException as e:
e.pos = node.pos
raise e

View File

@@ -1,6 +1,10 @@
from smnp.runtime.evaluator import Evaluator
from smnp.type.model import Type
from smnp.type.value import Value
def evaluateInteger(integer, environment):
return Value(Type.INTEGER, integer.value)
class IntegerEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Value(Type.INTEGER, node.value)

View File

@@ -0,0 +1,10 @@
def abstractIterableEvaluator(itemEvaluator):
def evaluator(node, environment):
evaluatedItems = []
for item in node.children:
result = itemEvaluator(item, environment)
evaluatedItems.append(result.value)
return evaluatedItems
return evaluator

View File

@@ -1,17 +1,13 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import evaluate
from smnp.runtime.tools import flatListNode
from smnp.runtime.evaluator import Evaluator
from smnp.runtime.evaluators.expression import evaluateExpression
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
from smnp.type.model import Type
from smnp.type.value import Value
def evaluateList(list, environment):
newList = []
for elem in flatListNode(list):
item = evaluate(elem, environment)
if item.type == Type.VOID:
raise RuntimeException(f"Expected expression, found '{item.type.name}'", elem.pos)
newList.append(item)
return Value(Type.LIST, newList)
class ListEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
list = abstractIterableEvaluator(evaluateExpression)(node, environment)
return Value(Type.LIST, list)

View File

@@ -1,6 +1,10 @@
from smnp.runtime.evaluator import Evaluator
from smnp.type.model import Type
from smnp.type.value import Value
def evaluateNote(note, environment):
return Value(Type.NOTE, note.value)
class NoteEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Value(Type.NOTE, node.value)

View File

@@ -1,6 +1,9 @@
from smnp.runtime.evaluator import evaluate
from smnp.runtime.evaluator import Evaluator, evaluate
def evaluateProgram(program, environment):
for node in program.children:
evaluate(node, environment)
class ProgramEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
for n in node.children:
evaluate(n, environment)

View File

@@ -1,28 +1,25 @@
import re
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import Evaluator
from smnp.type.model import Type
from smnp.type.value import Value
def evaluateString(string, environment):
value = interpolate(string, environment)
return Value(Type.STRING, value)
class StringEvaluator(Evaluator):
@classmethod
def evaluator(cls, node, environment):
return Value(Type.STRING, node.value)
def interpolate(string, environment):
interpolated = string.value
for scope in reversed(environment.scopes):
for name, value in scope.items():
interpolated = interpolated.replace('{' + name + '}', value.stringify())
nonMatchedVariables = re.findall(r"\{\w+\}", interpolated)
if len(nonMatchedVariables) > 0:
raise RuntimeException(f"Variable '{nonMatchedVariables[0][1:len(nonMatchedVariables[0])-1]}' is not declared",
(string.pos[0], string.pos[1] + string.value.find(nonMatchedVariables[0])+1))
return interpolated
# or scope in reversed(environment.scopes):
# for k, v in scope.items():
# value = value.replace('{' + k + '}', v) #TODO: poprawic
# TODO: make use of complex interpreter for code inside '{' and '}'
# def interpolate(string, environment):
# interpolated = string.value
# for scope in reversed(environment.scopes):
# for name, value in scope.items():
# interpolated = interpolated.replace('{' + name + '}', value.stringify())
#
# nonMatchedVariables = re.findall(r"\{\w+\}", interpolated)
# if len(nonMatchedVariables) > 0:
# raise RuntimeException(f"Variable '{nonMatchedVariables[0][1:len(nonMatchedVariables[0])-1]}' is not declared",
# (string.pos[0], string.pos[1] + string.value.find(nonMatchedVariables[0])+1))
#
# return interpolated
#