Last active
July 18, 2019 00:27
-
-
Save alphaKAI/d38f473a432cb9bde81b51863050467f to your computer and use it in GitHub Desktop.
Stack Machine VM based calculator in Rust
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#[derive(Debug, Copy, Clone, PartialEq)] | |
enum VMIns { | |
Push(i32), | |
Add, | |
Sub, | |
Mul, | |
Div, | |
Println, | |
} | |
fn exec(code: &Vec<VMIns>) { | |
let mut pc = 0; | |
let mut stack = Vec::new(); | |
loop { | |
if pc < code.len() { | |
let ins = code[pc]; | |
match ins { | |
VMIns::Push(x) => { | |
stack.push(x); | |
} | |
VMIns::Add | VMIns::Sub | VMIns::Mul | VMIns::Div => { | |
let (y, x) = match (stack.pop(), stack.pop()) { | |
(Some(y), Some(x)) => (y, x), | |
_ => panic!("stack is empty"), | |
}; | |
match ins { | |
VMIns::Add => stack.push(x + y), | |
VMIns::Sub => stack.push(x - y), | |
VMIns::Mul => stack.push(x * y), | |
VMIns::Div => match y { | |
0 => panic!("can't divied by zero"), | |
_ => stack.push(x / y), | |
}, | |
_ => unreachable!(), | |
} | |
} | |
VMIns::Println => { | |
let v = stack.pop().unwrap(); | |
println!("{}", v) | |
} | |
_ => unreachable!(), | |
} | |
pc += 1; | |
} else { | |
break; | |
} | |
} | |
} | |
#[derive(Debug, Copy, Clone, PartialEq)] | |
enum Token { | |
Num(i32), | |
Add, | |
Sub, | |
Mul, | |
Div, | |
LParen, | |
RParen, | |
} | |
impl Token { | |
fn to_VMIns(token: Token) -> VMIns { | |
match token { | |
Token::Num(x) => VMIns::Push(x), | |
Token::Add => VMIns::Add, | |
Token::Sub => VMIns::Sub, | |
Token::Mul => VMIns::Mul, | |
Token::Div => VMIns::Div, | |
_ => unreachable!(), | |
} | |
} | |
fn get_priority(token: Token) -> i32 { | |
match token { | |
Token::Add => 1, | |
Token::Sub => 1, | |
Token::Mul => 2, | |
Token::Div => 3, | |
Token::LParen => 0, | |
_ => panic!("unknown priority for {:?}", token), | |
} | |
} | |
} | |
fn parse_number(cs: &Vec<char>, cs_size: usize, i: &mut usize) -> i32 { | |
let mut tmp = Vec::new(); | |
while *i < cs_size && cs[*i].is_digit(10) { | |
let c = cs[*i]; | |
tmp.push(c); | |
*i += 1; | |
} | |
let mut s = String::new(); | |
for ch in tmp.iter() { | |
s.push(*ch); | |
} | |
return s.parse().unwrap(); | |
} | |
fn tokenize(src: String) -> Vec<Token> { | |
let mut i = 0; | |
let mut stack = Vec::new(); | |
let cs: Vec<char> = src.chars().collect(); | |
let cs_size = cs.len(); | |
loop { | |
if !(i < cs_size) { | |
break; | |
} | |
let c = cs[i]; | |
match c { | |
' ' => i += 1, | |
'0'..='9' => { | |
stack.push(Token::Num(parse_number(&cs, cs_size, &mut i))); | |
} | |
'+' => { | |
stack.push(Token::Add); | |
i += 1; | |
} | |
'-' => { | |
let j = i + 1; | |
if j < cs_size && cs[j].is_digit(10) { | |
i += 1; | |
stack.push(Token::Num(-1 * parse_number(&cs, cs_size, &mut i))); | |
} else { | |
stack.push(Token::Sub); | |
i += 1; | |
} | |
} | |
'*' => { | |
stack.push(Token::Mul); | |
i += 1; | |
} | |
'/' => { | |
stack.push(Token::Div); | |
i += 1; | |
} | |
'(' => { | |
stack.push(Token::LParen); | |
i += 1; | |
} | |
')' => { | |
stack.push(Token::RParen); | |
i += 1; | |
} | |
_ => panic!("unknown char given {}", c), | |
} | |
} | |
return stack; | |
} | |
fn parse_expr(tokens: Vec<Token>) -> Vec<VMIns> { | |
let mut code = Vec::new(); | |
let mut opstack = Vec::new(); | |
fn proc_op(e: Token, opstack: &mut Vec<Token>, code: &mut Vec<VMIns>) { | |
if opstack.is_empty() { | |
opstack.push(e); | |
} else { | |
let top = opstack.last().unwrap(); | |
let top_priority = Token::get_priority(*top); | |
let e_priority = Token::get_priority(e); | |
if top_priority < e_priority { | |
opstack.push(e); | |
} else { | |
while let Some(top) = opstack.last() { | |
let top_priority = Token::get_priority(*top); | |
if top_priority < e_priority { | |
break; | |
} else { | |
code.push(Token::to_VMIns(opstack.pop().unwrap())); | |
} | |
} | |
opstack.push(e); | |
} | |
} | |
} | |
for &e in tokens.iter() { | |
match e { | |
Token::Add | Token::Sub | Token::Mul | Token::Div => { | |
proc_op(e, &mut opstack, &mut code) | |
} | |
Token::Num(_) => code.push(Token::to_VMIns(e)), | |
Token::RParen => { | |
while let Some(v) = opstack.pop() { | |
if v == Token::LParen { | |
break; | |
} | |
code.push(Token::to_VMIns(v)); | |
} | |
} | |
Token::LParen => opstack.push(Token::LParen), | |
} | |
} | |
while let Some(e) = opstack.pop() { | |
code.push(Token::to_VMIns(e)) | |
} | |
code.push(VMIns::Println); | |
return code; | |
} | |
fn main() { | |
println!("please input formula : "); | |
let mut s = String::new(); | |
std::io::stdin() | |
.read_line(&mut s) | |
.expect("Failed to read line"); | |
let tokens = tokenize(s.trim().to_string()); | |
let code = parse_expr(tokens); | |
println!("code : {:?}", code); | |
exec(&code); | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment