1
1
use std::io::{stdout, Write};
2

            
3
use budvm::{
4
    ir::{CodeBlock, Destination, Instruction, Literal, LiteralOrSource},
5
    DynamicValue, FaultKind, PoppedValues, Symbol, Value, ValueKind, VirtualMachine,
6
};
7

            
8
1
fn main() {
9
1
    // Create a runtime with a function `stdout()` which returns our dynamic
10
1
    // `StdOut` type.
11
1
    let mut vm =
12
1
        VirtualMachine::empty().with_native_function("stdout", |args: &mut PoppedValues<'_>| {
13
1
            args.verify_empty()?;
14
1
            Ok(Value::dynamic(StdOut))
15
1
        });
16
1

            
17
1
    let mut block = CodeBlock::build();
18
1

            
19
1
    // Call the stdout function we defined above, and store the result (our
20
1
    // StdOut instance) in a temporary variable.
21
1
    let stdout = block.new_temporary_variable();
22
1
    block.push(Instruction::Call {
23
1
        function: Some(Symbol::from("stdout")),
24
1
        arg_count: 0,
25
1
        destination: Destination::from(&stdout),
26
1
    });
27
1

            
28
1
    // Call write on our StdOut instance, passing in "Hello, World!".
29
1
    block.push(Instruction::Push(LiteralOrSource::from(Literal::from(
30
1
        "Hello, World!",
31
1
    ))));
32
1
    block.push(Instruction::CallInstance {
33
1
        target: LiteralOrSource::from(&stdout),
34
1
        name: Symbol::from("write"),
35
1
        arg_count: 1,
36
1
        destination: Destination::Return,
37
1
    });
38
1
    // Finish building the ir, link the code block, and execute it.
39
1
    let result: String = block
40
1
        .finish()
41
1
        .link(&vm)
42
1
        .unwrap()
43
1
        .execute_in(&mut vm)
44
1
        .unwrap();
45
1

            
46
1
    // The function not only outputs to stdout, but it also returns the value.
47
1
    assert_eq!(result, "Hello, World!");
48
1
}
49

            
50
#[derive(Debug, Clone)]
51
pub struct StdOut;
52

            
53
impl DynamicValue for StdOut {
54
    fn is_truthy(&self) -> bool {
55
        true
56
    }
57

            
58
    fn kind(&self) -> Symbol {
59
        Symbol::from("StdOut")
60
    }
61

            
62
1
    fn call(&self, name: &Symbol, args: &mut PoppedValues<'_>) -> Result<Value, FaultKind> {
63
1
        // Look up the function being called
64
1
        match name.as_str() {
65
1
            "write" => {
66
1
                let value = args.next_argument(&Symbol::from("value"))?;
67
1
                args.verify_empty()?;
68

            
69
1
                let mut stdout = stdout().lock();
70
1
                if let Some(value) = value.as_dynamic::<String>() {
71
                    // Value is a string, print the string as-is
72
1
                    stdout.write_all(value.as_bytes())?;
73
                } else {
74
                    // Value isn't a string, use Display to convert it.
75
                    let value = value.to_string();
76
                    stdout.write_all(value.as_bytes())?;
77
                }
78

            
79
1
                stdout.write_all(b"\n")?;
80
1
                stdout.flush()?;
81
1
                Ok(value)
82
            }
83
            _ => Err(FaultKind::UnknownFunction {
84
                kind: ValueKind::Dynamic(self.kind()),
85
                name: name.clone(),
86
            }),
87
        }
88
1
    }
89
}
90

            
91
1
#[test]
92
1
fn runs() {
93
1
    main();
94
1
}