mod address;
mod address_cache;
mod built_ins;
mod evaluate;
mod instruction;
mod location;
mod ram_value;
mod stack_value;
mod stringify;
pub(crate) use address_cache::*;
pub(crate) use instruction::*;
pub(crate) use ram_value::*;
pub use stack_value::*;
pub use address::*;
pub use built_ins::*;
pub use location::*;
use std::{collections::HashMap, io::Write, path::PathBuf};
pub type Err = (String, Location);
pub type Number = f32;
#[derive(Debug, PartialEq, Clone)]
pub enum Value {
Number(Number),
String(String),
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum ReadMode {
On,
Off,
SingleWord,
}
pub struct Interpreter<State> {
#[allow(unused)]
pub state: State,
address_cache: AddressCache,
break_loop: bool,
program_counter: usize,
program: Vec<Instruction>,
program_counter_stack: Vec<usize>,
compiling: bool,
program_debug_locations: Vec<Location>,
exit: bool,
read_mode: ReadMode,
repl_mode: bool,
next_address: Address,
ram: HashMap<Address, RamValue<State>>,
stack: Vec<StackValue>,
documentation_table: HashMap<Address, String>,
name_table: HashMap<String, Address>,
}
impl<State> Interpreter<State> {
pub fn new(state: State) -> Self {
let mut interpreter = Self {
state,
compiling: false,
break_loop: false,
address_cache: AddressCache::uninitalized(),
program_counter: 0,
program: vec![],
program_counter_stack: vec![],
program_debug_locations: vec![],
exit: false,
read_mode: ReadMode::Off,
repl_mode: false,
ram: HashMap::new(),
stack: vec![],
next_address: Address::default(),
documentation_table: HashMap::new(),
name_table: HashMap::new(),
};
interpreter.register_builtins();
AddressCache::initialize(&mut interpreter);
interpreter
}
pub fn evaluate(&mut self, code: &str, path: Option<PathBuf>) -> Result<(), Err> {
self.load_program(code, path)?;
self.execute()?;
Ok(())
}
pub fn get_name(&self, address: Address) -> String {
for (name, addr) in &self.name_table {
if *addr == address {
return name.clone();
}
}
for (k, value) in &self.ram {
if let Some(addr) = value.address() {
if addr == address {
return format!("@{}", self.get_name(*k));
}
}
}
format!("@UNKNOWN-{:?}", address)
}
pub fn pop(&mut self) -> Result<StackValue, Err> {
self.stack
.pop()
.ok_or(("Stack is empty".to_string(), self.location()))
}
pub fn pop_bool(&mut self) -> Result<bool, Err> {
match self.pop()? {
StackValue::Value(Value::Number(number)) => Ok(number != 0.0),
_ => Err(("Expected a boolean/number".to_string(), self.location())),
}
}
pub fn pop_number(&mut self) -> Result<f32, Err> {
match self.pop()? {
StackValue::Value(Value::Number(number)) => Ok(number),
_ => Err(("Expected a number".to_string(), self.location())),
}
}
pub fn push_number(&mut self, number: f32) {
self.stack.push(StackValue::Value(Value::Number(number)));
}
pub fn push_address(&mut self, address: Address) {
self.stack.push(StackValue::Address(address));
}
pub fn pop_address(&mut self) -> Result<Address, Err> {
match self.pop()? {
StackValue::Address(address) => Ok(address),
_ => Err(("Expected an address".to_string(), self.location())),
}
}
pub fn push_string(&mut self, string: String) {
self.stack.push(StackValue::Value(Value::String(string)));
}
pub fn pop_string(&mut self) -> Result<String, Err> {
match self.pop()? {
StackValue::Value(Value::String(string)) => Ok(string),
_ => Err(("Expected a string".to_string(), self.location())),
}
}
pub fn start_repl(&mut self) -> Result<(), Err> {
self.repl_mode = true;
println!("Use 'repl-exit' to exit REPL mode.");
while !self.exit && self.repl_mode {
let mut input = String::new();
print!("> ");
std::io::stdout().flush().unwrap();
std::io::stdin().read_line(&mut input).unwrap();
let input = format!("{} print-stack", input.trim());
match self.evaluate(&input, None) {
Ok(_) => (),
Err((e, location)) => println!("{}: {}", location, e),
}
}
Ok(())
}
pub fn register_builtin(
&mut self,
name: &str,
stack_modification: &str,
documentation: &str,
example: &str,
func: BuiltIn<State>,
) {
let address = self.get_address(name);
self.register_documentation(address, stack_modification, documentation, example);
self.ram.insert(address, RamValue::BuiltIn(func));
}
pub(crate) fn register_documentation(
&mut self,
address: Address,
stack_modification: &str,
documentation: &str,
example: &str,
) {
let stack_modification = if stack_modification.is_empty() {
"".to_string()
} else {
format!("\t( {} )", stack_modification.replace("...", "..").trim())
};
let example = if example.is_empty() {
"".to_string()
} else {
format!("\n\tExample '{}'", example)
};
let documentation = documentation.trim();
let documentation = format!("{stack_modification}\n\t{}{}", documentation, example);
self.documentation_table.insert(address, documentation);
}
pub fn print_documentation(&self) {
println!("Documentation:");
let mut names = self.name_table.keys().cloned().collect::<Vec<_>>();
names.sort();
for name in names {
let address = self.name_table[&name];
if let Some(documentation) = self.documentation_table.get(&address) {
println!("{} {}\n", name, documentation);
}
}
println!();
}
pub(crate) fn get_address(&mut self, name: &str) -> Address {
if let Some(address) = self.name_table.get(name) {
address.clone()
} else {
let address = self.next_address;
self.next_address = self.next_address.next();
self.name_table.insert(name.to_string(), address);
address
}
}
pub(crate) fn chomp_instruction(&mut self) -> Result<Instruction, Err> {
if self.program_counter >= self.program.len() {
return Err(("No more instructions".to_string(), self.location()));
}
let instruction = self.program[self.program_counter].clone();
self.program_counter += 1;
Ok(instruction)
}
pub fn location(&self) -> Location {
if self.program_debug_locations.is_empty()
|| self.program_counter >= self.program_debug_locations.len()
{
return Location::default();
}
self.program_debug_locations[self.program_counter].clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_address() {
let mut interpreter = Interpreter::new(());
let expected = interpreter.next_address;
let actual = interpreter.get_address("test");
assert_eq!(expected, actual);
}
#[test]
fn pop_returns_err_if_stack_is_empty() {
let mut interpreter = Interpreter::new(());
let err = interpreter.pop().unwrap_err();
assert_eq!(err, ("Stack is empty".to_string(), Location::default()));
}
#[test]
fn pop_number_returns_err_if_not_number() {
let mut interpreter = Interpreter::new(());
interpreter.push_address(Address::default());
let err = interpreter.pop_number().unwrap_err();
assert_eq!(err, ("Expected a number".to_string(), Location::default()));
}
#[test]
fn pop_bool_returns_err_if_not_bool() {
let mut interpreter = Interpreter::new(());
interpreter.push_string("hello".to_string());
let err = interpreter.pop_bool().unwrap_err();
assert_eq!(
err,
("Expected a boolean/number".to_string(), Location::default())
);
}
#[test]
fn pop_bool_returns_true() {
let mut interpreter = Interpreter::new(());
interpreter.push_number(0.1);
let value = interpreter.pop_bool().unwrap();
assert_eq!(value, true);
}
#[test]
fn pop_bool_returns_false() {
let mut interpreter = Interpreter::new(());
interpreter.push_number(0.0);
let value = interpreter.pop_bool().unwrap();
assert_eq!(value, false);
}
#[test]
fn pop_number_returns_value() {
let mut interpreter = Interpreter::new(());
interpreter.evaluate("1", None).unwrap();
let value = interpreter.pop_number().unwrap();
assert_eq!(value, 1.0);
}
#[test]
fn pop_string_returns_err_if_not_string() {
let mut interpreter = Interpreter::new(());
interpreter.evaluate("1", None).unwrap();
let err = interpreter.pop_string().unwrap_err();
assert_eq!(err, ("Expected a string".to_string(), Location::default()));
}
#[test]
fn pop_string_returns_value() {
let mut interpreter = Interpreter::new(());
interpreter.push_string("hello".to_string());
let value = interpreter.pop_string().unwrap();
assert_eq!(value, "hello");
}
#[test]
fn pop_address_returns_err_if_not_address() {
let mut interpreter = Interpreter::new(());
interpreter.evaluate("1", None).unwrap();
let err = interpreter.pop_address().unwrap_err();
assert_eq!(
err,
("Expected an address".to_string(), Location::default())
);
}
#[test]
fn pop_address_returns_value() {
let mut interpreter = Interpreter::new(());
let address = interpreter.next_address;
interpreter.push_address(address.clone());
let value = interpreter.pop_address().unwrap();
assert_eq!(value, address);
}
#[test]
fn parse_string_returns_err_if_not_closed() {
let code = r#""hello"#;
let mut interpreter = Interpreter::new(());
let result = interpreter.evaluate(code, None);
assert!(result.is_err());
assert_eq!(result.unwrap_err().0, "Unclosed string");
}
#[test]
fn parse_string_puts_string_on_stack() {
let code = r#""hello""#;
let mut interpreter = Interpreter::new(());
interpreter.evaluate(code, None).unwrap();
let value = interpreter.pop_string().unwrap();
assert_eq!(value, "hello");
}
#[test]
fn var_only_creates_if_not_already_in_ram() {
let mut interpreter = Interpreter::new(());
let address = interpreter.next_address;
interpreter.evaluate("var test", None).unwrap();
let first_call = interpreter.ram.get(&address).unwrap().clone();
interpreter.evaluate("var test", None).unwrap();
let second_call = interpreter.ram.get(&address).unwrap().clone();
assert_eq!(first_call, second_call);
}
}