Skip to main content

shape_ast/parser/
mod.rs

1//! Parser module for Shape language
2//!
3//! This module contains the complete parser implementation using Pest.
4//! It's organized into submodules for different language constructs.
5
6use crate::ast::Span;
7use crate::error::{Result, ShapeError, SourceLocation};
8use pest::Parser;
9use pest::iterators::Pair;
10use pest_derive::Parser;
11
12/// Extract a lightweight Span from a Pest pair for AST nodes
13pub fn pair_span(pair: &Pair<Rule>) -> Span {
14    let span = pair.as_span();
15    Span::new(span.start(), span.end())
16}
17
18/// Extract source location from a Pest pair for error reporting
19pub(crate) fn pair_location(pair: &Pair<Rule>) -> SourceLocation {
20    let span = pair.as_span();
21    let (line, col) = span.start_pos().line_col();
22    let source_line = span.start_pos().line_of().to_string();
23    let length = span.end() - span.start();
24
25    SourceLocation::new(line, col)
26        .with_length(length)
27        .with_source_line(source_line)
28}
29
30// Submodules for different parsing concerns
31pub mod data_sources;
32pub mod expressions;
33pub mod extensions;
34pub mod functions;
35pub mod items;
36pub mod modules;
37pub mod preprocessor;
38pub mod queries;
39pub mod resilient;
40pub mod statements;
41pub mod stream;
42pub mod string_literals;
43pub mod time;
44pub mod types;
45
46#[cfg(test)]
47mod tests;
48
49use crate::ast::{Item, Program};
50
51#[derive(Parser)]
52#[grammar = "src/shape.pest"]
53pub struct ShapeParser;
54
55/// Parse a complete Shape program
56pub fn parse_program(input: &str) -> Result<Program> {
57    let processed = preprocessor::preprocess_semicolons(input);
58    let pairs = ShapeParser::parse(Rule::program, &processed).map_err(|e| {
59        // Use the structured error converter for rich error messages
60        let structured = crate::error::pest_converter::convert_pest_error(&e, &processed);
61        ShapeError::StructuredParse(Box::new(structured))
62    })?;
63
64    let mut items = Vec::new();
65
66    for pair in pairs {
67        if pair.as_rule() == Rule::program {
68            for inner in pair.into_inner() {
69                match inner.as_rule() {
70                    Rule::item => {
71                        items.push(parse_item(inner)?);
72                    }
73                    Rule::item_recovery => {
74                        let span = inner.as_span();
75                        let text = inner.as_str().trim();
76                        let preview = if text.len() > 40 {
77                            format!("{}...", &text[..40])
78                        } else {
79                            text.to_string()
80                        };
81                        return Err(ShapeError::ParseError {
82                            message: format!("Syntax error near: {}", preview),
83                            location: Some(
84                                pair_location(&inner).with_length(span.end() - span.start()),
85                            ),
86                        });
87                    }
88                    _ => {}
89                }
90            }
91        }
92    }
93
94    Ok(Program { items })
95}
96
97/// Parse an individual item (pattern, query, assignment, or expression)
98pub fn parse_item(pair: pest::iterators::Pair<Rule>) -> Result<Item> {
99    let pair_loc = pair_location(&pair);
100    let inner = pair
101        .into_inner()
102        .next()
103        .ok_or_else(|| ShapeError::ParseError {
104            message: "expected item content".to_string(),
105            location: Some(pair_loc.clone().with_hint(
106                "provide a pattern, query, function, variable declaration, or expression",
107            )),
108        })?;
109
110    let span = pair_span(&inner);
111
112    match inner.as_rule() {
113        Rule::query => Ok(Item::Query(queries::parse_query(inner)?, span)),
114        Rule::variable_decl => Ok(Item::VariableDecl(items::parse_variable_decl(inner)?, span)),
115        Rule::assignment => {
116            let inner_loc = pair_location(&inner);
117            let mut inner = inner.into_inner();
118            let pattern_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
119                message: "expected pattern in assignment".to_string(),
120                location: Some(inner_loc.clone()),
121            })?;
122            let pattern = items::parse_pattern(pattern_pair)?;
123            let value_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
124                message: "expected value expression in assignment".to_string(),
125                location: Some(inner_loc.with_hint("provide a value after '='")),
126            })?;
127            let value = expressions::parse_expression(value_pair)?;
128            Ok(Item::Assignment(
129                crate::ast::Assignment { pattern, value },
130                span,
131            ))
132        }
133        Rule::expression_stmt => {
134            let inner_loc = pair_location(&inner);
135            let expr_pair = inner
136                .into_inner()
137                .next()
138                .ok_or_else(|| ShapeError::ParseError {
139                    message: "expected expression in statement".to_string(),
140                    location: Some(inner_loc),
141                })?;
142            let expr = expressions::parse_expression(expr_pair)?;
143            Ok(Item::Expression(expr, span))
144        }
145        Rule::import_stmt => Ok(Item::Import(modules::parse_import_stmt(inner)?, span)),
146        Rule::module_decl => Ok(Item::Module(modules::parse_module_decl(inner)?, span)),
147        Rule::pub_item => Ok(Item::Export(modules::parse_export_item(inner)?, span)),
148        Rule::struct_type_def => Ok(Item::StructType(types::parse_struct_type_def(inner)?, span)),
149        Rule::native_struct_type_def => Ok(Item::StructType(
150            types::parse_native_struct_type_def(inner)?,
151            span,
152        )),
153        Rule::builtin_type_decl => Ok(Item::BuiltinTypeDecl(
154            types::parse_builtin_type_decl(inner)?,
155            span,
156        )),
157        Rule::type_alias_def => Ok(Item::TypeAlias(types::parse_type_alias_def(inner)?, span)),
158        Rule::trait_def => Ok(Item::Trait(types::parse_trait_def(inner)?, span)),
159        Rule::enum_def => Ok(Item::Enum(types::parse_enum_def(inner)?, span)),
160        Rule::extern_native_function_def => Ok(Item::ForeignFunction(
161            functions::parse_extern_native_function_def(inner)?,
162            span,
163        )),
164        Rule::foreign_function_def => Ok(Item::ForeignFunction(
165            functions::parse_foreign_function_def(inner)?,
166            span,
167        )),
168        Rule::function_def => Ok(Item::Function(functions::parse_function_def(inner)?, span)),
169        Rule::builtin_function_decl => Ok(Item::BuiltinFunctionDecl(
170            functions::parse_builtin_function_decl(inner)?,
171            span,
172        )),
173        Rule::stream_def => Ok(Item::Stream(stream::parse_stream_def(inner)?, span)),
174        Rule::test_def => Err(ShapeError::ParseError {
175            message: "Embedded test definitions are no longer supported in this refactor"
176                .to_string(),
177            location: None,
178        }),
179        Rule::statement => Ok(Item::Statement(statements::parse_statement(inner)?, span)),
180        Rule::extend_statement => Ok(Item::Extend(
181            extensions::parse_extend_statement(inner)?,
182            span,
183        )),
184        Rule::impl_block => Ok(Item::Impl(extensions::parse_impl_block(inner)?, span)),
185        Rule::optimize_statement => Ok(Item::Optimize(
186            extensions::parse_optimize_statement(inner)?,
187            span,
188        )),
189        Rule::annotation_def => Ok(Item::AnnotationDef(
190            extensions::parse_annotation_def(inner)?,
191            span,
192        )),
193        Rule::datasource_def => Ok(Item::DataSource(
194            data_sources::parse_datasource_def(inner)?,
195            span,
196        )),
197        Rule::query_decl => Ok(Item::QueryDecl(
198            data_sources::parse_query_decl(inner)?,
199            span,
200        )),
201        Rule::comptime_block => {
202            let block_pair = inner
203                .into_inner()
204                .next()
205                .ok_or_else(|| ShapeError::ParseError {
206                    message: "expected block after 'comptime'".to_string(),
207                    location: None,
208                })?;
209            let block_expr = expressions::control_flow::parse_block_expr(block_pair)?;
210            let stmts = expressions::primary::block_items_to_statements(block_expr, span);
211            Ok(Item::Comptime(stmts, span))
212        }
213        _ => Err(ShapeError::ParseError {
214            message: format!("unexpected item type: {:?}", inner.as_rule()),
215            location: Some(pair_location(&inner)),
216        }),
217    }
218}
219
220// Re-export commonly used functions for convenience
221pub use expressions::parse_expression;
222pub use items::{parse_pattern, parse_variable_decl};
223pub use types::parse_type_annotation;
224
225/// Parse a single expression from a string
226///
227/// This is useful for parsing expressions extracted from string interpolation.
228pub fn parse_expression_str(input: &str) -> Result<crate::ast::Expr> {
229    let pairs = ShapeParser::parse(Rule::expression, input).map_err(|e| {
230        let structured = crate::error::pest_converter::convert_pest_error(&e, input);
231        ShapeError::StructuredParse(Box::new(structured))
232    })?;
233
234    let pair = pairs
235        .into_iter()
236        .next()
237        .ok_or_else(|| ShapeError::ParseError {
238            message: "Expected expression".to_string(),
239            location: None,
240        })?;
241
242    expressions::parse_expression(pair)
243}