stackr_rs/interpreter/
evaluate.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use super::*;

impl<State> Interpreter<State> {
    /// Resets the program counter and clears the program and program debug locations but preserves RAM and stack.
    #[allow(dead_code)]
    pub fn reset_program(&mut self) {
        self.program_counter = 0;
        self.program.clear();
        self.program_debug_locations.clear();
    }

    /// Load a program into the interpreter.
    pub(crate) fn load_program(&mut self, code: &str, path: Option<PathBuf>) -> Result<(), Err> {
        let mut location = Location::new(path);

        let mut maybe_make_word = |buffer: &mut String, word_location: &Location| {
            if !buffer.is_empty() {
                let word = buffer.trim();
                let location = word_location.clone();
                self.program_debug_locations.push(location);

                // Try to parse number
                let instruction = if let Ok(number) = word.parse::<Number>() {
                    Instruction::PushNumber(number)
                } else if word.starts_with('"') {
                    // Remove the first and last character
                    let mut word = word.to_string();
                    word.remove(0);
                    word.pop();
                    Instruction::PushString(word)
                } else {
                    Instruction::Address(self.get_address(word))
                };
                self.program.push(instruction);
                *buffer = String::new();
            }
        };

        let mut buffer = String::new();
        let mut word_location = location.clone();
        let mut making_string = false;
        let mut made_string = false;

        for c in code.chars() {
            if c == '"' {
                if !making_string {
                    making_string = true;
                } else {
                    let last_char = buffer.chars().last();
                    if last_char != Some('\\') {
                        making_string = false;
                        made_string = true;
                        buffer.push(c);
                    }
                }
            }
            if !making_string && c.is_whitespace() || made_string {
                made_string = false;
                maybe_make_word(&mut buffer, &word_location);
            } else {
                // set word location to current location if buffer is empty
                // as this is the first character of the word
                if buffer.is_empty() {
                    word_location = location.clone();
                }
                buffer.push(c);
            }

            // Update location
            if c == '\n' {
                location.new_line();
            } else {
                location.next();
            }
        }

        if making_string {
            return Err(("Unclosed string".into(), word_location));
        }

        // Handle case if buffer is not empty
        maybe_make_word(&mut buffer, &word_location);

        Ok(())
    }

    /// Execute the program.
    pub(crate) fn execute(&mut self) -> Result<(), Err> {
        while self.program_counter < self.program.len() {
            let instruction = self.program[self.program_counter].clone();
            self.execute_instruction(instruction)?;
            self.program_counter += 1;
        }

        Ok(())
    }

    /// Execute an instruction.
    pub(crate) fn execute_instruction(&mut self, instruction: Instruction) -> Result<(), Err> {
        match instruction {
            Instruction::PushString(string) => self.push_string(string),
            Instruction::PushNumber(number) => self.push_number(number),
            Instruction::Address(address) => {
                // If we are in read mode, we only want to push the address if it is not the end of the read mode
                if self.read_mode != ReadMode::Off && address != self.address_cache.read_mode_end {
                    if self.read_mode == ReadMode::SingleWord {
                        self.read_mode = ReadMode::Off;
                        let variable_location = self.next_address;
                        self.next_address = self.next_address.next();
                        self.ram
                            .insert(address, RamValue::Address(variable_location));
                    } else {
                        self.push_address(address);
                    }
                } else {
                    // Get address contents from memory and execute
                    let contents = self.ram.get(&address).ok_or((
                        format!("Address not found: {}", self.get_name(address)),
                        self.location(),
                    ))?;
                    let ops_to_execute = match contents {
                        RamValue::Compiled(ops) => Some(ops.clone()),
                        RamValue::BuiltIn(method) => {
                            method(self)?;
                            None
                        }
                        RamValue::Value(value) => {
                            self.stack.push(StackValue::Value(value.clone()));
                            None
                        }
                        RamValue::Address(address) => {
                            self.stack.push(address.into());
                            None
                        }
                    };
                    if let Some(ops) = ops_to_execute {
                        for op in ops {
                            self.execute_instruction(op)?;
                        }
                    }
                }
            }
        }
        Ok(())
    }
}