glyph-parser 0.0.1

Python-like parser for the Glyph programming language
Documentation
//! Parser tests for Glyph

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() {
    // LLMs sometimes forget colons
    let source = r#"
@program(name="test")

def main()
    pass
"#;
    let result = parse_glyph(source);
    // Should fail but with a clear error
    assert!(result.is_err());
}

#[test]
fn test_error_recovery_bad_indent() {
    // LLMs sometimes mess up indentation
    let source = r#"
@program(name="test")

def main():
pass
"#;
    let result = parse_glyph(source);
    // Should fail but with a clear error
    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());
}