use myc::ast::compiler::emitter; use myc::ast::parser::Parser; use std::fs; /// Verifies the round-trip property for all example scripts: /// /// 1. Parse source → `ast1` /// 2. Emit `ast1` → `code1` /// 3. Parse `code1` → `ast2` /// 4. Emit `ast2` → `code2` /// 5. Assert `code1 == code2` /// /// If the emitter is idempotent (a second pass produces the same output), /// the AST is structurally stable across round-trips. #[test] fn test_roundtrip_examples() { let entries = fs::read_dir("examples").expect("examples/ directory not found"); let mut tested = 0; for entry in entries.filter_map(|e| e.ok()) { let path = entry.path(); if path.extension().is_none_or(|ext| ext != "myc") { continue; } let name = path.file_name().unwrap().to_string_lossy().to_string(); let source = fs::read_to_string(&path) .unwrap_or_else(|e| panic!("Failed to read {}: {}", name, e)); // Pass 1: parse original source, emit to code1 let mut parser1 = Parser::new(&source); let ast1 = parser1.parse_expression(); assert!( !parser1.diagnostics.has_errors(), "{}: parse errors on original source: {:?}", name, parser1.diagnostics.items ); let code1 = emitter::emit(&ast1); // Pass 2: parse emitted code1, emit to code2 let mut parser2 = Parser::new(&code1); let ast2 = parser2.parse_expression(); assert!( !parser2.diagnostics.has_errors(), "{}: parse errors on round-tripped code:\n---\n{}\n---\nErrors: {:?}", name, code1, parser2.diagnostics.items ); let code2 = emitter::emit(&ast2); assert_eq!( code1, code2, "{}: emitter is not idempotent (AST changed after second round-trip)", name ); tested += 1; } assert!(tested > 0, "No .myc example files found in examples/"); }