mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-19 14:57:15 +03:00
Parse if
and args w/ idents, format w/ parens
This commit is contained in:
parent
2ced7ee5d8
commit
ebaed27193
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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>,
|
||||
|
@ -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]
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user