Parse if and args w/ idents, format w/ parens

This commit is contained in:
Richard Feldman 2019-10-04 11:46:37 +03:00
parent 2ced7ee5d8
commit ebaed27193
5 changed files with 184 additions and 20 deletions

View File

@ -343,7 +343,12 @@ impl<'a> Expr<'a> {
}
}
pub fn format<'a>(arena: &'a Bump, expr: &'a Expr<'a>, indent: u16) -> String<'a> {
pub fn format<'a>(
arena: &'a Bump,
expr: &'a Expr<'a>,
indent: u16,
apply_needs_parens: bool,
) -> String<'a> {
use self::Expr::*;
let mut buf = String::new_in(arena);
@ -351,10 +356,10 @@ pub fn format<'a>(arena: &'a Bump, expr: &'a Expr<'a>, indent: u16) -> String<'a
match expr {
SpaceBefore(sub_expr, spaces) => {
buf.push_str(&format_spaces(arena, spaces.iter(), indent));
buf.push_str(&format(arena, sub_expr, indent));
buf.push_str(&format(arena, sub_expr, indent, apply_needs_parens));
}
SpaceAfter(sub_expr, spaces) => {
buf.push_str(&format(arena, sub_expr, indent));
buf.push_str(&format(arena, sub_expr, indent, apply_needs_parens));
buf.push_str(&format_spaces(arena, spaces.iter(), indent));
}
@ -372,12 +377,20 @@ pub fn format<'a>(arena: &'a Bump, expr: &'a Expr<'a>, indent: u16) -> String<'a
buf.push_str(name);
}
Apply((loc_expr, loc_args)) => {
buf.push_str(&format(arena, &loc_expr.value, indent));
if apply_needs_parens {
buf.push('(');
}
buf.push_str(&format(arena, &loc_expr.value, indent, true));
for loc_arg in loc_args {
buf.push(' ');
buf.push_str(&format(arena, &loc_arg.value, indent));
buf.push_str(&format(arena, &loc_arg.value, indent, true));
}
if apply_needs_parens {
buf.push(')');
}
}
BlockStr(lines) => {
@ -424,7 +437,7 @@ pub fn format<'a>(arena: &'a Bump, expr: &'a Expr<'a>, indent: u16) -> String<'a
buf.push_str("-> ");
buf.push_str(&format(arena, &loc_ret.value, indent));
buf.push_str(&format(arena, &loc_ret.value, indent, false));
}
Defs((defs, ret)) => {
// The first def is actually at the end of the list, because
@ -445,7 +458,15 @@ pub fn format<'a>(arena: &'a Bump, expr: &'a Expr<'a>, indent: u16) -> String<'a
buf.push_str(&format_def(arena, def, indent));
}
buf.push_str(&format(arena, &ret.value, indent));
buf.push_str(&format(arena, &ret.value, indent, false));
}
If((loc_condition, loc_then, loc_else)) => {
buf.push_str("if ");
buf.push_str(&format(arena, &loc_condition.value, indent, false));
buf.push_str(" then ");
buf.push_str(&format(arena, &loc_then.value, indent, false));
buf.push_str(" else ");
buf.push_str(&format(arena, &loc_else.value, indent, false));
}
other => panic!("TODO implement Display for AST variant {:?}", other),
}
@ -463,7 +484,7 @@ pub fn format_def<'a>(arena: &'a Bump, def: &'a Def<'a>, indent: u16) -> String<
BodyOnly(loc_pattern, loc_expr) => {
buf.push_str(&format_pattern(arena, &loc_pattern.value, indent, true));
buf.push_str(" = ");
buf.push_str(&format(arena, &loc_expr.value, indent));
buf.push_str(&format(arena, &loc_expr.value, indent, false));
}
AnnotatedBody(_loc_annotation, _loc_pattern, _loc_expr) => {
panic!("TODO have format_def support AnnotationOnly")
@ -539,10 +560,20 @@ fn format_pattern<'a>(
// Space
SpaceBefore(sub_pattern, spaces) => {
buf.push_str(&format_spaces(arena, spaces.iter(), indent));
buf.push_str(&format_pattern(arena, sub_pattern, indent, true));
buf.push_str(&format_pattern(
arena,
sub_pattern,
indent,
apply_needs_parens,
));
}
SpaceAfter(sub_pattern, spaces) => {
buf.push_str(&format_pattern(arena, sub_pattern, indent, true));
buf.push_str(&format_pattern(
arena,
sub_pattern,
indent,
apply_needs_parens,
));
buf.push_str(&format_spaces(arena, spaces.iter(), indent));
}

View File

@ -26,12 +26,12 @@ use bumpalo::Bump;
use operator::Operator;
use parse::ast::{Attempting, Def, Expr, Pattern, Spaceable};
use parse::blankspace::{
space0, space0_after, space0_around, space0_before, space1, space1_before,
space0, space0_after, space0_around, space0_before, space1, space1_around, space1_before,
};
use parse::ident::{ident, Ident, MaybeQualified};
use parse::number_literal::number_literal;
use parse::parser::{
and, attempt, between, char, either, loc, map, map_with_arena, not_followed_by, one_of16,
and, attempt, between, char, either, loc, map, map_with_arena, not, not_followed_by, one_of16,
one_of2, one_of4, one_of5, one_of9, one_or_more, optional, sep_by0, skip_first, skip_second,
string, then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason, ParseResult,
Parser, State,
@ -438,9 +438,43 @@ fn parse_def_expr<'a>(
}
fn loc_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> {
// Don't parse operators, because they have a higher precedence than function application.
// If we encounter one, we're done parsing function args!
move |arena, state| loc_parse_expr_body_without_operators(min_indent, arena, state)
skip_first(
// If this is a reserved keyword ("if", "then", "case, "when"), then
// it is not a function argument!
not(reserved_keyword()),
// Don't parse operators, because they have a higher precedence than function application.
// If we encounter one, we're done parsing function args!
move |arena, state| loc_parse_function_arg(min_indent, arena, state),
)
}
fn loc_parse_function_arg<'a>(
min_indent: u16,
arena: &'a Bump,
state: State<'a>,
) -> ParseResult<'a, Located<Expr<'a>>> {
one_of9(
loc_parenthetical_expr(min_indent),
loc(string_literal()),
loc(number_literal()),
loc(closure(min_indent)),
loc(record_literal(min_indent)),
loc(list_literal(min_indent)),
loc(case_expr(min_indent)),
loc(if_expr(min_indent)),
loc(ident_without_apply()),
)
.parse(arena, state)
}
fn reserved_keyword<'a>() -> impl Parser<'a, ()> {
one_of5(
string(keyword::IF),
string(keyword::THEN),
string(keyword::ELSE),
string(keyword::CASE),
string(keyword::WHEN),
)
}
fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
@ -548,10 +582,37 @@ pub fn case_expr<'a>(_min_indent: u16) -> impl Parser<'a, Expr<'a>> {
})
}
pub fn if_expr<'a>(_min_indent: u16) -> impl Parser<'a, Expr<'a>> {
map(string(keyword::IF), |_| {
panic!("TODO implement IF");
})
pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
map_with_arena(
and(
skip_first(
string(keyword::IF),
space1_around(
loc(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent,
),
),
and(
skip_first(
string(keyword::THEN),
space1_around(
loc(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent,
),
),
skip_first(
string(keyword::ELSE),
space1_before(
loc(move |arena, state| parse_expr(min_indent, arena, state)),
min_indent,
),
),
),
),
|arena, (condition, (then_branch, else_branch))| {
Expr::If(arena.alloc((condition, then_branch, else_branch)))
},
)
}
pub fn loc_function_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Expr<'a>>>> {
@ -623,6 +684,12 @@ pub fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
)
}
pub fn ident_without_apply<'a>() -> impl Parser<'a, Expr<'a>> {
then(loc(ident()), move |_arena, state, loc_ident| {
Ok((ident_to_expr(loc_ident.value), state))
})
}
pub fn equals_with_indent<'a>() -> impl Parser<'a, u16> {
move |_arena, state: State<'a>| {
let mut iter = state.input.chars();

View File

@ -200,6 +200,26 @@ where
}
}
pub fn not<'a, P, Val>(parser: P) -> impl Parser<'a, ()>
where
P: Parser<'a, Val>,
{
move |arena, state: State<'a>| {
let original_state = state.clone();
match parser.parse(arena, state) {
Ok((_, _)) => Err((
Fail {
reason: FailReason::ConditionFailed,
attempting: original_state.attempting,
},
original_state,
)),
Err((_, _)) => Ok(((), original_state)),
}
}
}
pub fn lookahead<'a, Peek, P, PeekVal, Val>(peek: Peek, parser: P) -> impl Parser<'a, Val>
where
Peek: Parser<'a, PeekVal>,

