sipha_parse/
helpers.rs

1//! Helper functions for building grammar rules.
2//!
3//! This module provides convenience functions for constructing `GrammarRule`
4//! instances in a more ergonomic way than using the enum variants directly.
5
6use crate::grammar::GrammarRule;
7use sipha_core::traits::{RuleId, TokenKind};
8
9/// Create a token rule.
10pub fn token<K: TokenKind, R: RuleId>(token: K) -> GrammarRule<K, R> {
11    GrammarRule::Token(token)
12}
13
14/// Create a rule reference.
15pub fn rule<K: TokenKind, R: RuleId>(rule: R) -> GrammarRule<K, R> {
16    GrammarRule::Rule(rule)
17}
18
19/// Create a choice rule (alternatives).
20///
21/// The parser will try each alternative in order until one succeeds.
22pub fn choice<K: TokenKind, R: RuleId>(alternatives: Vec<GrammarRule<K, R>>) -> GrammarRule<K, R> {
23    GrammarRule::Choice(alternatives)
24}
25
26/// Create a sequence rule.
27///
28/// Matches all rules in order.
29pub fn seq<K: TokenKind, R: RuleId>(rules: Vec<GrammarRule<K, R>>) -> GrammarRule<K, R> {
30    GrammarRule::Sequence(rules)
31}
32
33/// Create an optional rule (zero or one).
34pub fn opt<K: TokenKind, R: RuleId>(rule: GrammarRule<K, R>) -> GrammarRule<K, R> {
35    GrammarRule::Optional(Box::new(rule))
36}
37
38/// Create a zero-or-more repetition rule.
39pub fn many<K: TokenKind, R: RuleId>(rule: GrammarRule<K, R>) -> GrammarRule<K, R> {
40    GrammarRule::ZeroOrMore(Box::new(rule))
41}
42
43/// Create a one-or-more repetition rule.
44pub fn many1<K: TokenKind, R: RuleId>(rule: GrammarRule<K, R>) -> GrammarRule<K, R> {
45    GrammarRule::OneOrMore(Box::new(rule))
46}
47
48/// Create a delimited rule (open, content, close).
49///
50/// Matches `open`, then `content`, then `close` (e.g., parentheses: `(content)`).
51pub fn delimited<K: TokenKind, R: RuleId>(
52    open: K,
53    content: GrammarRule<K, R>,
54    close: K,
55) -> GrammarRule<K, R> {
56    seq(vec![token(open), content, token(close)])
57}
58
59/// Create a separated list rule.
60///
61/// Matches one or more items separated by a separator token (e.g., `a, b, c`).
62pub fn separated_list<K: TokenKind, R: RuleId>(
63    item: GrammarRule<K, R>,
64    separator: K,
65) -> GrammarRule<K, R> {
66    seq(vec![
67        item.clone(),
68        many(seq(vec![token(separator), item])),
69    ])
70}
71
72/// Create a terminated rule (item followed by terminator).
73///
74/// Matches `item` followed by `terminator` (e.g., `item;`).
75pub fn terminated<K: TokenKind, R: RuleId>(
76    item: GrammarRule<K, R>,
77    terminator: K,
78) -> GrammarRule<K, R> {
79    seq(vec![item, token(terminator)])
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
87    enum TestToken {
88        A,
89    }
90
91    impl TokenKind for TestToken {
92        fn is_trivia(&self) -> bool {
93            false
94        }
95    }
96
97    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
98    enum TestRule {
99        Expr,
100    }
101
102    #[test]
103    fn test_helper_functions() {
104        let _token_rule: GrammarRule<TestToken, TestRule> = token(TestToken::A);
105        let _rule_ref: GrammarRule<TestToken, TestRule> = rule(TestRule::Expr);
106        let _choice_rule: GrammarRule<TestToken, TestRule> = choice(vec![token(TestToken::A)]);
107        let _seq_rule: GrammarRule<TestToken, TestRule> = seq(vec![token(TestToken::A)]);
108        let _opt_rule: GrammarRule<TestToken, TestRule> = opt(token(TestToken::A));
109        let _many_rule: GrammarRule<TestToken, TestRule> = many(token(TestToken::A));
110        let _many1_rule: GrammarRule<TestToken, TestRule> = many1(token(TestToken::A));
111    }
112}