use glyph_parser::{ast::*, parse_glyph, ParseError};
#[test]
fn test_minimal_program() {
let source = r#"
@program(name="hello")
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
let module = result.unwrap();
assert_eq!(module.program.name, "hello");
assert_eq!(module.program.version, "0.1.0");
assert!(module.program.requires.is_empty());
}
#[test]
fn test_program_with_version() {
let source = r#"
@program(name="test", version="2.0.0")
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
let module = result.unwrap();
assert_eq!(module.program.name, "test");
assert_eq!(module.program.version, "2.0.0");
}
#[test]
fn test_program_with_capabilities() {
let source = r#"
@program(name="voice_demo", requires=["voice", "display"])
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
let module = result.unwrap();
assert_eq!(module.program.requires, vec!["voice", "display"]);
}
#[test]
fn test_missing_program_decorator() {
let source = r#"
def main():
pass
"#;
let result = parse_glyph(source);
assert!(result.is_err());
}
#[test]
fn test_missing_program_name() {
let source = r#"
@program(version="1.0.0")
"#;
let result = parse_glyph(source);
assert!(result.is_err());
match result {
Err(ParseError::MissingProgramField { field }) => {
assert_eq!(field, "name");
}
_ => panic!("Expected MissingProgramField error"),
}
}
#[test]
fn test_simple_function() {
let source = r#"
@program(name="test")
def greet(name: str) -> str:
return f"Hello, {name}!"
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
let module = result.unwrap();
assert_eq!(module.statements.len(), 1);
match &module.statements[0] {
Statement::FunctionDef(func) => {
assert_eq!(func.name, "greet");
assert_eq!(func.params.len(), 1);
assert_eq!(func.params[0].name, "name");
assert!(func.return_type.is_some());
}
_ => panic!("Expected function definition"),
}
}
#[test]
fn test_function_with_default_params() {
let source = r#"
@program(name="test")
def greet(name: str = "World", excited: bool = False):
pass
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
let module = result.unwrap();
match &module.statements[0] {
Statement::FunctionDef(func) => {
assert_eq!(func.params.len(), 2);
assert!(func.params[0].default.is_some());
assert!(func.params[1].default.is_some());
}
_ => panic!("Expected function definition"),
}
}
#[test]
fn test_let_binding() {
let source = r#"
@program(name="test")
def main():
let x = 42
let message: str = "Hello"
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_if_statement() {
let source = r#"
@program(name="test")
def check(x: int):
if x > 0:
return "positive"
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
let module = result.unwrap();
match &module.statements[0] {
Statement::FunctionDef(func) => {
assert_eq!(func.body.len(), 1);
match &func.body[0] {
Statement::If(if_stmt) => {
assert_eq!(if_stmt.then_body.len(), 1);
}
_ => panic!("Expected if statement"),
}
}
_ => panic!("Expected function definition"),
}
}
#[test]
fn test_match_statement() {
let source = r#"
@program(name="test")
def handle_result(r: result[int, str]):
match r:
case Ok(value):
return value
case Err(msg):
return 0
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_expressions() {
let source = r#"
@program(name="test")
def calculate():
let a = 1 + 2 * 3
let b = (1 + 2) * 3
let c = 2 ** 3
let d = not True or False and True
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_list_and_dict_literals() {
let source = r#"
@program(name="test")
def make_collections():
let nums = [1, 2, 3]
let empty = []
let config = {"name": "test", "debug": True}
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_function_calls() {
let source = r#"
@program(name="test")
def main():
voice.speak(text="Hello", rate=1.5)
result = process(data)
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_imports() {
let source = r#"
@program(name="test")
import math
from collections import defaultdict, Counter
def main():
pass
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
let module = result.unwrap();
assert_eq!(module.imports.len(), 2);
}
#[test]
fn test_type_annotations() {
let source = r#"
@program(name="test")
def process(
data: list[int],
mapping: dict[str, float],
maybe: optional[str],
future: promise[int],
outcome: result[str, Error]
) -> result[str, Error]:
pass
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_await_expression() {
let source = r#"
@program(name="test")
def fetch_data():
data = net.fetch(url="https://api.example.com") await
return data
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_pattern_matching_literals() {
let source = r#"
@program(name="test")
def classify(value):
match value:
case 0:
return "zero"
case "hello":
return "greeting"
case True:
return "yes"
case _:
return "other"
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_error_recovery_missing_colon() {
let source = r#"
@program(name="test")
def main()
pass
"#;
let result = parse_glyph(source);
assert!(result.is_err());
}
#[test]
fn test_error_recovery_bad_indent() {
let source = r#"
@program(name="test")
def main():
pass
"#;
let result = parse_glyph(source);
assert!(result.is_err());
}
#[test]
fn test_multiline_function_params() {
let source = r#"
@program(name="test")
def complex_function(
first_param: int,
second_param: str = "default",
third_param: list[float] = []
) -> optional[dict[str, int]]:
pass
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_nested_expressions() {
let source = r#"
@program(name="test")
def nested():
result = func1(func2(x, y), func3(z))
value = data.items[0].properties["name"]
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}
#[test]
fn test_comparison_operators() {
let source = r#"
@program(name="test")
def compare(a: int, b: int):
if a < b:
return -1
elif a > b:
return 1
else:
return 0
"#;
let result = parse_glyph(source);
assert!(result.is_ok());
}