Skip to content

Commit 6cbb4c5

Browse files
committed
parser: move into a struct, proper error handling
1 parent 9cc649c commit 6cbb4c5

File tree

10 files changed

+350
-299
lines changed

10 files changed

+350
-299
lines changed

src/backend/errors/backend_error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl BackendError {
5151
kind: BackendErrorKind::InvalidStringName,
5252
}
5353
}
54-
pub fn invalid_character(line: u32, char: u32, c: Option<char>) -> Self {
54+
pub fn invalid_char(line: u32, char: u32, c: Option<char>) -> Self {
5555
BackendError {
5656
main_location: ErrorLocation::LineAndChar(line, char),
5757
kind: BackendErrorKind::Parse3InvalidCharacter(c),

src/backend/errors/backend_error_kind.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ In this position, you may have:
6060
- a rest
6161
- a single char element aligned left or right
6262
- another multichar element of the same cardinality"#.into()
63-
),
63+
),
6464
BackendErrorKind::FretTooLarge => ("Too large fret".to_string(), "The maximum allowed fret is 99.".to_string()),
6565
}
6666
}

src/backend/fixup/mod.rs

Lines changed: 68 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
},
1313
Backend,
1414
},
15-
parser::parse,
15+
parser::{dump_tracks, Parser},
1616
time, BufLines,
1717
};
1818

