Make rules necessarily be in a single block

This commit is contained in:
Nicolas Abril 2024-05-21 09:37:33 +02:00
parent a58d5aae89
commit e81f990208
12 changed files with 132 additions and 51 deletions

View File

@ -62,8 +62,10 @@ 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 last_rule = None;
while !self.is_eof() {
let ini_idx = *self.index();
// Imp type definition
if self.try_parse_keyword("type") {
let mut prs = PyParser { input: self.input, index: *self.index() };
@ -72,6 +74,7 @@ impl<'a> TermParser<'a> {
let end_idx = *self.index();
prs.add_type(enum_, &mut book, ini_idx, end_idx, builtin)?;
indent = nxt_indent;
last_rule = None;
continue;
}
// Imp record type definition
@ -82,8 +85,10 @@ impl<'a> TermParser<'a> {
let end_idx = *self.index();
prs.add_object(obj, &mut book, ini_idx, end_idx, builtin)?;
indent = nxt_indent;
last_rule = None;
continue;
}
// Imp function definition
if self.try_parse_keyword("def") {
let mut prs = PyParser { input: self.input, index: *self.index() };
@ -92,20 +97,46 @@ impl<'a> TermParser<'a> {
let end_idx = *self.index();
prs.add_def(def, &mut book, ini_idx, end_idx)?;
indent = nxt_indent;
last_rule = None;
continue;
}
// Fun type definition
if self.try_parse_keyword("data") {
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();
last_rule = None;
continue;
}
// Fun function definition
let ini_idx = *self.index();
let (name, rule) = self.parse_rule()?;
book.add_rule(name, rule, builtin);
let end_idx = *self.index();
// Add to book
if let Some(def) = book.defs.get_mut(&name) {
if let Some(last_rule) = last_rule {
if last_rule == name {
// Continuing with a new rule to the current definition
def.rules.push(rule);
} else {
// Trying to add a new rule to a previous definition, coming from a different rule.
let msg = format!("Redefinition of function '{name}'");
return self.with_ctx(Err(msg), ini_idx, end_idx);
}
} else {
// Trying to add a new rule to a previous definition, coming from another kind of top-level.
let msg = format!("Redefinition of function '{name}'");
return self.with_ctx(Err(msg), ini_idx, end_idx);
}
} else {
// 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();
last_rule = Some(name);
}
Ok(book)
@ -756,14 +787,6 @@ impl Book {
}
Ok(())
}
fn add_rule(&mut self, name: Name, rule: Rule, builtin: bool) {
if let Some(def) = self.defs.get_mut(&name) {
def.rules.push(rule);
} else {
self.defs.insert(name.clone(), Definition { name, rules: vec![rule], builtin });
}
}
}
impl<'a> ParserCommons<'a> for TermParser<'a> {}
@ -789,13 +812,8 @@ pub trait ParserCommons<'a>: Parser<'a> {
}
fn parse_bend_name(&mut self) -> ParseResult<Name> {
let nam = self.parse_exactly_name()?;
Ok(Name::new(nam))
}
fn parse_exactly_name(&mut self) -> ParseResult<String> {
let name = self.take_while(|c| c.is_ascii_alphanumeric() || c == '_' || c == '.' || c == '-' || c == '/');
if name.is_empty() { self.expected("name") } else { Ok(name.to_owned()) }
if name.is_empty() { self.expected("name") } else { Ok(Name::new(name.to_owned())) }
}
/// Consumes exactly the text without skipping.
@ -862,14 +880,6 @@ pub trait ParserCommons<'a>: Parser<'a> {
self.advance_trivia_inline();
}
fn skip_trivia_maybe_inline(&mut self, inline: bool) {
if inline {
self.skip_trivia_inline();
} else {
self.skip_trivia();
}
}
fn expected_spanned<T>(&mut self, exp: &str, ini_idx: usize, end_idx: usize) -> ParseResult<T> {
let is_eof = self.is_eof();
let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) });

View File

