Implement multi statement code

- Add statements
- Add mandatory semicolons after statements
This commit is contained in:
Kai-Philipp Nosper 2022-01-29 23:18:15 +01:00
parent 23d336d63e
commit 35fbae8ab9
5 changed files with 108 additions and 48 deletions

View File

@ -36,6 +36,7 @@
- [x] Variables - [x] Variables
- [x] Declaration - [x] Declaration
- [x] Assignment - [x] Assignment
- [x] Statements with semicolon & Multiline programs
- [ ] Control flow - [ ] Control flow
- [ ] While loop `while X { ... }` - [ ] While loop `while X { ... }`
- [ ] If else statement `if X { ... } else { ... }` - [ ] If else statement `if X { ... } else { ... }`
@ -50,7 +51,6 @@
## Grammar ## Grammar
### Expressions ### Expressions
``` ```
expr_primary = LITERAL | IDENT | "(" expr ")" | "-" expr_primary | "~" expr_primary expr_primary = LITERAL | IDENT | "(" expr ")" | "-" expr_primary | "~" expr_primary
expr_mul = 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_bxor = expr_band ("^" expr_band)*
expr_bor = expr_bxor ("|" expr_bxor)* expr_bor = expr_bxor ("|" expr_bxor)*
expr = expr_bor expr = expr_bor
```
### Statements
```
stmt_expr = expr ";"
stmt = stmt_expr
``` ```

View File

