#[macro_use]
extern crate failure;
use endbasic::ast::{ArgSep, Expr, Value};
use endbasic::exec::{BuiltinCommand, Machine, MachineBuilder};
use failure::Fallible;
use std::cell::RefCell;
use std::io;
use std::rc::Rc;
const INPUT: &str = r#"
' Use the custom STORE command to save a string value "out of band".
STORE "some long text"
' Use the custom RETRIEVE command to print the previously-stored value by STORE.
RETRIEVE
"#;
struct StoreCommand {
state: Rc<RefCell<String>>,
}
impl BuiltinCommand for StoreCommand {
fn name(&self) -> &'static str {
"STORE"
}
fn syntax(&self) -> &'static str {
"\"string to save\""
}
fn description(&self) -> &'static str {
"Saves the string argument given to it in a Rust variable."
}
fn exec(&self, args: &[(Option<Expr>, ArgSep)], machine: &mut Machine) -> Fallible<()> {
ensure!(args.len() == 1, "STORE takes one argument only");
let expr = args[0]
.0
.as_ref()
.expect("A single argument can never be empty");
match expr.eval(machine.get_vars())? {
Value::Text(t) => *self.state.borrow_mut() = t,
_ => bail!("Mismatched expression type"),
}
Ok(())
}
}
struct RetrieveCommand {
state: Rc<RefCell<String>>,
}
impl BuiltinCommand for RetrieveCommand {
fn name(&self) -> &'static str {
"RETRIEVE"
}
fn syntax(&self) -> &'static str {
""
}
fn description(&self) -> &'static str {
"Retrieves the string saved by `StoreCommand` and prints it to the console."
}
fn exec(&self, args: &[(Option<Expr>, ArgSep)], _machine: &mut Machine) -> Fallible<()> {
ensure!(args.is_empty(), "RETRIEVE takes no arguments");
println!("The stored value was: {}", self.state.borrow());
Ok(())
}
}
fn main() {
let state = Rc::from(RefCell::from(String::new()));
let mut machine = MachineBuilder::default()
.add_builtin(Rc::from(StoreCommand {
state: state.clone(),
}))
.add_builtin(Rc::from(RetrieveCommand { state }))
.build();
let mut cursor = io::Cursor::new(INPUT.as_bytes());
machine.exec(&mut cursor).expect("Execution error");
}