Reformat evaluator #1

This commit is contained in:
Bartłomiej Pluta
2019-07-04 17:57:12 +02:00
parent f0cbf37fe9
commit 34a0eda199
36 changed files with 470 additions and 270 deletions

57
smnp/runtime/evaluator.py Normal file
View File

@@ -0,0 +1,57 @@
from smnp.ast.node.access import AccessNode
from smnp.ast.node.assignment import AssignmentNode
from smnp.ast.node.asterisk import AsteriskNode
from smnp.ast.node.block import BlockNode
from smnp.ast.node.colon import ColonNode
from smnp.ast.node.function import FunctionDefinitionNode, FunctionCallNode
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.percent import PercentNode
from smnp.ast.node.program import Program
from smnp.ast.node.string import StringLiteralNode
def evaluate(input, environment):
from smnp.runtime.evaluators.access import evaluateAccess
from smnp.runtime.evaluators.assignment import evaluateAssignment
from smnp.runtime.evaluators.asterisk import evaluateAsterisk
from smnp.runtime.evaluators.block import evaluateBlock
from smnp.runtime.evaluators.colon import evaluateColon
from smnp.runtime.evaluators.function import evaluateFunctionDefinition, evaluateFunctionCall
from smnp.runtime.evaluators.identifier import evaluateIdentifier
from smnp.runtime.evaluators.integer import evaluateInteger
from smnp.runtime.evaluators.list import evaluateList
from smnp.runtime.evaluators.note import evaluateNote
from smnp.runtime.evaluators.percent import evaluatePercent
from smnp.runtime.evaluators.program import evaluateProgram
from smnp.runtime.evaluators.string import evaluateString
if isinstance(input, Program):
return evaluateProgram(input, environment)
if isinstance(input, IntegerLiteralNode):
return evaluateInteger(input, environment)
if isinstance(input, PercentNode):
return evaluatePercent(input, environment)
if isinstance(input, StringLiteralNode):
return evaluateString(input, environment)
if isinstance(input, NoteLiteralNode):
return evaluateNote(input, environment)
if isinstance(input, FunctionDefinitionNode):
return evaluateFunctionDefinition(input, environment)
if isinstance(input, FunctionCallNode):
return evaluateFunctionCall(input, environment)
if isinstance(input, AccessNode):
return evaluateAccess(input, environment)
if isinstance(input, BlockNode):
return evaluateBlock(input, environment)
if isinstance(input, ListNode):
return evaluateList(input, environment)
if isinstance(input, AssignmentNode):
return evaluateAssignment(input, environment)
if isinstance(input, AsteriskNode):
return evaluateAsterisk(input, environment)
if isinstance(input, ColonNode):
return evaluateColon(input, environment)
if isinstance(input, IdentifierNode):
return evaluateIdentifier(input, environment)

View File

View File

@@ -0,0 +1,33 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluators.list import evaluateList
def evaluateAccess(access, environment):
pass
#element = evaluate(access.element, environment)
# TODO: narazie tylko metody działają
#e = evaluateMethodCall(element, access.property, environment)
def evaluateMethodCall(element, functionCall, environment):
funcName = functionCall.identifier.identifier
arguments = evaluateList(functionCall.arguments, environment)
arguments.insert(0, element)
# for name, library in environment.customFunctions.items():
# if funcName == name:
# if len(library['params']) != len(arguments):
# raise RuntimeException(functionCall.pos, f"Calling '{funcName}' requires {len(library['params'])} and {len(arguments)} was passed")
# environment.scopes.append({ library['params'][i].identifier: v for i, v in enumerate(arguments) })
# returnValue = None
# for node in library['body']:
# if not isinstance(node, ReturnNode):
# evaluate(node, environment)
# else:
# returnValue = evaluateReturn(node, environment)
# environment.scopes.pop(-1)
# return returnValue
for name, definition in environment.methods[type(element)].items():
if name == funcName:
return definition(arguments, environment)
raise RuntimeException(f"Method '{funcName}' does not exist", functionCall.pos)

View File

@@ -0,0 +1,15 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import evaluate
from smnp.type.model import Type
def evaluateAssignment(assignment, environment):
target = assignment.target.identifier
value = evaluate(assignment.value, environment)
if value.type == Type.VOID:
raise RuntimeException(f"Expected expression, found '{value.type.name}'", assignment.value.pos)
scopeOfExistingVariable = environment.findVariableScope(target)
if scopeOfExistingVariable is not None:
scopeOfExistingVariable[target] = value
else:
environment.scopes[-1][target] = value

View File

