Last active
February 13, 2019 16:09
-
-
Save mbillingr/01ab259ec0a1e5bb8142b77afeb36cfb to your computer and use it in GitHub Desktop.
(WIP) A simple but powerful Forth dialect written in Rust.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const INLINE_THRESHOLD: usize = 32; // inline words up to a length of 32 commands | |
type Stack = Vec<Data>; | |
#[derive(Debug, Copy, Clone, PartialEq)] | |
struct WordId(usize); | |
type Result<T> = std::result::Result<T, ForthError>; | |
#[derive(Debug)] | |
enum ForthError { | |
NotInCompileMode, | |
EmptyStack, | |
UnknownWord(String), | |
ParseError, | |
MathError, | |
TypeError, | |
BoundsError, | |
} | |
#[derive(Debug, Clone)] | |
enum Data { | |
True, | |
False, | |
I32(i32), | |
String(String), | |
Block(Vec<Command>), | |
WordId(WordId), | |
} | |
impl std::fmt::Display for Data { | |
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
match self { | |
Data::True => write!(f, "true"), | |
Data::False => write!(f, "false"), | |
Data::I32(i) => write!(f, "{}", i), | |
Data::String(s) => write!(f, "\"{}\"", s), | |
Data::Block(ops) => write!(f, "[{:?}]", ops), | |
Data::WordId(id) => write!(f, "{:?}", id), | |
} | |
} | |
} | |
impl std::ops::Add for Data { | |
type Output = Result<Data>; | |
fn add(self, other: Data) -> Result<Data> { | |
match (self, other) { | |
(Data::I32(a), Data::I32(b)) => { | |
a.checked_add(b).map(Data::I32).ok_or(ForthError::MathError) | |
} | |
(Data::Block(mut a), Data::Block(b)) => { | |
a.extend(b); | |
Ok(a.into_data()) | |
} | |
(Data::String(mut a), Data::String(b)) => { | |
a += &b; | |
Ok(a.into_data()) | |
} | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
} | |
impl std::ops::Sub for Data { | |
type Output = Result<Data>; | |
fn sub(self, other: Data) -> Result<Data> { | |
match (self, other) { | |
(Data::I32(a), Data::I32(b)) => { | |
a.checked_sub(b).map(Data::I32).ok_or(ForthError::MathError) | |
} | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
} | |
impl std::ops::Mul for Data { | |
type Output = Result<Data>; | |
fn mul(self, other: Data) -> Result<Data> { | |
match (self, other) { | |
(Data::I32(a), Data::I32(b)) => { | |
a.checked_mul(b).map(Data::I32).ok_or(ForthError::MathError) | |
} | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
} | |
impl std::ops::Div for Data { | |
type Output = Result<Data>; | |
fn div(self, other: Data) -> Result<Data> { | |
match (self, other) { | |
(Data::I32(a), Data::I32(b)) => { | |
a.checked_div(b).map(Data::I32).ok_or(ForthError::MathError) | |
} | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
} | |
impl std::ops::Rem for Data { | |
type Output = Result<Data>; | |
fn rem(self, other: Data) -> Result<Data> { | |
match (self, other) { | |
(Data::I32(a), Data::I32(b)) => { | |
a.checked_rem(b).map(Data::I32).ok_or(ForthError::MathError) | |
} | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
} | |
impl Data { | |
fn exec(self, mut forth: Forth) -> Result<Forth> { | |
match self { | |
Data::Block(ops) => forth.exec_block(&ops), | |
Data::WordId(id) => { | |
let block = forth.words[id.0].ops.clone(); | |
forth.exec_block(&block) | |
} | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
fn call(self, mut forth: Forth) -> Result<Forth> { | |
match self { | |
Data::Block(ops) => forth.exec_block(&ops), | |
Data::WordId(id) => forth.call_wordid(id), | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
fn into_bool(self) -> Result<bool> { | |
match self { | |
Data::True => Ok(true), | |
Data::False => Ok(false), | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
fn into_i32(self) -> Result<i32> { | |
match self { | |
Data::I32(i) => Ok(i), | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
fn into_usize(self) -> Result<usize> { | |
match self { | |
Data::I32(i) if i >= 0 => Ok(i as usize), | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
fn into_char(self) -> Result<char> { | |
match self { | |
Data::I32(i) if i >= 0 && i <= 255 => Ok(i as u8 as char), | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
fn into_string(self) -> Result<String> { | |
match self { | |
Data::String(s) => Ok(s), | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
fn into_block(self) -> Result<Vec<Command>> { | |
match self { | |
Data::Block(block) => Ok(block), | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
fn into_wordid(self) -> Result<WordId> { | |
match self { | |
Data::WordId(id) => Ok(id), | |
_ => Err(ForthError::TypeError), | |
} | |
} | |
} | |
trait IntoData { | |
fn into_data(self) -> Data; | |
} | |
impl IntoData for Data { | |
fn into_data(self) -> Data { | |
self | |
} | |
} | |
impl IntoData for bool { | |
fn into_data(self) -> Data { | |
if self { | |
Data::True | |
} else { | |
Data::False | |
} | |
} | |
} | |
impl IntoData for i32 { | |
fn into_data(self) -> Data { | |
Data::I32(self) | |
} | |
} | |
impl IntoData for Vec<Command> { | |
fn into_data(self) -> Data { | |
Data::Block(self) | |
} | |
} | |
impl IntoData for WordId { | |
fn into_data(self) -> Data { | |
Data::WordId(self) | |
} | |
} | |
impl IntoData for String { | |
fn into_data(self) -> Data { | |
Data::String(self) | |
} | |
} | |
#[derive(Clone)] | |
enum Command { | |
Literal(Data), | |
Builtin(String, fn(Forth) -> Result<Forth>), | |
Call(WordId), | |
} | |
impl Command { | |
fn exec(&self, mut forth: Forth) -> Result<Forth> { | |
match self { | |
Command::Literal(x) => forth.stack.push(x.clone()), | |
Command::Builtin(_, func) => return func(forth), | |
Command::Call(id) => return forth.call_wordid(*id), | |
} | |
Ok(forth) | |
} | |
} | |
impl std::fmt::Debug for Command { | |
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
match self { | |
Command::Literal(x) => write!(f, "{:?}", x), | |
Command::Builtin(name, _) => write!(f, "{}", name), | |
Command::Call(id) => write!(f, "@{}", id.0), | |
} | |
} | |
} | |
struct Word { | |
name: String, | |
comment: String, | |
ops: Vec<Command>, | |
class: Option<WordId>, | |
} | |
impl Word { | |
fn new(name: &str, comment: &str, ops: Vec<Command>, class: Option<WordId>) -> Self { | |
Word { | |
name: name.to_string(), | |
comment: comment.to_string(), | |
ops, | |
class, | |
} | |
} | |
} | |
#[derive(Debug)] | |
enum Mode { | |
Execute, | |
Compile(Vec<Command>), | |
} | |
struct Forth { | |
mode: Vec<Mode>, | |
stack: Stack, | |
words: Vec<Word>, | |
} | |
impl Forth { | |
fn new() -> Self { | |
let forth = Forth { | |
stack: vec![], | |
words: vec![], | |
mode: vec![Mode::Execute], | |
}; | |
forth.populate_words().unwrap() | |
} | |
fn populate_words(mut self) -> Result<Self> { | |
// | |
// Tier 0 | |
// | |
// prefix handlers | |
self.add_builtin("prefix::", "new word", |mut forth| { | |
//println!("{:?}", forth.stack); | |
let default_class = forth.try_lookup("class:default")?; | |
forth.push(Data::WordId(default_class)); | |
forth = forth.call()?; | |
let class = forth.pop()?.into_wordid()?; | |
let name = forth.pop()?.into_string()?; | |
let ops = forth.pop()?.into_block()?; | |
forth.words.push( | |
WordBuilder::new() | |
.with_name_string(name) | |
.with_class(class) | |
.with_ops(ops) | |
.build(), | |
); | |
Ok(forth) | |
}); | |
self.add_builtin("prefix:\"", "string literal", |mut forth| { | |
let mut s = forth.pop()?.into_string()?; | |
match s.pop() { | |
Some('"') => {} | |
_ => return Err(ForthError::ParseError), | |
} | |
match forth.mode.last_mut().unwrap() { | |
Mode::Execute => forth.push(Data::String(s)), | |
Mode::Compile(ref mut target_ops) => { | |
target_ops.push(Command::Literal(Data::String(s))) | |
} | |
} | |
Ok(forth) | |
}); | |
self.add_builtin("prefix:(", "comment", |mut forth| { | |
let mut s = forth.pop()?.into_string()?; | |
match s.pop() { | |
Some(')') => {} | |
_ => return Err(ForthError::ParseError), | |
} | |
match forth.mode.last_mut().unwrap() { | |
Mode::Execute => {}, | |
Mode::Compile(ref mut target_ops) => {} | |
} | |
Ok(forth) | |
}); | |
// word classes | |
self.add_builtin("class:macro", "class handler", |forth| { | |
// expect word id on stack | |
match forth.mode.last().unwrap() { | |
Mode::Execute => forth.exec(), | |
Mode::Compile(_) => forth.exec(), | |
} | |
}); | |
self.add_builtin("class:word", "class handler", |forth| { | |
// expect word id on stack | |
match forth.mode.last().unwrap() { | |
Mode::Execute => forth.exec(), | |
Mode::Compile(_) => forth.compile(), | |
} | |
}); | |
self.add_word( | |
WordBuilder::new() | |
.with_name("class:default") | |
.with_class(self.lookup("class:macro").unwrap()) | |
.with_comment("default class") | |
.with_ops(vec![Command::Literal(Data::WordId( | |
self.lookup("class:word").unwrap(), | |
))]) | |
.build(), | |
); | |
// system commands | |
self.add_word( | |
WordBuilder::new() | |
.with_name("[") | |
.with_comment("begin block") | |
.with_class(self.try_lookup("class:macro")?) | |
.builtin(|mut forth| { | |
//println!("entering compile mode"); | |
forth.mode.push(Mode::Compile(vec![])); | |
Ok(forth) | |
}), | |
); | |
self.add_word( | |
WordBuilder::new() | |
.with_name("]") | |
.with_comment("finish block") | |
.with_class(self.try_lookup("class:macro")?) | |
.builtin(|mut forth| { | |
//println!("leaving compile mode"); | |
if let Mode::Compile(ops) = forth.mode.pop().unwrap() { | |
match forth.mode.last_mut().unwrap() { | |
Mode::Execute => forth.push(Data::Block(ops)), | |
Mode::Compile(ref mut target_ops) => { | |
target_ops.push(Command::Literal(Data::Block(ops))) | |
} | |
} | |
Ok(forth) | |
} else { | |
Err(ForthError::NotInCompileMode) | |
} | |
}), | |
); | |
self.add_builtin("lookup", "s -- (w) b", |mut forth| { | |
let name = forth.pop()?.into_string()?; | |
match forth.lookup(&name) { | |
None => forth.push(Data::False), | |
Some(id) => { | |
forth.push(id); | |
forth.push(Data::True); | |
} | |
} | |
Ok(forth) | |
}); | |
self.add_builtin("handle_class", "w -- ?", Forth::handle_class); | |
self.add_builtin("handle_literal", "s -- x", Forth::handle_literal); | |
self.add_macro("process_token", "", Forth::process_token); | |
// dictionary and word commands | |
// debug, info, and i/o commands | |
self.add_builtin(".", "x --", |mut forth| { | |
println!("{}", forth.pop()?); | |
Ok(forth) | |
}); | |
self.add_builtin(".s", "--", |forth| { | |
println!("{:?}", forth.stack); | |
Ok(forth) | |
}); | |
self.add_builtin("emit", "ch --", |mut forth| { | |
let ch = forth.pop()?.into_char()?; | |
print!("{:?}", ch); | |
Ok(forth) | |
}); | |
self.add_builtin("spaces", "n --", |mut forth| { | |
let n = forth.pop()?.into_i32()?; | |
for _ in 0..n { | |
print!(" "); | |
} | |
Ok(forth) | |
}); | |
// stack operations | |
self.add_builtin("drop", "x --", |mut forth| forth.pop().map(|_| forth)); | |
self.add_builtin("dup", "x -- x x", |mut forth| { | |
let x = forth.pop()?; | |
forth.push(x.clone()); | |
forth.push(x); | |
Ok(forth) | |
}); | |
self.add_builtin("swap", "x1 x2 -- x2 x1", |mut forth| { | |
let b = forth.pop()?; | |
let a = forth.pop()?; | |
forth.push(b); | |
forth.push(a); | |
Ok(forth) | |
}); | |
self.add_builtin("rot", "x1 x2 x3 -- x2 x3 x1", |mut forth| { | |
let c = forth.pop()?; | |
let b = forth.pop()?; | |
let a = forth.pop()?; | |
forth.push(b); | |
forth.push(c); | |
forth.push(a); | |
Ok(forth) | |
}); | |
self.add_builtin("n_dup", "n -- x", |mut forth| { | |
let n = forth.pop()?.into_usize()?; | |
let i = forth | |
.stack | |
.len() | |
.checked_sub(n) | |
.ok_or(ForthError::BoundsError)?; | |
let x = forth.stack[i].clone(); | |
forth.push(x); | |
Ok(forth) | |
}); | |
// branching | |
self.add_builtin("call", "w -- ?", Forth::call); | |
self.add_builtin("if", "b f1 f2 -- ?", |mut forth| { | |
let else_branch = forth.pop()?; | |
let if_branch = forth.pop()?; | |
let cond = forth.pop()?.into_bool()?; | |
let branch = if cond { if_branch } else { else_branch }; | |
branch.call(forth) | |
}); | |
// math operations | |
self.add_builtin("+", "a b -- sum", |mut forth| { | |
let b = forth.pop()?; | |
let a = forth.pop()?; | |
forth.push((a + b)?); | |
Ok(forth) | |
}); | |
self.add_builtin("-", "a b -- sub", |mut forth| { | |
let b = forth.pop()?; | |
let a = forth.pop()?; | |
forth.push((a - b)?); | |
Ok(forth) | |
}); | |
self.add_builtin("*", "a b -- prod", |mut forth| { | |
let b = forth.pop()?; | |
let a = forth.pop()?; | |
forth.push((a * b)?); | |
Ok(forth) | |
}); | |
self.add_builtin("/", "a b -- quot", |mut forth| { | |
let b = forth.pop()?; | |
let a = forth.pop()?; | |
forth.push((a / b)?); | |
Ok(forth) | |
}); | |
// string operations | |
self.add_builtin("str:split_at", "s n -- s s", |mut forth| { | |
let n = forth.pop()?.into_usize()?; | |
let s = forth.pop()?.into_string()?; | |
match s.char_indices().skip(n).next() { | |
None => forth.push2(s, String::new()), | |
Some((i, _)) => { | |
let (a, b) = s.split_at(i); | |
forth.push2(a.to_string(), b.to_string()); | |
} | |
} | |
Ok(forth) | |
}); | |
// | |
// Tier 1 | |
// | |
self = self.run(" [ 42 ] :fourty_two")?; | |
self = self.run(" [ \"prefix:\" swap + lookup ] :lookup_prefix")?; | |
// this does not work atm... | |
/*self = self.run("[ (token) | |
dup 1 str:split_at swap lookup_prefix | |
[ (token tail handler) rot drop call ] | |
[ (token tail) drop dup lookup | |
[ (token id) swap drop handle_class ] | |
[ (token) handle_literal ] | |
if ] | |
if ] :process_token")?;*/ | |
Ok(self) | |
} | |
fn run(mut self, input: &str) -> Result<Self> { | |
for token in tokenize(input) { | |
println!("token: {:?}", token); | |
self.push(token.to_string()); | |
//self = self.process_token()?; | |
self.push(self.try_lookup("process_token")?); | |
self = self.call()?; | |
} | |
Ok(self) | |
} | |
fn process_token(mut self) -> Result<Self> { | |
let token = self.pop()?.into_string()?; | |
println!("token: {:?}", token); | |
match token.chars().next() { | |
None => Ok(self), // ignore empty tokens | |
Some(prefix) => { | |
if let Some(word_id) = self.lookup_prefix(prefix) { | |
let tail = token.splitn(2, prefix).skip(1).next().unwrap(); | |
self.push(Data::String(tail.to_string())); | |
self.handle_prefix(word_id) | |
} else if let Some(word_id) = self.lookup(&token) { | |
self.push(word_id); | |
self.handle_class() | |
} else { | |
self.push(token); | |
self.handle_literal() | |
} | |
} | |
} | |
} | |
fn lookup(&self, name: &str) -> Option<WordId> { | |
self.words | |
.iter() | |
.enumerate() | |
.rev() | |
.find(|(_, word)| word.name == name) | |
.map(|(i, _)| WordId(i)) | |
} | |
fn try_lookup(&self, name: &str) -> Result<WordId> { | |
self.lookup(name) | |
.ok_or_else(|| ForthError::UnknownWord(name.to_string())) | |
} | |
fn lookup_prefix(&self, prefix: char) -> Option<WordId> { | |
let mut name = "prefix:".to_string(); | |
name.push(prefix); | |
self.lookup(&name) | |
} | |
fn get_word(&self, id: WordId) -> &Word { | |
&self.words[id.0] | |
} | |
fn add_word(&mut self, word: Word) -> WordId { | |
let id = WordId(self.words.len()); | |
self.words.push(word); | |
id | |
} | |
fn add_builtin( | |
&mut self, | |
name: &str, | |
comment: &str, | |
func: fn(Forth) -> Result<Forth>, | |
) -> WordId { | |
self.add_word(Word::new( | |
name, | |
comment, | |
vec![Command::Builtin(name.to_string(), func)], | |
self.lookup("class:word"), // that's ugly... maybe add_builtin should return a result instead? | |
)) | |
} | |
fn add_macro( | |
&mut self, | |
name: &str, | |
comment: &str, | |
func: fn(Forth) -> Result<Forth>, | |
) -> WordId { | |
self.add_word(Word::new( | |
name, | |
comment, | |
vec![Command::Builtin(name.to_string(), func)], | |
self.lookup("class:macro"), // that's ugly... maybe add_builtin should return a result instead? | |
)) | |
} | |
fn handle_literal(mut self) -> Result<Self> { | |
let token = self.pop()?.into_string()?; | |
let x = token | |
.parse() | |
.map_err(|_| ForthError::UnknownWord(token.to_string()))?; | |
let cmd = Command::Literal(Data::I32(x)); | |
match self.mode.last_mut().unwrap() { | |
Mode::Execute => cmd.exec(self), | |
Mode::Compile(ref mut ops) => { | |
ops.push(cmd); | |
Ok(self) | |
} | |
} | |
} | |
fn handle_prefix(self, id: WordId) -> Result<Self> { | |
Command::Call(id).exec(self) | |
} | |
fn handle_class(mut self) -> Result<Self> { | |
let id = self.top()?.clone().into_wordid()?; | |
let word = self.get_word(id); | |
match word.class { | |
None => unimplemented!(),//Command::Call(id).exec(self), | |
Some(cls) => Command::Call(cls).exec(self), | |
} | |
} | |
fn exec(mut self) -> Result<Self> { | |
println!("{:?} call", self.stack); | |
self.pop()?.exec(self) | |
} | |
fn exec_block(mut self, block: &[Command]) -> Result<Self> { | |
println!("exec_block({:?})", block); | |
for op in block { | |
self = op.exec(self)?; | |
} | |
Ok(self) | |
} | |
fn call(mut self) -> Result<Self> { | |
println!("{:?} call", self.stack); | |
self.pop()?.call(self) | |
} | |
fn call_wordid(mut self, id: WordId) -> Result<Self> { | |
println!("call_wordid({:?})", id); | |
let word = self.get_word(id); | |
match word.class { | |
None => { | |
let ops = word.ops.clone(); | |
self.exec_block(&ops) | |
} | |
Some(cls) => { | |
self.push(id); | |
self.call_wordid(cls) | |
} | |
} | |
} | |
fn compile(mut self) -> Result<Self> { | |
println!("compile"); | |
let id = self.pop()?.into_wordid()?; | |
if let Mode::Compile(ref mut ops) = self.mode.last_mut().unwrap() { | |
if self.words[id.0].ops.len() <= INLINE_THRESHOLD { | |
ops.extend(self.words[id.0].ops.iter().cloned()); | |
} else { | |
ops.push(Command::Call(id)); | |
} | |
Ok(self) | |
} else { | |
Err(ForthError::NotInCompileMode) | |
} | |
} | |
fn push<T: IntoData>(&mut self, x: T) { | |
self.stack.push(x.into_data()); | |
} | |
fn push2<S: IntoData, T: IntoData>(&mut self, x: S, y: T) { | |
self.stack.push(x.into_data()); | |
self.stack.push(y.into_data()); | |
} | |
fn top(&mut self) -> Result<&Data> { | |
self.stack.last().ok_or(ForthError::EmptyStack) | |
} | |
fn pop(&mut self) -> Result<Data> { | |
self.stack.pop().ok_or(ForthError::EmptyStack) | |
} | |
} | |
fn tokenize(input: &str) -> impl Iterator<Item = &str> { | |
let mut it = input.char_indices().peekable(); | |
std::iter::repeat(()) | |
.map(move |_| { | |
while let Some(true) = it.peek().map(|(_, ch)| ch.is_whitespace()) { | |
it.next(); | |
} | |
match it.peek() { | |
None => return None, | |
Some((_, '"')) => { | |
let (a, _) = it.next().unwrap(); | |
while let Some(false) = it.peek().map(|(_, ch)| *ch == '"') { | |
it.next(); | |
} | |
it.next(); | |
match it.peek() { | |
Some((b, _)) => return Some(&input[a..*b]), | |
None => return Some(&input[a..]), | |
} | |
} | |
Some((_, '(')) => { | |
let (a, _) = it.next().unwrap(); | |
while let Some(false) = it.peek().map(|(_, ch)| *ch == ')') { | |
it.next(); | |
} | |
it.next(); | |
match it.peek() { | |
Some((b, _)) => return Some(&input[a..*b]), | |
None => return Some(&input[a..]), | |
} | |
} | |
Some((i, _)) => { | |
let a = *i; | |
while let Some(false) = it.peek().map(|(_, ch)| ch.is_whitespace()) { | |
it.next(); | |
} | |
match it.peek() { | |
Some((b, _)) => return Some(&input[a..*b]), | |
None => return Some(&input[a..]), | |
} | |
} | |
} | |
}) | |
.take_while(Option::is_some) | |
.map(Option::unwrap) | |
} | |
struct WordBuilder { | |
class: Option<WordId>, | |
comment: String, | |
name: String, | |
ops: Vec<Command>, | |
} | |
impl WordBuilder { | |
fn new() -> Self { | |
WordBuilder { | |
class: None, | |
comment: String::new(), | |
name: String::new(), | |
ops: vec![], | |
} | |
} | |
fn build(self) -> Word { | |
Word { | |
class: self.class, | |
comment: self.comment, | |
name: self.name, | |
ops: self.ops, | |
} | |
} | |
fn builtin(mut self, func: fn(Forth) -> Result<Forth>) -> Word { | |
assert_eq!(self.ops.len(), 0); | |
self.ops = vec![Command::Builtin(self.name.clone(), func)]; | |
self.build() | |
} | |
fn with_comment(mut self, c: &str) -> Self { | |
self.comment = c.to_string(); | |
self | |
} | |
fn with_class(mut self, class: WordId) -> Self { | |
self.class = Some(class); | |
self | |
} | |
fn with_class_opt(mut self, class: Option<WordId>) -> Self { | |
self.class = class; | |
self | |
} | |
fn with_name(mut self, name: &str) -> Self { | |
self.name = name.to_string(); | |
self | |
} | |
fn with_name_string(mut self, name: String) -> Self { | |
self.name = name; | |
self | |
} | |
fn with_ops<I: IntoIterator<Item = Command>>(mut self, ops: I) -> Self { | |
self.ops.extend(ops); | |
self | |
} | |
} | |
fn main() -> Result<()> { | |
let mut forth = Forth::new(); | |
forth = forth.run(".s")?; | |
forth = forth.run("17 25 + .")?; | |
forth = forth.run("[ dup * ] :sqr")?; | |
forth = forth.run("[ sqr sqr ] :sqr")?; | |
forth = forth.run("[ sqr sqr ] :sqr")?; | |
forth = forth.run("[ sqr sqr ] :sqr")?; | |
/*forth = forth.run("[ [ dup * ] ] :sqrmaker")?; | |
forth = forth.run("sqrmaker :sqr .s")?; | |
forth = forth.run("[ sqr sqr ] :quad")?; | |
forth = forth.run("\"sqr\" lookup .s")?; | |
forth = forth.run("[ [ \"say true\" ] [ \"say no\" ] if . ] :say_what")?; | |
forth.push(false); | |
forth = forth.run("say_what")?; | |
forth.push(true); | |
forth = forth.run("say_what")?; | |
forth = forth.run("[ quad quad ] :superquad")?; | |
forth = forth.run("\"abc def\" 42")?; | |
forth = forth.run(".s")?; | |
forth = forth.run("[ quad quad ] :superquad")?; | |
forth = forth.run("[ superquad superquad ] :megaquad")?; | |
forth = forth.run("[ 42 ] :the_answer")?; | |
forth = forth.run("the_answer sqr .")?; | |
forth = forth.run("10 3 / .")?; | |
forth = forth.run("[ 42 . ]")?; | |
forth = forth.run("[ 42 . ] dup call swap call")?;*/ | |
/*forth.run(".s ( comment )")?; | |
forth.run("42 emit")?; | |
forth.run(": star 42 emit")?; | |
forth.run(": margin cr 30 spaces")?; | |
forth.run(": blip margin star")?; | |
forth.run(": stars [ star ] repeat")?; | |
forth.run(": bar margin 5 stars")?; | |
forth.run(": f bar blip bar blip blip cr")?; | |
forth.run("f f f")?; | |
forth.run("cr cr")?; | |
forth.run(": yards->in ( n -- n ) 36 *")?; | |
forth.run(": ft->in ( n -- n ) 12 *")?; | |
forth.run("10 yards->in .")?; | |
forth.run("2 ft->in .")?; | |
forth.run(": yards ( n -- n ) yards->in")?; | |
forth.run(": feet ( n -- n ) ft->in")?; | |
forth.run(": inches ( -- ) ")?; | |
forth.run("10 yards 2 feet 9 inches + + ."); | |
forth.run("22 4 /mod . .")?; | |
forth.run("1 2 3 4 .s")?; | |
forth.run("rot .s")?; | |
forth.run(": flip ( a b c -- c b a ) swap rot")?; | |
forth.run("flip .s")?; | |
forth.run(": over ( a b -- a b a ) swap dup rot swap")?; | |
forth.run("over .s")?; | |
forth.run(": -rot ( a b c -- c a b ) rot rot")?; | |
forth.run("-rot .s")?; | |
forth.run(": eq1 ( n -- result ) dup 1 + swap /")?; | |
forth.run(": eq2 ( x -- result ) dup 7 * 5 + *")?; | |
forth.run(": eq3 ( a b -- result ) over 9 * swap - *")?; | |
forth.run("drop drop drop 3 4 .s 2swap .s")?; | |
forth.run("2over .s")?; | |
forth.run("swap 2swap swap .s")?; | |
forth.run("forget eq1")?;*/ | |
for ( | |
id, | |
Word { | |
name, | |
comment, | |
ops, | |
class, | |
.. | |
}, | |
) in forth.words.iter().enumerate() | |
{ | |
let class = match class { | |
None => "", | |
Some(id) => &forth.words[id.0].name, | |
}; | |
print!("{:>5}: {:<32} {:<16} {:<16} [", id, name, comment, class); | |
for op in ops { | |
match op { | |
Command::Call(id) => print!(" {}", forth.words[id.0].name), | |
_ => print!(" {:?}", op), | |
} | |
} | |
println!(" ]") | |
} | |
Ok(()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment