diff --git a/ziffers/classes.py b/ziffers/classes.py index 061e168..cff89a9 100644 --- a/ziffers/classes.py +++ b/ziffers/classes.py @@ -16,6 +16,10 @@ class Item(Meta): ''' Class for all Ziffers text based items ''' text: str +@dataclass +class Whitespace(Item): + ''' Class for whitespace ''' + @dataclass class DurationChange(Item): ''' Class for changing duration ''' @@ -73,8 +77,8 @@ class dataclass_property(property): # pylint: disable=invalid-name @dataclass class Sequence(Meta): ''' Class for sequences of items''' - values: list - text: str = None + values: list[Item] + text: str = field(init=False) _text: str = field(default=None, init=False, repr=False) @dataclass_property @@ -85,23 +89,11 @@ class Sequence(Meta): def text(self, text: str) -> None: self._text = text - wrapper: str = None - _wrapper: str = field(default=None, init=False, repr=False) - - @dataclass_property - def wrapper(self) -> str: - return self._wrapper - - @wrapper.setter - def wrapper(self, wrapper: str) -> None: - self._wrapper = wrapper - if self.text != None: - self.text = self.wrapper[0] + self.text + self.wrapper[1] + wrap_start: str = field(default=None, repr=False) + wrap_end: str = field(default=None, repr=False) def __post_init__(self): self.text = self.collect_text() - if self.text != None and self.wrapper != None: - self.text = self.wrapper[0] + self.text + self.wrapper[1] def update_values(self, new_values): ''' Update value attributes from dict ''' @@ -111,8 +103,13 @@ class Sequence(Meta): setattr(obj, key, value) def collect_text(self) -> str: - return "".join([val.text for val in self.values]) - + text = "".join([val.text for val in self.values]) + if self.wrap_start != None: + text = self.wrap_start + text + if self.wrap_end != None: + text = text + self.wrap_end + return text + def pcs(self) -> list[int]: return [val.pc for val in self.values if type(val) is Pitch] @@ -125,12 +122,15 @@ class Sequence(Meta): @dataclass class ListSequence(Sequence): ''' Class for Ziffers list sequences ''' - prefix: dict = None - values: list = None - def __init__(self): - super.__init__() - if self.prefix!=None: - self.update(self.prefix) + wrap_start: str = field(default="(", repr=False) + wrap_end: str = field(default=")", repr=False) + +@dataclass +class RepeatedListSequence(Sequence): + ''' Class for Ziffers list sequences ''' + repeats: Item = None + wrap_start: str = field(default="(:", repr=False) + wrap_end: str = field(default=":)", repr=False) @dataclass class Subdivision(Item): @@ -141,6 +141,9 @@ class Subdivision(Item): class Cyclic(Sequence): ''' Class for cyclic sequences''' cycle: int = 0 + wrap_start: str = field(default="<", repr=False) + wrap_end: str = field(default=">", repr=False) + def __post_init__(self): super().__post_init__() # TODO: Do spaced need to be filtered out? @@ -189,6 +192,8 @@ class Operation(Item): class Eval(Sequence): ''' Class for evaluation notation ''' result: ... = None + wrap_start: str = field(default="{", repr=False) + wrap_end: str = field(default="}", repr=False) def __post_init__(self): super().__post_init__() self.result = eval(self.text) @@ -210,4 +215,12 @@ class Euclid(Item): length: int onset: list offset: list = None - rotate: int = None \ No newline at end of file + rotate: int = None + +@dataclass +class RepeatedSequence(Sequence): + ''' Class for repeats ''' + repeats: Item = None + wrap_start: str = field(default="[:", repr=False) + wrap_end: str = field(default=":]", repr=False) + \ No newline at end of file diff --git a/ziffers/mapper.py b/ziffers/mapper.py index 3144620..5e4370a 100644 --- a/ziffers/mapper.py +++ b/ziffers/mapper.py @@ -6,8 +6,11 @@ import operator class ZiffersTransformer(Transformer): + def start(self,items): + return Sequence(values=items[0]) + def sequence(self,items): - return Sequence(values=flatten(items)) + return flatten(items) def random_integer(self,s): val = s[0][1:-1].split(",") @@ -18,8 +21,8 @@ class ZiffersTransformer(Transformer): return Range(start=val[0],end=val[1],text=s[0]) def cycle(self, items): - values = items[0].values - return Cyclic(values=values, wrapper="<>") + values = items[0] + return Cyclic(values=values) def pc(self, s): if(len(s)>1): @@ -95,7 +98,7 @@ class ZiffersTransformer(Transformer): return chardur def WS(self,s): - return Item(text=s[0]) + return Whitespace(text=s[0]) def subdivision(self,items): values = flatten(items[0]) @@ -108,7 +111,7 @@ class ZiffersTransformer(Transformer): def eval(self,s): val = s[0] - return Eval(values=val,wrapper="{}") + return Eval(values=val) def operation(self,s): return s @@ -122,14 +125,28 @@ class ZiffersTransformer(Transformer): def list(self,items): if len(items)>1: prefixes = sum_dict(items[0:-1]) - seq = items[-1] - seq.wrapper = "()" - seq.text = prefixes["text"] + seq.text + values = items[-1] + seq = ListSequence(values=values,wrap_start=prefixes["text"]+"(") seq.update_values(prefixes) return seq else: - seq = items[0] - seq.wrapper = "()" + seq = ListSequence(values=items[0]) + return seq + + def repeated_list(self,items): + if len(items)>2: + prefixes = sum_dict(items[0:-2]) # If there are prefixes + if items[-1]!=None: + seq = RepeatedListSequence(values=items[-2],repeats=items[-1],wrap_end=":"+items[-1].text+")") + else: + seq = RepeatedListSequence(values=items[-2],repeats=Integer(text='1', value=1)) + seq.update_values(prefixes) + return seq + else: + if items[-1]!=None: + seq = RepeatedListSequence(values=items[-2],repeats=items[-1],wrap_end=":"+items[-1].text+")") + else: + seq = RepeatedListSequence(values=items[-2],repeats=Integer(text='1', value=1)) return seq def SIGNED_NUMBER(self, s): @@ -139,6 +156,9 @@ class ZiffersTransformer(Transformer): def number(self,s): return s + def cyclic_number(self,s): + return Cyclic(values=s) + def lisp_operation(self,s): op = s[0] values = s[1:] @@ -167,4 +187,10 @@ class ZiffersTransformer(Transformer): return Euclid(**init) def euclid_operator(self,s): - return s.value \ No newline at end of file + return s.value + + def repeat(self,s): + if s[-1]!=None: + return RepeatedSequence(values=s[0],repeats=s[-1],wrap_end=":"+s[-1].text+"]") + else: + return RepeatedSequence(values=s[0],repeats=Integer(value=1,text='1')) diff --git a/ziffers/ziffers.lark b/ziffers/ziffers.lark index c5a2c2a..49f5558 100644 --- a/ziffers/ziffers.lark +++ b/ziffers/ziffers.lark @@ -1,6 +1,6 @@ // Root for the rules - ?root: sequence - sequence: (pc | dur_change | oct_mod | oct_change | WS | chord | cycle | random_integer | random_pitch | random_percent | range | list | lisp_operation | list_op | subdivision | eval | euclid)* + ?root: sequence -> start + sequence: (pc | dur_change | oct_mod | oct_change | WS | chord | cycle | random_integer | random_pitch | random_percent | range | list | repeated_list | lisp_operation | list_op | subdivision | eval | euclid | repeat)* // Pitch classes pc: prefix* pitch @@ -14,15 +14,21 @@ chord: pc pc+ // Valid as integer - ?number: SIGNED_NUMBER | random_integer + ?number: SIGNED_NUMBER | random_integer | cyclic_number + cyclic_number: "<" number (WS number)* ">" + + // Repeats + repeat: "[:" sequence ":" [number] "]" // List list: prefix* "(" sequence ")" + repeated_list: prefix* "(:" sequence ":" [number] ")" // Right recursive list operation list_op: list (operator (list | number))+ operator: /([\+\-\*\/%]|<<|>>)/ + // Euclidean cycles euclid: list euclid_operator list? ?euclid_operator: /<[0-9]+,[0-9]+(,[0-9])?>/