View File

@ -29,7 +29,7 @@ mod test_format {
let expected = expected.trim_end();
match parse_with(&arena, input) {
Ok(actual) => assert_eq!(format(&arena, &actual, 0), expected),
Ok(actual) => assert_eq!(format(&arena, &actual, 0, false), expected),
Err(error) => panic!("Unexpected parse failure when parsing this for formatting:\n\n{:?}\n\nParse error was:\n\n{:?}\n\n", input, error)
}
}
@ -178,6 +178,23 @@ mod test_format {
assert_formats_same("{}");
}
// IF
#[test]
fn single_line_if() {
assert_formats_same(indoc!(
r#"
if foo bar then a b c else d e f
"#
));
assert_formats_same(indoc!(
r#"
if foo (a b c) then a b c else d e f
"#
));
}
// NEWLINES
#[test]

View File

@ -578,6 +578,21 @@ mod test_parse {
assert_eq!(Ok(expected), actual);
}
#[test]
fn apply_three_args() {
let arena = Bump::new();
let module_parts = Vec::new_in(&arena).into_bump_slice();
let arg1 = Located::new(0, 0, 2, 3, Var(module_parts, "b"));
let arg2 = Located::new(0, 0, 4, 5, Var(module_parts, "c"));
let arg3 = Located::new(0, 0, 6, 7, Var(module_parts, "d"));
let args = bumpalo::vec![in &arena; arg1, arg2, arg3];
let tuple = arena.alloc((Located::new(0, 0, 0, 1, Var(module_parts, "a")), args));
let expected = Expr::Apply(tuple);
let actual = parse_with(&arena, "a b c d");
assert_eq!(Ok(expected), actual);
}
#[test]
fn parenthetical_apply() {
let arena = Bump::new();
@ -630,6 +645,20 @@ mod test_parse {
assert_eq!(Ok(expected), actual);
}
#[test]
fn three_arg_closure() {
let arena = Bump::new();
let arg1 = Located::new(0, 0, 1, 2, Identifier("a"));
let arg2 = Located::new(0, 0, 3, 4, Identifier("b"));
let arg3 = Located::new(0, 0, 5, 6, Identifier("c"));
let patterns = bumpalo::vec![in &arena; arg1, arg2, arg3];
let tuple = arena.alloc((patterns, Located::new(0, 0, 10, 12, Int("42"))));
let expected = Closure(tuple);
let actual = parse_with(&arena, "\\a b c -> 42");
assert_eq!(Ok(expected), actual);
}
#[test]
fn closure_with_underscores() {
let arena = Bump::new();