Don't allow tabs

This commit is contained in:
imaqtkatt 2024-06-10 10:04:09 -03:00
parent d44bb151f0
commit 168892ddb7
4 changed files with 80 additions and 64 deletions

View File

@ -65,7 +65,7 @@ impl<'a> TermParser<'a> {
pub fn parse_book(&mut self, default_book: Book, builtin: bool) -> ParseResult<Book> {
let mut book = default_book;
let mut indent = self.advance_newlines();
let mut indent = self.advance_newlines()?;
let mut last_rule = None;
while !self.is_eof() {
let ini_idx = *self.index();
@ -115,7 +115,7 @@ impl<'a> TermParser<'a> {
let (nam, adt) = self.parse_datatype(builtin)?;
let end_idx = *self.index();
self.with_ctx(book.add_adt(nam, adt), ini_idx, end_idx)?;
indent = self.advance_newlines();
indent = self.advance_newlines()?;
last_rule = None;
continue;
}
@ -145,7 +145,7 @@ impl<'a> TermParser<'a> {
// Adding the first rule of a new definition
book.defs.insert(name.clone(), Definition { name: name.clone(), rules: vec![rule], builtin });
}
indent = self.advance_newlines();
indent = self.advance_newlines()?;
last_rule = Some(name);
}
@ -864,34 +864,38 @@ pub trait ParserCommons<'a>: Parser<'a> {
}
fn consume_new_line(&mut self) -> ParseResult<()> {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.try_consume_exactly("\r");
self.labelled(|p| p.consume_exactly("\n"), "newline")
}
/// Skips trivia, returns the number of trivia characters skipped in the last line.
fn advance_newlines(&mut self) -> Indent {
fn advance_newlines(&mut self) -> ParseResult<Indent> {
loop {
let num_spaces = self.advance_trivia_inline();
let num_spaces = self.advance_trivia_inline()?;
if self.peek_one() == Some('\r') {
self.advance_one();
}
if self.peek_one() == Some('\n') {
self.advance_one();
} else if self.is_eof() {
return Indent::Eof;
return Ok(Indent::Eof);
} else {
return Indent::Val(num_spaces);
return Ok(Indent::Val(num_spaces));
}
}
}
/// Advances the parser to the next non-trivia character in the same line.
/// Returns how many characters were advanced.
fn advance_trivia_inline(&mut self) -> isize {
fn advance_trivia_inline(&mut self) -> ParseResult<isize> {
let mut char_count = 0;
while let Some(c) = self.peek_one() {
if " \t".contains(c) {
if c == '\t' {
let idx = *self.index();
return self.with_ctx(Err("Tabs are not accepted for indentation.".to_string()), idx, idx);
}
if " ".contains(c) {
self.advance_one();
char_count += 1;
continue;
@ -909,12 +913,13 @@ pub trait ParserCommons<'a>: Parser<'a> {
}
break;
}
char_count
Ok(char_count)
}
/// Skips until the next non-trivia character in the same line.
fn skip_trivia_inline(&mut self) {
self.advance_trivia_inline();
fn skip_trivia_inline(&mut self) -> ParseResult<()> {
self.advance_trivia_inline()?;
Ok(())
}
fn expected_spanned<T>(&mut self, exp: &str, ini_idx: usize, end_idx: usize) -> ParseResult<T> {

View File

@ -79,7 +79,7 @@ impl<'a> PyParser<'a> {
///
fn parse_simple_expr(&mut self, inline: bool) -> ParseResult<Expr> {
if inline {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
} else {
self.skip_trivia();
}
@ -133,7 +133,7 @@ impl<'a> PyParser<'a> {
// postfixes
if inline {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
} else {
self.skip_trivia();
}
@ -335,7 +335,7 @@ impl<'a> PyParser<'a> {
}
if inline {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
} else {
self.skip_trivia();
}
@ -373,7 +373,7 @@ impl<'a> PyParser<'a> {
fn parse_infix_expr(&mut self, prec: usize, inline: bool) -> ParseResult<Expr> {
maybe_grow(|| {
if inline {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
} else {
self.skip_trivia();
}
@ -382,7 +382,7 @@ impl<'a> PyParser<'a> {
}
let mut lhs = self.parse_infix_expr(prec + 1, inline)?;
if inline {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
} else {
self.skip_trivia();
}
@ -391,7 +391,7 @@ impl<'a> PyParser<'a> {
self.try_parse_oper().unwrap();
let rhs = self.parse_infix_expr(prec + 1, inline)?;
lhs = Expr::Opr { op, lhs: Box::new(lhs), rhs: Box::new(rhs) };
self.skip_trivia_inline();
self.skip_trivia_inline()?;
} else {
break;
}
@ -401,7 +401,7 @@ impl<'a> PyParser<'a> {
}
fn consume_indent_at_most(&mut self, expected: Indent) -> ParseResult<Indent> {
let got = self.advance_newlines();
let got = self.advance_newlines()?;
match (expected, got) {
(_, Indent::Eof) => Ok(Indent::Eof),
(Indent::Val(expected), Indent::Val(got)) if got <= expected => Ok(Indent::Val(got)),
@ -410,7 +410,7 @@ impl<'a> PyParser<'a> {
}
fn consume_indent_exactly(&mut self, expected: Indent) -> ParseResult<()> {
let got = self.advance_newlines();
let got = self.advance_newlines()?;
match (expected, got) {
(Indent::Eof, Indent::Eof) => Ok(()),
(Indent::Val(expected), Indent::Val(got)) if got == expected => Ok(()),
@ -453,18 +453,18 @@ impl<'a> PyParser<'a> {
let ini_idx = *self.index();
let pat = self.parse_assign_pattern()?;
let end_idx = *self.index();
self.skip_trivia_inline();
self.skip_trivia_inline()?;
// Assignment
if self.starts_with("=") {
self.advance_one();
let val = self.parse_expr(true)?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.try_consume_exactly(";");
if !self.is_eof() {
self.consume_new_line()?;
}
let nxt_indent = self.advance_newlines();
let nxt_indent = self.advance_newlines()?;
if nxt_indent == *indent {
let (nxt, nxt_indent) = self.parse_statement(indent)?;
let stmt = Stmt::Assign { pat, val: Box::new(val), nxt: Some(Box::new(nxt)) };
@ -478,7 +478,7 @@ impl<'a> PyParser<'a> {
if self.starts_with("<-") {
self.consume("<-")?;
let val = self.parse_expr(true)?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.try_consume_exactly(";");
self.consume_indent_exactly(*indent)?;
let (nxt, nxt_indent) = self.parse_statement(indent)?;
@ -494,7 +494,7 @@ impl<'a> PyParser<'a> {
}
if let Some(op) = self.parse_in_place_op()? {
let val = self.parse_expr(true)?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.try_consume_exactly(";");
self.consume_indent_exactly(*indent)?;
let (nxt, nxt_indent) = self.parse_statement(indent)?;
@ -506,7 +506,7 @@ impl<'a> PyParser<'a> {
}
fn parse_in_place_op(&mut self) -> ParseResult<Option<InPlaceOp>> {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let op = if self.starts_with("+=") {
self.consume("+=")?;
Some(InPlaceOp::Add)
@ -539,18 +539,18 @@ impl<'a> PyParser<'a> {
fn parse_return(&mut self) -> ParseResult<(Stmt, Indent)> {
let term = self.parse_expr(true)?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.try_consume_exactly(";");
if !self.is_eof() {
self.consume_new_line()?;
}
let indent = self.advance_newlines();
let indent = self.advance_newlines()?;
Ok((Stmt::Return { term: Box::new(term) }, indent))
}
fn parse_if(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
let cond = self.parse_expr(true)?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
indent.enter_level();
@ -564,7 +564,7 @@ impl<'a> PyParser<'a> {
let mut elifs = Vec::new();
while self.try_parse_keyword("elif") {
let cond = self.parse_expr(true)?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
indent.enter_level();
self.consume_indent_exactly(*indent)?;
@ -577,7 +577,7 @@ impl<'a> PyParser<'a> {
elifs.push((cond, then));
}
self.parse_keyword("else")?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
indent.enter_level();
@ -609,7 +609,7 @@ impl<'a> PyParser<'a> {
fn parse_match(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
let (bnd, arg) = self.parse_match_arg()?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let (with_bnd, with_arg) = self.parse_with_clause()?;
self.consume_new_line()?;
indent.enter_level();
@ -638,7 +638,7 @@ impl<'a> PyParser<'a> {
let arg = self.parse_expr(true)?;
let end_idx = *self.index();
self.skip_trivia_inline();
self.skip_trivia_inline()?;
match (arg, self.starts_with("=")) {
(Expr::Var { nam }, true) => {
self.advance_one();
@ -651,7 +651,7 @@ impl<'a> PyParser<'a> {
}
fn parse_with_clause(&mut self) -> ParseResult<(Vec<Option<Name>>, Vec<Expr>)> {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let res = if self.try_parse_keyword("with") {
self.list_like(|p| p.parse_with_arg(), "", ":", ",", true, 1)?.into_iter().unzip()
} else {
@ -663,7 +663,7 @@ impl<'a> PyParser<'a> {
fn parse_with_arg(&mut self) -> ParseResult<(Option<Name>, Expr)> {
let bind = self.parse_bend_name()?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
if self.try_consume("=") {
let arg = self.parse_expr(false)?;
Ok((Some(bind), arg))
@ -674,14 +674,14 @@ impl<'a> PyParser<'a> {
fn parse_match_case(&mut self, indent: &mut Indent) -> ParseResult<(MatchArm, Indent)> {
self.parse_keyword("case")?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let pat = if self.try_consume_exactly("_") {
None
} else {
let nam = self.labelled(|p| p.parse_bend_name(), "name or '_'")?;
Some(nam)
};
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
self.consume_new_line()?;
indent.enter_level();
@ -696,7 +696,7 @@ impl<'a> PyParser<'a> {
fn parse_switch(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
let (bnd, arg) = self.parse_match_arg()?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let (with_bnd, with_arg) = self.parse_with_clause()?;
indent.enter_level();
@ -741,7 +741,7 @@ impl<'a> PyParser<'a> {
fn parse_switch_case(&mut self, indent: &mut Indent) -> ParseResult<(Option<u32>, Stmt, Indent)> {
self.parse_keyword("case")?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let case = if let Some(c) = self.peek_one() {
match c {
'_' => {
@ -755,7 +755,7 @@ impl<'a> PyParser<'a> {
return self.expected("number or '_'")?;
};
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
self.consume_new_line()?;
indent.enter_level();
@ -772,7 +772,7 @@ impl<'a> PyParser<'a> {
fn parse_fold(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
// Actually identical to match, except the return
let (bind, arg) = self.parse_match_arg()?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let (with_bnd, with_arg) = self.parse_with_clause()?;
self.consume_new_line()?;
indent.enter_level();
@ -811,7 +811,7 @@ impl<'a> PyParser<'a> {
self.consume_indent_exactly(*indent)?;
self.parse_keyword("when")?;
let cond = self.parse_expr(true)?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
self.consume_new_line()?;
indent.enter_level();
@ -824,7 +824,7 @@ impl<'a> PyParser<'a> {
return self.expected_indent(*indent, nxt_indent);
}
self.parse_keyword("else")?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
self.consume_new_line()?;
indent.enter_level();
@ -862,9 +862,9 @@ impl<'a> PyParser<'a> {
/// <bod>
/// <nxt>?
fn parse_with(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let typ = self.parse_bend_name()?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
self.consume_new_line()?;
indent.enter_level();
@ -911,7 +911,7 @@ impl<'a> PyParser<'a> {
// Chn pattern
if self.starts_with("$") {
self.advance_one();
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let nam = self.parse_bend_name()?;
return Ok(AssignPattern::Chn(nam));
}
@ -932,13 +932,13 @@ impl<'a> PyParser<'a> {
/// "open" {typ} ":" {var} ";"? {nxt}
fn parse_open(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let typ = self.labelled(|p| p.parse_bend_name(), "type name")?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let var = self.labelled(|p| p.parse_bend_name(), "variable name")?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.try_consume_exactly(";");
self.consume_new_line()?;
self.consume_indent_exactly(*indent)?;
@ -948,13 +948,13 @@ impl<'a> PyParser<'a> {
}
fn parse_use(&mut self, indent: &mut Indent) -> ParseResult<(Stmt, Indent)> {
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let nam = self.parse_bend_name()?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly("=")?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let bod = self.parse_expr(true)?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.try_consume_exactly(";");
self.consume_new_line()?;
self.consume_indent_exactly(*indent)?;
@ -970,15 +970,15 @@ impl<'a> PyParser<'a> {
return self.with_ctx(Err(msg), idx, idx + 1);
}
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let name = self.parse_top_level_name()?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let params = if self.starts_with("(") {
self.list_like(|p| p.parse_bend_name(), "(", ")", ",", true, 0)?
} else {
vec![]
};
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
self.consume_new_line()?;
indent.enter_level();
@ -998,9 +998,9 @@ impl<'a> PyParser<'a> {
return self.with_ctx(Err(msg), idx, idx + 1);
}
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let typ_name = self.parse_top_level_name()?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
self.consume_exactly(":")?;
self.consume_new_line()?;
indent.enter_level();
@ -1025,7 +1025,7 @@ impl<'a> PyParser<'a> {
let ctr_name = self.parse_top_level_name()?;
let ctr_name = Name::new(format!("{typ_name}/{ctr_name}"));
let mut fields = Vec::new();
self.skip_trivia_inline();
self.skip_trivia_inline()?;
if self.starts_with("{") {
fields = self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)?;
}
@ -1039,9 +1039,9 @@ impl<'a> PyParser<'a> {
return self.with_ctx(Err(msg), idx, idx + 1);
}
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let name = self.parse_top_level_name()?;
self.skip_trivia_inline();
self.skip_trivia_inline()?;
let fields = if self.starts_with("{") {
self.list_like(|p| p.parse_variant_field(), "{", "}", ",", true, 0)?
} else {
@ -1050,7 +1050,7 @@ impl<'a> PyParser<'a> {
if !self.is_eof() {
self.consume_new_line()?;
}
let nxt_indent = self.advance_newlines();
let nxt_indent = self.advance_newlines()?;
Ok((Variant { name, fields }, nxt_indent))
}

View File

@ -0,0 +1,3 @@
def main:
x = 2
return x + 1

View File

@ -0,0 +1,8 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/tab.bend
---
Errors:
In tests/golden_tests/parse_file/tab.bend :
Tabs are not accepted for indentation.
 2 |  x = 2