Enable support for types' properties
This commit is contained in:
@@ -2,7 +2,6 @@ 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, debug
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
def createEnvironment():
|
||||
@@ -30,7 +29,7 @@ def createEnvironment():
|
||||
]
|
||||
|
||||
variables = {
|
||||
"bpm": Value(Type.INTEGER, 120)
|
||||
"bpm": Type.integer(120)
|
||||
}
|
||||
|
||||
return Environment([ variables ], functions, methods)
|
||||
|
||||
@@ -3,7 +3,6 @@ from functools import reduce
|
||||
from smnp.library.model import Function
|
||||
from smnp.library.signature import varargSignature, ofTypes
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
_signature = varargSignature(ofTypes(Type.LIST))
|
||||
def _function(env, vararg):
|
||||
@@ -11,7 +10,7 @@ def _function(env, vararg):
|
||||
return vararg[0]
|
||||
|
||||
combined = reduce(lambda x, y: x.value + y.value, vararg)
|
||||
return Value(Type.LIST, combined)
|
||||
return Type.list(combined)
|
||||
|
||||
|
||||
function = Function(_signature, _function, 'combine')
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
from smnp.library.model import Function
|
||||
from smnp.library.signature import signature, ofTypes
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
_signature = signature(ofTypes(Type.NOTE), ofTypes(Type.INTEGER))
|
||||
def _function(env, note, duration):
|
||||
return Value(Type.NOTE, note.value.withDuration(duration.value))
|
||||
return Type.note(note.value.withDuration(duration.value))
|
||||
|
||||
|
||||
function = Function(_signature, _function, 'withDuration')
|
||||
@@ -1,15 +1,16 @@
|
||||
from smnp.library.model import Function
|
||||
from smnp.library.signature import varargSignature, allTypes
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
_signature = varargSignature(allTypes())
|
||||
def _function(env, vararg):
|
||||
return Value(Type.LIST, doFlat(vararg, [])).decompose()
|
||||
return Type.list(doFlat(vararg, [])).decompose()
|
||||
|
||||
|
||||
def doFlat(input, output=[]):
|
||||
def doFlat(input, output=None):
|
||||
if output is None:
|
||||
output = []
|
||||
|
||||
for item in input:
|
||||
if item.type == Type.LIST:
|
||||
doFlat(item.value, output)
|
||||
|
||||
@@ -3,21 +3,19 @@ from smnp.library.signature import varargSignature, ofTypes, listOf
|
||||
from smnp.note.interval import intervalToString
|
||||
from smnp.note.model import Note
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
||||
def _function1(env, vararg):
|
||||
withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
|
||||
if len(withoutPauses) < 2:
|
||||
return Value(Type.LIST, [])
|
||||
return Type.list([])
|
||||
semitones = [Note.checkInterval(withoutPauses[i-1], withoutPauses[i]) for i in range(1, len(withoutPauses))]
|
||||
return Value(Type.LIST, [Value(Type.STRING, intervalToString(s)) for s in semitones]).decompose()
|
||||
return Type.list([Type.string(intervalToString(s)) for s in semitones]).decompose()
|
||||
|
||||
|
||||
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
||||
def _function2(env, vararg):
|
||||
return Value(Type.LIST, [_function1(env, arg.value) for arg in vararg]).decompose()
|
||||
return Type.list([_function1(env, arg.value) for arg in vararg]).decompose()
|
||||
|
||||
|
||||
function = CombinedFunction(
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
from smnp.library.model import Function
|
||||
from smnp.library.signature import signature, ofTypes
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
_signature = signature(ofTypes(Type.NOTE), ofTypes(Type.INTEGER))
|
||||
def _function(env, note, octave):
|
||||
return Value(Type.NOTE, note.value.withOctave(octave.value))
|
||||
return Type.note(note.value.withOctave(octave.value))
|
||||
|
||||
|
||||
function = Function(_signature, _function, 'withOctave')
|
||||
@@ -2,20 +2,18 @@ from smnp.library.model import Function, CombinedFunction
|
||||
from smnp.library.signature import varargSignature, ofTypes, listOf
|
||||
from smnp.note.model import Note
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
_signature1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
|
||||
def _function1(env, vararg):
|
||||
withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
|
||||
if len(withoutPauses) < 2:
|
||||
return Value(Type.LIST, [])
|
||||
return Value(Type.LIST, [Value(Type.INTEGER, Note.checkInterval(withoutPauses[i-1], withoutPauses[i])) for i in range(1, len(withoutPauses))]).decompose()
|
||||
return Type.list([])
|
||||
return Type.list([Type.integer(Note.checkInterval(withoutPauses[i-1], withoutPauses[i])) for i in range(1, len(withoutPauses))]).decompose()
|
||||
|
||||
|
||||
_signature2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
|
||||
def _function2(env, vararg):
|
||||
return Value(Type.LIST, [_function1(env, arg.value) for arg in vararg]).decompose()
|
||||
return Type.list([_function1(env, arg.value) for arg in vararg]).decompose()
|
||||
|
||||
|
||||
function = CombinedFunction(
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
from smnp.library.model import CombinedFunction, Function
|
||||
from smnp.library.signature import varargSignature, ofTypes, listOf
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
_signature1 = varargSignature(ofTypes(Type.INTEGER, Type.NOTE), ofTypes(Type.INTEGER))
|
||||
def _function1(env, value, vararg):
|
||||
transposed = [Value(Type.NOTE, arg.value.transpose(value.value)) if arg.type == Type.NOTE else arg for arg in vararg]
|
||||
return Value(Type.LIST, transposed).decompose()
|
||||
transposed = [Type.note(arg.value.transpose(value.value)) if arg.type == Type.NOTE else arg for arg in vararg]
|
||||
return Type.list(transposed).decompose()
|
||||
|
||||
|
||||
_signature2 = varargSignature(listOf(Type.INTEGER, Type.NOTE), ofTypes(Type.INTEGER))
|
||||
def _function2(env, value, vararg):
|
||||
return Value(Type.LIST, [_function1(env, value, arg.value) for arg in vararg]).decompose()
|
||||
return Type.list([_function1(env, value, arg.value) for arg in vararg]).decompose()
|
||||
|
||||
|
||||
function = CombinedFunction(
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
from smnp.library.model import CombinedFunction, Function
|
||||
from smnp.library.signature import signature, listOf, ofTypes, varargSignature
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
_signature1 = varargSignature(ofTypes(Type.NOTE), ofTypes(Type.INTEGER), ofTypes(Type.INTEGER))
|
||||
def _function1(env, n, m, vararg):
|
||||
t = [Value(Type.NOTE, arg.value.withDuration(arg.value.duration * n.value / m.value)) for arg in vararg]
|
||||
return Value(Type.LIST, t).decompose()
|
||||
t = [Type.note(arg.value.withDuration(arg.value.duration * n.value / m.value)) for arg in vararg]
|
||||
return Type.list(t).decompose()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from enum import Enum, auto
|
||||
|
||||
from smnp.error.function import IllegalFunctionInvocationException
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
class FunctionType(Enum):
|
||||
@@ -24,7 +23,7 @@ class Function:
|
||||
if result[0]:
|
||||
ret = self.function(env, *result[1:])
|
||||
if ret is None:
|
||||
return Value(Type.VOID, None)
|
||||
return Type.void()
|
||||
return ret
|
||||
raise IllegalFunctionInvocationException(self.stringSignature(), f"{self.name}{types(args)}") #TODO: argumenty do typów, nie wartości
|
||||
|
||||
@@ -44,7 +43,7 @@ class CombinedFunction(Function):
|
||||
if result[0]:
|
||||
ret = function.function(env, *result[1:])
|
||||
if ret is None:
|
||||
return Value(Type.VOID, None)
|
||||
return Type.void()
|
||||
return ret
|
||||
raise IllegalFunctionInvocationException(self.stringSignature(), f"{self.name}{types(args)}")
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ from smnp.ast.node.function import FunctionDefinitionNode
|
||||
from smnp.ast.node.program import Program
|
||||
from smnp.error.runtime import RuntimeException
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
class Evaluator:
|
||||
@@ -18,7 +17,7 @@ class Evaluator:
|
||||
def evaluate(cls, node, environment):
|
||||
result = cls.evaluator(node, environment)
|
||||
if result is None:
|
||||
return EvaluationResult.OK(Value(Type.VOID, None))
|
||||
return EvaluationResult.OK(Type.void())
|
||||
return EvaluationResult.OK(result)
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from smnp.ast.node.identifier import IdentifierNode
|
||||
from smnp.ast.node.invocation import FunctionCallNode
|
||||
from smnp.error.runtime import RuntimeException
|
||||
from smnp.runtime.evaluator import Evaluator
|
||||
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
|
||||
@@ -12,10 +13,13 @@ class AccessEvaluator(Evaluator):
|
||||
left = expressionEvaluator(doAssert=True)(node.left, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
right = node.right
|
||||
|
||||
if type(node.right) == IdentifierNode:
|
||||
raise RuntimeError("Accessing properties is not implemented yet")
|
||||
if type(right) == IdentifierNode:
|
||||
try:
|
||||
return left.properties[right.value]
|
||||
except KeyError:
|
||||
raise RuntimeException(f"Unknown property '{right.value}' of type '{left.type.name.lower()}'", right.pos)
|
||||
|
||||
if type(node.right) == FunctionCallNode:
|
||||
if type(right) == FunctionCallNode:
|
||||
arguments = abstractIterableEvaluator(expressionEvaluator(True))(right.arguments, environment)
|
||||
return environment.invokeMethod(left, right.name.value, arguments)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from smnp.ast.node.identifier import IdentifierNode
|
||||
from smnp.runtime.evaluator import evaluate, Evaluator, EvaluationResult
|
||||
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
class AsteriskEvaluator(Evaluator):
|
||||
@@ -22,7 +21,7 @@ class AsteriskEvaluator(Evaluator):
|
||||
results = []
|
||||
automaticVariable = cls._automaticNamedVariable(node.iterator, environment, "_")
|
||||
for i in range(evaluatedIterator.value):
|
||||
environment.scopes[-1][automaticVariable] = Value(Type.INTEGER, i + 1)
|
||||
environment.scopes[-1][automaticVariable] = Type.integer(i + 1)
|
||||
result = evaluate(node.statement, environment).value #TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
if result is None or result.type == Type.VOID:
|
||||
results = None
|
||||
@@ -32,7 +31,7 @@ class AsteriskEvaluator(Evaluator):
|
||||
|
||||
del environment.scopes[-1][automaticVariable]
|
||||
|
||||
return EvaluationResult.OK(Value(Type.LIST, results).decompose() if results is not None else Value(Type.VOID, None))
|
||||
return EvaluationResult.OK(Type.list(results).decompose() if results is not None else Type.void())
|
||||
|
||||
return EvaluationResult.FAIL()
|
||||
|
||||
@@ -65,7 +64,7 @@ class AsteriskEvaluator(Evaluator):
|
||||
automaticVariableKey = cls._automaticNamedVariable(node.iterator, environment, "_")
|
||||
automaticVariableValue = cls._automaticNamedVariable(node.iterator, environment, "__")
|
||||
for i, v in enumerate(evaluatedIterator.value):
|
||||
environment.scopes[-1][automaticVariableKey] = Value(Type.INTEGER, i + 1)
|
||||
environment.scopes[-1][automaticVariableKey] = Type.integer(i + 1)
|
||||
environment.scopes[-1][automaticVariableValue] = v
|
||||
result = evaluate(node.statement, environment).value # TODO check if it isn't necessary to verify 'result' attr of EvaluatioNResult
|
||||
if result is not None and result.type != Type.VOID:
|
||||
@@ -74,52 +73,8 @@ class AsteriskEvaluator(Evaluator):
|
||||
del environment.scopes[-1][automaticVariableKey]
|
||||
del environment.scopes[-1][automaticVariableValue]
|
||||
|
||||
return EvaluationResult.OK(Value(Type.LIST, results).decompose())
|
||||
return EvaluationResult.OK(Type.list(results).decompose())
|
||||
|
||||
return EvaluationResult.FAIL()
|
||||
|
||||
return evaluator
|
||||
|
||||
#
|
||||
# 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]["__"]
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
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)
|
||||
@@ -1,10 +1,9 @@
|
||||
from smnp.runtime.evaluator import Evaluator
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
class IntegerEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
def evaluator(cls, node, environment):
|
||||
return Value(Type.INTEGER, node.value)
|
||||
return Type.integer(node.value)
|
||||
@@ -2,7 +2,6 @@ from smnp.runtime.evaluator import Evaluator
|
||||
from smnp.runtime.evaluators.expression import expressionEvaluator
|
||||
from smnp.runtime.evaluators.iterable import abstractIterableEvaluator
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
class ListEvaluator(Evaluator):
|
||||
@@ -10,4 +9,4 @@ class ListEvaluator(Evaluator):
|
||||
@classmethod
|
||||
def evaluator(cls, node, environment):
|
||||
list = abstractIterableEvaluator(expressionEvaluator(doAssert=True))(node, environment)
|
||||
return Value(Type.LIST, list)
|
||||
return Type.list(list)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from smnp.runtime.evaluator import Evaluator
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
class NoteEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
def evaluator(cls, node, environment):
|
||||
return Value(Type.NOTE, node.value)
|
||||
return Type.note(node.value)
|
||||
@@ -1,6 +0,0 @@
|
||||
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)
|
||||
@@ -1,13 +1,12 @@
|
||||
from smnp.runtime.evaluator import Evaluator
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
class StringEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
def evaluator(cls, node, environment):
|
||||
return Value(Type.STRING, node.value)
|
||||
return Type.string(node.value)
|
||||
|
||||
# TODO: make use of complex interpreter for code inside '{' and '}'
|
||||
# def interpolate(string, environment):
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
from smnp.runtime.evaluator import Evaluator
|
||||
from smnp.type.model import Type
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
class TypeEvaluator(Evaluator):
|
||||
|
||||
@classmethod
|
||||
def evaluator(cls, node, environment):
|
||||
return Value(Type.TYPE, node.type)
|
||||
return Type.type(node.type)
|
||||
@@ -2,6 +2,7 @@ from enum import Enum
|
||||
|
||||
from smnp.error.runtime import RuntimeException
|
||||
from smnp.note.model import Note
|
||||
from smnp.type.value import Value
|
||||
|
||||
|
||||
class Type(Enum):
|
||||
@@ -16,6 +17,37 @@ class Type(Enum):
|
||||
def stringify(self, element):
|
||||
return self.value[1](element)
|
||||
|
||||
@staticmethod
|
||||
def integer(value):
|
||||
return Value(Type.INTEGER, value, {})
|
||||
|
||||
@staticmethod
|
||||
def string(value):
|
||||
return Value(Type.STRING, value, {
|
||||
"length": Type.integer(len(value))
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def list(value):
|
||||
return Value(Type.LIST, value, {
|
||||
"size": Type.integer(len(value))
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def note(value):
|
||||
return Value(Type.NOTE, value, {
|
||||
"octave": Type.integer(value.octave),
|
||||
"duration": Type.integer(value.duration),
|
||||
"dot": Type.string('.' if value.dot else '')
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def type(value):
|
||||
return Value(Type.TYPE, value, {})
|
||||
|
||||
@staticmethod
|
||||
def void():
|
||||
return Value(Type.VOID, None)
|
||||
|
||||
def _failStringify(t):
|
||||
raise RuntimeException(f"Not able to interpret {t.name}'", None)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from smnp.type.model import Type
|
||||
|
||||
|
||||
class Value:
|
||||
def __init__(self, objectType, value):
|
||||
def __init__(self, objectType, value, properties=None):
|
||||
if properties is None:
|
||||
properties = {}
|
||||
|
||||
self.value = value
|
||||
self.properties = properties
|
||||
|
||||
if objectType.value[0] is None or type(value) == objectType.value[0]:
|
||||
self.type = objectType
|
||||
@@ -18,6 +19,7 @@ class Value:
|
||||
return self.type.stringify(self.value)
|
||||
|
||||
def decompose(self):
|
||||
from smnp.type.model import Type
|
||||
if self.type != Type.LIST:
|
||||
raise RuntimeError(f"Method 'decompose' can be applied only for lists")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user