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.get_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(())
}
}