Implement variable declaration
This commit is contained in:
parent
b128b3357a
commit
7646177030
@ -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 { ... }`
|
||||
|
||||
@ -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<String, Value>
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
19
src/lexer.rs
19
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)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -62,19 +62,32 @@ pub enum UnOpType {
|
||||
}
|
||||
|
||||
#[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)
|
||||
I64(i64),
|
||||
/// Identifier (variable name)
|
||||
Ident(String),
|
||||
/// 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
|
||||
UnOp(UnOpType, Box<Ast>),
|
||||
UnOp(UnOpType, Box<Expr>),
|
||||
}
|
||||
|
||||
/*
|
||||
## 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<T: Iterator<Item = Token>> {
|
||||
@ -98,16 +116,52 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
||||
}
|
||||
|
||||
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<T: Iterator<Item = Token>> Parser<T> {
|
||||
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<T: Iterator<Item = Token>> Parser<T> {
|
||||
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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user