From 260500cc508813d0b9f42b8c6f16880a4b6dad71 Mon Sep 17 00:00:00 2001 From: Irene Knapp Date: Sun, 7 Apr 2024 02:45:03 -0700 Subject: fully move commands into readtables, wow! that ws a lot Change-Id: I6f94b698df68bdf386f6d5e5443ddd21a4b5baf0 --- src/main.rs | 343 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 189 insertions(+), 154 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1ee9e88..5ee8d68 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,7 @@ const HELP_TEXT: &str = d duplicate the top item on the stack\n\ c clear the stack\n\ , drop the top item from the stack\n\ - ! something semi-implemented\n\ + ! assert the stack height, with the top item as desired height\n\ \n\ REGISTER OPERATIONS\n\ l load from register (register name as next character)\n\ @@ -117,6 +117,7 @@ fn main() { (Mode::Integer, make_integer_readtable()), (Mode::Decimal, make_decimal_readtable()), (Mode::Str, make_string_readtable()), + (Mode::CommandChar, make_command_character_readtable()), (Mode::CommandNamed, make_command_string_readtable()), (Mode::RegisterChar, make_register_character_readtable()), (Mode::RegisterStr, make_register_string_readtable()), @@ -180,7 +181,7 @@ impl ReadTable { } fn set_action(&mut self, c: char, - action: fn(char, &mut RPState) -> Result<(), Exit>) + action: impl Fn(char, &mut RPState) -> Result<(), Exit> + 'static) { let _ = self.action_map.insert(c, Rc::new(action)); } @@ -266,168 +267,210 @@ fn make_command_string_readtable() -> ReadTable { } -fn command_char(c: char, state: &mut RPState) -> Result<(), Exit> { - if c.is_digit(RADIX) { +fn make_command_character_readtable() -> ReadTable { + let mut result = ReadTable::new(move |c, _| { + panic!("{} isn't implemented yet!", c) + }); + + //////////////////////////////////////////////////////////// + // Whitespace actions + // + result.set_action(' ', move |_, _| Ok(())); + + //////////////////////////////////////////////////////////// + // Literal actions + // + result.set_digit_action(move |c, state| { state.mode = Mode::Integer; state.num = dec!(0); state.num += to_decimal(c.to_digit(10) .ok_or_else(|| err_msg(format!("{} isn't a digit", c)))?); Ok(()) - } else if c == '_' { + }); + + result.set_action('_', move |_, state| { state.mode = Mode::Integer; state.num = dec!(0); state.is_num_negative = true; Ok(()) - } else if c == ' ' { - // do nothing - Ok(()) - } else if c == '(' { - state.mode = Mode::CommandNamed; + }); + + result.set_action('[', move |_, state| { + state.mode = Mode::Str; state.wip_str.clear(); state.eat_count = 0; Ok(()) - } else if c == '[' { - state.mode = Mode::Str; + }); + + result.set_action('(', move |_, state| { + state.mode = Mode::CommandNamed; state.wip_str.clear(); state.eat_count = 0; Ok(()) - } else if let 'p' | 'n' | 'q' | 'l' | 's' | 'v' | 'x' | 'd' | ',' | 'c' - | '!' | '?' = c + }); + + //////////////////////////////////////////////////////////// + // Arithmetic operation actions + // + let make_arithmetic_action = + move |arithmetic: fn(Decimal, Decimal) -> Decimal| { - match c { - '?' => { - print!("{}", HELP_TEXT); - }, - '!' => { - if state.stack.is_empty() { - return Err(err_msg("Data underflow!".into())); - } - match state.stack.pop() { - Some(Value::Num(d)) => { - if usize_to_decimal(state.stack.len()) < d { - return Err(err_msg(format!( - "Stack depth should be at least: {}", d))); - } - } - Some(Value::Str(_)) => return Err(err_msg( - "Cannot assert a string as a stack depth".into())), - None => return Err(err_msg("Data underflow!".into())) - } - }, - 'q' => return Err(Exit::Quit), - 'c' => state.stack.clear(), - 'l' => { - state.reg_command = "l".into(); - state.mode = Mode::RegisterChar; + move |_, state: &mut RPState| { + if state.stack.len() < 2 { + return Err(err_msg("Data underflow!".into())); } - 'x' => { - if state.stack.is_empty() { - return Err(err_msg("Data underflow!".into())); - } - match state.stack.pop() { - Some(Value::Str(s)) => { - return eval(&s, state) - } - Some(v) => { - return Err(err_msg(format!("Cannot eval {}", v))); - } - None => { - return Err(err_msg("Data underflow!".into())); - } - } - } - 's' => { - state.reg_command = "s".into(); - state.mode = Mode::RegisterChar; - } - 'd' => { - if state.stack.is_empty() { - return Err(err_msg("Data underflow!".into())); + + let right = state.stack.pop().ok_or_else(|| err_msg( + "Data underflow!".into()))?; + let left = state.stack.pop().ok_or_else(|| err_msg( + "Data underflow!".into()))?; + + match (&left, &right) { + (Value::Num(left), Value::Num(right)) => { + let result = arithmetic(*left, *right); + + state.stack.push(Value::Num(result)); + + Ok(()) } - let val = state.stack.pop().unwrap(); - state.stack.push(val.clone()); - state.stack.push(val.clone()); - } - ',' => { - if state.stack.is_empty() { - return Err(err_msg("Data underflow!".into())); + _ => { + state.stack.push(left); + state.stack.push(right); + + Err(err_msg("Can't do arithmetic on non-numbers!".into())) } - state.stack.pop(); } - 'v' => { - if state.stack.is_empty() { - return Err(err_msg("Data underflow!".into())); - } - match state.stack.pop() { - Some(Value::Num(d)) => { - match d.sqrt() { - Some(n) => state.stack.push(Value::Num(n)), - None => return Err(err_msg( - "Error attempting square root!".into())), - } - } - Some(v) => { - return Err(err_msg(format!("Invalid attempt to sqrt {}", v))); - } - None => { - return Err(err_msg("Impossible data underflow!".into())); + } + }; + + result.set_action('+', make_arithmetic_action(|left, right| left + right)); + result.set_action('-', make_arithmetic_action(|left, right| left - right)); + result.set_action('/', make_arithmetic_action(|left, right| left / right)); + result.set_action('*', make_arithmetic_action(|left, right| left * right)); + result.set_action('%', make_arithmetic_action(|left, right| left % right)); + result.set_action('^', make_arithmetic_action(|left, right| + Decimal::pow(left, right))); + + result.set_action('v', move |_, state| { + match state.stack.pop() { + Some(Value::Num(d)) => { + match d.sqrt() { + Some(n) => { + state.stack.push(Value::Num(n)); + Ok(()) } + None => Err(err_msg("Error attempting square root!".into())) } } - 'p' => { - if state.stack.is_empty() { - return Err(err_msg("Data underflow!".into())); - } - print_value(state.stack.last().unwrap()); - }, - 'n' => { - if state.stack.is_empty() { - return Err(err_msg("Data underflow!".into())); - } - print_value(&state.stack.pop().unwrap()); - }, - _ => return Err(err_msg(format!( - "{} is unimplemented, this shouldn't be reachable!", c))), + Some(v) => Err(err_msg(format!("Invalid attempt to sqrt {}", v))), + None => Err(err_msg("Data underflow!".into())) } + }); + + //////////////////////////////////////////////////////////// + // Stack operation actions + // + result.set_action('d', move |_, state| { + if state.stack.is_empty() { + return Err(err_msg("Data underflow!".into())); + } + + let val = state.stack.pop().unwrap(); + state.stack.push(val.clone()); + state.stack.push(val.clone()); Ok(()) - } else if let '+' | '-' | '/' | '*' | '%' | '^' = c { - if state.stack.len() < 2 { + }); + + result.set_action('c', move |_, state| { + state.stack.clear(); + + Ok(()) + }); + + result.set_action(',', move |_, state| { + if state.stack.is_empty() { return Err(err_msg("Data underflow!".into())); } - let a = state.stack.pop().ok_or_else(|| err_msg( - "Data underflow!".into()))?; - let b = state.stack.pop().ok_or_else(|| err_msg( - "Data underflow!".into()))?; - - match (&a, &b) { - (Value::Num(ia), Value::Num(ib)) => { - let value = match c { - '+' => Some(*ib + *ia), - '-' => Some(*ib - *ia), - '/' => Some(*ib / *ia), - '*' => Some(*ib * *ia), - '%' => Some(*ib % *ia), - '^' => Some(Decimal::pow(*ib, *ia)), - _ => None - }.ok_or_else(|| err_msg("This should never happen".into()))?; - - state.stack.push(Value::Num(value)); + state.stack.pop(); + + Ok(()) + }); + + result.set_action('!', move |_, state| { + match state.stack.pop() { + Some(Value::Num(d)) => { + if usize_to_decimal(state.stack.len()) < d { + Err(err_msg(format!("Stack depth should be at least: {}", d))) + } else { + Ok(()) + } + } + Some(Value::Str(_)) => { + Err(err_msg("Cannot assert a string as a stack depth".into())) } - _ => { - state.stack.push(b); - state.stack.push(a); - return Err(err_msg(format!( - "Invalid operation {} on non-numbers!", c))) + None => { + Err(err_msg("Data underflow!".into())) } } + }); + + //////////////////////////////////////////////////////////// + // Register operation actions + // + let register_command_action = move |c: char, state: &mut RPState| { + state.reg_command = c.into(); + state.mode = Mode::RegisterChar; + Ok::<(), Exit>(()) + }; + + result.set_action('l', register_command_action.clone()); + result.set_action('s', register_command_action.clone()); + + //////////////////////////////////////////////////////////// + // Control flow actions + // + result.set_action('x', move |_, state| { + match state.stack.pop() { + Some(Value::Str(s)) => eval(&s, state), + Some(v) => Err(err_msg(format!("Cannot eval {}", v))), + None => Err(err_msg("Data underflow!".into())), + } + }); + + //////////////////////////////////////////////////////////// + // Meta actions + // + result.set_action('p', move |_, state| { + if state.stack.is_empty() { + return Err(err_msg("Data underflow!".into())); + } + + print_value(state.stack.last().unwrap()); + Ok(()) - } else { - panic!("{} isn't implemented yet!", c) - } -} + }); + result.set_action('n', move |_, state| { + if state.stack.is_empty() { + return Err(err_msg("Data underflow!".into())); + } + + print_value(&state.stack.pop().unwrap()); + + Ok(()) + }); + + result.set_action('q', move |_, _| Err(Exit::Quit)); + + result.set_action('?', move |_, _| { + print!("{}", HELP_TEXT); + Ok(()) + }); + + result +} fn finish_num(c: char, state: &mut RPState) -> Result<(), Exit> { // print!("finishing number, negative? {}", state.is_num_negative); @@ -437,7 +480,7 @@ fn finish_num(c: char, state: &mut RPState) -> Result<(), Exit> { state.stack.push(Value::Num(state.num)); state.mode = Mode::CommandChar; state.is_num_negative = false; - command_char(c, state) + eval_one(c, state) } @@ -588,30 +631,9 @@ fn load_from_register(name: &str, state: &mut RPState) -> Result<(), Exit> { fn eval(input: &str, state: &mut RPState) -> Result<(), Exit> { for (_cpos, c) in input.char_indices() { - // TODO eventually this slightly weird logic will go away because - // everything will be in a readtable - let action = { - if let Some(readtable) = state.readtables.get_mut(&state.mode) { - if let Some(action) = readtable.action_map.get_mut(&c) { - Some(Rc::clone(&action)) - } else { - Some(Rc::clone(&readtable.default_action)) - } - } else { - None - } - }; - - if let Some(action) = action { - action(c, state)?; - } else { - match state.mode { - Mode::CommandChar => command_char(c, state), - _ => return Err(err_msg( - "Mode has neither readtable nor hardcoded case".to_string())), - }?; - } + eval_one(c, state)? } + match state.mode { Mode::Integer | Mode::Decimal => { return finish_num(' ', state) @@ -622,3 +644,16 @@ fn eval(input: &str, state: &mut RPState) -> Result<(), Exit> { Ok(()) } + +fn eval_one(c: char, state: &mut RPState) -> Result<(), Exit> { + if let Some(readtable) = state.readtables.get_mut(&state.mode) { + if let Some(action) = readtable.action_map.get_mut(&c) { + (Rc::clone(&action))(c, state) + } else { + (Rc::clone(&readtable.default_action))(c, state) + } + } else { + Err(err_msg("No readtable found for this mode".into())) + } +} + -- cgit 1.4.1