hashmapに変数名と値を入れて評価する。
use std::collections::HashMap; #[derive(Debug, Clone, PartialEq, Eq)] enum Value<'src> { Num(i32), Op(&'src str), Sym(&'src str), Block(Vec<Value<'src>>), } impl<'src> Value<'src> { fn as_num(&self) -> i32 { match self { Self::Num(val) => *val, _ => panic!("Value is not a number"), } } fn to_block(self) -> Vec<Value<'src>> { match self { Self::Block(val) => val, _ => panic!("Value is not a block"), } } fn as_sym(&self) -> &'src str { if let Self::Sym(sym) = self { *sym } else { panic!("Value is not a symbol"); } } } struct Vm<'src> { stack: Vec<Value<'src>>, vars: HashMap<&'src str, Value<'src>>, } impl<'src> Vm<'src> { fn new() -> Self { Self { stack: vec![], vars: HashMap::new(), } } } fn main() { for line in std::io::stdin().lines().flatten() { parse(&line); } } fn parse<'a>(line: &'a str) -> Vec<Value> { let mut vm = Vm::new(); let input: Vec<_> = line.split(" ").collect(); let mut words = &input[..]; while let Some((&word, mut rest)) = words.split_first(){ if word.is_empty() { break; } if word == "{" { let value; (value, rest) = parse_block(rest); vm.stack.push(value); } else { let code = if let Ok(num) = word.parse::<i32>() { Value::Num(num) } else if word.starts_with("/") { Value::Sym(&word[1..]) } else { Value::Op(word) }; eval(code, &mut vm); } words = rest; } println!("stack: {:?}", vm.stack); vm.stack } fn eval<'src>(code: Value<'src>, vm: &mut Vm<'src>) { match code { Value::Op(op) => match op { "+" => add(&mut vm.stack), "-" => sub(&mut vm.stack), "*" => mul(&mut vm.stack), "/" => div(&mut vm.stack), "<" => lt(&mut vm.stack), "if" => op_if(vm), "def" => op_def(vm), _ => { let val = vm.vars.get(op).expect(&format!( "{op:?} is not a defined operation" )); vm.stack.push(val.clone()); } }, _ => vm.stack.push(code.clone()), } } fn parse_block<'src, 'a>( input: &'a [&'src str], ) -> (Value<'src>, &'a [&'src str]){ let mut tokens = vec![]; let mut words = input; while let Some((&word, mut rest)) = words.split_first(){ if word.is_empty() { break; } if word == "{" { let value; (value, rest) = parse_block(rest); tokens.push(value); } else if word == "}" { return (Value::Block(tokens), rest); } else if let Ok(value) = word.parse::<i32>() { tokens.push(Value::Num(value)); } else { tokens.push(Value::Op(word)); } words = rest; } (Value::Block(tokens), words) } macro_rules! impl_op { {$name:ident, $op:tt} => { fn $name(stack: &mut Vec<Value>) { let rhs = stack.pop().unwrap().as_num(); let lhs = stack.pop().unwrap().as_num(); stack.push(Value::Num((lhs $op rhs) as i32)); } } } impl_op!(add, +); impl_op!(sub, -); impl_op!(mul, *); impl_op!(div, /); impl_op!(lt, <); fn op_if(vm: &mut Vm) { let false_branch = vm.stack.pop().unwrap().to_block(); let true_branch = vm.stack.pop().unwrap().to_block(); let cond = vm.stack.pop().unwrap().to_block(); for code in cond { eval(code, vm); } let cond_result = vm.stack.pop().unwrap().as_num(); if cond_result != 0 { for code in true_branch { eval(code, vm); } } else { for code in false_branch { eval(code, vm); } } } fn op_def(vm: &mut Vm) { let value = vm.stack.pop().unwrap(); eval(value, vm); let value = vm.stack.pop().unwrap(); let sym = vm.stack.pop().unwrap().as_sym(); vm.vars.insert(sym, value); } #[cfg(test)] mod test { use super::{parse, Value::*}; #[test] fn test_group(){ assert_eq!( parse("1 2 + { 3 4 }"), vec![Num(3), Block(vec![Num(3), Num(4)])] ); } #[test] fn test_if_false(){ assert_eq!( parse("{ 1 -1 + } { 100 } { -100 } if"), vec![Num(-100)] ); } #[test] fn test_if_true() { assert_eq!( parse("{ 1 1 + } { 100 } { -100 } if"), vec![Num(100)] ); } #[test] fn test_var() { assert_eq!( parse("/x 10 def /y 20 def x y *"), vec![Num(200)] ) } #[test] fn test_var_if() { assert_eq!( parse("/x 10 def /y 20 def { x y < } { x } { y } if"), vec![Num(10)] ); } }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/stackmachine`
/a 100 def /b 200 def { a b < } { a } { 0 } if
stack: [Num(100)]
/a 100 def /b 200 def { b a < } { a } { 0 } if
stack: [Num(0)]