Enable support for types' properties

This commit is contained in:
Bartłomiej Pluta
2019-07-08 22:06:38 +02:00
parent 74eec7f997
commit b06a8533c0
22 changed files with 77 additions and 126 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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]["__"]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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