Reformat evaluator #1
This commit is contained in:
57
smnp/runtime/evaluator.py
Normal file
57
smnp/runtime/evaluator.py
Normal 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)
|
||||
0
smnp/runtime/evaluators/__init__.py
Normal file
0
smnp/runtime/evaluators/__init__.py
Normal file
33
smnp/runtime/evaluators/access.py
Normal file
33
smnp/runtime/evaluators/access.py
Normal 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)
|
||||
15
smnp/runtime/evaluators/assignment.py
Normal file
15
smnp/runtime/evaluators/assignment.py
Normal 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
|
||||
48
smnp/runtime/evaluators/asterisk.py
Normal file
48
smnp/runtime/evaluators/asterisk.py
Normal 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]["__"]
|
||||
11
smnp/runtime/evaluators/block.py
Normal file
11
smnp/runtime/evaluators/block.py
Normal 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)
|
||||
|
||||
|
||||
16
smnp/runtime/evaluators/colon.py
Normal file
16
smnp/runtime/evaluators/colon.py
Normal 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)
|
||||
54
smnp/runtime/evaluators/function.py
Normal file
54
smnp/runtime/evaluators/function.py
Normal 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")
|
||||
10
smnp/runtime/evaluators/identifier.py
Normal file
10
smnp/runtime/evaluators/identifier.py
Normal 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
|
||||
6
smnp/runtime/evaluators/integer.py
Normal file
6
smnp/runtime/evaluators/integer.py
Normal 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)
|
||||
17
smnp/runtime/evaluators/list.py
Normal file
17
smnp/runtime/evaluators/list.py
Normal 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)
|
||||
|
||||
|
||||
6
smnp/runtime/evaluators/note.py
Normal file
6
smnp/runtime/evaluators/note.py
Normal 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)
|
||||
6
smnp/runtime/evaluators/percent.py
Normal file
6
smnp/runtime/evaluators/percent.py
Normal 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)
|
||||
6
smnp/runtime/evaluators/program.py
Normal file
6
smnp/runtime/evaluators/program.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from smnp.runtime.evaluator import evaluate
|
||||
|
||||
|
||||
def evaluateProgram(program, environment):
|
||||
for node in program.children:
|
||||
evaluate(node, environment)
|
||||
5
smnp/runtime/evaluators/ret.py
Normal file
5
smnp/runtime/evaluators/ret.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from smnp.runtime.evaluator import evaluate
|
||||
|
||||
|
||||
def evaluateReturn(returnNode, environment):
|
||||
return evaluate(returnNode.value, environment)
|
||||
28
smnp/runtime/evaluators/string.py
Normal file
28
smnp/runtime/evaluators/string.py
Normal 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
13
smnp/runtime/tools.py
Normal 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
|
||||
Reference in New Issue
Block a user