Merge branch 'parse-record-expr' into import-alias

This commit is contained in:
Folkert 2021-03-03 00:51:27 +01:00
commit a255805331
20 changed files with 1950 additions and 997 deletions

View file

@ -344,6 +344,253 @@ pub fn can_problem<'b>(
}
}
fn to_bad_ident_expr_report<'b>(
alloc: &'b RocDocAllocator<'b>,
bad_ident: roc_parse::ident::BadIdent,
surroundings: Region,
) -> RocDocBuilder<'b> {
use roc_parse::ident::BadIdent::*;
match bad_ident {
Start(_, _) | Space(_, _, _) => unreachable!("these are handled in the parser"),
WeirdDotAccess(row, col) | StrayDot(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow(r"I trying to parse a record field accessor here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("Something like "),
alloc.parser_suggestion(".name"),
alloc.reflow(" or "),
alloc.parser_suggestion(".height"),
alloc.reflow(" that accesses a value from a record."),
]),
])
}
PartStartsWithNumber(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow("I trying to parse a record field access here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("So I expect to see a lowercase letter next, like "),
alloc.parser_suggestion(".name"),
alloc.reflow(" or "),
alloc.parser_suggestion(".height"),
alloc.reflow("."),
]),
])
}
WeirdAccessor(_row, _col) => alloc.stack(vec![
alloc.reflow("I am very confused by this field access"),
alloc.region(surroundings),
alloc.concat(vec![
alloc.reflow("It looks like a field access on an accessor. I parse"),
alloc.parser_suggestion(".client.name"),
alloc.reflow(" as "),
alloc.parser_suggestion("(.client).name"),
alloc.reflow(". Maybe use an anonymous function like "),
alloc.parser_suggestion("(\\r -> r.client.name)"),
alloc.reflow(" instead"),
alloc.reflow("?"),
]),
]),
WeirdDotQualified(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow("I am trying to parse a qualified name here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("I was expecting to see an identifier next, like "),
alloc.parser_suggestion("height"),
alloc.reflow(". A complete qualified name looks something like "),
alloc.parser_suggestion("Json.Decode.string"),
alloc.text("."),
]),
])
}
QualifiedTag(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow("I am trying to parse a qualified name here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow(r"This looks like a qualified tag name to me, "),
alloc.reflow(r"but tags cannot be qualified! "),
alloc.reflow(r"Maybe you wanted a qualified name, something like "),
alloc.parser_suggestion("Json.Decode.string"),
alloc.text("?"),
]),
])
}
PrivateTagNotUppercase(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow("I am trying to parse a private tag here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow(r"But after the "),
alloc.keyword("@"),
alloc.reflow(r" symbol I found a lowercase letter. "),
alloc.reflow(r"All tag names (global and private)"),
alloc.reflow(r" must start with an uppercase letter, like "),
alloc.parser_suggestion("@UUID"),
alloc.reflow(" or "),
alloc.parser_suggestion("@Secrets"),
alloc.reflow("."),
]),
])
}
PrivateTagFieldAccess(_row, _col) => alloc.stack(vec![
alloc.reflow("I am very confused by this field access:"),
alloc.region(surroundings),
alloc.concat(vec![
alloc.reflow(r"It looks like a record field access on a private tag.")
]),
]),
_ => todo!(),
}
}
fn to_bad_ident_pattern_report<'b>(
alloc: &'b RocDocAllocator<'b>,
bad_ident: roc_parse::ident::BadIdent,
surroundings: Region,
) -> RocDocBuilder<'b> {
use roc_parse::ident::BadIdent::*;
match bad_ident {
Start(_, _) | Space(_, _, _) => unreachable!("these are handled in the parser"),
WeirdDotAccess(row, col) | StrayDot(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow(r"I trying to parse a record field accessor here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("Something like "),
alloc.parser_suggestion(".name"),
alloc.reflow(" or "),
alloc.parser_suggestion(".height"),
alloc.reflow(" that accesses a value from a record."),
]),
])
}
PartStartsWithNumber(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow("I trying to parse a record field access here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("So I expect to see a lowercase letter next, like "),
alloc.parser_suggestion(".name"),
alloc.reflow(" or "),
alloc.parser_suggestion(".height"),
alloc.reflow("."),
]),
])
}
WeirdAccessor(_row, _col) => alloc.stack(vec![
alloc.reflow("I am very confused by this field access"),
alloc.region(surroundings),
alloc.concat(vec![
alloc.reflow("It looks like a field access on an accessor. I parse"),
alloc.parser_suggestion(".client.name"),
alloc.reflow(" as "),
alloc.parser_suggestion("(.client).name"),
alloc.reflow(". Maybe use an anonymous function like "),
alloc.parser_suggestion("(\\r -> r.client.name)"),
alloc.reflow(" instead"),
alloc.reflow("?"),
]),
]),
WeirdDotQualified(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow("I am trying to parse a qualified name here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("I was expecting to see an identifier next, like "),
alloc.parser_suggestion("height"),
alloc.reflow(". A complete qualified name looks something like "),
alloc.parser_suggestion("Json.Decode.string"),
alloc.text("."),
]),
])
}
QualifiedTag(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow("I am trying to parse a qualified name here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow(r"This looks like a qualified tag name to me, "),
alloc.reflow(r"but tags cannot be qualified! "),
alloc.reflow(r"Maybe you wanted a qualified name, something like "),
alloc.parser_suggestion("Json.Decode.string"),
alloc.text("?"),
]),
])
}
PrivateTagNotUppercase(row, col) => {
let region = Region::from_row_col(row, col);
alloc.stack(vec![
alloc.reflow("I am trying to parse a private tag here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow(r"But after the "),
alloc.keyword("@"),
alloc.reflow(r" symbol I found a lowercase letter. "),
alloc.reflow(r"All tag names (global and private)"),
alloc.reflow(r" must start with an uppercase letter, like "),
alloc.parser_suggestion("@UUID"),
alloc.reflow(" or "),
alloc.parser_suggestion("@Secrets"),
alloc.reflow("."),
]),
])
}
PrivateTagFieldAccess(_row, _col) => alloc.stack(vec![
alloc.reflow("I am very confused by this field access:"),
alloc.region(surroundings),
alloc.concat(vec![
alloc.reflow(r"It looks like a record field access on a private tag.")
]),
]),
Underscore(row, col) => {
let region = Region::from_row_col(row, col - 1);
alloc.stack(vec![
alloc.reflow("I am trying to parse an identifier here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![alloc.reflow(
r"Underscores are not allowed in identifiers. Use camelCase instead!",
)]),
])
}
_ => todo!(),
}
}
fn pretty_runtime_error<'b>(
alloc: &'b RocDocAllocator<'b>,
runtime_error: RuntimeError,
@ -432,6 +679,7 @@ fn pretty_runtime_error<'b>(
MalformedBase(Base::Binary) => " binary integer ",
MalformedBase(Base::Octal) => " octal integer ",
MalformedBase(Base::Decimal) => " integer ",
BadIdent(bad_ident) => return to_bad_ident_pattern_report(alloc, bad_ident, region),
Unknown => " ",
QualifiedIdentifier => " qualified ",
};
@ -440,7 +688,7 @@ fn pretty_runtime_error<'b>(
MalformedInt | MalformedFloat | MalformedBase(_) => alloc
.tip()
.append(alloc.reflow("Learn more about number literals at TODO")),
Unknown => alloc.nil(),
Unknown | BadIdent(_) => alloc.nil(),
QualifiedIdentifier => alloc.tip().append(
alloc.reflow("In patterns, only private and global tags can be qualified"),
),
@ -482,15 +730,10 @@ fn pretty_runtime_error<'b>(
// do nothing, reported with PrecedenceProblem
unreachable!()
}
RuntimeError::MalformedIdentifier(box_str, region) => {
alloc.stack(vec![
alloc.concat(vec![
alloc.reflow("The ")
.append(format!("`{}`", box_str))
.append(alloc.reflow(" identifier is malformed:")),
]),
alloc.region(region),
])
RuntimeError::MalformedIdentifier(_box_str, bad_ident, surroundings) => {
to_bad_ident_expr_report(alloc, bad_ident, surroundings)
}
RuntimeError::MalformedClosure(_) => todo!(""),
RuntimeError::InvalidFloat(sign @ FloatErrorKind::PositiveInfinity, region, _raw_str)

View file

@ -145,23 +145,32 @@ fn to_syntax_report<'a>(
}
Type(typ) => to_type_report(alloc, filename, &typ, 0, 0),
Pattern(pat) => to_pattern_report(alloc, filename, &pat, 0, 0),
Expr(expr) => to_expr_report(alloc, filename, Context::InDef, &expr, 0, 0),
Expr(expr) => to_expr_report(
alloc,
filename,
Context::InDef(start_row, start_col),
&expr,
0,
0,
),
_ => todo!("unhandled parse error: {:?}", parse_problem),
}
}
enum Context {
InNode(Node, Row, Col, Box<Context>),
InDef,
InDef(Row, Col),
}
enum Node {
WhenCondition,
WhenBranch,
WhenIfGuard,
IfCondition,
IfThenBranch,
IfElseBranch,
ListElement,
InsideParens,
}
fn to_expr_report<'a>(
@ -169,8 +178,8 @@ fn to_expr_report<'a>(
filename: PathBuf,
context: Context,
parse_problem: &roc_parse::parser::EExpr<'a>,
_start_row: Row,
_start_col: Col,
start_row: Row,
start_col: Col,
) -> Report<'a> {
use roc_parse::parser::EExpr;
@ -184,6 +193,134 @@ fn to_expr_report<'a>(
EExpr::Str(string, row, col) => {
to_str_report(alloc, filename, context, &string, *row, *col)
}
EExpr::InParens(expr, row, col) => {
to_expr_in_parens_report(alloc, filename, context, &expr, *row, *col)
}
EExpr::Type(tipe, row, col) => to_type_report(alloc, filename, &tipe, *row, *col),
EExpr::Def(syntax, row, col) => to_syntax_report(alloc, filename, syntax, *row, *col),
EExpr::ElmStyleFunction(region, row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = *region;
let doc = alloc.stack(vec![
alloc.reflow(r"I am in the middle of parsing a definition, but I got stuck here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("Looks like you are trying to define a function. "),
alloc.reflow("In roc, functions are always written as a lambda, like "),
alloc.parser_suggestion("increment = \\n -> n + 1"),
alloc.reflow("."),
]),
]);
Report {
filename,
doc,
title: "ARGUMENTS BEFORE EQUALS".to_string(),
}
}
EExpr::Ident(_row, _col) => unreachable!("another branch would be taken"),
EExpr::QualifiedTag(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
let doc = alloc.stack(vec![
alloc.reflow(r"I am very confused by this identifier:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("Are you trying to qualify a name? I am execting something like "),
alloc.parser_suggestion("Json.Decode.string"),
alloc.reflow(". Maybe you are trying to qualify a tag? Tags like "),
alloc.parser_suggestion("Err"),
alloc.reflow(" are globally scoped in roc, and cannot be qualified."),
]),
]);
Report {
filename,
doc,
title: "WEIRD IDENTIFIER".to_string(),
}
}
EExpr::Start(row, col) => {
let (context_row, context_col, a_thing) = match context {
Context::InNode(node, r, c, _) => match node {
Node::WhenCondition | Node::WhenBranch | Node::WhenIfGuard => (
r,
c,
alloc.concat(vec![
alloc.text("an "),
alloc.keyword("when"),
alloc.text(" expression"),
]),
),
Node::IfCondition | Node::IfThenBranch | Node::IfElseBranch => (
r,
c,
alloc.concat(vec![
alloc.text("an "),
alloc.keyword("if"),
alloc.text(" expression"),
]),
),
Node::ListElement => (r, c, alloc.text("a list")),
Node::InsideParens => (r, c, alloc.text("some parentheses")),
},
Context::InDef(r, c) => (r, c, alloc.text("a definition")),
};
let surroundings = Region::from_rows_cols(context_row, context_col, *row, *col);
let region = Region::from_row_col(*row, *col);
let doc = alloc.stack(vec![
alloc.concat(vec![
alloc.reflow(r"I am partway through parsing "),
a_thing,
alloc.reflow(", but I got stuck here:"),
]),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("I was expecting to see an expression like "),
alloc.parser_suggestion("42"),
alloc.reflow(" or "),
alloc.parser_suggestion("\"hello\""),
alloc.text("."),
]),
]);
Report {
filename,
doc,
title: "MISSING EXPRESSION".to_string(),
}
}
EExpr::Colon(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, *row, *col);
let region = Region::from_row_col(*row, *col);
let doc = alloc.stack(vec![
alloc.reflow(r"I am in the middle of parsing a definition, but I got stuck here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow("Looks like you are trying to define a function. "),
alloc.reflow("In roc, functions are always written as a lambda, like "),
alloc.parser_suggestion("increment = \\n -> n + 1"),
alloc.reflow("."),
]),
]);
Report {
filename,
doc,
title: "ARGUMENTS BEFORE EQUALS".to_string(),
}
}
_ => todo!("unhandled parse error: {:?}", parse_problem),
}
}
@ -334,7 +471,14 @@ fn to_lambda_report<'a>(
ELambda::Start(_row, _col) => unreachable!("another branch would have been taken"),
ELambda::Syntax(syntax, row, col) => to_syntax_report(alloc, filename, syntax, row, col),
ELambda::Body(expr, row, col) => to_expr_report(
alloc,
filename,
Context::InDef(start_row, start_col),
expr,
row,
col,
),
ELambda::Pattern(ref pattern, row, col) => {
to_pattern_report(alloc, filename, pattern, row, col)
}
@ -538,6 +682,75 @@ fn to_str_report<'a>(
}
}
}
fn to_expr_in_parens_report<'a>(
alloc: &'a RocDocAllocator<'a>,
filename: PathBuf,
context: Context,
parse_problem: &roc_parse::parser::EInParens<'a>,
start_row: Row,
start_col: Col,
) -> Report<'a> {
use roc_parse::parser::EInParens;
match *parse_problem {
EInParens::Space(error, row, col) => to_space_report(alloc, filename, &error, row, col),
EInParens::Expr(expr, row, col) => to_expr_report(
alloc,
filename,
Context::InNode(Node::InsideParens, start_row, start_col, Box::new(context)),
expr,
row,
col,
),
EInParens::End(row, col) | EInParens::IndentEnd(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
alloc
.reflow("I am partway through parsing a record pattern, but I got stuck here:"),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow(
r"I was expecting to see a closing parenthesis next, so try adding a ",
),
alloc.parser_suggestion(")"),
alloc.reflow(" and see if that helps?"),
]),
]);
Report {
filename,
doc,
title: "UNFINISHED PARENTHESES".to_string(),
}
}
EInParens::Open(row, col) | EInParens::IndentOpen(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);
let region = Region::from_row_col(row, col);
let doc = alloc.stack(vec![
alloc.reflow(
r"I just started parsing an expression in parentheses, but I got stuck here:",
),
alloc.region_with_subregion(surroundings, region),
alloc.concat(vec![
alloc.reflow(r"An expression in parentheses looks like "),
alloc.parser_suggestion("(32)"),
alloc.reflow(r" or "),
alloc.parser_suggestion("(\"hello\")"),
alloc.reflow(" so I was expecting to see an expression next."),
]),
]);
Report {
filename,
doc,
title: "UNFINISHED PARENTHESES".to_string(),
}
}
}
}
fn to_list_report<'a>(
alloc: &'a RocDocAllocator<'a>,
@ -799,18 +1012,14 @@ fn to_when_report<'a>(
title: "IF GUARD NO CONDITION".to_string(),
}
}
_ => {
// to_expr_report(
// alloc,
// filename,
// Context::InNode(Node::WhenIfGuard, start_row, start_col, Box::new(context)),
// expr,
// row,
// col,
// )
to_syntax_report(alloc, filename, nested, row, col)
}
_ => to_expr_report(
alloc,
filename,
Context::InNode(Node::WhenIfGuard, start_row, start_col, Box::new(context)),
nested,
row,
col,
),
},
When::Arrow(row, col) => {
let surroundings = Region::from_rows_cols(start_row, start_col, row, col);