Added more rules and tests

This commit is contained in:
2023-02-01 21:15:09 +02:00
parent 6fea0c275d
commit 3add48c938
22 changed files with 86 additions and 47 deletions

8
.gitignore vendored
View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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