Implement multi statement code
- Add statements - Add mandatory semicolons after statements
This commit is contained in:
parent
14e8a0b507
commit
2ea2aa5203
@ -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)*
|
||||||
@ -63,3 +63,9 @@ 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
|
||||||
|
```
|
||||||
@ -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);
|
||||||
|
|||||||
@ -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),
|
||||||
|
|||||||
20
src/main.rs
20
src/main.rs
@ -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);
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user