-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
140 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
/target | ||
Cargo.lock | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,3 @@ | ||
pub mod lexer; | ||
pub mod syntax; | ||
|
||
pub fn test() { | ||
println!("Hello"); | ||
} | ||
pub mod parser; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,10 @@ | ||
use mini_ml::lexer; | ||
use mini_ml::parser; | ||
|
||
fn main() { | ||
let mut lexer = lexer::Lexer::new("if true then 3 else 4;;"); | ||
let tokens = lexer.lex(); | ||
println!("{:?}", tokens); | ||
let mut lexer = lexer::Lexer::new("if true then if false then 1 else 2 else 4;;"); | ||
let tokens = lexer.lex().unwrap(); | ||
let mut parser = parser::Parser::new(tokens); | ||
let ast = parser.parse(); | ||
println!("{:?}", ast); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
use crate::lexer::Token; | ||
use crate::syntax::{BinOpKind, Expr}; | ||
|
||
#[derive(Debug)] | ||
pub enum ParseError { | ||
Eof, | ||
UnexpectedToken, | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct Parser<'a> { | ||
tokens: &'a Vec<Token>, | ||
pos: usize, | ||
} | ||
|
||
impl<'a> Parser<'a> { | ||
pub fn new(tokens: &'a Vec<Token>) -> Self { | ||
Parser { tokens, pos: 0 } | ||
} | ||
|
||
/// Take a look at a next token and return its kind. | ||
fn peek(&self) -> Option<&Token> { | ||
if self.tokens.len() == self.pos { | ||
return None; | ||
} | ||
Some(&self.tokens[self.pos]) | ||
} | ||
|
||
/// Return current token and move `pos` forward. | ||
fn next(&mut self) -> Option<Token> { | ||
if self.tokens.len() == self.pos { | ||
return None; | ||
} | ||
let token = self.tokens[self.pos].clone(); | ||
self.pos += 1; | ||
Some(token) | ||
} | ||
|
||
/// Check if a current token has expected type and proceed to next one. | ||
fn expect_token(&mut self, expected_token: Token) -> Result<(), ParseError> { | ||
self.next().ok_or(ParseError::Eof).and_then(|token| { | ||
if token == expected_token { | ||
Ok(()) | ||
} else { | ||
println!("{:?}", token); | ||
println!("{:?}", expected_token); | ||
Err(ParseError::UnexpectedToken) | ||
} | ||
}) | ||
} | ||
|
||
/// Parse tokens and build AST. | ||
pub fn parse(&mut self) -> Result<Vec<Expr>, ParseError> { | ||
let mut asts = Vec::new(); | ||
loop { | ||
let ast = self.parse_expr()?; | ||
self.expect_token(Token::SemiColon)?; | ||
asts.push(ast); | ||
if self.peek().is_none() { | ||
break; | ||
} | ||
} | ||
Ok(asts) | ||
} | ||
|
||
fn parse_expr(&mut self) -> Result<Expr, ParseError> { | ||
match self.peek() { | ||
Some(&Token::If) => self.parse_if(), | ||
_ => self.parse_add(), | ||
} | ||
} | ||
|
||
fn parse_if(&mut self) -> Result<Expr, ParseError> { | ||
self.expect_token(Token::If)?; | ||
let condition = self.parse_expr()?; | ||
self.expect_token(Token::Then)?; | ||
let then = self.parse_expr()?; | ||
self.expect_token(Token::Else)?; | ||
let els = self.parse_expr()?; | ||
Ok(Expr::If(Box::new(condition), Box::new(then), Box::new(els))) | ||
} | ||
|
||
fn parse_add(&mut self) -> Result<Expr, ParseError> { | ||
let mut lhs = self.parse_mul()?; | ||
loop { | ||
if self.peek() == Some(&Token::Plus) { | ||
self.next(); | ||
let rhs = self.parse_mul()?; | ||
lhs = Expr::Binop(BinOpKind::Plus, Box::new(lhs), Box::new(rhs)); | ||
} else { | ||
break; | ||
} | ||
} | ||
Ok(lhs) | ||
} | ||
|
||
fn parse_mul(&mut self) -> Result<Expr, ParseError> { | ||
let mut lhs = self.parse_primary()?; | ||
loop { | ||
if self.peek() == Some(&Token::Asterisk) { | ||
self.next(); | ||
let rhs = self.parse_primary()?; | ||
lhs = Expr::Binop(BinOpKind::Plus, Box::new(lhs), Box::new(rhs)); | ||
} else { | ||
break; | ||
} | ||
} | ||
Ok(lhs) | ||
} | ||
|
||
fn parse_primary(&mut self) -> Result<Expr, ParseError> { | ||
self.next() | ||
.ok_or(ParseError::Eof) | ||
.and_then(|token| match token { | ||
Token::Number(n) => Ok(Expr::U64(n)), | ||
Token::Identifier(var) => Ok(Expr::Var(var)), | ||
Token::True => Ok(Expr::Bool(true)), | ||
Token::False => Ok(Expr::Bool(false)), | ||
_ => unimplemented!(), | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters