Nilton Constantino 407111cfef
pr 07
2026-01-27 14:09:14 +00:00

156 lines
3.9 KiB
Rust

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\""));
}