Reformat evaluator #1
This commit is contained in:
@@ -1,204 +0,0 @@
|
||||
from AST import *
|
||||
from Note import Note
|
||||
from Error import RuntimeException
|
||||
|
||||
def evaluateProgram(program, environment):
|
||||
for node in program.children:
|
||||
evaluate(node, environment)
|
||||
|
||||
def evaluateInteger(integer, environment):
|
||||
return integer.value
|
||||
|
||||
def evaluatePercent(percent, environment):
|
||||
return percent.value.value * 0.01
|
||||
|
||||
def evaluateIdentifier(identifier, environment):
|
||||
value = environment.findVariable(identifier.identifier)
|
||||
return value
|
||||
|
||||
def evaluateString(string, environment):
|
||||
value = string.value
|
||||
for scope in reversed(environment.scopes):
|
||||
for k, v in scope.items():
|
||||
value = value.replace('{' + k + '}', objectString(v)) #TODO: poprawic
|
||||
return value
|
||||
|
||||
|
||||
|
||||
def evaluateNote(note, environment):
|
||||
return note.value
|
||||
|
||||
def evaluateFunctionDefinition(definition, environment):
|
||||
name = definition.name
|
||||
params = list([p for p in flatListNode(definition.parameters) if not isinstance(p, CommaNode)])
|
||||
body = definition.body
|
||||
|
||||
if not isinstance(definition.parent, Program):
|
||||
raise RuntimeException(name.pos, f"Functions can be defined only on the top level of script")
|
||||
|
||||
for p in params:
|
||||
if not isinstance(p, IdentifierNode):
|
||||
raise RuntimeException(p.pos, "Parameter of function definition must be an identifier")
|
||||
|
||||
if name.identifier in environment.customFunctions or name.identifier in environment.functions:
|
||||
raise RuntimeException(name.pos, f"Function '{name.identifier}' already exists")
|
||||
|
||||
environment.customFunctions[name.identifier] = {
|
||||
'params': params,
|
||||
'body': flatListNode(body)
|
||||
}
|
||||
|
||||
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:
|
||||
child1 = listItemNode.children[0]
|
||||
child2 = listItemNode.children[1]
|
||||
list.append(child1)
|
||||
_flatListNode(child2, list)
|
||||
return list
|
||||
|
||||
def evaluateAccess(access, environment):
|
||||
|
||||
element = evaluate(access.element, environment)
|
||||
#TODO: narazie tylko metody działają
|
||||
e = evaluateMethodCall(element, access.property, environment)
|
||||
return e
|
||||
|
||||
def evaluateMethodCall(element, functionCall, environment):
|
||||
funcName = functionCall.identifier.identifier
|
||||
arguments = evaluateList(functionCall.arguments, environment)
|
||||
arguments.insert(0, element)
|
||||
#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.methods[type(element)].items():
|
||||
if name == funcName:
|
||||
return definition(arguments, environment)
|
||||
raise RuntimeException(functionCall.pos, f"Method '{funcName}' does not exist")
|
||||
|
||||
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")
|
||||
|
||||
def evaluateReturn(returnNode, environment):
|
||||
return evaluate(returnNode.value, environment)
|
||||
|
||||
def evaluateComma(comma, environment):
|
||||
pass
|
||||
|
||||
def evaluateBlock(block, environment):
|
||||
environment.scopes.append({})
|
||||
for node in flatListNode(block):
|
||||
evaluate(node, environment)
|
||||
environment.scopes.pop(-1)
|
||||
|
||||
def evaluateList(list, environment):
|
||||
return [evaluate(e, environment) for e in flatListNode(list) if not isinstance(e, CommaNode)]
|
||||
|
||||
def evaluateAssignment(assignment, environment):
|
||||
target = assignment.target.identifier
|
||||
value = evaluate(assignment.value, environment)
|
||||
scopeOfExistingVariable = environment.findVariableScope(target)
|
||||
if scopeOfExistingVariable is not None:
|
||||
scopeOfExistingVariable[target] = value
|
||||
else:
|
||||
environment.scopes[-1][target] = value
|
||||
|
||||
def evaluateAsterisk(asterisk, environment):
|
||||
count = evaluate(asterisk.iterator, environment)
|
||||
if isinstance(count, int):
|
||||
for i in range(count):
|
||||
if isinstance(asterisk.iterator, IdentifierNode):
|
||||
environment.scopes[-1][f"_{asterisk.iterator.identifier}"] = i+1
|
||||
else:
|
||||
environment.scopes[-1]["_"] = i+1
|
||||
evaluate(asterisk.statement, environment)
|
||||
if isinstance(asterisk.iterator, IdentifierNode):
|
||||
del environment.scopes[-1][f"_{asterisk.iterator.identifier}"]
|
||||
else:
|
||||
environment.scopes[-1]["_"] = i+1
|
||||
elif isinstance(count, list):
|
||||
for i, v in enumerate(count):
|
||||
if isinstance(asterisk.iterator, IdentifierNode):
|
||||
environment.scopes[-1][f"_{asterisk.iterator.identifier}"] = i+1
|
||||
environment.scopes[-1][f"{asterisk.iterator.identifier}_"] = v
|
||||
else:
|
||||
environment.scopes[-1]["_"] = i+1
|
||||
environment.scopes[-1]["__"] = v
|
||||
evaluate(asterisk.statement, environment)
|
||||
if isinstance(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]["__"]
|
||||
|
||||
def evaluateColon(colon, environment):
|
||||
if isinstance(colon.a, NoteLiteralNode) and isinstance(colon.b, NoteLiteralNode):
|
||||
return Note.range(colon.a.value, colon.b.value)
|
||||
elif isinstance(colon.a, IntegerLiteralNode) and isinstance(colon.b, IntegerLiteralNode):
|
||||
return list(range(colon.a.value, colon.b.value+1))
|
||||
raise RuntimeException(colon.pos, "Invalid colon arguments")
|
||||
|
||||
def evaluate(input, environment):
|
||||
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, CommaNode):
|
||||
return evaluateComma(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)
|
||||
@@ -1,4 +1,5 @@
|
||||
from smnp.ast.node.block import BlockNode, CloseBlockNode, BlockItemNode
|
||||
from smnp.ast.parsers.statement import parseStatement
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from smnp.ast.node.colon import ColonNode
|
||||
from smnp.ast.parsers.expression import parseExpression
|
||||
from smnp.error.syntax import SyntaxException
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
@@ -11,7 +12,7 @@ def parseColon(expr1, input, parent):
|
||||
expr2 = parseExpression(input, parent)
|
||||
|
||||
if expr2 is None:
|
||||
raise SyntaxException(input.current().pos, f"Expected expression '{input.current().value}'")
|
||||
raise SyntaxException(f"Expected expression '{input.current().value}'", input.current().pos)
|
||||
colon = ColonNode(expr1, expr2, parent, token.pos)
|
||||
expr1.parent = colon
|
||||
expr2.parent = colon
|
||||
|
||||
@@ -7,6 +7,6 @@ def parseToken(input, parent):
|
||||
value = combineParsers([ parseStatement ])(input, parent)
|
||||
|
||||
if value is None:
|
||||
raise SyntaxException(None, "Unknown statement") # TODO
|
||||
raise SyntaxException("Unknown statement") # TODO
|
||||
|
||||
return value
|
||||
@@ -15,9 +15,9 @@ def rollup(parser):
|
||||
|
||||
def assertToken(expected, input):
|
||||
if not input.hasCurrent():
|
||||
raise SyntaxException(None, f"Expected '{expected}'")
|
||||
raise SyntaxException(f"Expected '{expected}'")
|
||||
if expected != input.current().type:
|
||||
raise SyntaxException(input.current().pos, f"Expected '{expected}', found '{input.current().value}'")
|
||||
raise SyntaxException(f"Expected '{expected}', found '{input.current().value}'", input.current().pos)
|
||||
|
||||
|
||||
def combineParsers(parsers):
|
||||
|
||||
@@ -36,8 +36,8 @@ class Environment():
|
||||
return value
|
||||
else:
|
||||
return value
|
||||
raise RuntimeException(pos, f"Variable '{name}' is not declared" + (
|
||||
"" if type is None else f" (expected type: {type})"))
|
||||
raise RuntimeException(f"Variable '{name}' is not declared" + (
|
||||
"" if type is None else f" (expected type: {type})"), pos)
|
||||
|
||||
def findVariableScope(self, name, type=None):
|
||||
for scope in reversed(self.scopes):
|
||||
@@ -47,3 +47,19 @@ class Environment():
|
||||
return scope
|
||||
else:
|
||||
return scope
|
||||
|
||||
def scopesToString(self):
|
||||
return "Scopes:\n" + ("\n".join([ f" [{i}]: {scope}" for i, scope in enumerate(self.scopes) ]))
|
||||
|
||||
def functionsToString(self):
|
||||
return "Functions:\n" + ("\n".join([ f" {function.name}(...)" for function in self.functions ]))
|
||||
|
||||
def methodsToString(self):
|
||||
return "Methods:\n" + ("\n".join([f" {function.name}(...)" for function in self.methods]))
|
||||
|
||||
|
||||
def __str__(self):
|
||||
return self.scopesToString() + self.functionsToString() + self.methodsToString()
|
||||
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
@@ -1,6 +1,8 @@
|
||||
from smnp.environment.environment import Environment
|
||||
from smnp.library.function import display, sleep, semitones, interval, combine, flat, wait, rand, tuplet, synth, pause, \
|
||||
transpose, type, exit, duration, octave
|
||||
transpose, type, exit, duration, octave, debug
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
def createEnvironment():
|
||||
@@ -18,7 +20,8 @@ def createEnvironment():
|
||||
tuplet.function,
|
||||
synth.function,
|
||||
pause.function,
|
||||
transpose.function
|
||||
transpose.function,
|
||||
debug.function
|
||||
]
|
||||
|
||||
methods = [
|
||||
@@ -27,7 +30,7 @@ def createEnvironment():
|
||||
]
|
||||
|
||||
variables = {
|
||||
"bpm": 120
|
||||
"bpm": Value(Type.INTEGER, 120)
|
||||
}
|
||||
|
||||
return Environment([ variables ], functions, methods)
|
||||
|
||||
@@ -1,2 +1,13 @@
|
||||
class SmnpException(Exception):
|
||||
def __init__(self, msg, pos):
|
||||
self.msg = msg
|
||||
self.pos = pos
|
||||
|
||||
def _title(self):
|
||||
pass
|
||||
|
||||
def _position(self):
|
||||
return "" if self.pos is None else f" [line {self.pos[0]+1}, col {self.pos[1]+1}]"
|
||||
|
||||
def message(self):
|
||||
return f"{self._title()}{self._position()}:\n{self.msg}"
|
||||
|
||||
@@ -2,20 +2,32 @@ from smnp.error.base import SmnpException
|
||||
|
||||
|
||||
class IllegalFunctionInvocationException(SmnpException):
|
||||
def __init__(self, expected, found):
|
||||
self.msg = f"Illegal function invocation\n\nExpected signature:\n{expected}\n\nFound:\n{found}"
|
||||
def __init__(self, expected, found, pos=None):
|
||||
super().__init__(f"Expected signature:\n{expected}\n\nFound:\n{found}", pos)
|
||||
|
||||
def _title(self):
|
||||
return "Invocation Error"
|
||||
|
||||
|
||||
class FunctionNotFoundException(SmnpException):
|
||||
def __init__(self, function):
|
||||
self.msg = f"Function '{function}' not found"
|
||||
def __init__(self, function, pos=None):
|
||||
super().__init__(f"Function '{function}' not found", pos)
|
||||
|
||||
def _title(self):
|
||||
return "Invocation Error"
|
||||
|
||||
|
||||
class MethodNotFoundException(SmnpException):
|
||||
def __init__(self, object, method):
|
||||
self.msg = f"Method '{method}' of type '{object}' not found"
|
||||
def __init__(self, object, method, pos=None):
|
||||
super().__init__(f"Method '{method}' of type '{object}' not found", pos)
|
||||
|
||||
def _title(self):
|
||||
return "Invocation Error"
|
||||
|
||||
|
||||
class IllegalArgumentException(SmnpException):
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
def __init__(self, msg, pos=None):
|
||||
super().__init__(msg, pos)
|
||||
|
||||
def _title(self):
|
||||
return "Argument Error"
|
||||
@@ -2,6 +2,12 @@ from smnp.error.base import SmnpException
|
||||
|
||||
|
||||
class RuntimeException(SmnpException):
|
||||
def __init__(self, pos, msg):
|
||||
posStr = "" if pos is None else f" [line {pos[0]+1}, col {pos[1]+1}]"
|
||||
self.msg = f"Runtime error{posStr}:\n{msg}"
|
||||
def __init__(self, msg, pos):
|
||||
super().__init__(msg, pos)
|
||||
|
||||
def _title(self):
|
||||
return "Runtime Error"
|
||||
|
||||
# def message(self):
|
||||
# posStr = "" if self.pos is None else f" [line {self.pos[0] + 1}, col {self.pos[1] + 1}]"
|
||||
# return f"Runtime error{posStr}:\n{self.mmsg}"
|
||||
|
||||
@@ -2,6 +2,9 @@ from smnp.error.base import SmnpException
|
||||
|
||||
|
||||
class SyntaxException(SmnpException):
|
||||
def __init__(self, pos, msg):
|
||||
posStr = "" if pos is None else f" [line {pos[0]+1}, col {pos[1]+1}]"
|
||||
self.msg = f"Syntax error{posStr}:\n{msg}"
|
||||
def __init__(self, msg, pos=None):
|
||||
super().__init__(msg, pos)
|
||||
|
||||
def _title(self):
|
||||
return "Syntax Error"
|
||||
|
||||
|
||||
24
smnp/library/function/debug.py
Normal file
24
smnp/library/function/debug.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from smnp.error.function import IllegalArgumentException
|
||||
from smnp.library.model import Function
|
||||
from smnp.library.signature import signature, ofTypes
|
||||
from smnp.type.model import Type
|
||||
|
||||
_signature = signature(ofTypes(Type.STRING))
|
||||
def _function(env, parameter):
|
||||
if parameter.value == "environment":
|
||||
print(env)
|
||||
return
|
||||
elif parameter.value == "variables":
|
||||
print(env.scopesToString())
|
||||
return
|
||||
elif parameter.value == "functions":
|
||||
print(env.functionsToString())
|
||||
return
|
||||
elif parameter.value == "methods":
|
||||
print(env.methodsToString())
|
||||
return
|
||||
|
||||
raise IllegalArgumentException(f"Unknown parameter '{parameter.value}'")
|
||||
|
||||
|
||||
function = Function(_signature, _function, 'debug')
|
||||
@@ -7,7 +7,7 @@ from smnp.type.model import Type
|
||||
_signature = signature(ofTypes(Type.INTEGER))
|
||||
def _function(env, value):
|
||||
bpm = env.findVariable('bpm')
|
||||
player.pause(value.value, bpm)
|
||||
player.pause(value.value, bpm.value)
|
||||
|
||||
|
||||
function = Function(_signature, _function, 'pause')
|
||||
@@ -8,7 +8,7 @@ _signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
||||
def _function1(env, vararg):
|
||||
notes = [arg.value for arg in vararg]
|
||||
bpm = env.findVariable('bpm')
|
||||
playNotes(notes, bpm)
|
||||
playNotes(notes, bpm.value)
|
||||
|
||||
|
||||
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
||||
|
||||
@@ -18,6 +18,6 @@ def _function2(env, n, m, notes):
|
||||
|
||||
function = CombinedFunction(
|
||||
'tuplet',
|
||||
Function(_function1, _function1),
|
||||
Function(_function2, _function2)
|
||||
Function(_signature1, _function1),
|
||||
Function(_signature2, _function2)
|
||||
)
|
||||
|
||||
27
smnp/main.py
27
smnp/main.py
@@ -1,29 +1,26 @@
|
||||
import sys
|
||||
from smnp.error.syntax import SyntaxException
|
||||
from smnp.error.runtime import RuntimeException
|
||||
from smnp.token.tokenizer import tokenize
|
||||
|
||||
from smnp.ast.parser import parse
|
||||
#from Tokenizer import tokenize
|
||||
#from Evaluator import evaluate
|
||||
#from Environment import createEnvironment
|
||||
#from Error import SyntaxException, RuntimeException
|
||||
from smnp.environment.factory import createEnvironment
|
||||
from smnp.error.base import SmnpException
|
||||
from smnp.runtime.evaluator import evaluate
|
||||
from smnp.token.tokenizer import tokenize
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
with open(sys.argv[1], 'r') as source:
|
||||
lines = [line.rstrip('\n') for line in source.readlines()]
|
||||
|
||||
#env = createEnvironment()
|
||||
|
||||
tokens = tokenize(lines)
|
||||
|
||||
ast = parse(tokens)
|
||||
|
||||
#evaluate(ast, env)
|
||||
except SyntaxException as e:
|
||||
print(e.msg)
|
||||
except RuntimeException as e:
|
||||
print(e.msg)
|
||||
env = createEnvironment()
|
||||
|
||||
evaluate(ast, env)
|
||||
|
||||
except SmnpException as e:
|
||||
print(e.message())
|
||||
except KeyboardInterrupt:
|
||||
print("Program interrupted")
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
from enum import Enum
|
||||
|
||||
from smnp.error.syntax import SyntaxException
|
||||
|
||||
|
||||
class NotePitch(Enum):
|
||||
C = 0
|
||||
CIS = 1
|
||||
@@ -46,7 +48,7 @@ class NotePitch(Enum):
|
||||
try:
|
||||
return stringToPitch[string.lower()]
|
||||
except KeyError as e:
|
||||
raise SyntaxException(None, f"Note '{string}' does not exist")
|
||||
raise SyntaxException(f"Note '{string}' does not exist") #TODO jakis inny exception
|
||||
|
||||
stringToPitch = {
|
||||
'c': NotePitch.C,
|
||||
|
||||
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
|
||||
@@ -1,27 +1,24 @@
|
||||
import sys
|
||||
import time
|
||||
import re
|
||||
from smnp.error.syntax import SyntaxException
|
||||
from smnp.token.type import TokenType
|
||||
from smnp.token.model import Token, TokenList
|
||||
from smnp.token.tools import tokenizeChar, tokenizeRegexPattern
|
||||
from smnp.token.tokenizers.paren import tokenizeOpenParen, tokenizeCloseParen
|
||||
from smnp.token.tokenizers.asterisk import tokenizeAsterisk
|
||||
from smnp.token.tokenizers.whitespace import tokenizeWhitespaces
|
||||
from smnp.token.tokenizers.identifier import tokenizeIdentifier
|
||||
from smnp.token.tokenizers.comma import tokenizeComma
|
||||
from smnp.token.tokenizers.string import tokenizeString
|
||||
from smnp.token.tokenizers.integer import tokenizeInteger
|
||||
from smnp.token.tokenizers.bracket import tokenizeOpenBracket, tokenizeCloseBracket
|
||||
from smnp.error.syntax import SyntaxException
|
||||
from smnp.token.model import TokenList
|
||||
from smnp.token.tokenizers.assign import tokenizeAssign
|
||||
from smnp.token.tokenizers.asterisk import tokenizeAsterisk
|
||||
from smnp.token.tokenizers.bracket import tokenizeOpenBracket, tokenizeCloseBracket
|
||||
from smnp.token.tokenizers.colon import tokenizeColon
|
||||
from smnp.token.tokenizers.comma import tokenizeComma
|
||||
from smnp.token.tokenizers.comment import tokenizeComment
|
||||
from smnp.token.tokenizers.note import tokenizeNote
|
||||
from smnp.token.tokenizers.function import tokenizeFunction
|
||||
from smnp.token.tokenizers.ret import tokenizeReturn
|
||||
from smnp.token.tokenizers.percent import tokenizePercent
|
||||
from smnp.token.tokenizers.minus import tokenizeMinus
|
||||
from smnp.token.tokenizers.dot import tokenizeDot
|
||||
from smnp.token.tokenizers.function import tokenizeFunction
|
||||
from smnp.token.tokenizers.identifier import tokenizeIdentifier
|
||||
from smnp.token.tokenizers.integer import tokenizeInteger
|
||||
from smnp.token.tokenizers.minus import tokenizeMinus
|
||||
from smnp.token.tokenizers.note import tokenizeNote
|
||||
from smnp.token.tokenizers.paren import tokenizeOpenParen, tokenizeCloseParen
|
||||
from smnp.token.tokenizers.percent import tokenizePercent
|
||||
from smnp.token.tokenizers.ret import tokenizeReturn
|
||||
from smnp.token.tokenizers.string import tokenizeString
|
||||
from smnp.token.tokenizers.whitespace import tokenizeWhitespaces
|
||||
from smnp.token.type import TokenType
|
||||
|
||||
tokenizers = (
|
||||
tokenizeOpenParen,
|
||||
@@ -58,7 +55,7 @@ def tokenize(lines):
|
||||
consumedChars, token = combinedTokenizer(line, current, lineNumber)
|
||||
|
||||
if consumedChars == 0:
|
||||
raise SyntaxException((lineNumber, current), f"Unknown symbol '{line[current]}'")
|
||||
raise SyntaxException(f"Unknown symbol '{line[current]}'", (lineNumber, current))
|
||||
|
||||
current += consumedChars
|
||||
tokens.append(token)
|
||||
|
||||
@@ -17,7 +17,7 @@ class Type(Enum):
|
||||
|
||||
|
||||
def _failStringify(t):
|
||||
raise RuntimeException(None, f"Not able to interpret {t.name}'")
|
||||
raise RuntimeException(f"Not able to interpret {t.name}'")
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user