abc_parser/
grammar.rs

1use crate::datatypes::*;
2
3peg::parser! {
4    /// Module generated by [rust_peg](https://crates.io/crates/peg)
5    ///
6    /// Each function corresponds to a rule in the grammar and can be called with an input string
7    /// to begin parsing that type of ABC object.
8    ///
9    /// # Examples
10    /// Usually you will want to parse whole ABC files in which case you will want to use
11    /// `abc::tune_book`.
12    /// ```
13    /// use abc_parser::datatypes::*;
14    /// use abc_parser::abc;
15    ///
16    /// let parsed = abc::tune_book("X:1\nT:Example\nK:D\n").unwrap();
17    /// assert_eq!(
18    ///     parsed,
19    ///     TuneBook::new(vec![], None, vec![
20    ///         Tune::new(
21    ///             TuneHeader::new(vec![
22    ///                 HeaderLine::Field(InfoField::new('X', "1".to_string()), None),
23    ///                 HeaderLine::Field(InfoField::new('T', "Example".to_string()), None),
24    ///                 HeaderLine::Field(InfoField::new('K', "D".to_string()), None),
25    ///             ]),
26    ///             None
27    ///         )
28    ///     ])
29    /// )
30    /// ```
31    /// If you know that you will only be parsing one tune you can use `abc::tune`.
32    /// ```
33    /// use abc_parser::datatypes::*;
34    /// use abc_parser::abc;
35    ///
36    /// let parsed = abc::tune("X:1\nT:Example\nK:D\n").unwrap();
37    /// assert_eq!(
38    ///     parsed,
39    ///     Tune::new(
40    ///         TuneHeader::new(vec![
41    ///             HeaderLine::Field(InfoField::new('X', "1".to_string()), None),
42    ///             HeaderLine::Field(InfoField::new('T', "Example".to_string()), None),
43    ///             HeaderLine::Field(InfoField::new('K', "D".to_string()), None),
44    ///         ]),
45    ///         None
46    ///     )
47    /// )
48    /// ```
49    pub grammar abc() for str {
50        pub rule tune_book() -> TuneBook =
51            c:ignored_line()* h:file_header()? ts:tune()* { TuneBook::new(c, h, ts) } /
52            ![_] { TuneBook::new(vec![], None, vec![]) }
53
54        pub rule ignored_line() -> IgnoredLine =
55            "\n" { IgnoredLine::EmptyLine } /
56            c:comment_line() { IgnoredLine::Comment(c) }
57
58        pub rule file_header() -> FileHeader =
59            l:header_line()* "\n" { FileHeader::new(l) }
60
61        pub rule tune() -> Tune =
62            h:tune_header() b:tune_body()? { Tune::new(h, b) }
63
64        pub rule tune_header() -> TuneHeader =
65            c1:comment_line()*
66            "X:" optional_space() n:number() optional_space() cx:inline_comment()? music_line_end()
67            c2:comment_line()*
68            t:info_field_header(<$("T")>) ct:inline_comment()? music_line_end()
69            extra:info_field_header_non_special()*
70            k:info_field_header(<$("K")>) ck:inline_comment()? music_line_end()
71            c3:comment_line()* {
72                let mut info = c1.into_iter().map(HeaderLine::Comment).collect::<Vec<_>>();
73                info.push(
74                    HeaderLine::Field(
75                        InfoField::new('X', n.to_string()),
76                        cx.map(|s| Comment::Comment(s.to_string())),
77                    )
78                );
79                info.extend(c2.into_iter().map(HeaderLine::Comment));
80                info.push(HeaderLine::Field(t, ct.map(|s| Comment::Comment(s.to_string()))));
81                info.extend(extra);
82                info.push(HeaderLine::Field(k, ck.map(|s| Comment::Comment(s.to_string()))));
83                info.extend(c3.into_iter().map(HeaderLine::Comment));
84                TuneHeader::new(info)
85            }
86
87        rule info_field_header<'a>(n: rule<&'a str>) -> InfoField =
88            name:n() ":" optional_space() t:$(([^ '\n' | '%'])*) {
89                InfoField::new(
90                    name.chars().next().unwrap(),
91                    t.to_string()
92                )
93            }
94
95        rule info_field_inline() -> InfoField =
96            name:$(['H'..='Y' | 'h'..='y' | '+']) ":" optional_space() t:$((!"]"!"\n"[_])*) {
97                InfoField::new(
98                    name.chars().next().unwrap(),
99                    t.to_string()
100                )
101            }
102
103        rule header_line() -> HeaderLine =
104            f:info_field_header(<$(['A'..='Z' | 'a'..='z' | '+'])>)
105            c:inline_comment()?
106            music_line_end() {
107                HeaderLine::Field(f, c.map(|s| Comment::Comment(s.to_string())))
108            } /
109            c:comment_line() { HeaderLine::Comment(c) }
110
111        rule info_field_header_non_special() -> HeaderLine =
112            f:info_field_header(<$(!['K' | 'X']['A'..='Z' | 'a'..='z' | '+'])>)
113            c:inline_comment()?
114            music_line_end() {
115                HeaderLine::Field(f, c.map(|s| Comment::Comment(s.to_string())))
116            } /
117            c:comment_line() { HeaderLine::Comment(c) }
118
119        pub rule tune_body() -> TuneBody =
120            l:tune_line()+ { TuneBody::new(l) } /
121            "\n" { TuneBody::new(vec![]) }
122
123        pub rule tune_line() -> TuneLine =
124            c:comment_line() { TuneLine::Comment(c) } /
125            m:music_line() { TuneLine::Music(m) } /
126            s:symbol_line() { TuneLine::Symbol(s) } /
127            l:lyric_line() { TuneLine::Lyric(l) } /
128            i:inline_field_line() { TuneLine::Music(i) }
129
130        pub rule music_line() -> MusicLine =
131            s:music_symbol()+ c:inline_comment()? music_line_end() {
132                let mut symbols = s;
133                if let Some(c) = c {
134                    symbols.push(
135                        MusicSymbol::Comment(Comment::Comment(c.to_string()))
136                    );
137                }
138                MusicLine::new(symbols)
139            }
140
141        pub rule symbol_line() -> SymbolLine =
142            "s:" s:symbol_line_symbol()* music_line_end() {
143                SymbolLine::new(s)
144            }
145
146        pub rule lyric_line() -> LyricLine =
147            "w:" s:lyric_line_symbol()* music_line_end() {
148                LyricLine::new(s)
149            }
150
151        pub rule inline_field_line() -> MusicLine =
152            i:info_field_inline() music_line_end() {
153                MusicLine::new(vec![MusicSymbol::InlineField(i, false)])
154            }
155
156        pub rule music_symbol() -> MusicSymbol =
157            broken_rhythm() /
158            note() /
159            rest() /
160            spacer() /
161            chord() /
162            "[" i:info_field_inline() "]" { MusicSymbol::InlineField(i, true) } /
163            bar() /
164            beam() /
165            ending() /
166            grace_notes() /
167            tuplet() /
168            slur() /
169            d:decoration() { MusicSymbol::Decoration(d) } /
170            a:annotation() { MusicSymbol::Annotation(a) } /
171            music_space() /
172            reserved()
173
174        pub rule symbol_line_symbol() -> SymbolLineSymbol =
175            s:symbol_alignment() { SymbolLineSymbol::SymbolAlignment(s) } /
176            a:annotation() { SymbolLineSymbol::Annotation(a) } /
177            d:decoration() { SymbolLineSymbol::Decoration(d) } /
178            s:space() { SymbolLineSymbol::Space(s.to_string()) }
179
180        pub rule lyric_line_symbol() -> LyricSymbol =
181            s:symbol_alignment() { LyricSymbol::SymbolAlignment(s) } /
182            l:$([^ '-' | '_' | '*' | '~' | '\\' | '|' | ' ' | '\n']+) {
183                LyricSymbol::Syllable(l.to_string())
184            } /
185            s:space() { LyricSymbol::Space(s.to_string()) }
186
187        //
188        // Music Symbols
189        //
190
191        pub rule broken_rhythm() -> MusicSymbol =
192            b:(t:broken_rhythm_target() s:music_space()? {
193                match s {
194                    Some(s) => vec![t, s],
195                    None => vec![t],
196                }
197            })
198            r:$("<"*<1,3> / ">"*<1,3>)
199                a:(s:music_space()? t:(broken_rhythm() / broken_rhythm_target()) {
200                match s {
201                    Some(s) => vec![s, t],
202                    None => vec![t],
203                }
204            }) {
205                MusicSymbol::BrokenRhythm {
206                    rhythm: r.to_string(),
207                    before: b,
208                    after: a,
209                }
210            }
211
212        pub rule broken_rhythm_target() -> MusicSymbol =
213            note() /
214            rest() /
215            chord() /
216            grace_notes()
217
218        pub rule note() -> MusicSymbol =
219            a:accidental()? n:note_uppercase() o:octave()? l:length()? t:tie()? {
220                MusicSymbol::new_note(
221                    a, n, o.unwrap_or(1), l, t
222                )
223            } /
224            a:accidental()? n:note_lowercase() o:octave()? l:length()? t:tie()? {
225                MusicSymbol::new_note(
226                    a, n, o.unwrap_or(1) + 1, l, t
227                )
228            }
229
230        pub rule bar() -> MusicSymbol =
231            b:$(
232                "."?
233                (
234                    [':' | '|' | '\\' | ']'] /
235                    "[" !['H'..='Y' | 'h'..='y' | '1'..='9']
236                )+
237            )
238            e:ending_number()?
239            { MusicSymbol::Bar(b.to_string(), e) }
240
241        pub rule beam() -> MusicSymbol =
242             b:$("`"+) { MusicSymbol::Beam(b.to_string()) }
243
244        pub rule slur() -> MusicSymbol =
245            "(" { MusicSymbol::Slur(Slur::Begin) } /
246            ".(" { MusicSymbol::Slur(Slur::BeginDotted) } /
247            ")" { MusicSymbol::Slur(Slur::End) }
248
249        pub rule rest() -> MusicSymbol =
250            "z" l:length()? { MusicSymbol::Rest(Rest::Note(l)) } /
251            "Z" l:length()? { MusicSymbol::Rest(Rest::Measure(l)) } /
252            "x" l:length()? { MusicSymbol::Rest(Rest::NoteHidden(l)) } /
253            "X" l:length()? { MusicSymbol::Rest(Rest::MeasureHidden(l)) }
254
255        pub rule spacer() -> MusicSymbol =
256            "y" { MusicSymbol::Spacer }
257
258        pub rule ending() -> MusicSymbol =
259            "[" i:ending_identifier() { MusicSymbol::Ending(i.to_string()) }
260
261        pub rule ending_identifier() -> String =
262            n1:ending_number() n2:("," n:ending_number() { n })* {
263                let mut s = n1;
264                for n in n2 {
265                    s.push(',');
266                    s.push_str(&n);
267                }
268                s
269            }
270
271        pub rule ending_number() -> String =
272            d1:number_digits() d2:("-" d:number_digits() { d } )? {
273                let mut s = d1.to_string();
274                if let Some(d2) = d2 {
275                    s.push('-');
276                    s.push_str(d2);
277                }
278                s
279            }
280
281        pub rule chord() -> MusicSymbol =
282            "[" n:chord_inner_symbol()+ "]" l:length()? t:tie()? {
283                MusicSymbol::Chord {
284                    notes: n,
285                    length: l,
286                    tie: t,
287                }
288            }
289
290        pub rule chord_inner_symbol() -> MusicSymbol =
291            note() /
292            d:decoration() { MusicSymbol::Decoration(d) }
293
294        pub rule grace_notes() -> MusicSymbol =
295            "{" a:"/"? n:grace_notes_inner_symbol()+ "}" {
296                MusicSymbol::GraceNotes {
297                    acciaccatura: a,
298                    notes: n
299                }
300            }
301
302        pub rule grace_notes_inner_symbol() -> MusicSymbol =
303            broken_rhythm() /
304            note() /
305            music_space()
306
307        pub rule tuplet() -> MusicSymbol =
308            "("
309            p:digit_at_least_2()
310            q:(":" x:digit_at_least_1()? { x })?
311            r:(":" x:digit_at_least_1()? { x })? {
312                MusicSymbol::new_tuplet(p, q.flatten(), r.flatten())
313            }
314
315        pub rule music_space() -> MusicSymbol =
316            s:space() { MusicSymbol::Space(s.to_string()) }
317
318        pub rule reserved() -> MusicSymbol =
319            r:['#' | '*' | ';' | '?' | '@'] {
320                MusicSymbol::Reserved(r.to_string())
321            }
322
323        // Music symbol components
324
325        rule accidental() -> Accidental =
326            "^^" { Accidental::DoubleSharp } /
327            "__" { Accidental::DoubleFlat } /
328            "^" { Accidental::Sharp } /
329            "_" { Accidental::Flat } /
330            "=" { Accidental::Natural }
331
332        pub rule note_uppercase() -> Note =
333            "A" { Note::A } /
334            "B" { Note::B } /
335            "C" { Note::C } /
336            "D" { Note::D } /
337            "E" { Note::E } /
338            "F" { Note::F } /
339            "G" { Note::G }
340
341        pub rule note_lowercase() -> Note =
342            "a" { Note::A } /
343            "b" { Note::B } /
344            "c" { Note::C } /
345            "d" { Note::D } /
346            "e" { Note::E } /
347            "f" { Note::F } /
348            "g" { Note::G }
349
350        rule octave() -> i8 =
351            p:$([',' | '\'']+) {
352                let mut octave = 1;
353                for c in p.chars() {
354                    match c {
355                        ',' => octave -= 1,
356                        '\'' => octave += 1,
357                        _ => panic!("Parser malfunctioned. Unexpected octave identifier")
358                    }
359                }
360                octave
361            }
362
363        rule tie() -> Tie =
364            ".-" { Tie::Dotted } /
365            "-" { Tie::Solid }
366
367        //
368        // Shared Symbols
369        //
370
371        rule inline_comment() -> &'input str =
372            "%" c:$([^'\n']*) { c }
373
374        rule comment_line() -> Comment =
375            "%" c:inline_comment() music_line_end() {
376                Comment::StylesheetDirective(c.to_string())
377            } /
378            s:space() c:inline_comment() music_line_end() {
379                Comment::CommentLine(s.to_string(), c.to_string())
380            } /
381            c:inline_comment() music_line_end() {
382                Comment::CommentLine("".to_string(), c.to_string())
383            }
384
385        rule length() -> Length =
386            "/" n:number() {
387                Length::new(1.0 / (n as f32))
388            } /
389            s:$("/"+) {
390                Length::new(1.0 / (s.len() as f32).exp2())
391            } /
392            n1:number() "/" n2:number() {
393                Length::new((n1 as f32) / (n2 as f32))
394            } /
395            n:number() { Length::new(n as f32) }
396
397        pub rule annotation() -> Annotation =
398            "\"" p:placement()? s:$((!"\""[_])*) "\"" {
399                Annotation::new(p, s.to_string())
400            }
401
402        pub rule placement() -> Placement =
403            "^" { Placement::Above } /
404            "_" { Placement::Below } /
405            "<" { Placement::Left } /
406            ">" { Placement::Right } /
407            "@" { Placement::Auto }
408
409        rule decoration() -> Decoration =
410            "." { Decoration::Staccato } /
411            "~" { Decoration::Roll } /
412            "H" { Decoration::Fermata } /
413            "L" { Decoration::Accent } /
414            "M" { Decoration::LowerMordent } /
415            "O" { Decoration::Coda } /
416            "P" { Decoration::UpperMordent } /
417            "S" { Decoration::Segno } /
418            "T" { Decoration::Trill } /
419            "u" { Decoration::UpBow } /
420            "v" { Decoration::DownBow } /
421            "!" d:$((!['\n' | '!'][_])+) "!" {
422                Decoration::Unresolved(d.to_string())
423            }
424
425        pub rule symbol_alignment() -> SymbolAlignment =
426            "-" { SymbolAlignment::Break } /
427            "_" { SymbolAlignment::Extend } /
428            "*" { SymbolAlignment::Skip } /
429            "~" { SymbolAlignment::Space } /
430            "\\-" { SymbolAlignment::Hyphen } /
431            "|" { SymbolAlignment::Bar }
432
433        //
434        // Primitives
435        //
436
437        rule number() -> u32 =
438            n:number_digits() { n.parse().unwrap() }
439
440        rule number_digits() -> &'input str =
441            $(['1'..='9']['0'..='9']*)
442
443        rule digit_at_least_1() -> u32 =
444            n:$(['1'..='9']) { n.parse().unwrap() }
445
446        rule digit_at_least_2() -> u32 =
447            n:$(['2'..='9']) { n.parse().unwrap() }
448
449        rule space() -> &'input str =
450            s:$([' ' | '\t']+) { s }
451
452        rule optional_space() =
453            [' ' | '\t']*
454
455        rule music_line_end() =
456            "\n" / ![_]
457    }
458}