1pub(crate) mod key;
2pub(crate) mod meter;
3pub(crate) mod misc;
4pub(crate) mod tempo;
5pub(crate) mod voice;
6
7use crate::diagnostic::{Diagnostic, Span};
8use crate::model::Fraction;
9use crate::options::{AbcSpecVersion, DiagnosticOptions, ParseMode, ParseOptions};
10use crate::source::SourceText;
11use crate::syntax::tune::{ContinuationKind, LineContext, LineKind, SurfaceMap};
12
13pub(crate) use key::parse_key;
14use meter::ensure_default_unit_note_length;
15pub(crate) use meter::{parse_meter, parse_unit_note_length};
16use misc::{
17 invalid_field_warning, parse_version_line, parse_version_value, split_first_word,
18 trimmed_uncommented_span, unknown_field_warning, unknown_instruction_warning,
19};
20use tempo::{parse_decoration_delimiter, parse_line_break_mode, parse_macro, parse_user_symbol};
21use voice::parse_voice;
22pub(crate) use voice::{parse_score_directive, parse_voice_for_music};
23
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct ParsedAbcFields {
26 pub fields: Vec<ParsedField>,
27 pub file_header: FieldState,
28 pub tunes: Vec<ParsedTuneFields>,
29 pub state_transitions: Vec<StateTransition>,
30}
31
32impl ParsedAbcFields {
33 pub fn tune(&self, tune_index: usize) -> Option<&ParsedTuneFields> {
34 self.tunes.iter().find(|tune| tune.index == tune_index)
35 }
36
37 pub fn field(&self, index: usize) -> Option<&ParsedField> {
38 self.fields.get(index)
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct ParsedTuneFields {
44 pub index: usize,
45 pub span: Span,
46 pub inherited_file_header: FieldState,
47 pub header: FieldState,
48 pub current: FieldState,
49 pub field_indices: Vec<usize>,
50 pub header_field_indices: Vec<usize>,
51 pub body_field_indices: Vec<usize>,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct ParsedField {
56 pub code: char,
57 pub line_index: usize,
58 pub context: LineContext,
59 pub line_span: Span,
60 pub marker_span: Span,
61 pub value_span: Span,
62 pub parsed_value_span: Span,
63 pub continuation_spans: Vec<Span>,
64 pub kind: ParsedFieldKind,
65}
66
67#[allow(clippy::large_enum_variant)]
68#[derive(Debug, Clone, PartialEq, Eq)]
69pub enum ParsedFieldKind {
70 Version(VersionDirective),
71 Reference(Spanned<String>),
72 Title(Spanned<String>),
73 Meter(Spanned<Meter>),
74 UnitNoteLength(Spanned<UnitNoteLength>),
75 Key(Spanned<KeySignature>),
76 Tempo(Spanned<String>),
77 Part(Spanned<String>),
78 Voice(Spanned<VoiceDefinition>),
79 TextMetadata(TextMetadataField),
80 LyricLine(Spanned<String>),
81 SymbolLine(Spanned<String>),
82 Interpretation(InterpretationField),
83 UserSymbol(UserSymbol),
84 Macro(MacroDefinition),
85 Continuation(Spanned<String>),
86 Unknown(UnknownField),
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct UnknownField {
91 pub code: char,
92 pub value: Spanned<String>,
93}
94
95#[derive(Debug, Clone, PartialEq, Eq)]
96pub struct VersionDirective {
97 pub version: Option<Spanned<AbcVersion>>,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub enum InterpretationField {
102 AbcVersion {
103 version: Spanned<AbcVersion>,
104 },
105 AbcCharset {
106 charset: Spanned<String>,
107 },
108 LineBreak {
109 mode: Spanned<LineBreakMode>,
110 },
111 Decoration {
112 delimiter: Spanned<DecorationDelimiter>,
113 },
114 Score {
115 directive: ScoreDirective,
116 },
117 CromaTimeSymbol {
118 value: Spanned<String>,
119 },
120 Unknown {
121 directive: Spanned<String>,
122 value: Spanned<String>,
123 },
124}
125
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct TextMetadataField {
128 pub code: char,
129 pub value: Spanned<String>,
130}
131
132#[derive(Debug, Clone, PartialEq, Eq)]
133pub struct UserSymbol {
134 pub symbol: Option<Spanned<char>>,
135 pub replacement: Spanned<String>,
136}
137
138#[derive(Debug, Clone, PartialEq, Eq)]
139pub struct UserSymbolDefinition {
140 pub symbol: Spanned<char>,
141 pub replacement: Spanned<String>,
142 pub span: Span,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq)]
146pub struct MacroDefinition {
147 pub name: Spanned<String>,
148 pub replacement: Spanned<String>,
149}
150
151#[derive(Debug, Clone, PartialEq, Eq)]
152pub struct VoiceDefinition {
153 pub id: Spanned<String>,
154 pub properties: Spanned<String>,
155 pub parsed_properties: VoiceProperties,
156}
157
158#[derive(Debug, Clone, PartialEq, Eq, Default)]
159pub struct VoiceProperties {
160 pub name: Option<Spanned<String>>,
161 pub nm: Option<Spanned<String>>,
162 pub subname: Option<Spanned<String>>,
163 pub snm: Option<Spanned<String>>,
164 pub clef: Option<Spanned<String>>,
165 pub stem: Option<Spanned<StemDirection>>,
166 pub octave: Option<Spanned<String>>,
167 pub transpose: Option<Spanned<String>>,
168 pub middle: Option<Spanned<String>>,
169 pub other: Vec<VoiceProperty>,
170}
171
172#[derive(Debug, Clone, PartialEq, Eq)]
173pub struct VoiceProperty {
174 pub key: Spanned<String>,
175 pub value: Spanned<String>,
176 pub span: Span,
177}
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180pub enum StemDirection {
181 Up,
182 Down,
183}
184
185#[derive(Debug, Clone, PartialEq, Eq)]
186pub struct ScoreDirective {
187 pub value: Spanned<String>,
188 pub tokens: Vec<ScoreDirectiveToken>,
189}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
192pub struct ScoreDirectiveToken {
193 pub span: Span,
194 pub kind: ScoreDirectiveTokenKind,
195}
196
197#[derive(Debug, Clone, PartialEq, Eq)]
198pub enum ScoreDirectiveTokenKind {
199 Voice(String),
200 GroupStart(char),
201 GroupEnd(char),
202 StaffSeparator,
203 MeasureSeparator,
204 FloatingVoiceMarker,
205}
206
207#[derive(Debug, Clone, PartialEq, Eq)]
208pub struct Spanned<T> {
209 pub value: T,
210 pub span: Span,
211}
212
213impl<T> Spanned<T> {
214 pub fn new(value: T, span: Span) -> Self {
215 Self { value, span }
216 }
217}
218
219#[derive(Debug, Clone, PartialEq, Eq)]
220pub struct FieldState {
221 pub dialect: DialectState,
222 pub meter: Option<Spanned<Meter>>,
223 pub unit_note_length: Option<Spanned<UnitNoteLength>>,
224 pub key: Option<Spanned<KeySignature>>,
225 pub voice: Option<Spanned<VoiceDefinition>>,
226 pub voices: Vec<Spanned<VoiceDefinition>>,
227}
228
229impl FieldState {
230 pub fn from_options(options: ParseOptions) -> Self {
231 Self {
232 dialect: DialectState::from_options(options),
233 meter: None,
234 unit_note_length: None,
235 key: None,
236 voice: None,
237 voices: Vec::new(),
238 }
239 }
240
241 pub(crate) fn unit_note_length_fraction(&self) -> Fraction {
242 self.unit_note_length
243 .as_ref()
244 .map(|unit| unit.value.fraction.to_model_fraction())
245 .unwrap_or_else(|| NoteLengthFraction::new(1, 8).to_model_fraction())
246 }
247}
248
249#[derive(Debug, Clone, PartialEq, Eq)]
250pub struct DialectState {
251 pub spec: AbcSpecVersion,
252 pub mode: ParseMode,
253 pub diagnostics: DiagnosticOptions,
254 pub declared_version: Option<Spanned<AbcVersion>>,
255 pub charset: Option<Spanned<String>>,
256 pub line_break: LineBreakMode,
257 pub decoration_delimiter: DecorationDelimiter,
258 pub user_symbols: Vec<UserSymbolDefinition>,
259}
260
261impl DialectState {
262 pub fn from_options(options: ParseOptions) -> Self {
263 Self {
264 spec: options.spec,
265 mode: options.mode,
266 diagnostics: options.diagnostics,
267 declared_version: None,
268 charset: None,
269 line_break: LineBreakMode::default(),
270 decoration_delimiter: DecorationDelimiter::Bang,
271 user_symbols: Vec::new(),
272 }
273 }
274}
275
276#[derive(Debug, Clone, PartialEq, Eq)]
277pub struct AbcVersion {
278 pub raw: String,
279 pub major: Option<u16>,
280 pub minor: Option<u16>,
281}
282
283#[derive(Debug, Clone, Copy, PartialEq, Eq)]
284pub struct LineBreakMode {
285 pub end_of_line: bool,
286 pub dollar: bool,
287 pub bang: bool,
288}
289
290impl LineBreakMode {
291 pub const fn none() -> Self {
292 Self {
293 end_of_line: false,
294 dollar: false,
295 bang: false,
296 }
297 }
298
299 pub const fn uses_bang(self) -> bool {
300 self.bang
301 }
302}
303
304impl Default for LineBreakMode {
305 fn default() -> Self {
306 Self {
307 end_of_line: true,
308 dollar: true,
309 bang: false,
310 }
311 }
312}
313
314#[derive(Debug, Clone, Copy, PartialEq, Eq)]
315pub enum DecorationDelimiter {
316 Bang,
317 Plus,
318}
319
320#[derive(Debug, Clone, PartialEq, Eq)]
321pub struct StateTransition {
322 pub scope: FieldScope,
323 pub field_index: Option<usize>,
324 pub span: Span,
325 pub kind: StateTransitionKind,
326}
327
328#[derive(Debug, Clone, Copy, PartialEq, Eq)]
329pub enum FieldScope {
330 Preamble,
331 FileHeader,
332 BetweenBlocks,
333 FreeText,
334 TypesetText,
335 TuneHeader { tune_index: usize },
336 TuneBody { tune_index: usize },
337 TuneTerminator { tune_index: usize },
338}
339
340impl From<LineContext> for FieldScope {
341 fn from(context: LineContext) -> Self {
342 match context {
343 LineContext::Preamble => Self::Preamble,
344 LineContext::FileHeader => Self::FileHeader,
345 LineContext::BetweenBlocks => Self::BetweenBlocks,
346 LineContext::FreeText => Self::FreeText,
347 LineContext::TypesetText => Self::TypesetText,
348 LineContext::TuneHeader { tune_index } => Self::TuneHeader { tune_index },
349 LineContext::TuneBody { tune_index } => Self::TuneBody { tune_index },
350 LineContext::TuneTerminator { tune_index } => Self::TuneTerminator { tune_index },
351 }
352 }
353}
354
355#[allow(clippy::large_enum_variant)]
356#[derive(Debug, Clone, PartialEq, Eq)]
357pub enum StateTransitionKind {
358 SpecVersion {
359 from: AbcSpecVersion,
360 to: AbcSpecVersion,
361 },
362 InterpretationMode {
363 from: ParseMode,
364 to: ParseMode,
365 },
366 Charset {
367 from: Option<String>,
368 to: String,
369 },
370 LineBreak {
371 from: LineBreakMode,
372 to: LineBreakMode,
373 },
374 DecorationDelimiter {
375 from: DecorationDelimiter,
376 to: DecorationDelimiter,
377 },
378 Meter {
379 from: Option<Meter>,
380 to: Meter,
381 },
382 UnitNoteLength {
383 from: Option<UnitNoteLength>,
384 to: UnitNoteLength,
385 },
386 Key {
387 from: Option<KeySignature>,
388 to: KeySignature,
389 },
390 Voice {
391 from: Option<VoiceDefinition>,
392 to: VoiceDefinition,
393 },
394 UserSymbol {
395 symbol: Option<char>,
396 },
397 Macro {
398 name: String,
399 },
400}
401
402#[derive(Debug, Clone, PartialEq, Eq)]
403pub struct Meter {
404 pub raw: String,
405 pub kind: MeterKind,
406}
407
408#[derive(Debug, Clone, PartialEq, Eq)]
409pub enum MeterKind {
410 CommonTime,
411 CutTime,
412 None,
413 Fraction { numerator: u32, denominator: u32 },
414 Complex,
415}
416
417#[derive(Debug, Clone, Copy, PartialEq, Eq)]
418pub struct NoteLengthFraction {
419 pub numerator: u32,
420 pub denominator: u32,
421}
422
423impl NoteLengthFraction {
424 pub const fn new(numerator: u32, denominator: u32) -> Self {
425 Self {
426 numerator,
427 denominator,
428 }
429 }
430
431 pub(crate) fn to_model_fraction(self) -> Fraction {
432 Fraction::new(self.numerator, self.denominator)
433 }
434}
435
436#[derive(Debug, Clone, Copy, PartialEq, Eq)]
437pub struct UnitNoteLength {
438 pub fraction: NoteLengthFraction,
439 pub origin: UnitNoteLengthOrigin,
440}
441
442#[derive(Debug, Clone, Copy, PartialEq, Eq)]
443pub enum UnitNoteLengthOrigin {
444 Explicit,
445 DefaultFromMeter,
446 DefaultFreeMeter,
447}
448
449#[derive(Debug, Clone, PartialEq, Eq)]
450pub struct KeySignature {
451 pub raw: String,
452 pub tonic: Option<KeyTonic>,
453 pub mode: KeyMode,
454 pub accidentals: Vec<KeyAccidental>,
455 pub explicit: bool,
456 pub compact_accidentals_ignored: bool,
457 pub tonic_trailing_junk_ignored: bool,
461 pub properties: VoiceProperties,
465}
466
467#[derive(Debug, Clone, Copy, PartialEq, Eq)]
468pub struct KeyTonic {
469 pub step: char,
470 pub accidental: Option<KeyTonicAccidental>,
471}
472
473#[derive(Debug, Clone, Copy, PartialEq, Eq)]
474pub enum KeyTonicAccidental {
475 Sharp,
476 Flat,
477}
478
479#[derive(Debug, Clone, PartialEq, Eq)]
480pub enum KeyMode {
481 Major,
482 Ionian,
483 Minor,
484 Aeolian,
485 Mixolydian,
486 Dorian,
487 Phrygian,
488 Lydian,
489 Locrian,
490 Explicit,
491 None,
492 HighlandPipes,
493 HighlandPipesMarked,
494 Unknown(String),
495}
496
497#[derive(Debug, Clone, PartialEq, Eq)]
498pub struct KeyAccidental {
499 pub sign: AccidentalSign,
500 pub note: Spanned<char>,
501 pub span: Span,
502}
503
504#[derive(Debug, Clone, Copy, PartialEq, Eq)]
505pub enum AccidentalSign {
506 DoubleFlat,
507 Flat,
508 Natural,
509 Sharp,
510 DoubleSharp,
511}
512
513pub(crate) fn parse_fields(
514 source: &SourceText,
515 surface: &SurfaceMap,
516 options: ParseOptions,
517) -> (ParsedAbcFields, Vec<Diagnostic>) {
518 let mut parser = FieldParser::new(source, surface, options);
519 parser.parse();
520 parser.finish()
521}
522
523struct FieldParser<'source> {
524 source: &'source SourceText,
525 surface: &'source SurfaceMap,
526 options: ParseOptions,
527 fields: Vec<ParsedField>,
528 diagnostics: Vec<Diagnostic>,
529 state_transitions: Vec<StateTransition>,
530 file_header: FieldState,
531 tunes: Vec<Option<ParsedTuneFields>>,
532}
533
534impl<'source> FieldParser<'source> {
535 fn new(
536 source: &'source SourceText,
537 surface: &'source SurfaceMap,
538 options: ParseOptions,
539 ) -> Self {
540 let tunes = surface.line_map.tunes.iter().map(|_| None).collect();
541 Self {
542 source,
543 surface,
544 options,
545 fields: Vec::new(),
546 diagnostics: Vec::new(),
547 state_transitions: Vec::new(),
548 file_header: FieldState::from_options(options),
549 tunes,
550 }
551 }
552
553 fn parse(&mut self) {
554 for line in &self.surface.line_map.lines {
555 match line.kind {
556 LineKind::VersionLine => self.parse_version_line(line),
557 LineKind::InformationField => self.parse_information_field(line),
558 LineKind::FieldContinuation => self.parse_field_continuation(line),
559 LineKind::EmptyLine
560 | LineKind::Comment
561 | LineKind::StylesheetDirective
562 | LineKind::MusicCode
563 | LineKind::FreeText
564 | LineKind::TypesetTextDirective => {}
565 }
566 }
567
568 for index in 0..self.tunes.len() {
569 if let Some(tune) = self.tunes.get_mut(index).and_then(Option::as_mut) {
570 ensure_default_unit_note_length(&mut tune.header, tune.span, self.options);
571 ensure_default_unit_note_length(&mut tune.current, tune.span, self.options);
572 }
573 }
574 }
575
576 fn parse_version_line(&mut self, line: &crate::syntax::tune::ClassifiedLine) {
577 let Some(line_text) = self.source.slice(line.text_span) else {
578 return;
579 };
580 let Some((marker_span, version)) = parse_version_line(line_text, line.text_span.start)
581 else {
582 return;
583 };
584 let value_span = version
585 .as_ref()
586 .map(|version| version.span)
587 .unwrap_or_else(|| Span::new(marker_span.end, marker_span.end));
588 let field = ParsedField {
589 code: '%',
590 line_index: line.index,
591 context: line.context,
592 line_span: line.span,
593 marker_span,
594 value_span,
595 parsed_value_span: value_span,
596 continuation_spans: Vec::new(),
597 kind: ParsedFieldKind::Version(VersionDirective { version }),
598 };
599
600 let field_index = self.push_field(field);
601 let version = match &self.fields[field_index].kind {
602 ParsedFieldKind::Version(version) => version.version.clone(),
603 _ => None,
604 };
605 self.apply_version_directive(field_index, line.context, version.as_ref(), value_span);
606 }
607
608 fn parse_information_field(&mut self, line: &crate::syntax::tune::ClassifiedLine) {
609 let Some(field_header) = line.field else {
610 return;
611 };
612 let value = self.field_value(line, field_header.value_span);
613 let kind = self.parse_field_kind(field_header.code, value.clone());
614 let field = ParsedField {
615 code: field_header.code,
616 line_index: line.index,
617 context: line.context,
618 line_span: line.span,
619 marker_span: field_header.marker_span,
620 value_span: field_header.value_span,
621 parsed_value_span: value.span,
622 continuation_spans: self.continuation_spans(line.index),
623 kind,
624 };
625 let field_index = self.push_field(field);
626 self.apply_field_state(field_index);
627 }
628
629 fn parse_field_continuation(&mut self, line: &crate::syntax::tune::ClassifiedLine) {
630 let Some(marker_span) = line.marker_span else {
631 return;
632 };
633 let value_span = Span::new(marker_span.end, line.text_span.end);
634 let value = self.field_value(line, value_span);
635 let field = ParsedField {
636 code: '+',
637 line_index: line.index,
638 context: line.context,
639 line_span: line.span,
640 marker_span,
641 value_span,
642 parsed_value_span: value.span,
643 continuation_spans: Vec::new(),
644 kind: ParsedFieldKind::Continuation(value),
645 };
646 self.push_field(field);
647 }
648
649 fn field_value(
650 &self,
651 line: &crate::syntax::tune::ClassifiedLine,
652 value_span: Span,
653 ) -> Spanned<String> {
654 let mut spans = vec![trimmed_uncommented_span(self.source, line, value_span)];
655 spans.extend(self.continuation_value_spans(line.index));
656
657 let mut value = String::new();
658 let mut parsed_span = spans.first().copied().unwrap_or(value_span);
659 for span in spans.into_iter().filter(|span| !span.is_empty()) {
660 if !value.is_empty() {
661 value.push('\n');
662 }
663 if let Some(text) = self.source.slice(span) {
664 value.push_str(text);
665 }
666 parsed_span = if parsed_span.is_empty() {
667 span
668 } else {
669 Span::new(
670 parsed_span.start.min(span.start),
671 parsed_span.end.max(span.end),
672 )
673 };
674 }
675
676 Spanned::new(value, parsed_span)
677 }
678
679 fn continuation_spans(&self, line_index: usize) -> Vec<Span> {
680 self.surface
681 .line_map
682 .continuation_edges
683 .iter()
684 .filter(|edge| {
685 edge.kind == ContinuationKind::FieldContinuation && edge.from_line == line_index
686 })
687 .filter_map(|edge| {
688 self.surface
689 .line_map
690 .lines
691 .get(edge.to_line)
692 .map(|line| line.content_span)
693 })
694 .collect()
695 }
696
697 fn continuation_value_spans(&self, line_index: usize) -> Vec<Span> {
698 self.surface
699 .line_map
700 .continuation_edges
701 .iter()
702 .filter(|edge| {
703 edge.kind == ContinuationKind::FieldContinuation && edge.from_line == line_index
704 })
705 .filter_map(|edge| {
706 let line = self.surface.line_map.lines.get(edge.to_line)?;
707 let marker_span = line.marker_span?;
708 Some(trimmed_uncommented_span(
709 self.source,
710 line,
711 Span::new(marker_span.end, line.text_span.end),
712 ))
713 })
714 .collect()
715 }
716
717 fn parse_field_kind(&mut self, code: char, value: Spanned<String>) -> ParsedFieldKind {
718 match code {
719 'X' => ParsedFieldKind::Reference(value),
720 'T' => ParsedFieldKind::Title(value),
721 'M' => ParsedFieldKind::Meter(Spanned::new(parse_meter(&value.value), value.span)),
722 'L' => match parse_unit_note_length(&value.value) {
723 Some(unit) => ParsedFieldKind::UnitNoteLength(Spanned::new(unit, value.span)),
724 None => {
725 self.diagnostics.push(invalid_field_warning(
726 "abc.field.invalid_l",
727 "L",
728 value.span,
729 ));
730 ParsedFieldKind::Unknown(UnknownField {
731 code,
732 value: value.clone(),
733 })
734 }
735 },
736 'K' => ParsedFieldKind::Key(Spanned::new(
737 parse_key(&value.value, value.span),
738 value.span,
739 )),
740 'Q' => ParsedFieldKind::Tempo(value),
741 'P' => ParsedFieldKind::Part(value),
742 'V' => {
743 let span = value.span;
744 ParsedFieldKind::Voice(Spanned::new(parse_voice(value), span))
745 }
746 'C' | 'O' | 'R' | 'N' | 'Z' | 'W' => {
747 ParsedFieldKind::TextMetadata(TextMetadataField { code, value })
748 }
749 'w' => ParsedFieldKind::LyricLine(value),
750 's' => ParsedFieldKind::SymbolLine(value),
751 'I' => self.parse_interpretation(value),
752 'U' => ParsedFieldKind::UserSymbol(parse_user_symbol(value)),
753 'm' => ParsedFieldKind::Macro(parse_macro(value)),
754 _ => {
755 self.diagnostics
756 .push(unknown_field_warning(code, value.span));
757 ParsedFieldKind::Unknown(UnknownField { code, value })
758 }
759 }
760 }
761
762 fn parse_interpretation(&mut self, value: Spanned<String>) -> ParsedFieldKind {
763 let (directive, rest) = split_first_word(value);
764 let directive_lower = directive.value.to_ascii_lowercase();
765 match directive_lower.as_str() {
766 "abc-version" => {
767 let version = parse_version_value(rest.value.trim());
768 if version.major.is_none() {
769 self.diagnostics.push(invalid_field_warning(
770 "abc.field.invalid_abc_version",
771 "I:abc-version",
772 rest.span,
773 ));
774 }
775 ParsedFieldKind::Interpretation(InterpretationField::AbcVersion {
776 version: Spanned::new(version, rest.span),
777 })
778 }
779 "abc-charset" => {
780 ParsedFieldKind::Interpretation(InterpretationField::AbcCharset { charset: rest })
781 }
782 "linebreak" => match parse_line_break_mode(&rest.value) {
783 Some(mode) => ParsedFieldKind::Interpretation(InterpretationField::LineBreak {
784 mode: Spanned::new(mode, rest.span),
785 }),
786 None => {
787 self.diagnostics.push(invalid_field_warning(
788 "abc.field.invalid_linebreak",
789 "I:linebreak",
790 rest.span,
791 ));
792 ParsedFieldKind::Interpretation(InterpretationField::Unknown {
793 directive,
794 value: rest,
795 })
796 }
797 },
798 "decoration" => match parse_decoration_delimiter(&rest.value) {
799 Some(delimiter) => {
800 ParsedFieldKind::Interpretation(InterpretationField::Decoration {
801 delimiter: Spanned::new(delimiter, rest.span),
802 })
803 }
804 None => {
805 self.diagnostics.push(invalid_field_warning(
806 "abc.field.invalid_decoration",
807 "I:decoration",
808 rest.span,
809 ));
810 ParsedFieldKind::Interpretation(InterpretationField::Unknown {
811 directive,
812 value: rest,
813 })
814 }
815 },
816 "score" | "staves" => ParsedFieldKind::Interpretation(InterpretationField::Score {
818 directive: parse_score_directive(rest),
819 }),
820 "croma-time-symbol" => {
821 ParsedFieldKind::Interpretation(InterpretationField::CromaTimeSymbol {
822 value: rest,
823 })
824 }
825 _ => {
826 if self
827 .options
828 .diagnostics
829 .should_emit_croma_carrier_warning(&directive.value)
830 {
831 self.diagnostics.push(unknown_instruction_warning(
832 &directive.value,
833 directive.span,
834 ));
835 }
836 ParsedFieldKind::Interpretation(InterpretationField::Unknown {
837 directive,
838 value: rest,
839 })
840 }
841 }
842 }
843
844 fn push_field(&mut self, field: ParsedField) -> usize {
845 let field_index = self.fields.len();
846 let context = field.context;
847 self.fields.push(field);
848
849 match context {
850 LineContext::TuneHeader { tune_index } | LineContext::TuneBody { tune_index } => {
851 self.ensure_tune(tune_index);
852 if let Some(tune) = self.tunes.get_mut(tune_index).and_then(Option::as_mut) {
853 tune.field_indices.push(field_index);
854 match context {
855 LineContext::TuneHeader { .. } => {
856 tune.header_field_indices.push(field_index);
857 }
858 LineContext::TuneBody { .. } => {
859 tune.body_field_indices.push(field_index);
860 }
861 _ => {}
862 }
863 }
864 }
865 LineContext::Preamble
866 | LineContext::FileHeader
867 | LineContext::BetweenBlocks
868 | LineContext::FreeText
869 | LineContext::TypesetText
870 | LineContext::TuneTerminator { .. } => {}
871 }
872
873 field_index
874 }
875
876 fn ensure_tune(&mut self, tune_index: usize) {
877 if self
878 .tunes
879 .get(tune_index)
880 .and_then(Option::as_ref)
881 .is_some()
882 {
883 return;
884 }
885
886 let Some(tune_block) = self
887 .surface
888 .line_map
889 .tunes
890 .iter()
891 .find(|tune| tune.index == tune_index)
892 else {
893 return;
894 };
895 let inherited = self.file_header.clone();
896 if let Some(slot) = self.tunes.get_mut(tune_index) {
897 *slot = Some(ParsedTuneFields {
898 index: tune_index,
899 span: tune_block.span,
900 inherited_file_header: inherited.clone(),
901 header: inherited.clone(),
902 current: inherited,
903 field_indices: Vec::new(),
904 header_field_indices: Vec::new(),
905 body_field_indices: Vec::new(),
906 });
907 }
908 }
909
910 fn finish(self) -> (ParsedAbcFields, Vec<Diagnostic>) {
911 (
912 ParsedAbcFields {
913 fields: self.fields,
914 file_header: self.file_header,
915 tunes: self.tunes.into_iter().flatten().collect(),
916 state_transitions: self.state_transitions,
917 },
918 self.diagnostics,
919 )
920 }
921}
922
923#[cfg(test)]
924#[path = "mod_tests.rs"]
925mod tests;