From 29b4f225b231867eedd4455fd9a2683e6b760c35 Mon Sep 17 00:00:00 2001 From: Snoweuph Date: Fri, 14 Feb 2025 19:07:32 +0100 Subject: [PATCH] Math :D --- src/ast.rs | 10 +++++++++ src/executor.rs | 42 +++++++++++++++++++++++++++++++++++++ src/main.rs | 17 ++++++++++++++- src/parser.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ src/tokens.rs | 30 +++++++++++++++++++++++++++ test.math | 2 ++ 6 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 src/ast.rs create mode 100644 src/executor.rs create mode 100644 src/parser.rs create mode 100644 src/tokens.rs create mode 100644 test.math diff --git a/src/ast.rs b/src/ast.rs new file mode 100644 index 0000000..f5f3665 --- /dev/null +++ b/src/ast.rs @@ -0,0 +1,10 @@ +#[derive(Debug)] +pub enum Expresion { + Integer(isize), + Negate(Box), + Add(Box, Box), + Subtract(Box, Box), + Multiply(Box, Box), + Divide(Box, Box), + Modulo(Box, Box), +} diff --git a/src/executor.rs b/src/executor.rs new file mode 100644 index 0000000..7cc4f2d --- /dev/null +++ b/src/executor.rs @@ -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, String), + Ast(Vec>), +} + +pub fn execute(code: &str) -> Result { + 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(), + } + } +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..56cb9a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,18 @@ +mod ast; +mod executor; +mod parser; +mod tokens; +use std::{env, fs}; 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), + }, + } } diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..7bfe8ae --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,55 @@ +use crate::ast::Expresion; +use crate::tokens::Token; +use chumsky::prelude::*; + +pub fn parser() -> impl Parser> { + 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()) +} diff --git a/src/tokens.rs b/src/tokens.rs new file mode 100644 index 0000000..59cca76 --- /dev/null +++ b/src/tokens.rs @@ -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::().unwrap())] + Integer(isize), +} diff --git a/test.math b/test.math new file mode 100644 index 0000000..f1008ee --- /dev/null +++ b/test.math @@ -0,0 +1,2 @@ +-128 + 5 * ( 37 + 8 - 19) * 39 / 27 * (-17) + 55 % 45 + -59 +