@ -90,7 +90,11 @@ impl<'a> PyParser<'a> {
/// <var> <postfix>?
///
fn parse_simple_expr(&mut self, inline: bool) -> ParseResult<Expr> {
self.skip_trivia_maybe_inline(inline);
if inline {
self.skip_trivia_inline();
} else {
self.skip_trivia();
}
let Some(head) = self.peek_one() else { return self.expected("expression")? };
let ini_idx = *self.index();
@ -133,7 +137,7 @@ impl<'a> PyParser<'a> {
}
}
// List or Comprehension
'[' => self.list_or_comprehension()?,
'[' => self.parse_list_or_comprehension()?,
// Symbol
'`' => Expr::Num { val: Num::U24(self.parse_quoted_symbol()?) },
// String
@ -173,7 +177,11 @@ impl<'a> PyParser<'a> {
let end_idx = *self.index();
// postfixes
self.skip_trivia_maybe_inline(inline);
if inline {
self.skip_trivia_inline();
} else {
self.skip_trivia();
}
// call
if self.starts_with("(") {
self.advance_one();
@ -265,7 +273,7 @@ impl<'a> PyParser<'a> {
Ok((key, val))
}
fn list_or_comprehension(&mut self) -> ParseResult<Expr> {
fn parse_list_or_comprehension(&mut self) -> ParseResult<Expr> {
self.consume_exactly("[")?;
// Empty list
@ -316,7 +324,11 @@ impl<'a> PyParser<'a> {
}
}
self.skip_trivia_maybe_inline(inline);
if inline {
self.skip_trivia_inline();
} else {
self.skip_trivia();
}
// lambda
if self.try_parse_keyword("lambda") | self.try_consume_exactly("λ") {
@ -350,18 +362,26 @@ impl<'a> PyParser<'a> {
/// <simple> (<infix_op> <infix>)?
fn parse_infix_expr(&mut self, prec: usize, inline: bool) -> ParseResult<Expr> {
maybe_grow(|| {
self.skip_trivia_maybe_inline(inline);
if inline {
self.skip_trivia_inline();
} else {
self.skip_trivia();
}
if prec > PREC.len() - 1 {
return self.parse_simple_expr(inline);
}
let mut lhs = self.parse_infix_expr(prec + 1, inline)?;
self.skip_trivia_maybe_inline(inline);
if inline {
self.skip_trivia_inline();
} else {
self.skip_trivia();
}
while let Some(op) = self.peek_oper() {
if PREC[prec].iter().any(|r| *r == op) {
self.parse_oper()?;
let rhs = self.parse_infix_expr(prec + 1, inline)?;
lhs = Expr::Bin { op, lhs: Box::new(lhs), rhs: Box::new(rhs) };
self.advance_trivia_inline();
self.skip_trivia_inline();
} else {
break;
}
@ -423,7 +443,7 @@ impl<'a> PyParser<'a> {
let ini_idx = *self.index();
let pat = self.parse_assign_pattern()?;
let end_idx = *self.index();
self.advance_trivia_inline();
self.skip_trivia_inline();
// Assignment
if self.starts_with("=") {
@ -472,7 +492,7 @@ impl<'a> PyParser<'a> {
}
fn parse_in_place_op(&mut self) -> ParseResult<Option<InPlaceOp>> {
self.advance_trivia_inline();
self.skip_trivia_inline();
let op = if self.starts_with("+=") {
self.consume("+=")?;
Some(InPlaceOp::Add)
@ -579,7 +599,7 @@ impl<'a> PyParser<'a> {
let arg = self.parse_expr(true)?;
let end_idx = *self.index();
self.advance_trivia_inline();
self.skip_trivia_inline();
match (arg, self.starts_with("=")) {
(Expr::Var { nam }, true) => {
self.advance_one();
@ -591,24 +611,15 @@ impl<'a> PyParser<'a> {
}
}
fn name_or_wildcard(&mut self) -> ParseResult<Option<Name>> {
self.labelled(
|p| {
if p.try_consume_exactly("_") {
Ok(None)
} else {
let nam = p.parse_bend_name()?;
Ok(Some(nam))
}
},
"name or '_'",
)
}
fn parse_match_case(&mut self, indent: &mut Indent) -> ParseResult<(MatchArm, Indent)> {
self.parse_keyword("case")?;
self.skip_trivia_inline();
let pat = self.name_or_wildcard()?;
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.consume_exactly(":")?;
self.consume_new_line()?;
@ -683,7 +694,7 @@ impl<'a> PyParser<'a> {
return self.expected("number or '_'")?;
};
self.advance_trivia_inline();
self.skip_trivia_inline();
self.consume_exactly(":")?;
self.consume_new_line()?;
indent.enter_level();
@ -853,7 +864,7 @@ impl<'a> PyParser<'a> {
// Chn pattern
if self.starts_with("$") {
self.advance_one();
self.advance_trivia_inline();
self.skip_trivia_inline();
let nam = self.parse_bend_name()?;
return Ok(AssignPattern::Chn(nam));
}

View File

@ -0,0 +1,4 @@
# We shouldn't allow a fun rule to be extended by an imp function
(A) = @x x
def A:
return 0

View File

@ -0,0 +1,5 @@
# We shouldn't allow an imp function to have a fun rule
def A:
return 0
(A) = 1

View File

@ -0,0 +1,4 @@
# We shouldn't allow rules of other functions in the middle of a definition
(A) = @x x
(B) = @x x
(A) = @x x

View File

@ -0,0 +1,3 @@
A = 0
object B
A = 1

View File

@ -0,0 +1,3 @@
A = 0
data B = B
A = 1

View File

@ -0,0 +1,9 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/redefinition_fun_imp.bend
---
Errors:
In tests/golden_tests/parse_file/redefinition_fun_imp.bend :
Redefinition of function 'A'.
 3 | def A:
 4 |  return 0

View File

@ -0,0 +1,8 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/redefinition_imp_fun.bend
---
Errors:
In tests/golden_tests/parse_file/redefinition_imp_fun.bend :
Redefinition of function 'A'
 5 | (A) = 1

View File

@ -0,0 +1,8 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/redefinition_with_def_between.bend
---
Errors:
In tests/golden_tests/parse_file/redefinition_with_def_between.bend :
Redefinition of function 'A'
 4 | (A) = @x x

View File

@ -0,0 +1,8 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/redefinition_with_object_between.bend
---
Errors:
In tests/golden_tests/parse_file/redefinition_with_object_between.bend :
Redefinition of function 'A'
 3 | A = 1

View File

@ -0,0 +1,8 @@
---
source: tests/golden_tests.rs
input_file: tests/golden_tests/parse_file/redefinition_with_type_between.bend
---
Errors:
In tests/golden_tests/parse_file/redefinition_with_type_between.bend :
Redefinition of function 'A'
 3 | A = 1