From 7646177030f956d82717e78384ed79f3ba69b495 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Fri, 28 Jan 2022 18:49:30 +0100 Subject: [PATCH] Implement variable declaration --- README.md | 4 +- src/interpreter.rs | 61 +++++++++++++++++++----------- src/lexer.rs | 19 +++++++++- src/parser.rs | 94 +++++++++++++++++++++++++++++++++++++--------- 4 files changed, 135 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 2638c73..a16555e 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ - [x] Negate `-X` - [x] Parentheses `(X+Y)*Z` - [x] Logical boolean operators -- [ ] Variables - - [ ] Declaration +- [x] Variables + - [x] Declaration - [ ] Assignment - [ ] While loop `while X { ... }` - [ ] If else statement `if X { ... } else { ... }` diff --git a/src/interpreter.rs b/src/interpreter.rs index 8fa69f8..a997b30 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,4 +1,6 @@ -use crate::parser::{Ast, BinOpType, UnOpType}; +use std::collections::HashMap; + +use crate::parser::{Expr, BinOpType, UnOpType, Ast, Stmt}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Value { @@ -6,29 +8,46 @@ pub enum Value { } pub struct Interpreter { - // Runtime storage, for example variables ... + /// The variable table maps all variables by their names to their values + vartable: HashMap } impl Interpreter { pub fn new() -> Self { - Self {} + let vartable = HashMap::new(); + Self { vartable } } pub fn run(&mut self, prog: Ast) { - let result = self.resolve_expr(prog); - - println!("Result = {:?}", result); - } - - fn resolve_expr(&mut self, expr: Ast) -> Value { - match expr { - Ast::I64(val) => Value::I64(val), - Ast::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs), - Ast::UnOp(uo, val) => self.resolve_unop(uo, *val), + for stmt in prog.prog { + match stmt { + Stmt::Expr(expr) => { + let result = self.resolve_expr(expr); + println!("Result = {:?}", result); + } + Stmt::Let(name, rhs) => { + let result = self.resolve_expr(rhs); + self.vartable.insert(name, result); + }, + } } } - fn resolve_binop(&mut self, bo: BinOpType, lhs: Ast, rhs: Ast) -> Value { + fn resolve_expr(&mut self, expr: Expr) -> Value { + match expr { + Expr::I64(val) => Value::I64(val), + Expr::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs), + Expr::UnOp(uo, val) => self.resolve_unop(uo, *val), + Expr::Ident(name) => { + match self.vartable.get(&name) { + None => panic!("Runtime error: Use of undeclared variable '{}'", name), + Some(val) => val.clone() + } + } + } + } + + fn resolve_binop(&mut self, bo: BinOpType, lhs: Expr, rhs: Expr) -> Value { let lhs = self.resolve_expr(lhs); let rhs = self.resolve_expr(rhs); @@ -55,7 +74,7 @@ impl Interpreter { } } - fn resolve_unop(&mut self, uo: UnOpType, val: Ast) -> Value { + fn resolve_unop(&mut self, uo: UnOpType, val: Expr) -> Value { let val = self.resolve_expr(val); match val { Value::I64(val) => match uo { @@ -69,21 +88,21 @@ impl Interpreter { #[cfg(test)] mod test { use super::{Interpreter, Value}; - use crate::parser::{Ast, BinOpType}; + use crate::parser::{Expr, BinOpType}; #[test] fn test_interpreter_expr() { // Expression: 1 + 2 * 3 + 4 // With precedence: (1 + (2 * 3)) + 4 - let ast = Ast::BinOp( + let ast = Expr::BinOp( BinOpType::Add, - Ast::BinOp( + Expr::BinOp( BinOpType::Add, - Ast::I64(1).into(), - Ast::BinOp(BinOpType::Mul, Ast::I64(2).into(), Ast::I64(3).into()).into(), + Expr::I64(1).into(), + Expr::BinOp(BinOpType::Mul, Expr::I64(2).into(), Expr::I64(3).into()).into(), ) .into(), - Ast::I64(4).into(), + Expr::I64(4).into(), ); let expected = Value::I64(11); diff --git a/src/lexer.rs b/src/lexer.rs index 0a35530..42a1429 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -13,6 +13,15 @@ pub enum Token { /// Right parentheses (')') RParen, + /// Identifier (variable / function / ... name) + Ident(String), + + /// Let identifier (let) + Let, + + /// Assignment (single equal) (=) + Assign, + /// Plus (+) Add, @@ -61,6 +70,9 @@ pub enum Token { /// Less or equal (<=) Le, + /// Semicolon (;) + Semicolon, + /// End of file EoF, } @@ -81,7 +93,7 @@ impl<'a> Lexer<'a> { while let Some(ch) = self.next() { match ch { // Skip whitespace - ' ' => (), + ' ' | '\r' | '\n' | '\t' => (), // Lex numbers '0'..='9' => { @@ -144,6 +156,8 @@ impl<'a> Lexer<'a> { ')' => tokens.push(Token::RParen), '<' => tokens.push(Token::Lt), '>' => tokens.push(Token::Gt), + '=' => tokens.push(Token::Assign), + ';' => tokens.push(Token::Semicolon), 'a'..='z' | 'A'..='Z' | '_' => { let mut ident = String::from(ch); @@ -157,7 +171,8 @@ impl<'a> Lexer<'a> { match ident.as_str() { "true" => tokens.push(Token::I64(1)), "false" => tokens.push(Token::I64(0)), - _ => panic!("Lexer encountered unknown ident: '{}'", ident), + "let" => tokens.push(Token::Let), + _ => tokens.push(Token::Ident(ident)), } } diff --git a/src/parser.rs b/src/parser.rs index 82ca2c9..d9fde80 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -62,19 +62,32 @@ pub enum UnOpType { } #[derive(Debug, PartialEq, Eq, Clone)] -pub enum Ast { +pub struct Ast { + pub prog: Vec, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Stmt { + Expr(Expr), + Let(String, Expr), +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Expr { /// Integer literal (64-bit) I64(i64), + /// Identifier (variable name) + Ident(String), /// Binary operation. Consists of type, left hand side and right hand side - BinOp(BinOpType, Box, Box), + BinOp(BinOpType, Box, Box), /// Unary operation. Consists of type and the value that is operated on - UnOp(UnOpType, Box), + UnOp(UnOpType, Box), } /* ## Grammar ### Expressions -expr_primary = LITERAL | "(" expr ")" | "-" expr_primary +expr_primary = LITERAL | IDENT | "(" expr ")" | "-" expr_primary expr_mul = expr_primary (("*" | "/" | "%") expr_primary)* expr_add = expr_mul (("+" | "-") expr_mul)* expr_shift = expr_add ((">>" | "<<") expr_add)* @@ -84,6 +97,11 @@ expr_band = expr_equ ("&" expr_equ)* expr_bxor = expr_band ("^") expr_band)* expr_bor = expr_bxor ("|" expr_bxor)* expr = expr_bor + +## Statements +stmt_expr = expr +stmt_let = "let" IDENT "=" expr +stmt = stmt_expr | stmt_let (";")* */ struct Parser> { @@ -98,16 +116,52 @@ impl> Parser { } fn parse(&mut self) -> Ast { - self.parse_expr() + let mut prog = Vec::new(); + + loop { + let stmt = match self.peek() { + Token::Semicolon => { + self.next(); + continue; + } + Token::EoF => break, + Token::Let => self.parse_let_stmt(), + // By default try to parse an expression + _ => Stmt::Expr(self.parse_expr()), + }; + + prog.push(stmt); + } + + Ast { prog } } - fn parse_expr(&mut self) -> Ast { + fn parse_let_stmt(&mut self) -> Stmt { + if !matches!(self.next(), Token::Let) { + panic!("Error parsing let: Expected let token"); + } + + let name = match self.next() { + Token::Ident(name) => name, + _ => panic!("Error parsing let: Expected identifier after let"), + }; + + if !matches!(self.next(), Token::Assign) { + panic!("Error parsing let: Expected assignment token"); + } + + let rhs = self.parse_expr(); + + Stmt::Let(name, rhs) + } + + fn parse_expr(&mut self) -> Expr { let lhs = self.parse_primary(); self.parse_expr_precedence(lhs, 0) } /// Parse binary expressions with a precedence equal to or higher than min_prec - fn parse_expr_precedence(&mut self, mut lhs: Ast, min_prec: u8) -> Ast { + fn parse_expr_precedence(&mut self, mut lhs: Expr, min_prec: u8) -> Expr { while let Some(binop) = &self.peek().try_to_binop() { // Stop if the next operator has a lower binding power if !(binop.precedence() >= min_prec) { @@ -128,16 +182,18 @@ impl> Parser { rhs = self.parse_expr_precedence(rhs, binop.precedence() + 1); } - lhs = Ast::BinOp(binop, lhs.into(), rhs.into()); + lhs = Expr::BinOp(binop, lhs.into(), rhs.into()); } lhs } /// Parse a primary expression (for now only number) - fn parse_primary(&mut self) -> Ast { + fn parse_primary(&mut self) -> Expr { match self.next() { - Token::I64(val) => Ast::I64(val), + Token::I64(val) => Expr::I64(val), + + Token::Ident(name) => Expr::Ident(name), Token::LParen => { // The tokens was an opening parenthesis, so parse a full expression again as the @@ -152,7 +208,7 @@ impl> Parser { inner } - Token::Sub => Ast::UnOp(UnOpType::Neg, self.parse_primary().into()), + Token::Sub => Expr::UnOp(UnOpType::Neg, self.parse_primary().into()), tok => panic!("Error parsing primary expr: Unexpected Token '{:?}'", tok), } @@ -197,8 +253,8 @@ impl BinOpType { #[cfg(test)] mod tests { - use super::{parse, Ast, BinOpType}; - use crate::lexer::Token; + use super::{parse, Expr, BinOpType}; + use crate::{lexer::Token, parser::{Stmt, Ast}}; #[test] fn test_parser() { @@ -214,17 +270,19 @@ mod tests { Token::I64(4), ]; - let expected = Ast::BinOp( + let expected = Expr::BinOp( BinOpType::Sub, - Ast::BinOp( + Expr::BinOp( BinOpType::Add, - Ast::I64(1).into(), - Ast::BinOp(BinOpType::Mul, Ast::I64(2).into(), Ast::I64(3).into()).into(), + Expr::I64(1).into(), + Expr::BinOp(BinOpType::Mul, Expr::I64(2).into(), Expr::I64(3).into()).into(), ) .into(), - Ast::I64(4).into(), + Expr::I64(4).into(), ); + let expected = Ast { prog: vec![Stmt::Expr(expected)] }; + let actual = parse(tokens); assert_eq!(expected, actual); }