1
1
use budvm::{
2
    ir::{
3
        CodeBlockBuilder, CompareAction, Destination, Instruction, Literal, LiteralOrSource, Scope,
4
    },
5
    Comparison, Function, Value, ValueOrSource, VirtualMachine,
6
};
7

            
8
1
fn main() {
9
1
    let mut block = CodeBlockBuilder::default();
10
1
    let arg_n = block.new_argument("n");
11
1
    let if_greater_than_two = block.named_label("if_greater_than_two");
12
1
    // if n <= 2
13
1
    block.push(Instruction::Compare {
14
1
        comparison: Comparison::LessThanOrEqual,
15
1
        left: LiteralOrSource::from(&arg_n),
16
1
        right: LiteralOrSource::from(Literal::Integer(2)),
17
1
        action: CompareAction::JumpIfFalse(if_greater_than_two.clone()),
18
1
    });
19
1
    // return 1
20
1
    block.push(Instruction::Return(Some(LiteralOrSource::from(
21
1
        Literal::Integer(1),
22
1
    ))));
23
1
    // else (#if_greater_than_two)
24
1
    block.label(if_greater_than_two);
25
1
    // n - 1, push result to stack
26
1
    block.push(Instruction::Sub {
27
1
        left: LiteralOrSource::from(&arg_n),
28
1
        right: LiteralOrSource::from(Literal::Integer(1)),
29
1
        destination: Destination::Stack,
30
1
    });
31
1
    // recurse call
32
1
    block.push(Instruction::Call {
33
1
        function: None,
34
1
        arg_count: 1,
35
1
        destination: Destination::Stack,
36
1
    });
37
1
    // n - 2, push result to stack
38
1
    block.push(Instruction::Sub {
39
1
        left: LiteralOrSource::from(arg_n),
40
1
        right: LiteralOrSource::from(Literal::Integer(2)),
41
1
        destination: Destination::Stack,
42
1
    });
43
1
    // recurse call
44
1
    block.push(Instruction::Call {
45
1
        function: None,
46
1
        arg_count: 1,
47
1
        destination: Destination::Stack,
48
1
    });
49
1
    // Add the two variables together, and return the result.
50
1
    block.push(Instruction::Add {
51
1
        left: LiteralOrSource::Stack,
52
1
        right: LiteralOrSource::Stack,
53
1
        destination: Destination::Return,
54
1
    });
55
1
    let block = block.finish();
56
1
    println!("IR:");
57
1
    println!("{block}");
58
1

            
59
1
    // The code block needs to be linked, which will convert the intermediate
60
1
    // representation into virtual machine instructions.
61
1
    let mut vm = VirtualMachine::empty();
62
1
    let block = block.link(&vm).unwrap();
63
1
    // Define a new function in the virtual machine using the block we just linked.
64
1
    let fibonacci_vtable_index = vm
65
1
        .define_function(Function::new("fibonacci", 1, block))
66
1
        .unwrap();
67
1

            
68
1
    // Invoke the function by pushing a value and then calling the vtable index
69
1
    // we received from defining the function.
70
1
    let result: i64 = vm
71
1
        .run(
72
1
            &[
73
1
                budvm::Instruction::Push(ValueOrSource::Value(Value::Integer(10))),
74
1
                budvm::Instruction::Call {
75
1
                    vtable_index: Some(fibonacci_vtable_index),
76
1
                    arg_count: 1,
77
1
                    destination: budvm::Destination::Stack,
78
1
                },
79
1
            ],
80
1
            0,
81
1
        )
82
1
        .unwrap();
83
1
    assert_eq!(result, 55);
84
1
}
85

            
86
1
#[test]
87
1
fn runs() {
88
1
    main()
89
1
}