monkey-compiler 0.9.1

a compiler for monkeylang
Documentation
#[cfg(test)]
mod tests {
    use std::rc::Rc;
    use crate::compiler_test::{run_compiler_test, CompilerTestCase};
    use crate::op_code::Opcode::*;
    use crate::op_code::{concat_instructions, make_instructions};
    use object::Object;

    #[test]
    fn test_functions() {
        let tests = vec![
            CompilerTestCase {
                input: "fn() { return 5 + 10; }",
                expected_constants: vec![
                    Object::Integer(5),
                    Object::Integer(10),
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpConst, &vec![0]),
                            make_instructions(OpConst, &vec![1]),
                            make_instructions(OpAdd, &vec![0]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 0,
                        num_parameters: 0,
                    })),
                ],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![2, 0]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
            CompilerTestCase {
                input: "fn() { 5 + 10; }",
                expected_constants: vec![
                    Object::Integer(5),
                    Object::Integer(10),
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpConst, &vec![0]),
                            make_instructions(OpConst, &vec![1]),
                            make_instructions(OpAdd, &vec![0]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 0,
                        num_parameters: 0,
                    })),
                ],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![2, 0]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
            CompilerTestCase {
                input: "fn() { 1; 2}",
                expected_constants: vec![
                    Object::Integer(1),
                    Object::Integer(2),
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpConst, &vec![0]),
                            make_instructions(OpPop, &vec![0]),
                            make_instructions(OpConst, &vec![1]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 0,
                        num_parameters: 0,
                    })),
                ],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![2, 0]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
        ];
        run_compiler_test(tests);
    }

    #[test]
    fn test_function_without_return_value() {
        let tests = vec![CompilerTestCase {
            input: "fn() { }",
            expected_constants: vec![Object::CompiledFunction(Rc::from(object::CompiledFunction {
                instructions: concat_instructions(&vec![make_instructions(OpReturn, &vec![0])])
                    .data,
                num_locals: 0,
                num_parameters: 0,
            }))],
            expected_instructions: vec![
                make_instructions(OpClosure, &vec![0, 0]),
                make_instructions(OpPop, &vec![0]),
            ],
        }];
        run_compiler_test(tests);
    }

    #[test]
    fn test_function_calls() {
        let tests = vec![
            CompilerTestCase {
                input: "fn() { 24 }();",
                expected_constants: vec![
                    Object::Integer(24),
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpConst, &vec![0]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 0,
                        num_parameters: 0,
                    })),
                ],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![1, 0]),
                    make_instructions(OpCall, &vec![0]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
            CompilerTestCase {
                input: "let noArg = fn() { 24; }; noArg();",
                expected_constants: vec![
                    Object::Integer(24),
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpConst, &vec![0]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 0,
                        num_parameters: 0,
                    })),
                ],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![1, 0]),
                    make_instructions(OpSetGlobal, &vec![0]),
                    make_instructions(OpGetGlobal, &vec![0]),
                    make_instructions(OpCall, &vec![0]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
            CompilerTestCase {
                input: "let oneArg = fn(a) { a; }; oneArg(24);",
                expected_constants: vec![
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpGetLocal, &vec![0]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 1,
                        num_parameters: 1,
                    })),
                    Object::Integer(24),
                ],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![0, 0]),
                    make_instructions(OpSetGlobal, &vec![0]),
                    make_instructions(OpGetGlobal, &vec![0]),
                    make_instructions(OpConst, &vec![1]),
                    make_instructions(OpCall, &vec![1]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
            CompilerTestCase {
                input: "let manyArg = fn(a, b, c) { a; b; c; }; manyArg(24, 25, 26);",
                expected_constants: vec![
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpGetLocal, &vec![0]),
                            make_instructions(OpPop, &vec![0]),
                            make_instructions(OpGetLocal, &vec![1]),
                            make_instructions(OpPop, &vec![0]),
                            make_instructions(OpGetLocal, &vec![2]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 3,
                        num_parameters: 3,
                    })),
                    Object::Integer(24),
                    Object::Integer(25),
                    Object::Integer(26),
                ],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![0, 0]),
                    make_instructions(OpSetGlobal, &vec![0]),
                    make_instructions(OpGetGlobal, &vec![0]),
                    make_instructions(OpConst, &vec![1]),
                    make_instructions(OpConst, &vec![2]),
                    make_instructions(OpConst, &vec![3]),
                    make_instructions(OpCall, &vec![3]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
        ];

        run_compiler_test(tests);
    }

    #[test]
    fn test_let_statement_scope() {
        let tests = vec![
            CompilerTestCase {
                input: "let num = 55; fn() { num; }",
                expected_constants: vec![
                    Object::Integer(55),
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpGetGlobal, &vec![0]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 0,
                        num_parameters: 0,
                    })),
                ],
                expected_instructions: vec![
                    make_instructions(OpConst, &vec![0]),
                    make_instructions(OpSetGlobal, &vec![0]),
                    make_instructions(OpClosure, &vec![1, 0]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
            CompilerTestCase {
                input: "fn() { let num = 55; num; }",
                expected_constants: vec![
                    Object::Integer(55),
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpConst, &vec![0]),
                            make_instructions(OpSetLocal, &vec![0]),
                            make_instructions(OpGetLocal, &vec![0]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 1,
                        num_parameters: 0,
                    })),
                ],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![1, 0]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
            CompilerTestCase {
                input: "fn() { let a = 55; let b = 77; a + b; }",
                expected_constants: vec![
                    Object::Integer(55),
                    Object::Integer(77),
                    Object::CompiledFunction(Rc::from(object::CompiledFunction {
                        instructions: concat_instructions(&vec![
                            make_instructions(OpConst, &vec![0]),
                            make_instructions(OpSetLocal, &vec![0]),
                            make_instructions(OpConst, &vec![1]),
                            make_instructions(OpSetLocal, &vec![1]),
                            make_instructions(OpGetLocal, &vec![0]),
                            make_instructions(OpGetLocal, &vec![1]),
                            make_instructions(OpAdd, &vec![0]),
                            make_instructions(OpReturnValue, &vec![0]),
                        ])
                            .data,
                        num_locals: 2,
                        num_parameters: 0,
                    })),
                ],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![2, 0]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
        ];
        run_compiler_test(tests);
    }

    #[test]
    fn test_builtins() {
        let tests = vec![
            CompilerTestCase {
                input: "len([]); push([], 1);",
                expected_constants: vec![Object::Integer(1)],
                expected_instructions: vec![
                    make_instructions(OpGetBuiltin, &vec![0]),
                    make_instructions(OpArray, &vec![0]),
                    make_instructions(OpCall, &vec![1]),
                    make_instructions(OpPop, &vec![0]),
                    make_instructions(OpGetBuiltin, &vec![5]),
                    make_instructions(OpArray, &vec![0]),
                    make_instructions(OpConst, &vec![0]),
                    make_instructions(OpCall, &vec![2]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
            CompilerTestCase {
                input: "fn() { len([]) }",
                expected_constants: vec![Object::CompiledFunction(Rc::from(object::CompiledFunction {
                    instructions: concat_instructions(&vec![
                        make_instructions(OpGetBuiltin, &vec![0]),
                        make_instructions(OpArray, &vec![0]),
                        make_instructions(OpCall, &vec![1]),
                        make_instructions(OpReturnValue, &vec![0]),
                    ])
                        .data,
                    num_locals: 0,
                    num_parameters: 0,
                }))],
                expected_instructions: vec![
                    make_instructions(OpClosure, &vec![0, 0]),
                    make_instructions(OpPop, &vec![0]),
                ],
            },
        ];
        run_compiler_test(tests);
    }
}