diff --git a/src/fun/parser.rs b/src/fun/parser.rs index 747f0219..95935098 100644 --- a/src/fun/parser.rs +++ b/src/fun/parser.rs @@ -62,8 +62,10 @@ impl<'a> TermParser<'a> { pub fn parse_book(&mut self, default_book: Book, builtin: bool) -> ParseResult { 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 { - let nam = self.parse_exactly_name()?; - Ok(Name::new(nam)) - } - - fn parse_exactly_name(&mut self) -> ParseResult { 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(&mut self, exp: &str, ini_idx: usize, end_idx: usize) -> ParseResult { let is_eof = self.is_eof(); let detected = DisplayFn(|f| if is_eof { write!(f, " end of input") } else { Ok(()) }); diff --git a/src/imp/parser.rs b/src/imp/parser.rs index 2c1e1a9c..dfffbb14 100644 --- a/src/imp/parser.rs +++ b/src/imp/parser.rs @@ -90,7 +90,11 @@ impl<'a> PyParser<'a> { /// ? /// fn parse_simple_expr(&mut self, inline: bool) -> ParseResult { - 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 { + fn parse_list_or_comprehension(&mut self) -> ParseResult { 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> { /// ( )? fn parse_infix_expr(&mut self, prec: usize, inline: bool) -> ParseResult { 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> { - 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> { - 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)); } diff --git a/tests/golden_tests/parse_file/redefinition_fun_imp.bend b/tests/golden_tests/parse_file/redefinition_fun_imp.bend new file mode 100644 index 00000000..a9790a2c --- /dev/null +++ b/tests/golden_tests/parse_file/redefinition_fun_imp.bend @@ -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 \ No newline at end of file diff --git a/tests/golden_tests/parse_file/redefinition_imp_fun.bend b/tests/golden_tests/parse_file/redefinition_imp_fun.bend new file mode 100644 index 00000000..1e28f3c8 --- /dev/null +++ b/tests/golden_tests/parse_file/redefinition_imp_fun.bend @@ -0,0 +1,5 @@ +# We shouldn't allow an imp function to have a fun rule +def A: + return 0 + +(A) = 1 \ No newline at end of file diff --git a/tests/golden_tests/parse_file/redefinition_with_def_between.bend b/tests/golden_tests/parse_file/redefinition_with_def_between.bend new file mode 100644 index 00000000..b0215744 --- /dev/null +++ b/tests/golden_tests/parse_file/redefinition_with_def_between.bend @@ -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 \ No newline at end of file diff --git a/tests/golden_tests/parse_file/redefinition_with_object_between.bend b/tests/golden_tests/parse_file/redefinition_with_object_between.bend new file mode 100644 index 00000000..37150485 --- /dev/null +++ b/tests/golden_tests/parse_file/redefinition_with_object_between.bend @@ -0,0 +1,3 @@ +A = 0 +object B +A = 1 \ No newline at end of file diff --git a/tests/golden_tests/parse_file/redefinition_with_type_between.bend b/tests/golden_tests/parse_file/redefinition_with_type_between.bend new file mode 100644 index 00000000..5a32ccae --- /dev/null +++ b/tests/golden_tests/parse_file/redefinition_with_type_between.bend @@ -0,0 +1,3 @@ +A = 0 +data B = B +A = 1 \ No newline at end of file diff --git a/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap b/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap new file mode 100644 index 00000000..01b3a8b5 --- /dev/null +++ b/tests/snapshots/parse_file__redefinition_fun_imp.bend.snap @@ -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 diff --git a/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap b/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap new file mode 100644 index 00000000..e14ae79d --- /dev/null +++ b/tests/snapshots/parse_file__redefinition_imp_fun.bend.snap @@ -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 diff --git a/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap new file mode 100644 index 00000000..aca21a52 --- /dev/null +++ b/tests/snapshots/parse_file__redefinition_with_def_between.bend.snap @@ -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 diff --git a/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap new file mode 100644 index 00000000..a95e8146 --- /dev/null +++ b/tests/snapshots/parse_file__redefinition_with_object_between.bend.snap @@ -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 diff --git a/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap b/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap new file mode 100644 index 00000000..264ad129 --- /dev/null +++ b/tests/snapshots/parse_file__redefinition_with_type_between.bend.snap @@ -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