Skip to content

Commit

Permalink
✨ Implement parser
Browse files Browse the repository at this point in the history
  • Loading branch information
ikanago committed May 21, 2020
1 parent 49d7def commit 7505115
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 15 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
Cargo.lock
.vscode
10 changes: 5 additions & 5 deletions src/lexer.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::collections::HashMap;
use std::str::from_utf8;

#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub enum Token {
Number(usize),
Number(u64),
Identifier(String),
True,
False,
Expand All @@ -17,8 +17,8 @@ pub enum Token {

fn reserve_keyword() -> HashMap<String, Token> {
let mut keywords = HashMap::new();
keywords.insert("True".to_string(), Token::True);
keywords.insert("False".to_string(), Token::False);
keywords.insert("true".to_string(), Token::True);
keywords.insert("false".to_string(), Token::False);
keywords.insert("if".to_string(), Token::If);
keywords.insert("then".to_string(), Token::Then);
keywords.insert("else".to_string(), Token::Else);
Expand Down Expand Up @@ -79,7 +79,7 @@ impl<'a> Lexer<'a> {
let end = self.read_many(|b| b"0123456789".contains(&b));
let num = from_utf8(&self.input[start..end])
.unwrap()
.parse::<usize>()
.parse::<u64>()
.unwrap();
self.pos = end;
Some(Token::Number(num))
Expand Down
5 changes: 1 addition & 4 deletions src/lib.rs
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;
9 changes: 6 additions & 3 deletions src/main.rs
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);
}
122 changes: 122 additions & 0 deletions src/parser.rs
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!(),
})
}
}
8 changes: 5 additions & 3 deletions src/syntax.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
enum BinopKind {
#[derive(Debug)]
pub enum BinOpKind {
Plus,
Mult,
}

enum Expr {
#[derive(Debug)]
pub enum Expr {
Var(String),
U64(u64),
Bool(bool),
Binop(BinopKind, Box<Expr>, Box<Expr>),
Binop(BinOpKind, Box<Expr>, Box<Expr>),
If(Box<Expr>, Box<Expr>, Box<Expr>),
}

Expand Down

0 comments on commit 7505115

Please sign in to comment.