Added more rules and tests
This commit is contained in:
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,8 +1,11 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
**/__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# Pytest
|
||||
.pytest_cache
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
@ -159,3 +162,6 @@ cython_debug/
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
# Debugging file
|
||||
debug.py
|
||||
|
||||
|
||||
11
main.py
11
main.py
@ -7,7 +7,7 @@ if __name__ == "__main__":
|
||||
'Chords': "0 024 2 246",
|
||||
'Note lengths': "w 0 h 1 q 2 e 3 s 4",
|
||||
'Subdivision': "[1 2 [3 4]]",
|
||||
'Decimal durations': "0.25 0 1 [0.333]2 3",
|
||||
'Decimal durations': "0.25 0 1 <0.333>2 3",
|
||||
'Octaves': "^ 0 ^ 1 _ 2 _ 3",
|
||||
'Escaped octave': "<2> 1 <1>1<-2>3",
|
||||
'Roman chords': "i ii iii+4 iv+5 v+8 vi+10 vii+20",
|
||||
@ -32,8 +32,9 @@ if __name__ == "__main__":
|
||||
'Functions': "(0 1 2 3){x%3==0?x-2:x+2}",
|
||||
'Polynomials': "(-10..10){(x**3)*(x+1)%12}",
|
||||
}
|
||||
for expression in expressions:
|
||||
try:
|
||||
parse_expression(expression)
|
||||
for ex in expressions:
|
||||
try:
|
||||
print(f"Parsed: "+parse_expression(expressions[ex]).text)
|
||||
except Exception as e:
|
||||
print(f"[red]Failed on {expression}[/red]: {str(e)[0:40]}...")
|
||||
print(f"[red]Failed on {ex}[/red]")
|
||||
#print(f"[red]Failed on {ex}[/red]: {str(e)[0:40]}...")
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -1,4 +1,5 @@
|
||||
from ziffers import *
|
||||
import pytest
|
||||
|
||||
def test_can_parse():
|
||||
expressions = [
|
||||
@ -24,10 +25,22 @@ def test_can_parse():
|
||||
print(results)
|
||||
assert all(results)
|
||||
|
||||
#print(ziffers_parser.parse("[1 [2 3]]"))
|
||||
#print(ziffers_parser.parse("(1 (1,3) 1..3)"))
|
||||
#print(ziffers_parser.parse("_^ q _qe^3 qww_4 _123 <1 2>"))
|
||||
#print(ziffers_parser.parse("q _2 _ 3 ^ 343"))
|
||||
#print(ziffers_parser.parse("2 qe2 e4").values)
|
||||
#print(ziffers_parser.parse("q 2 <3 343>"))
|
||||
#print(ziffers_parser.parse("q (2 <3 343 (3 4)>)"))
|
||||
@pytest.mark.parametrize(
|
||||
"pattern",
|
||||
[
|
||||
"1 2 3",
|
||||
"q3 e4 s5",
|
||||
],
|
||||
)
|
||||
def test_parsing_text(pattern: str):
|
||||
assert parse_expression(pattern).text == pattern
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"pattern,expected",
|
||||
[
|
||||
("1 2 3", [1,2,3]),
|
||||
("q2 eq3", [2,3]),
|
||||
],
|
||||
)
|
||||
def test_pcs(pattern: str, expected: list):
|
||||
assert parse_expression(pattern).pcs == expected
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -5,11 +5,15 @@ class Meta:
|
||||
text: str
|
||||
|
||||
@dataclass
|
||||
class Duration(Meta):
|
||||
class DurationChange(Meta):
|
||||
dur: float
|
||||
|
||||
@dataclass
|
||||
class Octave(Meta):
|
||||
class OctaveChange(Meta):
|
||||
oct: int
|
||||
|
||||
@dataclass
|
||||
class OctaveMod(Meta):
|
||||
oct: int
|
||||
|
||||
@dataclass
|
||||
@ -25,7 +29,6 @@ class Pitch(Event):
|
||||
@dataclass
|
||||
class RandomPitch(Event):
|
||||
pc: int = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Chord(Event):
|
||||
@ -42,8 +45,10 @@ class Ziffers:
|
||||
text: str = None
|
||||
def __post_init__(self):
|
||||
self.text = self.collect_text()
|
||||
def collect_text(self):
|
||||
def collect_text(self) -> str:
|
||||
return "".join([val.text for val in self.values])
|
||||
def pcs(self) -> list[int]:
|
||||
return [val.pc for val in self.values if type(val) is Pitch]
|
||||
|
||||
@dataclass
|
||||
class Sequence(Meta):
|
||||
|
||||
@ -2,7 +2,6 @@ from lark import Transformer
|
||||
from .classes import *
|
||||
from .common import flatten
|
||||
from .defaults import default_durs
|
||||
from collections import Counter
|
||||
|
||||
class ZiffersTransformer(Transformer):
|
||||
|
||||
@ -28,11 +27,14 @@ class ZiffersTransformer(Transformer):
|
||||
|
||||
def pc(self, s):
|
||||
if(len(s)>1):
|
||||
counter = Counter()
|
||||
for d in s:
|
||||
counter.update(d)
|
||||
result = dict(counter)
|
||||
result["text"] = result["text"][::-1]
|
||||
# Collect&sum prefixes from any order: _qee^s4 etc.
|
||||
result = s[0]
|
||||
for hash in s[1:]:
|
||||
for key in hash.keys():
|
||||
if key in result:
|
||||
result[key] = result[key] + hash[key]
|
||||
else:
|
||||
result[key] = hash[key]
|
||||
return Pitch(**result)
|
||||
else:
|
||||
val = s[0]
|
||||
@ -46,7 +48,15 @@ class ZiffersTransformer(Transformer):
|
||||
|
||||
def oct_change(self,s):
|
||||
octave = s[0]
|
||||
return [Octave(oct=octave["oct"],text=octave["text"]),s[1]]
|
||||
return [OctaveChange(oct=octave["oct"],text=octave["text"]),s[1]]
|
||||
|
||||
def oct_mod(self,s):
|
||||
octave = s[0]
|
||||
return [OctaveMod(oct=octave["oct"],text=octave["text"]),s[1]]
|
||||
|
||||
def escaped_octave(self,s):
|
||||
value = s[0][1:-1]
|
||||
return {"oct": int(value), "text":s[0].value}
|
||||
|
||||
def octave(self,s):
|
||||
value = sum([1 if char=='^' else -1 for char in s[0].value])
|
||||
@ -57,14 +67,19 @@ class ZiffersTransformer(Transformer):
|
||||
|
||||
def dur_change(self,s):
|
||||
duration = s[0]
|
||||
return [Duration(dur=duration["dur"], text=duration["text"]),s[1]]
|
||||
return [DurationChange(dur=duration["dur"], text=duration["text"]),s[1]]
|
||||
|
||||
def duration(self,s):
|
||||
def escaped_decimal(self,s):
|
||||
val = s[0]
|
||||
val["text"] = "<"+val["text"]+">"
|
||||
return val
|
||||
|
||||
def duration_chars(self,s):
|
||||
durations = [val[1] for val in s]
|
||||
characters = "".join([val[0] for val in s])
|
||||
return {"dur": sum(durations), "text":characters[::-1]}
|
||||
return {"dur": sum(durations), "text":characters}
|
||||
|
||||
def dur(self,s):
|
||||
def dotted_dur(self,s):
|
||||
key = s[0]
|
||||
val = default_durs[key]
|
||||
dots = len(s)-1
|
||||
@ -72,6 +87,10 @@ class ZiffersTransformer(Transformer):
|
||||
val = val * (2.0-(1.0/(2*dots)))
|
||||
return [key+"."*dots,val]
|
||||
|
||||
def decimal(self,s):
|
||||
val = s[0]
|
||||
return {"dur": float(val),"text": val.value}
|
||||
|
||||
def dot(self,s):
|
||||
return "."
|
||||
|
||||
|
||||
@ -9,13 +9,4 @@ grammar = grammar_path / "ziffers.lark"
|
||||
ziffers_parser = Lark.open(grammar, rel_to=__file__, start='value', parser='lalr', transformer=ZiffersTransformer())
|
||||
|
||||
def parse_expression(expr):
|
||||
return ziffers_parser.parse(expr)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(ziffers_parser.parse("[1 [2 3]]"))
|
||||
#print(ziffers_parser.parse("(1 (1,3) 1..3)"))
|
||||
#print(ziffers_parser.parse("_^ q _qe^3 qww_4 _123 <1 2>"))
|
||||
#print(ziffers_parser.parse("q _2 _ 3 ^ 343"))
|
||||
#print(ziffers_parser.parse("2 qe2 e4").values)
|
||||
#print(ziffers_parser.parse("q 2 <3 343>"))
|
||||
#print(ziffers_parser.parse("q (2 <3 343 (3 4)>)"))
|
||||
return ziffers_parser.parse(expr)
|
||||
@ -1,19 +1,23 @@
|
||||
|
||||
?value: root
|
||||
root: (pc | dur_change | oct_change | WS | chord | cycle | randompitch | range | list | subdivision)*
|
||||
root: (pc | dur_change | oct_mod | oct_change | WS | chord | cycle | randompitch | range | list | subdivision)*
|
||||
list: "(" root ")"
|
||||
randompitch: /[\(][-?0-9][,][-?0-9][\)]/
|
||||
range: /[-?0-9]\.\.[-?0-9]/
|
||||
randompitch: /\(-?[0-9],-?[0-9]\)/
|
||||
range: /-?[0-9]\.\.-?[0-9]/
|
||||
cycle: "<" root ">"
|
||||
pc: prefix* pitch
|
||||
pitch: /[-?0-9TE]/
|
||||
prefix: octave | duration
|
||||
oct_change: octave WS
|
||||
pitch: /-?[0-9TE]/
|
||||
prefix: (octave | duration_chars | escaped_decimal | escaped_octave)
|
||||
oct_change: escaped_octave WS
|
||||
escaped_octave: /<-?[0-9]>/
|
||||
oct_mod: octave WS
|
||||
octave: /[_^]+/
|
||||
chord: pc pc+
|
||||
dur_change: duration WS
|
||||
duration: dur+
|
||||
dur: dchar dot*
|
||||
escaped_decimal: "<" decimal ">"
|
||||
dur_change: (duration_chars | decimal) WS
|
||||
duration_chars: dotted_dur+
|
||||
dotted_dur: dchar dot*
|
||||
decimal: /-?[0-9]+\.[0-9]+/
|
||||
dchar: /[mklpdcwyhnqaefsxtgujzo]/
|
||||
dot: "."
|
||||
subitems: (pc | WS | chord | cycle | subdivision)*
|
||||
|
||||
Reference in New Issue
Block a user