1use super::ast::Span;
2use super::Rule;
3use crate::error::LemmaError;
4use crate::semantic::TypeDef;
5use pest::iterators::Pair;
6use std::sync::Arc;
7
8pub(crate) fn parse_type_definition(
9 pair: Pair<Rule>,
10 attribute: &str,
11 doc_name: &str,
12) -> Result<TypeDef, LemmaError> {
13 let span = Span::from_pest_span(pair.as_span());
14 let pair_str = pair.as_str();
15 let source_location = crate::Source::new(attribute, span.clone(), doc_name);
16 let mut type_name = None;
17 let mut type_arrow_chain = None;
18
19 for inner_pair in pair.into_inner() {
20 match inner_pair.as_rule() {
21 Rule::type_name_def => {
22 type_name = Some(inner_pair.as_str().to_string());
23 }
24 Rule::type_arrow_chain => {
25 type_arrow_chain = Some(inner_pair);
26 }
27 _ => {}
28 }
29 }
30
31 let type_name_str = type_name.ok_or_else(|| {
32 LemmaError::engine(
33 "Grammar error: type_definition missing type_name_def",
34 span.clone(),
35 attribute,
36 Arc::from(pair_str),
37 doc_name,
38 1,
39 None::<String>,
40 )
41 })?;
42
43 let arrow_chain_pair = type_arrow_chain.ok_or_else(|| {
44 LemmaError::engine(
45 "Grammar error: type_definition missing type_arrow_chain",
46 span,
47 attribute,
48 Arc::from(pair_str),
49 doc_name,
50 1,
51 None::<String>,
52 )
53 })?;
54
55 let (parent, overrides, _from) =
56 parse_type_arrow_chain_with_commands(arrow_chain_pair, attribute, doc_name)?;
57 Ok(TypeDef::Regular {
60 source_location,
61 name: type_name_str,
62 parent,
63 overrides,
64 })
65}
66
67pub(crate) fn parse_type_import(
68 pair: Pair<Rule>,
69 attribute: &str,
70 doc_name: &str,
71) -> Result<TypeDef, LemmaError> {
72 let span = Span::from_pest_span(pair.as_span());
73 let pair_str = pair.as_str();
74 let source_location = crate::Source::new(attribute, span.clone(), doc_name);
75 let type_import_def = pair.into_inner().next().ok_or_else(|| {
77 LemmaError::engine(
78 "Grammar error: type_import must contain type_import_def",
79 span.clone(),
80 attribute,
81 Arc::from(pair_str),
82 doc_name,
83 1,
84 None::<String>,
85 )
86 })?;
87
88 let mut type_names = Vec::new();
89 let mut imported_doc_name = None;
90
91 for inner_pair in type_import_def.into_inner() {
92 match inner_pair.as_rule() {
93 Rule::type_name_def => {
94 type_names.push(inner_pair.as_str().to_string());
95 }
96 Rule::doc_name => {
97 imported_doc_name = Some(inner_pair.as_str().to_string());
98 }
99 _ => {}
100 }
101 }
102
103 let imported_doc_name = imported_doc_name.ok_or_else(|| {
104 LemmaError::engine(
105 "Grammar error: type_import missing doc_name",
106 span.clone(),
107 attribute,
108 Arc::from(pair_str),
109 doc_name,
110 1,
111 None::<String>,
112 )
113 })?;
114
115 if type_names.is_empty() {
116 return Err(LemmaError::engine(
117 "Grammar error: type_import missing type_name_def",
118 span,
119 attribute,
120 Arc::from(pair_str),
121 doc_name,
122 1,
123 None::<String>,
124 ));
125 }
126
127 let source_type_name = if type_names.len() == 1 {
128 type_names[0].clone()
129 } else {
130 type_names[1].clone()
131 };
132
133 let final_type_name = type_names[0].clone();
134
135 Ok(TypeDef::Import {
136 source_location,
137 name: final_type_name,
138 source_type: source_type_name,
139 from: imported_doc_name,
140 overrides: None,
141 })
142}
143
144type TypeArrowChainResult = (String, Option<Vec<(String, Vec<String>)>>, Option<String>);
145
146pub(crate) fn parse_type_arrow_chain_with_commands(
147 pair: Pair<Rule>,
148 attribute: &str,
149 doc_name: &str,
150) -> Result<TypeArrowChainResult, LemmaError> {
151 let span = Span::from_pest_span(pair.as_span());
152 let pair_str = pair.as_str();
153 let mut inner = pair.into_inner();
154 let first = inner.next().ok_or_else(|| {
155 LemmaError::engine(
156 "Grammar error: type_arrow_chain cannot be empty",
157 span.clone(),
158 attribute,
159 Arc::from(pair_str),
160 doc_name,
161 1,
162 None::<String>,
163 )
164 })?;
165
166 let remaining_items: Vec<_> = inner.collect();
168
169 let (parent_name, from_doc) = match first.as_rule() {
170 Rule::type_name_def => {
171 let mut inner = first.clone().into_inner();
173 match inner.next() {
174 Some(child) => match child.as_rule() {
175 Rule::type_standard => {
176 (first.as_str().to_lowercase(), None)
178 }
179 Rule::type_custom => {
180 (first.as_str().to_string(), None)
182 }
183 _ => {
184 let child_span = Span::from_pest_span(child.as_span());
185 return Err(LemmaError::engine(
186 format!("Unexpected rule in type_name_def: {:?}", child.as_rule()),
187 child_span,
188 attribute,
189 Arc::from(first.as_str()),
190 doc_name,
191 1,
192 None::<String>,
193 ));
194 }
195 },
196 None => {
197 let first_span = Span::from_pest_span(first.as_span());
198 return Err(LemmaError::engine(
199 "Grammar error: type_name_def must contain type_custom or type_standard",
200 first_span,
201 attribute,
202 Arc::from(first.as_str()),
203 doc_name,
204 1,
205 None::<String>,
206 ));
207 }
208 }
209 }
210 Rule::type_import_def => {
211 let inner = first.clone().into_inner();
213 let mut type_name_def = None;
214 let mut imported_doc_name = None;
215
216 for item in inner {
217 match item.as_rule() {
218 Rule::type_name_def => {
219 let mut type_inner = item.clone().into_inner();
220 match type_inner.next() {
221 Some(child) => match child.as_rule() {
222 Rule::type_standard => {
223 type_name_def = Some(item.as_str().to_lowercase());
224 }
225 Rule::type_custom => {
226 type_name_def = Some(item.as_str().to_string());
227 }
228 _ => {
229 let child_span = Span::from_pest_span(child.as_span());
230 return Err(LemmaError::engine(
231 format!(
232 "Unexpected rule in type_name_def: {:?}",
233 child.as_rule()
234 ),
235 child_span,
236 attribute,
237 Arc::from(item.as_str()),
238 doc_name,
239 1,
240 None::<String>,
241 ));
242 }
243 },
244 None => {
245 let item_span = Span::from_pest_span(item.as_span());
246 return Err(LemmaError::engine(
247 "Grammar error: type_name_def must contain type_custom or type_standard",
248 item_span,
249 attribute,
250 Arc::from(item.as_str()),
251 doc_name,
252 1,
253 None::<String>,
254 ));
255 }
256 }
257 }
258 Rule::doc_name => {
259 imported_doc_name = Some(item.as_str().to_string());
260 }
261 _ => {}
262 }
263 }
264
265 let first_span = Span::from_pest_span(first.as_span());
266 let source_type = type_name_def.ok_or_else(|| {
267 LemmaError::engine(
268 "Grammar error: type_import_def missing type_name_def",
269 first_span.clone(),
270 attribute,
271 Arc::from(first.as_str()),
272 doc_name,
273 1,
274 None::<String>,
275 )
276 })?;
277
278 let from = imported_doc_name.ok_or_else(|| {
279 LemmaError::engine(
280 "Grammar error: type_import_def missing doc_name",
281 first_span,
282 attribute,
283 Arc::from(first.as_str()),
284 doc_name,
285 1,
286 None::<String>,
287 )
288 })?;
289
290 (source_type, Some(from))
291 }
292 _ => {
293 return Err(LemmaError::engine(
294 format!("Unexpected rule in type_arrow_chain: {:?}", first.as_rule()),
295 span.clone(),
296 attribute,
297 Arc::from(pair_str),
298 doc_name,
299 1,
300 None::<String>,
301 ));
302 }
303 };
304
305 let mut commands = Vec::new();
306 let mut expecting_command = false;
307
308 for item in remaining_items {
309 match item.as_rule() {
310 Rule::arrow_symbol => {
311 expecting_command = true;
312 }
313 Rule::command => {
314 if !expecting_command {
315 let item_span = Span::from_pest_span(item.as_span());
316 return Err(LemmaError::engine(
317 "Grammar error: command must follow arrow_symbol",
318 item_span,
319 attribute,
320 Arc::from(item.as_str()),
321 doc_name,
322 1,
323 None::<String>,
324 ));
325 }
326 let (command_name, args) = parse_command(item, attribute, doc_name)?;
327 commands.push((command_name, args));
328 expecting_command = false;
329 }
330 _ => {
331 let item_span = Span::from_pest_span(item.as_span());
332 return Err(LemmaError::engine(
333 format!("Unexpected rule in type_arrow_chain: {:?}", item.as_rule()),
334 item_span,
335 attribute,
336 Arc::from(item.as_str()),
337 doc_name,
338 1,
339 None::<String>,
340 ));
341 }
342 }
343 }
344
345 if expecting_command {
346 return Err(LemmaError::engine(
347 "Grammar error: arrow_symbol must be followed by command",
348 span.clone(),
349 attribute,
350 Arc::from(pair_str),
351 doc_name,
352 1,
353 None::<String>,
354 ));
355 }
356
357 let overrides = if commands.is_empty() {
358 None
359 } else {
360 Some(commands)
361 };
362
363 Ok((parent_name, overrides, from_doc))
364}
365
366fn parse_command(
367 pair: Pair<Rule>,
368 attribute: &str,
369 doc_name: &str,
370) -> Result<(String, Vec<String>), LemmaError> {
371 let span = Span::from_pest_span(pair.as_span());
372 let pair_str = pair.as_str();
373 let mut command_name = None;
374 let mut command_args = Vec::new();
375
376 for inner_pair in pair.into_inner() {
377 match inner_pair.as_rule() {
378 Rule::command_name => {
379 command_name = Some(inner_pair.as_str().to_string());
380 }
381 Rule::command_arg => {
382 command_args.push(inner_pair.as_str().to_string());
383 }
384 _ => {}
385 }
386 }
387
388 let name = command_name.ok_or_else(|| {
389 LemmaError::engine(
390 "Grammar error: command must contain command_name",
391 span,
392 attribute,
393 Arc::from(pair_str),
394 doc_name,
395 1,
396 None::<String>,
397 )
398 })?;
399
400 Ok((name, command_args))
401}
402
403#[cfg(test)]
408mod tests {
409 use crate::{parse, ResourceLimits};
410
411 #[test]
412 fn type_definition_parsing_produces_regular_typedef_with_overrides() {
413 let code = r#"doc test
414type dice = number -> minimum 0 -> maximum 6"#;
415
416 let docs = parse(code, "test.lemma", &ResourceLimits::default()).unwrap();
417 assert_eq!(docs.len(), 1);
418
419 let doc = &docs[0];
420 assert_eq!(doc.name, "test");
421 assert_eq!(doc.types.len(), 1);
422
423 let type_def = &doc.types[0];
424 match type_def {
425 crate::TypeDef::Regular {
426 name,
427 parent,
428 overrides,
429 ..
430 } => {
431 assert_eq!(name, "dice");
432 assert_eq!(parent, "number");
433 assert!(overrides.is_some());
434
435 let overrides = overrides.as_ref().unwrap();
436 assert_eq!(overrides.len(), 2);
437 assert_eq!(overrides[0].0, "minimum");
438 assert_eq!(overrides[0].1, vec!["0"]);
439 assert_eq!(overrides[1].0, "maximum");
440 assert_eq!(overrides[1].1, vec!["6"]);
441 }
442 other => panic!("Expected Regular type definition, got {:?}", other),
443 }
444 }
445}