@@ -0,0 +1,48 @@
from smnp.ast.node.identifier import IdentifierNode
from smnp.runtime.evaluator import evaluate
from smnp.type.model import Type
from smnp.type.value import Value
def evaluateAsterisk(asterisk, environment):
iterator = evaluate(asterisk.iterator, environment)
if iterator.type == Type.INTEGER:
evaluateAsteriskForNumber(asterisk, environment, iterator)
if iterator.type == Type.LIST:
evaluateAsteriskForList(asterisk, environment, iterator)
def evaluateAsteriskForNumber(asterisk, environment, count):
for i in range(count.value):
if type(asterisk.iterator) == IdentifierNode:
environment.scopes[-1][f"_{asterisk.iterator.identifier}"] = Value(Type.INTEGER, i+1)
else:
environment.scopes[-1]["_"] = Value(Type.INTEGER, i+1)
evaluate(asterisk.statement, environment)
if type(asterisk.iterator) == IdentifierNode:
del environment.scopes[-1][f"_{asterisk.iterator.identifier}"]
else:
del environment.scopes[-1]["_"]
def evaluateAsteriskForList(asterisk, environment, list):
for i, v in enumerate(list.value):
if type(asterisk.iterator) == IdentifierNode:
environment.scopes[-1][f"_{asterisk.iterator.identifier}"] = Value(Type.INTEGER, i+1)
environment.scopes[-1][f"{asterisk.iterator.identifier}_"] = v
else:
environment.scopes[-1]["_"] = Value(Type.INTEGER, i+1)
environment.scopes[-1]["__"] = v
evaluate(asterisk.statement, environment)
if type(asterisk.iterator) == IdentifierNode:
del environment.scopes[-1][f"_{asterisk.iterator.identifier}"]
del environment.scopes[-1][f"{asterisk.iterator.identifier}_"]
else:
del environment.scopes[-1]["_"]
del environment.scopes[-1]["__"]

View File

@@ -0,0 +1,11 @@
from smnp.runtime.evaluator import evaluate
from smnp.runtime.tools import flatListNode
def evaluateBlock(block, environment):
environment.scopes.append({})
for node in flatListNode(block):
evaluate(node, environment)
environment.scopes.pop(-1)

View File

@@ -0,0 +1,16 @@
from smnp.ast.node.integer import IntegerLiteralNode
from smnp.ast.node.note import NoteLiteralNode
from smnp.error.runtime import RuntimeException
from smnp.note.model import Note
from smnp.type.model import Type
from smnp.type.value import Value
def evaluateColon(colon, environment):
if isinstance(colon.a, NoteLiteralNode) and isinstance(colon.b, NoteLiteralNode):
return Value(Type.LIST, [Value(Type.NOTE, n) for n in Note.range(colon.a.value, colon.b.value)])
elif isinstance(colon.a, IntegerLiteralNode) and isinstance(colon.b, IntegerLiteralNode):
return Value(Type.LIST, [Value(Type.INTEGER, i) for i in range(colon.a.value, colon.b.value + 1)])
raise RuntimeException("Invalid colon arguments", colon.pos)

View File

@@ -0,0 +1,54 @@
from smnp.ast.node.identifier import IdentifierNode
from smnp.ast.node.program import Program
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluators.list import evaluateList
from smnp.runtime.tools import flatListNode
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):
functionName = functionCall.identifier.identifier
arguments = evaluateList(functionCall.arguments, environment).value
return environment.invokeFunction(functionName, arguments)
# def evaluateFunctionCall(functionCall, environment):
# funcName = functionCall.identifier.identifier
# arguments = evaluateList(functionCall.arguments, environment)
# for name, function in environment.customFunctions.items():
# if funcName == name:
# if len(function['params']) != len(arguments):
# raise RuntimeException(functionCall.pos, f"Calling '{funcName}' requires {len(function['params'])} and {len(arguments)} was passed")
# environment.scopes.append({ function['params'][i].identifier: v for i, v in enumerate(arguments) })
# returnValue = None
# for node in function['body']:
# if not isinstance(node, ReturnNode):
# evaluate(node, environment)
# else:
# returnValue = evaluateReturn(node, environment)
# environment.scopes.pop(-1)
# return returnValue
# for name, definition in environment.functions.items():
# if name == funcName:
# return definition(arguments, environment)
# raise RuntimeException(functionCall.pos, f"Function '{funcName}' does not exist")

View File

@@ -0,0 +1,10 @@
from smnp.error.runtime import RuntimeException
def evaluateIdentifier(identifier, environment):
try:
value = environment.findVariable(identifier.identifier)
return value
except RuntimeException as e:
e.pos = identifier.pos
raise e

View File

@@ -0,0 +1,6 @@
from smnp.type.model import Type
from smnp.type.value import Value
def evaluateInteger(integer, environment):
return Value(Type.INTEGER, integer.value)

View File

@@ -0,0 +1,17 @@
from smnp.error.runtime import RuntimeException
from smnp.runtime.evaluator import evaluate
from smnp.runtime.tools import flatListNode
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)

View File

@@ -0,0 +1,6 @@
from smnp.type.model import Type
from smnp.type.value import Value
def evaluateNote(note, environment):
return Value(Type.NOTE, note.value)

View File

@@ -0,0 +1,6 @@
from smnp.type.model import Type
from smnp.type.value import Value
def evaluatePercent(percent, environment):
return Value(Type.PERCENT, percent.value.value * 0.01)

View File

@@ -0,0 +1,6 @@
from smnp.runtime.evaluator import evaluate
def evaluateProgram(program, environment):
for node in program.children:
evaluate(node, environment)

View File

@@ -0,0 +1,5 @@
from smnp.runtime.evaluator import evaluate
def evaluateReturn(returnNode, environment):
return evaluate(returnNode.value, environment)

View File

@@ -0,0 +1,28 @@
import re
from smnp.error.runtime import RuntimeException
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)
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

13
smnp/runtime/tools.py Normal file
View File

@@ -0,0 +1,13 @@
def flatListNode(listNode):
if len(listNode.children[0].children) == 1:
return []
return _flatListNode(listNode.children[0], [])
def _flatListNode(listItemNode, list = []):
if len(listItemNode.children) == 2:
value = listItemNode.children[0]
next = listItemNode.children[1]
list.append(value)
_flatListNode(next, list)
return list