shape_ast/parser/
modules.rs1use crate::error::{Result, ShapeError};
4use crate::parser::pair_location;
5use pest::iterators::Pair;
6
7use crate::ast::{
8 ExportItem, ExportSpec, ExportStmt, ImportItems, ImportSpec, ImportStmt, Item, ModuleDecl,
9};
10use crate::parser::{Rule, functions, items, pair_span};
11
12pub fn parse_import_stmt(pair: Pair<Rule>) -> Result<ImportStmt> {
19 let pair_loc = pair_location(&pair);
20 let mut inner = pair.into_inner();
21 let first = inner.next().ok_or_else(|| ShapeError::ParseError {
22 message: "invalid import statement".to_string(),
23 location: Some(pair_loc.clone()),
24 })?;
25 let first_str = first.as_str();
26 let first_rule = first.as_rule();
27
28 match first_rule {
30 Rule::module_path => {
31 let module_path = first_str.to_string();
32 match inner.next() {
33 Some(pair) if pair.as_rule() == Rule::import_item_list => {
34 let specs = parse_import_item_list(pair)?;
36 Ok(ImportStmt {
37 items: ImportItems::Named(specs),
38 from: module_path,
39 })
40 }
41 Some(pair) if pair.as_rule() == Rule::ident => {
42 let alias = pair.as_str().to_string();
44 let local_name = module_path
45 .rsplit("::")
46 .next()
47 .unwrap_or(module_path.as_str())
48 .to_string();
49 Ok(ImportStmt {
50 items: ImportItems::Namespace {
51 name: local_name,
52 alias: Some(alias),
53 },
54 from: module_path,
55 })
56 }
57 None => {
58 let local_name = module_path
60 .rsplit("::")
61 .next()
62 .unwrap_or(module_path.as_str())
63 .to_string();
64 Ok(ImportStmt {
65 items: ImportItems::Namespace {
66 name: local_name,
67 alias: None,
68 },
69 from: module_path,
70 })
71 }
72 _ => Err(ShapeError::ParseError {
73 message: "unexpected token in use statement".to_string(),
74 location: Some(pair_loc),
75 }),
76 }
77 }
78 _ => Err(ShapeError::ParseError {
79 message: format!(
80 "unexpected token in import statement: {:?} '{}'",
81 first_rule, first_str
82 ),
83 location: Some(pair_loc.with_hint("use 'from path use { ... }' or 'use path'")),
84 }),
85 }
86}
87
88fn parse_import_item_list(pair: Pair<Rule>) -> Result<Vec<ImportSpec>> {
90 let mut imports = Vec::new();
91
92 for item_pair in pair.into_inner() {
93 if item_pair.as_rule() == Rule::import_item {
94 imports.push(parse_import_item(item_pair)?);
95 }
96 }
97
98 Ok(imports)
99}
100
101fn parse_import_item(pair: Pair<Rule>) -> Result<ImportSpec> {
103 let pair_loc = pair_location(&pair);
104 let mut inner = pair.into_inner();
105
106 let item_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
107 message: "expected import item name".to_string(),
108 location: Some(pair_loc.clone()),
109 })?;
110
111 match item_pair.as_rule() {
112 Rule::annotation_import_item => {
113 let mut annotation_inner = item_pair.into_inner();
114 let name_pair = annotation_inner
115 .next()
116 .ok_or_else(|| ShapeError::ParseError {
117 message: "expected annotation import name".to_string(),
118 location: Some(pair_loc.clone()),
119 })?;
120 Ok(ImportSpec {
121 name: name_pair.as_str().to_string(),
122 alias: None,
123 is_annotation: true,
124 })
125 }
126 Rule::regular_import_item => {
127 let mut regular_inner = item_pair.into_inner();
128 let name_pair = regular_inner.next().ok_or_else(|| ShapeError::ParseError {
129 message: "expected import item name".to_string(),
130 location: Some(pair_loc.clone()),
131 })?;
132 Ok(ImportSpec {
133 name: name_pair.as_str().to_string(),
134 alias: regular_inner.next().map(|p| p.as_str().to_string()),
135 is_annotation: false,
136 })
137 }
138 _ => Err(ShapeError::ParseError {
139 message: format!("unexpected import item: {:?}", item_pair.as_rule()),
140 location: Some(pair_location(&item_pair)),
141 }),
142 }
143}
144
145pub fn parse_export_item(pair: Pair<Rule>) -> Result<ExportStmt> {
147 let pair_loc = pair_location(&pair);
148 let mut inner = pair.into_inner();
149
150 let next_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
152 message: "expected pub item content".to_string(),
153 location: Some(
154 pair_loc
155 .clone()
156 .with_hint("use 'pub fn', 'pub enum', 'pub type', or 'pub { name }'"),
157 ),
158 })?;
159
160 let item = match next_pair.as_rule() {
161 Rule::foreign_function_def => {
162 ExportItem::ForeignFunction(functions::parse_foreign_function_def(next_pair)?)
163 }
164 Rule::extern_native_function_def => {
165 ExportItem::ForeignFunction(functions::parse_extern_native_function_def(next_pair)?)
166 }
167 Rule::function_def => ExportItem::Function(functions::parse_function_def(next_pair)?),
168 Rule::builtin_function_decl => {
169 ExportItem::BuiltinFunction(functions::parse_builtin_function_decl(next_pair)?)
170 }
171 Rule::builtin_type_decl => {
172 ExportItem::BuiltinType(crate::parser::types::parse_builtin_type_decl(next_pair)?)
173 }
174 Rule::type_alias_def => {
175 ExportItem::TypeAlias(crate::parser::types::parse_type_alias_def(next_pair)?)
176 }
177 Rule::enum_def => ExportItem::Enum(crate::parser::types::parse_enum_def(next_pair)?),
178 Rule::struct_type_def => {
179 ExportItem::Struct(crate::parser::types::parse_struct_type_def(next_pair)?)
180 }
181 Rule::native_struct_type_def => ExportItem::Struct(
182 crate::parser::types::parse_native_struct_type_def(next_pair)?,
183 ),
184 Rule::interface_def => {
185 ExportItem::Interface(crate::parser::types::parse_interface_def(next_pair)?)
186 }
187 Rule::trait_def => ExportItem::Trait(crate::parser::types::parse_trait_def(next_pair)?),
188 Rule::annotation_def => {
189 ExportItem::Annotation(crate::parser::extensions::parse_annotation_def(next_pair)?)
190 }
191 Rule::variable_decl => {
192 let var_decl = items::parse_variable_decl(next_pair.clone())?;
193 match var_decl.pattern.as_identifier() {
194 Some(name) => {
195 let item = ExportItem::Named(vec![ExportSpec {
196 name: name.to_string(),
197 alias: None,
198 }]);
199 return Ok(ExportStmt {
200 item,
201 source_decl: Some(var_decl),
202 });
203 }
204 None => {
205 return Err(ShapeError::ParseError {
206 message: "destructuring patterns are not supported in pub declarations"
207 .to_string(),
208 location: Some(
209 pair_location(&next_pair)
210 .with_hint("use a simple name: 'pub let name = value'"),
211 ),
212 });
213 }
214 }
215 }
216 Rule::export_spec_list => {
217 let specs = parse_export_spec_list(next_pair)?;
218 ExportItem::Named(specs)
219 }
220 _ => {
221 return Err(ShapeError::ParseError {
222 message: format!("unexpected pub item type: {:?}", next_pair.as_rule()),
223 location: Some(pair_location(&next_pair)),
224 });
225 }
226 };
227
228 Ok(ExportStmt {
229 item,
230 source_decl: None,
231 })
232}
233
234fn parse_export_spec_list(pair: Pair<Rule>) -> Result<Vec<ExportSpec>> {
236 let mut specs = Vec::new();
237
238 for spec_pair in pair.into_inner() {
239 if spec_pair.as_rule() == Rule::export_spec {
240 specs.push(parse_export_spec(spec_pair)?);
241 }
242 }
243
244 Ok(specs)
245}
246
247fn parse_export_spec(pair: Pair<Rule>) -> Result<ExportSpec> {
249 let pair_loc = pair_location(&pair);
250 let mut inner = pair.into_inner();
251
252 let name_pair = inner.next().ok_or_else(|| ShapeError::ParseError {
253 message: "expected export specification name".to_string(),
254 location: Some(pair_loc),
255 })?;
256 let name = name_pair.as_str().to_string();
257 let alias = inner.next().map(|p| p.as_str().to_string());
258
259 Ok(ExportSpec { name, alias })
260}
261
262pub fn parse_module_decl(pair: Pair<Rule>) -> Result<ModuleDecl> {
264 let pair_loc = pair_location(&pair);
265 let mut annotations = Vec::new();
266 let mut name: Option<String> = None;
267 let mut name_span = crate::ast::Span::DUMMY;
268 let mut items_out: Vec<Item> = Vec::new();
269
270 for part in pair.into_inner() {
271 match part.as_rule() {
272 Rule::annotations => {
273 annotations = functions::parse_annotations(part)?;
274 }
275 Rule::ident => {
276 if name.is_none() {
277 name = Some(part.as_str().to_string());
278 name_span = pair_span(&part);
279 }
280 }
281 Rule::item => {
282 items_out.push(crate::parser::parse_item(part)?);
283 }
284 Rule::item_recovery => {
285 let span = part.as_span();
286 let text = part.as_str().trim();
287 let preview = if text.len() > 40 {
288 format!("{}...", &text[..40])
289 } else {
290 text.to_string()
291 };
292 return Err(ShapeError::ParseError {
293 message: format!("Syntax error in module body near: {}", preview),
294 location: Some(pair_location(&part).with_length(span.end() - span.start())),
295 });
296 }
297 _ => {}
298 }
299 }
300
301 let name = name.ok_or_else(|| ShapeError::ParseError {
302 message: "missing module name".to_string(),
303 location: Some(pair_loc),
304 })?;
305
306 Ok(ModuleDecl {
307 name,
308 name_span,
309 doc_comment: None,
310 annotations,
311 items: items_out,
312 })
313}