use prometeu_compiler::frontends::pbs::parser::Parser; use prometeu_compiler::frontends::pbs::ast::*; use serde_json; #[test] fn test_parse_empty_file() { let mut parser = Parser::new("", 0); let result = parser.parse_file().unwrap(); assert_eq!(result.imports.len(), 0); assert_eq!(result.decls.len(), 0); } #[test] fn test_parse_imports() { let source = r#" import std.io from "std"; import math from "./math.pbs"; "#; let mut parser = Parser::new(source, 0); let result = parser.parse_file().unwrap(); assert_eq!(result.imports.len(), 2); if let Node::Import(ref imp) = result.imports[0] { assert_eq!(imp.from, "std"); if let Node::ImportSpec(ref spec) = *imp.spec { assert_eq!(spec.path, vec!["std", "io"]); } else { panic!("Expected ImportSpec"); } } else { panic!("Expected Import"); } } #[test] fn test_parse_fn_decl() { let source = r#" fn add(a: int, b: int): int { return a + b; } "#; let mut parser = Parser::new(source, 0); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); if let Node::FnDecl(ref f) = result.decls[0] { assert_eq!(f.name, "add"); assert_eq!(f.params.len(), 2); assert_eq!(f.params[0].name, "a"); assert_eq!(f.params[1].name, "b"); } else { panic!("Expected FnDecl"); } } #[test] fn test_parse_type_decl() { let source = r#" pub declare struct Point { x: int, y: int } "#; let mut parser = Parser::new(source, 0); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); if let Node::TypeDecl(ref t) = result.decls[0] { assert_eq!(t.name, "Point"); assert_eq!(t.type_kind, "struct"); assert_eq!(t.vis, Some("pub".to_string())); } else { panic!("Expected TypeDecl"); } } #[test] fn test_parse_service_decl() { let source = r#" pub service Audio { fn play(sound: Sound); fn stop(): bool; } "#; let mut parser = Parser::new(source, 0); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); if let Node::ServiceDecl(ref s) = result.decls[0] { assert_eq!(s.name, "Audio"); assert_eq!(s.members.len(), 2); } else { panic!("Expected ServiceDecl"); } } #[test] fn test_parse_expressions() { let source = r#" fn main() { let x = 10 + 20 * 30; let y = (x - 5) / 2; foo(x, y); } "#; let mut parser = Parser::new(source, 0); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); } #[test] fn test_parse_if_when() { let source = r#" fn main(x: int) { if x > 0 { print("positive"); } else { print("non-positive"); } let msg = when { x == 0 -> { return "zero"; }, x == 1 -> { return "one"; } }; } "#; let mut parser = Parser::new(source, 0); let result = parser.parse_file().unwrap(); assert_eq!(result.decls.len(), 1); } #[test] fn test_parse_error_recovery() { let source = r#" fn bad() { let x = ; // Missing init let y = 10; } fn good() {} "#; let mut parser = Parser::new(source, 0); let result = parser.parse_file(); // It should fail but we should see both good and bad decls if we didn't return Err early // Currently parse_file returns Err if there are any errors. assert!(result.is_err()); } #[test] fn test_ast_json_snapshot() { let source = r#" fn main() { return 42; } "#; let mut parser = Parser::new(source, 0); let result = parser.parse_file().unwrap(); let json = serde_json::to_string_pretty(&Node::File(result)).unwrap(); // We don't assert the exact string here because spans will vary, // but we check that it serializes correctly and has the "kind" field. assert!(json.contains("\"kind\": \"File\"")); assert!(json.contains("\"kind\": \"FnDecl\"")); assert!(json.contains("\"name\": \"main\"")); }