better indentation errors (use original location for error)

This commit is contained in:
Folkert 2021-02-12 13:47:56 +01:00
parent 21efa8cd71
commit 19d3e43f09
5 changed files with 241 additions and 38 deletions

View file

@ -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))?;
}

View file

@ -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,

View file

@ -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)?;