Math :D
This commit is contained in:
parent
9aa09537a9
commit
29b4f225b2
6 changed files with 155 additions and 1 deletions
10
src/ast.rs
Normal file
10
src/ast.rs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Expresion {
|
||||||
|
Integer(isize),
|
||||||
|
Negate(Box<Expresion>),
|
||||||
|
Add(Box<Expresion>, Box<Expresion>),
|
||||||
|
Subtract(Box<Expresion>, Box<Expresion>),
|
||||||
|
Multiply(Box<Expresion>, Box<Expresion>),
|
||||||
|
Divide(Box<Expresion>, Box<Expresion>),
|
||||||
|
Modulo(Box<Expresion>, Box<Expresion>),
|
||||||
|
}
|
42
src/executor.rs
Normal file
42
src/executor.rs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
use chumsky::{error::Simple, Parser};
|
||||||
|
use core::ops::Range;
|
||||||
|
use logos::Logos;
|
||||||
|
|
||||||
|
use crate::{ast::Expresion, parser::parser, tokens::Token};
|
||||||
|
|
||||||
|
pub enum Error {
|
||||||
|
Lexing(Range<usize>, String),
|
||||||
|
Ast(Vec<Simple<Token>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(code: &str) -> Result<isize, Error> {
|
||||||
|
let lexer = Token::lexer(code);
|
||||||
|
let mut tokens = vec![];
|
||||||
|
for (token, span) in lexer.spanned() {
|
||||||
|
match token {
|
||||||
|
Ok(token) => tokens.push(token),
|
||||||
|
Err(error) => return Err(Error::Lexing(span, error)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ast_result = parser().parse(tokens);
|
||||||
|
let Ok(ast) = ast_result else {
|
||||||
|
return Err(Error::Ast(ast_result.unwrap_err()));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ast.evaluate())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Expresion {
|
||||||
|
fn evaluate(&self) -> isize {
|
||||||
|
match self {
|
||||||
|
Expresion::Integer(value) => *value,
|
||||||
|
Expresion::Negate(value) => -value.evaluate(),
|
||||||
|
Expresion::Add(left, right) => left.evaluate() + right.evaluate(),
|
||||||
|
Expresion::Subtract(left, right) => left.evaluate() - right.evaluate(),
|
||||||
|
Expresion::Multiply(left, right) => left.evaluate() * right.evaluate(),
|
||||||
|
Expresion::Divide(left, right) => left.evaluate() / right.evaluate(),
|
||||||
|
Expresion::Modulo(left, right) => left.evaluate() % right.evaluate(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
src/main.rs
17
src/main.rs
|
@ -1,3 +1,18 @@
|
||||||
|
mod ast;
|
||||||
|
mod executor;
|
||||||
|
mod parser;
|
||||||
|
mod tokens;
|
||||||
|
use std::{env, fs};
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
let src = fs::read_to_string(env::args().nth(1).expect("Expected a file"))
|
||||||
|
.expect("Failed to read file");
|
||||||
|
match executor::execute(&src) {
|
||||||
|
Ok(result) => println!("{}", result),
|
||||||
|
Err(err) => match err {
|
||||||
|
executor::Error::Lexing(range, msg) => {
|
||||||
|
println!("Invalid Syntax at: {:?} | {}", range, msg)
|
||||||
|
}
|
||||||
|
executor::Error::Ast(ast) => println!("AST Error: {:?}", ast),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
55
src/parser.rs
Normal file
55
src/parser.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use crate::ast::Expresion;
|
||||||
|
use crate::tokens::Token;
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
|
pub fn parser() -> impl Parser<Token, Expresion, Error = Simple<Token>> {
|
||||||
|
recursive(|parser| {
|
||||||
|
let atom = {
|
||||||
|
let parenthesized = parser
|
||||||
|
.clone()
|
||||||
|
.delimited_by(just(Token::ParanthesisOpen), just(Token::ParenthesisClose));
|
||||||
|
|
||||||
|
let integer = select! {
|
||||||
|
Token::Integer(n) => Expresion::Integer(n),
|
||||||
|
};
|
||||||
|
|
||||||
|
parenthesized.or(integer)
|
||||||
|
};
|
||||||
|
|
||||||
|
let unary = just(Token::Minus)
|
||||||
|
.repeated()
|
||||||
|
.then(atom)
|
||||||
|
.foldr(|_operator, value| Expresion::Negate(Box::new(value)));
|
||||||
|
|
||||||
|
let multiply_and_divide_and_modulo = unary
|
||||||
|
.clone()
|
||||||
|
.then(
|
||||||
|
just(Token::Multiply)
|
||||||
|
.or(just(Token::Divide))
|
||||||
|
.or(just(Token::Modulo))
|
||||||
|
.then(unary)
|
||||||
|
.repeated(),
|
||||||
|
)
|
||||||
|
.foldl(|left, (operator, right)| match operator {
|
||||||
|
Token::Multiply => Expresion::Multiply(Box::new(left), Box::new(right)),
|
||||||
|
Token::Divide => Expresion::Divide(Box::new(left), Box::new(right)),
|
||||||
|
Token::Modulo => Expresion::Modulo(Box::new(left), Box::new(right)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
});
|
||||||
|
|
||||||
|
multiply_and_divide_and_modulo
|
||||||
|
.clone()
|
||||||
|
.then(
|
||||||
|
just(Token::Plus)
|
||||||
|
.or(just(Token::Minus))
|
||||||
|
.then(multiply_and_divide_and_modulo)
|
||||||
|
.repeated(),
|
||||||
|
)
|
||||||
|
.foldl(|left, (operator, right)| match operator {
|
||||||
|
Token::Plus => Expresion::Add(Box::new(left), Box::new(right)),
|
||||||
|
Token::Minus => Expresion::Subtract(Box::new(left), Box::new(right)),
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then_ignore(end())
|
||||||
|
}
|
30
src/tokens.rs
Normal file
30
src/tokens.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
use logos::Logos;
|
||||||
|
|
||||||
|
#[derive(Logos, Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
|
#[logos(skip r"[ \t\n]+")]
|
||||||
|
#[logos(error = String)]
|
||||||
|
pub enum Token {
|
||||||
|
#[token("+")]
|
||||||
|
Plus,
|
||||||
|
|
||||||
|
#[token("-")]
|
||||||
|
Minus,
|
||||||
|
|
||||||
|
#[token("*")]
|
||||||
|
Multiply,
|
||||||
|
|
||||||
|
#[token("/")]
|
||||||
|
Divide,
|
||||||
|
|
||||||
|
#[token("%")]
|
||||||
|
Modulo,
|
||||||
|
|
||||||
|
#[token("(")]
|
||||||
|
ParanthesisOpen,
|
||||||
|
|
||||||
|
#[token(")")]
|
||||||
|
ParenthesisClose,
|
||||||
|
|
||||||
|
#[regex("[0-9]+", |lex| lex.slice().parse::<isize>().unwrap())]
|
||||||
|
Integer(isize),
|
||||||
|
}
|
2
test.math
Normal file
2
test.math
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-128 + 5 * ( 37 + 8 - 19) * 39 / 27 * (-17) + 55 % 45 + -59
|
||||||
|
|
Loading…
Add table
Reference in a new issue