mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
better indentation errors (use original location for error)
This commit is contained in:
parent
21efa8cd71
commit
19d3e43f09
5 changed files with 241 additions and 38 deletions
|
@ -444,6 +444,9 @@ where
|
|||
let mut state = state;
|
||||
let mut any_newlines = false;
|
||||
|
||||
let start_row = original_state.line;
|
||||
let start_col = original_state.column;
|
||||
|
||||
let start_bytes_len = state.bytes.len();
|
||||
|
||||
while !state.bytes.is_empty() {
|
||||
|
@ -491,7 +494,13 @@ where
|
|||
let progress =
|
||||
Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
state = state
|
||||
.check_indent_e(arena, min_indent, indent_problem)
|
||||
.check_indent_e(
|
||||
arena,
|
||||
min_indent,
|
||||
indent_problem,
|
||||
start_row,
|
||||
start_col,
|
||||
)
|
||||
.map_err(|(fail, _)| {
|
||||
(progress, fail, original_state.clone())
|
||||
})?
|
||||
|
@ -523,7 +532,13 @@ where
|
|||
);
|
||||
if any_newlines {
|
||||
state = state
|
||||
.check_indent_e(arena, min_indent, indent_problem)
|
||||
.check_indent_e(
|
||||
arena,
|
||||
min_indent,
|
||||
indent_problem,
|
||||
start_row,
|
||||
start_col,
|
||||
)
|
||||
.map_err(|(fail, _)| {
|
||||
(progress, fail, original_state.clone())
|
||||
})?;
|
||||
|
@ -692,7 +707,13 @@ where
|
|||
progress,
|
||||
space_slice,
|
||||
state
|
||||
.check_indent_e(arena, min_indent, indent_problem)
|
||||
.check_indent_e(
|
||||
arena,
|
||||
min_indent,
|
||||
indent_problem,
|
||||
start_row,
|
||||
start_col,
|
||||
)
|
||||
.map_err(|(fail, _)| (progress, fail, original_state))?,
|
||||
));
|
||||
}
|
||||
|
@ -720,7 +741,7 @@ where
|
|||
let progress = Progress::from_lengths(start_bytes_len, state.bytes.len());
|
||||
if any_newlines {
|
||||
state = state
|
||||
.check_indent_e(arena, min_indent, indent_problem)
|
||||
.check_indent_e(arena, min_indent, indent_problem, start_row, start_col)
|
||||
.map_err(|(fail, _)| (progress, fail, original_state))?;
|
||||
}
|
||||
|
||||
|
|
|
@ -72,12 +72,14 @@ impl<'a> State<'a> {
|
|||
_arena: &'a Bump,
|
||||
min_indent: u16,
|
||||
to_error: TE,
|
||||
row: Row,
|
||||
col: Col,
|
||||
) -> Result<Self, (E, Self)>
|
||||
where
|
||||
TE: Fn(Row, Col) -> E,
|
||||
{
|
||||
if self.indent_col < min_indent {
|
||||
Err((to_error(self.line, self.column), self))
|
||||
Err((to_error(row, col), self))
|
||||
} else {
|
||||
Ok(self)
|
||||
}
|
||||
|
@ -378,6 +380,7 @@ pub enum Type<'a> {
|
|||
TStart(Row, Col),
|
||||
TEnd(Row, Col),
|
||||
TSpace(BadInputError, Row, Col),
|
||||
TFunctionArgument(Row, Col),
|
||||
///
|
||||
TIndentStart(Row, Col),
|
||||
TIndentEnd(Row, Col),
|
||||
|
@ -1496,6 +1499,31 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub fn check_indent<'a, TE, E>(min_indent: u16, to_problem: TE) -> impl Parser<'a, (), E>
|
||||
where
|
||||
TE: Fn(Row, Col) -> E,
|
||||
E: 'a,
|
||||
{
|
||||
move |_arena, state: State<'a>| {
|
||||
dbg!(state.indent_col, min_indent);
|
||||
if state.indent_col < min_indent {
|
||||
Err((NoProgress, to_problem(state.line, state.column), state))
|
||||
} else {
|
||||
Ok((NoProgress, (), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! word1_check_indent {
|
||||
($word:expr, $word_problem:expr, $min_indent:expr, $indent_problem:expr) => {
|
||||
and!(
|
||||
word1($word, $word_problem),
|
||||
crate::parser::check_indent($min_indent, $indent_problem)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn in_context<'a, AddContext, P1, P2, Start, A, X, Y>(
|
||||
add_context: AddContext,
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
use crate::ast::{AssignedField, CommentOrNewline, Tag, TypeAnnotation};
|
||||
use crate::blankspace::{
|
||||
space0, space0_around, space0_around_e, space0_before, space0_before_e, space0_e,
|
||||
};
|
||||
use crate::blankspace::{space0_around_e, space0_before_e, space0_e};
|
||||
use crate::expr::{global_tag, private_tag};
|
||||
use crate::ident::join_module_parts;
|
||||
use crate::keyword;
|
||||
use crate::parser::{
|
||||
allocated, ascii_char, ascii_string, backtrackable, not_e, optional, peek_utf8_char_e,
|
||||
specialize, specialize_ref, word1, word2, BadInputError, Col, Either, ParseResult, Parser,
|
||||
allocated, backtrackable, not_e, optional, peek_utf8_char_e, specialize, specialize_ref, word1,
|
||||
word2, BadInputError, Either, ParseResult, Parser,
|
||||
Progress::{self, *},
|
||||
Row, State, SyntaxError, TApply, TInParens, TRecord, TTagUnion, TVariable, Type,
|
||||
State, SyntaxError, TApply, TInParens, TRecord, TTagUnion, TVariable, Type,
|
||||
};
|
||||
use bumpalo::collections::string::String;
|
||||
use bumpalo::collections::vec::Vec;
|
||||
|
@ -54,20 +52,6 @@ fn tag_union_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TT
|
|||
}
|
||||
}
|
||||
|
||||
fn check_indent<'a, TE, E>(min_indent: u16, to_problem: TE) -> impl Parser<'a, (), E>
|
||||
where
|
||||
TE: Fn(Row, Col) -> E,
|
||||
E: 'a,
|
||||
{
|
||||
move |_arena, state: State<'a>| {
|
||||
if state.indent_col < min_indent {
|
||||
Err((NoProgress, to_problem(state.line, state.column), state))
|
||||
} else {
|
||||
Ok((NoProgress, (), state))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn term<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>>, SyntaxError<'a>> {
|
||||
specialize(|x, _, _| SyntaxError::Type(x), term_help(min_indent))
|
||||
|
@ -247,7 +231,7 @@ fn record_type_field<'a>(
|
|||
debug_assert_eq!(progress, MadeProgress);
|
||||
|
||||
let (_, spaces, state) =
|
||||
space0_e(min_indent, TRecord::Space, TRecord::IndentEnd).parse(arena, state)?;
|
||||
debug!(space0_e(min_indent, TRecord::Space, TRecord::IndentEnd)).parse(arena, state)?;
|
||||
|
||||
// Having a value is optional; both `{ email }` and `{ email: blah }` work.
|
||||
// (This is true in both literals and types.)
|
||||
|
@ -312,9 +296,11 @@ fn record_type<'a>(min_indent: u16) -> impl Parser<'a, TypeAnnotation<'a>, TReco
|
|||
|
||||
move |arena, state| {
|
||||
let (_, (fields, final_comments), state) = collection_trailing_sep_e!(
|
||||
// word1_check_indent!(b'{', TRecord::Open, min_indent, TRecord::IndentOpen),
|
||||
word1(b'{', TRecord::Open),
|
||||
loc!(record_type_field(min_indent)),
|
||||
word1(b',', TRecord::End),
|
||||
// word1_check_indent!(b'}', TRecord::End, min_indent, TRecord::IndentEnd),
|
||||
word1(b'}', TRecord::End),
|
||||
min_indent,
|
||||
TRecord::Open,
|
||||
|
@ -377,13 +363,20 @@ fn expression<'a>(min_indent: u16) -> impl Parser<'a, Located<TypeAnnotation<'a>
|
|||
.parse(arena, state)?;
|
||||
|
||||
let (p2, rest, state) = zero_or_more!(skip_first!(
|
||||
word1(b',', Type::TStart),
|
||||
space0_around_e(
|
||||
term_help(min_indent),
|
||||
min_indent,
|
||||
Type::TSpace,
|
||||
Type::TIndentStart
|
||||
)
|
||||
word1(b',', Type::TFunctionArgument),
|
||||
one_of![
|
||||
space0_around_e(
|
||||
term_help(min_indent),
|
||||
min_indent,
|
||||
Type::TSpace,
|
||||
Type::TIndentStart
|
||||
),
|
||||
|_, state: State<'a>| Err((
|
||||
NoProgress,
|
||||
Type::TFunctionArgument(state.line, state.column),
|
||||
state
|
||||
))
|
||||
]
|
||||
))
|
||||
.parse(arena, state)?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue