mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 06:44:46 +00:00
parameterize Parser on an Error type
This commit is contained in:
parent
da28486184
commit
148fffe969
17 changed files with 372 additions and 271 deletions
|
@ -2,9 +2,9 @@ use crate::ast::CommentOrNewline::{self, *};
|
|||
use crate::ast::{Attempting, Spaceable};
|
||||
use crate::parser::{
|
||||
self, and, ascii_char, ascii_string, backtrackable, optional, parse_utf8, peek_utf8_char, then,
|
||||
unexpected, unexpected_eof, FailReason, Parser,
|
||||
unexpected, unexpected_eof, Parser,
|
||||
Progress::{self, *},
|
||||
State,
|
||||
State, SyntaxError,
|
||||
};
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
|
@ -15,12 +15,15 @@ use roc_region::all::Located;
|
|||
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
|
||||
/// If any newlines or comments were found, the Expr will be wrapped in a SpaceBefore and/or
|
||||
/// SpaceAfter as appropriate.
|
||||
pub fn space0_around<'a, P, S>(parser: P, min_indent: u16) -> impl Parser<'a, Located<S>>
|
||||
pub fn space0_around<'a, P, S>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<S>, SyntaxError>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
S: Sized,
|
||||
P: Parser<'a, Located<S>>,
|
||||
P: Parser<'a, Located<S>, SyntaxError>,
|
||||
P: 'a,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
|
@ -62,11 +65,14 @@ where
|
|||
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
|
||||
/// If any newlines or comments were found, the Expr will be wrapped in a SpaceBefore and/or
|
||||
/// SpaceAfter as appropriate.
|
||||
pub fn space1_around<'a, P, S>(parser: P, min_indent: u16) -> impl Parser<'a, Located<S>>
|
||||
pub fn space1_around<'a, P, S>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<S>, SyntaxError>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Located<S>>,
|
||||
P: Parser<'a, Located<S>, SyntaxError>,
|
||||
P: 'a,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
|
@ -100,11 +106,14 @@ where
|
|||
/// Parses the given expression with 0 or more (spaces/comments/newlines) before it.
|
||||
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
|
||||
/// The Expr will be wrapped in a SpaceBefore if there were any newlines or comments found.
|
||||
pub fn space0_before<'a, P, S>(parser: P, min_indent: u16) -> impl Parser<'a, Located<S>>
|
||||
pub fn space0_before<'a, P, S>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<S>, SyntaxError>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Located<S>>,
|
||||
P: Parser<'a, Located<S>, SyntaxError>,
|
||||
P: 'a,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
|
@ -124,11 +133,14 @@ where
|
|||
/// Parses the given expression with 1 or more (spaces/comments/newlines) before it.
|
||||
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
|
||||
/// The Expr will be wrapped in a SpaceBefore if there were any newlines or comments found.
|
||||
pub fn space1_before<'a, P, S>(parser: P, min_indent: u16) -> impl Parser<'a, Located<S>>
|
||||
pub fn space1_before<'a, P, S>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<S>, SyntaxError>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Located<S>>,
|
||||
P: Parser<'a, Located<S>, SyntaxError>,
|
||||
P: 'a,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
|
@ -148,11 +160,14 @@ where
|
|||
/// Parses the given expression with 0 or more (spaces/comments/newlines) after it.
|
||||
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
|
||||
/// The Expr will be wrapped in a SpaceAfter if there were any newlines or comments found.
|
||||
pub fn space0_after<'a, P, S>(parser: P, min_indent: u16) -> impl Parser<'a, Located<S>>
|
||||
pub fn space0_after<'a, P, S>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<S>, SyntaxError>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Located<S>>,
|
||||
P: Parser<'a, Located<S>, SyntaxError>,
|
||||
P: 'a,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
|
@ -172,11 +187,14 @@ where
|
|||
/// Parses the given expression with 1 or more (spaces/comments/newlines) after it.
|
||||
/// Returns a Located<Expr> where the location is around the Expr, ignoring the spaces.
|
||||
/// The Expr will be wrapped in a SpaceAfter if there were any newlines or comments found.
|
||||
pub fn space1_after<'a, P, S>(parser: P, min_indent: u16) -> impl Parser<'a, Located<S>>
|
||||
pub fn space1_after<'a, P, S>(
|
||||
parser: P,
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<S>, SyntaxError>
|
||||
where
|
||||
S: Spaceable<'a>,
|
||||
S: 'a,
|
||||
P: Parser<'a, Located<S>>,
|
||||
P: Parser<'a, Located<S>, SyntaxError>,
|
||||
P: 'a,
|
||||
{
|
||||
parser::map_with_arena(
|
||||
|
@ -194,12 +212,12 @@ where
|
|||
}
|
||||
|
||||
/// Zero or more (spaces/comments/newlines).
|
||||
pub fn space0<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>]> {
|
||||
pub fn space0<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError> {
|
||||
spaces(false, min_indent)
|
||||
}
|
||||
|
||||
/// One or more (spaces/comments/newlines).
|
||||
pub fn space1<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>]> {
|
||||
pub fn space1<'a>(min_indent: u16) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError> {
|
||||
// TODO try benchmarking a short-circuit for the typical case: see if there is
|
||||
// exactly one space followed by char that isn't [' ', '\n', or '#'], and
|
||||
// if so, return empty slice. The case where there's exactly 1 space should
|
||||
|
@ -214,7 +232,7 @@ enum LineState {
|
|||
DocComment,
|
||||
}
|
||||
|
||||
pub fn line_comment<'a>() -> impl Parser<'a, &'a str> {
|
||||
pub fn line_comment<'a>() -> impl Parser<'a, &'a str, SyntaxError> {
|
||||
then(
|
||||
and!(ascii_char(b'#'), optional(ascii_string("# "))),
|
||||
|arena: &'a Bump, state: State<'a>, _, (_, opt_doc)| {
|
||||
|
@ -242,7 +260,7 @@ pub fn line_comment<'a>() -> impl Parser<'a, &'a str> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn spaces_exactly<'a>(spaces_expected: u16) -> impl Parser<'a, ()> {
|
||||
pub fn spaces_exactly<'a>(spaces_expected: u16) -> impl Parser<'a, (), SyntaxError> {
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
if spaces_expected == 0 {
|
||||
return Ok((NoProgress, (), state));
|
||||
|
@ -269,10 +287,10 @@ pub fn spaces_exactly<'a>(spaces_expected: u16) -> impl Parser<'a, ()> {
|
|||
));
|
||||
}
|
||||
|
||||
Err(FailReason::BadUtf8) => {
|
||||
Err(SyntaxError::BadUtf8) => {
|
||||
// If we hit an invalid UTF-8 character, bail out immediately.
|
||||
let progress = Progress::progress_when(spaces_seen != 0);
|
||||
return state.fail(arena, progress, FailReason::BadUtf8);
|
||||
return state.fail(arena, progress, SyntaxError::BadUtf8);
|
||||
}
|
||||
Err(_) => {
|
||||
if spaces_seen == 0 {
|
||||
|
@ -306,7 +324,7 @@ pub fn spaces_exactly<'a>(spaces_expected: u16) -> impl Parser<'a, ()> {
|
|||
fn spaces<'a>(
|
||||
require_at_least_one: bool,
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, &'a [CommentOrNewline<'a>]> {
|
||||
) -> impl Parser<'a, &'a [CommentOrNewline<'a>], SyntaxError> {
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
let original_state = state.clone();
|
||||
let mut space_list = Vec::new_in(arena);
|
||||
|
@ -478,10 +496,10 @@ fn spaces<'a>(
|
|||
}
|
||||
}
|
||||
}
|
||||
Err(FailReason::BadUtf8) => {
|
||||
Err(SyntaxError::BadUtf8) => {
|
||||
// If we hit an invalid UTF-8 character, bail out immediately.
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
return state.fail(arena, progress, FailReason::BadUtf8);
|
||||
return state.fail(arena, progress, SyntaxError::BadUtf8);
|
||||
}
|
||||
Err(_) => {
|
||||
if require_at_least_one && bytes_parsed == 0 {
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::number_literal::number_literal;
|
|||
use crate::parser::{
|
||||
self, allocated, and_then_with_indent_level, ascii_char, ascii_string, attempt, backtrackable,
|
||||
fail, map, newline_char, not, not_followed_by, optional, sep_by1, then, unexpected,
|
||||
unexpected_eof, Bag, Either, FailReason, ParseResult, Parser, State,
|
||||
unexpected_eof, Bag, Either, ParseResult, Parser, State, SyntaxError,
|
||||
};
|
||||
use crate::type_annotation;
|
||||
use bumpalo::collections::string::String;
|
||||
|
@ -21,7 +21,7 @@ use roc_module::operator::{BinOp, CalledVia, UnaryOp};
|
|||
use roc_region::all::{Located, Region};
|
||||
|
||||
use crate::parser::Progress::{self, *};
|
||||
pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
// Recursive parsers must not directly invoke functions which return (impl Parser),
|
||||
// as this causes rustc to stack overflow. Thus, parse_expr must be a
|
||||
// separate function which recurses by calling itself directly.
|
||||
|
@ -152,7 +152,7 @@ fn loc_parse_expr_body_without_operators<'a>(
|
|||
min_indent: u16,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>> {
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, SyntaxError> {
|
||||
one_of!(
|
||||
loc_parenthetical_expr!(min_indent, loc_function_args(min_indent)),
|
||||
loc!(string_literal()),
|
||||
|
@ -171,7 +171,7 @@ fn loc_parse_expr_body_without_operators<'a>(
|
|||
/// Unary (!) or (-)
|
||||
///
|
||||
/// e.g. `!x` or `-x`
|
||||
pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
one_of!(
|
||||
map_with_arena!(
|
||||
// must backtrack to distinguish `!x` from `!= y`
|
||||
|
@ -196,7 +196,11 @@ pub fn unary_op<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Expr<'a>> {
|
||||
fn parse_expr<'a>(
|
||||
min_indent: u16,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, SyntaxError> {
|
||||
let expr_parser = crate::parser::map_with_arena(
|
||||
and!(
|
||||
// First parse the body without operators, then try to parse possible operators after.
|
||||
|
@ -239,7 +243,7 @@ fn parse_expr<'a>(min_indent: u16, arena: &'a Bump, state: State<'a>) -> ParseRe
|
|||
|
||||
/// If the given Expr would parse the same way as a valid Pattern, convert it.
|
||||
/// Example: (foo) could be either an Expr::Var("foo") or Pattern::Identifier("foo")
|
||||
fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>, FailReason> {
|
||||
fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>, SyntaxError> {
|
||||
match expr {
|
||||
Expr::Var { module_name, ident } => {
|
||||
if module_name.is_empty() {
|
||||
|
@ -322,7 +326,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
|||
| Expr::Record {
|
||||
update: Some(_), ..
|
||||
}
|
||||
| Expr::UnaryOp(_, _) => Err(FailReason::InvalidPattern),
|
||||
| Expr::UnaryOp(_, _) => Err(SyntaxError::InvalidPattern),
|
||||
|
||||
Expr::Str(string) => Ok(Pattern::StrLiteral(string.clone())),
|
||||
Expr::MalformedIdent(string) => Ok(Pattern::Malformed(string)),
|
||||
|
@ -333,7 +337,7 @@ fn expr_to_pattern<'a>(arena: &'a Bump, expr: &Expr<'a>) -> Result<Pattern<'a>,
|
|||
pub fn assigned_expr_field_to_pattern<'a>(
|
||||
arena: &'a Bump,
|
||||
assigned_field: &AssignedField<'a, Expr<'a>>,
|
||||
) -> Result<Pattern<'a>, FailReason> {
|
||||
) -> Result<Pattern<'a>, SyntaxError> {
|
||||
// the assigned fields always store spaces, but this slice is often empty
|
||||
Ok(match assigned_field {
|
||||
AssignedField::RequiredValue(name, spaces, value) => {
|
||||
|
@ -383,7 +387,7 @@ pub fn assigned_pattern_field_to_pattern<'a>(
|
|||
arena: &'a Bump,
|
||||
assigned_field: &AssignedField<'a, Expr<'a>>,
|
||||
backup_region: Region,
|
||||
) -> Result<Located<Pattern<'a>>, FailReason> {
|
||||
) -> Result<Located<Pattern<'a>>, SyntaxError> {
|
||||
// the assigned fields always store spaces, but this slice is often empty
|
||||
Ok(match assigned_field {
|
||||
AssignedField::RequiredValue(name, spaces, value) => {
|
||||
|
@ -486,12 +490,12 @@ pub fn assigned_pattern_field_to_pattern<'a>(
|
|||
|
||||
/// The '=' used in a def can't be followed by another '=' (or else it's actually
|
||||
/// an "==") and also it can't be followed by '>' (or else it's actually an "=>")
|
||||
fn equals_for_def<'a>() -> impl Parser<'a, ()> {
|
||||
fn equals_for_def<'a>() -> impl Parser<'a, (), SyntaxError> {
|
||||
|arena, state: State<'a>| match state.bytes.get(0) {
|
||||
Some(b'=') => match state.bytes.get(1) {
|
||||
Some(b'=') | Some(b'>') => Err((
|
||||
NoProgress,
|
||||
Bag::from_state(arena, &state, FailReason::ConditionFailed),
|
||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
||||
state,
|
||||
)),
|
||||
_ => {
|
||||
|
@ -502,7 +506,7 @@ fn equals_for_def<'a>() -> impl Parser<'a, ()> {
|
|||
},
|
||||
_ => Err((
|
||||
NoProgress,
|
||||
Bag::from_state(arena, &state, FailReason::ConditionFailed),
|
||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
||||
state,
|
||||
)),
|
||||
}
|
||||
|
@ -514,7 +518,7 @@ fn equals_for_def<'a>() -> impl Parser<'a, ()> {
|
|||
/// * A pattern followed by '=' and then an expression
|
||||
/// * A type annotation
|
||||
/// * A type annotation followed on the next line by a pattern, an `=`, and an expression
|
||||
pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> {
|
||||
pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>, SyntaxError> {
|
||||
let indented_more = min_indent + 1;
|
||||
|
||||
enum DefKind {
|
||||
|
@ -594,11 +598,11 @@ pub fn def<'a>(min_indent: u16) -> impl Parser<'a, Def<'a>> {
|
|||
|
||||
// PARSER HELPERS
|
||||
|
||||
fn pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
fn pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError> {
|
||||
space0_after(loc_closure_param(min_indent), min_indent)
|
||||
}
|
||||
|
||||
fn spaces_then_comment_or_newline<'a>() -> impl Parser<'a, Option<&'a str>> {
|
||||
fn spaces_then_comment_or_newline<'a>() -> impl Parser<'a, Option<&'a str>, SyntaxError> {
|
||||
skip_first!(
|
||||
zero_or_more!(ascii_char(b' ')),
|
||||
map!(
|
||||
|
@ -613,7 +617,7 @@ fn spaces_then_comment_or_newline<'a>() -> impl Parser<'a, Option<&'a str>> {
|
|||
|
||||
type Body<'a> = (Located<Pattern<'a>>, Located<Expr<'a>>);
|
||||
|
||||
fn body_at_indent<'a>(indent_level: u16) -> impl Parser<'a, Body<'a>> {
|
||||
fn body_at_indent<'a>(indent_level: u16) -> impl Parser<'a, Body<'a>, SyntaxError> {
|
||||
let indented_more = indent_level + 1;
|
||||
and!(
|
||||
skip_first!(spaces_exactly(indent_level), pattern(indent_level)),
|
||||
|
@ -716,7 +720,7 @@ fn annotation_or_alias<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_defs<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, &'a Located<Def<'a>>>> {
|
||||
fn parse_defs<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, &'a Located<Def<'a>>>, SyntaxError> {
|
||||
let parse_def = move |a, s| space1_before(loc!(def(min_indent)), min_indent).parse(a, s);
|
||||
|
||||
zero_or_more!(allocated(parse_def))
|
||||
|
@ -730,11 +734,11 @@ fn parse_def_expr<'a>(
|
|||
state: State<'a>,
|
||||
loc_first_pattern: Located<Pattern<'a>>,
|
||||
spaces_after_equals: &'a [CommentOrNewline<'a>],
|
||||
) -> ParseResult<'a, Expr<'a>> {
|
||||
) -> ParseResult<'a, Expr<'a>, SyntaxError> {
|
||||
if def_start_col < min_indent {
|
||||
Err((
|
||||
NoProgress,
|
||||
Bag::from_state(arena, &state, FailReason::OutdentedTooFar),
|
||||
Bag::from_state(arena, &state, SyntaxError::OutdentedTooFar),
|
||||
state,
|
||||
))
|
||||
// `<` because '=' should be same indent (or greater) as the entire def-expr
|
||||
|
@ -745,7 +749,7 @@ fn parse_def_expr<'a>(
|
|||
);
|
||||
Err((
|
||||
NoProgress,
|
||||
Bag::from_state(arena, &state, FailReason::NotYetImplemented(msg)),
|
||||
Bag::from_state(arena, &state, SyntaxError::NotYetImplemented(msg)),
|
||||
state,
|
||||
))
|
||||
} else {
|
||||
|
@ -821,13 +825,13 @@ fn parse_def_signature<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
loc_first_pattern: Located<Pattern<'a>>,
|
||||
) -> ParseResult<'a, Expr<'a>> {
|
||||
) -> ParseResult<'a, Expr<'a>, SyntaxError> {
|
||||
let original_indent = state.indent_col;
|
||||
|
||||
if original_indent < min_indent {
|
||||
Err((
|
||||
NoProgress,
|
||||
Bag::from_state(arena, &state, FailReason::OutdentedTooFar),
|
||||
Bag::from_state(arena, &state, SyntaxError::OutdentedTooFar),
|
||||
state,
|
||||
))
|
||||
// `<` because ':' should be same indent or greater
|
||||
|
@ -837,7 +841,7 @@ fn parse_def_signature<'a>(
|
|||
Bag::from_state(
|
||||
arena,
|
||||
&state,
|
||||
FailReason::NotYetImplemented(
|
||||
SyntaxError::NotYetImplemented(
|
||||
"TODO the : in this declaration seems outdented".to_string(),
|
||||
),
|
||||
),
|
||||
|
@ -933,11 +937,11 @@ fn parse_def_signature<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
// fn to_expr<'a>(arena, state, ((loc_first_annotation, opt_body), (mut defs, loc_ret))-> ParseResult<'a, Expr<'a>>{
|
||||
// fn to_expr<'a>(arena, state, ((loc_first_annotation, opt_body), (mut defs, loc_ret))-> ParseResult<'a, Expr<'a>, SyntaxError>{
|
||||
|
||||
// }
|
||||
|
||||
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>>, SyntaxError> {
|
||||
skip_first!(
|
||||
// If this is a reserved keyword ("if", "then", "case, "when"),
|
||||
// followed by a blank space, then it is not a function argument!
|
||||
|
@ -956,7 +960,7 @@ fn loc_parse_function_arg<'a>(
|
|||
min_indent: u16,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Expr<'a>>> {
|
||||
) -> ParseResult<'a, Located<Expr<'a>>, SyntaxError> {
|
||||
one_of!(
|
||||
loc_parenthetical_expr!(min_indent, fail() /* don't parse args within args! */),
|
||||
loc!(string_literal()),
|
||||
|
@ -972,7 +976,7 @@ fn loc_parse_function_arg<'a>(
|
|||
.parse(arena, state)
|
||||
}
|
||||
|
||||
fn reserved_keyword<'a>() -> impl Parser<'a, ()> {
|
||||
fn reserved_keyword<'a>() -> impl Parser<'a, (), SyntaxError> {
|
||||
one_of!(
|
||||
ascii_string(keyword::IF),
|
||||
ascii_string(keyword::THEN),
|
||||
|
@ -983,7 +987,7 @@ fn reserved_keyword<'a>() -> impl Parser<'a, ()> {
|
|||
)
|
||||
}
|
||||
|
||||
fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
map_with_arena!(
|
||||
skip_first!(
|
||||
// All closures start with a '\' - e.g. (\x -> x + 1)
|
||||
|
@ -1026,7 +1030,7 @@ fn closure<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
fn loc_closure_param<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
fn loc_closure_param<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError> {
|
||||
move |arena, state| parse_closure_param(arena, state, min_indent)
|
||||
}
|
||||
|
||||
|
@ -1034,7 +1038,7 @@ fn parse_closure_param<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
min_indent: u16,
|
||||
) -> ParseResult<'a, Located<Pattern<'a>>> {
|
||||
) -> ParseResult<'a, Located<Pattern<'a>>, SyntaxError> {
|
||||
one_of!(
|
||||
// An ident is the most common param, e.g. \foo -> ...
|
||||
loc_ident_pattern(min_indent, true),
|
||||
|
@ -1053,7 +1057,7 @@ fn parse_closure_param<'a>(
|
|||
.parse(arena, state)
|
||||
}
|
||||
|
||||
fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError> {
|
||||
skip_first!(
|
||||
// If this is a reserved keyword ("if", "then", "case, "when"), then
|
||||
// it is not a pattern!
|
||||
|
@ -1069,11 +1073,13 @@ fn loc_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
|||
)
|
||||
}
|
||||
|
||||
fn loc_tag_pattern_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Pattern<'a>>>> {
|
||||
fn loc_tag_pattern_args<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Vec<'a, Located<Pattern<'a>>>, SyntaxError> {
|
||||
zero_or_more!(space1_before(loc_tag_pattern_arg(min_indent), min_indent))
|
||||
}
|
||||
|
||||
fn loc_tag_pattern_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
fn loc_tag_pattern_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError> {
|
||||
skip_first!(
|
||||
// If this is a reserved keyword ("if", "then", "case, "when"), then
|
||||
// it is not a function argument!
|
||||
|
@ -1088,7 +1094,7 @@ fn loc_parse_tag_pattern_arg<'a>(
|
|||
min_indent: u16,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Located<Pattern<'a>>> {
|
||||
) -> ParseResult<'a, Located<Pattern<'a>>, SyntaxError> {
|
||||
one_of!(
|
||||
loc_parenthetical_pattern(min_indent),
|
||||
loc!(underscore_pattern()),
|
||||
|
@ -1101,7 +1107,9 @@ fn loc_parse_tag_pattern_arg<'a>(
|
|||
.parse(arena, state)
|
||||
}
|
||||
|
||||
fn loc_parenthetical_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
fn loc_parenthetical_pattern<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError> {
|
||||
between!(
|
||||
ascii_char(b'('),
|
||||
space0_around(
|
||||
|
@ -1112,17 +1120,17 @@ fn loc_parenthetical_pattern<'a>(min_indent: u16) -> impl Parser<'a, Located<Pat
|
|||
)
|
||||
}
|
||||
|
||||
fn number_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
||||
fn number_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError> {
|
||||
map_with_arena!(number_literal(), |arena, expr| {
|
||||
expr_to_pattern(arena, &expr).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
fn string_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
||||
fn string_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError> {
|
||||
map!(crate::string_literal::parse(), Pattern::StrLiteral)
|
||||
}
|
||||
|
||||
fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
||||
fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>, SyntaxError> {
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
let (_, _, next_state) = ascii_char(b'_').parse(arena, state)?;
|
||||
|
||||
|
@ -1135,13 +1143,17 @@ fn underscore_pattern<'a>() -> impl Parser<'a, Pattern<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
||||
fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>, SyntaxError> {
|
||||
then(
|
||||
collection!(
|
||||
ascii_char(b'{'),
|
||||
move |arena: &'a bumpalo::Bump,
|
||||
state: crate::parser::State<'a>|
|
||||
-> crate::parser::ParseResult<'a, Located<crate::ast::Pattern<'a>>> {
|
||||
-> crate::parser::ParseResult<
|
||||
'a,
|
||||
Located<crate::ast::Pattern<'a>>,
|
||||
SyntaxError,
|
||||
> {
|
||||
use crate::blankspace::{space0, space0_before};
|
||||
use crate::ident::lowercase_ident;
|
||||
use crate::parser::Either::*;
|
||||
|
@ -1218,7 +1230,7 @@ fn record_destructure<'a>(min_indent: u16) -> impl Parser<'a, Pattern<'a>> {
|
|||
fn loc_ident_pattern<'a>(
|
||||
min_indent: u16,
|
||||
can_have_arguments: bool,
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>> {
|
||||
) -> impl Parser<'a, Located<Pattern<'a>>, SyntaxError> {
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
let (_, loc_ident, state) = loc!(ident()).parse(arena, state)?;
|
||||
|
||||
|
@ -1318,7 +1330,7 @@ fn loc_ident_pattern<'a>(
|
|||
Ident::Malformed(malformed) => {
|
||||
debug_assert!(!malformed.is_empty());
|
||||
|
||||
let bag = Bag::from_state(arena, &state, FailReason::InvalidPattern);
|
||||
let bag = Bag::from_state(arena, &state, SyntaxError::InvalidPattern);
|
||||
|
||||
Err((MadeProgress, bag, state))
|
||||
}
|
||||
|
@ -1331,7 +1343,7 @@ mod when {
|
|||
use crate::ast::WhenBranch;
|
||||
|
||||
/// Parser for when expressions.
|
||||
pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
pub fn expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
then(
|
||||
and!(
|
||||
when_with_indent(min_indent),
|
||||
|
@ -1353,7 +1365,7 @@ mod when {
|
|||
Bag::from_state(
|
||||
arena,
|
||||
&state,
|
||||
FailReason::NotYetImplemented(
|
||||
SyntaxError::NotYetImplemented(
|
||||
"TODO case wasn't indented enough".to_string(),
|
||||
),
|
||||
),
|
||||
|
@ -1377,7 +1389,7 @@ mod when {
|
|||
}
|
||||
|
||||
/// Parsing when with indentation.
|
||||
fn when_with_indent<'a>(min_indent: u16) -> impl Parser<'a, u16> {
|
||||
fn when_with_indent<'a>(min_indent: u16) -> impl Parser<'a, u16, SyntaxError> {
|
||||
move |arena, state: State<'a>| {
|
||||
parser::keyword(keyword::WHEN, min_indent)
|
||||
.parse(arena, state)
|
||||
|
@ -1386,7 +1398,7 @@ mod when {
|
|||
}
|
||||
|
||||
/// Parsing branches of when conditional.
|
||||
fn branches<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>> {
|
||||
fn branches<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, &'a WhenBranch<'a>>, SyntaxError> {
|
||||
move |arena, state| {
|
||||
let mut branches: Vec<'a, &'a WhenBranch<'a>> = Vec::with_capacity_in(2, arena);
|
||||
|
||||
|
@ -1421,7 +1433,7 @@ mod when {
|
|||
Err((
|
||||
MadeProgress,
|
||||
Bag::from_state( arena, &state,
|
||||
FailReason::NotYetImplemented(
|
||||
SyntaxError::NotYetImplemented(
|
||||
"TODO additional branch didn't have same indentation as first branch".to_string(),
|
||||
),
|
||||
),
|
||||
|
@ -1464,7 +1476,8 @@ mod when {
|
|||
/// Parsing alternative patterns in when branches.
|
||||
fn branch_alternatives<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, (Vec<'a, Located<Pattern<'a>>>, Option<Located<Expr<'a>>>)> {
|
||||
) -> impl Parser<'a, (Vec<'a, Located<Pattern<'a>>>, Option<Located<Expr<'a>>>), SyntaxError>
|
||||
{
|
||||
and!(
|
||||
sep_by1(
|
||||
ascii_char(b'|'),
|
||||
|
@ -1495,7 +1508,7 @@ mod when {
|
|||
}
|
||||
|
||||
/// Parsing the righthandside of a branch in a when conditional.
|
||||
fn branch_result<'a>(indent: u16) -> impl Parser<'a, Located<Expr<'a>>> {
|
||||
fn branch_result<'a>(indent: u16) -> impl Parser<'a, Located<Expr<'a>>, SyntaxError> {
|
||||
skip_first!(
|
||||
ascii_string("->"),
|
||||
space0_before(
|
||||
|
@ -1506,7 +1519,7 @@ mod when {
|
|||
}
|
||||
}
|
||||
|
||||
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>, SyntaxError> {
|
||||
map_with_arena!(
|
||||
and!(
|
||||
skip_first!(
|
||||
|
@ -1558,7 +1571,9 @@ pub fn if_expr<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
/// any time we encounter a '-' it is unary iff it is both preceded by spaces
|
||||
/// and is *not* followed by a whitespace character.
|
||||
#[inline(always)]
|
||||
fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Expr<'a>>> {
|
||||
fn unary_negate_function_arg<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<Expr<'a>>, SyntaxError> {
|
||||
then(
|
||||
// Spaces, then '-', then *not* more spaces.
|
||||
not_followed_by(
|
||||
|
@ -1621,7 +1636,9 @@ fn unary_negate_function_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<Exp
|
|||
)
|
||||
}
|
||||
|
||||
fn loc_function_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Expr<'a>>>> {
|
||||
fn loc_function_args<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Vec<'a, Located<Expr<'a>>>, SyntaxError> {
|
||||
one_or_more!(move |arena: &'a Bump, s| {
|
||||
map!(
|
||||
and!(
|
||||
|
@ -1652,7 +1669,7 @@ fn loc_function_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<Exp
|
|||
/// 3. The beginning of a definition (e.g. `foo =`)
|
||||
/// 4. The beginning of a type annotation (e.g. `foo :`)
|
||||
/// 5. A reserved keyword (e.g. `if ` or `case `), meaning we should do something else.
|
||||
fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
then(
|
||||
and!(
|
||||
loc!(ident()),
|
||||
|
@ -1678,7 +1695,7 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
// We got args with an '=' after them, e.g. `foo a b = ...` This is a syntax error!
|
||||
let region = Region::across_all(loc_args.iter().map(|v| &v.region));
|
||||
let fail =
|
||||
Bag::from_state(arena, &state, FailReason::ArgumentsBeforeEquals(region));
|
||||
Bag::from_state(arena, &state, SyntaxError::ArgumentsBeforeEquals(region));
|
||||
Err((MadeProgress, fail, state))
|
||||
}
|
||||
(None, Some((spaces_before_equals, Either::First(equals_indent)))) => {
|
||||
|
@ -1756,7 +1773,7 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
Bag::from_state(
|
||||
arena,
|
||||
&state,
|
||||
FailReason::NotYetImplemented(format!(
|
||||
SyntaxError::NotYetImplemented(format!(
|
||||
"TODO early return malformed pattern {:?}",
|
||||
malformed
|
||||
)),
|
||||
|
@ -1801,14 +1818,14 @@ fn ident_etc<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn ident_without_apply<'a>() -> impl Parser<'a, Expr<'a>> {
|
||||
pub fn ident_without_apply<'a>() -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
then(loc!(ident()), move |arena, state, progress, loc_ident| {
|
||||
Ok((progress, ident_to_expr(arena, loc_ident.value), state))
|
||||
})
|
||||
}
|
||||
|
||||
/// Like equals_for_def(), except it produces the indent_col of the state rather than ()
|
||||
pub fn equals_with_indent<'a>() -> impl Parser<'a, u16> {
|
||||
pub fn equals_with_indent<'a>() -> impl Parser<'a, u16, SyntaxError> {
|
||||
move |arena, state: State<'a>| {
|
||||
match state.bytes.first() {
|
||||
Some(b'=') => {
|
||||
|
@ -1834,7 +1851,7 @@ pub fn equals_with_indent<'a>() -> impl Parser<'a, u16> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn colon_with_indent<'a>() -> impl Parser<'a, u16> {
|
||||
pub fn colon_with_indent<'a>() -> impl Parser<'a, u16, SyntaxError> {
|
||||
move |arena, state: State<'a>| match state.bytes.first() {
|
||||
Some(&byte) if byte == b':' => Ok((
|
||||
MadeProgress,
|
||||
|
@ -1878,7 +1895,7 @@ pub fn ident_to_expr<'a>(arena: &'a Bump, src: Ident<'a>) -> Expr<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn binop<'a>() -> impl Parser<'a, BinOp> {
|
||||
fn binop<'a>() -> impl Parser<'a, BinOp, SyntaxError> {
|
||||
one_of!(
|
||||
// Sorted from highest to lowest predicted usage in practice,
|
||||
// so that successful matches short-circuit as early as possible.
|
||||
|
@ -1906,7 +1923,7 @@ fn binop<'a>() -> impl Parser<'a, BinOp> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
let elems = collection_trailing_sep!(
|
||||
ascii_char(b'['),
|
||||
loc!(expr(min_indent)),
|
||||
|
@ -1937,7 +1954,7 @@ pub fn list_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
}
|
||||
|
||||
// Parser<'a, Vec<'a, Located<AssignedField<'a, S>>>>
|
||||
fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
||||
fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
then(
|
||||
and!(
|
||||
attempt!(
|
||||
|
@ -2051,7 +2068,7 @@ fn record_literal<'a>(min_indent: u16) -> impl Parser<'a, Expr<'a>> {
|
|||
}
|
||||
|
||||
/// This is mainly for matching tags in closure params, e.g. \@Foo -> ...
|
||||
pub fn private_tag<'a>() -> impl Parser<'a, &'a str> {
|
||||
pub fn private_tag<'a>() -> impl Parser<'a, &'a str, SyntaxError> {
|
||||
map_with_arena!(
|
||||
skip_first!(ascii_char(b'@'), global_tag()),
|
||||
|arena: &'a Bump, name: &'a str| {
|
||||
|
@ -2065,10 +2082,10 @@ pub fn private_tag<'a>() -> impl Parser<'a, &'a str> {
|
|||
}
|
||||
|
||||
/// This is mainly for matching tags in closure params, e.g. \Foo -> ...
|
||||
pub fn global_tag<'a>() -> impl Parser<'a, &'a str> {
|
||||
pub fn global_tag<'a>() -> impl Parser<'a, &'a str, SyntaxError> {
|
||||
global_tag_or_ident(|first_char| first_char.is_uppercase())
|
||||
}
|
||||
|
||||
pub fn string_literal<'a>() -> impl Parser<'a, Expr<'a>> {
|
||||
pub fn string_literal<'a>() -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
map!(crate::string_literal::parse(), Expr::Str)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::ast::{CommentOrNewline, Spaceable, StrLiteral, TypeAnnotation};
|
|||
use crate::blankspace::space0;
|
||||
use crate::ident::lowercase_ident;
|
||||
use crate::module::package_name;
|
||||
use crate::parser::{ascii_char, optional, Either, Parser, Progress::*, State};
|
||||
use crate::parser::{ascii_char, optional, Either, Parser, Progress::*, State, SyntaxError};
|
||||
use crate::string_literal;
|
||||
use bumpalo::collections::Vec;
|
||||
use inlinable_string::InlinableString;
|
||||
|
@ -239,7 +239,7 @@ impl<'a> Spaceable<'a> for PackageEntry<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>> {
|
||||
pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>, SyntaxError> {
|
||||
move |arena, state| {
|
||||
// You may optionally have a package shorthand,
|
||||
// e.g. "uc" in `uc: roc/unicode 1.0.0`
|
||||
|
@ -269,7 +269,7 @@ pub fn package_entry<'a>() -> impl Parser<'a, PackageEntry<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>> {
|
||||
pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>, SyntaxError> {
|
||||
map!(
|
||||
either!(
|
||||
string_literal::parse(),
|
||||
|
@ -287,6 +287,6 @@ pub fn package_or_path<'a>() -> impl Parser<'a, PackageOrPath<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
fn package_version<'a>() -> impl Parser<'a, Version<'a>> {
|
||||
fn package_version<'a>() -> impl Parser<'a, Version<'a>, SyntaxError> {
|
||||
move |_, _| todo!("TODO parse package version")
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::ast::Attempting;
|
||||
use crate::keyword;
|
||||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{peek_utf8_char, unexpected, Bag, FailReason, ParseResult, Parser, State};
|
||||
use crate::parser::{peek_utf8_char, unexpected, Bag, ParseResult, Parser, State, SyntaxError};
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
|
@ -71,7 +71,7 @@ impl<'a> Ident<'a> {
|
|||
pub fn parse_ident<'a>(
|
||||
arena: &'a Bump,
|
||||
mut state: State<'a>,
|
||||
) -> ParseResult<'a, (Ident<'a>, Option<char>)> {
|
||||
) -> ParseResult<'a, (Ident<'a>, Option<char>), SyntaxError> {
|
||||
let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.)
|
||||
let mut capitalized_parts: Vec<&'a str> = Vec::new_in(arena);
|
||||
let mut noncapitalized_parts: Vec<&'a str> = Vec::new_in(arena);
|
||||
|
@ -279,7 +279,7 @@ fn malformed<'a>(
|
|||
mut state: State<'a>,
|
||||
capitalized_parts: Vec<&'a str>,
|
||||
noncapitalized_parts: Vec<&'a str>,
|
||||
) -> ParseResult<'a, (Ident<'a>, Option<char>)> {
|
||||
) -> ParseResult<'a, (Ident<'a>, Option<char>), SyntaxError> {
|
||||
// Reconstruct the original string that we've been parsing.
|
||||
let mut full_string = String::new_in(arena);
|
||||
|
||||
|
@ -321,7 +321,7 @@ fn malformed<'a>(
|
|||
))
|
||||
}
|
||||
|
||||
pub fn ident<'a>() -> impl Parser<'a, Ident<'a>> {
|
||||
pub fn ident<'a>() -> impl Parser<'a, Ident<'a>, SyntaxError> {
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
// Discard next_char; we don't need it.
|
||||
let (progress, (string, _), state) = parse_ident(arena, state)?;
|
||||
|
@ -330,7 +330,7 @@ pub fn ident<'a>() -> impl Parser<'a, Ident<'a>> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn global_tag_or_ident<'a, F>(pred: F) -> impl Parser<'a, &'a str>
|
||||
pub fn global_tag_or_ident<'a, F>(pred: F) -> impl Parser<'a, &'a str, SyntaxError>
|
||||
where
|
||||
F: Fn(char) -> bool,
|
||||
{
|
||||
|
@ -382,7 +382,7 @@ where
|
|||
///
|
||||
/// * A record field, e.g. "email" in `.email` or in `email:`
|
||||
/// * A named pattern match, e.g. "foo" in `foo =` or `foo ->` or `\foo ->`
|
||||
pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
||||
pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str, SyntaxError> {
|
||||
move |arena, state| {
|
||||
let (progress, ident, state) =
|
||||
global_tag_or_ident(|first_char| first_char.is_lowercase()).parse(arena, state)?;
|
||||
|
@ -401,7 +401,7 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
|||
let region = Region::zero();
|
||||
Err((
|
||||
MadeProgress,
|
||||
Bag::from_state(arena, &state, FailReason::ReservedKeyword(region)),
|
||||
Bag::from_state(arena, &state, SyntaxError::ReservedKeyword(region)),
|
||||
state,
|
||||
))
|
||||
} else {
|
||||
|
@ -415,11 +415,11 @@ pub fn lowercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
|||
/// * A module name
|
||||
/// * A type name
|
||||
/// * A global tag
|
||||
pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str> {
|
||||
pub fn uppercase_ident<'a>() -> impl Parser<'a, &'a str, SyntaxError> {
|
||||
global_tag_or_ident(|first_char| first_char.is_uppercase())
|
||||
}
|
||||
|
||||
pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str> {
|
||||
pub fn unqualified_ident<'a>() -> impl Parser<'a, &'a str, SyntaxError> {
|
||||
global_tag_or_ident(|first_char| first_char.is_alphabetic())
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::ident::{lowercase_ident, unqualified_ident, uppercase_ident};
|
|||
use crate::parser::Progress::{self, *};
|
||||
use crate::parser::{
|
||||
self, ascii_char, ascii_string, backtrackable, end_of_file, loc, optional, peek_utf8_char,
|
||||
peek_utf8_char_at, unexpected, unexpected_eof, Either, ParseResult, Parser, State,
|
||||
peek_utf8_char_at, unexpected, unexpected_eof, Either, ParseResult, Parser, State, SyntaxError,
|
||||
};
|
||||
use crate::string_literal;
|
||||
use crate::type_annotation;
|
||||
|
@ -18,29 +18,29 @@ use bumpalo::collections::{String, Vec};
|
|||
use bumpalo::Bump;
|
||||
use roc_region::all::Located;
|
||||
|
||||
pub fn header<'a>() -> impl Parser<'a, Module<'a>> {
|
||||
pub fn header<'a>() -> impl Parser<'a, Module<'a>, SyntaxError> {
|
||||
one_of!(interface_module(), app_module(), platform_module())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn app_module<'a>() -> impl Parser<'a, Module<'a>> {
|
||||
fn app_module<'a>() -> impl Parser<'a, Module<'a>, SyntaxError> {
|
||||
map!(app_header(), |header| { Module::App { header } })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn platform_module<'a>() -> impl Parser<'a, Module<'a>> {
|
||||
fn platform_module<'a>() -> impl Parser<'a, Module<'a>, SyntaxError> {
|
||||
map!(platform_header(), |header| { Module::Platform { header } })
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn interface_module<'a>() -> impl Parser<'a, Module<'a>> {
|
||||
fn interface_module<'a>() -> impl Parser<'a, Module<'a>, SyntaxError> {
|
||||
map!(interface_header(), |header| {
|
||||
Module::Interface { header }
|
||||
})
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
|
||||
pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>, SyntaxError> {
|
||||
parser::map(
|
||||
and!(
|
||||
skip_first!(
|
||||
|
@ -71,7 +71,7 @@ pub fn interface_header<'a>() -> impl Parser<'a, InterfaceHeader<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>> {
|
||||
pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>, SyntaxError> {
|
||||
// e.g. rtfeldman/blah
|
||||
//
|
||||
// Package names and accounts can be capitalized and can contain dashes.
|
||||
|
@ -87,7 +87,10 @@ pub fn package_name<'a>() -> impl Parser<'a, PackageName<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn parse_package_part<'a>(arena: &'a Bump, mut state: State<'a>) -> ParseResult<'a, &'a str> {
|
||||
pub fn parse_package_part<'a>(
|
||||
arena: &'a Bump,
|
||||
mut state: State<'a>,
|
||||
) -> ParseResult<'a, &'a str, SyntaxError> {
|
||||
let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.)
|
||||
|
||||
while !state.bytes.is_empty() {
|
||||
|
@ -113,7 +116,7 @@ pub fn parse_package_part<'a>(arena: &'a Bump, mut state: State<'a>) -> ParseRes
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>> {
|
||||
pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError> {
|
||||
move |arena, mut state: State<'a>| {
|
||||
match peek_utf8_char(&state) {
|
||||
Ok((first_letter, bytes_parsed)) => {
|
||||
|
@ -182,7 +185,7 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>> {
|
||||
pub fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>, SyntaxError> {
|
||||
map_with_arena!(
|
||||
and!(
|
||||
skip_first!(
|
||||
|
@ -240,7 +243,7 @@ pub fn app_header<'a>() -> impl Parser<'a, AppHeader<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
|
||||
pub fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>, SyntaxError> {
|
||||
parser::map(
|
||||
and!(
|
||||
skip_first!(
|
||||
|
@ -296,7 +299,7 @@ pub fn platform_header<'a>() -> impl Parser<'a, PlatformHeader<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>> {
|
||||
pub fn module_defs<'a>() -> impl Parser<'a, Vec<'a, Located<Def<'a>>>, SyntaxError> {
|
||||
// force that we pare until the end of the input
|
||||
skip_second!(zero_or_more!(space0_around(loc(def(0)), 0)), end_of_file())
|
||||
}
|
||||
|
@ -312,7 +315,7 @@ struct ProvidesTo<'a> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>> {
|
||||
fn provides_to<'a>() -> impl Parser<'a, ProvidesTo<'a>, SyntaxError> {
|
||||
map!(
|
||||
and!(
|
||||
and!(
|
||||
|
@ -372,6 +375,7 @@ fn provides_without_to<'a>() -> impl Parser<
|
|||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
),
|
||||
SyntaxError,
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("provides")), space1(1)),
|
||||
|
@ -392,6 +396,7 @@ fn requires<'a>() -> impl Parser<
|
|||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<TypedIdent<'a>>>,
|
||||
),
|
||||
SyntaxError,
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("requires")), space1(1)),
|
||||
|
@ -412,6 +417,7 @@ fn exposes_values<'a>() -> impl Parser<
|
|||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a, &'a str>>>,
|
||||
),
|
||||
SyntaxError,
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)),
|
||||
|
@ -432,6 +438,7 @@ fn exposes_modules<'a>() -> impl Parser<
|
|||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ExposesEntry<'a, ModuleName<'a>>>>,
|
||||
),
|
||||
SyntaxError,
|
||||
> {
|
||||
and!(
|
||||
and!(skip_second!(space1(1), ascii_string("exposes")), space1(1)),
|
||||
|
@ -454,7 +461,7 @@ struct Packages<'a> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn packages<'a>() -> impl Parser<'a, Packages<'a>> {
|
||||
fn packages<'a>() -> impl Parser<'a, Packages<'a>, SyntaxError> {
|
||||
map!(
|
||||
and!(
|
||||
and!(
|
||||
|
@ -486,6 +493,7 @@ fn imports<'a>() -> impl Parser<
|
|||
(&'a [CommentOrNewline<'a>], &'a [CommentOrNewline<'a>]),
|
||||
Vec<'a, Located<ImportsEntry<'a>>>,
|
||||
),
|
||||
SyntaxError,
|
||||
> {
|
||||
and!(
|
||||
and!(
|
||||
|
@ -503,7 +511,7 @@ fn imports<'a>() -> impl Parser<
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn effects<'a>() -> impl Parser<'a, Effects<'a>> {
|
||||
fn effects<'a>() -> impl Parser<'a, Effects<'a>, SyntaxError> {
|
||||
move |arena, state| {
|
||||
let (_, spaces_before_effects_keyword, state) =
|
||||
skip_second!(space1(0), ascii_string("effects")).parse(arena, state)?;
|
||||
|
@ -540,7 +548,7 @@ fn effects<'a>() -> impl Parser<'a, Effects<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>> {
|
||||
fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>, SyntaxError> {
|
||||
move |arena, state| {
|
||||
// You must have a field name, e.g. "email"
|
||||
let (_, ident, state) = loc!(lowercase_ident()).parse(arena, state)?;
|
||||
|
@ -571,7 +579,7 @@ fn typed_ident<'a>() -> impl Parser<'a, TypedIdent<'a>> {
|
|||
|
||||
#[inline(always)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>> {
|
||||
fn imports_entry<'a>() -> impl Parser<'a, ImportsEntry<'a>, SyntaxError> {
|
||||
map_with_arena!(
|
||||
and!(
|
||||
and!(
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
use crate::ast::{Attempting, Base, Expr};
|
||||
use crate::parser::{parse_utf8, unexpected, unexpected_eof, ParseResult, Parser, Progress, State};
|
||||
use crate::parser::{
|
||||
parse_utf8, unexpected, unexpected_eof, ParseResult, Parser, Progress, State, SyntaxError,
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use std::char;
|
||||
use std::str::from_utf8_unchecked;
|
||||
|
||||
pub fn number_literal<'a>() -> impl Parser<'a, Expr<'a>> {
|
||||
pub fn number_literal<'a>() -> impl Parser<'a, Expr<'a>, SyntaxError> {
|
||||
move |arena, state: State<'a>| {
|
||||
let bytes = &mut state.bytes.iter();
|
||||
|
||||
|
@ -28,7 +30,7 @@ fn parse_number_literal<'a, I>(
|
|||
bytes: &mut I,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>>
|
||||
) -> ParseResult<'a, Expr<'a>, SyntaxError>
|
||||
where
|
||||
I: Iterator<Item = &'a u8>,
|
||||
{
|
||||
|
@ -164,7 +166,7 @@ fn from_base<'a>(
|
|||
bytes_parsed: usize,
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>> {
|
||||
) -> ParseResult<'a, Expr<'a>, SyntaxError> {
|
||||
let is_negative = first_ch == '-';
|
||||
let bytes = if is_negative {
|
||||
&state.bytes[3..bytes_parsed]
|
||||
|
|
|
@ -55,10 +55,14 @@ impl<'a> State<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_indent(self, arena: &'a Bump, min_indent: u16) -> Result<Self, (Bag<'a>, Self)> {
|
||||
pub fn check_indent(
|
||||
self,
|
||||
arena: &'a Bump,
|
||||
min_indent: u16,
|
||||
) -> Result<Self, (Bag<'a, SyntaxError>, Self)> {
|
||||
if self.indent_col < min_indent {
|
||||
Err((
|
||||
Bag::from_state(arena, &self, FailReason::OutdentedTooFar),
|
||||
Bag::from_state(arena, &self, SyntaxError::OutdentedTooFar),
|
||||
self,
|
||||
))
|
||||
} else {
|
||||
|
@ -80,7 +84,7 @@ impl<'a> State<'a> {
|
|||
|
||||
/// Increments the line, then resets column, indent_col, and is_indenting.
|
||||
/// Advances the input by 1, to consume the newline character.
|
||||
pub fn newline(&self, arena: &'a Bump) -> Result<Self, (Progress, Bag<'a>, Self)> {
|
||||
pub fn newline(&self, arena: &'a Bump) -> Result<Self, (Progress, Bag<'a, SyntaxError>, Self)> {
|
||||
match self.line.checked_add(1) {
|
||||
Some(line) => Ok(State {
|
||||
bytes: &self.bytes[1..],
|
||||
|
@ -93,7 +97,7 @@ impl<'a> State<'a> {
|
|||
}),
|
||||
None => Err((
|
||||
Progress::NoProgress,
|
||||
Bag::from_state(arena, &self, FailReason::TooManyLines),
|
||||
Bag::from_state(arena, &self, SyntaxError::TooManyLines),
|
||||
self.clone(),
|
||||
)),
|
||||
}
|
||||
|
@ -107,7 +111,7 @@ impl<'a> State<'a> {
|
|||
self,
|
||||
arena: &'a Bump,
|
||||
quantity: usize,
|
||||
) -> Result<Self, (Progress, Bag<'a>, Self)> {
|
||||
) -> Result<Self, (Progress, Bag<'a, SyntaxError>, Self)> {
|
||||
match (self.column as usize).checked_add(quantity) {
|
||||
Some(column_usize) if column_usize <= u16::MAX as usize => {
|
||||
Ok(State {
|
||||
|
@ -127,7 +131,7 @@ impl<'a> State<'a> {
|
|||
&self,
|
||||
arena: &'a Bump,
|
||||
spaces: usize,
|
||||
) -> Result<Self, (Progress, Bag<'a>, Self)> {
|
||||
) -> Result<Self, (Progress, Bag<'a, SyntaxError>, Self)> {
|
||||
match (self.column as usize).checked_add(spaces) {
|
||||
Some(column_usize) if column_usize <= u16::MAX as usize => {
|
||||
// Spaces don't affect is_indenting; if we were previously indneting,
|
||||
|
@ -184,8 +188,8 @@ impl<'a> State<'a> {
|
|||
self,
|
||||
arena: &'a Bump,
|
||||
progress: Progress,
|
||||
reason: FailReason,
|
||||
) -> Result<(Progress, T, Self), (Progress, Bag<'a>, Self)> {
|
||||
reason: SyntaxError,
|
||||
) -> Result<(Progress, T, Self), (Progress, Bag<'a, SyntaxError>, Self)> {
|
||||
Err((progress, Bag::from_state(arena, &self, reason), self))
|
||||
}
|
||||
}
|
||||
|
@ -217,8 +221,8 @@ fn state_size() {
|
|||
assert!(state_size <= maximum, "{:?} <= {:?}", state_size, maximum);
|
||||
}
|
||||
|
||||
pub type ParseResult<'a, Output> =
|
||||
Result<(Progress, Output, State<'a>), (Progress, Bag<'a>, State<'a>)>;
|
||||
pub type ParseResult<'a, Output, Error> =
|
||||
Result<(Progress, Output, State<'a>), (Progress, Bag<'a, Error>, State<'a>)>;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Progress {
|
||||
|
@ -252,7 +256,7 @@ impl Progress {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum FailReason {
|
||||
pub enum SyntaxError {
|
||||
Unexpected(Region),
|
||||
OutdentedTooFar,
|
||||
ConditionFailed,
|
||||
|
@ -305,22 +309,22 @@ pub struct ContextItem {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DeadEnd<'a> {
|
||||
pub struct DeadEnd<'a, T> {
|
||||
pub line: u32,
|
||||
pub column: u16,
|
||||
pub problem: FailReason,
|
||||
pub problem: T,
|
||||
pub context_stack: ContextStack<'a>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Bag<'a>(Vec<'a, DeadEnd<'a>>);
|
||||
pub struct Bag<'a, T>(Vec<'a, DeadEnd<'a, T>>);
|
||||
|
||||
impl<'a> Bag<'a> {
|
||||
impl<'a, T> Bag<'a, T> {
|
||||
pub fn new_in(arena: &'a Bump) -> Self {
|
||||
Bag(Vec::new_in(arena))
|
||||
}
|
||||
|
||||
pub fn from_state(arena: &'a Bump, state: &State<'a>, x: FailReason) -> Self {
|
||||
pub fn from_state(arena: &'a Bump, state: &State<'a>, x: T) -> Self {
|
||||
let mut dead_ends = Vec::with_capacity_in(1, arena);
|
||||
|
||||
let dead_end = DeadEnd {
|
||||
|
@ -334,7 +338,7 @@ impl<'a> Bag<'a> {
|
|||
Bag(dead_ends)
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<DeadEnd<'a>> {
|
||||
fn pop(&mut self) -> Option<DeadEnd<'a, T>> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
|
@ -342,7 +346,7 @@ impl<'a> Bag<'a> {
|
|||
mut self,
|
||||
filename: std::path::PathBuf,
|
||||
bytes: &[u8],
|
||||
) -> ParseProblem<'_> {
|
||||
) -> ParseProblem<'_, T> {
|
||||
match self.pop() {
|
||||
None => unreachable!("there is a parse error, but no problem"),
|
||||
Some(dead_end) => {
|
||||
|
@ -365,41 +369,43 @@ impl<'a> Bag<'a> {
|
|||
/// since this is only used when there is in fact an error
|
||||
/// I think this is fine
|
||||
#[derive(Debug)]
|
||||
pub struct ParseProblem<'a> {
|
||||
pub struct ParseProblem<'a, T> {
|
||||
pub line: u32,
|
||||
pub column: u16,
|
||||
pub problem: FailReason,
|
||||
pub problem: T,
|
||||
pub context_stack: std::vec::Vec<ContextItem>,
|
||||
pub filename: std::path::PathBuf,
|
||||
pub bytes: &'a [u8],
|
||||
}
|
||||
|
||||
pub fn fail<'a, T>() -> impl Parser<'a, T> {
|
||||
pub fn fail<'a, T>() -> impl Parser<'a, T, SyntaxError> {
|
||||
move |arena, state: State<'a>| {
|
||||
Err((
|
||||
NoProgress,
|
||||
Bag::from_state(arena, &state, FailReason::ConditionFailed),
|
||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Parser<'a, Output> {
|
||||
fn parse(&self, _: &'a Bump, _: State<'a>) -> ParseResult<'a, Output>;
|
||||
pub trait Parser<'a, Output, Error> {
|
||||
fn parse(&self, _: &'a Bump, _: State<'a>) -> ParseResult<'a, Output, Error>;
|
||||
}
|
||||
|
||||
impl<'a, F, Output> Parser<'a, Output> for F
|
||||
impl<'a, F, Output, Error> Parser<'a, Output, Error> for F
|
||||
where
|
||||
F: Fn(&'a Bump, State<'a>) -> ParseResult<'a, Output>,
|
||||
Error: 'a,
|
||||
F: Fn(&'a Bump, State<'a>) -> ParseResult<'a, Output, Error>,
|
||||
{
|
||||
fn parse(&self, arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Output> {
|
||||
fn parse(&self, arena: &'a Bump, state: State<'a>) -> ParseResult<'a, Output, Error> {
|
||||
self(arena, state)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocated<'a, P, Val>(parser: P) -> impl Parser<'a, &'a Val>
|
||||
pub fn allocated<'a, P, Val, Error>(parser: P) -> impl Parser<'a, &'a Val, Error>
|
||||
where
|
||||
P: Parser<'a, Val>,
|
||||
Error: 'a,
|
||||
P: Parser<'a, Val, Error>,
|
||||
Val: 'a,
|
||||
{
|
||||
move |arena, state: State<'a>| {
|
||||
|
@ -409,10 +415,13 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn not_followed_by<'a, P, ByParser, By, Val>(parser: P, by: ByParser) -> impl Parser<'a, Val>
|
||||
pub fn not_followed_by<'a, P, ByParser, By, Val>(
|
||||
parser: P,
|
||||
by: ByParser,
|
||||
) -> impl Parser<'a, Val, SyntaxError>
|
||||
where
|
||||
ByParser: Parser<'a, By>,
|
||||
P: Parser<'a, Val>,
|
||||
ByParser: Parser<'a, By, SyntaxError>,
|
||||
P: Parser<'a, Val, SyntaxError>,
|
||||
{
|
||||
move |arena, state: State<'a>| {
|
||||
let original_state = state.clone();
|
||||
|
@ -425,7 +434,7 @@ where
|
|||
match by.parse(arena, state) {
|
||||
Ok((_, _, state)) => Err((
|
||||
NoProgress,
|
||||
Bag::from_state(arena, &state, FailReason::ConditionFailed),
|
||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
||||
original_state,
|
||||
)),
|
||||
Err(_) => Ok((progress, answer, after_parse)),
|
||||
|
@ -434,9 +443,9 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn not<'a, P, Val>(parser: P) -> impl Parser<'a, ()>
|
||||
pub fn not<'a, P, Val>(parser: P) -> impl Parser<'a, (), SyntaxError>
|
||||
where
|
||||
P: Parser<'a, Val>,
|
||||
P: Parser<'a, Val, SyntaxError>,
|
||||
{
|
||||
move |arena, state: State<'a>| {
|
||||
let original_state = state.clone();
|
||||
|
@ -444,7 +453,7 @@ where
|
|||
match parser.parse(arena, state) {
|
||||
Ok((_, _, _)) => Err((
|
||||
NoProgress,
|
||||
Bag::from_state(arena, &original_state, FailReason::ConditionFailed),
|
||||
Bag::from_state(arena, &original_state, SyntaxError::ConditionFailed),
|
||||
original_state,
|
||||
)),
|
||||
Err((_, _, _)) => Ok((NoProgress, (), original_state)),
|
||||
|
@ -452,10 +461,14 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn lookahead<'a, Peek, P, PeekVal, Val>(peek: Peek, parser: P) -> impl Parser<'a, Val>
|
||||
pub fn lookahead<'a, Peek, P, PeekVal, Val, Error>(
|
||||
peek: Peek,
|
||||
parser: P,
|
||||
) -> impl Parser<'a, Val, Error>
|
||||
where
|
||||
Peek: Parser<'a, PeekVal>,
|
||||
P: Parser<'a, Val>,
|
||||
Error: 'a,
|
||||
Peek: Parser<'a, PeekVal, Error>,
|
||||
P: Parser<'a, Val, Error>,
|
||||
{
|
||||
move |arena, state: State<'a>| {
|
||||
let original_state = state.clone();
|
||||
|
@ -465,11 +478,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn and_then<'a, P1, P2, F, Before, After>(parser: P1, transform: F) -> impl Parser<'a, After>
|
||||
pub fn and_then<'a, P1, P2, F, Before, After, Error>(
|
||||
parser: P1,
|
||||
transform: F,
|
||||
) -> impl Parser<'a, After, Error>
|
||||
where
|
||||
P1: Parser<'a, Before>,
|
||||
P2: Parser<'a, After>,
|
||||
P1: Parser<'a, Before, Error>,
|
||||
P2: Parser<'a, After, Error>,
|
||||
F: Fn(Progress, Before) -> P2,
|
||||
Error: 'a,
|
||||
{
|
||||
move |arena, state| {
|
||||
parser
|
||||
|
@ -480,14 +497,15 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn and_then_with_indent_level<'a, P1, P2, F, Before, After>(
|
||||
pub fn and_then_with_indent_level<'a, P1, P2, F, Before, After, E>(
|
||||
parser: P1,
|
||||
transform: F,
|
||||
) -> impl Parser<'a, After>
|
||||
) -> impl Parser<'a, After, E>
|
||||
where
|
||||
P1: Parser<'a, Before>,
|
||||
P2: Parser<'a, After>,
|
||||
P1: Parser<'a, Before, E>,
|
||||
P2: Parser<'a, After, E>,
|
||||
F: Fn(Progress, Before, u16) -> P2,
|
||||
E: 'a,
|
||||
{
|
||||
move |arena, state| {
|
||||
parser
|
||||
|
@ -498,11 +516,12 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn then<'a, P1, F, Before, After>(parser: P1, transform: F) -> impl Parser<'a, After>
|
||||
pub fn then<'a, P1, F, Before, After, E>(parser: P1, transform: F) -> impl Parser<'a, After, E>
|
||||
where
|
||||
P1: Parser<'a, Before>,
|
||||
P1: Parser<'a, Before, E>,
|
||||
After: 'a,
|
||||
F: Fn(&'a Bump, State<'a>, Progress, Before) -> ParseResult<'a, After>,
|
||||
E: 'a,
|
||||
F: Fn(&'a Bump, State<'a>, Progress, Before) -> ParseResult<'a, After, E>,
|
||||
{
|
||||
move |arena, state| {
|
||||
parser
|
||||
|
@ -517,9 +536,9 @@ pub fn unexpected_eof<'a>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
chars_consumed: usize,
|
||||
) -> (Progress, Bag<'a>, State<'a>) {
|
||||
) -> (Progress, Bag<'a, SyntaxError>, State<'a>) {
|
||||
checked_unexpected(arena, state, chars_consumed, |region| {
|
||||
FailReason::Eof(region)
|
||||
SyntaxError::Eof(region)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -528,11 +547,11 @@ pub fn unexpected<'a>(
|
|||
chars_consumed: usize,
|
||||
_attempting: Attempting,
|
||||
state: State<'a>,
|
||||
) -> (Progress, Bag<'a>, State<'a>) {
|
||||
) -> (Progress, Bag<'a, SyntaxError>, State<'a>) {
|
||||
// NOTE state is the last argument because chars_consumed often depends on the state's fields
|
||||
// having state be the final argument prevents borrowing issues
|
||||
checked_unexpected(arena, state, chars_consumed, |region| {
|
||||
FailReason::Unexpected(region)
|
||||
SyntaxError::Unexpected(region)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -545,9 +564,9 @@ fn checked_unexpected<'a, F>(
|
|||
state: State<'a>,
|
||||
chars_consumed: usize,
|
||||
problem_from_region: F,
|
||||
) -> (Progress, Bag<'a>, State<'a>)
|
||||
) -> (Progress, Bag<'a, SyntaxError>, State<'a>)
|
||||
where
|
||||
F: FnOnce(Region) -> FailReason,
|
||||
F: FnOnce(Region) -> SyntaxError,
|
||||
{
|
||||
match (state.column as usize).checked_add(chars_consumed) {
|
||||
// Crucially, this is < u16::MAX and not <= u16::MAX. This means if
|
||||
|
@ -578,8 +597,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn line_too_long<'a>(arena: &'a Bump, state: State<'a>) -> (Progress, Bag<'a>, State<'a>) {
|
||||
let problem = FailReason::LineTooLong(state.line);
|
||||
fn line_too_long<'a>(
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
) -> (Progress, Bag<'a, SyntaxError>, State<'a>) {
|
||||
let problem = SyntaxError::LineTooLong(state.line);
|
||||
// Set column to MAX and advance the parser to end of input.
|
||||
// This way, all future parsers will fail on EOF, and then
|
||||
// unexpected_eof will take them back here - thus propagating
|
||||
|
@ -606,7 +628,7 @@ fn line_too_long<'a>(arena: &'a Bump, state: State<'a>) -> (Progress, Bag<'a>, S
|
|||
|
||||
/// A single ASCII char that isn't a newline.
|
||||
/// (For newlines, use newline_char(), which handles line numbers)
|
||||
pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, ()> {
|
||||
pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, (), SyntaxError> {
|
||||
// Make sure this really is not a newline!
|
||||
debug_assert_ne!(expected, b'\n');
|
||||
|
||||
|
@ -624,7 +646,7 @@ pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, ()> {
|
|||
/// A single '\n' character.
|
||||
/// Use this instead of ascii_char('\n') because it properly handles
|
||||
/// incrementing the line number.
|
||||
pub fn newline_char<'a>() -> impl Parser<'a, ()> {
|
||||
pub fn newline_char<'a>() -> impl Parser<'a, (), SyntaxError> {
|
||||
move |arena, state: State<'a>| match state.bytes.first() {
|
||||
Some(b'\n') => Ok((Progress::MadeProgress, (), state.newline(arena)?)),
|
||||
Some(_) => Err(unexpected(arena, 0, Attempting::Keyword, state)),
|
||||
|
@ -634,7 +656,7 @@ pub fn newline_char<'a>() -> impl Parser<'a, ()> {
|
|||
|
||||
/// One or more ASCII hex digits. (Useful when parsing unicode escape codes,
|
||||
/// which must consist entirely of ASCII hex digits.)
|
||||
pub fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str> {
|
||||
pub fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, SyntaxError> {
|
||||
move |arena, state: State<'a>| {
|
||||
let mut buf = bumpalo::collections::String::new_in(arena);
|
||||
|
||||
|
@ -657,14 +679,14 @@ pub fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str> {
|
|||
|
||||
/// A single UTF-8-encoded char. This will both parse *and* validate that the
|
||||
/// char is valid UTF-8, but it will *not* advance the state.
|
||||
pub fn peek_utf8_char(state: &State) -> Result<(char, usize), FailReason> {
|
||||
pub fn peek_utf8_char(state: &State) -> Result<(char, usize), SyntaxError> {
|
||||
if !state.bytes.is_empty() {
|
||||
match char::from_utf8_slice_start(state.bytes) {
|
||||
Ok((ch, len_utf8)) => Ok((ch, len_utf8)),
|
||||
Err(_) => Err(FailReason::BadUtf8),
|
||||
Err(_) => Err(SyntaxError::BadUtf8),
|
||||
}
|
||||
} else {
|
||||
Err(FailReason::Eof(
|
||||
Err(SyntaxError::Eof(
|
||||
Region::zero(), /* TODO get a better region */
|
||||
))
|
||||
}
|
||||
|
@ -672,22 +694,22 @@ pub fn peek_utf8_char(state: &State) -> Result<(char, usize), FailReason> {
|
|||
|
||||
/// A single UTF-8-encoded char, with an offset. This will both parse *and*
|
||||
/// validate that the char is valid UTF-8, but it will *not* advance the state.
|
||||
pub fn peek_utf8_char_at(state: &State, offset: usize) -> Result<(char, usize), FailReason> {
|
||||
pub fn peek_utf8_char_at(state: &State, offset: usize) -> Result<(char, usize), SyntaxError> {
|
||||
if state.bytes.len() > offset {
|
||||
let bytes = &state.bytes[offset..];
|
||||
|
||||
match char::from_utf8_slice_start(bytes) {
|
||||
Ok((ch, len_utf8)) => Ok((ch, len_utf8)),
|
||||
Err(_) => Err(FailReason::BadUtf8),
|
||||
Err(_) => Err(SyntaxError::BadUtf8),
|
||||
}
|
||||
} else {
|
||||
Err(FailReason::Eof(
|
||||
Err(SyntaxError::Eof(
|
||||
Region::zero(), /* TODO get a better region */
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keyword<'a>(keyword: &'static str, min_indent: u16) -> impl Parser<'a, ()> {
|
||||
pub fn keyword<'a>(keyword: &'static str, min_indent: u16) -> impl Parser<'a, (), SyntaxError> {
|
||||
move |arena, state: State<'a>| {
|
||||
let initial_state = state.clone();
|
||||
// first parse the keyword characters
|
||||
|
@ -712,7 +734,7 @@ pub fn keyword<'a>(keyword: &'static str, min_indent: u16) -> impl Parser<'a, ()
|
|||
}
|
||||
|
||||
/// A hardcoded string with no newlines, consisting only of ASCII characters
|
||||
pub fn ascii_string<'a>(keyword: &'static str) -> impl Parser<'a, ()> {
|
||||
pub fn ascii_string<'a>(keyword: &'static str) -> impl Parser<'a, (), SyntaxError> {
|
||||
// Verify that this really is exclusively ASCII characters.
|
||||
// The `unsafe` block in this function relies upon this assumption!
|
||||
//
|
||||
|
@ -744,10 +766,14 @@ pub fn ascii_string<'a>(keyword: &'static str) -> impl Parser<'a, ()> {
|
|||
|
||||
/// Parse zero or more values separated by a delimiter (e.g. a comma) whose
|
||||
/// values are discarded
|
||||
pub fn sep_by0<'a, P, D, Val>(delimiter: D, parser: P) -> impl Parser<'a, Vec<'a, Val>>
|
||||
pub fn sep_by0<'a, P, D, Val, Error>(
|
||||
delimiter: D,
|
||||
parser: P,
|
||||
) -> impl Parser<'a, Vec<'a, Val>, Error>
|
||||
where
|
||||
D: Parser<'a, ()>,
|
||||
P: Parser<'a, Val>,
|
||||
D: Parser<'a, (), Error>,
|
||||
P: Parser<'a, Val, Error>,
|
||||
Error: 'a,
|
||||
{
|
||||
move |arena, state: State<'a>| {
|
||||
let start_bytes_len = state.bytes.len();
|
||||
|
@ -801,10 +827,14 @@ where
|
|||
|
||||
/// Parse zero or more values separated by a delimiter (e.g. a comma)
|
||||
/// with an optional trailing delimiter whose values are discarded
|
||||
pub fn trailing_sep_by0<'a, P, D, Val>(delimiter: D, parser: P) -> impl Parser<'a, Vec<'a, Val>>
|
||||
pub fn trailing_sep_by0<'a, P, D, Val, Error>(
|
||||
delimiter: D,
|
||||
parser: P,
|
||||
) -> impl Parser<'a, Vec<'a, Val>, Error>
|
||||
where
|
||||
D: Parser<'a, ()>,
|
||||
P: Parser<'a, Val>,
|
||||
D: Parser<'a, (), Error>,
|
||||
P: Parser<'a, Val, Error>,
|
||||
Error: 'a,
|
||||
{
|
||||
move |arena, state: State<'a>| {
|
||||
let start_bytes_len = state.bytes.len();
|
||||
|
@ -858,10 +888,14 @@ where
|
|||
|
||||
/// Parse one or more values separated by a delimiter (e.g. a comma) whose
|
||||
/// values are discarded
|
||||
pub fn sep_by1<'a, P, D, Val>(delimiter: D, parser: P) -> impl Parser<'a, Vec<'a, Val>>
|
||||
pub fn sep_by1<'a, P, D, Val, Error>(
|
||||
delimiter: D,
|
||||
parser: P,
|
||||
) -> impl Parser<'a, Vec<'a, Val>, Error>
|
||||
where
|
||||
D: Parser<'a, ()>,
|
||||
P: Parser<'a, Val>,
|
||||
D: Parser<'a, (), Error>,
|
||||
P: Parser<'a, Val, Error>,
|
||||
Error: 'a,
|
||||
{
|
||||
move |arena, state: State<'a>| {
|
||||
let start_bytes_len = state.bytes.len();
|
||||
|
@ -913,21 +947,21 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn fail_when_progress<'a, T>(
|
||||
pub fn fail_when_progress<'a, T, E>(
|
||||
progress: Progress,
|
||||
fail: Bag<'a>,
|
||||
fail: Bag<'a, E>,
|
||||
value: T,
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, T> {
|
||||
) -> ParseResult<'a, T, E> {
|
||||
match progress {
|
||||
MadeProgress => Err((MadeProgress, fail, state)),
|
||||
NoProgress => Ok((NoProgress, value, state)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn satisfies<'a, P, A, F>(parser: P, predicate: F) -> impl Parser<'a, A>
|
||||
pub fn satisfies<'a, P, A, F>(parser: P, predicate: F) -> impl Parser<'a, A, SyntaxError>
|
||||
where
|
||||
P: Parser<'a, A>,
|
||||
P: Parser<'a, A, SyntaxError>,
|
||||
F: Fn(&A) -> bool,
|
||||
{
|
||||
move |arena: &'a Bump, state: State<'a>| match parser.parse(arena, state.clone()) {
|
||||
|
@ -936,15 +970,16 @@ where
|
|||
}
|
||||
Ok((progress, _, _)) | Err((progress, _, _)) => Err((
|
||||
progress,
|
||||
Bag::from_state(arena, &state, FailReason::ConditionFailed),
|
||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
||||
state,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optional<'a, P, T>(parser: P) -> impl Parser<'a, Option<T>>
|
||||
pub fn optional<'a, P, T, E>(parser: P) -> impl Parser<'a, Option<T>, E>
|
||||
where
|
||||
P: Parser<'a, T>,
|
||||
P: Parser<'a, T, E>,
|
||||
E: 'a,
|
||||
{
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
// We have to clone this because if the optional parser fails,
|
||||
|
@ -1330,7 +1365,7 @@ macro_rules! record_field {
|
|||
($val_parser:expr, $min_indent:expr) => {
|
||||
move |arena: &'a bumpalo::Bump,
|
||||
state: $crate::parser::State<'a>|
|
||||
-> $crate::parser::ParseResult<'a, $crate::ast::AssignedField<'a, _>> {
|
||||
-> $crate::parser::ParseResult<'a, $crate::ast::AssignedField<'a, _>, _> {
|
||||
use $crate::ast::AssignedField::*;
|
||||
use $crate::blankspace::{space0, space0_before};
|
||||
use $crate::ident::lowercase_ident;
|
||||
|
@ -1433,84 +1468,93 @@ macro_rules! record {
|
|||
|
||||
/// For some reason, some usages won't compile unless they use this instead of the macro version
|
||||
#[inline(always)]
|
||||
pub fn and<'a, P1, P2, A, B>(p1: P1, p2: P2) -> impl Parser<'a, (A, B)>
|
||||
pub fn and<'a, P1, P2, A, B, E>(p1: P1, p2: P2) -> impl Parser<'a, (A, B), E>
|
||||
where
|
||||
P1: Parser<'a, A>,
|
||||
P2: Parser<'a, B>,
|
||||
P1: Parser<'a, A, E>,
|
||||
P2: Parser<'a, B, E>,
|
||||
P1: 'a,
|
||||
P2: 'a,
|
||||
A: 'a,
|
||||
B: 'a,
|
||||
E: 'a,
|
||||
{
|
||||
and!(p1, p2)
|
||||
}
|
||||
|
||||
/// For some reason, some usages won't compile unless they use this instead of the macro version
|
||||
#[inline(always)]
|
||||
pub fn loc<'a, P, Val>(parser: P) -> impl Parser<'a, Located<Val>>
|
||||
pub fn loc<'a, P, Val, Error>(parser: P) -> impl Parser<'a, Located<Val>, Error>
|
||||
where
|
||||
P: Parser<'a, Val>,
|
||||
P: Parser<'a, Val, Error>,
|
||||
Error: 'a,
|
||||
{
|
||||
loc!(parser)
|
||||
}
|
||||
|
||||
/// For some reason, some usages won't compile unless they use this instead of the macro version
|
||||
#[inline(always)]
|
||||
pub fn map<'a, P, F, Before, After>(parser: P, transform: F) -> impl Parser<'a, After>
|
||||
pub fn map<'a, P, F, Before, After, E>(parser: P, transform: F) -> impl Parser<'a, After, E>
|
||||
where
|
||||
P: Parser<'a, Before>,
|
||||
P: Parser<'a, Before, E>,
|
||||
F: Fn(Before) -> After,
|
||||
E: 'a,
|
||||
{
|
||||
map!(parser, transform)
|
||||
}
|
||||
|
||||
/// For some reason, some usages won't compile unless they use this instead of the macro version
|
||||
#[inline(always)]
|
||||
pub fn map_with_arena<'a, P, F, Before, After>(parser: P, transform: F) -> impl Parser<'a, After>
|
||||
pub fn map_with_arena<'a, P, F, Before, After, E>(
|
||||
parser: P,
|
||||
transform: F,
|
||||
) -> impl Parser<'a, After, E>
|
||||
where
|
||||
P: Parser<'a, Before>,
|
||||
P: Parser<'a, Before, E>,
|
||||
P: 'a,
|
||||
F: Fn(&'a Bump, Before) -> After,
|
||||
F: 'a,
|
||||
Before: 'a,
|
||||
After: 'a,
|
||||
E: 'a,
|
||||
{
|
||||
map_with_arena!(parser, transform)
|
||||
}
|
||||
|
||||
/// For some reason, some usages won't compile unless they use this instead of the macro version
|
||||
#[inline(always)]
|
||||
pub fn attempt<'a, P, Val>(attempting: Attempting, parser: P) -> impl Parser<'a, Val>
|
||||
pub fn attempt<'a, P, Val, Error>(attempting: Attempting, parser: P) -> impl Parser<'a, Val, Error>
|
||||
where
|
||||
P: Parser<'a, Val>,
|
||||
P: Parser<'a, Val, Error>,
|
||||
Error: 'a,
|
||||
{
|
||||
attempt!(attempting, parser)
|
||||
}
|
||||
|
||||
pub fn parse_utf8(bytes: &[u8]) -> Result<&str, FailReason> {
|
||||
pub fn parse_utf8(bytes: &[u8]) -> Result<&str, SyntaxError> {
|
||||
match from_utf8(bytes) {
|
||||
Ok(string) => Ok(string),
|
||||
Err(_) => Err(FailReason::BadUtf8),
|
||||
Err(_) => Err(SyntaxError::BadUtf8),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn end_of_file<'a>() -> impl Parser<'a, ()> {
|
||||
pub fn end_of_file<'a>() -> impl Parser<'a, (), SyntaxError> {
|
||||
|arena: &'a Bump, state: State<'a>| {
|
||||
if state.has_reached_end() {
|
||||
Ok((NoProgress, (), state))
|
||||
} else {
|
||||
Err((
|
||||
NoProgress,
|
||||
Bag::from_state(arena, &state, FailReason::ConditionFailed),
|
||||
Bag::from_state(arena, &state, SyntaxError::ConditionFailed),
|
||||
state,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn backtrackable<'a, P, Val>(parser: P) -> impl Parser<'a, Val>
|
||||
pub fn backtrackable<'a, P, Val, Error>(parser: P) -> impl Parser<'a, Val, Error>
|
||||
where
|
||||
P: Parser<'a, Val>,
|
||||
P: Parser<'a, Val, Error>,
|
||||
Error: 'a,
|
||||
{
|
||||
move |arena: &'a Bump, state: State<'a>| {
|
||||
let old_state = state.clone();
|
||||
|
|
|
@ -3,12 +3,12 @@ use crate::expr;
|
|||
use crate::parser::Progress::*;
|
||||
use crate::parser::{
|
||||
allocated, ascii_char, ascii_hex_digits, loc, parse_utf8, unexpected, unexpected_eof, Bag,
|
||||
FailReason, ParseResult, Parser, State,
|
||||
ParseResult, Parser, State, SyntaxError,
|
||||
};
|
||||
use bumpalo::collections::vec::Vec;
|
||||
use bumpalo::Bump;
|
||||
|
||||
pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>> {
|
||||
pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, SyntaxError> {
|
||||
use StrLiteral::*;
|
||||
|
||||
move |arena: &'a Bump, mut state: State<'a>| {
|
||||
|
@ -256,7 +256,7 @@ fn parse_block_string<'a, I>(
|
|||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
bytes: &mut I,
|
||||
) -> ParseResult<'a, StrLiteral<'a>>
|
||||
) -> ParseResult<'a, StrLiteral<'a>, SyntaxError>
|
||||
where
|
||||
I: Iterator<Item = &'a u8>,
|
||||
{
|
||||
|
@ -294,7 +294,7 @@ where
|
|||
Bag::from_state(
|
||||
arena,
|
||||
&state,
|
||||
FailReason::NotYetImplemented(format!(
|
||||
SyntaxError::NotYetImplemented(format!(
|
||||
"TODO parse this line in a block string: {:?}",
|
||||
line
|
||||
)),
|
||||
|
|
|
@ -2,17 +2,23 @@ use crate::ast::{self, Attempting};
|
|||
use crate::blankspace::space0_before;
|
||||
use crate::expr::expr;
|
||||
use crate::module::{header, module_defs};
|
||||
use crate::parser::{loc, Bag, Parser, State};
|
||||
use crate::parser::{loc, Bag, Parser, State, SyntaxError};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
use roc_region::all::Located;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn parse_expr_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Expr<'a>, Bag<'a>> {
|
||||
pub fn parse_expr_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<ast::Expr<'a>, Bag<'a, SyntaxError>> {
|
||||
parse_loc_with(arena, input).map(|loc_expr| loc_expr.value)
|
||||
}
|
||||
|
||||
pub fn parse_header_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Module<'a>, Bag<'a>> {
|
||||
pub fn parse_header_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<ast::Module<'a>, Bag<'a, SyntaxError>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let answer = header().parse(arena, state);
|
||||
|
||||
|
@ -25,7 +31,7 @@ pub fn parse_header_with<'a>(arena: &'a Bump, input: &'a str) -> Result<ast::Mod
|
|||
pub fn parse_defs_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Vec<'a, Located<ast::Def<'a>>>, Bag<'a>> {
|
||||
) -> Result<Vec<'a, Located<ast::Def<'a>>>, Bag<'a, SyntaxError>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let answer = module_defs().parse(arena, state);
|
||||
answer
|
||||
|
@ -37,7 +43,7 @@ pub fn parse_defs_with<'a>(
|
|||
pub fn parse_loc_with<'a>(
|
||||
arena: &'a Bump,
|
||||
input: &'a str,
|
||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a>> {
|
||||
) -> Result<Located<ast::Expr<'a>>, Bag<'a, SyntaxError>> {
|
||||
let state = State::new_in(arena, input.trim().as_bytes(), Attempting::Module);
|
||||
let parser = space0_before(loc(expr(0)), 0);
|
||||
let answer = parser.parse(&arena, state);
|
||||
|
|
|
@ -5,9 +5,9 @@ use crate::ident::join_module_parts;
|
|||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
allocated, ascii_char, ascii_string, not, optional, peek_utf8_char, unexpected, Bag, Either,
|
||||
FailReason, ParseResult, Parser,
|
||||
ParseResult, Parser,
|
||||
Progress::{self, *},
|
||||
State,
|
||||
State, SyntaxError,
|
||||
};
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
|
@ -15,7 +15,7 @@ use bumpalo::Bump;
|
|||
use roc_collections::all::arena_join;
|
||||
use roc_region::all::{Located, Region};
|
||||
|
||||
pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
pub fn located<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, SyntaxError> {
|
||||
expression(min_indent)
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ macro_rules! tag_union {
|
|||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
pub fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, SyntaxError> {
|
||||
map_with_arena!(
|
||||
and!(
|
||||
one_of!(
|
||||
|
@ -95,13 +95,15 @@ pub fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>>
|
|||
}
|
||||
|
||||
/// The `*` type variable, e.g. in (List *) Wildcard,
|
||||
fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
fn loc_wildcard<'a>() -> impl Parser<'a, Located<TypeAnnotation<'a>>, SyntaxError> {
|
||||
map!(loc!(ascii_char(b'*')), |loc_val: Located<()>| {
|
||||
loc_val.map(|_| TypeAnnotation::Wildcard)
|
||||
})
|
||||
}
|
||||
|
||||
fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
fn loc_applied_arg<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, SyntaxError> {
|
||||
skip_first!(
|
||||
// Once we hit an "as", stop parsing args
|
||||
// and roll back parsing of preceding spaces
|
||||
|
@ -123,12 +125,16 @@ fn loc_applied_arg<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotatio
|
|||
)
|
||||
}
|
||||
|
||||
fn loc_applied_args<'a>(min_indent: u16) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>> {
|
||||
fn loc_applied_args<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Vec<'a, Located<TypeAnnotation<'a>>>, SyntaxError> {
|
||||
zero_or_more!(loc_applied_arg(min_indent))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn loc_parenthetical_type<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
fn loc_parenthetical_type<'a>(
|
||||
min_indent: u16,
|
||||
) -> impl Parser<'a, Located<TypeAnnotation<'a>>, SyntaxError> {
|
||||
between!(
|
||||
ascii_char(b'('),
|
||||
space0_around(
|
||||
|
@ -141,7 +147,7 @@ fn loc_parenthetical_type<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAn
|
|||
|
||||
#[inline(always)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn tag_type<'a>(min_indent: u16) -> impl Parser<'a, Tag<'a>> {
|
||||
fn tag_type<'a>(min_indent: u16) -> impl Parser<'a, Tag<'a>, SyntaxError> {
|
||||
map!(
|
||||
and!(
|
||||
either!(loc!(private_tag()), loc!(global_tag())),
|
||||
|
@ -166,7 +172,7 @@ fn tag_type<'a>(min_indent: u16) -> impl Parser<'a, Tag<'a>> {
|
|||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> {
|
||||
fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, SyntaxError> {
|
||||
use crate::type_annotation::TypeAnnotation::*;
|
||||
type Fields<'a> = Vec<'a, Located<AssignedField<'a, TypeAnnotation<'a>>>>;
|
||||
map!(
|
||||
|
@ -193,7 +199,7 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> {
|
||||
fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, SyntaxError> {
|
||||
map!(
|
||||
and!(
|
||||
parse_concrete_type,
|
||||
|
@ -218,7 +224,7 @@ fn applied_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>> {
|
|||
)
|
||||
}
|
||||
|
||||
fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>> {
|
||||
fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, SyntaxError> {
|
||||
use crate::blankspace::space0;
|
||||
move |arena, state: State<'a>| {
|
||||
let (p1, first, state) = space0_before(term(min_indent), min_indent).parse(arena, state)?;
|
||||
|
@ -261,7 +267,7 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
|||
.to_string();
|
||||
Err((
|
||||
progress,
|
||||
Bag::from_state(arena, &state, FailReason::NotYetImplemented(msg)),
|
||||
Bag::from_state(arena, &state, SyntaxError::NotYetImplemented(msg)),
|
||||
state,
|
||||
))
|
||||
}
|
||||
|
@ -288,7 +294,7 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
|||
fn parse_concrete_type<'a>(
|
||||
arena: &'a Bump,
|
||||
mut state: State<'a>,
|
||||
) -> ParseResult<'a, TypeAnnotation<'a>> {
|
||||
) -> ParseResult<'a, TypeAnnotation<'a>, SyntaxError> {
|
||||
let mut part_buf = String::new_in(arena); // The current "part" (parts are dot-separated.)
|
||||
let mut parts: Vec<&'a str> = Vec::new_in(arena);
|
||||
|
||||
|
@ -389,7 +395,7 @@ fn parse_concrete_type<'a>(
|
|||
fn parse_type_variable<'a>(
|
||||
arena: &'a Bump,
|
||||
mut state: State<'a>,
|
||||
) -> ParseResult<'a, TypeAnnotation<'a>> {
|
||||
) -> ParseResult<'a, TypeAnnotation<'a>, SyntaxError> {
|
||||
let mut buf = String::new_in(arena);
|
||||
|
||||
let start_bytes_len = state.bytes.len();
|
||||
|
@ -445,7 +451,7 @@ fn malformed<'a>(
|
|||
arena: &'a Bump,
|
||||
mut state: State<'a>,
|
||||
parts: Vec<&'a str>,
|
||||
) -> ParseResult<'a, TypeAnnotation<'a>> {
|
||||
) -> ParseResult<'a, TypeAnnotation<'a>, SyntaxError> {
|
||||
// assumption: progress was made to conclude that the annotation is malformed
|
||||
|
||||
// Reconstruct the original string that we've been parsing.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue