Skip to content

Commit 839fa37

Browse files
committed
Accumulate errors in parser state
1 parent 74562f6 commit 839fa37

File tree

1 file changed

+43
-51
lines changed

1 file changed

+43
-51
lines changed

src/parsing/parser.rs

Lines changed: 43 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@ use std::path::Path;
33
use crate::language::*;
44
use crate::regex::*;
55

6-
#[derive(Debug, PartialEq, Eq)]
7-
struct ParseResult<T> {
8-
pub value: T,
9-
pub errors: Vec<ParsingError>,
10-
}
11-
126
// This could be adapted to return both the partial document and the errors.
137
// But for our purposes if the parse fails then there's no point trying to do
148
// deeper validation or analysis; the input syntax is broken and the user
@@ -23,16 +17,7 @@ pub fn parse_with_recovery<'i>(
2317
input.filename(path);
2418
input.initialize(content);
2519

26-
let result = input.parse_collecting_errors();
27-
28-
if result
29-
.errors
30-
.is_empty()
31-
{
32-
Ok(result.value)
33-
} else {
34-
Err(result.errors)
35-
}
20+
input.parse_collecting_errors()
3621
}
3722

3823
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -156,7 +141,7 @@ impl<'i> Parser<'i> {
156141
}
157142
}
158143

159-
fn parse_collecting_errors(&mut self) -> ParseResult<Document<'i>> {
144+
fn parse_collecting_errors(&mut self) -> Result<Document<'i>, Vec<ParsingError>> {
160145
// Clear any existing errors
161146
self.problems
162147
.clear();
@@ -296,9 +281,11 @@ impl<'i> Parser<'i> {
296281

297282
let document = Document { header, body };
298283
let errors = std::mem::take(&mut self.problems);
299-
ParseResult {
300-
value: document,
301-
errors,
284+
285+
if errors.is_empty() {
286+
Ok(document)
287+
} else {
288+
Err(errors)
302289
}
303290
}
304291

@@ -362,7 +349,6 @@ impl<'i> Parser<'i> {
362349
// Pass to closure for processing
363350
let result = function(&mut parser);
364351

365-
// Merge any errors from the subparser into the parent
366352
self.problems
367353
.extend(parser.problems);
368354

@@ -449,6 +435,9 @@ impl<'i> Parser<'i> {
449435
// Pass to closure for processing
450436
let result = function(&mut parser)?;
451437

438+
self.problems
439+
.extend(parser.problems);
440+
452441
// Advance parser state
453442
self.source = &self.source[l..];
454443
self.offset += l;
@@ -492,6 +481,9 @@ impl<'i> Parser<'i> {
492481
// Pass to closure for processing
493482
let result = function(&mut parser)?;
494483

484+
self.problems
485+
.extend(parser.problems);
486+
495487
// Advance parser state past the entire delimited block
496488
let end = end + width;
497489
self.source = &self.source[end..];
@@ -515,6 +507,9 @@ impl<'i> Parser<'i> {
515507
// Pass to closure for processing
516508
let result = function(&mut parser)?;
517509

510+
self.problems
511+
.extend(parser.problems);
512+
518513
// Advance parser state
519514
self.source = &self.source[end_pos..];
520515
self.offset += end_pos;
@@ -539,6 +534,8 @@ impl<'i> Parser<'i> {
539534
}
540535
let mut parser = self.subparser(0, trimmed);
541536
results.push(function(&mut parser)?);
537+
self.problems
538+
.extend(parser.problems);
542539
}
543540

544541
// Advance parser past all consumed content
@@ -561,6 +558,9 @@ impl<'i> Parser<'i> {
561558
let mut parser = self.subparser(0, paragraph);
562559
let result = function(&mut parser)?;
563560

561+
self.problems
562+
.extend(parser.problems);
563+
564564
// Advance past this paragraph and the \n\n delimiter if present
565565
if i < content.len() {
566566
i += 2;
@@ -4725,42 +4725,34 @@ echo test
47254725
// Test with valid content - should have no errors
47264726
input.initialize("% technique v1\nvalid_proc : A -> B\n# Title\nDescription");
47274727
let result = input.parse_collecting_errors();
4728-
assert_eq!(
4729-
result
4730-
.errors
4731-
.len(),
4732-
0
4733-
);
4734-
assert!(result
4735-
.value
4736-
.header
4737-
.is_some());
4738-
assert!(result
4739-
.value
4740-
.body
4741-
.is_some());
4728+
match result {
4729+
Ok(document) => {
4730+
assert!(document
4731+
.header
4732+
.is_some());
4733+
assert!(document
4734+
.body
4735+
.is_some());
4736+
}
4737+
Err(_) => panic!("Expected successful parse for valid content"),
4738+
}
47424739

47434740
// Test with invalid header - should collect header error
47444741
input.initialize("% wrong v1");
47454742
let result = input.parse_collecting_errors();
4746-
assert!(
4747-
result
4748-
.errors
4749-
.len()
4750-
> 0
4751-
);
4752-
assert!(result
4753-
.errors
4754-
.iter()
4755-
.any(|e| matches!(e, ParsingError::InvalidHeader(_))));
4756-
assert!(result
4757-
.value
4758-
.header
4759-
.is_none());
4743+
match result {
4744+
Ok(_) => panic!("Expected errors for invalid content"),
4745+
Err(errors) => {
4746+
assert!(errors.len() > 0);
4747+
assert!(errors
4748+
.iter()
4749+
.any(|e| matches!(e, ParsingError::InvalidHeader(_))));
4750+
}
4751+
}
47604752

4761-
// Test that the method returns ParseResult instead of Result
4753+
// Test that the method returns Result instead of ParseResult
47624754
input.initialize("some content");
4763-
let _result: ParseResult<Document> = input.parse_collecting_errors();
4755+
let _result: Result<Document, Vec<ParsingError>> = input.parse_collecting_errors();
47644756
// If this compiles, the method signature is correct
47654757
}
47664758

0 commit comments

Comments
 (0)