feat(parser): add parser
added a basic ast parser that is likely riddled with bugs
This commit is contained in:
4 files changed
+179
-4
No files matched your search
+3
-1
@@ -3,13 +3,14 @@ use std::str::FromStr;
|
||||
use crate::math::types::Operator;
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Token {
|
||||
Number(Decimal),
|
||||
Identifier(String),
|
||||
Operator(Operator),
|
||||
OpenParen,
|
||||
CloseParen,
|
||||
Comma,
|
||||
Eof,
|
||||
}
|
||||
|
||||
@@ -73,6 +74,7 @@ impl<'a> Lexer<'a> {
|
||||
match self.chars.next() {
|
||||
Some('(') => Token::OpenParen,
|
||||
Some(')') => Token::CloseParen,
|
||||
Some(',') => Token::Comma,
|
||||
Some('+') => Token::Operator(Operator::Add),
|
||||
Some('-') => Token::Operator(Operator::Subtract),
|
||||
Some('*') => Token::Operator(Operator::Multiply),
|
||||
|
||||
+6
-2
@@ -1,8 +1,12 @@
|
||||
use crate::math::types::Expression;
|
||||
|
||||
mod lexer;
|
||||
mod parser;
|
||||
|
||||
pub mod types;
|
||||
|
||||
pub fn parse(source: &str) -> Vec<lexer::Token> {
|
||||
pub fn parse(source: &str) -> Expression {
|
||||
let lexer = lexer::from(source);
|
||||
lexer.collect()
|
||||
let mut parser = parser::from(lexer.collect());
|
||||
parser.parse()
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
use crate::math::{
|
||||
lexer::Token,
|
||||
types::{Expression, Operator},
|
||||
};
|
||||
|
||||
pub fn from(tokens: Vec<Token>) -> Parser {
|
||||
Parser::new(tokens)
|
||||
}
|
||||
|
||||
pub struct Parser {
|
||||
tokens: Vec<Token>,
|
||||
position: usize,
|
||||
}
|
||||
|
||||
impl Parser {
|
||||
pub fn new(tokens: Vec<Token>) -> Self {
|
||||
Self {
|
||||
tokens,
|
||||
position: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(&mut self) -> Expression {
|
||||
self.parse_additive()
|
||||
}
|
||||
|
||||
fn parse_additive(&mut self) -> Expression {
|
||||
let mut left = self.parse_multiplicative();
|
||||
|
||||
while let Some(token) = self.tokens.get(self.position) {
|
||||
match token {
|
||||
Token::Operator(Operator::Add) => {
|
||||
self.position += 1;
|
||||
let right = self.parse_multiplicative();
|
||||
left = Expression::Operation(Operator::Add, Box::new(left), Box::new(right));
|
||||
}
|
||||
Token::Operator(Operator::Subtract) => {
|
||||
self.position += 1;
|
||||
let right = self.parse_multiplicative();
|
||||
left =
|
||||
Expression::Operation(Operator::Subtract, Box::new(left), Box::new(right));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
left
|
||||
}
|
||||
|
||||
fn parse_multiplicative(&mut self) -> Expression {
|
||||
let mut left = self.parse_primary();
|
||||
|
||||
while let Some(token) = self.tokens.get(self.position) {
|
||||
match token {
|
||||
Token::Operator(Operator::Multiply) => {
|
||||
self.position += 1;
|
||||
let right = self.parse_exponential();
|
||||
left =
|
||||
Expression::Operation(Operator::Multiply, Box::new(left), Box::new(right));
|
||||
}
|
||||
Token::Operator(Operator::Divide) => {
|
||||
self.position += 1;
|
||||
let right = self.parse_exponential();
|
||||
left = Expression::Fraction(Box::new(left), Box::new(right));
|
||||
}
|
||||
Token::Operator(Operator::Modulo) => {
|
||||
self.position += 1;
|
||||
let right = self.parse_exponential();
|
||||
left = Expression::Operation(Operator::Modulo, Box::new(left), Box::new(right));
|
||||
}
|
||||
Token::OpenParen => {
|
||||
self.position += 1;
|
||||
let right = self.parse_group();
|
||||
left =
|
||||
Expression::Operation(Operator::Multiply, Box::new(left), Box::new(right));
|
||||
self.position += 1;
|
||||
}
|
||||
Token::Identifier(_) => {
|
||||
let right = self.parse_primary();
|
||||
left =
|
||||
Expression::Operation(Operator::Multiply, Box::new(left), Box::new(right));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
left
|
||||
}
|
||||
|
||||
fn parse_exponential(&mut self) -> Expression {
|
||||
let mut left = self.parse_primary();
|
||||
|
||||
while let Some(token) = self.tokens.get(self.position) {
|
||||
match token {
|
||||
Token::Operator(Operator::Power) => {
|
||||
self.position += 1;
|
||||
let right = self.parse_group();
|
||||
left =
|
||||
Expression::Operation(Operator::Multiply, Box::new(left), Box::new(right));
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
left
|
||||
}
|
||||
|
||||
fn parse_group(&mut self) -> Expression {
|
||||
match self.tokens.get(self.position).expect("expected token!") {
|
||||
Token::OpenParen => {
|
||||
self.position += 1;
|
||||
self.parse_additive()
|
||||
}
|
||||
_ => self.parse_primary(),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_primary(&mut self) -> Expression {
|
||||
let token = self
|
||||
.tokens
|
||||
.get(self.position)
|
||||
.cloned()
|
||||
.expect("expected token!");
|
||||
|
||||
match token {
|
||||
Token::Number(num) => {
|
||||
self.position += 1;
|
||||
Expression::Number(num)
|
||||
}
|
||||
Token::Identifier(identifier) => {
|
||||
let is_function =
|
||||
matches!(self.tokens.get(self.position + 1), Some(Token::OpenParen));
|
||||
if is_function {
|
||||
self.position += 2;
|
||||
let params = self.parse_function_params();
|
||||
Expression::Function(identifier, params)
|
||||
} else {
|
||||
self.position += 1;
|
||||
Expression::Variable(identifier)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("unexpected token! {:?}", token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_function_params(&mut self) -> Vec<Expression> {
|
||||
let mut params: Vec<Expression> = Vec::new();
|
||||
|
||||
while let Some(token) = self.tokens.get(self.position) {
|
||||
match token {
|
||||
Token::Comma => {
|
||||
self.position += 1;
|
||||
}
|
||||
Token::CloseParen => {
|
||||
self.position += 1;
|
||||
break;
|
||||
}
|
||||
_ => {
|
||||
let expression = self.parse_additive();
|
||||
params.push(expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -10,7 +10,7 @@ pub enum Expression {
|
||||
Function(String, Vec<Expression>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Operator {
|
||||
Add,
|
||||
Subtract,
|
||||
|
||||
Reference in new issue
Block a user