mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
basic structure
This commit is contained in:
parent
28cc3348a6
commit
703a8de72f
5 changed files with 362 additions and 18 deletions
|
@ -117,6 +117,22 @@ where
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_indent<'a, E>(
|
||||||
|
min_indent: u16,
|
||||||
|
indent_problem: fn(Row, Col) -> E,
|
||||||
|
) -> impl Parser<'a, (), E>
|
||||||
|
where
|
||||||
|
E: 'a,
|
||||||
|
{
|
||||||
|
move |_, state: State<'a>| {
|
||||||
|
if state.column > state.indent_col {
|
||||||
|
Ok((NoProgress, (), state))
|
||||||
|
} else {
|
||||||
|
Err((NoProgress, indent_problem(state.line, state.column), state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn space0_e<'a, E>(
|
pub fn space0_e<'a, E>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
space_problem: fn(BadInputError, Row, Col) -> E,
|
space_problem: fn(BadInputError, Row, Col) -> E,
|
||||||
|
|
|
@ -368,6 +368,22 @@ fn parse_loc_term<'a>(
|
||||||
.parse(arena, state)
|
.parse(arena, state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_loc_term_better<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
arena: &'a Bump,
|
||||||
|
state: State<'a>,
|
||||||
|
) -> ParseResult<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
|
one_of!(
|
||||||
|
in_parens_region_fix(min_indent),
|
||||||
|
loc!(specialize(EExpr::Str, string_literal_help())),
|
||||||
|
loc!(specialize(EExpr::Number, positive_number_literal_help())),
|
||||||
|
loc!(record_literal_help(min_indent)),
|
||||||
|
loc!(specialize(EExpr::List, list_literal_help(min_indent))),
|
||||||
|
loc!(ident_etc_help(min_indent))
|
||||||
|
)
|
||||||
|
.parse(arena, state)
|
||||||
|
}
|
||||||
|
|
||||||
fn loc_possibly_negative_or_negated_term<'a>(
|
fn loc_possibly_negative_or_negated_term<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
|
) -> impl Parser<'a, Located<Expr<'a>>, EExpr<'a>> {
|
||||||
|
@ -555,29 +571,271 @@ fn foobar<'a>(
|
||||||
|
|
||||||
match space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state) {
|
match space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state) {
|
||||||
Err((_, _, state)) => Ok((MadeProgress, expr.value, state)),
|
Err((_, _, state)) => Ok((MadeProgress, expr.value, state)),
|
||||||
Ok((_, spaces_before_op, state)) => parse_expr_end(
|
Ok((_, spaces_before_op, state)) => {
|
||||||
min_indent,
|
let expr_state = ExprState {
|
||||||
Vec::new_in(arena),
|
operators: Vec::new_in(arena),
|
||||||
Vec::new_in(arena),
|
arguments: Vec::new_in(arena),
|
||||||
expr,
|
expr,
|
||||||
spaces_before_op,
|
spaces_after: spaces_before_op,
|
||||||
initial,
|
initial,
|
||||||
arena,
|
end: state.get_position(),
|
||||||
state,
|
};
|
||||||
),
|
|
||||||
|
parse_expr_end(min_indent, expr_state, arena, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ExprState<'a> {
|
||||||
|
operators: Vec<'a, (Located<Expr<'a>>, Located<BinOp>)>,
|
||||||
|
arguments: Vec<'a, &'a Located<Expr<'a>>>,
|
||||||
|
expr: Located<Expr<'a>>,
|
||||||
|
spaces_after: &'a [CommentOrNewline<'a>],
|
||||||
|
initial: State<'a>,
|
||||||
|
end: Position,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr_final<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
expr_state: ExprState<'a>,
|
||||||
|
arena: &'a Bump,
|
||||||
|
state: State<'a>,
|
||||||
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
|
let mut expr = to_call(
|
||||||
|
arena,
|
||||||
|
expr_state.arguments,
|
||||||
|
expr_state.expr,
|
||||||
|
expr_state.spaces_after,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (left_arg, op) in expr_state.operators.into_iter().rev() {
|
||||||
|
let region = Region::span_across(&left_arg.region, &expr.region);
|
||||||
|
let new = Expr::BinOp(arena.alloc((left_arg, op, expr)));
|
||||||
|
expr = Located::at(region, new);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((MadeProgress, expr.value, state))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_call<'a>(
|
||||||
|
arena: &'a Bump,
|
||||||
|
arguments: Vec<'a, &'a Located<Expr<'a>>>,
|
||||||
|
loc_expr1: Located<Expr<'a>>,
|
||||||
|
_spaces_before: &'a [CommentOrNewline<'a>],
|
||||||
|
) -> Located<Expr<'a>> {
|
||||||
|
if arguments.is_empty() {
|
||||||
|
loc_expr1
|
||||||
|
} else {
|
||||||
|
let last = arguments.last().map(|x| x.region).unwrap_or_default();
|
||||||
|
let region = Region::span_across(&loc_expr1.region, &last);
|
||||||
|
|
||||||
|
let apply = Expr::Apply(
|
||||||
|
arena.alloc(loc_expr1),
|
||||||
|
arguments.into_bump_slice(),
|
||||||
|
CalledVia::Space,
|
||||||
|
);
|
||||||
|
|
||||||
|
Located::at(region, apply)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr_operator<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
mut expr_state: ExprState<'a>,
|
||||||
|
loc_op: Located<BinOp>,
|
||||||
|
arena: &'a Bump,
|
||||||
|
state: State<'a>,
|
||||||
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
|
let (space_progress, mut spaces, state) =
|
||||||
|
space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state)?;
|
||||||
|
|
||||||
|
// add previous spaces to the expr
|
||||||
|
std::mem::swap(&mut spaces, &mut expr_state.spaces_after);
|
||||||
|
|
||||||
|
expr_state.expr = if spaces.is_empty() {
|
||||||
|
expr_state.expr
|
||||||
|
} else {
|
||||||
|
arena
|
||||||
|
.alloc(expr_state.expr.value)
|
||||||
|
.with_spaces_after(spaces, expr_state.expr.region)
|
||||||
|
};
|
||||||
|
|
||||||
|
// a `-` is unary if it is preceded by a space and not followed by a space
|
||||||
|
|
||||||
|
let op = loc_op.value;
|
||||||
|
let op_start = loc_op.region.start();
|
||||||
|
let op_end = loc_op.region.end();
|
||||||
|
let new_start = state.get_position();
|
||||||
|
if op == BinOp::Minus && expr_state.end != op_start && op_end == new_start {
|
||||||
|
// negative terms
|
||||||
|
|
||||||
|
let (_, arg, state) = parse_loc_term(min_indent, arena, state)?;
|
||||||
|
let new_end = state.get_position();
|
||||||
|
todo!()
|
||||||
|
} else {
|
||||||
|
match loc_possibly_negative_or_negated_term(min_indent).parse(arena, state) {
|
||||||
|
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
||||||
|
Ok((_, new_expr, state)) => {
|
||||||
|
let new_end = state.get_position();
|
||||||
|
|
||||||
|
expr_state.initial = state;
|
||||||
|
|
||||||
|
match space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state) {
|
||||||
|
Err((_, _, state)) => {
|
||||||
|
let spaces = expr_state.spaces_after;
|
||||||
|
|
||||||
|
let new_expr = if spaces.is_empty() {
|
||||||
|
new_expr
|
||||||
|
} else {
|
||||||
|
arena
|
||||||
|
.alloc(new_expr.value)
|
||||||
|
.with_spaces_before(spaces, new_expr.region)
|
||||||
|
};
|
||||||
|
|
||||||
|
let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena));
|
||||||
|
|
||||||
|
let call = to_call(arena, args, expr_state.expr, spaces);
|
||||||
|
|
||||||
|
expr_state.operators.push((call, loc_op));
|
||||||
|
expr_state.expr = new_expr;
|
||||||
|
expr_state.end = new_end;
|
||||||
|
expr_state.spaces_after = &[];
|
||||||
|
|
||||||
|
parse_expr_final(min_indent, expr_state, arena, state)
|
||||||
|
}
|
||||||
|
Ok((_, mut spaces, state)) => {
|
||||||
|
std::mem::swap(&mut spaces, &mut expr_state.spaces_after);
|
||||||
|
|
||||||
|
let new_expr = if spaces.is_empty() {
|
||||||
|
new_expr
|
||||||
|
} else {
|
||||||
|
arena
|
||||||
|
.alloc(new_expr.value)
|
||||||
|
.with_spaces_before(spaces, new_expr.region)
|
||||||
|
};
|
||||||
|
|
||||||
|
let args = std::mem::replace(&mut expr_state.arguments, Vec::new_in(arena));
|
||||||
|
|
||||||
|
let call = to_call(arena, args, expr_state.expr, spaces);
|
||||||
|
|
||||||
|
expr_state.operators.push((call, loc_op));
|
||||||
|
expr_state.expr = new_expr;
|
||||||
|
expr_state.end = new_end;
|
||||||
|
|
||||||
|
parse_expr_end(min_indent, expr_state, arena, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err((NoProgress, _, _)) => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr_end2<'a>(
|
||||||
|
min_indent: u16,
|
||||||
|
mut expr_state: ExprState<'a>,
|
||||||
|
arena: &'a Bump,
|
||||||
|
state: State<'a>,
|
||||||
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
|
let parser = skip_first!(
|
||||||
|
crate::blankspace::check_indent(min_indent, EExpr::IndentEnd),
|
||||||
|
move |a, s| parse_loc_term_better(min_indent, a, s)
|
||||||
|
);
|
||||||
|
|
||||||
|
match parser.parse(arena, state) {
|
||||||
|
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
||||||
|
Ok((_, arg, state)) => {
|
||||||
|
let new_end = state.get_position();
|
||||||
|
|
||||||
|
expr_state.initial = state;
|
||||||
|
|
||||||
|
match space0_e(min_indent, EExpr::Space, EExpr::IndentEnd).parse(arena, state) {
|
||||||
|
Err((_, _, state)) => {
|
||||||
|
let spaces = expr_state.spaces_after;
|
||||||
|
|
||||||
|
let arg = if spaces.is_empty() {
|
||||||
|
arg
|
||||||
|
} else {
|
||||||
|
arena
|
||||||
|
.alloc(arg.value)
|
||||||
|
.with_spaces_before(spaces, arg.region)
|
||||||
|
};
|
||||||
|
|
||||||
|
expr_state.arguments.push(arena.alloc(arg));
|
||||||
|
expr_state.end = new_end;
|
||||||
|
expr_state.spaces_after = &[];
|
||||||
|
|
||||||
|
parse_expr_final(min_indent, expr_state, arena, state)
|
||||||
|
}
|
||||||
|
Ok((_, mut spaces, state)) => {
|
||||||
|
std::mem::swap(&mut spaces, &mut expr_state.spaces_after);
|
||||||
|
|
||||||
|
let arg = if spaces.is_empty() {
|
||||||
|
arg
|
||||||
|
} else {
|
||||||
|
arena
|
||||||
|
.alloc(arg.value)
|
||||||
|
.with_spaces_before(spaces, arg.region)
|
||||||
|
};
|
||||||
|
|
||||||
|
expr_state.arguments.push(arena.alloc(arg));
|
||||||
|
expr_state.end = new_end;
|
||||||
|
|
||||||
|
parse_expr_end(min_indent, expr_state, arena, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err((NoProgress, _, _)) => {
|
||||||
|
// try an operator
|
||||||
|
match loc!(operator()).parse(arena, state) {
|
||||||
|
Err((MadeProgress, f, s)) => Err((MadeProgress, f, s)),
|
||||||
|
Ok((_, loc_op, state)) => {
|
||||||
|
parse_expr_operator(min_indent, expr_state, loc_op, arena, state)
|
||||||
|
}
|
||||||
|
Err((NoProgress, _, _)) => {
|
||||||
|
// roll back space parsing
|
||||||
|
let state = expr_state.initial;
|
||||||
|
|
||||||
|
if expr_state.operators.is_empty() {
|
||||||
|
let expr = to_call(
|
||||||
|
arena,
|
||||||
|
expr_state.arguments,
|
||||||
|
expr_state.expr,
|
||||||
|
expr_state.spaces_after,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok((MadeProgress, expr.value, state))
|
||||||
|
} else {
|
||||||
|
let mut expr = to_call(
|
||||||
|
arena,
|
||||||
|
expr_state.arguments,
|
||||||
|
expr_state.expr,
|
||||||
|
expr_state.spaces_after,
|
||||||
|
);
|
||||||
|
|
||||||
|
for (left_arg, op) in expr_state.operators.into_iter().rev() {
|
||||||
|
let region = Region::span_across(&left_arg.region, &expr.region);
|
||||||
|
let new = Expr::BinOp(arena.alloc((left_arg, op, expr)));
|
||||||
|
expr = Located::at(region, new);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((MadeProgress, expr.value, state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr_end<'a>(
|
fn parse_expr_end<'a>(
|
||||||
min_indent: u16,
|
min_indent: u16,
|
||||||
operators: Vec<'a, (Located<Expr<'a>>, BinOp)>,
|
mut expr_state: ExprState<'a>,
|
||||||
arguments: Vec<'a, Located<Expr<'a>>>,
|
|
||||||
loc_expr1: Located<Expr<'a>>,
|
|
||||||
spaces_before: &'a [CommentOrNewline<'a>],
|
|
||||||
initial: State<'a>,
|
|
||||||
arena: &'a Bump,
|
arena: &'a Bump,
|
||||||
state: State<'a>,
|
state: State<'a>,
|
||||||
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
) -> ParseResult<'a, Expr<'a>, EExpr<'a>> {
|
||||||
|
return parse_expr_end2(min_indent, expr_state, arena, state);
|
||||||
let parser = and!(
|
let parser = and!(
|
||||||
loc!(operator()),
|
loc!(operator()),
|
||||||
// The spaces *after* the operator can be attached directly to
|
// The spaces *after* the operator can be attached directly to
|
||||||
|
@ -592,6 +850,12 @@ fn parse_expr_end<'a>(
|
||||||
|
|
||||||
match parser.parse(arena, state) {
|
match parser.parse(arena, state) {
|
||||||
Ok((_, (loc_op, loc_expr2), state)) => {
|
Ok((_, (loc_op, loc_expr2), state)) => {
|
||||||
|
let ExprState {
|
||||||
|
expr: loc_expr1,
|
||||||
|
spaces_after: spaces_before,
|
||||||
|
..
|
||||||
|
} = expr_state;
|
||||||
|
|
||||||
let loc_expr1 = if spaces_before.is_empty() {
|
let loc_expr1 = if spaces_before.is_empty() {
|
||||||
loc_expr1
|
loc_expr1
|
||||||
} else {
|
} else {
|
||||||
|
@ -604,7 +868,7 @@ fn parse_expr_end<'a>(
|
||||||
|
|
||||||
Ok((MadeProgress, Expr::BinOp(tuple), state))
|
Ok((MadeProgress, Expr::BinOp(tuple), state))
|
||||||
}
|
}
|
||||||
Err((NoProgress, _, _)) => Ok((MadeProgress, loc_expr1.value, initial)),
|
Err((NoProgress, _, _)) => Ok((MadeProgress, expr_state.expr.value, expr_state.initial)),
|
||||||
Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)),
|
Err((MadeProgress, fail, state)) => Err((MadeProgress, fail, state)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2389,6 +2653,29 @@ fn string_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString<'a>> {
|
||||||
map!(crate::string_literal::parse(), Expr::Str)
|
map!(crate::string_literal::parse(), Expr::Str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn positive_number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, Number> {
|
||||||
|
map!(
|
||||||
|
crate::number_literal::positive_number_literal(),
|
||||||
|
|literal| {
|
||||||
|
use crate::number_literal::NumLiteral::*;
|
||||||
|
|
||||||
|
match literal {
|
||||||
|
Num(s) => Expr::Num(s),
|
||||||
|
Float(s) => Expr::Float(s),
|
||||||
|
NonBase10Int {
|
||||||
|
string,
|
||||||
|
base,
|
||||||
|
is_negative,
|
||||||
|
} => Expr::NonBase10Int {
|
||||||
|
string,
|
||||||
|
base,
|
||||||
|
is_negative,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, Number> {
|
fn number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, Number> {
|
||||||
map!(crate::number_literal::number_literal(), |literal| {
|
map!(crate::number_literal::number_literal(), |literal| {
|
||||||
use crate::number_literal::NumLiteral::*;
|
use crate::number_literal::NumLiteral::*;
|
||||||
|
|
|
@ -11,6 +11,20 @@ pub enum NumLiteral<'a> {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn positive_number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, Number> {
|
||||||
|
move |_arena, state: State<'a>| {
|
||||||
|
match state.bytes.get(0) {
|
||||||
|
Some(first_byte) if (*first_byte as char).is_ascii_digit() => {
|
||||||
|
parse_number_base(false, &state.bytes, state)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// this is not a number at all
|
||||||
|
Err((Progress::NoProgress, Number::End, state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, Number> {
|
pub fn number_literal<'a>() -> impl Parser<'a, NumLiteral<'a>, Number> {
|
||||||
move |_arena, state: State<'a>| {
|
move |_arena, state: State<'a>| {
|
||||||
match state.bytes.get(0) {
|
match state.bytes.get(0) {
|
||||||
|
|
|
@ -640,6 +640,33 @@ mod test_parse {
|
||||||
assert_eq!(Ok(expected), actual);
|
assert_eq!(Ok(expected), actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn newline_and_spaces_before_less_than() {
|
||||||
|
let arena = Bump::new();
|
||||||
|
let spaced_int = arena.alloc(Num("1")).after(&[Newline]);
|
||||||
|
let tuple = arena.alloc((
|
||||||
|
Located::new(0, 0, 4, 5, spaced_int),
|
||||||
|
Located::new(1, 1, 4, 5, LessThan),
|
||||||
|
Located::new(1, 1, 6, 7, Num("2")),
|
||||||
|
));
|
||||||
|
|
||||||
|
let newlines = bumpalo::vec![in &arena; Newline, Newline];
|
||||||
|
let def = Def::Body(
|
||||||
|
arena.alloc(Located::new(0, 0, 0, 1, Identifier("x"))),
|
||||||
|
arena.alloc(Located::new(0, 1, 4, 7, BinOp(tuple))),
|
||||||
|
);
|
||||||
|
let loc_def = &*arena.alloc(Located::new(0, 1, 0, 7, def));
|
||||||
|
let defs = &[loc_def];
|
||||||
|
let ret = Expr::SpaceBefore(arena.alloc(Num("42")), newlines.into_bump_slice());
|
||||||
|
let loc_ret = Located::new(3, 3, 0, 2, ret);
|
||||||
|
let expected = Defs(defs, arena.alloc(loc_ret));
|
||||||
|
|
||||||
|
// let expected = BinOp(tuple);
|
||||||
|
let actual = parse_expr_with(&arena, "x = 1\n < 2\n\n42");
|
||||||
|
|
||||||
|
assert_eq!(Ok(expected), actual);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn comment_with_non_ascii() {
|
fn comment_with_non_ascii() {
|
||||||
let arena = Bump::new();
|
let arena = Bump::new();
|
||||||
|
|
|
@ -4199,14 +4199,14 @@ mod test_reporting {
|
||||||
x == 5
|
x == 5
|
||||||
Num.add 1 2
|
Num.add 1 2
|
||||||
|
|
||||||
x y
|
{ x, y }
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
── TOO MANY ARGS ───────────────────────────────────────────────────────────────
|
── TOO MANY ARGS ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
The `add` function expects 2 arguments, but it got 4 instead:
|
The `add` function expects 2 arguments, but it got 3 instead:
|
||||||
|
|
||||||
4│ Num.add 1 2
|
4│ Num.add 1 2
|
||||||
^^^^^^^
|
^^^^^^^
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue