1use crate::ast::Span;
7use crate::error::{Result, ShapeError, SourceLocation};
8use pest::Parser;
9use pest::iterators::Pair;
10use pest_derive::Parser;
11
12pub fn pair_span(pair: &Pair<Rule>) -> Span {
14 let span = pair.as_span();
15 Span::new(span.start(), span.end())
16}
17
18pub(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
30pub mod data_sources;
32pub mod docs;
33pub mod expressions;
34pub mod extensions;
35pub mod functions;
36pub mod items;
37pub mod modules;
38pub mod preprocessor;
39pub mod queries;
40pub mod resilient;
41pub mod statements;
42pub mod stream;
43pub mod string_literals;
44pub mod time;
45pub mod types;
46
47#[cfg(test)]
48mod tests;
49
50use crate::ast::{DocComment, ExportItem, Item, Program};
51
52#[derive(Parser)]
53#[grammar = "src/shape.pest"]
54pub struct ShapeParser;
55
56pub fn parse_program(input: &str) -> Result<Program> {
58 let processed = preprocessor::preprocess_semicolons(input);
59 let pairs = ShapeParser::parse(Rule::program, &processed).map_err(|e| {
60 let structured = crate::error::pest_converter::convert_pest_error(&e, &processed);
62 ShapeError::StructuredParse(Box::new(structured))
63 })?;
64
65 let mut items = Vec::new();
66 let mut module_doc_comment = None;
67
68 for pair in pairs {
69 if pair.as_rule() == Rule::program {
70 for inner in pair.into_inner() {
71 match inner.as_rule() {
72 Rule::program_doc_comment => {
73 module_doc_comment = Some(docs::parse_doc_comment(inner));
74 }
75 Rule::item => {
76 items.push(parse_item(inner)?);
77 }
78 Rule::item_recovery => {
79 let span = inner.as_span();
80 let text = inner.as_str().trim();
81 let preview = if text.len() > 40 {
82 format!("{}...", &text[..40])
83 } else {
84 text.to_string()
85 };
86 return Err(ShapeError::ParseError {
87 message: format!("Syntax error near: {}", preview),
88 location: Some(
89 pair_location(&inner).with_length(span.end() - span.start()),
90 ),
91 });
92 }
93 _ => {}
94 }
95 }
96 }
97 }
98
99 let mut program = Program {
100 items,
101 docs: crate::ast::ProgramDocs::default(),
102 };
103 program.docs = docs::build_program_docs(&program, module_doc_comment.as_ref());
104 Ok(program)
105}
106
107pub fn parse_item(pair: pest::iterators::Pair<Rule>) -> Result<Item> {
109 let pair_loc = pair_location(&pair);
110 let mut item_inner = pair.into_inner();
111 let mut doc_comment = None;
112 let mut inner = item_inner.next().ok_or_else(|| ShapeError::ParseError {
113 message: "expected item content".to_string(),
114 location: Some(pair_loc.clone().with_hint(
115 "provide a pattern, query, function, variable declaration, or expression",
116 )),
117 })?;
118
119 if inner.as_rule() == Rule::doc_comment {
120 doc_comment = Some(docs::parse_doc_comment(inner));
121 inner = item_inner.next().ok_or_else(|| ShapeError::ParseError {
122 message: "expected item after doc comment".to_string(),
123 location: Some(pair_loc.clone()),
124 })?;
125 }
126
127 if inner.as_rule() == Rule::item_core {
128 inner = inner
129 .into_inner()
130 .next()
131 .ok_or_else(|| ShapeError::ParseError {
132 message: "expected item content".to_string(),
133 location: Some(pair_loc.clone()),
134 })?;
135 }
136
137 let span = pair_span(&inner);
138 let mut item = match inner.as_rule() {
139 Rule::query => Item::Query(queries::parse_query(inner)?, span),
140 Rule::variable_decl => Item::VariableDecl(items::parse_variable_decl(inner)?, span),
141 Rule::assignment => {
142 let inner_loc = pair_location(&inner);
143 let mut inner = inner.into_inner();
144 let pattern_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
145 message: "expected pattern in assignment".to_string(),
146 location: Some(inner_loc.clone()),
147 })?;
148 let pattern = items::parse_pattern(pattern_pair)?;
149 let value_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
150 message: "expected value expression in assignment".to_string(),
151 location: Some(inner_loc.with_hint("provide a value after '='")),
152 })?;
153 let value = expressions::parse_expression(value_pair)?;
154 Item::Assignment(
155 crate::ast::Assignment { pattern, value },
156 span,
157 )
158 }
159 Rule::expression_stmt => {
160 let inner_loc = pair_location(&inner);
161 let expr_pair = inner
162 .into_inner()
163 .next()
164 .ok_or_else(|| ShapeError::ParseError {
165 message: "expected expression in statement".to_string(),
166 location: Some(inner_loc),
167 })?;
168 let expr = expressions::parse_expression(expr_pair)?;
169 Item::Expression(expr, span)
170 }
171 Rule::import_stmt => Item::Import(modules::parse_import_stmt(inner)?, span),
172 Rule::module_decl => Item::Module(modules::parse_module_decl(inner)?, span),
173 Rule::pub_item => Item::Export(modules::parse_export_item(inner)?, span),
174 Rule::struct_type_def => Item::StructType(types::parse_struct_type_def(inner)?, span),
175 Rule::native_struct_type_def => Item::StructType(
176 types::parse_native_struct_type_def(inner)?,
177 span,
178 ),
179 Rule::builtin_type_decl => Item::BuiltinTypeDecl(
180 types::parse_builtin_type_decl(inner)?,
181 span,
182 ),
183 Rule::type_alias_def => Item::TypeAlias(types::parse_type_alias_def(inner)?, span),
184 Rule::interface_def => Item::Interface(types::parse_interface_def(inner)?, span),
185 Rule::trait_def => Item::Trait(types::parse_trait_def(inner)?, span),
186 Rule::enum_def => Item::Enum(types::parse_enum_def(inner)?, span),
187 Rule::extern_native_function_def => Item::ForeignFunction(
188 functions::parse_extern_native_function_def(inner)?,
189 span,
190 ),
191 Rule::foreign_function_def => Item::ForeignFunction(
192 functions::parse_foreign_function_def(inner)?,
193 span,
194 ),
195 Rule::function_def => Item::Function(functions::parse_function_def(inner)?, span),
196 Rule::builtin_function_decl => Item::BuiltinFunctionDecl(
197 functions::parse_builtin_function_decl(inner)?,
198 span,
199 ),
200 Rule::stream_def => Item::Stream(stream::parse_stream_def(inner)?, span),
201 Rule::test_def => {
202 return Err(ShapeError::ParseError {
203 message: "Embedded test definitions are no longer supported in this refactor"
204 .to_string(),
205 location: None,
206 });
207 }
208 Rule::statement => Item::Statement(statements::parse_statement(inner)?, span),
209 Rule::extend_statement => Item::Extend(
210 extensions::parse_extend_statement(inner)?,
211 span,
212 ),
213 Rule::impl_block => Item::Impl(extensions::parse_impl_block(inner)?, span),
214 Rule::optimize_statement => Item::Optimize(
215 extensions::parse_optimize_statement(inner)?,
216 span,
217 ),
218 Rule::annotation_def => Item::AnnotationDef(
219 extensions::parse_annotation_def(inner)?,
220 span,
221 ),
222 Rule::datasource_def => Item::DataSource(
223 data_sources::parse_datasource_def(inner)?,
224 span,
225 ),
226 Rule::query_decl => Item::QueryDecl(
227 data_sources::parse_query_decl(inner)?,
228 span,
229 ),
230 Rule::comptime_block => {
231 let block_pair = inner
232 .into_inner()
233 .next()
234 .ok_or_else(|| ShapeError::ParseError {
235 message: "expected block after 'comptime'".to_string(),
236 location: None,
237 })?;
238 let block_expr = expressions::control_flow::parse_block_expr(block_pair)?;
239 let stmts = expressions::primary::block_items_to_statements(block_expr, span);
240 Item::Comptime(stmts, span)
241 }
242 _ => {
243 return Err(ShapeError::ParseError {
244 message: format!("unexpected item type: {:?}", inner.as_rule()),
245 location: Some(pair_location(&inner)),
246 });
247 }
248 };
249
250 if let Some(doc_comment) = doc_comment {
251 attach_item_doc_comment(&mut item, doc_comment);
252 }
253
254 Ok(item)
255}
256
257fn attach_item_doc_comment(item: &mut Item, doc_comment: DocComment) {
258 match item {
259 Item::Module(module, _) => module.doc_comment = Some(doc_comment),
260 Item::TypeAlias(alias, _) => alias.doc_comment = Some(doc_comment),
261 Item::Interface(interface, _) => interface.doc_comment = Some(doc_comment),
262 Item::Trait(trait_def, _) => trait_def.doc_comment = Some(doc_comment),
263 Item::Enum(enum_def, _) => enum_def.doc_comment = Some(doc_comment),
264 Item::Function(function, _) => function.doc_comment = Some(doc_comment),
265 Item::AnnotationDef(annotation_def, _) => annotation_def.doc_comment = Some(doc_comment),
266 Item::StructType(struct_def, _) => struct_def.doc_comment = Some(doc_comment),
267 Item::BuiltinTypeDecl(ty, _) => ty.doc_comment = Some(doc_comment),
268 Item::BuiltinFunctionDecl(function, _) => function.doc_comment = Some(doc_comment),
269 Item::ForeignFunction(function, _) => function.doc_comment = Some(doc_comment),
270 Item::Export(export, _) => attach_export_doc_comment(&mut export.item, doc_comment),
271 _ => {}
272 }
273}
274
275fn attach_export_doc_comment(item: &mut ExportItem, doc_comment: DocComment) {
276 match item {
277 ExportItem::Function(function) => function.doc_comment = Some(doc_comment),
278 ExportItem::TypeAlias(alias) => alias.doc_comment = Some(doc_comment),
279 ExportItem::Enum(enum_def) => enum_def.doc_comment = Some(doc_comment),
280 ExportItem::Struct(struct_def) => struct_def.doc_comment = Some(doc_comment),
281 ExportItem::Interface(interface) => interface.doc_comment = Some(doc_comment),
282 ExportItem::Trait(trait_def) => trait_def.doc_comment = Some(doc_comment),
283 ExportItem::ForeignFunction(function) => function.doc_comment = Some(doc_comment),
284 ExportItem::Named(_) => {}
285 }
286}
287
288pub use expressions::parse_expression;
290pub use items::{parse_pattern, parse_variable_decl};
291pub use types::parse_type_annotation;
292
293pub fn parse_expression_str(input: &str) -> Result<crate::ast::Expr> {
297 let pairs = ShapeParser::parse(Rule::expression, input).map_err(|e| {
298 let structured = crate::error::pest_converter::convert_pest_error(&e, input);
299 ShapeError::StructuredParse(Box::new(structured))
300 })?;
301
302 let pair = pairs
303 .into_iter()
304 .next()
305 .ok_or_else(|| ShapeError::ParseError {
306 message: "Expected expression".to_string(),
307 location: None,
308 })?;
309
310 expressions::parse_expression(pair)
311}