diff options
-rw-r--r-- | src/main.rs | 758 |
1 files changed, 384 insertions, 374 deletions
diff --git a/src/main.rs b/src/main.rs index 420cdb3..987bce0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,124 +47,126 @@ const HELP_TEXT: &str = \n\ Good luck!\n\n"; + #[derive(Debug,PartialEq,Eq,Clone)] enum Value { - Str(String), - Num(Decimal), + Str(String), + Num(Decimal), } impl fmt::Display for Value { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Value::Str(s) => write!(f, "[{}]", s), - Value::Num(d) => write!(f, "{}", d), - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Value::Str(s) => write!(f, "[{}]", s), + Value::Num(d) => write!(f, "{}", d), } + } } - fn print_value(v: &Value) { - match v { - Value::Str(s) => print!("{}", s), - Value::Num(d) => print!("{}", d), - } + match v { + Value::Str(s) => print!("{}", s), + Value::Num(d) => print!("{}", d), + } } #[derive(Debug)] enum Mode { - Integer, - Decimal, - Str, - CommandChar, - CommandNamed, - RegisterChar, - RegisterStr, + Integer, + Decimal, + Str, + CommandChar, + CommandNamed, + RegisterChar, + RegisterStr, } #[derive(Debug)] enum Exit { - WithMessage(String), - Quit, + WithMessage(String), + Quit, } - fn err_msg(msg: String) -> Exit { - Exit::WithMessage(msg) + Exit::WithMessage(msg) } fn baseline_registers() -> HashMap<String, Value> { - let kvs = [ - ("F->C", "1! 32- 5 9/*"), - ("C->F", "1! 9 5/* 32+"), - ("C->K", "1! 273+"), - ("K->C", "1! 273-"), - ("Km->mi", "1! 1.609344/"), - ("mi->Km", "1! 1.609344*"), - ].map(|kv: (&str, &str)| (String::from(kv.0), Value::Str(kv.1.into()))); - HashMap::from(kvs) + let kvs = [ + ("F->C", "1! 32- 5 9/*"), + ("C->F", "1! 9 5/* 32+"), + ("C->K", "1! 273+"), + ("K->C", "1! 273-"), + ("Km->mi", "1! 1.609344/"), + ("mi->Km", "1! 1.609344*"), + ].map(|kv: (&str, &str)| (String::from(kv.0), Value::Str(kv.1.into()))); + + HashMap::from(kvs) } fn main() { - let mut state = RPState { - registers: baseline_registers(), - stack: Vec::new(), - mode: Mode::CommandChar, - wip_str: String::from(""), - // TODO: I don't think we need a return stack - // But this the closest thing we have right now - reg_command: String::from(""), - eat_count: 0, - num: dec!(0.0), - is_num_negative: false, - decimal_offset: dec!(1), - }; - let stdin = io::stdin(); - for line in stdin.lock().lines() { - match eval(&line.unwrap(), &mut state) { - Ok(()) => { - let mut first = true; - print!("{}", "stack: {"); - for elem in state.stack.iter() { - if !first { print!("{}", ", "); } - print!("{}", elem); - first = false; - } - println!("{}", "}"); - }, - Err(Exit::WithMessage(msg)) => println!("Error: {}", msg), - Err(Exit::Quit) => break, + let mut state = RPState { + registers: baseline_registers(), + stack: Vec::new(), + mode: Mode::CommandChar, + wip_str: String::from(""), + // TODO: I don't think we need a return stack + // But this the closest thing we have right now + reg_command: String::from(""), + eat_count: 0, + num: dec!(0.0), + is_num_negative: false, + decimal_offset: dec!(1), + }; + let stdin = io::stdin(); + for line in stdin.lock().lines() { + match eval(&line.unwrap(), &mut state) { + Ok(()) => { + let mut first = true; + print!("{}", "stack: {"); + for elem in state.stack.iter() { + if !first { print!("{}", ", "); } + print!("{}", elem); + first = false; } + println!("{}", "}"); + }, + Err(Exit::WithMessage(msg)) => println!("Error: {}", msg), + Err(Exit::Quit) => break, } + } } + const RADIX: u32 = 10; const INTEGER_RADIX: Decimal = dec!(10); fn to_decimal(input: u32) -> Decimal { - <u32 as Into<Decimal>>::into(input) + <u32 as Into<Decimal>>::into(input) } fn usize_to_decimal(input: usize) -> Decimal { - <usize as Into<Decimal>>::into(input) + <usize as Into<Decimal>>::into(input) } struct RPState { - registers: HashMap<String, Value>, - stack: Vec<Value>, - mode: Mode, - reg_command: String, - wip_str: String, - eat_count: u32, - is_num_negative: bool, - num: Decimal, - decimal_offset: Decimal + registers: HashMap<String, Value>, + stack: Vec<Value>, + mode: Mode, + reg_command: String, + wip_str: String, + eat_count: u32, + is_num_negative: bool, + num: Decimal, + decimal_offset: Decimal } + // TODO: Generalized shuffles // [[a-aa\](shuf)]s[dup] // [[ab-ba\](shuf)]s[swap] @@ -175,344 +177,352 @@ struct RPState { // } fn command_str(c: char, state: &mut RPState) -> Result<(), Exit> { - if c != ')' { - state.wip_str.push(c); - } else if c == ')' { - match state.wip_str.as_str() { - // TODO: - // (times) - // (while) - // (?) -- (cond ifTrue ifFalse) - // [(?)x]s[if-else] - // [[\](?)x]s[if] - // (r?) -- show contents of registers - // Scope out - word => { - if state.registers.contains_key(word) { - match &state.registers[word].clone() { - Value::Str(eval_body) => { - state.wip_str.clear(); - state.mode = Mode::CommandChar; - return eval(&eval_body, state); - } - _ => { - return Err(err_msg(format!( - "Unable to execute ({}) as a named word", - word))) - } - } - } else { - return Err(err_msg(format!( - "Unable to execute ({}) as a named word", word))) - } + if c != ')' { + state.wip_str.push(c); + } else if c == ')' { + match state.wip_str.as_str() { + // TODO: + // (times) + // (while) + // (?) -- (cond ifTrue ifFalse) + // [(?)x]s[if-else] + // [[\](?)x]s[if] + // (r?) -- show contents of registers + // Scope out + word => { + if state.registers.contains_key(word) { + match &state.registers[word].clone() { + Value::Str(eval_body) => { + state.wip_str.clear(); + state.mode = Mode::CommandChar; + return eval(&eval_body, state); } - + _ => { + return Err(err_msg(format!( + "Unable to execute ({}) as a named word", word))) + } + } + } else { + return Err(err_msg(format!( + "Unable to execute ({}) as a named word", word))) } + } } + } - Ok(()) + Ok(()) } + fn command_char(c: char, state: &mut RPState) -> Result<(), Exit> { - if c.is_digit(RADIX) { - 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 == '_' { - 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; - state.wip_str.clear(); - state.eat_count = 0; - Ok(()) - } else if c == '[' { - state.mode = Mode::Str; - state.wip_str.clear(); - state.eat_count = 0; - Ok(()) - } else if let 'p' | 'n' | 'q' | 'l' | 's' | 'v' | 'x' | 'd' | ',' | 'c' - | '!' | '?' = c - { - 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; - } - '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 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.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())); - } - } + if c.is_digit(RADIX) { + 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 == '_' { + 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; + state.wip_str.clear(); + state.eat_count = 0; + Ok(()) + } else if c == '[' { + state.mode = Mode::Str; + state.wip_str.clear(); + state.eat_count = 0; + Ok(()) + } else if let 'p' | 'n' | 'q' | 'l' | 's' | 'v' | 'x' | 'd' | ',' | 'c' + | '!' | '?' = c + { + 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))); } - '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(Value::Str(_)) => return Err(err_msg( + "Cannot assert a string as a stack depth".into())), + None => return Err(err_msg("Data underflow!".into())) } - Ok(()) - - } else if let '+' | '-' | '/' | '*' | '%' | '^' = c { - if state.stack.len() < 2 { + }, + 'q' => return Err(Exit::Quit), + 'c' => state.stack.clear(), + 'l' => { + state.reg_command = "l".into(); + state.mode = Mode::RegisterChar; + } + '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())); + } } - - 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.push(b); - state.stack.push(a); - return Err(err_msg(format!( - "Invalid operation {} on non-numbers!", c))) - } + } + 's' => { + state.reg_command = "s".into(); + state.mode = Mode::RegisterChar; + } + 'd' => { + 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()); + } + ',' => { + if state.stack.is_empty() { + return Err(err_msg("Data underflow!".into())); + } + state.stack.pop(); + } + 'v' => { + if state.stack.is_empty() { + return Err(err_msg("Data underflow!".into())); } - Ok(()) - } else { - panic!("{} isn't implemented yet!", c) + 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())); + } + } + } + '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))), } + + Ok(()) + } else if let '+' | '-' | '/' | '*' | '%' | '^' = c { + if state.stack.len() < 2 { + 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.push(b); + state.stack.push(a); + return Err(err_msg(format!( + "Invalid operation {} on non-numbers!", c))) + } + } + Ok(()) + } else { + panic!("{} isn't implemented yet!", c) + } } + fn finish_num(c: char, state: &mut RPState) -> Result<(), Exit> { - // print!("finishing number, negative? {}", state.is_num_negative); - if state.is_num_negative { - state.num *= dec!(-1); - } - state.stack.push(Value::Num(state.num)); - state.mode = Mode::CommandChar; - state.is_num_negative = false; - command_char(c, state) + // print!("finishing number, negative? {}", state.is_num_negative); + if state.is_num_negative { + state.num *= dec!(-1); + } + state.stack.push(Value::Num(state.num)); + state.mode = Mode::CommandChar; + state.is_num_negative = false; + command_char(c, state) } + fn integer(c: char, state: &mut RPState) -> Result<(), Exit> { - if c.is_digit(RADIX) { - state.num *= INTEGER_RADIX; - state.num += to_decimal(c.to_digit(10).unwrap()); - } else if c == '.' { - state.decimal_offset = dec!(1); - state.mode = Mode::Decimal; - } else if c == '_' { - state.is_num_negative = true; - } else { - return finish_num(c, state); - } - return Ok(()); + if c.is_digit(RADIX) { + state.num *= INTEGER_RADIX; + state.num += to_decimal(c.to_digit(10).unwrap()); + } else if c == '.' { + state.decimal_offset = dec!(1); + state.mode = Mode::Decimal; + } else if c == '_' { + state.is_num_negative = true; + } else { + return finish_num(c, state); + } + return Ok(()); } + fn decimal(c: char, state: &mut RPState) -> Result<(), Exit> { - if c.is_digit(RADIX) { - state.decimal_offset *= dec!(0.1); - state.num += to_decimal(c.to_digit(10).unwrap()) - * state.decimal_offset; - } else { - return finish_num(c, state) - } - return Ok(()); + if c.is_digit(RADIX) { + state.decimal_offset *= dec!(0.1); + state.num += to_decimal(c.to_digit(10).unwrap()) + * state.decimal_offset; + } else { + return finish_num(c, state) + } + return Ok(()); } + fn string(c: char, state: &mut RPState) -> Result<(), Exit> { - if state.eat_count > 0 { - state.eat_count-=1; - state.wip_str.push(c); - } else if c == '\\' { - state.eat_count = 1; - } else if c != ']' { - state.wip_str.push(c); - } else if c == ']' { - state.mode = Mode::CommandChar; - state.stack.push(Value::Str(state.wip_str.clone())); - state.wip_str.clear(); - } else { - return Err(err_msg("Should Not Get Here!".into())) - } - Ok(()) + if state.eat_count > 0 { + state.eat_count-=1; + state.wip_str.push(c); + } else if c == '\\' { + state.eat_count = 1; + } else if c != ']' { + state.wip_str.push(c); + } else if c == ']' { + state.mode = Mode::CommandChar; + state.stack.push(Value::Str(state.wip_str.clone())); + state.wip_str.clear(); + } else { + return Err(err_msg("Should Not Get Here!".into())) + } + Ok(()) } + fn register_str(c: char, state: &mut RPState) -> Result<(), Exit> { - match (c, state.reg_command.as_str()) { - (']', "l") => { - if state.registers.contains_key(&state.wip_str) { - state.stack.push(state.registers[&state.wip_str].clone()); - } - state.wip_str.clear(); - state.mode = Mode::CommandChar; - } - (']', "s") => { - if state.stack.is_empty() { - return Err(err_msg(format!( - "Data underflow attempting to store to register {}", c))); - } - state.registers.insert(state.wip_str.clone(), - state.stack.pop().unwrap()); - state.wip_str.clear(); - state.mode = Mode::CommandChar; - } - (_, "l"|"s") => { - state.wip_str.push(c) - } - _ => { - state.mode = Mode::CommandChar; - return Err(err_msg(format!( - "Unsupported register command {}", state.reg_command))); - } + match (c, state.reg_command.as_str()) { + (']', "l") => { + if state.registers.contains_key(&state.wip_str) { + state.stack.push(state.registers[&state.wip_str].clone()); + } + state.wip_str.clear(); + state.mode = Mode::CommandChar; } - Ok(()) + (']', "s") => { + if state.stack.is_empty() { + return Err(err_msg(format!( + "Data underflow attempting to store to register {}", c))); + } + state.registers.insert(state.wip_str.clone(), + state.stack.pop().unwrap()); + state.wip_str.clear(); + state.mode = Mode::CommandChar; + } + (_, "l"|"s") => { + state.wip_str.push(c) + } + _ => { + state.mode = Mode::CommandChar; + return Err(err_msg(format!( + "Unsupported register command {}", state.reg_command))); + } + } + + Ok(()) } + fn register_char(c: char, state: &mut RPState) -> Result<(), Exit> { - match (state.reg_command.as_str(), c) { - (_, '[') => { - state.mode = Mode::RegisterStr; - Ok(()) - } - ("s", c) => { - if state.stack.is_empty() { - return Err(err_msg(format!( - "Data underflow attempting to store to register {}", c))); - } - state.registers.insert(String::from(c), - state.stack.pop().unwrap()); - state.mode = Mode::CommandChar; - Ok(()) - } - ("l", c) => { - if state.registers.contains_key(&String::from(c)) { - state.stack.push(state.registers[&String::from(c)].clone()); - } - state.mode = Mode::CommandChar; - Ok(()) - } - _ => { - state.mode = Mode::CommandChar; - return Err(err_msg(format!( - "Unsupported register command {}", state.reg_command))); - } + match (state.reg_command.as_str(), c) { + (_, '[') => { + state.mode = Mode::RegisterStr; + Ok(()) + } + ("s", c) => { + if state.stack.is_empty() { + return Err(err_msg(format!( + "Data underflow attempting to store to register {}", c))); + } + state.registers.insert(String::from(c), + state.stack.pop().unwrap()); + state.mode = Mode::CommandChar; + Ok(()) + } + ("l", c) => { + if state.registers.contains_key(&String::from(c)) { + state.stack.push(state.registers[&String::from(c)].clone()); + } + state.mode = Mode::CommandChar; + Ok(()) } + _ => { + state.mode = Mode::CommandChar; + return Err(err_msg(format!( + "Unsupported register command {}", state.reg_command))); + } + } } + fn eval(input: &str, state: &mut RPState) -> Result<(), Exit> { - for (_cpos, c) in input.char_indices() { - let res = match state.mode { - Mode::CommandChar => command_char(c, state), - Mode::CommandNamed => command_str(c, state), - Mode::Integer => integer(c, state), - Mode::Decimal => decimal(c, state), - Mode::Str => string(c, state), - Mode::RegisterChar => register_char(c, state), - Mode::RegisterStr => register_str(c, state), - }; - if res.is_err() { - return res - } - } - match state.mode { - Mode::Integer | Mode::Decimal => { - return finish_num(' ', state) - }, - _ => {} + for (_cpos, c) in input.char_indices() { + let res = match state.mode { + Mode::CommandChar => command_char(c, state), + Mode::CommandNamed => command_str(c, state), + Mode::Integer => integer(c, state), + Mode::Decimal => decimal(c, state), + Mode::Str => string(c, state), + Mode::RegisterChar => register_char(c, state), + Mode::RegisterStr => register_str(c, state), }; - return Ok(()); + if res.is_err() { + return res + } + } + match state.mode { + Mode::Integer | Mode::Decimal => { + return finish_num(' ', state) + }, + _ => {} + }; + + return Ok(()); } + |