mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 07:41:12 +00:00
Parse if
and args w/ idents, format w/ parens
This commit is contained in:
parent
2ced7ee5d8
commit
ebaed27193
5 changed files with 184 additions and 20 deletions
|
@ -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::*;
|
use self::Expr::*;
|
||||||
|
|
||||||
let mut buf = String::new_in(arena);
|
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 {
|
match expr {
|
||||||
SpaceBefore(sub_expr, spaces) => {
|
SpaceBefore(sub_expr, spaces) => {
|
||||||
buf.push_str(&format_spaces(arena, spaces.iter(), indent));
|
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) => {
|
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));
|
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);
|
buf.push_str(name);
|
||||||
}
|
}
|
||||||
Apply((loc_expr, loc_args)) => {
|
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 {
|
for loc_arg in loc_args {
|
||||||
buf.push(' ');
|
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) => {
|
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("-> ");
|
||||||
|
|
||||||
buf.push_str(&format(arena, &loc_ret.value, indent));
|
buf.push_str(&format(arena, &loc_ret.value, indent, false));
|
||||||
}
|
}
|
||||||
Defs((defs, ret)) => {
|
Defs((defs, ret)) => {
|
||||||
// The first def is actually at the end of the list, because
|
// 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_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),
|
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) => {
|
BodyOnly(loc_pattern, loc_expr) => {
|
||||||
buf.push_str(&format_pattern(arena, &loc_pattern.value, indent, true));
|
buf.push_str(&format_pattern(arena, &loc_pattern.value, indent, true));
|
||||||
buf.push_str(" = ");
|
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) => {
|
AnnotatedBody(_loc_annotation, _loc_pattern, _loc_expr) => {
|
||||||
panic!("TODO have format_def support AnnotationOnly")
|
panic!("TODO have format_def support AnnotationOnly")
|
||||||
|
@ -539,10 +560,20 @@ fn format_pattern<'a>(
|
||||||
// Space
|
// Space
|
||||||
SpaceBefore(sub_pattern, spaces) => {
|
SpaceBefore(sub_pattern, spaces) => {
|
||||||
buf.push_str(&format_spaces(arena, spaces.iter(), indent));
|
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) => {
|
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));
|
buf.push_str(&format_spaces(arena, spaces.iter(), indent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,12 +26,12 @@ use bumpalo::Bump;
|
||||||
use operator::Operator;
|
use operator::Operator;
|
||||||
use parse::ast::{Attempting, Def, Expr, Pattern, Spaceable};
|
use parse::ast::{Attempting, Def, Expr, Pattern, Spaceable};
|
||||||
use parse::blankspace::{
|
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::ident::{ident, Ident, MaybeQualified};
|
||||||
use parse::number_literal::number_literal;
|
use parse::number_literal::number_literal;
|
||||||
use parse::parser::{
|
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,
|
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,
|
string, then, unexpected, unexpected_eof, zero_or_more, Either, Fail, FailReason, ParseResult,
|
||||||
Parser, State,
|
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>>> {
|
fn loc_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> {
|
||||||
|
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.
|
// Don't parse operators, because they have a higher precedence than function application.
|
||||||
// If we encounter one, we're done parsing function args!
|
// If we encounter one, we're done parsing function args!
|
||||||
move |arena, state| loc_parse_expr_body_without_operators(min_indent, arena, state)
|
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>> {
|
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>> {
|
pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||||
map(string(keyword::IF), |_| {
|
map_with_arena(
|
||||||
panic!("TODO implement IF");
|
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>>>> {
|
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> {
|
pub fn equals_with_indent<'a>() -> impl Parser<'a, u16> {
|
||||||
move |_arena, state: State<'a>| {
|
move |_arena, state: State<'a>| {
|
||||||
let mut iter = state.input.chars();
|
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>
|
pub fn lookahead<'a, Peek, P, PeekVal, Val>(peek: Peek, parser: P) -> impl Parser<'a, Val>
|
||||||
where
|
where
|
||||||
Peek: Parser<'a, PeekVal>,
|
Peek: Parser<'a, PeekVal>,
|
||||||
|
|
|
@ -29,7 +29,7 @@ mod test_format {
|
||||||
let expected = expected.trim_end();
|
let expected = expected.trim_end();
|
||||||
|
|
||||||
match parse_with(&arena, input) {
|
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)
|
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("{}");
|
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
|
// NEWLINES
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -578,6 +578,21 @@ mod test_parse {
|
||||||
assert_eq!(Ok(expected), actual);
|
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]
|
#[test]
|
||||||
fn parenthetical_apply() {
|
fn parenthetical_apply() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
@ -630,6 +645,20 @@ mod test_parse {
|
||||||
assert_eq!(Ok(expected), actual);
|
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]
|
#[test]
|
||||||
fn closure_with_underscores() {
|
fn closure_with_underscores() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue