Improve library

This commit is contained in:
Bartłomiej Pluta
2019-07-04 13:14:12 +02:00
parent f86055272e
commit 558e955d07
19 changed files with 250 additions and 138 deletions

View File

@@ -1,44 +1,60 @@
import smnp.environment.function.list as l
from smnp.environment.environment import Environment
from smnp.environment.function import synth, base, interval, note, transposer, rand, mic
from smnp.environment.function.model import Function, ONLY_FUNCTION
from smnp.note.model import Note
from smnp.library.function.combine import combine
from smnp.library.function.display import display
from smnp.library.function.exit import exit
from smnp.library.function.flat import flat
from smnp.library.function.interval import interval
from smnp.library.function.semitones import semitones
from smnp.library.function.sleep import sleep
from smnp.library.function.type import objectType
def createEnvironment():
functions = {
'exit': Function(base.exit, ONLY_FUNCTION),
'print': Function(base.display, ONLY_FUNCTION),
'read': Function(base.read, ONLY_FUNCTION),
'type': Function(base.objectType, ONLY_FUNCTION),
'sleep': Function(base.sleep, ONLY_FUNCTION),
'synth': Function(synth.synth, ONLY_FUNCTION),
'pause': Function(synth.pause, ONLY_FUNCTION),
'changeDuration': Function(note.changeDuration, ONLY_FUNCTION),
'changeOctave': Function(note.changeOctave, ONLY_FUNCTION),
'semitones': Function(interval.semitones, ONLY_FUNCTION),
'interval': Function(interval.interval, ONLY_FUNCTION),
'transpose': Function(transposer.transpose, ONLY_FUNCTION),
'transposeTo': Function(transposer.transposeTo, ONLY_FUNCTION),
'random': Function(rand.random, ONLY_FUNCTION),
# 'sample': sample,
'wait': Function(mic.wait, ONLY_FUNCTION),
'tuplet': Function(note.tuplet, ONLY_FUNCTION),
'combine': Function(l.combine, ONLY_FUNCTION),
'flat': Function(l.flat, ONLY_FUNCTION),
'debug': Function(lambda args, env: print(args), ONLY_FUNCTION),
functions = [
display,
objectType,
exit,
sleep,
semitones,
interval,
combine,
flat
]
}
methods = [
methods = {
str: {},
list: {},
float: {},
Note: {
'synth': synth.synth
},
type(None): {},
}
]
# 'exit': Function(base.exit, ONLY_FUNCTION),
# 'print': Function(base.display, ONLY_FUNCTION),
# 'read': Function(base.read, ONLY_FUNCTION),
# 'type': Function(base.objectType, ONLY_FUNCTION),
# 'sleep': Function(base.sleep, ONLY_FUNCTION),
# 'synth': Function(synth.synth, ONLY_FUNCTION),
# 'pause': Function(synth.pause, ONLY_FUNCTION),
# 'changeDuration': Function(note.changeDuration, ONLY_FUNCTION),
# 'changeOctave': Function(note.changeOctave, ONLY_FUNCTION),
# 'semitones': Function(interval.semitones, ONLY_FUNCTION),
# 'interval': Function(interval.interval, ONLY_FUNCTION),
# 'transpose': Function(transposer.transpose, ONLY_FUNCTION),
# 'transposeTo': Function(transposer.transposeTo, ONLY_FUNCTION),
# 'random': Function(rand.random, ONLY_FUNCTION),
# # 'sample': sample,
# 'wait': Function(mic.wait, ONLY_FUNCTION),
# 'tuplet': Function(note.tuplet, ONLY_FUNCTION),
# 'combine': Function(l.combine, ONLY_FUNCTION),
# 'flat': Function(l.flat, ONLY_FUNCTION),
# 'debug': Function(lambda args, env: print(args), ONLY_FUNCTION),
# methods = {
# str: {},
# list: {},
# float: {},
# Note: {
# 'synth': synth.synth
# },
# type(None): {},
# }
variables = {
"bpm": 120

View File

@@ -1,55 +0,0 @@
import sys
import time
def display(args, env):
print("".join([arg.stringify() for arg in args]))
def objectType(args, env):
if len(args) == 1:
return args[0].stringify()
else:
pass # not valid signature
def exit(args, env):
if len(args) == 1 and isinstance(args[0], int):
sys.exit(args[0])
else:
pass # not valid signature
def sleep(args, env):
if len(args) == 1 and isinstance(args[0], int):
time.sleep(args[0])
else:
pass # not valid signature
def read(args, env):
if len(args) == 2 and isinstance(args[0], str) and isinstance(args[1], str):
print(args[0], end="")
value = input()
if args[1] == "integer":
try:
return int(value)
except ValueError as v:
pass # not int
elif args[1] == "string":
return value
# TODO: note - wydzielić parsowanie nut do osobnej funkcji w pakiecie smnp.note
# elif args[1] == "note":
# chars, token = tokenizeNote(value, 0, 0)
# if chars == 0:
# return # not note
# return parseNote([token], None).value
else:
pass # invalid type
elif len(args) == 1 and isinstance(args[0], str):
print(args[0], end="")
return input()
elif len(args) == 0:
return input()
else:
pass # not valid signature

View File

@@ -0,0 +1,22 @@
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
def _combine(env, vararg):
if len(vararg) == 1:
return vararg[0]
combined = reduce(lambda x, y: x.value + y.value, vararg)
return Value(Type.LIST, combined)
_sign = varargSignature(ofTypes(Type.LIST))
combine = Function(_sign, _combine, 'combine')

View File

@@ -0,0 +1,10 @@
from smnp.library.model import Function
from smnp.library.signature import varargSignature, allTypes
def _display(env, vararg):
print("".join([arg.stringify() for arg in vararg]))
_sign = varargSignature(allTypes())
display = Function(_sign, _display, 'print')

View File

@@ -0,0 +1,13 @@
import sys
from smnp.library.model import Function
from smnp.library.signature import signature, ofTypes
from smnp.type.model import Type
def _exit(env, code):
sys.exit(code.value)
_sign = signature(ofTypes(Type.INTEGER))
exit = Function(_sign, _exit, 'exit')

View File

@@ -0,0 +1,21 @@
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
def _flat(env, vararg):
return Value(Type.LIST, doFlat(vararg, [])).decompose()
def doFlat(input, output=[]):
for item in input:
if item.type == Type.LIST:
doFlat(item.value, output)
else:
output.append(item)
return output
_sign = varargSignature(allTypes())
flat = Function(_sign, _flat, 'flat')

View File

@@ -1,27 +1,30 @@
from smnp.library.tools import returnElementOrList
from smnp.library.model import Function, CombinedFunction
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
def interval(args, env):
if len(args) > 0 and isinstance(args[0], list):
return intervalList(args[0])
return intervalList(args)
def _interval1(env, vararg):
withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
if len(withoutPauses) < 2:
return Value(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()
def intervalList(list):
r = [intervalToString(x) for x in semitonesList(list)]
return returnElementOrList(r)
_sign1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def semitonesList(list):
for x in list:
if not isinstance(x, Note) and not isinstance(x, int):
pass # invalid arguments
withoutPauses = tuple(filter(lambda x: isinstance(x, Note), list))
r = [Note.checkInterval(withoutPauses[i-1], withoutPauses[i]) for i, _ in enumerate(withoutPauses) if i != 0]
return r
def semitones(args, env):
if len(args) > 0 and isinstance(args[0], list):
return returnElementOrList(semitonesList(args[0]))
return returnElementOrList(semitonesList(args))
def _interval2(env, vararg):
return Value(Type.LIST, [_interval1(env, arg.value) for arg in vararg]).decompose()
_sign2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
interval = CombinedFunction(
'interval',
Function(_sign1, _interval1),
Function(_sign2, _interval2)
)

View File

@@ -1,19 +0,0 @@
from functools import reduce
def combine(args, env):
if all(type(x) == list for x in args):
return reduce((lambda x, y: x + y), args)
def flat(args, env):
return _flat(args, [])
def _flat(input, output = []):
for item in input:
if type(item) == list:
_flat(item, output)
else:
output.append(item)
return output

View File

@@ -0,0 +1,28 @@
# TODO read function
# def read(args, env):
# if len(args) == 2 and isinstance(args[0], str) and isinstance(args[1], str):
# print(args[0], end="")
# value = input()
# if args[1] == "integer":
# try:
# return int(value)
# except ValueError as v:
# pass # not int
# elif args[1] == "string":
# return value
# # TODO: note - wydzielić parsowanie nut do osobnej funkcji w pakiecie smnp.note
# # elif args[1] == "note":
# # chars, token = tokenizeNote(value, 0, 0)
# # if chars == 0:
# # return # not note
# # return parseNote([token], None).value
# else:
# pass # invalid type
# elif len(args) == 1 and isinstance(args[0], str):
# print(args[0], end="")
# return input()
# elif len(args) == 0:
# return input()
# else:
# pass # not valid signature

View File

@@ -0,0 +1,29 @@
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
def _semitones1(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()
_sign1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
def _semitones2(env, vararg):
return Value(Type.LIST, [_semitones1(env, arg.value) for arg in vararg]).decompose()
_sign2 = varargSignature(listOf(Type.NOTE, Type.INTEGER))
semitones = CombinedFunction(
"semitones",
Function(_sign1, _semitones1),
Function(_sign2, _semitones2),
)

View File

@@ -0,0 +1,13 @@
import time
from smnp.library.model import Function
from smnp.library.signature import ofTypes, signature
from smnp.type.model import Type
def _sleep(env, value):
time.sleep(value.value)
_sign = signature(ofTypes(Type.INTEGER))
sleep = Function(_sign, _sleep, 'sleep')

View File

@@ -1,5 +1,6 @@
from smnp.library.function.interval import semitonesList
from smnp.library.tools import returnElementOrList
from smnp.library.function.semitones import semitonesList
from smnp.note.model import Note

View File

@@ -0,0 +1,10 @@
from smnp.library.model import Function
from smnp.library.signature import signature, allTypes
def _objectType(env, obj):
return obj.type.name
_sign = signature(allTypes())
objectType = Function(_sign, _objectType, 'type')

View File

@@ -11,7 +11,7 @@ class FunctionType(Enum):
class Function:
def __init__(self, name, signature, function):
def __init__(self, signature, function, name=None):
self.name = name
self.signature = signature
self.function = function
@@ -25,6 +25,7 @@ class Function:
ret = self.function(env, *result[1:])
if ret is None:
return Value(Type.VOID, None)
return ret
raise IllegalFunctionInvocationException(self.stringSignature(), f"{self.name}{types(args)}") #TODO: argumenty do typów, nie wartości
@@ -44,6 +45,7 @@ class CombinedFunction(Function):
ret = function.function(env, *result[1:])
if ret is None:
return Value(Type.VOID, None)
return ret
raise IllegalFunctionInvocationException(self.stringSignature(), f"{self.name}{types(args)}")

View File

@@ -84,6 +84,11 @@ def doesNotMatch(sign):
return (False, *[None for n in sign])
def allTypes():
allowedTypes = [t for t in Type if t != Type.VOID]
return ofTypes(*allowedTypes)
def ofTypes(*types):
def check(value):
return value.type in types
@@ -103,7 +108,6 @@ def listMatches(*pattern):
return Matcher(Type.LIST, check, f"({', '.join([str(m) for m in pattern])})")
def recursiveListMatcher(matcher):
if matcher.type == Type.LIST:
raise RuntimeError(f"Passed matcher will be handling non-list types, so it cannot have type set to {Type.LIST}")

View File

@@ -1,2 +0,0 @@
def returnElementOrList(list):
return list[0] if len(list) == 1 else list

View File

@@ -2,4 +2,4 @@ from smnp.token.tools import tokenizeKeyword
from smnp.token.type import TokenType
def tokenizeFunction(input, current, line):
return tokenizeKeyword(TokenType.FUNCTION, 'function', input, current, line)
return tokenizeKeyword(TokenType.FUNCTION, 'library', input, current, line)

View File

@@ -10,14 +10,14 @@ class Type(Enum):
LIST = (list, lambda x: f"({', '.join([e.stringify() for e in x])})")
PERCENT = (float, lambda x: f"{int(x * 100)}%")
NOTE = (Note, lambda x: x.note.name)
VOID = (type(None), lambda x: _failStringify(x))
VOID = (type(None), lambda x: _failStringify(Type.VOID))
def stringify(self, element):
return self.value[1](element)
def _failStringify(obj):
raise RuntimeException(None, f"Not able to interpret '{obj.type.name()}'")
def _failStringify(t):
raise RuntimeException(None, f"Not able to interpret {t.name}'")

View File

@@ -1,17 +1,33 @@
from smnp.type.model import Type
class Value:
def __init__(self, objectType, value):
self.value = value
if type(value) == objectType.value[0]:
self.type = objectType
elif type(value) == Value:
raise RuntimeError("Trying to pass object of 'Value' type as value of it")
else:
raise RuntimeError(f"Invalid type '{objectType.name}' for value '{value}'")
def stringify(self):
return self.type.stringify(self.value)
def decompose(self):
if self.type != Type.LIST:
raise RuntimeError(f"Method 'decompose' can be applied only for lists")
if len(self.value) == 1:
return Value(self.value[0].type, self.value[0].value)
return self
def __str__(self):
return f"{self.type.name}({self.stringify()})"
return f"{self.type.name}({self.value})"
def __repr__(self):
return self.__str__()