@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::parser::{Ast, BinOpType, UnOpType}; use crate::parser::{Expression, BinOpType, UnOpType, Ast, Statement};
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Value { pub enum Value {
@ -20,17 +20,23 @@ impl Interpreter {
} }
pub fn run(&mut self, prog: Ast) { 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 { match expr {
Ast::I64(val) => Value::I64(val), Expression::I64(val) => Value::I64(val),
Ast::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs), Expression::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs),
Ast::UnOp(uo, operand) => self.resolve_unop(uo, *operand), Expression::UnOp(uo, operand) => self.resolve_unop(uo, *operand),
Ast::Var(name) => self.resolve_var(name), 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); let operand = self.resolve_expr(operand);
match (operand, uo) { 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); let rhs = self.resolve_expr(rhs);
match (&bo, &lhs) { match (&bo, &lhs) {
(BinOpType::Declare, Ast::Var(name)) => { (BinOpType::Declare, Expression::Var(name)) => {
self.vartable.insert(name.clone(), rhs.clone()); self.vartable.insert(name.clone(), rhs.clone());
return rhs; return rhs;
} }
(BinOpType::Assign, Ast::Var(name)) => { (BinOpType::Assign, Expression::Var(name)) => {
match self.vartable.get_mut(name) { match self.vartable.get_mut(name) {
Some(val) => *val = rhs.clone(), Some(val) => *val = rhs.clone(),
None => panic!("Runtime Error: Trying to assign value to undeclared variable"), None => panic!("Runtime Error: Trying to assign value to undeclared variable"),
@ -100,21 +106,21 @@ impl Interpreter {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::{Interpreter, Value}; use super::{Interpreter, Value};
use crate::parser::{Ast, BinOpType}; use crate::parser::{Expression, BinOpType};
#[test] #[test]
fn test_interpreter_expr() { fn test_interpreter_expr() {
// Expression: 1 + 2 * 3 + 4 // Expression: 1 + 2 * 3 + 4
// With precedence: (1 + (2 * 3)) + 4 // With precedence: (1 + (2 * 3)) + 4
let ast = Ast::BinOp( let ast = Expression::BinOp(
BinOpType::Add, BinOpType::Add,
Ast::BinOp( Expression::BinOp(
BinOpType::Add, BinOpType::Add,
Ast::I64(1).into(), Expression::I64(1).into(),
Ast::BinOp(BinOpType::Mul, Ast::I64(2).into(), Ast::I64(3).into()).into(), Expression::BinOp(BinOpType::Mul, Expression::I64(2).into(), Expression::I64(3).into()).into(),
) )
.into(), .into(),
Ast::I64(4).into(), Expression::I64(4).into(),
); );
let expected = Value::I64(11); let expected = Value::I64(11);

View File

@ -73,6 +73,9 @@ pub enum Token {
/// Equal Sign (=) /// Equal Sign (=)
Equ, Equ,
/// Semicolon (;)
Semicolon,
/// End of file /// End of file
EoF, EoF,
} }
@ -93,7 +96,7 @@ impl<'a> Lexer<'a> {
loop { loop {
match self.next() { match self.next() {
// Skip whitespace // Skip whitespace
' ' | '\t' => (), ' ' | '\t' | '\n' | '\r' => (),
// Stop lexing at EOF // Stop lexing at EOF
'\0' => break, '\0' => break,
@ -127,6 +130,7 @@ impl<'a> Lexer<'a> {
tokens.push(Token::LArrow); tokens.push(Token::LArrow);
} }
';' => tokens.push(Token::Semicolon),
'+' => tokens.push(Token::Add), '+' => tokens.push(Token::Add),
'-' => tokens.push(Token::Sub), '-' => tokens.push(Token::Sub),
'*' => tokens.push(Token::Mul), '*' => tokens.push(Token::Mul),

View File

@ -7,15 +7,19 @@ fn main() {
let mut interpreter = Interpreter::new(); let mut interpreter = Interpreter::new();
let mut code = String::new(); // let mut code = String::new();
let code = "
a <- 5;
a * 2;
";
loop { // loop {
print!(">> "); // print!(">> ");
std::io::stdout().flush().unwrap(); // std::io::stdout().flush().unwrap();
code.clear(); // code.clear();
std::io::stdin().read_line(&mut code).unwrap(); // std::io::stdin().read_line(&mut code).unwrap();
let code = code.trim(); // let code = code.trim();
let tokens = lex(&code); let tokens = lex(&code);
@ -26,6 +30,6 @@ fn main() {
println!("Ast: {:#?}\n", ast); println!("Ast: {:#?}\n", ast);
interpreter.run(ast); interpreter.run(ast);
} // }
} }

View File

@ -70,15 +70,25 @@ pub enum UnOpType {
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Ast { pub enum Expression {
/// Integer literal (64-bit) /// Integer literal (64-bit)
I64(i64), I64(i64),
/// Variable /// Variable
Var(String), Var(String),
/// Binary operation. Consists of type, left hand side and right hand side /// Binary operation. Consists of type, left hand side and right hand side
BinOp(BinOpType, Box<Ast>, Box<Ast>), BinOp(BinOpType, Box<Expression>, Box<Expression>),
/// Unary operation. Consists of type and operand /// Unary operation. Consists of type and operand
UnOp(UnOpType, Box<Ast>), UnOp(UnOpType, Box<Expression>),
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Statement {
Expr(Expression),
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Ast {
pub prog: Vec<Statement>
} }
struct Parser<T: Iterator<Item = Token>> { struct Parser<T: Iterator<Item = Token>> {
@ -93,16 +103,44 @@ impl<T: Iterator<Item = Token>> Parser<T> {
} }
fn parse(&mut self) -> Ast { 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(); let lhs = self.parse_primary();
self.parse_expr_precedence(lhs, 0) self.parse_expr_precedence(lhs, 0)
} }
/// Parse binary expressions with a precedence equal to or higher than min_prec /// 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() { while let Some(binop) = &self.peek().try_to_binop() {
// Stop if the next operator has a lower binding power // Stop if the next operator has a lower binding power
if !(binop.precedence() >= min_prec) { if !(binop.precedence() >= min_prec) {
@ -123,19 +161,19 @@ impl<T: Iterator<Item = Token>> Parser<T> {
rhs = self.parse_expr_precedence(rhs, binop.precedence() + 1); 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 lhs
} }
/// Parse a primary expression (for now only number) /// Parse a primary expression (for now only number)
fn parse_primary(&mut self) -> Ast { fn parse_primary(&mut self) -> Expression {
match self.next() { match self.next() {
// Literal i64 // 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 // Parentheses grouping
Token::LParen => { Token::LParen => {
@ -152,12 +190,12 @@ impl<T: Iterator<Item = Token>> Parser<T> {
// Unary negation // Unary negation
Token::Sub => { Token::Sub => {
let operand = self.parse_primary(); let operand = self.parse_primary();
Ast::UnOp(UnOpType::Negate, operand.into()) Expression::UnOp(UnOpType::Negate, operand.into())
} }
Token::Tilde => { Token::Tilde => {
let operand = self.parse_primary(); 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), tok => panic!("Error parsing primary expr: Unexpected Token '{:?}'", tok),
@ -206,8 +244,8 @@ impl BinOpType {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{parse, Ast, BinOpType}; use super::{parse, Expression, BinOpType};
use crate::lexer::Token; use crate::{lexer::Token, parser::{Statement, Ast}};
#[test] #[test]
fn test_parser() { fn test_parser() {
@ -223,16 +261,18 @@ mod tests {
Token::I64(4), Token::I64(4),
]; ];
let expected = Ast::BinOp( let expected = Statement::Expr(Expression::BinOp(
BinOpType::Sub, BinOpType::Sub,
Ast::BinOp( Expression::BinOp(
BinOpType::Add, BinOpType::Add,
Ast::I64(1).into(), Expression::I64(1).into(),
Ast::BinOp(BinOpType::Mul, Ast::I64(2).into(), Ast::I64(3).into()).into(), Expression::BinOp(BinOpType::Mul, Expression::I64(2).into(), Expression::I64(3).into()).into(),
) )
.into(), .into(),
Ast::I64(4).into(), Expression::I64(4).into(),
); ));
let expected = Ast { prog: vec![expected] };
let actual = parse(tokens); let actual = parse(tokens);
assert_eq!(expected, actual); assert_eq!(expected, actual);