summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/main.rs72
-rw-r--r--src/tests.rs33
2 files changed, 90 insertions, 15 deletions
diff --git a/src/main.rs b/src/main.rs
index 7420e3f..96e7abb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -32,6 +32,7 @@ fn print_value(v: &Value) {
     }
 }
 
+#[derive(Debug)]
 enum Mode {
     Integer,
     Decimal,
@@ -52,9 +53,22 @@ fn err_msg(msg: String) -> Exit {
     Exit::WithMessage(msg)
 }
 
+fn baseline_regsiters() -> 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)
+}
+
+
 fn main() {
     let mut state = RPState {
-        registers: HashMap::new(),
+        registers: baseline_regsiters(),
         stack: Vec::new(),
         mode: Mode::CommandChar,
         wip_str: String::from(""),
@@ -63,6 +77,7 @@ fn main() {
         reg_command: String::from(""), 
         eat_count: 0,
         num: dec!(0.0),
+        is_num_negative: false,
         decimal_offset: dec!(1),
     };
     let stdin = io::stdin();
@@ -91,6 +106,10 @@ fn to_decimal(input: u32) -> Decimal {
     <u32 as Into<Decimal>>::into(input)
 }
 
+fn usize_to_decimal(input: usize) -> Decimal {
+    <usize as Into<Decimal>>::into(input)
+}
+
 
 struct RPState {
     registers: HashMap<String, Value>,
@@ -99,6 +118,7 @@ struct RPState {
     reg_command: String,
     wip_str: String,
     eat_count: u32,
+    is_num_negative: bool,
     num: Decimal,
     decimal_offset: Decimal
 }
@@ -134,7 +154,7 @@ fn command_str(c: char, state: &mut RPState) -> Result<(), Exit> {
                             return eval(&eval_body, state);
                         }
                         _ => {
-                    return Err(err_msg(format!("Unable to execute ({}) as a named word", word)))
+                            return Err(err_msg(format!("Unable to execute ({}) as a named word", word)))
                         }
                     }
                     
@@ -155,6 +175,11 @@ fn command_char(c: char, state: &mut RPState) -> Result<(), Exit> {
         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(())
@@ -169,8 +194,22 @@ fn command_char(c: char, state: &mut RPState) -> Result<(), Exit> {
         state.eat_count = 0;
         Ok(())
     }
-    else if let 'p' | 'n' | 'q' | 'l' | 's' | 'v' | 'x' | 'd' | ',' | 'c' = c {
+    else if let 'p' | 'n' | 'q' | 'l' | 's' | 'v' | 'x' | 'd' | ',' | 'c' | '!' = c {
         match c {
+            '!' => {
+                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' => {
@@ -280,6 +319,17 @@ fn command_char(c: char, state: &mut RPState) -> Result<(), Exit> {
     }
 }
 
+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)
+}
+
 fn integer(c: char, state: &mut RPState) -> Result<(), Exit> {
     if c.is_digit(RADIX) {
         state.num *= INTEGER_RADIX;
@@ -287,11 +337,10 @@ fn integer(c: char, state: &mut RPState) -> Result<(), Exit> {
     } else if c == '.' {
         state.decimal_offset = dec!(1);
         state.mode = Mode::Decimal;
-    }
-    else {
-        state.stack.push(Value::Num(state.num));
-        state.mode = Mode::CommandChar;
-        return command_char(c, state);
+    } else if c == '_' {
+        state.is_num_negative = true;
+    } else {
+        return finish_num(c, state);
     }
     return Ok(());
 }
@@ -301,9 +350,7 @@ fn decimal(c: char, state: &mut RPState) -> Result<(), Exit> {
         state.decimal_offset *= dec!(0.1);
         state.num += to_decimal(c.to_digit(10).unwrap()) * state.decimal_offset;
     } else {
-        state.stack.push(Value::Num(state.num));
-        state.mode = Mode::CommandChar;
-        return command_char(c, state);
+        return finish_num(c, state)
     }
     return Ok(());
 }
@@ -399,8 +446,7 @@ fn eval(input: &str, state: &mut RPState) -> Result<(), Exit> {
     }
     match state.mode {
         Mode::Integer | Mode::Decimal => { 
-            state.stack.push(Value::Num(state.num));
-            state.mode = Mode::CommandChar;
+            return finish_num(' ', state)
         },
         _ => {}
     };
diff --git a/src/tests.rs b/src/tests.rs
index 170d4f2..013696b 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -1,16 +1,16 @@
 
 #[cfg(test)]
 mod tests {
-    use std::collections::HashMap;
     use rust_decimal_macros::dec;
     use crate::*;
 
     fn new_state() -> RPState {
         return RPState {
-            registers: HashMap::new(),
+            registers: baseline_regsiters(),
             stack: Vec::new(),
             eat_count: 0,
             mode: Mode::CommandChar,
+            is_num_negative: false,
             wip_str: String::from(""),
             reg_command: String::from(""),
             num: dec!(0.0),
@@ -43,6 +43,9 @@ mod tests {
         eval("2 2^", &mut state)?;
         assert_eq!(state.stack[6], Value::Num(dec!(4)));
 
+        eval("c_40 32-", &mut state)?;
+        assert_eq!(state.stack[0], Value::Num(dec!(-72)));
+
         Ok(())
     }
 
@@ -54,7 +57,33 @@ mod tests {
         eval("1(inc)(inc)", &mut state)?;
         assert_eq!(state.stack[0], Value::Num(dec!(3)));
 
+        eval("c1(inc)", &mut state)?;
+        assert_eq!(state.stack[0], Value::Num(dec!(2)));
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_stdlib() -> Result<(), Exit> {
+        let mut state = new_state();
+        // TODO: Add this back in one we've figured out booleans
+        // comparison
+        //
+        // Right now, `5 9 /` results in 0.5555555555555555555555555556
+        // Which means that this conversion results in 
+        // -40.000000000000000000000000003
+        // Which, like, decimal bases are fun and all
+        //
+        // And I don't want to fuss with unwrapping state and all that
+        // mess. I'd much rather do something like 
+        // eval("_40(F->C)", &mut state)?;
+        // assert_eq!(state.stack[0], Value::Num(dec!(-40)));
+
+        eval("_40(C->F)", &mut state)?;
+        assert_eq!(state.stack[0], Value::Num(dec!(-40)));
 
+        eval("c0(C->F)", &mut state)?;
+        assert_eq!(state.stack[0], Value::Num(dec!(32)));
 
         Ok(())
     }