@@ -57,15 +57,21 @@ impl Backend for FixupBackend {
5757
parser_input: &BufLines, out: &mut Out, settings: Self::BackendSettings,
5858
) -> BackendResult {
5959
if let Some(dump) = settings.dump {
60-
let (parse_time, parsed) = time(|| parse(parser_input));
60+
let (parse_time, parsed) = time(|| Parser::parse(parser_input));
6161
let mut r = BackendResult::new(vec![], None, Some(parse_time), None);
62+
let parsed = match parsed {
63+
Ok(x) => x,
64+
Err((y, x)) => {
65+
r.err = Some(y);
66+
x
67+
}
68+
};
6269
match dump {
6370
FixupDumpOptions::TickStream => writeln!(out, "{:?}", parsed.tick_stream).unwrap(),
6471
FixupDumpOptions::PrettyTracks => {
65-
writeln!(out, "{}", parsed.dump_tracks()).unwrap()
72+
writeln!(out, "{}", dump_tracks(&parsed.as_ref())).unwrap()
6673
}
6774
}
68-
r.err = parsed.error;
6975
return r;
7076
}
7177

@@ -76,81 +82,66 @@ impl Backend for FixupBackend {
7682
let mut location_tracker = LocationTracker::new();
7783
loop {
7884
let parse_start = Instant::now();
79-
let parsed = parse(&parser_input);
85+
let r = Parser::parse(&parser_input);
8086
parse_time = parse_start.elapsed();
81-
match &parsed.error {
82-
None => break,
83-
Some(err) => {
84-
location_tracker.add(err.main_location.clone());
85-
if location_tracker.is_same() {
86-
let lines =
87-
err.main_location.get_line_idx().map(|x| x..=x).unwrap_or(0..=0);
88-
return BackendResult::new(
89-
diagnostics,
90-
Some(BackendError::fixup_failed(err.main_location.clone(), lines)),
91-
Some(parse_time),
92-
Some(fixup_start.elapsed()),
93-
);
94-
}
95-
match err.kind {
96-
BackendErrorKind::IOError(_) | BackendErrorKind::FmtError(_) => {
97-
return BackendResult::new(
98-
diagnostics,
99-
parsed.error,
100-
Some(parse_time),
101-
None,
102-
);
103-
}
104-
BackendErrorKind::BendOnInvalid => {} // todo: bendOnInvalid fixup: remove the bend
105-
BackendErrorKind::InvalidStringName => {}
106-
BackendErrorKind::EmptyScore => {}
107-
BackendErrorKind::BothSlotsMultiChar => {} // todo: fix BothSlotsMultichar errors
108-
BackendErrorKind::FretTooLarge => {} // todo: fix FretTooLarge errors (add
109-
// space between)
110-
BackendErrorKind::MultiBothSlotsFilled => {
111-
let Some((line_idx, char_idx)) = err
112-
.main_location
113-
.get_line_idx()
114-
.zip(err.main_location.get_char_idx())
115-
else {
116-
continue;
117-
};
118-
// we just replace both with a rest to be sure
119-
// todo: use a better strategy by checking the actual ticks
120-
let line_mut = parser_input[line_idx].to_mut();
121-
line_mut.replace_range(char_idx..char_idx + 1, "-");
122-
line_mut.replace_range(char_idx + 1..char_idx + 2, "-");
123-
diagnostics.push(Diagnostic::info(
124-
err.main_location.clone(),
125-
DiagnosticKind::FormatReplacedInvalid,
126-
));
127-
}
128-
BackendErrorKind::NoClosingBarline => {
129-
let l_idx = err.main_location.get_line_idx().unwrap();
130-
parser_input[l_idx].to_mut().push('|');
131-
diagnostics.push(Diagnostic::info(
132-
err.main_location.clone(),
133-
DiagnosticKind::FormatAddedBarline,
134-
))
135-
}
136-
BackendErrorKind::FixupFailed => unreachable!(),
137-
BackendErrorKind::Parse3InvalidCharacter(_) => {
138-
let Some((line_idx, char_idx)) = err
139-
.main_location
140-
.get_line_idx()
141-
.zip(err.main_location.get_char_idx())
142-
else {
143-
continue;
144-
};
145-
parser_input[line_idx]
146-
.to_mut()
147-
.replace_range(char_idx + 1..char_idx + 2, "-");
148-
diagnostics.push(Diagnostic::info(
149-
err.main_location.clone(),
150-
DiagnosticKind::FormatReplacedInvalid,
151-
))
152-
}
87+
let Err((err, _)) = r else { break };
88+
89+
location_tracker.add(err.main_location.clone());
90+
if location_tracker.is_same() {
91+
let lines = err.main_location.get_line_idx().map(|x| x..=x).unwrap_or(0..=0);
92+
return BackendResult::new(
93+
diagnostics,
94+
Some(BackendError::fixup_failed(err.main_location.clone(), lines)),
95+
Some(parse_time),
96+
Some(fixup_start.elapsed()),
97+
);
98+
}
99+
match err.kind {
100+
BackendErrorKind::IOError(_) | BackendErrorKind::FmtError(_) => {
101+
return BackendResult::new(diagnostics, Some(err), Some(parse_time), None);
102+
}
103+
BackendErrorKind::BendOnInvalid => {} // TODO: bendOnInvalid fixup: remove the bend
104+
BackendErrorKind::InvalidStringName => {}
105+
BackendErrorKind::EmptyScore => {}
106+
BackendErrorKind::BothSlotsMultiChar => {} // TODO: fix BothSlotsMultichar errors
107+
BackendErrorKind::FretTooLarge => {} // TODO: fix FretTooLarge errors (add
108+
// space between)
109+
BackendErrorKind::MultiBothSlotsFilled => {
110+
let Some((line_idx, char_idx)) =
111+
err.main_location.get_line_idx().zip(err.main_location.get_char_idx())
112+
else {
113+
continue;
114+
};
115+
// we just replace both with a rest to be sure
116+
// TODO: use a better strategy by checking the actual ticks
117+
let line_mut = parser_input[line_idx].to_mut();
118+
line_mut.replace_range(char_idx..char_idx + 1, "-");
119+
line_mut.replace_range(char_idx + 1..char_idx + 2, "-");
120+
diagnostics.push(Diagnostic::info(
121+
err.main_location.clone(),
122+
DiagnosticKind::FormatReplacedInvalid,
123+
));
124+
}
125+
BackendErrorKind::NoClosingBarline => {
126+
let l_idx = err.main_location.get_line_idx().unwrap();
127+
parser_input[l_idx].to_mut().push('|');
128+
diagnostics.push(Diagnostic::info(
129+
err.main_location.clone(),
130+
DiagnosticKind::FormatAddedBarline,
131+
))
132+
}
133+
BackendErrorKind::FixupFailed => unreachable!(),
134+
BackendErrorKind::Parse3InvalidCharacter(_) => {
135+
let Some((line_idx, char_idx)) =
136+
err.main_location.get_line_idx().zip(err.main_location.get_char_idx())
137+
else {
138+
continue;
153139
};
140+
parser_input[line_idx].to_mut().replace_range(char_idx + 1..char_idx + 2, "-");
141+
diagnostics.push(Diagnostic::info(
142+
err.main_location.clone(),
143+
DiagnosticKind::FormatReplacedInvalid,
144+
))
154145
}
155146
}
156147
}

src/backend/midi/mod.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ use midly::{
77
use tracing::trace;
88

99
use super::{Backend, BackendResult};
10-
use crate::time;
10+
use crate::{parser::ParserResult, time};
1111
use crate::{
1212
parser::{
13-
parse,
1413
tab_element::TabElement::{self, Fret},
15-
ParseResult,
14+
Parser,
1615
},
1716
BufLines,
1817
};
@@ -31,17 +30,15 @@ impl Backend for MidiBackend {
3130
input: &BufLines, out: &mut Out, _settings: Self::BackendSettings,
3231
) -> BackendResult {
3332
let diagnostics = vec![];
34-
let (parse_time, parse_result) = time(|| parse(input));
35-
match parse_result.error {
36-
None => (),
37-
Some(e) => {
38-
return BackendResult::new(diagnostics, Some(e), Some(parse_time), None);
39-
}
40-
}
33+
let (parse_time, parsed0) = time(|| Parser::parse(input));
34+
let parsed = match parsed0 {
35+
Ok(x) => x,
36+
Err(y) => return BackendResult::new(diagnostics, Some(y.0), Some(parse_time), None),
37+
};
4138
// TODO: the parser now gives us things like tick count, can probably preallocate based on
4239
// that
4340
let gen_start = Instant::now();
44-
let mut midi_tracks = convert_to_midi(&parse_result);
41+
let mut midi_tracks = convert_to_midi(&parsed);
4542
//diagnostics.extend(parse_result.diagnostics);
4643
trace!(LENGTH_OF_QUARTER, "Length of quarter");
4744
let mut tracks = vec![vec![
@@ -69,7 +66,7 @@ impl Backend for MidiBackend {
6966
}
7067
}
7168

72-
fn convert_to_midi(parsed: &ParseResult) -> Vec<Vec<TrackEvent<'static>>> {
69+
fn convert_to_midi(parsed: &ParserResult) -> Vec<Vec<TrackEvent<'static>>> {
7370
// TODO: maybe use the traditional note resolving logic here?
7471
let mut string_freq = HashMap::new();
7572
string_freq.insert('E', 52);

src/backend/muxml/mod.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ mod muxml2_tests;
55
pub mod settings;
66
use crate::backend::errors::backend_error::BackendError;
77
use crate::parser::tab_element::TabElement;
8-
use crate::parser::{source_location_from_stream, ParseResult};
8+
use crate::parser::{source_location_from_stream, Parser, ParserResult};
9+
use crate::BufLines;
910
use crate::{
1011
backend::{Backend, BackendResult},
1112
rlen, time,
1213
};
13-
use crate::{parser, BufLines};
1414
use formatters::{
1515
write_muxml2_measure_prelude, write_muxml2_note, write_muxml2_rest, MUXML2_DOCUMENT_END,
1616
MUXML_INCOMPLETE_DOC_PRELUDE,
@@ -28,11 +28,13 @@ impl Backend for MuxmlBackend {
2828
fn process<Out: std::io::Write>(
2929
input: &BufLines, out: &mut Out, settings: Self::BackendSettings,
3030
) -> BackendResult {
31-
let (parse_time, parse_result) = time(|| parser::parse(input));
32-
if let Some(err) = parse_result.error {
33-
return BackendResult::new(vec![], Some(err), Some(parse_time), None);
34-
}
35-
let generator = MuxmlGenerator::init(parse_result, parse_time, settings);
31+
let (parse_time, parsed0) = time(|| Parser::parse(input));
32+
let parsed = match parsed0 {
33+
Ok(x) => x,
34+
Err(y) => return BackendResult::new(vec![], Some(y.0), Some(parse_time), None),
35+
};
36+
37+
let generator = MuxmlGenerator::init(parsed, parse_time, settings);
3638
let (gen_time, (xml_out, mut gen_result)) = time(|| generator.gen());
3739
gen_result.timing_gen = Some(gen_time);
3840
if gen_result.err.is_some() {
@@ -112,7 +114,7 @@ pub enum Vibrato {
112114
Stop,
113115
}
114116
pub struct MuxmlGenerator {
115-
parsed: ParseResult,
117+
parsed: ParserResult,
116118
settings: settings::Settings,
117119

118120
measure_buf: Vec<Muxml2TabElement>,
@@ -123,15 +125,18 @@ pub struct MuxmlGenerator {
123125
r: BackendResult,
124126
}
125127
impl MuxmlGenerator {
126-
pub fn estimate_capacity(parsed: &ParseResult) -> usize {
128+
pub fn estimate_capacity(parsed: &ParserResult) -> usize {
127129
// TODO: this is technically wrong, as .reserve reserves *additional* capacity.
128130
// Should fix this, but that needs readjusting the *20 multiplier.
129131
MUXML_INCOMPLETE_DOC_PRELUDE.len()
130132
+ MUXML2_DOCUMENT_END.len()
131133
+ parsed.tick_stream.len() * 20
132134
}
135+
pub fn get_note_properties(&self, x: u32) -> Option<&NoteProperties> {
136+
self.note_properties.get(&x)
137+
}
133138
/// Allocates heavily.
134-
pub fn init(parsed: ParseResult, parse_time: Duration, settings: settings::Settings) -> Self {
139+
pub fn init(parsed: ParserResult, parse_time: Duration, settings: settings::Settings) -> Self {
135140
let cap = Self::estimate_capacity(&parsed);
136141
let mut document = String::from(MUXML_INCOMPLETE_DOC_PRELUDE);
137142
document.reserve(cap);
@@ -200,7 +205,8 @@ impl MuxmlGenerator {
200205
None => {
201206
trace!(stream_idx, "hanging bend on at stream end");
202207
let TabElement::Fret(x) = self.parsed.tick_stream[last_idx] else {
203-
let (line, char) = source_location_from_stream(&self.parsed, stream_idx as u32);
208+
let (line, char) =
209+
source_location_from_stream(&self.parsed.as_ref(), stream_idx as u32);
204210
return Err(BackendError::bend_on_invalid(line, char));
205211
};
206212

@@ -212,7 +218,8 @@ impl MuxmlGenerator {
212218
// we can just silently replace it and add the correct note
213219
Some(TabElement::Rest) => {
214220
let TabElement::Fret(x) = self.parsed.tick_stream[last_idx] else {
215-
let (line, char) = source_location_from_stream(&self.parsed, stream_idx as u32);
221+
let (line, char) =
222+
source_location_from_stream(&self.parsed.as_ref(), stream_idx as u32);
216223
return Err(BackendError::bend_on_invalid(line, char));
217224
};
218225
self.parsed.tick_stream[next_idx] = TabElement::Fret(x + 1);

src/parser/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ pub fn char(c: char) -> impl Fn(&str) -> Result<(&str, char), &str> {
1111
}
1212
}
1313

14-
pub fn string_name() -> impl Fn(&str) -> Result<(&str, char), &str> {
15-
move |s: &str| match s.chars().next() {
14+
pub fn string_name(s: &str) -> Result<(&str, char), &str> {
15+
match s.chars().next() {
1616
Some(c) if c.is_alphabetic() => Ok((&s[1..], c)),
1717
_ => Err(s),
1818
}

0 commit comments

Comments
 (0)