Start implementing Parser

- Implemented pratt parser for expressions
- Implemented BinOps Add, Mul
This commit is contained in:
Daniel M 2022-01-02 21:03:14 +01:00
parent 02f63ad9ad
commit 541d905551
4 changed files with 144 additions and 8 deletions

View File

@ -1,12 +1,20 @@
use std::{iter::Peekable, str::Chars}; use std::{iter::Peekable, str::Chars};
use crate::parser::BinOpType;
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
pub enum Token { pub enum Token {
/// Integer literal (64-bit)
I64(i64), I64(i64),
/// Plus (+)
Add, Add,
/// Asterisk (*)
Mul, Mul,
/// End of file
EoF,
} }
struct Lexer<'a> { struct Lexer<'a> {
@ -24,7 +32,10 @@ impl<'a> Lexer<'a> {
while let Some(ch) = self.next() { while let Some(ch) = self.next() {
match ch { match ch {
// Skip whitespace
' ' => (), ' ' => (),
// Lex numbers
'0'..='9' => { '0'..='9' => {
let mut sval = String::from(ch); let mut sval = String::from(ch);
@ -43,7 +54,6 @@ impl<'a> Lexer<'a> {
//TODO: Don't panic, keep calm //TODO: Don't panic, keep calm
_ => panic!("Lexer encountered unexpected char: '{}'", ch), _ => panic!("Lexer encountered unexpected char: '{}'", ch),
} }
} }
@ -55,20 +65,33 @@ impl<'a> Lexer<'a> {
self.code.next() self.code.next()
} }
/// Shows next character /// Get the next character without removing it
fn peek(&mut self) -> Option<char> { fn peek(&mut self) -> Option<char> {
self.code.peek().copied() self.code.peek().copied()
} }
} }
/// Lex the provided code into a Token Buffer
///
/// TODO: Don't panic and implement error handling using Result
pub fn lex(code: &str) -> Vec<Token> { pub fn lex(code: &str) -> Vec<Token> {
let mut lexer = Lexer::new(code); let mut lexer = Lexer::new(code);
lexer.lex() lexer.lex()
} }
impl Token {
pub fn try_to_binop(&self) -> Option<BinOpType> {
Some(match self {
Token::Add => BinOpType::Add,
Token::Mul => BinOpType::Mul,
_ => return None,
})
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{Token, lex}; use super::{lex, Token};
#[test] #[test]
fn test_lexer() { fn test_lexer() {

View File

@ -1,2 +1,2 @@
pub mod lexer; pub mod lexer;
pub mod parser;

View File

@ -1,12 +1,23 @@
use nek_lang::lexer::lex; use nek_lang::{lexer::lex, parser::parse};
fn main() { fn main() {
let code = "33 +5*2"; let code = "33 +5*2";
// Should produce ast: Add {
// lhs: I64(33),
// rhs: Mul: {
// lhs: I64(5),
// rhs: I64(2)
// }
// }
let tokens = lex(code); let tokens = lex(code);
println!("{:?}", tokens); println!("Tokens: {:?}\n", tokens);
let ast = parse(tokens);
println!("Ast: {:#?}", ast);
} }

102
src/parser.rs Normal file
View File

@ -0,0 +1,102 @@
use std::iter::Peekable;
use crate::lexer::Token;
#[derive(Debug)]
pub enum BinOpType {
/// Addition
Add,
/// Multiplication
Mul,
}
#[derive(Debug)]
pub enum Ast {
/// Integer literal (64-bit)
I64(i64),
/// Binary operation. Consists of type, left hand side and right hand side
BinOp(BinOpType, Box<Ast>, Box<Ast>),
}
struct Parser<T: Iterator<Item = Token>> {
tokens: Peekable<T>,
}
impl<T: Iterator<Item = Token>> Parser<T> {
/// Create a new parser to parse the given Token Stream
fn new<A: IntoIterator<IntoIter = T>>(tokens: A) -> Self {
let tokens = tokens.into_iter().peekable();
Self { tokens }
}
fn parse(&mut self) -> Ast {
self.parse_expr()
}
fn parse_expr(&mut self) -> Ast {
let lhs = self.parse_primary();
self.parse_expr_precedence(lhs, 0)
}
fn parse_expr_precedence(&mut self, mut lhs: Ast, min_prec: u8) -> Ast {
while let Some(binop) = &self.peek().try_to_binop() {
if !(binop.precedence() >= min_prec) {
break;
}
// The while condition already verified that this is some while peeking, so unwrap is
// valid
let binop = self.next().try_to_binop().unwrap();
let mut rhs = self.parse_primary();
while let Some(binop2) = &self.peek().try_to_binop() {
if !(binop2.precedence() > binop.precedence()) {
break;
}
rhs = self.parse_expr_precedence(rhs, binop.precedence() + 1);
}
lhs = Ast::BinOp(binop, lhs.into(), rhs.into());
}
lhs
}
/// Parse a primary expression (for now only number)
fn parse_primary(&mut self) -> Ast {
match self.next() {
Token::I64(val) => Ast::I64(val),
tok => panic!("Error parsing primary expr: Unexpected Token '{:?}'", tok),
}
}
/// Get the next Token without removing it
fn peek(&mut self) -> &Token {
self.tokens.peek().unwrap_or(&Token::EoF)
}
/// Advance to next Token and return the removed Token
fn next(&mut self) -> Token {
self.tokens.next().unwrap_or(Token::EoF)
}
}
pub fn parse<T: Iterator<Item = Token>, A: IntoIterator<IntoIter = T>>(tokens: A) -> Ast {
let mut parser = Parser::new(tokens);
parser.parse()
}
impl BinOpType {
/// Get the precedence for a binary operator. Higher value means the OP is stronger binding.
/// For example Multiplication is stronger than addition, so Mul has higher precedence than Add.
fn precedence(&self) -> u8 {
match self {
BinOpType::Add => 0,
BinOpType::Mul => 1,
}
}
}