Skip to main content

telltale_runtime/compiler/
extension_parser.rs

1//! Extension-aware parser that integrates with the existing parser
2//!
3//! This module provides a parser that can handle both standard choreographic
4//! constructs and extension-defined syntax by leveraging the shared extension
5//! registry and parser dispatch path.
6
7use crate::compiler::grammar::GrammarCompositionError;
8use crate::compiler::parser::{parse_choreography_str_with_extensions, ParseError};
9use crate::extensions::{ExtensionRegistry, GrammarExtension, StatementParser};
10
11/// Extension-aware parser that can handle both core and extension syntax
12pub struct ExtensionParser {
13    extension_registry: ExtensionRegistry,
14    /// Pre-allocated buffer for parsing operations to reduce allocations
15    parse_buffer: String,
16}
17
18impl ExtensionParser {
19    /// Create a new extension parser
20    pub fn new() -> Self {
21        Self {
22            extension_registry: ExtensionRegistry::new(),
23            parse_buffer: String::with_capacity(1024), // Pre-allocate for common case
24        }
25    }
26
27    /// Register an extension with both grammar and parsing support.
28    ///
29    /// # Errors
30    ///
31    /// Returns an error if the grammar extension cannot be registered.
32    pub fn register_extension<G, P>(
33        &mut self,
34        grammar_ext: G,
35        statement_parser: P,
36    ) -> Result<(), crate::extensions::ParseError>
37    where
38        G: GrammarExtension + 'static,
39        P: StatementParser + 'static,
40    {
41        let parser_id = grammar_ext.extension_id().to_string();
42        self.extension_registry.register_grammar(grammar_ext)?;
43        self.extension_registry
44            .register_parser(statement_parser, parser_id);
45        Ok(())
46    }
47
48    /// Parse a choreography string with extension support (optimized)
49    pub fn parse_with_extensions(
50        &mut self,
51        input: &str,
52    ) -> Result<crate::ast::Choreography, ExtensionParseError> {
53        // Clear and reuse buffers to reduce allocations
54        self.parse_buffer.clear();
55
56        // Reserve capacity based on input size for efficient parsing
57        self.parse_buffer.reserve(input.len());
58        parse_choreography_str_with_extensions(input, &self.extension_registry)
59            .map(|(choreography, _)| choreography)
60            .map_err(ExtensionParseError::StandardParseError)
61    }
62
63    /// Check if an extension can handle a given statement
64    pub fn can_handle_statement(&self, statement_type: &str) -> bool {
65        self.extension_registry.can_handle(statement_type)
66    }
67
68    /// Get the composed grammar for debugging
69    pub fn get_composed_grammar(&self) -> Result<String, GrammarCompositionError> {
70        compose_grammar_from_registry(&self.extension_registry)
71    }
72
73    /// Get statistics about registered extensions
74    pub fn extension_stats(&self) -> ExtensionStats {
75        ExtensionStats {
76            grammar_extensions: self.extension_registry.grammar_extensions().count(),
77            statement_parsers: self.extension_registry.statement_parser_count(),
78        }
79    }
80}
81
82impl Default for ExtensionParser {
83    fn default() -> Self {
84        Self::new()
85    }
86}
87
88fn compose_grammar_from_registry(
89    registry: &ExtensionRegistry,
90) -> Result<String, GrammarCompositionError> {
91    let base_grammar = include_str!("choreography.pest");
92    let extension_rules = registry.compose_grammar("");
93    if extension_rules.trim().is_empty() {
94        return Ok(base_grammar.to_string());
95    }
96
97    let statement_rules: Vec<_> = registry.statement_rules();
98    let mut lines: Vec<String> = base_grammar.lines().map(ToOwned::to_owned).collect();
99    let (stmt_start, stmt_end) = find_statement_rule_bounds(&lines)?;
100    let indent = find_statement_indent(&lines, stmt_start, stmt_end);
101    let insert_lines: Vec<String> = statement_rules
102        .iter()
103        .map(|rule| format!("{indent}| {rule}"))
104        .collect();
105    lines.splice(stmt_end..stmt_end, insert_lines);
106
107    let mut composed = lines.join("\n");
108    composed.push('\n');
109    composed.push_str("// Extension Rules\n");
110    composed.push_str(&extension_rules);
111    Ok(composed)
112}
113
114fn find_statement_rule_bounds(lines: &[String]) -> Result<(usize, usize), GrammarCompositionError> {
115    let start = lines
116        .iter()
117        .position(|line| line.trim_start().starts_with("statement = _{"))
118        .ok_or_else(|| {
119            GrammarCompositionError::InvalidBaseGrammar(
120                "Could not find statement rule in base grammar".to_string(),
121            )
122        })?;
123
124    for (idx, line) in lines.iter().enumerate().skip(start + 1) {
125        if line.trim_start().starts_with('}') {
126            return Ok((start, idx));
127        }
128    }
129
130    Err(GrammarCompositionError::InvalidBaseGrammar(
131        "Could not find end of statement rule in base grammar".to_string(),
132    ))
133}
134
135fn find_statement_indent(lines: &[String], start: usize, end: usize) -> String {
136    for line in lines.iter().take(end).skip(start + 1) {
137        let trimmed = line.trim_start();
138        if trimmed.starts_with('|') {
139            let indent_len = line.len().saturating_sub(trimmed.len());
140            return line[..indent_len].to_string();
141        }
142    }
143    "    ".to_string()
144}
145
146/// Statistics about registered extensions
147#[derive(Debug, Clone)]
148pub struct ExtensionStats {
149    pub grammar_extensions: usize,
150    pub statement_parsers: usize,
151}
152
153/// Errors that can occur during extension-aware parsing
154#[derive(Debug, thiserror::Error)]
155pub enum ExtensionParseError {
156    #[error("Standard parsing failed: {0}")]
157    StandardParseError(#[from] ParseError),
158
159    #[error("Grammar composition failed: {0}")]
160    GrammarComposition(#[from] GrammarCompositionError),
161
162    #[error("Extension parsing failed: {0}")]
163    ExtensionParsing(String),
164
165    #[error("Unknown extension statement: {0}")]
166    UnknownExtension(String),
167}
168
169/// Builder for extension parsers
170pub struct ExtensionParserBuilder {
171    parser: ExtensionParser,
172}
173
174impl ExtensionParserBuilder {
175    pub fn new() -> Self {
176        Self {
177            parser: ExtensionParser::new(),
178        }
179    }
180
181    pub fn with_extension<G, P>(
182        mut self,
183        grammar_ext: G,
184        statement_parser: P,
185    ) -> Result<Self, crate::extensions::ParseError>
186    where
187        G: crate::extensions::GrammarExtension + 'static,
188        P: StatementParser + 'static,
189    {
190        self.parser
191            .register_extension(grammar_ext, statement_parser)?;
192        Ok(self)
193    }
194
195    pub fn build(self) -> ExtensionParser {
196        self.parser
197    }
198}
199
200impl Default for ExtensionParserBuilder {
201    fn default() -> Self {
202        Self::new()
203    }
204}
205
206/// Helper function to create an extension parser with common extensions
207pub fn create_standard_extension_parser() -> ExtensionParser {
208    ExtensionParserBuilder::new()
209        // Add standard extensions here as they're developed
210        .build()
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216    use crate::extensions::{GrammarExtension, ParseContext, StatementParser};
217
218    #[derive(Debug)]
219    struct TestGrammarExtension;
220
221    impl GrammarExtension for TestGrammarExtension {
222        fn grammar_rules(&self) -> &'static str {
223            "test_extension_stmt = { \"test\" ~ ident }"
224        }
225
226        fn statement_rules(&self) -> Vec<&'static str> {
227            vec!["test_extension_stmt"]
228        }
229
230        fn extension_id(&self) -> &'static str {
231            "test_extension"
232        }
233    }
234
235    #[derive(Debug)]
236    struct TestStatementParser;
237
238    impl StatementParser for TestStatementParser {
239        fn can_parse(&self, rule_name: &str) -> bool {
240            rule_name == "test_extension_stmt"
241        }
242
243        fn supported_rules(&self) -> Vec<String> {
244            vec!["test_extension_stmt".to_string()]
245        }
246
247        fn parse_statement(
248            &self,
249            _rule_name: &str,
250            _content: &str,
251            _context: &ParseContext,
252        ) -> Result<Box<dyn crate::extensions::ProtocolExtension>, crate::extensions::ParseError>
253        {
254            // This would normally parse the statement content
255            Err(crate::extensions::ParseError::Syntax {
256                message: "Test parser - not implemented".to_string(),
257            })
258        }
259    }
260
261    #[test]
262    fn test_extension_parser_creation() {
263        let parser = ExtensionParser::new();
264        let stats = parser.extension_stats();
265        assert_eq!(stats.grammar_extensions, 0);
266    }
267
268    #[test]
269    fn test_extension_registration() {
270        let mut parser = ExtensionParser::new();
271        parser
272            .register_extension(TestGrammarExtension, TestStatementParser)
273            .expect("extension should register");
274
275        let stats = parser.extension_stats();
276        assert_eq!(stats.grammar_extensions, 1);
277        assert!(parser.can_handle_statement("test_extension_stmt"));
278    }
279
280    #[test]
281    fn test_builder_pattern() {
282        let parser = ExtensionParserBuilder::new()
283            .with_extension(TestGrammarExtension, TestStatementParser)
284            .expect("test extension should register")
285            .build();
286
287        assert!(parser.can_handle_statement("test_extension_stmt"));
288    }
289
290    #[test]
291    fn test_standard_parsing() {
292        let mut parser = ExtensionParser::new();
293
294        let input = "protocol TestProtocol =\n  roles Alice, Bob\n  Alice -> Bob : Message\n";
295
296        let result = parser.parse_with_extensions(input);
297        assert!(result.is_ok(), "Should parse standard choreography");
298    }
299
300    #[test]
301    fn test_composed_grammar_generation() {
302        let mut parser = ExtensionParser::new();
303        parser
304            .register_extension(TestGrammarExtension, TestStatementParser)
305            .expect("extension should register");
306
307        let result = parser.get_composed_grammar();
308        assert!(result.is_ok(), "Should generate composed grammar");
309
310        let grammar = result.unwrap();
311        assert!(
312            grammar.contains("test_extension_stmt"),
313            "Should contain extension rule"
314        );
315        assert!(
316            grammar.contains("choreography"),
317            "Should contain base rules"
318        );
319    }
320}