mortar_compiler 0.2.0

Mortar language compiler core library
Documentation
use crate::parser::{
    Arg, ChoiceDest, ChoiceItem, Condition, Event, EventAction, FuncCall, FunctionDecl, NodeDef,
    NodeJump, NodeStmt, Param, ParseHandler, Program, TopLevel,
};

fn check_parsing(source: &str, expected_program: Program) {
    let program = ParseHandler::parse_source_code(source).unwrap();
    assert_eq!(program, expected_program);
}

#[test]
fn test_parse_empty_source() {
    let source = "";
    let program = ParseHandler::parse_source_code(source).unwrap();
    assert_eq!(program.body.len(), 0);
}

#[test]
fn test_parse_node_def() {
    let source = r#"
        node start_node {
            text: "Hello, world!"
        } -> next_node
    "#;
    let expected = Program {
        body: vec![TopLevel::NodeDef(NodeDef {
            name: "start_node".to_string(),
            body: vec![NodeStmt::Text("Hello, world!".to_string())],
            jump: Some(NodeJump::Identifier("next_node".to_string())),
        })],
    };
    check_parsing(source, expected);
}

#[test]
fn test_parse_function_decl() {
    let source = r#"
        fn my_function(param1: String, param2: Number) -> String
    "#;
    let expected = Program {
        body: vec![TopLevel::FunctionDecl(FunctionDecl {
            name: "my_function".to_string(),
            params: vec![
                Param {
                    name: "param1".to_string(),
                    type_name: "String".to_string(),
                },
                Param {
                    name: "param2".to_string(),
                    type_name: "Number".to_string(),
                },
            ],
            return_type: Some("String".to_string()),
        })],
    };
    check_parsing(source, expected);
}

#[test]
fn test_parse_node_with_events() {
    let source = r#"
        node event_node {
            events: [
                0, say("hello")
                1.5, say("world").wait(1)
            ]
        }
    "#;
    let expected = Program {
        body: vec![TopLevel::NodeDef(NodeDef {
            name: "event_node".to_string(),
            body: vec![NodeStmt::Events(vec![
                Event {
                    index: 0.0,
                    action: EventAction {
                        call: FuncCall {
                            name: "say".to_string(),
                            args: vec![Arg::String("hello".to_string())],
                        },
                        chains: vec![],
                    },
                },
                Event {
                    index: 1.5,
                    action: EventAction {
                        call: FuncCall {
                            name: "say".to_string(),
                            args: vec![Arg::String("world".to_string())],
                        },
                        chains: vec![FuncCall {
                            name: "wait".to_string(),
                            args: vec![Arg::Number(1.0)],
                        }],
                    },
                },
            ])],
            jump: None,
        })],
    };
    check_parsing(source, expected);
}

#[test]
fn test_parse_node_with_choices() {
    let source = r#"
        node choice_node {
            choice: [
                "Choice 1" -> next_node,
                "Choice 2".when(is_ready) -> return,
                ("Choice 3").when(check(arg1)) -> break,
                "Choice 4" -> [
                    "Nested 1" -> nested_node
                ]
            ]
        }
    "#;
    let expected = Program {
        body: vec![TopLevel::NodeDef(NodeDef {
            name: "choice_node".to_string(),
            body: vec![NodeStmt::Choice(vec![
                ChoiceItem {
                    text: "Choice 1".to_string(),
                    condition: None,
                    target: ChoiceDest::Identifier("next_node".to_string()),
                },
                ChoiceItem {
                    text: "Choice 2".to_string(),
                    condition: Some(Condition::Identifier("is_ready".to_string())),
                    target: ChoiceDest::Return,
                },
                ChoiceItem {
                    text: "Choice 3".to_string(),
                    condition: Some(Condition::FuncCall(FuncCall {
                        name: "check".to_string(),
                        args: vec![Arg::Identifier("arg1".to_string())],
                    })),
                    target: ChoiceDest::Break,
                },
                ChoiceItem {
                    text: "Choice 4".to_string(),
                    condition: None,
                    target: ChoiceDest::NestedChoices(vec![ChoiceItem {
                        text: "Nested 1".to_string(),
                        condition: None,
                        target: ChoiceDest::Identifier("nested_node".to_string()),
                    }]),
                },
            ])],
            jump: None,
        })],
    };
    check_parsing(source, expected);
}

#[test]
fn test_parse_multiple_top_level() {
    let source = r#"
        fn func1()
        node node1 {}
        fn func2()
        node node2 {}
    "#;
    let program = ParseHandler::parse_source_code(source).unwrap();
    assert_eq!(program.body.len(), 4);
    assert!(matches!(program.body[0], TopLevel::FunctionDecl(_)));
    assert!(matches!(program.body[1], TopLevel::NodeDef(_)));
    assert!(matches!(program.body[2], TopLevel::FunctionDecl(_)));
    assert!(matches!(program.body[3], TopLevel::NodeDef(_)));
}