1pub mod ast;
50mod chumsky;
51pub mod error;
52
53pub use ast::*;
54pub use chumsky::{ParseError as ChumskyParseError, format_errors};
55pub use error::{ParseError, ParseResult};
56
57pub fn parse(source: &str) -> Result<File, Vec<ChumskyParseError>> {
64 let (ast, errors) = chumsky::parse(source);
65
66 if let Some(file) = ast {
67 if errors.is_empty() {
68 Ok(file)
69 } else {
70 Err(errors)
73 }
74 } else {
75 if errors.is_empty() {
77 Err(vec![ChumskyParseError {
78 span: atoxide_lexer::Span::new(0, 0, 1, 1),
79 message: "failed to parse".to_string(),
80 expected: vec![],
81 found: None,
82 help: None,
83 }])
84 } else {
85 Err(errors)
86 }
87 }
88}
89
90pub fn parse_with_recovery(source: &str) -> (Option<File>, Vec<ChumskyParseError>) {
96 chumsky::parse(source)
97}
98
99pub fn parse_with_formatted_errors(source: &str, filename: &str) -> (Option<File>, Option<String>) {
104 chumsky::parse_with_errors(source, filename)
105}
106
107pub fn parse_with_source(
111 source: &str,
112) -> (
113 Result<File, Vec<ChumskyParseError>>,
114 miette::NamedSource<String>,
115) {
116 let named_source = miette::NamedSource::new("<input>", source.to_string());
117 let result = parse(source);
118 (result, named_source)
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_parse_empty() {
127 let source = "";
128 let file = parse(source).unwrap();
129 assert!(file.statements.is_empty());
130 }
131
132 #[test]
133 fn test_parse_pragma() {
134 let source = "#pragma experiment(\"FOR_LOOP\")\n";
135 let file = parse(source).unwrap();
136 assert_eq!(file.statements.len(), 1);
137 assert!(matches!(file.statements[0], Statement::Pragma(_)));
138 }
139
140 #[test]
141 fn test_parse_import() {
142 let source = "import ElectricPower\n";
143 let file = parse(source).unwrap();
144 assert_eq!(file.statements.len(), 1);
145 assert!(matches!(file.statements[0], Statement::Import(_)));
146 }
147
148 #[test]
149 fn test_parse_from_import() {
150 let source = "from \"path/to/file.ato\" import Module\n";
151 let file = parse(source).unwrap();
152 assert_eq!(file.statements.len(), 1);
153 if let Statement::Import(import) = &file.statements[0] {
154 assert!(import.from_path.is_some());
155 } else {
156 panic!("Expected import statement");
157 }
158 }
159
160 #[test]
161 fn test_parse_simple_module() {
162 let source = "module M:\n pass\n";
163 let file = parse(source).unwrap();
164 assert_eq!(file.statements.len(), 1);
165 if let Statement::BlockDef(block) = &file.statements[0] {
166 assert_eq!(block.kind, BlockKind::Module);
167 assert_eq!(block.name.name, "M");
168 assert_eq!(block.body.len(), 1);
169 } else {
170 panic!("Expected block definition");
171 }
172 }
173
174 #[test]
175 fn test_parse_module_with_super() {
176 let source = "module Child from Parent:\n pass\n";
177 let file = parse(source).unwrap();
178 if let Statement::BlockDef(block) = &file.statements[0] {
179 assert!(block.super_type.is_some());
180 assert_eq!(block.super_type.as_ref().unwrap().parts[0].name, "Parent");
181 } else {
182 panic!("Expected block definition");
183 }
184 }
185
186 #[test]
187 fn test_parse_component() {
188 let source = "component C:\n pin p1\n";
189 let file = parse(source).unwrap();
190 if let Statement::BlockDef(block) = &file.statements[0] {
191 assert_eq!(block.kind, BlockKind::Component);
192 } else {
193 panic!("Expected component");
194 }
195 }
196
197 #[test]
198 fn test_parse_interface() {
199 let source = "interface I:\n pass\n";
200 let file = parse(source).unwrap();
201 if let Statement::BlockDef(block) = &file.statements[0] {
202 assert_eq!(block.kind, BlockKind::Interface);
203 } else {
204 panic!("Expected interface");
205 }
206 }
207
208 #[test]
209 fn test_parse_pin() {
210 let source = "module M:\n pin p1\n pin 1\n pin \"GND\"\n";
211 let file = parse(source).unwrap();
212 if let Statement::BlockDef(block) = &file.statements[0] {
213 assert_eq!(block.body.len(), 3);
214 } else {
215 panic!("Expected block");
216 }
217 }
218
219 #[test]
220 fn test_parse_signal() {
221 let source = "module M:\n signal sig\n";
222 let file = parse(source).unwrap();
223 if let Statement::BlockDef(block) = &file.statements[0] {
224 assert!(matches!(block.body[0], Statement::SignalDef(_)));
225 } else {
226 panic!("Expected block");
227 }
228 }
229
230 #[test]
231 fn test_parse_assignment() {
232 let source = "module M:\n x = 5\n";
233 let file = parse(source).unwrap();
234 if let Statement::BlockDef(block) = &file.statements[0] {
235 assert!(matches!(block.body[0], Statement::Assignment(_)));
236 } else {
237 panic!("Expected block");
238 }
239 }
240
241 #[test]
242 fn test_parse_new_expression() {
243 let source = "module M:\n x = new SomeType\n";
244 let file = parse(source).unwrap();
245 if let Statement::BlockDef(block) = &file.statements[0] {
246 if let Statement::Assignment(assign) = &block.body[0] {
247 assert!(matches!(assign.value, Assignable::New(_)));
248 } else {
249 panic!("Expected assignment");
250 }
251 } else {
252 panic!("Expected block");
253 }
254 }
255
256 #[test]
257 fn test_parse_new_with_count() {
258 let source = "module M:\n x = new SomeType[10]\n";
259 let file = parse(source).unwrap();
260 if let Statement::BlockDef(block) = &file.statements[0] {
261 if let Statement::Assignment(assign) = &block.body[0] {
262 if let Assignable::New(new_expr) = &assign.value {
263 assert!(new_expr.count.is_some());
264 } else {
265 panic!("Expected new expression");
266 }
267 } else {
268 panic!("Expected assignment");
269 }
270 } else {
271 panic!("Expected block");
272 }
273 }
274
275 #[test]
276 fn test_parse_new_with_template() {
277 let source = "module M:\n x = new SomeType<param=1>\n";
278 let file = parse(source).unwrap();
279 if let Statement::BlockDef(block) = &file.statements[0] {
280 if let Statement::Assignment(assign) = &block.body[0] {
281 if let Assignable::New(new_expr) = &assign.value {
282 assert!(new_expr.template.is_some());
283 } else {
284 panic!("Expected new expression");
285 }
286 } else {
287 panic!("Expected assignment");
288 }
289 } else {
290 panic!("Expected block");
291 }
292 }
293
294 #[test]
295 fn test_parse_connection() {
296 let source = "module M:\n a ~ b\n";
297 let file = parse(source).unwrap();
298 if let Statement::BlockDef(block) = &file.statements[0] {
299 assert!(matches!(block.body[0], Statement::Connection(_)));
300 } else {
301 panic!("Expected block");
302 }
303 }
304
305 #[test]
306 fn test_parse_directed_connection() {
307 let source = "module M:\n a ~> b ~> c\n";
308 let file = parse(source).unwrap();
309 if let Statement::BlockDef(block) = &file.statements[0] {
310 if let Statement::DirectedConnection(conn) = &block.body[0] {
311 assert_eq!(conn.direction, ConnectionDirection::Forward);
312 assert_eq!(conn.elements.len(), 3);
313 } else {
314 panic!("Expected directed connection");
315 }
316 } else {
317 panic!("Expected block");
318 }
319 }
320
321 #[test]
322 fn test_parse_backward_connection() {
323 let source = "module M:\n a <~ b <~ c\n";
324 let file = parse(source).unwrap();
325 if let Statement::BlockDef(block) = &file.statements[0] {
326 if let Statement::DirectedConnection(conn) = &block.body[0] {
327 assert_eq!(conn.direction, ConnectionDirection::Backward);
328 } else {
329 panic!("Expected directed connection");
330 }
331 } else {
332 panic!("Expected block");
333 }
334 }
335
336 #[test]
337 fn test_parse_retype() {
338 let source = "module M:\n x -> NewType\n";
339 let file = parse(source).unwrap();
340 if let Statement::BlockDef(block) = &file.statements[0] {
341 assert!(matches!(block.body[0], Statement::Retype(_)));
342 } else {
343 panic!("Expected block");
344 }
345 }
346
347 #[test]
348 fn test_parse_assert() {
349 let source = "module M:\n assert x > 5\n";
350 let file = parse(source).unwrap();
351 if let Statement::BlockDef(block) = &file.statements[0] {
352 assert!(matches!(block.body[0], Statement::Assert(_)));
353 } else {
354 panic!("Expected block");
355 }
356 }
357
358 #[test]
359 fn test_parse_assert_within() {
360 let source = "module M:\n assert x within 1 to 10\n";
361 let file = parse(source).unwrap();
362 if let Statement::BlockDef(block) = &file.statements[0] {
363 if let Statement::Assert(assert_stmt) = &block.body[0] {
364 assert_eq!(
365 assert_stmt.comparison.operations[0].kind,
366 CompareOpKind::Within
367 );
368 } else {
369 panic!("Expected assert");
370 }
371 } else {
372 panic!("Expected block");
373 }
374 }
375
376 #[test]
377 fn test_parse_trait() {
378 let source = "module M:\n trait some_trait\n";
379 let file = parse(source).unwrap();
380 if let Statement::BlockDef(block) = &file.statements[0] {
381 assert!(matches!(block.body[0], Statement::Trait(_)));
382 } else {
383 panic!("Expected block");
384 }
385 }
386
387 #[test]
388 fn test_parse_trait_with_constructor() {
389 let source = "module M:\n trait some_trait::constructor\n";
390 let file = parse(source).unwrap();
391 if let Statement::BlockDef(block) = &file.statements[0] {
392 if let Statement::Trait(trait_stmt) = &block.body[0] {
393 assert!(trait_stmt.constructor.is_some());
394 } else {
395 panic!("Expected trait");
396 }
397 } else {
398 panic!("Expected block");
399 }
400 }
401
402 #[test]
403 fn test_parse_trait_with_template() {
404 let source = "module M:\n trait some_trait<arg=1>\n";
405 let file = parse(source).unwrap();
406 if let Statement::BlockDef(block) = &file.statements[0] {
407 if let Statement::Trait(trait_stmt) = &block.body[0] {
408 assert!(trait_stmt.template.is_some());
409 } else {
410 panic!("Expected trait");
411 }
412 } else {
413 panic!("Expected block");
414 }
415 }
416
417 #[test]
418 fn test_parse_for_loop() {
419 let source = "module M:\n for item in container:\n pass\n";
420 let file = parse(source).unwrap();
421 if let Statement::BlockDef(block) = &file.statements[0] {
422 if let Statement::For(for_stmt) = &block.body[0] {
423 assert_eq!(for_stmt.variable.name, "item");
424 } else {
425 panic!("Expected for loop");
426 }
427 } else {
428 panic!("Expected block");
429 }
430 }
431
432 #[test]
433 fn test_parse_declaration() {
434 let source = "module M:\n field: ohm\n";
435 let file = parse(source).unwrap();
436 if let Statement::BlockDef(block) = &file.statements[0] {
437 assert!(matches!(block.body[0], Statement::Declaration(_)));
438 } else {
439 panic!("Expected block");
440 }
441 }
442
443 #[test]
444 fn test_parse_declaration_with_assignment() {
445 let source = "module M:\n field: ohm = 100\n";
446 let file = parse(source).unwrap();
447 if let Statement::BlockDef(block) = &file.statements[0] {
448 if let Statement::Assignment(assign) = &block.body[0] {
449 assert!(matches!(assign.target, AssignTarget::Declaration(_)));
450 } else {
451 panic!("Expected assignment");
452 }
453 } else {
454 panic!("Expected block");
455 }
456 }
457
458 #[test]
459 fn test_parse_physical_quantity() {
460 let source = "module M:\n x = 10kohm\n";
461 let file = parse(source).unwrap();
462 if let Statement::BlockDef(block) = &file.statements[0] {
463 if let Statement::Assignment(assign) = &block.body[0] {
464 assert!(matches!(assign.value, Assignable::Physical(_)));
465 } else {
466 panic!("Expected assignment");
467 }
468 } else {
469 panic!("Expected block");
470 }
471 }
472
473 #[test]
474 fn test_parse_range() {
475 let source = "module M:\n x = 1 to 10\n";
476 let file = parse(source).unwrap();
477 if let Statement::BlockDef(block) = &file.statements[0] {
478 if let Statement::Assignment(assign) = &block.body[0] {
479 if let Assignable::Physical(PhysicalLiteral::Range(_)) = &assign.value {
480 } else {
482 panic!("Expected range");
483 }
484 } else {
485 panic!("Expected assignment");
486 }
487 } else {
488 panic!("Expected block");
489 }
490 }
491
492 #[test]
493 fn test_parse_bilateral() {
494 let source = "module M:\n x = 10 +/- 5%\n";
495 let file = parse(source).unwrap();
496 if let Statement::BlockDef(block) = &file.statements[0] {
497 if let Statement::Assignment(assign) = &block.body[0] {
498 if let Assignable::Physical(PhysicalLiteral::Bilateral(_)) = &assign.value {
499 } else {
501 panic!("Expected bilateral");
502 }
503 } else {
504 panic!("Expected assignment");
505 }
506 } else {
507 panic!("Expected block");
508 }
509 }
510
511 #[test]
512 fn test_parse_arithmetic() {
513 let source = "module M:\n x = a + b * c\n";
514 let file = parse(source).unwrap();
515 if let Statement::BlockDef(block) = &file.statements[0] {
516 if let Statement::Assignment(assign) = &block.body[0] {
517 assert!(matches!(assign.value, Assignable::Arithmetic(_)));
518 } else {
519 panic!("Expected assignment");
520 }
521 } else {
522 panic!("Expected block");
523 }
524 }
525
526 #[test]
527 fn test_parse_string_stmt() {
528 let source = "module M:\n \"docstring\"\n";
529 let file = parse(source).unwrap();
530 if let Statement::BlockDef(block) = &file.statements[0] {
531 assert!(matches!(block.body[0], Statement::StringStmt(_)));
532 } else {
533 panic!("Expected block");
534 }
535 }
536
537 #[test]
538 fn test_parse_semicolon_separated() {
539 let source = "module M:\n pass; pass; pass\n";
540 let file = parse(source).unwrap();
541 if let Statement::BlockDef(block) = &file.statements[0] {
542 assert_eq!(block.body.len(), 3);
543 } else {
544 panic!("Expected block");
545 }
546 }
547
548 #[test]
549 fn test_parse_single_line_block() {
550 let source = "module M: pass\n";
551 let file = parse(source).unwrap();
552 if let Statement::BlockDef(block) = &file.statements[0] {
553 assert_eq!(block.body.len(), 1);
554 } else {
555 panic!("Expected block");
556 }
557 }
558
559 #[test]
560 fn test_parse_nested_modules() {
561 let source = "module A:\n module B:\n pass\n";
562 let file = parse(source).unwrap();
563 if let Statement::BlockDef(outer) = &file.statements[0] {
564 if let Statement::BlockDef(inner) = &outer.body[0] {
565 assert_eq!(inner.name.name, "B");
566 } else {
567 panic!("Expected inner block");
568 }
569 } else {
570 panic!("Expected outer block");
571 }
572 }
573
574 #[test]
575 fn test_parse_field_reference() {
576 let source = "module M:\n a.b.c = 1\n";
577 let file = parse(source).unwrap();
578 if let Statement::BlockDef(block) = &file.statements[0] {
579 if let Statement::Assignment(assign) = &block.body[0] {
580 if let AssignTarget::FieldRef(field_ref) = &assign.target {
581 assert_eq!(field_ref.parts.len(), 3);
582 } else {
583 panic!("Expected field ref");
584 }
585 } else {
586 panic!("Expected assignment");
587 }
588 } else {
589 panic!("Expected block");
590 }
591 }
592
593 #[test]
594 fn test_parse_field_with_index() {
595 let source = "module M:\n a[0].b = 1\n";
596 let file = parse(source).unwrap();
597 if let Statement::BlockDef(block) = &file.statements[0] {
598 if let Statement::Assignment(assign) = &block.body[0] {
599 if let AssignTarget::FieldRef(field_ref) = &assign.target {
600 assert!(field_ref.parts[0].index.is_some());
601 } else {
602 panic!("Expected field ref");
603 }
604 } else {
605 panic!("Expected assignment");
606 }
607 } else {
608 panic!("Expected block");
609 }
610 }
611
612 #[test]
613 fn test_parse_assert_multiply() {
614 let source = "module M:\n assert x >= y * 1.5\n";
615 let file = parse(source).unwrap();
616 if let Statement::BlockDef(block) = &file.statements[0] {
617 assert!(matches!(block.body[0], Statement::Assert(_)));
618 } else {
619 panic!("Expected block");
620 }
621 }
622
623 #[test]
624 fn test_parse_assign_multiply() {
625 let source = "module M:\n x = 300 * y\n";
627 let file = parse(source).unwrap();
628 if let Statement::BlockDef(block) = &file.statements[0] {
629 if let Statement::Assignment(assign) = &block.body[0] {
630 assert!(matches!(assign.value, Assignable::Arithmetic(_)));
631 } else {
632 panic!("Expected assignment");
633 }
634 } else {
635 panic!("Expected block");
636 }
637 }
638
639 #[test]
640 fn test_parse_assign_chained_multiply() {
641 let source = "module M:\n k_iset = 300 * k_i * k_r\n";
643 let file = parse(source).unwrap();
644 if let Statement::BlockDef(block) = &file.statements[0] {
645 if let Statement::Assignment(assign) = &block.body[0] {
646 assert!(matches!(assign.value, Assignable::Arithmetic(_)));
647 } else {
648 panic!("Expected assignment");
649 }
650 } else {
651 panic!("Expected block");
652 }
653 }
654
655 #[test]
656 fn test_parse_assert_multiply_rhs() {
657 let source = "module M:\n assert x >= y * 1.5\n";
659 let file = parse(source).unwrap();
660 if let Statement::BlockDef(block) = &file.statements[0] {
661 assert!(matches!(block.body[0], Statement::Assert(_)));
662 } else {
663 panic!("Expected block");
664 }
665 }
666
667 #[test]
668 fn test_physical_literal_still_assignable() {
669 let source = "module M:\n x = 10kohm +/- 10%\n";
671 let file = parse(source).unwrap();
672 if let Statement::BlockDef(block) = &file.statements[0] {
673 if let Statement::Assignment(assign) = &block.body[0] {
674 assert!(matches!(
675 assign.value,
676 Assignable::Physical(PhysicalLiteral::Bilateral(_))
677 ));
678 } else {
679 panic!("Expected assignment");
680 }
681 } else {
682 panic!("Expected block");
683 }
684 }
685
686 #[test]
687 fn test_parse_from_py_import() {
688 let source = r#"from "ResistanceMapper.py" import ResistanceMapper
691module M:
692 pass
693"#;
694 let file = parse(source).unwrap();
695 assert!(!file.statements.is_empty());
696 }
697
698 #[test]
699 fn test_error_recovery() {
700 let source = "module Bad\n pass\n";
702 let (ast, errors) = parse_with_recovery(source);
703
704 assert!(!errors.is_empty(), "Should have parse errors");
706 let _ = ast;
708 }
709}