Implement variable declaration

This commit is contained in:
Daniel M 2022-01-28 18:49:30 +01:00
parent b128b3357a
commit 7646177030
4 changed files with 135 additions and 43 deletions

View File

@ -14,8 +14,8 @@
- [x] Negate `-X` - [x] Negate `-X`
- [x] Parentheses `(X+Y)*Z` - [x] Parentheses `(X+Y)*Z`
- [x] Logical boolean operators - [x] Logical boolean operators
- [ ] Variables - [x] Variables
- [ ] Declaration - [x] Declaration
- [ ] Assignment - [ ] Assignment
- [ ] While loop `while X { ... }` - [ ] While loop `while X { ... }`
- [ ] If else statement `if X { ... } else { ... }` - [ ] If else statement `if X { ... } else { ... }`

View File

@ -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)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Value { pub enum Value {
@ -6,29 +8,46 @@ pub enum Value {
} }
pub struct Interpreter { pub struct Interpreter {
// Runtime storage, for example variables ... /// The variable table maps all variables by their names to their values
vartable: HashMap<String, Value>
} }
impl Interpreter { impl Interpreter {
pub fn new() -> Self { pub fn new() -> Self {
Self {} let vartable = HashMap::new();
Self { vartable }
} }
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 {
println!("Result = {:?}", result); Stmt::Expr(expr) => {
} let result = self.resolve_expr(expr);
println!("Result = {:?}", result);
fn resolve_expr(&mut self, expr: Ast) -> Value { }
match expr { Stmt::Let(name, rhs) => {
Ast::I64(val) => Value::I64(val), let result = self.resolve_expr(rhs);
Ast::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, *lhs, *rhs), self.vartable.insert(name, result);
Ast::UnOp(uo, val) => self.resolve_unop(uo, *val), },
}
} }
} }
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 lhs = self.resolve_expr(lhs);
let rhs = self.resolve_expr(rhs); 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); let val = self.resolve_expr(val);
match val { match val {
Value::I64(val) => match uo { Value::I64(val) => match uo {
@ -69,21 +88,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::{Expr, 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 = Expr::BinOp(
BinOpType::Add, BinOpType::Add,
Ast::BinOp( Expr::BinOp(
BinOpType::Add, BinOpType::Add,
Ast::I64(1).into(), Expr::I64(1).into(),
Ast::BinOp(BinOpType::Mul, Ast::I64(2).into(), Ast::I64(3).into()).into(), Expr::BinOp(BinOpType::Mul, Expr::I64(2).into(), Expr::I64(3).into()).into(),
) )
.into(), .into(),
Ast::I64(4).into(), Expr::I64(4).into(),
); );
let expected = Value::I64(11); let expected = Value::I64(11);

View File

@ -13,6 +13,15 @@ pub enum Token {
/// Right parentheses (')') /// Right parentheses (')')
RParen, RParen,
/// Identifier (variable / function / ... name)
Ident(String),
/// Let identifier (let)
Let,
/// Assignment (single equal) (=)
Assign,
/// Plus (+) /// Plus (+)
Add, Add,
@ -61,6 +70,9 @@ pub enum Token {
/// Less or equal (<=) /// Less or equal (<=)
Le, Le,
/// Semicolon (;)
Semicolon,
/// End of file /// End of file
EoF, EoF,
} }
@ -81,7 +93,7 @@ impl<'a> Lexer<'a> {
while let Some(ch) = self.next() { while let Some(ch) = self.next() {
match ch { match ch {
// Skip whitespace // Skip whitespace
' ' => (), ' ' | '\r' | '\n' | '\t' => (),
// Lex numbers // Lex numbers
'0'..='9' => { '0'..='9' => {
@ -144,6 +156,8 @@ impl<'a> Lexer<'a> {
')' => tokens.push(Token::RParen), ')' => tokens.push(Token::RParen),
'<' => tokens.push(Token::Lt), '<' => tokens.push(Token::Lt),
'>' => tokens.push(Token::Gt), '>' => tokens.push(Token::Gt),
'=' => tokens.push(Token::Assign),
';' => tokens.push(Token::Semicolon),
'a'..='z' | 'A'..='Z' | '_' => { 'a'..='z' | 'A'..='Z' | '_' => {
let mut ident = String::from(ch); let mut ident = String::from(ch);
@ -157,7 +171,8 @@ impl<'a> Lexer<'a> {
match ident.as_str() { match ident.as_str() {
"true" => tokens.push(Token::I64(1)), "true" => tokens.push(Token::I64(1)),
"false" => tokens.push(Token::I64(0)), "false" => tokens.push(Token::I64(0)),
_ => panic!("Lexer encountered unknown ident: '{}'", ident), "let" => tokens.push(Token::Let),
_ => tokens.push(Token::Ident(ident)),
} }
} }

View File

@ -62,19 +62,32 @@ pub enum UnOpType {
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Ast { pub struct Ast {
pub prog: Vec<Stmt>,
}
#[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) /// Integer literal (64-bit)
I64(i64), I64(i64),
/// Identifier (variable name)
Ident(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<Expr>, Box<Expr>),
/// Unary operation. Consists of type and the value that is operated on /// Unary operation. Consists of type and the value that is operated on
UnOp(UnOpType, Box<Ast>), UnOp(UnOpType, Box<Expr>),
} }
/* /*
## Grammar ## Grammar
### Expressions ### Expressions
expr_primary = LITERAL | "(" expr ")" | "-" expr_primary expr_primary = LITERAL | IDENT | "(" expr ")" | "-" expr_primary
expr_mul = expr_primary (("*" | "/" | "%") expr_primary)* expr_mul = expr_primary (("*" | "/" | "%") expr_primary)*
expr_add = expr_mul (("+" | "-") expr_mul)* expr_add = expr_mul (("+" | "-") expr_mul)*
expr_shift = expr_add ((">>" | "<<") expr_add)* expr_shift = expr_add ((">>" | "<<") expr_add)*
@ -84,6 +97,11 @@ 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_let = "let" IDENT "=" expr
stmt = stmt_expr | stmt_let (";")*
*/ */
struct Parser<T: Iterator<Item = Token>> { struct Parser<T: Iterator<Item = Token>> {
@ -98,16 +116,52 @@ 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 {
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(); 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: Expr, min_prec: u8) -> Expr {
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) {
@ -128,16 +182,18 @@ 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 = Expr::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) -> Expr {
match self.next() { match self.next() {
Token::I64(val) => Ast::I64(val), Token::I64(val) => Expr::I64(val),
Token::Ident(name) => Expr::Ident(name),
Token::LParen => { Token::LParen => {
// The tokens was an opening parenthesis, so parse a full expression again as the // The tokens was an opening parenthesis, so parse a full expression again as the
@ -152,7 +208,7 @@ impl<T: Iterator<Item = Token>> Parser<T> {
inner 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), tok => panic!("Error parsing primary expr: Unexpected Token '{:?}'", tok),
} }
@ -197,8 +253,8 @@ impl BinOpType {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{parse, Ast, BinOpType}; use super::{parse, Expr, BinOpType};
use crate::lexer::Token; use crate::{lexer::Token, parser::{Stmt, Ast}};
#[test] #[test]
fn test_parser() { fn test_parser() {
@ -214,17 +270,19 @@ mod tests {
Token::I64(4), Token::I64(4),
]; ];
let expected = Ast::BinOp( let expected = Expr::BinOp(
BinOpType::Sub, BinOpType::Sub,
Ast::BinOp( Expr::BinOp(
BinOpType::Add, BinOpType::Add,
Ast::I64(1).into(), Expr::I64(1).into(),
Ast::BinOp(BinOpType::Mul, Ast::I64(2).into(), Ast::I64(3).into()).into(), Expr::BinOp(BinOpType::Mul, Expr::I64(2).into(), Expr::I64(3).into()).into(),
) )
.into(), .into(),
Ast::I64(4).into(), Expr::I64(4).into(),
); );
let expected = Ast { prog: vec![Stmt::Expr(expected)] };
let actual = parse(tokens); let actual = parse(tokens);
assert_eq!(expected, actual); assert_eq!(expected, actual);
} }