extern crate rust_simple_stack_processor;
pub use rust_simple_stack_processor::GasLimit;
use rust_simple_stack_processor::Opcode;
use rust_simple_stack_processor::StackMachine;
mod error;
pub use error::ForthError;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::convert::TryInto;
#[derive(Debug)]
pub enum Token {
Number(i64),
Command(String),
Colon(String),
SemiColon,
End,
Error(String),
}
macro_rules! hashmap {
($( $key: expr => $val: expr ),*) => {{
let mut map = ::std::collections::HashMap::new();
$( map.insert($key, $val); )*
map
}}
}
pub struct ForthCompiler {
pub sm: StackMachine,
intrinsic_words: HashMap<&'static str, Vec<Opcode>>,
word_addresses: HashMap<String, usize>,
last_function: usize,
}
impl ForthCompiler {
pub fn new() -> ForthCompiler {
ForthCompiler {
sm: StackMachine::new(),
intrinsic_words: hashmap![
"POP" => vec![Opcode::POP],
"SWAP" => vec![Opcode::SWAP],
"ADD" => vec![Opcode::ADD],
"SUB" => vec![Opcode::SUB],
"MUL" => vec![Opcode::MUL],
"DIV" => vec![Opcode::DIV],
"DUP" => vec![Opcode::DUP],
"TRAP" => vec![Opcode::TRAP],
"INC" => vec![Opcode::LDI(1),Opcode::ADD],
"DEC" => vec![Opcode::LDI(-1),Opcode::ADD]
],
word_addresses: HashMap::new(),
last_function: 0,
}
}
}
#[derive(Debug, PartialEq)]
enum Mode {
Interpreting,
Compiling(String),
}
#[derive(Debug)]
struct DeferredIfStatement {
if_location: usize,
else_location: Option<usize>,
}
impl DeferredIfStatement {
pub fn new(if_location: usize) -> DeferredIfStatement {
DeferredIfStatement {
if_location: if_location,
else_location: None,
}
}
}
impl ForthCompiler {
fn tokenize_string(&self, s: &str) -> Result<Vec<Token>, ForthError> {
let mut tv = Vec::new();
let mut string_iter = s.split_whitespace();
loop {
match string_iter.next() {
None => return Ok(tv),
Some(string_token) => {
tv.push(match string_token.parse::<i64>() {
Ok(n) => Token::Number(n),
Err(_) => match string_token {
":" => match &string_iter.next() {
Some(next_token) => Token::Colon(next_token.to_string()),
None => {
return Err(ForthError::InvalidSyntax(String::from(
"No token after :, but one needed to compile",
)))
}
},
";" => Token::SemiColon,
_ => Token::Command(string_token.to_owned()),
},
});
}
}
}
}
fn compile_token_vector_compile_and_remove_word_definitions(
&mut self,
token_vector: &[Token],
) -> Result<Vec<Opcode>, ForthError> {
let mut tvi = Vec::new();
let mut mode = Mode::Interpreting;
let mut starting_position = 0;
for i in 0..token_vector.len() {
match &token_vector[i] {
Token::Colon(s) => {
match mode {
Mode::Interpreting => {
if i > starting_position {
tvi.append(
&mut self.compile_token_vector(
&token_vector[starting_position..i],
)?,
);
}
starting_position = i + 1;
mode = Mode::Compiling(String::from(s));
}
Mode::Compiling(_) => {
return Err(ForthError::InvalidSyntax(
"Second colon before semicolon".to_string(),
));
}
}
}
Token::SemiColon => {
match mode {
Mode::Interpreting => {
return Err(ForthError::InvalidSyntax(
"Semicolon before colon".to_string(),
));
}
Mode::Compiling(s) => {
self.sm.st.opcodes.resize(self.last_function, Opcode::NOP);
let mut compiled =
self.compile_token_vector(&token_vector[starting_position..i])?;
compiled.push(Opcode::RET);
let function_start = self.last_function;
self.last_function += compiled.len();
self.sm.st.opcodes.append(&mut compiled);
self.word_addresses.insert(s, function_start);
starting_position = i + 1;
mode = Mode::Interpreting;
}
}
}
_ => (),
}
}
if mode != Mode::Interpreting {
return Err(ForthError::MissingSemicolonAfterColon);
}
let mut compiled = self.compile_token_vector(&token_vector[starting_position..])?;
tvi.append(&mut compiled);
tvi.push(Opcode::RET);
return Ok(tvi);
}
fn compile_token_vector(&mut self, token_vector: &[Token]) -> Result<Vec<Opcode>, ForthError> {
let mut deferred_if_statements = Vec::new();
let mut tv: Vec<Opcode> = Vec::new();
for t in token_vector.iter() {
match t {
Token::Number(n) => {
tv.push(Opcode::LDI(*n));
}
Token::Command(s) => {
let current_instruction = tv.len();
match s.as_ref() {
"IF" => {
deferred_if_statements
.push(DeferredIfStatement::new(current_instruction));
tv.push(Opcode::LDI(0));
tv.push(Opcode::JRNZ);
}
"ELSE" => {
if let Some(x) = deferred_if_statements.last_mut() {
x.else_location = Some(current_instruction);
tv.push(Opcode::LDI(0));
tv.push(Opcode::JR);
} else {
return Err(ForthError::InvalidSyntax(
"ELSE without IF".to_owned(),
));
}
}
"THEN" => {
if let Some(x) = deferred_if_statements.pop() {
let if_jump_location = x.if_location;
let if_jump_offset = match x.else_location {
None => (current_instruction as u64
- (x.if_location + 1) as u64)
.try_into()
.unwrap(),
Some(el) => (current_instruction as u64 - el as u64 + 1)
.try_into()
.unwrap(),
};
let (else_jump_location, else_jump_offset): (
Option<usize>,
Option<i64>,
) = match x.else_location {
Some(x) => (
Some(x),
Some(
i64::try_from(
current_instruction as u64 - (x + 1) as u64,
)
.unwrap(),
),
),
None => (None, None),
};
tv[if_jump_location] = Opcode::LDI(if_jump_offset);
if let (Some(location), Some(offset)) =
(else_jump_location, else_jump_offset)
{
tv[location] = Opcode::LDI(offset);
}
} else {
return Err(ForthError::InvalidSyntax(
"THEN without IF".to_owned(),
));
}
}
_ => {
if let Some(offset) = self.word_addresses.get(s) {
tv.push(Opcode::LDI(*offset as i64));
tv.push(Opcode::CALL);
} else {
if let Some(ol) = self.intrinsic_words.get::<str>(s) {
tv.append(&mut ol.clone());
} else {
return Err(ForthError::UnknownToken(s.to_string()));
}
}
}
}
}
Token::Colon(_) => {
panic!("Colon should never reach this function");
}
Token::SemiColon => {
panic!("SemiColon should never reach this function");
}
Token::End => {
panic!("Token::End not coded yet");
}
Token::Error(_) => {
panic!("Token::Error not coded yet");
}
}
}
return Ok(tv);
}
fn execute_token_vector(
&mut self,
token_vector: &[Token],
gas_limit: GasLimit,
) -> Result<(), ForthError> {
let mut ol = self.compile_token_vector_compile_and_remove_word_definitions(token_vector)?;
self.sm.st.opcodes.resize(self.last_function, Opcode::NOP);
self.sm.st.opcodes.append(&mut ol);
self.sm.execute(self.last_function, gas_limit)?;
println!("Total opcodes defined: {}", self.sm.st.opcodes.len());
println!("Total opcodes executed: {}", self.sm.st.gas_used());
Ok(())
}
pub fn execute_string(&mut self, s: &str, gas_limit: GasLimit) -> Result<(), ForthError> {
let tv = self.tokenize_string(s)?;
self.execute_token_vector(&tv, gas_limit)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
extern crate rust_simple_stack_processor;
use rust_simple_stack_processor::StackMachineError;
use rust_simple_stack_processor::TrapHandled;
use rust_simple_stack_processor::TrapHandler;
#[test]
fn test_execute_intrinsics_1() {
let mut fc = ForthCompiler::new();
fc.execute_string("123 321 ADD 2 MUL", GasLimit::Limited(100))
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![888_i64]);
fc.execute_string("123 321 ADD 2 MUL", GasLimit::Limited(100))
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![888_i64, 888]);
}
#[test]
fn test_compile_1() {
let mut fc = ForthCompiler::new();
fc.execute_string(
": RickTest 123 321 ADD 2 MUL ; RickTest",
GasLimit::Limited(100),
)
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![888_i64]);
fc.execute_string("123 321 ADD 2 MUL RickTest", GasLimit::Limited(100))
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![888_i64, 888, 888]);
}
#[test]
fn test_compile_2() {
let mut fc = ForthCompiler::new();
fc.execute_string(
": RickTest 123 321 ADD 2 MUL ; RickTest : RickTestB 123 321 ADD 2 MUL ;",
GasLimit::Limited(100),
)
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![888_i64]);
fc.execute_string("123 321 ADD 2 MUL RickTest", GasLimit::Limited(100))
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![888_i64, 888, 888]);
}
#[test]
fn test_compile_3() {
let mut fc = ForthCompiler::new();
fc.execute_string(
"2 2 SUB POP : RickTest 123 321 ADD 2 MUL ; RickTest : RickTestB 123 321 ADD 2 MUL ; 3 3 SUB POP",
GasLimit::Limited(100),
)
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![888_i64]);
fc.execute_string("123 321 ADD 2 MUL RickTest", GasLimit::Limited(100))
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![888_i64, 888, 888]);
}
#[test]
fn test_compile_4() {
let mut fc = ForthCompiler::new();
fc.execute_string(
"2 2 SUB POP : RickTest 123 321 ADD 2 MUL ; : RickTestB 123 321 ADD 2 MUL ; 3 3 SUB",
GasLimit::Limited(100),
)
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![0_i64]);
fc.execute_string("123 321 ADD 2 MUL RickTest", GasLimit::Limited(100))
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![0_i64, 888, 888]);
}
#[test]
fn test_compile_fail_1() {
let mut fc = ForthCompiler::new();
match fc.execute_string(
"2 2 SUB POP : RickTest 123 321 ADD 2 MUL ; : : RickTestB 123 321 ADD 2 MUL ; 3 3 SUB",
GasLimit::Limited(100),
) {
Err(ForthError::UnknownToken(ref x)) if x == "RickTestB" => (),
r => panic!("Incorrect error type returned {:?}", r),
}
}
#[test]
fn test_compile_fail_2() {
let mut fc = ForthCompiler::new();
match fc.execute_string(
"2 2 SUB POP : RickTest 123 321 ADD 2 MUL ; ; : RickTestB 123 321 ADD 2 MUL ; 3 3 SUB",
GasLimit::Limited(100),
) {
Err(ForthError::InvalidSyntax(_)) => (),
r => panic!("Incorrect error type returned {:?}", r),
}
}
#[test]
fn test_compile_fail_3() {
let mut fc = ForthCompiler::new();
match fc.execute_string(
"2 2 SUB POP : RickTest 123 321 ADD 2 MUL ; : RickTestB 123 321 ADD 2 MUL ; : ERROR 3 3 SUB",
GasLimit::Limited(100),
) {
Err(ForthError::MissingSemicolonAfterColon) => (),
r => panic!("Incorrect error type returned {:?}", r),
}
}
#[test]
fn test_if_else_1() {
let mut fc = ForthCompiler::new();
fc.execute_string(
"1 2 3 POP POP POP 0 IF 1 2 ADD ELSE 3 4 ADD THEN",
GasLimit::Limited(100),
)
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![3_i64]);
}
#[test]
fn test_if_else_2() {
let mut fc = ForthCompiler::new();
fc.execute_string(
"1 2 3 POP POP POP 1 IF 1 2 ADD ELSE 3 4 ADD THEN",
GasLimit::Limited(100),
)
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![7_i64]);
}
#[test]
fn test_if_else_3() {
let mut fc = ForthCompiler::new();
fc.execute_string("0 IF 1 2 ADD ELSE 3 4 ADD THEN", GasLimit::Limited(100))
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![3_i64]);
}
#[test]
fn test_if_else_4() {
let mut fc = ForthCompiler::new();
fc.execute_string("1 IF 1 2 ADD ELSE 3 4 ADD THEN", GasLimit::Limited(100))
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![7_i64]);
}
#[test]
fn test_trap_1() {
let mut fc = ForthCompiler::new();
fc.sm
.trap_handlers
.push(Box::from(TrapHandler::new(100, |_trap_id, st| {
let io_port = st
.number_stack
.pop()
.ok_or(StackMachineError::NumberStackUnderflow)?;
let io_value = st
.number_stack
.pop()
.ok_or(StackMachineError::NumberStackUnderflow)?;
println!(
"Simulated IO OUT command to Port: {} and Value: {}",
io_port, io_value
);
Ok(TrapHandled::Handled)
})));
fc.execute_string(
": IO_OUT 100 TRAP ; 123456 1000 IO_OUT",
GasLimit::Limited(100),
)
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![]);
}
#[test]
fn test_trap_2() {
let mut fc = ForthCompiler::new();
fc.sm
.trap_handlers
.push(Box::from(TrapHandler::new(101, |_trap_id, st| {
let io_port = st
.number_stack
.pop()
.ok_or(StackMachineError::NumberStackUnderflow)?;
let io_value = 654321_i64;
println!(
"Simulated IO IN command from Port: {} and Value: {}",
io_port, io_value
);
st.number_stack.push(io_value);
Ok(TrapHandled::Handled)
})));
fc.execute_string(": IO_IN 101 TRAP ; 1000 IO_IN", GasLimit::Limited(100))
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![654321]);
}
#[test]
fn test_trap_3() {
let mut fc = ForthCompiler::new();
fc.sm
.trap_handlers
.push(Box::from(TrapHandler::new(100, |_trap_id, st| {
let io_port = st
.number_stack
.pop()
.ok_or(StackMachineError::NumberStackUnderflow)?;
let io_value = st
.number_stack
.pop()
.ok_or(StackMachineError::NumberStackUnderflow)?;
println!(
"Simulated IO OUT command to Port: {} and Value: {}",
io_port, io_value
);
Ok(TrapHandled::Handled)
})));
fc.execute_string(
": IO_OUT 100 TRAP ; : OUT_DISPLAY 1000 IO_OUT ; 123456 OUT_DISPLAY",
GasLimit::Limited(100),
)
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![]);
}
#[test]
fn test_trap_4() {
let mut fc = ForthCompiler::new();
fc.sm
.trap_handlers
.push(Box::from(TrapHandler::new(101, |_trap_id, st| {
let io_port = st
.number_stack
.pop()
.ok_or(StackMachineError::NumberStackUnderflow)?;
let io_value = 654321_i64;
println!(
"Simulated IO IN command from Port: {} and Value: {}",
io_port, io_value
);
st.number_stack.push(io_value);
Ok(TrapHandled::Handled)
})));
fc.execute_string(
": IO_IN 101 TRAP ; : IN_KEYBOARD 1000 IO_IN ; IN_KEYBOARD",
GasLimit::Limited(100),
)
.unwrap();
assert_eq!(&fc.sm.st.number_stack, &vec![654321]);
}
}