diff options
| author | Edvard Thörnros <edvard.thornros@gmail.com> | 2021-01-09 18:37:08 +0100 |
|---|---|---|
| committer | Edvard Thörnros <edvard.thornros@gmail.com> | 2021-01-09 18:37:08 +0100 |
| commit | 97e77b94646fe97a2c83ba85ba1813eb9bd0346a (patch) | |
| tree | 8b8209e34700904c4e02cf623b1cb1930b52982d | |
| parent | efaab433309170e8330a7722e90c26a93dbec252 (diff) | |
| download | sylt-97e77b94646fe97a2c83ba85ba1813eb9bd0346a.tar.gz | |
Parsing simple mathematical expressions
| -rw-r--r-- | src/compiler.rs | 128 | ||||
| -rw-r--r-- | src/main.rs | 4 | ||||
| -rw-r--r-- | src/vm.rs | 12 | ||||
| -rw-r--r-- | tests/simple.tdy | 3 |
4 files changed, 126 insertions, 21 deletions
diff --git a/src/compiler.rs b/src/compiler.rs index 59659f4..ca4fcd3 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -6,6 +6,10 @@ struct Compiler { tokens: TokenStream, } +const PREC_NO: u64 = 0; +const PREC_TERM: u64 = 1; +const PREC_FACTOR: u64 = 2; + impl Compiler { pub fn new(tokens: TokenStream) -> Self { Self { @@ -15,12 +19,12 @@ impl Compiler { } fn error(&self, msg: &str) -> ! { - println!("ERROR: {}", msg); + println!("ERROR: {} range {:?}", msg, self.tokens[self.curr].1); panic!(); } fn peek(&self) -> Token { - if self.tokens.len() < self.curr { + if self.tokens.len() <= self.curr { crate::tokenizer::Token::EOF } else { self.tokens[self.curr].0.clone() @@ -33,39 +37,121 @@ impl Compiler { t } - fn value(&mut self) -> Value { - match self.eat() { + fn precedence(&self, token: Token) -> u64 { + match token { + Token::Minus => PREC_TERM, + Token::Plus => PREC_TERM, + + Token::Star => PREC_FACTOR, + Token::Slash => PREC_FACTOR, + + _ => PREC_NO, + } + } + + fn prefix(&mut self, token: Token, block: &mut Block) -> bool { + match token { + Token::LeftParen => self.grouping(block), + Token::Minus => self.unary(block), + + Token::Float(_) => self.value(block), + Token::Int(_) => self.value(block), + + _ => { return false; }, + } + return true; + } + + + fn infix(&mut self, token: Token, block: &mut Block) -> bool { + match token { + Token::Minus => self.binary(block), + Token::Plus => self.binary(block), + + Token::Slash => self.binary(block), + Token::Star => self.binary(block), + + _ => { return false; }, + } + return true; + } + + fn value(&mut self, block: &mut Block) { + let value = match self.eat() { Token::Float(f) => { Value::Float(f) }, Token::Int(f) => { Value::Int(f) } _ => { self.error("Invalid value.") } + }; + block.add(Op::Constant(value)); + } + + fn grouping(&mut self, block: &mut Block) { + if Token::LeftParen != self.eat() { + self.error("Expected left parentasis around expression."); } + + self.expression(block); + + if Token::RightParen != self.eat() { + self.error("Expected closing parentasis after expression."); + } + } + + fn unary(&mut self, block: &mut Block) { + if Token::Minus != self.eat() { + self.error("Expected minus at start of negation."); + } + self.value(block); + block.add(Op::Neg); + } + + fn binary(&mut self, block: &mut Block) { + let op = self.eat(); + + self.parse_precedence(block, self.precedence(op.clone()) + 1); + + let op = match op { + Token::Plus => Op::Add, + Token::Minus => Op::Sub, + Token::Star => Op::Mul, + Token::Slash => Op::Div, + _ => { self.error("Illegal operator"); } + }; + block.add(op); } fn expression(&mut self, block: &mut Block) { - let a = self.value(); - block.add(Op::Constant(a)); + self.parse_precedence(block, PREC_NO); + } - loop { - println!("{:?}", self.peek()); - let op = match self.eat() { - Token::Plus => Op::Add, - Token::Minus => Op::Sub, - Token::Star => Op::Mul, - Token::Slash => Op::Div, - _ => { break; } - }; - - let b = self.value(); - block.add(Op::Constant(b)); - block.add(op); + fn parse_precedence(&mut self, block: &mut Block, precedence: u64) { + println!("-- {:?}", self.peek()); + if !self.prefix(self.peek(), block) { + self.error("Expected expression."); + } + + while precedence <= self.precedence(self.peek()) { + if !self.infix(self.peek(), block) { + break; + } } } pub fn compile(&mut self, name: &str) -> Block { let mut block = Block::new(name); - self.expression(&mut block); - block.add(Op::Print); + loop { + if self.peek() == Token::EOF { + break; + } + + self.expression(&mut block); + block.add(Op::Print); + + if self.eat() != Token::Newline { + self.error("Invalid expression"); + } + } block.add(Op::Return); block diff --git a/src/main.rs b/src/main.rs index c9a7602..afe09ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,10 @@ mod compiler; fn main() { let tokens = tokenizer::file_to_tokens("tests/simple.tdy"); + for token in tokens.iter() { + println!("{:?}", token); + } + let block = compiler::compile("main", tokens); vm::run_block(block); @@ -10,10 +10,13 @@ pub enum Value { pub enum Op { Pop, Constant(Value), + Add, Sub, Mul, Div, + Neg, + Print, Return, } @@ -91,6 +94,15 @@ impl VM { self.stack.push(value); } + Op::Neg => { + let a = self.stack.pop().unwrap(); + match a { + Value::Float(a) => self.stack.push(Value::Float(-a)), + Value::Int(a) => self.stack.push(Value::Int(-a)), + _ => unimplemented!("Cannot negate '{:?}'.", a), + } + } + Op::Add => { let b = self.stack.pop().unwrap(); let a = self.stack.pop().unwrap(); diff --git a/tests/simple.tdy b/tests/simple.tdy index d248afd..6843b35 100644 --- a/tests/simple.tdy +++ b/tests/simple.tdy @@ -1 +1,4 @@ 1 + 1 * 2 +-1 * 3 + 2 +2 * 2 * 2 +1 + (3 * 2 + 3 + 2) * 3 |
