diff --git a/crates/plotnik-lib/src/parser/cst.rs b/crates/plotnik-lib/src/parser/cst.rs index eb952ede..5cf99fb4 100644 --- a/crates/plotnik-lib/src/parser/cst.rs +++ b/crates/plotnik-lib/src/parser/cst.rs @@ -100,6 +100,9 @@ pub enum SyntaxKind { #[token("MISSING")] KwMissing, + #[token("pub")] + Pub, + /// Identifier. Accepts dots/hyphens for tree-sitter compat; parser validates per context. /// Defined after keywords so they take precedence. #[regex(r"[a-zA-Z][a-zA-Z0-9_.\-]*")] diff --git a/crates/plotnik-lib/src/parser/grammar.rs b/crates/plotnik-lib/src/parser/grammar.rs index 7c68a3d5..0de4e142 100644 --- a/crates/plotnik-lib/src/parser/grammar.rs +++ b/crates/plotnik-lib/src/parser/grammar.rs @@ -21,21 +21,27 @@ impl Parser<'_> { let mut unnamed_def_spans: Vec = Vec::new(); while !self.has_fatal_error() && (self.peek() != SyntaxKind::Error || !self.eof()) { + if self.peek() == SyntaxKind::Pub { + self.parse_def(); + continue; + } + // LL(2): Id followed by Equals → named definition (if PascalCase) if self.peek() == SyntaxKind::Id && self.peek_nth(1) == SyntaxKind::Equals { self.parse_def(); - } else { - let start = self.current_span().start(); - self.start_node(SyntaxKind::Def); - let success = self.parse_expr_or_error(); - if !success { - self.synchronize_to_def_start(); - } - self.finish_node(); - if success { - let end = self.last_non_trivia_end().unwrap_or(start); - unnamed_def_spans.push(TextRange::new(start, end)); - } + continue; + } + + let start = self.current_span().start(); + self.start_node(SyntaxKind::Def); + let success = self.parse_expr_or_error(); + if !success { + self.synchronize_to_def_start(); + } + self.finish_node(); + if success { + let end = self.last_non_trivia_end().unwrap_or(start); + unnamed_def_spans.push(TextRange::new(start, end)); } } @@ -57,6 +63,9 @@ impl Parser<'_> { fn parse_def(&mut self) { self.start_node(SyntaxKind::Def); + self.eat(SyntaxKind::Pub); + self.peek(); + let span = self.current_span(); let name = token_text(self.source, &self.tokens[self.pos]); self.bump(); diff --git a/crates/plotnik-lib/src/parser/tests/grammar/definitions_tests.rs b/crates/plotnik-lib/src/parser/tests/grammar/definitions_tests.rs index e61e763a..792aa8d8 100644 --- a/crates/plotnik-lib/src/parser/tests/grammar/definitions_tests.rs +++ b/crates/plotnik-lib/src/parser/tests/grammar/definitions_tests.rs @@ -252,7 +252,7 @@ fn named_def_referencing_another() { #[test] fn named_def_with_quantifier() { let input = indoc! {r#" - Statements = (statement)+ + pub Statements = (statement)+ "#}; let query = Query::try_from(input).unwrap(); @@ -260,6 +260,7 @@ fn named_def_with_quantifier() { insta::assert_snapshot!(query.dump_cst(), @r#" Root Def + Pub "pub" Id "Statements" Equals "=" Quantifier