fix arg comma messages

This commit is contained in:
Folkert 2021-02-25 14:25:43 +01:00
parent 1a7fd57833
commit c208f500d9
4 changed files with 370 additions and 35 deletions

View file

@ -11,9 +11,9 @@ use crate::keyword;
use crate::number_literal::number_literal;
use crate::parser::{
self, allocated, and_then_with_indent_level, ascii_char, ascii_string, attempt, backtrackable,
fail, map, newline_char, not, not_followed_by, optional, sep_by1, specialize, specialize_ref,
then, unexpected, unexpected_eof, word1, word2, EExpr, ELambda, Either, ParseResult, Parser,
State, SyntaxError, When,
fail, map, newline_char, not, not_followed_by, optional, sep_by1, sep_by1_e, specialize,
specialize_ref, then, unexpected, unexpected_eof, word1, word2, EExpr, ELambda, Either,
ParseResult, Parser, State, SyntaxError, When,
};
use crate::pattern::loc_closure_param;
use crate::type_annotation;
@ -991,10 +991,10 @@ fn closure_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
word1(b'\\', ELambda::Start),
// Once we see the '\', we're committed to parsing this as a closure.
// It may turn out to be malformed, but it is definitely a closure.
optional(and!(
and!(
// Parse the params
// Params are comma-separated
sep_by1(
sep_by1_e(
word1(b',', ELambda::Comma),
space0_around_ee(
specialize(ELambda::Pattern, loc_closure_param(min_indent)),
@ -1002,7 +1002,8 @@ fn closure_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
ELambda::Space,
ELambda::IndentArg,
ELambda::IndentArrow
)
),
ELambda::Arg,
),
skip_first!(
// Parse the -> which separates params from body
@ -1018,16 +1019,13 @@ fn closure_help<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, ELambda<'a>> {
ELambda::IndentBody
)
)
))
)
),
|arena: &'a Bump, opt_contents| match opt_contents {
None => Expr::MalformedClosure,
Some((params, loc_body)) => {
let params: Vec<'a, Located<Pattern<'a>>> = params;
let params: &'a [Located<Pattern<'a>>] = params.into_bump_slice();
|arena: &'a Bump, (params, loc_body)| {
let params: Vec<'a, Located<Pattern<'a>>> = params;
let params: &'a [Located<Pattern<'a>>] = params.into_bump_slice();
Expr::Closure(params, arena.alloc(loc_body))
}
Expr::Closure(params, arena.alloc(loc_body))
}
)
}
@ -1792,25 +1790,6 @@ fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError<
)
}
/// This is mainly for matching tags in closure params, e.g. \@Foo -> ...
pub fn private_tag<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> {
map_with_arena!(
skip_first!(ascii_char(b'@'), global_tag()),
|arena: &'a Bump, name: &'a str| {
let mut buf = String::with_capacity_in(1 + name.len(), arena);
buf.push('@');
buf.push_str(name);
buf.into_bump_str()
}
)
}
/// This is mainly for matching tags in closure params, e.g. \Foo -> ...
pub fn global_tag<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> {
global_tag_or_ident(|first_char| first_char.is_uppercase())
}
pub fn string_literal<'a>() -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
map!(crate::string_literal::parse(), Expr::Str)
}

View file

@ -392,6 +392,7 @@ pub enum ELambda<'a> {
Start(Row, Col),
Arrow(Row, Col),
Comma(Row, Col),
Arg(Row, Col),
// TODO make EEXpr
Pattern(EPattern<'a>, Row, Col),
Syntax(&'a SyntaxError<'a>, Row, Col),
@ -1204,8 +1205,6 @@ where
buf.push(next_output);
}
Err((element_progress, fail, state)) => {
// If the delimiter parsed, but the following
// element did not, that's a fatal error.
return Err((element_progress, fail, state));
}
}
@ -1233,6 +1232,84 @@ where
}
}
/// Parse one or more values separated by a delimiter (e.g. a comma) whose
/// values are discarded
pub fn sep_by1_e<'a, P, V, D, Val, Error>(
delimiter: D,
parser: P,
to_element_error: V,
) -> impl Parser<'a, Vec<'a, Val>, Error>
where
D: Parser<'a, (), Error>,
P: Parser<'a, Val, Error>,
V: Fn(Row, Col) -> Error,
Error: 'a,
{
move |arena, state: State<'a>| {
let start_bytes_len = state.bytes.len();
match parser.parse(arena, state) {
Ok((progress, first_output, next_state)) => {
debug_assert_eq!(progress, MadeProgress);
let mut state = next_state;
let mut buf = Vec::with_capacity_in(1, arena);
buf.push(first_output);
loop {
match delimiter.parse(arena, state) {
Ok((_, (), next_state)) => {
// If the delimiter passed, check the element parser.
match parser.parse(arena, next_state) {
Ok((_, next_output, next_state)) => {
state = next_state;
buf.push(next_output);
}
Err((MadeProgress, fail, state)) => {
return Err((MadeProgress, fail, state));
}
Err((NoProgress, _fail, state)) => {
return Err((
NoProgress,
to_element_error(state.line, state.column),
state,
));
}
}
}
Err((delim_progress, fail, old_state)) => {
match delim_progress {
MadeProgress => {
// fail if the delimiter made progress
return Err((MadeProgress, fail, old_state));
}
NoProgress => {
let progress = Progress::from_lengths(
start_bytes_len,
old_state.bytes.len(),
);
return Ok((progress, buf, old_state));
}
}
}
}
}
}
Err((MadeProgress, fail, state)) => {
return Err((MadeProgress, fail, state));
}
Err((NoProgress, _fail, state)) => {
return Err((
NoProgress,
to_element_error(state.line, state.column),
state,
));
}
}
}
}
pub fn fail_when_progress<T, E>(
progress: Progress,
fail: E,