Merge pull request #120 from HigherOrderCO/bug/sc-354/hvm-lang-showing-multiple-errors-instead

[sc-354] Improve parsing to not show "multiple errors"
This commit is contained in:
Nicolas Abril 2024-01-17 18:08:21 +01:00 committed by GitHub
commit f10d4c66c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 89 additions and 9 deletions

View File

@ -4,7 +4,7 @@ use chumsky::{
extra,
input::{SpannedInput, Stream, ValueInput},
prelude::{Input, Rich},
primitive::{choice, just},
primitive::{choice, end, just},
recursive::recursive,
select,
span::SimpleSpan,
@ -66,7 +66,13 @@ fn soft_keyword<'a, I>(keyword: &'a str) -> impl Parser<'a, I, (), extra::Err<Ri
where
I: ValueInput<'a, Token = Token, Span = SimpleSpan>,
{
name().try_map(move |Name(nam), span| if nam == keyword { Ok(()) } else { Err(Rich::custom(span, "")) })
name().try_map(move |Name(nam), span| {
if nam == keyword {
Ok(())
} else {
Err(Rich::custom(span, format!("Expected `{keyword}`, found `{nam}`")))
}
})
}
fn name<'a, I>() -> impl Parser<'a, I, Name, extra::Err<Rich<'a, Token>>>
@ -334,14 +340,48 @@ where
})
}
fn rule<'a, I>() -> impl Parser<'a, I, (Name, Rule), extra::Err<Rich<'a, Token>>>
fn rule_pattern<'a, I>() -> impl Parser<'a, I, (Name, Vec<Pattern>), extra::Err<Rich<'a, Token>>>
where
I: ValueInput<'a, Token = Token, Span = SimpleSpan>,
{
let lhs = name().then(pattern().repeated().collect()).boxed();
let lhs = choice((lhs.clone(), lhs.clone().delimited_by(just(Token::LParen), just(Token::RParen))));
choice((lhs.clone(), lhs.clone().delimited_by(just(Token::LParen), just(Token::RParen))))
.then_ignore(just(Token::Equals))
}
lhs.then_ignore(just(Token::Equals)).then(term()).map(|((name, pats), body)| (name, Rule { pats, body }))
/// This rule always emits an error when it parses successfully
/// It is used to report a parsing error that would be unclear otherwise
fn rule_body_missing_paren<'a, I>()
-> impl Parser<'a, I, ((Name, Vec<Pattern>), Term), extra::Err<Rich<'a, Token>>>
where
I: ValueInput<'a, Token = Token, Span = SimpleSpan>,
{
let terms = tag(Tag::Static)
.then(term())
.foldl(term().and_is(soft_keyword("data").not()).repeated().at_least(1), |(tag, fun), arg| {
(tag.clone(), Term::App { tag, fun: Box::new(fun), arg: Box::new(arg) })
});
let end_of_rule = end().or(soft_keyword("data")).rewind();
rule_pattern()
.then(terms)
.map(|(rule, (_, app))| (rule, app))
.then_ignore(end_of_rule)
.validate(|((name, pats), term), span, emit| {
emit.emit(Rich::custom(span, format!("Missing Parenthesis around rule `{}` body", name)));
((name, pats), term)
})
.boxed()
}
fn rule<'a, I>() -> impl Parser<'a, I, (Name, Rule), extra::Err<Rich<'a, Token>>>
where
I: ValueInput<'a, Token = Token, Span = SimpleSpan>,
{
rule_body_missing_paren()
.or(rule_pattern().then(term()))
.map(|((name, pats), body)| (name, Rule { pats, body }))
}
fn datatype<'a, I>() -> impl Parser<'a, I, (Name, Adt), extra::Err<Rich<'a, Token>>>
@ -370,7 +410,7 @@ where
{
let top_level = choice((datatype().map(TopLevel::Adt), rule().map(TopLevel::Rule)));
top_level.repeated().collect::<Vec<_>>().try_map(|program, span| {
top_level.repeated().collect::<Vec<_>>().validate(|program, span, emit| {
let mut book = Book::new();
// Collect rules and adts into a book
@ -390,17 +430,17 @@ where
if let Entry::Vacant(e) = book.ctrs.entry(ctr) {
e.insert(nam.clone());
} else {
return Err(Rich::custom(span, format!("Repeated constructor '{}'", nam)));
emit.emit(Rich::custom(span, format!("Repeated constructor '{}'", nam)));
}
}
} else {
return Err(Rich::custom(span, format!("Repeated datatype '{}'", nam)));
emit.emit(Rich::custom(span, format!("Repeated datatype '{}'", nam)));
}
}
}
}
Ok(book)
book
})
}

View File

@ -155,6 +155,14 @@ fn flatten_rules() {
})
}
#[test]
fn parse_file() {
run_golden_test_dir(function_name!(), &|code| {
let book = do_parse_book(code)?;
Ok(book.to_string())
})
}
#[test]
fn encode_pattern_match() {
run_golden_test_dir(function_name!(), &|code| {

View File

@ -0,0 +1 @@
main = * *

View File

@ -0,0 +1,3 @@
main = * * * *
data Foo = Bar

View File

@ -0,0 +1,5 @@
data Foo = A
main = A B
data Foo = B

View File

@ -0,0 +1,2 @@
data Foo = A
data Foo = B

View File

@ -0,0 +1,5 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/missing_paren.hvm
---
Missing Parenthesis around rule `main` body

View File

@ -0,0 +1,5 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/missing_paren_then_adt.hvm
---
Missing Parenthesis around rule `main` body

View File

@ -0,0 +1,6 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/multiple_parsing_errors.hvm
---
Missing Parenthesis around rule `main` body
Repeated datatype 'Foo'

View File

@ -0,0 +1,5 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/repeated_adt_name.hvm
---
Repeated datatype 'Foo'