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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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__()

View File

@@ -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)

View File

@@ -1,2 +1,13 @@
class SmnpException(Exception):
pass
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}"

View File

@@ -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"

View File

@@ -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}"

View File

@@ -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"

View 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')

View File

@@ -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')

View File

@@ -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))

View File

@@ -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)
)

View File

@@ -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")

View File

@@ -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
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

View File

@@ -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)

View File

@@ -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}'")