mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
number parsing with new errors
This commit is contained in:
parent
d4de440943
commit
bb9a2525b5
8 changed files with 187 additions and 263 deletions
|
@ -371,7 +371,7 @@ pub fn line_comment<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> {
|
|||
}
|
||||
|
||||
let comment = &state.bytes[..length];
|
||||
let state = state.advance_without_indenting(arena, length + 1)?;
|
||||
let state = state.advance_without_indenting(length + 1)?;
|
||||
match parse_utf8(comment) {
|
||||
Ok(comment_str) => Ok((MadeProgress, comment_str, state)),
|
||||
Err(reason) => state.fail(arena, MadeProgress, reason),
|
||||
|
@ -535,7 +535,7 @@ where
|
|||
.map_err(|(fail, _)| {
|
||||
(progress, fail, original_state.clone())
|
||||
})?
|
||||
.advance_without_indenting_e(arena, 1, space_problem)?;
|
||||
.advance_without_indenting_e(1, space_problem)?;
|
||||
|
||||
// We're now parsing a line comment!
|
||||
line_state = LineState::Comment;
|
||||
|
@ -584,11 +584,7 @@ where
|
|||
match ch {
|
||||
' ' => {
|
||||
// If we're in a line comment, this won't affect indentation anyway.
|
||||
state = state.advance_without_indenting_e(
|
||||
arena,
|
||||
1,
|
||||
space_problem,
|
||||
)?;
|
||||
state = state.advance_without_indenting_e(1, space_problem)?;
|
||||
|
||||
if comment_line_buf.len() == 1 {
|
||||
match comment_line_buf.chars().next() {
|
||||
|
@ -650,7 +646,6 @@ where
|
|||
nonblank => {
|
||||
// Chars can have btye lengths of more than 1!
|
||||
state = state.advance_without_indenting_e(
|
||||
arena,
|
||||
nonblank.len_utf8(),
|
||||
space_problem,
|
||||
)?;
|
||||
|
@ -663,11 +658,7 @@ where
|
|||
match ch {
|
||||
' ' => {
|
||||
// If we're in a doc comment, this won't affect indentation anyway.
|
||||
state = state.advance_without_indenting_e(
|
||||
arena,
|
||||
1,
|
||||
space_problem,
|
||||
)?;
|
||||
state = state.advance_without_indenting_e(1, space_problem)?;
|
||||
|
||||
comment_line_buf.push(ch);
|
||||
}
|
||||
|
@ -692,11 +683,8 @@ where
|
|||
));
|
||||
}
|
||||
nonblank => {
|
||||
state = state.advance_without_indenting_e(
|
||||
arena,
|
||||
utf8_len,
|
||||
space_problem,
|
||||
)?;
|
||||
state = state
|
||||
.advance_without_indenting_e(utf8_len, space_problem)?;
|
||||
|
||||
comment_line_buf.push(nonblank);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ use crate::blankspace::{
|
|||
};
|
||||
use crate::ident::{ident, lowercase_ident, Ident};
|
||||
use crate::keyword;
|
||||
use crate::number_literal::number_literal;
|
||||
use crate::parser::{
|
||||
self, allocated, and_then_with_indent_level, ascii_char, ascii_string, attempt, backtrackable,
|
||||
map, newline_char, not, not_followed_by, optional, sep_by1, sep_by1_e, specialize,
|
||||
|
@ -673,7 +672,7 @@ fn equals_for_def<'a>() -> impl Parser<'a, (), SyntaxError<'a>> {
|
|||
Some(b'=') => match state.bytes.get(1) {
|
||||
Some(b'=') | Some(b'>') => Err((NoProgress, SyntaxError::ConditionFailed, state)),
|
||||
_ => {
|
||||
let state = state.advance_without_indenting(arena, 1)?;
|
||||
let state = state.advance_without_indenting(1)?;
|
||||
|
||||
Ok((MadeProgress, (), state))
|
||||
}
|
||||
|
@ -1831,7 +1830,7 @@ pub fn equals_with_indent_help<'a>() -> impl Parser<'a, u16, EExpr<'a>> {
|
|||
// The '=' must not be followed by another `=` or `>`
|
||||
// (See equals_for_def() for explanation)
|
||||
Some(b'=') | Some(b'>') => Err((NoProgress, equals, state)),
|
||||
Some(_) => match state.advance_without_indenting_e(arena, 1, EExpr::Space) {
|
||||
Some(_) => match state.advance_without_indenting_e(1, EExpr::Space) {
|
||||
Err(bad) => Err(bad),
|
||||
Ok(good) => Ok((MadeProgress, indent_col, good)),
|
||||
},
|
||||
|
@ -1856,11 +1855,11 @@ pub fn equals_with_indent<'a>() -> impl Parser<'a, u16, SyntaxError<'a>> {
|
|||
Some(_) => Ok((
|
||||
MadeProgress,
|
||||
state.indent_col,
|
||||
state.advance_without_indenting(arena, 1)?,
|
||||
state.advance_without_indenting(1)?,
|
||||
)),
|
||||
None => Err(unexpected_eof(
|
||||
arena,
|
||||
state.advance_without_indenting(arena, 1)?,
|
||||
state.advance_without_indenting(1)?,
|
||||
1,
|
||||
)),
|
||||
}
|
||||
|
@ -1876,7 +1875,7 @@ pub fn colon_with_indent<'a>() -> impl Parser<'a, u16, SyntaxError<'a>> {
|
|||
Some(&byte) if byte == b':' => Ok((
|
||||
MadeProgress,
|
||||
state.indent_col,
|
||||
state.advance_without_indenting(arena, 1)?,
|
||||
state.advance_without_indenting(1)?,
|
||||
)),
|
||||
Some(_) => Err(unexpected(arena, 0, Attempting::Def, state)),
|
||||
None => Err(unexpected_eof(arena, state, 0)),
|
||||
|
@ -2239,6 +2238,15 @@ fn string_literal_help<'a>() -> impl Parser<'a, Expr<'a>, EString> {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, Number> {
|
||||
specialize(|_, _, _| Number::NumberEnd, number_literal())
|
||||
fn number_literal<'a>() -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
// use crate::number_literal::number_literal;
|
||||
specialize(
|
||||
|e, r, c| SyntaxError::Expr(EExpr::Number(e, r, c)),
|
||||
crate::number_literal::number_literal(),
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn number_literal_help<'a>() -> impl Parser<'a, Expr<'a>, Number> {
|
||||
crate::number_literal::number_literal()
|
||||
}
|
||||
|
|
|
@ -91,20 +91,20 @@ pub fn parse_ident<'a>(
|
|||
is_capitalized = first_ch.is_uppercase();
|
||||
is_accessor_fn = false;
|
||||
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
} else if first_ch == '.' {
|
||||
is_capitalized = false;
|
||||
is_accessor_fn = true;
|
||||
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
} else if first_ch == '@' {
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
|
||||
// '@' must always be followed by a capital letter!
|
||||
match peek_utf8_char(&state) {
|
||||
Ok((next_ch, next_bytes_parsed)) => {
|
||||
if next_ch.is_uppercase() {
|
||||
state = state.advance_without_indenting(arena, next_bytes_parsed)?;
|
||||
state = state.advance_without_indenting(next_bytes_parsed)?;
|
||||
|
||||
part_buf.push('@');
|
||||
part_buf.push(next_ch);
|
||||
|
@ -193,7 +193,7 @@ pub fn parse_ident<'a>(
|
|||
break;
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
}
|
||||
Err(reason) => {
|
||||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
|
@ -308,7 +308,7 @@ fn malformed<'a>(
|
|||
break;
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
}
|
||||
Err(reason) => return state.fail(arena, MadeProgress, reason),
|
||||
}
|
||||
|
@ -351,7 +351,7 @@ where
|
|||
|
||||
buf.push(first_letter);
|
||||
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
|
||||
while !state.bytes.is_empty() {
|
||||
match peek_utf8_char(&state) {
|
||||
|
@ -364,7 +364,7 @@ where
|
|||
if ch.is_alphabetic() || ch.is_ascii_digit() {
|
||||
buf.push(ch);
|
||||
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
} else {
|
||||
// This is the end of the field. We're done!
|
||||
break;
|
||||
|
|
|
@ -99,7 +99,7 @@ pub fn parse_package_part<'a>(
|
|||
if ch == '-' || ch.is_ascii_alphanumeric() {
|
||||
part_buf.push(ch);
|
||||
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
} else {
|
||||
let progress = Progress::progress_when(!part_buf.is_empty());
|
||||
return Ok((progress, part_buf.into_bump_str(), state));
|
||||
|
@ -128,7 +128,7 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError<'a>> {
|
|||
|
||||
buf.push(first_letter);
|
||||
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
|
||||
while !state.bytes.is_empty() {
|
||||
match peek_utf8_char(&state) {
|
||||
|
@ -139,7 +139,7 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError<'a>> {
|
|||
// * ASCII digits - e.g. `1` but not `¾`, both of which pass .is_numeric()
|
||||
// * A '.' separating module parts
|
||||
if ch.is_alphabetic() || ch.is_ascii_digit() {
|
||||
state = state.advance_without_indenting(arena, bytes_parsed)?;
|
||||
state = state.advance_without_indenting(bytes_parsed)?;
|
||||
|
||||
buf.push(ch);
|
||||
} else if ch == '.' {
|
||||
|
@ -151,7 +151,6 @@ pub fn module_name<'a>() -> impl Parser<'a, ModuleName<'a>, SyntaxError<'a>> {
|
|||
buf.push(next);
|
||||
|
||||
state = state.advance_without_indenting(
|
||||
arena,
|
||||
bytes_parsed + next_bytes_parsed,
|
||||
)?;
|
||||
} else {
|
||||
|
|
|
@ -1,189 +1,131 @@
|
|||
use crate::ast::{Attempting, Base, Expr};
|
||||
use crate::parser::{
|
||||
parse_utf8, unexpected, unexpected_eof, ParseResult, Parser, Progress, State, SyntaxError,
|
||||
};
|
||||
use bumpalo::Bump;
|
||||
use crate::ast::{Base, Expr};
|
||||
use crate::parser::{parse_utf8, Number, ParseResult, Parser, Progress, State, SyntaxError};
|
||||
use std::char;
|
||||
use std::str::from_utf8_unchecked;
|
||||
|
||||
pub fn number_literal<'a>() -> impl Parser<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
pub fn number_literal<'a>() -> impl Parser<'a, Expr<'a>, Number> {
|
||||
move |arena, state: State<'a>| {
|
||||
let bytes = &mut state.bytes.iter();
|
||||
|
||||
match bytes.next() {
|
||||
Some(&first_byte) => {
|
||||
// Number literals must start with either an '-' or a digit.
|
||||
if first_byte == b'-' || (first_byte as char).is_ascii_digit() {
|
||||
parse_number_literal(first_byte as char, bytes, arena, state)
|
||||
} else {
|
||||
Err(unexpected(arena, 1, Attempting::NumberLiteral, state))
|
||||
}
|
||||
match state.bytes.get(0) {
|
||||
Some(first_byte) if *first_byte == b'-' => {
|
||||
// drop the minus
|
||||
parse_number_base(true, &state.bytes[1..], state)
|
||||
}
|
||||
Some(first_byte) if (*first_byte as char).is_ascii_digit() => {
|
||||
parse_number_base(false, &state.bytes, state)
|
||||
}
|
||||
_ => {
|
||||
// this is not a number at all
|
||||
Err((Progress::NoProgress, Number::End, state))
|
||||
}
|
||||
None => Err(unexpected_eof(arena, state, 0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn parse_number_literal<'a, I>(
|
||||
first_ch: char,
|
||||
bytes: &mut I,
|
||||
arena: &'a Bump,
|
||||
fn parse_number_base<'a>(
|
||||
is_negated: bool,
|
||||
bytes: &'a [u8],
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, SyntaxError<'a>>
|
||||
where
|
||||
I: Iterator<Item = &'a u8>,
|
||||
{
|
||||
use self::LiteralType::*;
|
||||
|
||||
let mut typ = Num;
|
||||
|
||||
// We already parsed 1 character (which may have been a minus sign).
|
||||
let mut bytes_parsed = 1;
|
||||
let mut prev_byte = first_ch as u8;
|
||||
let mut has_parsed_digits = first_ch.is_ascii_digit();
|
||||
|
||||
for &next_byte in bytes {
|
||||
let err_unexpected = || {
|
||||
Err(unexpected(
|
||||
arena,
|
||||
bytes_parsed,
|
||||
Attempting::NumberLiteral,
|
||||
state.clone(),
|
||||
))
|
||||
};
|
||||
|
||||
let is_potentially_non_base10 = || {
|
||||
(bytes_parsed == 1 && first_ch == '0')
|
||||
|| (bytes_parsed == 2 && first_ch == '-' && prev_byte == b'0')
|
||||
};
|
||||
|
||||
match next_byte as char {
|
||||
'.' => {
|
||||
if typ == Float {
|
||||
// You only get one decimal point!
|
||||
return err_unexpected();
|
||||
} else {
|
||||
typ = Float;
|
||||
}
|
||||
}
|
||||
'x' => {
|
||||
if is_potentially_non_base10() {
|
||||
typ = Hex;
|
||||
} else {
|
||||
return err_unexpected();
|
||||
}
|
||||
}
|
||||
'b' if typ == Num => {
|
||||
// We have to check for typ == Num because otherwise we get a false
|
||||
// positive here when parsing a hex literal that happens to have
|
||||
// a 'b' in it, e.g. 0xbbbb
|
||||
if is_potentially_non_base10() {
|
||||
typ = Binary;
|
||||
} else {
|
||||
return err_unexpected();
|
||||
}
|
||||
}
|
||||
'o' => {
|
||||
if is_potentially_non_base10() {
|
||||
typ = Octal;
|
||||
} else {
|
||||
return err_unexpected();
|
||||
}
|
||||
}
|
||||
'_' => {
|
||||
// Underscores are ignored.
|
||||
}
|
||||
next_ch => {
|
||||
if next_ch.is_ascii_digit() {
|
||||
has_parsed_digits = true;
|
||||
} else {
|
||||
if !has_parsed_digits {
|
||||
// No digits! We likely parsed a minus sign
|
||||
// that's actually a unary negation operator.
|
||||
return err_unexpected();
|
||||
}
|
||||
|
||||
// ASCII alphabetic chars (like 'a' and 'f') are
|
||||
// allowed in Hex int literals. We verify them in
|
||||
// canonicalization, so if there's a problem, we can
|
||||
// give a more helpful error (e.g. "the character 'f'
|
||||
// is not allowed in Octal literals" or
|
||||
// "the character 'g' is outside the range of valid
|
||||
// Hex literals") while still allowing the formatter
|
||||
// to format them normally.
|
||||
if !next_ch.is_ascii_alphabetic() {
|
||||
// We hit an invalid number literal character; we're done!
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Since we only consume characters in the ASCII range for number literals,
|
||||
// this will always be exactly 1. There's no need to call next_ch.utf8_len().
|
||||
bytes_parsed += 1;
|
||||
prev_byte = next_byte;
|
||||
}
|
||||
|
||||
// At this point we have a number, and will definitely succeed.
|
||||
// If the number is malformed (outside the supported range),
|
||||
// we'll succeed with an appropriate Expr which records that.
|
||||
match typ {
|
||||
Num => Ok((
|
||||
Progress::from_consumed(bytes_parsed),
|
||||
// SAFETY: it's safe to use from_utf8_unchecked here, because we've
|
||||
// already validated that this range contains only ASCII digits
|
||||
Expr::Num(unsafe { from_utf8_unchecked(&state.bytes[0..bytes_parsed]) }),
|
||||
state.advance_without_indenting(arena, bytes_parsed)?,
|
||||
)),
|
||||
Float => Ok((
|
||||
Progress::from_consumed(bytes_parsed),
|
||||
// SAFETY: it's safe to use from_utf8_unchecked here, because we've
|
||||
// already validated that this range contains only ASCII digits
|
||||
Expr::Float(unsafe { from_utf8_unchecked(&state.bytes[0..bytes_parsed]) }),
|
||||
state.advance_without_indenting(arena, bytes_parsed)?,
|
||||
)),
|
||||
// For these we trim off the 0x/0o/0b part
|
||||
Hex => from_base(Base::Hex, first_ch, bytes_parsed, arena, state),
|
||||
Octal => from_base(Base::Octal, first_ch, bytes_parsed, arena, state),
|
||||
Binary => from_base(Base::Binary, first_ch, bytes_parsed, arena, state),
|
||||
) -> ParseResult<'a, Expr<'a>, Number> {
|
||||
match bytes.get(0..2) {
|
||||
Some(b"0b") => chomp_number_base(Base::Binary, is_negated, &bytes[2..], state),
|
||||
Some(b"0o") => chomp_number_base(Base::Octal, is_negated, &bytes[2..], state),
|
||||
Some(b"0x") => chomp_number_base(Base::Hex, is_negated, &bytes[2..], state),
|
||||
_ => chomp_number_dec(is_negated, bytes, state),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum LiteralType {
|
||||
Num,
|
||||
Float,
|
||||
Hex,
|
||||
Octal,
|
||||
Binary,
|
||||
}
|
||||
|
||||
fn from_base<'a>(
|
||||
fn chomp_number_base<'a>(
|
||||
base: Base,
|
||||
first_ch: char,
|
||||
bytes_parsed: usize,
|
||||
arena: &'a Bump,
|
||||
is_negative: bool,
|
||||
bytes: &'a [u8],
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, SyntaxError<'a>> {
|
||||
let is_negative = first_ch == '-';
|
||||
let bytes = if is_negative {
|
||||
&state.bytes[3..bytes_parsed]
|
||||
} else {
|
||||
&state.bytes[2..bytes_parsed]
|
||||
};
|
||||
) -> ParseResult<'a, Expr<'a>, Number> {
|
||||
let (_is_float, mut chomped) = chomp_number(bytes);
|
||||
chomped += 2 + (is_negative as usize);
|
||||
|
||||
match parse_utf8(bytes) {
|
||||
Ok(string) => Ok((
|
||||
Progress::from_consumed(bytes_parsed),
|
||||
Expr::NonBase10Int {
|
||||
is_negative,
|
||||
string,
|
||||
base,
|
||||
},
|
||||
state.advance_without_indenting(arena, bytes_parsed)?,
|
||||
)),
|
||||
Err(reason) => state.fail(arena, Progress::from_consumed(bytes_parsed), reason),
|
||||
match parse_utf8(&bytes[0..chomped]) {
|
||||
Ok(string) => match state.advance_without_indenting(chomped) {
|
||||
Ok(new) => {
|
||||
// all is well
|
||||
Ok((
|
||||
Progress::MadeProgress,
|
||||
Expr::NonBase10Int {
|
||||
is_negative,
|
||||
string,
|
||||
base: Base::Binary,
|
||||
},
|
||||
new,
|
||||
))
|
||||
}
|
||||
Err((_, SyntaxError::LineTooLong(_), new)) => {
|
||||
// the only error we care about in this context
|
||||
Err((Progress::MadeProgress, Number::LineTooLong, new))
|
||||
}
|
||||
Err(_) => unreachable!("we know advancing will succeed if there is space on the line"),
|
||||
},
|
||||
|
||||
Err(_) => unreachable!("no invalid utf8 could have been chomped"),
|
||||
}
|
||||
}
|
||||
|
||||
fn chomp_number_dec<'a>(
|
||||
is_negative: bool,
|
||||
bytes: &'a [u8],
|
||||
state: State<'a>,
|
||||
) -> ParseResult<'a, Expr<'a>, Number> {
|
||||
let (is_float, mut chomped) = chomp_number(bytes);
|
||||
chomped += is_negative as usize;
|
||||
|
||||
let string = unsafe { from_utf8_unchecked(&state.bytes[0..chomped]) };
|
||||
|
||||
match state.advance_without_indenting(chomped) {
|
||||
Ok(new) => {
|
||||
// all is well
|
||||
Ok((
|
||||
Progress::MadeProgress,
|
||||
if is_float {
|
||||
Expr::Float(string)
|
||||
} else {
|
||||
Expr::Num(string)
|
||||
},
|
||||
new,
|
||||
))
|
||||
}
|
||||
Err((_, SyntaxError::LineTooLong(_), new)) => {
|
||||
// the only error we care about in this context
|
||||
Err((Progress::MadeProgress, Number::LineTooLong, new))
|
||||
}
|
||||
Err(_) => unreachable!("we know advancing will succeed if there is space on the line"),
|
||||
}
|
||||
}
|
||||
|
||||
fn chomp_number<'a>(mut bytes: &'a [u8]) -> (bool, usize) {
|
||||
let start_bytes_len = bytes.len();
|
||||
let mut is_float = false;
|
||||
|
||||
while let Some(byte) = bytes.get(0) {
|
||||
match byte {
|
||||
b'.' => {
|
||||
// skip, fix multiple `.`s in canonicalization
|
||||
is_float = true;
|
||||
bytes = &bytes[1..];
|
||||
}
|
||||
b'_' => {
|
||||
// skip
|
||||
bytes = &bytes[1..];
|
||||
}
|
||||
_ if byte.is_ascii_digit() || byte.is_ascii_alphabetic() => {
|
||||
// valid digits (alphabetic in hex digits, and the `e` in `12e26` scientific notation
|
||||
bytes = &bytes[1..];
|
||||
}
|
||||
_ => {
|
||||
// not a valid digit; we're done
|
||||
return (is_float, start_bytes_len - bytes.len());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the above loop exits, we must be dealing with an empty slice
|
||||
// therefore we parsed all of the bytes in the input
|
||||
(is_float, start_bytes_len)
|
||||
}
|
||||
|
|
|
@ -135,15 +135,13 @@ impl<'a> State<'a> {
|
|||
/// they weren't eligible to indent anyway.
|
||||
pub fn advance_without_indenting(
|
||||
self,
|
||||
arena: &'a Bump,
|
||||
quantity: usize,
|
||||
) -> Result<Self, (Progress, SyntaxError<'a>, Self)> {
|
||||
self.advance_without_indenting_e(arena, quantity, bad_input_to_syntax_error)
|
||||
self.advance_without_indenting_e(quantity, bad_input_to_syntax_error)
|
||||
}
|
||||
|
||||
pub fn advance_without_indenting_e<TE, E>(
|
||||
self,
|
||||
arena: &'a Bump,
|
||||
quantity: usize,
|
||||
to_error: TE,
|
||||
) -> Result<Self, (Progress, E, Self)>
|
||||
|
@ -160,7 +158,7 @@ impl<'a> State<'a> {
|
|||
..self
|
||||
})
|
||||
}
|
||||
_ => Err(line_too_long_e(arena, self.clone(), to_error)),
|
||||
_ => Err(line_too_long_e(self.clone(), to_error)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -214,7 +212,7 @@ impl<'a> State<'a> {
|
|||
original_len: self.original_len,
|
||||
})
|
||||
}
|
||||
_ => Err(line_too_long_e(arena, self.clone(), to_error)),
|
||||
_ => Err(line_too_long_e(self.clone(), to_error)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -403,8 +401,8 @@ pub enum EExpr<'a> {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Number {
|
||||
NumberEnd,
|
||||
NumberDot(i64),
|
||||
End,
|
||||
LineTooLong,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -878,9 +876,7 @@ pub fn unexpected_eof<'a>(
|
|||
state: State<'a>,
|
||||
chars_consumed: usize,
|
||||
) -> (Progress, SyntaxError<'a>, State<'a>) {
|
||||
checked_unexpected(arena, state, chars_consumed, |region| {
|
||||
SyntaxError::Eof(region)
|
||||
})
|
||||
checked_unexpected(state, chars_consumed, |region| SyntaxError::Eof(region))
|
||||
}
|
||||
|
||||
pub fn unexpected<'a>(
|
||||
|
@ -891,7 +887,7 @@ pub fn unexpected<'a>(
|
|||
) -> (Progress, SyntaxError<'a>, 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| {
|
||||
checked_unexpected(state, chars_consumed, |region| {
|
||||
SyntaxError::Unexpected(region)
|
||||
})
|
||||
}
|
||||
|
@ -901,7 +897,6 @@ pub fn unexpected<'a>(
|
|||
/// If maximum line length was exceeded, return a Problem indicating as much.
|
||||
#[inline(always)]
|
||||
fn checked_unexpected<'a, F>(
|
||||
arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
chars_consumed: usize,
|
||||
problem_from_region: F,
|
||||
|
@ -926,17 +921,13 @@ where
|
|||
(Progress::NoProgress, problem_from_region(region), state)
|
||||
}
|
||||
_ => {
|
||||
let (_progress, fail, state) = line_too_long(arena, state);
|
||||
let (_progress, fail, state) = line_too_long(state);
|
||||
(Progress::NoProgress, fail, state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn line_too_long_e<'a, TE, E>(
|
||||
_arena: &'a Bump,
|
||||
state: State<'a>,
|
||||
to_error: TE,
|
||||
) -> (Progress, E, State<'a>)
|
||||
fn line_too_long_e<'a, TE, E>(state: State<'a>, to_error: TE) -> (Progress, E, State<'a>)
|
||||
where
|
||||
TE: Fn(BadInputError, Row, Col) -> E,
|
||||
{
|
||||
|
@ -961,8 +952,8 @@ where
|
|||
(Progress::NoProgress, problem, state)
|
||||
}
|
||||
|
||||
fn line_too_long<'a>(arena: &'a Bump, state: State<'a>) -> (Progress, SyntaxError<'a>, State<'a>) {
|
||||
line_too_long_e(arena, state, |_, line, _| SyntaxError::LineTooLong(line))
|
||||
fn line_too_long<'a>(state: State<'a>) -> (Progress, SyntaxError<'a>, State<'a>) {
|
||||
line_too_long_e(state, |_, line, _| SyntaxError::LineTooLong(line))
|
||||
}
|
||||
|
||||
/// A single ASCII char that isn't a newline.
|
||||
|
@ -975,7 +966,7 @@ pub fn ascii_char<'a>(expected: u8) -> impl Parser<'a, (), SyntaxError<'a>> {
|
|||
Some(&actual) if expected == actual => Ok((
|
||||
Progress::MadeProgress,
|
||||
(),
|
||||
state.advance_without_indenting(arena, 1)?,
|
||||
state.advance_without_indenting(1)?,
|
||||
)),
|
||||
Some(_) => Err(unexpected(arena, 0, Attempting::Keyword, state)),
|
||||
_ => Err(unexpected_eof(arena, state, 0)),
|
||||
|
@ -1006,7 +997,7 @@ pub fn ascii_hex_digits<'a>() -> impl Parser<'a, &'a str, SyntaxError<'a>> {
|
|||
// We didn't find any hex digits!
|
||||
return Err(unexpected(arena, 0, Attempting::Keyword, state));
|
||||
} else {
|
||||
let state = state.advance_without_indenting(arena, buf.len())?;
|
||||
let state = state.advance_without_indenting(buf.len())?;
|
||||
|
||||
return Ok((Progress::MadeProgress, buf.into_bump_str(), state));
|
||||
}
|
||||
|
@ -1150,7 +1141,7 @@ pub fn ascii_string<'a>(keyword: &'static str) -> impl Parser<'a, (), SyntaxErro
|
|||
Ok((
|
||||
Progress::MadeProgress,
|
||||
(),
|
||||
state.advance_without_indenting(arena, len)?,
|
||||
state.advance_without_indenting(len)?,
|
||||
))
|
||||
} else {
|
||||
let (_, fail, state) = unexpected(arena, len, Attempting::Keyword, state);
|
||||
|
|
|
@ -27,7 +27,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, SyntaxError<'a>> {
|
|||
}
|
||||
|
||||
// Advance past the opening quotation mark.
|
||||
state = state.advance_without_indenting(arena, 1)?;
|
||||
state = state.advance_without_indenting(1)?;
|
||||
|
||||
// At the parsing stage we keep the entire raw string, because the formatter
|
||||
// needs the raw string. (For example, so it can "remember" whether you
|
||||
|
@ -44,7 +44,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, SyntaxError<'a>> {
|
|||
segments.push(StrSegment::EscapedChar($ch));
|
||||
|
||||
// Advance past the segment we just added
|
||||
state = state.advance_without_indenting(arena, segment_parsed_bytes)?;
|
||||
state = state.advance_without_indenting(segment_parsed_bytes)?;
|
||||
|
||||
// Reset the segment
|
||||
segment_parsed_bytes = 0;
|
||||
|
@ -63,7 +63,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, SyntaxError<'a>> {
|
|||
|
||||
match parse_utf8(string_bytes) {
|
||||
Ok(string) => {
|
||||
state = state.advance_without_indenting(arena, string.len())?;
|
||||
state = state.advance_without_indenting(string.len())?;
|
||||
|
||||
segments.push($transform(string));
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, SyntaxError<'a>> {
|
|||
return Ok((
|
||||
MadeProgress,
|
||||
PlainLine(""),
|
||||
state.advance_without_indenting(arena, 1)?,
|
||||
state.advance_without_indenting(1)?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -128,11 +128,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, SyntaxError<'a>> {
|
|||
};
|
||||
|
||||
// Advance the state 1 to account for the closing `"`
|
||||
return Ok((
|
||||
MadeProgress,
|
||||
expr,
|
||||
state.advance_without_indenting(arena, 1)?,
|
||||
));
|
||||
return Ok((MadeProgress, expr, state.advance_without_indenting(1)?));
|
||||
};
|
||||
}
|
||||
b'\n' => {
|
||||
|
@ -163,7 +159,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, SyntaxError<'a>> {
|
|||
match bytes.next() {
|
||||
Some(b'(') => {
|
||||
// Advance past the `\(` before using the expr parser
|
||||
state = state.advance_without_indenting(arena, 2)?;
|
||||
state = state.advance_without_indenting(2)?;
|
||||
|
||||
let original_byte_count = state.bytes.len();
|
||||
|
||||
|
@ -188,7 +184,7 @@ pub fn parse<'a>() -> impl Parser<'a, StrLiteral<'a>, SyntaxError<'a>> {
|
|||
}
|
||||
Some(b'u') => {
|
||||
// Advance past the `\u` before using the expr parser
|
||||
state = state.advance_without_indenting(arena, 2)?;
|
||||
state = state.advance_without_indenting(2)?;
|
||||
|
||||
let original_byte_count = state.bytes.len();
|
||||
|
||||
|
|
|
@ -208,19 +208,19 @@ where
|
|||
buf.push('@');
|
||||
buf.push(second_letter);
|
||||
|
||||
state = state
|
||||
.advance_without_indenting(arena, total_parsed)
|
||||
.map_err(|(progress, _, state)| {
|
||||
state = state.advance_without_indenting(total_parsed).map_err(
|
||||
|(progress, _, state)| {
|
||||
(progress, to_problem(state.line, state.column), state)
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
// important for error messages
|
||||
state = state
|
||||
.advance_without_indenting(arena, bytes_parsed)
|
||||
.map_err(|(progress, _, state)| {
|
||||
state = state.advance_without_indenting(bytes_parsed).map_err(
|
||||
|(progress, _, state)| {
|
||||
(progress, to_problem(state.line, state.column), state)
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
|
||||
let row = state.line;
|
||||
let col = state.column;
|
||||
|
@ -234,11 +234,11 @@ where
|
|||
|
||||
buf.push(first_letter);
|
||||
|
||||
state = state
|
||||
.advance_without_indenting(arena, bytes_parsed)
|
||||
.map_err(|(progress, _, state)| {
|
||||
state = state.advance_without_indenting(bytes_parsed).map_err(
|
||||
|(progress, _, state)| {
|
||||
(progress, to_problem(state.line, state.column), state)
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
@ -265,11 +265,11 @@ where
|
|||
if ch.is_alphabetic() || ch.is_ascii_digit() {
|
||||
buf.push(ch);
|
||||
|
||||
state = state
|
||||
.advance_without_indenting(arena, bytes_parsed)
|
||||
.map_err(|(progress, _, state)| {
|
||||
state = state.advance_without_indenting(bytes_parsed).map_err(
|
||||
|(progress, _, state)| {
|
||||
(progress, to_problem(state.line, state.column), state)
|
||||
})?;
|
||||
},
|
||||
)?;
|
||||
} else {
|
||||
// This is the end of the field. We're done!
|
||||
break;
|
||||
|
@ -528,7 +528,7 @@ fn parse_concrete_type<'a>(
|
|||
return Err((NoProgress, problem, state));
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting_e(arena, bytes_parsed, TApply::Space)?;
|
||||
state = state.advance_without_indenting_e(bytes_parsed, TApply::Space)?;
|
||||
}
|
||||
Err(reason) => return Err((NoProgress, reason, state)),
|
||||
}
|
||||
|
@ -582,7 +582,7 @@ fn parse_concrete_type<'a>(
|
|||
break;
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting_e(arena, bytes_parsed, TApply::Space)?;
|
||||
state = state.advance_without_indenting_e(bytes_parsed, TApply::Space)?;
|
||||
}
|
||||
Err(reason) => {
|
||||
return Err((MadeProgress, reason, state));
|
||||
|
@ -634,7 +634,7 @@ fn parse_type_variable<'a>(
|
|||
));
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting_e(arena, bytes_parsed, TVariable::Space)?;
|
||||
state = state.advance_without_indenting_e(bytes_parsed, TVariable::Space)?;
|
||||
}
|
||||
Err(reason) => return Err((NoProgress, reason, state)),
|
||||
}
|
||||
|
@ -653,7 +653,7 @@ fn parse_type_variable<'a>(
|
|||
break;
|
||||
}
|
||||
|
||||
state = state.advance_without_indenting_e(arena, bytes_parsed, TVariable::Space)?;
|
||||
state = state.advance_without_indenting_e(bytes_parsed, TVariable::Space)?;
|
||||
}
|
||||
Err(reason) => {
|
||||
return state.fail(arena, MadeProgress, reason);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue