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.environment import Environment
from smnp.environment.function import synth, base, interval, note, transposer, rand, mic from smnp.library.function.combine import combine
from smnp.environment.function.model import Function, ONLY_FUNCTION from smnp.library.function.display import display
from smnp.note.model import Note 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(): def createEnvironment():
functions = { functions = [
'exit': Function(base.exit, ONLY_FUNCTION), display,
'print': Function(base.display, ONLY_FUNCTION), objectType,
'read': Function(base.read, ONLY_FUNCTION), exit,
'type': Function(base.objectType, ONLY_FUNCTION), sleep,
'sleep': Function(base.sleep, ONLY_FUNCTION), semitones,
'synth': Function(synth.synth, ONLY_FUNCTION), interval,
'pause': Function(synth.pause, ONLY_FUNCTION), combine,
'changeDuration': Function(note.changeDuration, ONLY_FUNCTION), flat
'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 = [
methods = { ]
str: {}, # 'exit': Function(base.exit, ONLY_FUNCTION),
list: {}, # 'print': Function(base.display, ONLY_FUNCTION),
float: {}, # 'read': Function(base.read, ONLY_FUNCTION),
Note: { # 'type': Function(base.objectType, ONLY_FUNCTION),
'synth': synth.synth # 'sleep': Function(base.sleep, ONLY_FUNCTION),
}, # 'synth': Function(synth.synth, ONLY_FUNCTION),
type(None): {}, # '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 = { variables = {
"bpm": 120 "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.interval import intervalToString
from smnp.note.model import Note from smnp.note.model import Note
from smnp.type.model import Type
from smnp.type.value import Value
def interval(args, env): def _interval1(env, vararg):
if len(args) > 0 and isinstance(args[0], list): withoutPauses = [note.value for note in vararg if note.type == Type.NOTE]
return intervalList(args[0]) if len(withoutPauses) < 2:
return intervalList(args) 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): _sign1 = varargSignature(ofTypes(Type.NOTE, Type.INTEGER))
r = [intervalToString(x) for x in semitonesList(list)]
return returnElementOrList(r)
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): def _interval2(env, vararg):
if len(args) > 0 and isinstance(args[0], list): return Value(Type.LIST, [_interval1(env, arg.value) for arg in vararg]).decompose()
return returnElementOrList(semitonesList(args[0]))
return returnElementOrList(semitonesList(args)) _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.tools import returnElementOrList
from smnp.library.function.semitones import semitonesList
from smnp.note.model import Note 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: class Function:
def __init__(self, name, signature, function): def __init__(self, signature, function, name=None):
self.name = name self.name = name
self.signature = signature self.signature = signature
self.function = function self.function = function
@@ -25,6 +25,7 @@ class Function:
ret = self.function(env, *result[1:]) ret = self.function(env, *result[1:])
if ret is None: if ret is None:
return Value(Type.VOID, 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 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:]) ret = function.function(env, *result[1:])
if ret is None: if ret is None:
return Value(Type.VOID, None) return Value(Type.VOID, None)
return ret
raise IllegalFunctionInvocationException(self.stringSignature(), f"{self.name}{types(args)}") 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]) 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 ofTypes(*types):
def check(value): def check(value):
return value.type in types 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])})") return Matcher(Type.LIST, check, f"({', '.join([str(m) for m in pattern])})")
def recursiveListMatcher(matcher): def recursiveListMatcher(matcher):
if matcher.type == Type.LIST: 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}") 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 from smnp.token.type import TokenType
def tokenizeFunction(input, current, line): 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])})") LIST = (list, lambda x: f"({', '.join([e.stringify() for e in x])})")
PERCENT = (float, lambda x: f"{int(x * 100)}%") PERCENT = (float, lambda x: f"{int(x * 100)}%")
NOTE = (Note, lambda x: x.note.name) 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): def stringify(self, element):
return self.value[1](element) return self.value[1](element)
def _failStringify(obj): def _failStringify(t):
raise RuntimeException(None, f"Not able to interpret '{obj.type.name()}'") raise RuntimeException(None, f"Not able to interpret {t.name}'")

View File

@@ -1,17 +1,33 @@
from smnp.type.model import Type
class Value: class Value:
def __init__(self, objectType, value): def __init__(self, objectType, value):
self.value = value self.value = value
if type(value) == objectType.value[0]: if type(value) == objectType.value[0]:
self.type = objectType self.type = objectType
elif type(value) == Value:
raise RuntimeError("Trying to pass object of 'Value' type as value of it")
else: else:
raise RuntimeError(f"Invalid type '{objectType.name}' for value '{value}'") raise RuntimeError(f"Invalid type '{objectType.name}' for value '{value}'")
def stringify(self): def stringify(self):
return self.type.stringify(self.value) 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): def __str__(self):
return f"{self.type.name}({self.stringify()})" return f"{self.type.name}({self.value})"
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()