From 2ea2aa52039356f70609cce615eea79c7cd65fc9 Mon Sep 17 00:00:00 2001 From: Daniel M Date: Sat, 29 Jan 2022 23:18:15 +0100 Subject: [PATCH] Implement multi statement code - Add statements - Add mandatory semicolons after statements --- README.md | 8 ++++- src/interpreter.rs | 42 +++++++++++++----------- src/lexer.rs | 6 +++- src/main.rs | 20 +++++++----- src/parser.rs | 80 ++++++++++++++++++++++++++++++++++------------ 5 files changed, 108 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 29654b7..d8014d7 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ - [x] Variables - [x] Declaration - [x] Assignment +- [x] Statements with semicolon & Multiline programs - [ ] Control flow - [ ] While loop `while X { ... }` - [ ] If else statement `if X { ... } else { ... }` @@ -50,7 +51,6 @@ ## Grammar ### Expressions - ``` expr_primary = LITERAL | IDENT | "(" expr ")" | "-" expr_primary | "~" expr_primary expr_mul = expr_primary (("*" | "/" | "%") expr_primary)* @@ -62,4 +62,10 @@ 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 = stmt_expr ``` \ No newline at end of file diff --git a/src/interpreter.rs b/src/interpreter.rs index 4e36bbf..b9bda3f 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::parser::{Ast, BinOpType, UnOpType}; +use crate::parser::{Expression, BinOpType, UnOpType, Ast, Statement}; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Value { @@ -20,17 +20,23 @@ impl Interpreter { } pub fn run(&mut self, prog: Ast) { - let result = self.resolve_expr(prog); + for stmt in prog.prog { + match stmt { + Statement::Expr(expr) => { + let result = self.resolve_expr(expr); + println!("Result = {:?}", result); + } + } - println!("Result = {:?}", result); + } } - fn resolve_expr(&mut self, expr: Ast) -> Value { + fn resolve_expr(&mut self, expr: Expression) -> Value { match expr { - Ast::I64(val) => Value::I64(val), - Ast::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs), - Ast::UnOp(uo, operand) => self.resolve_unop(uo, *operand), - Ast::Var(name) => self.resolve_var(name), + Expression::I64(val) => Value::I64(val), + Expression::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs), + Expression::UnOp(uo, operand) => self.resolve_unop(uo, *operand), + Expression::Var(name) => self.resolve_var(name), } } @@ -41,7 +47,7 @@ impl Interpreter { } } - fn resolve_unop(&mut self, uo: UnOpType, operand: Ast) -> Value { + fn resolve_unop(&mut self, uo: UnOpType, operand: Expression) -> Value { let operand = self.resolve_expr(operand); match (operand, uo) { @@ -51,15 +57,15 @@ impl Interpreter { } } - fn resolve_binop(&mut self, bo: BinOpType, lhs: Ast, rhs: Ast) -> Value { + fn resolve_binop(&mut self, bo: BinOpType, lhs: Expression, rhs: Expression) -> Value { let rhs = self.resolve_expr(rhs); match (&bo, &lhs) { - (BinOpType::Declare, Ast::Var(name)) => { + (BinOpType::Declare, Expression::Var(name)) => { self.vartable.insert(name.clone(), rhs.clone()); return rhs; } - (BinOpType::Assign, Ast::Var(name)) => { + (BinOpType::Assign, Expression::Var(name)) => { match self.vartable.get_mut(name) { Some(val) => *val = rhs.clone(), None => panic!("Runtime Error: Trying to assign value to undeclared variable"), @@ -100,21 +106,21 @@ impl Interpreter { #[cfg(test)] mod test { use super::{Interpreter, Value}; - use crate::parser::{Ast, BinOpType}; + use crate::parser::{Expression, BinOpType}; #[test] fn test_interpreter_expr() { // Expression: 1 + 2 * 3 + 4 // With precedence: (1 + (2 * 3)) + 4 - let ast = Ast::BinOp( + let ast = Expression::BinOp( BinOpType::Add, - Ast::BinOp( + Expression::BinOp( BinOpType::Add, - Ast::I64(1).into(), - Ast::BinOp(BinOpType::Mul, Ast::I64(2).into(), Ast::I64(3).into()).into(), + Expression::I64(1).into(), + Expression::BinOp(BinOpType::Mul, Expression::I64(2).into(), Expression::I64(3).into()).into(), ) .into(), - Ast::I64(4).into(), + Expression::I64(4).into(), ); let expected = Value::I64(11); diff --git a/src/lexer.rs b/src/lexer.rs index 466dc21..5a2b577 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -73,6 +73,9 @@ pub enum Token { /// Equal Sign (=) Equ, + /// Semicolon (;) + Semicolon, + /// End of file EoF, } @@ -93,7 +96,7 @@ impl<'a> Lexer<'a> { loop { match self.next() { // Skip whitespace - ' ' | '\t' => (), + ' ' | '\t' | '\n' | '\r' => (), // Stop lexing at EOF '\0' => break, @@ -127,6 +130,7 @@ impl<'a> Lexer<'a> { tokens.push(Token::LArrow); } + ';' => tokens.push(Token::Semicolon), '+' => tokens.push(Token::Add), '-' => tokens.push(Token::Sub), '*' => tokens.push(Token::Mul), diff --git a/src/main.rs b/src/main.rs index 8f88550..140c190 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,15 +7,19 @@ fn main() { let mut interpreter = Interpreter::new(); - let mut code = String::new(); + // let mut code = String::new(); + let code = " + a <- 5; + a * 2; + "; - loop { - print!(">> "); - std::io::stdout().flush().unwrap(); + // loop { + // print!(">> "); + // std::io::stdout().flush().unwrap(); - code.clear(); - std::io::stdin().read_line(&mut code).unwrap(); - let code = code.trim(); + // code.clear(); + // std::io::stdin().read_line(&mut code).unwrap(); + // let code = code.trim(); let tokens = lex(&code); @@ -26,6 +30,6 @@ fn main() { println!("Ast: {:#?}\n", ast); interpreter.run(ast); - } + // } } diff --git a/src/parser.rs b/src/parser.rs index 2db475b..0471df7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -70,15 +70,25 @@ pub enum UnOpType { } #[derive(Debug, PartialEq, Eq, Clone)] -pub enum Ast { +pub enum Expression { /// Integer literal (64-bit) I64(i64), /// Variable Var(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 operand - UnOp(UnOpType, Box), + UnOp(UnOpType, Box), +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Statement { + Expr(Expression), +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Ast { + pub prog: Vec } struct Parser> { @@ -93,16 +103,44 @@ impl> Parser { } fn parse(&mut self) -> Ast { - self.parse_expr() + let mut prog = Vec::new(); + + loop { + match self.peek() { + Token::Semicolon => { + self.next(); + } + Token::EoF => break, + + // By default try to lex a statement + _ => { + prog.push(self.parse_stmt()) + } + } + } + + Ast { prog } + } - fn parse_expr(&mut self) -> Ast { + fn parse_stmt(&mut self) -> Statement { + let expr = self.parse_expr(); + + // After a statement, there must be a semicolon + if !matches!(self.next(), Token::Semicolon) { + panic!("Expected semicolon after statement"); + } + + Statement::Expr(expr) + } + + fn parse_expr(&mut self) -> Expression { 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: Expression, min_prec: u8) -> Expression { while let Some(binop) = &self.peek().try_to_binop() { // Stop if the next operator has a lower binding power if !(binop.precedence() >= min_prec) { @@ -123,19 +161,19 @@ impl> Parser { rhs = self.parse_expr_precedence(rhs, binop.precedence() + 1); } - lhs = Ast::BinOp(binop, lhs.into(), rhs.into()); + lhs = Expression::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) -> Expression { match self.next() { // Literal i64 - Token::I64(val) => Ast::I64(val), + Token::I64(val) => Expression::I64(val), - Token::Ident(name) => Ast::Var(name), + Token::Ident(name) => Expression::Var(name), // Parentheses grouping Token::LParen => { @@ -152,12 +190,12 @@ impl> Parser { // Unary negation Token::Sub => { let operand = self.parse_primary(); - Ast::UnOp(UnOpType::Negate, operand.into()) + Expression::UnOp(UnOpType::Negate, operand.into()) } Token::Tilde => { let operand = self.parse_primary(); - Ast::UnOp(UnOpType::BNot, operand.into()) + Expression::UnOp(UnOpType::BNot, operand.into()) } tok => panic!("Error parsing primary expr: Unexpected Token '{:?}'", tok), @@ -206,8 +244,8 @@ impl BinOpType { #[cfg(test)] mod tests { - use super::{parse, Ast, BinOpType}; - use crate::lexer::Token; + use super::{parse, Expression, BinOpType}; + use crate::{lexer::Token, parser::{Statement, Ast}}; #[test] fn test_parser() { @@ -223,16 +261,18 @@ mod tests { Token::I64(4), ]; - let expected = Ast::BinOp( + let expected = Statement::Expr(Expression::BinOp( BinOpType::Sub, - Ast::BinOp( + Expression::BinOp( BinOpType::Add, - Ast::I64(1).into(), - Ast::BinOp(BinOpType::Mul, Ast::I64(2).into(), Ast::I64(3).into()).into(), + Expression::I64(1).into(), + Expression::BinOp(BinOpType::Mul, Expression::I64(2).into(), Expression::I64(3).into()).into(), ) .into(), - Ast::I64(4).into(), - ); + Expression::I64(4).into(), + )); + + let expected = Ast { prog: vec![expected] }; let actual = parse(tokens); assert_eq!(expected, actual);