mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-06-23 19:21:26 +02:00
Initial commit
This commit is contained in:
commit
70d2913cb9
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
7
.vscode/launch.json
vendored
Normal file
7
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": []
|
||||||
|
}
|
5
Cargo.lock
generated
Normal file
5
Cargo.lock
generated
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "lek"
|
||||||
|
version = "0.1.0"
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "lek"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["paddi"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
#lto = true
|
||||||
|
#codegen-units = 1
|
||||||
|
#incremental = false
|
||||||
|
|
||||||
|
[dependencies]
|
158
src/interpreter.rs
Normal file
158
src/interpreter.rs
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
use std::{collections::HashMap, mem};
|
||||||
|
|
||||||
|
use crate::lexer::TokenKind;
|
||||||
|
use crate::parser::{Expr, Stmt, Unit};
|
||||||
|
use crate::prelude::{self, Prelude};
|
||||||
|
use crate::visitor::Visitor;
|
||||||
|
|
||||||
|
pub struct Interpreter {
|
||||||
|
pub symbol_table: HashMap<String, Stmt>,
|
||||||
|
angle_unit: Unit,
|
||||||
|
prelude: Prelude,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Interpreter {
|
||||||
|
pub fn new(angle_unit: Unit) -> Self {
|
||||||
|
let mut hashmap: HashMap<String, Stmt> = HashMap::new();
|
||||||
|
for constant in prelude::CONSTANTS {
|
||||||
|
hashmap.insert(
|
||||||
|
constant.0.to_string(),
|
||||||
|
Stmt::VarDecl(
|
||||||
|
constant.0.to_string(),
|
||||||
|
Box::new(Expr::Literal(constant.1.to_string())),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Interpreter {
|
||||||
|
angle_unit,
|
||||||
|
symbol_table: hashmap,
|
||||||
|
prelude: Prelude::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Option<f64> {
|
||||||
|
for (i, stmt) in statements.iter().enumerate() {
|
||||||
|
let value = self.visit_stmt(stmt);
|
||||||
|
|
||||||
|
if i == statements.len() - 1 {
|
||||||
|
if let Stmt::Expr(_) = stmt {
|
||||||
|
return Some(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_angle_unit(&mut self, angle_unit: Unit) {
|
||||||
|
self.prelude.angle_unit = angle_unit.clone();
|
||||||
|
self.angle_unit = angle_unit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenKind {
|
||||||
|
fn to_unit(&self) -> Unit {
|
||||||
|
match self {
|
||||||
|
TokenKind::Deg => Unit::Degrees,
|
||||||
|
TokenKind::Rad => Unit::Radians,
|
||||||
|
_ => panic!("Invalid unit."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Unit {
|
||||||
|
// TODO: Something more generic
|
||||||
|
fn compare(&self, second_token: &Unit) -> bool {
|
||||||
|
mem::discriminant(self) == mem::discriminant(second_token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Visitor<f64, f64> for Interpreter {
|
||||||
|
fn visit_stmt(&mut self, stmt: &Stmt) -> f64 {
|
||||||
|
match stmt {
|
||||||
|
Stmt::VarDecl(identifier, _) => {
|
||||||
|
self.symbol_table.insert(identifier.clone(), stmt.clone());
|
||||||
|
0f64
|
||||||
|
}
|
||||||
|
Stmt::FnDecl(identifier, argument, _) => {
|
||||||
|
self.visit_stmt(&Stmt::VarDecl(
|
||||||
|
argument.clone(),
|
||||||
|
Box::new(Expr::Literal(String::from("0"))),
|
||||||
|
));
|
||||||
|
self.symbol_table
|
||||||
|
.insert(format!("{}()", identifier.clone()), stmt.clone());
|
||||||
|
0f64
|
||||||
|
}
|
||||||
|
Stmt::Expr(expr) => self.visit_expr(&expr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, expr: &Expr) -> f64 {
|
||||||
|
match expr {
|
||||||
|
Expr::Binary(left, op, right) => {
|
||||||
|
let left = self.visit_expr(&left);
|
||||||
|
let right = self.visit_expr(&right);
|
||||||
|
|
||||||
|
match op {
|
||||||
|
TokenKind::Plus => left + right,
|
||||||
|
TokenKind::Minus => left - right,
|
||||||
|
TokenKind::Star => left * right,
|
||||||
|
TokenKind::Slash => left / right,
|
||||||
|
TokenKind::Power => left.powf(right),
|
||||||
|
_ => 0f64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Unary(_, expr) => self.visit_expr(&expr).clone(),
|
||||||
|
Expr::Unit(expr, kind) => {
|
||||||
|
let x = self.visit_expr(&expr);
|
||||||
|
|
||||||
|
// Don't do any angle conversions if the defauly angle unit is the same as the unit kind
|
||||||
|
if (kind.compare(&TokenKind::Deg) || kind.compare(&TokenKind::Rad))
|
||||||
|
&& self.angle_unit.compare(&kind.to_unit())
|
||||||
|
{
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
TokenKind::Deg => x.to_radians(),
|
||||||
|
TokenKind::Rad => x.to_degrees(),
|
||||||
|
_ => panic!("Invalid unit."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Var(identifier) => {
|
||||||
|
let value = self
|
||||||
|
.symbol_table
|
||||||
|
.get(identifier)
|
||||||
|
.expect("Undefined variable.")
|
||||||
|
.clone();
|
||||||
|
if let Stmt::VarDecl(_, expr) = value {
|
||||||
|
return self.visit_expr(&expr);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Unknown error.");
|
||||||
|
}
|
||||||
|
Expr::Literal(value) => value.parse().unwrap(),
|
||||||
|
Expr::Group(expr) => self.visit_expr(&expr),
|
||||||
|
Expr::FnCall(identifier, expr) => {
|
||||||
|
let x = self.visit_expr(&expr);
|
||||||
|
if let Some(result) = self.prelude.call_unary_func(identifier, x) {
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
let stmt = self
|
||||||
|
.symbol_table
|
||||||
|
.get(&format!("{}()", identifier))
|
||||||
|
.expect("Undefined function")
|
||||||
|
.clone();
|
||||||
|
match stmt {
|
||||||
|
Stmt::FnDecl(_, argument, fn_body) => {
|
||||||
|
self.visit_stmt(&Stmt::VarDecl(argument.clone(), expr.clone()));
|
||||||
|
self.visit_expr(&*fn_body)
|
||||||
|
}
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
179
src/lexer.rs
Normal file
179
src/lexer.rs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
use std::str;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum TokenKind {
|
||||||
|
Unknown,
|
||||||
|
Literal,
|
||||||
|
Identifier,
|
||||||
|
Plus,
|
||||||
|
Minus,
|
||||||
|
Star,
|
||||||
|
Slash,
|
||||||
|
Power,
|
||||||
|
Equals,
|
||||||
|
Deg,
|
||||||
|
Rad,
|
||||||
|
Pipe,
|
||||||
|
OpenParenthesis,
|
||||||
|
ClosedParenthesis,
|
||||||
|
EOF,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Token {
|
||||||
|
pub kind: TokenKind,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Lexer<'a> {
|
||||||
|
source: &'a [u8],
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Lexer<'a> {
|
||||||
|
pub fn lex(source: &str) -> Vec<Token> {
|
||||||
|
let mut lexer = Lexer {
|
||||||
|
source: source.as_bytes(),
|
||||||
|
index: 0,
|
||||||
|
};
|
||||||
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
|
while !lexer.is_at_end() {
|
||||||
|
tokens.push(lexer.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there isn't already an EOF token, add it.
|
||||||
|
if let TokenKind::EOF = tokens.last().unwrap().kind {
|
||||||
|
} else {
|
||||||
|
tokens.push(lexer.build(TokenKind::EOF, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) -> Token {
|
||||||
|
let mut c = self.peek();
|
||||||
|
|
||||||
|
while c == ' ' || c == '\t' || c == '\r' || c == '\n' {
|
||||||
|
self.advance();
|
||||||
|
|
||||||
|
if self.is_at_end() {
|
||||||
|
return self.build(TokenKind::EOF, "");
|
||||||
|
} else {
|
||||||
|
c = self.peek();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let token = match c {
|
||||||
|
'+' => {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::Plus, "")
|
||||||
|
}
|
||||||
|
'-' => {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::Minus, "")
|
||||||
|
}
|
||||||
|
'*' => {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::Star, "")
|
||||||
|
}
|
||||||
|
'/' => {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::Slash, "")
|
||||||
|
}
|
||||||
|
'^' => {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::Power, "")
|
||||||
|
}
|
||||||
|
'(' => {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::OpenParenthesis, "")
|
||||||
|
}
|
||||||
|
')' => {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::ClosedParenthesis, "")
|
||||||
|
}
|
||||||
|
'|' => {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::Pipe, "")
|
||||||
|
}
|
||||||
|
'=' => {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::Equals, "")
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if c.is_digit(10) {
|
||||||
|
self.next_number_literal()
|
||||||
|
} else if c.is_alphabetic() {
|
||||||
|
self.next_identifier()
|
||||||
|
} else {
|
||||||
|
self.advance();
|
||||||
|
self.build(TokenKind::Unknown, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
token
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_number_literal(&mut self) -> Token {
|
||||||
|
let start = self.index;
|
||||||
|
let mut end = start;
|
||||||
|
|
||||||
|
while !self.is_at_end() && (self.peek().is_digit(10) || self.peek() == '.') {
|
||||||
|
end += 1;
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(value) = str::from_utf8(&self.source[start..end]) {
|
||||||
|
self.build(TokenKind::Literal, value)
|
||||||
|
} else {
|
||||||
|
self.build(TokenKind::Unknown, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_identifier(&mut self) -> Token {
|
||||||
|
let start = self.index;
|
||||||
|
let mut end = start;
|
||||||
|
|
||||||
|
while !self.is_at_end() && is_valid_identifier(self.peek()) {
|
||||||
|
end += 1;
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(value) = str::from_utf8(&self.source[start..end]) {
|
||||||
|
let kind = match value {
|
||||||
|
"deg" | "°" => TokenKind::Deg,
|
||||||
|
"rad" => TokenKind::Rad,
|
||||||
|
_ => TokenKind::Identifier,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.build(kind, value)
|
||||||
|
} else {
|
||||||
|
self.build(TokenKind::Unknown, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build(&self, kind: TokenKind, value: &str) -> Token {
|
||||||
|
Token {
|
||||||
|
kind,
|
||||||
|
value: value.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&self) -> char {
|
||||||
|
self.source[self.index].into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance(&mut self) {
|
||||||
|
self.index = self.index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_at_end(&self) -> bool {
|
||||||
|
self.index >= self.source.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid_identifier(c: char) -> bool {
|
||||||
|
c.is_alphabetic() || c == '°' || c == '√' || c == '\''
|
||||||
|
}
|
51
src/main.rs
Normal file
51
src/main.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use std::env;
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
mod interpreter;
|
||||||
|
mod lexer;
|
||||||
|
mod math_parser;
|
||||||
|
mod parser;
|
||||||
|
mod prelude;
|
||||||
|
mod visitor;
|
||||||
|
use math_parser::MathParser;
|
||||||
|
use parser::Unit;
|
||||||
|
|
||||||
|
#[allow(unused_assignments)] // The compiler gives a warning that is not valid.
|
||||||
|
fn main() {
|
||||||
|
let angle_unit = if let Ok(angle_unit_var) = env::var("ANGLE_UNIT") {
|
||||||
|
match angle_unit_var.as_ref() {
|
||||||
|
"radians" => Unit::Radians,
|
||||||
|
"degrees" => Unit::Degrees,
|
||||||
|
_ => {
|
||||||
|
println!("Unexpected angle unit: {}.", angle_unit_var);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Unit::Radians
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut math_parser = MathParser::new();
|
||||||
|
math_parser.set_angle_unit(angle_unit);
|
||||||
|
|
||||||
|
if let Some(expr) = env::args().skip(1).next() {
|
||||||
|
if let Some(result) = math_parser.parse(&expr) {
|
||||||
|
println!("{}", result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut history: Vec<String> = Vec::new();
|
||||||
|
let mut input = String::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
eprint!(">>> ");
|
||||||
|
|
||||||
|
input = String::new();
|
||||||
|
io::stdin().read_line(&mut input).unwrap();
|
||||||
|
history.push(input.clone());
|
||||||
|
|
||||||
|
if let Some(result) = math_parser.parse(&input) {
|
||||||
|
println!("{}", result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
src/math_parser.rs
Normal file
30
src/math_parser.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use crate::interpreter::Interpreter;
|
||||||
|
use crate::lexer::Lexer;
|
||||||
|
use crate::parser::{Parser, Unit};
|
||||||
|
|
||||||
|
pub const DEFAULT_ANGLE_UNIT: Unit = Unit::Radians;
|
||||||
|
|
||||||
|
pub struct MathParser {
|
||||||
|
parser: Parser,
|
||||||
|
interpreter: Interpreter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MathParser {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
MathParser {
|
||||||
|
parser: Parser::new(),
|
||||||
|
interpreter: Interpreter::new(DEFAULT_ANGLE_UNIT),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self, source: &str) -> Option<f64> {
|
||||||
|
let tokens = Lexer::lex(source);
|
||||||
|
let statements = self.parser.parse(tokens);
|
||||||
|
|
||||||
|
self.interpreter.interpret(statements)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_angle_unit(&mut self, unit: Unit) {
|
||||||
|
self.interpreter.set_angle_unit(unit);
|
||||||
|
}
|
||||||
|
}
|
255
src/parser.rs
Normal file
255
src/parser.rs
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use crate::lexer::{Token, TokenKind};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Stmt {
|
||||||
|
VarDecl(String, Box<Expr>),
|
||||||
|
FnDecl(String, String, Box<Expr>),
|
||||||
|
Expr(Box<Expr>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Expr {
|
||||||
|
Binary(Box<Expr>, TokenKind, Box<Expr>),
|
||||||
|
Unary(TokenKind, Box<Expr>),
|
||||||
|
Unit(Box<Expr>, TokenKind),
|
||||||
|
Var(String),
|
||||||
|
Group(Box<Expr>),
|
||||||
|
FnCall(String, Box<Expr>),
|
||||||
|
Literal(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Unit {
|
||||||
|
Radians,
|
||||||
|
Degrees,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Parser {
|
||||||
|
tokens: Vec<Token>,
|
||||||
|
pos: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TokenKind {
|
||||||
|
pub fn is_unit(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
TokenKind::Deg | TokenKind::Rad => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compare(&self, second_token: &TokenKind) -> bool {
|
||||||
|
mem::discriminant(self) == mem::discriminant(second_token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parser {
|
||||||
|
pub fn new() -> Parser {
|
||||||
|
Parser {
|
||||||
|
tokens: Vec::new(),
|
||||||
|
pos: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&mut self, tokens: Vec<Token>) -> Vec<Stmt> {
|
||||||
|
self.tokens = tokens;
|
||||||
|
self.pos = 0;
|
||||||
|
|
||||||
|
let mut statements: Vec<Stmt> = Vec::new();
|
||||||
|
while !self.is_at_end() {
|
||||||
|
statements.push(self.parse_stmt());
|
||||||
|
}
|
||||||
|
|
||||||
|
statements
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_stmt(&mut self) -> Stmt {
|
||||||
|
if self.match_token(TokenKind::Identifier) {
|
||||||
|
return match self.peek_next().kind {
|
||||||
|
TokenKind::Equals => self.parse_var_decl_stmt(),
|
||||||
|
TokenKind::Identifier => self.parse_identifier_stmt(),
|
||||||
|
_ => Stmt::Expr(Box::new(self.parse_expr())),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Stmt::Expr(Box::new(self.parse_expr()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_identifier_stmt(&mut self) -> Stmt {
|
||||||
|
let began_at = self.pos;
|
||||||
|
let primary = self.parse_primary(); // Since function declarations and function calls look the same at first, simply parse a "function call", and re-use the data.
|
||||||
|
|
||||||
|
// If `primary` is followed by an equal sign, it is a function declaration.
|
||||||
|
if self.peek().kind.compare(&TokenKind::Equals) {
|
||||||
|
self.advance();
|
||||||
|
let expr = self.parse_expr();
|
||||||
|
|
||||||
|
match primary {
|
||||||
|
Expr::FnCall(identifier, argument) => match *argument {
|
||||||
|
Expr::Var(argument_identifier) => {
|
||||||
|
Stmt::FnDecl(identifier, argument_identifier, Box::new(expr))
|
||||||
|
}
|
||||||
|
_ => panic!("Unexpected error."),
|
||||||
|
},
|
||||||
|
_ => panic!("Unexpected error."),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// It is a function call, not a function declaration.
|
||||||
|
// Redo the parsing for this specific part.
|
||||||
|
self.pos = began_at;
|
||||||
|
Stmt::Expr(Box::new(self.parse_expr()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_var_decl_stmt(&mut self) -> Stmt {
|
||||||
|
let identifier = self.advance().clone();
|
||||||
|
self.advance(); // Equal sign
|
||||||
|
let expr = self.parse_expr();
|
||||||
|
|
||||||
|
Stmt::VarDecl(identifier.value, Box::new(expr))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr(&mut self) -> Expr {
|
||||||
|
self.parse_sum()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_sum(&mut self) -> Expr {
|
||||||
|
let mut left = self.parse_factor();
|
||||||
|
|
||||||
|
while self.match_token(TokenKind::Plus) || self.match_token(TokenKind::Minus) {
|
||||||
|
let op = self.peek().kind.clone();
|
||||||
|
self.advance();
|
||||||
|
let right = self.parse_factor();
|
||||||
|
|
||||||
|
left = Expr::Binary(Box::new(left), op, Box::new(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
left
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_factor(&mut self) -> Expr {
|
||||||
|
let mut left = self.parse_unary();
|
||||||
|
|
||||||
|
while self.match_token(TokenKind::Star)
|
||||||
|
|| self.match_token(TokenKind::Slash)
|
||||||
|
|| self.match_token(TokenKind::Identifier)
|
||||||
|
{
|
||||||
|
let mut op = self.peek().kind.clone();
|
||||||
|
|
||||||
|
// If the next token is an identifier, assume it's multiplication. Eg. 3y
|
||||||
|
if let TokenKind::Identifier = op {
|
||||||
|
op = TokenKind::Star;
|
||||||
|
} else {
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
let right = self.parse_unary();
|
||||||
|
left = Expr::Binary(Box::new(left), op, Box::new(right));
|
||||||
|
}
|
||||||
|
|
||||||
|
left
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_unary(&mut self) -> Expr {
|
||||||
|
if self.match_token(TokenKind::Minus) {
|
||||||
|
let op = self.advance().kind.clone();
|
||||||
|
return Expr::Unary(op, Box::new(self.parse_unary()));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.parse_exponent()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_exponent(&mut self) -> Expr {
|
||||||
|
let left = self.parse_primary();
|
||||||
|
|
||||||
|
if self.match_token(TokenKind::Power) {
|
||||||
|
let op = self.advance().kind.clone();
|
||||||
|
return Expr::Binary(Box::new(left), op, Box::new(self.parse_exponent()));
|
||||||
|
}
|
||||||
|
|
||||||
|
left
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_primary(&mut self) -> Expr {
|
||||||
|
let expr = match self.peek().kind {
|
||||||
|
TokenKind::OpenParenthesis => self.parse_group(),
|
||||||
|
TokenKind::Pipe => self.parse_abs(),
|
||||||
|
TokenKind::Identifier => self.parse_identifier(),
|
||||||
|
_ => Expr::Literal(self.consume(TokenKind::Literal).value.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !self.is_at_end() && self.peek().kind.is_unit() {
|
||||||
|
Expr::Unit(Box::new(expr), self.advance().kind.clone())
|
||||||
|
} else {
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_group(&mut self) -> Expr {
|
||||||
|
self.advance();
|
||||||
|
let group_expr = Expr::Group(Box::new(self.parse_expr()));
|
||||||
|
self.consume(TokenKind::ClosedParenthesis);
|
||||||
|
|
||||||
|
group_expr
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_abs(&mut self) -> Expr {
|
||||||
|
self.advance();
|
||||||
|
let group_expr = Expr::Group(Box::new(self.parse_expr()));
|
||||||
|
self.consume(TokenKind::Pipe);
|
||||||
|
|
||||||
|
Expr::FnCall(String::from("abs"), Box::new(group_expr))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_identifier(&mut self) -> Expr {
|
||||||
|
let identifier = self.advance().clone();
|
||||||
|
|
||||||
|
if self.match_token(TokenKind::OpenParenthesis) {
|
||||||
|
self.advance();
|
||||||
|
let parameter = self.parse_expr();
|
||||||
|
self.consume(TokenKind::ClosedParenthesis);
|
||||||
|
|
||||||
|
Expr::FnCall(identifier.value, Box::new(parameter))
|
||||||
|
} else {
|
||||||
|
Expr::Var(identifier.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek(&self) -> &Token {
|
||||||
|
&self.tokens[self.pos]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peek_next(&self) -> &Token {
|
||||||
|
&self.tokens[self.pos + 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn previous(&self) -> &Token {
|
||||||
|
&self.tokens[self.pos - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_token(&self, kind: TokenKind) -> bool {
|
||||||
|
if self.is_at_end() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.peek().kind.compare(&kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn advance(&mut self) -> &Token {
|
||||||
|
self.pos += 1;
|
||||||
|
self.previous()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consume(&mut self, kind: TokenKind) -> &Token {
|
||||||
|
if self.match_token(kind) {
|
||||||
|
return self.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("Unexpected token.");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_at_end(&self) -> bool {
|
||||||
|
self.pos >= self.tokens.len() || self.peek().kind.compare(&TokenKind::EOF)
|
||||||
|
}
|
||||||
|
}
|
286
src/prelude.rs
Normal file
286
src/prelude.rs
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
use crate::{math_parser, parser::Unit};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub const CONSTANTS: &[(&str, &str)] = &[
|
||||||
|
("pi", "3.14159265"),
|
||||||
|
("π", "3.14159265"),
|
||||||
|
("e", "2.71828182"),
|
||||||
|
("tau", "6.28318530"),
|
||||||
|
("τ", "6.28318530"),
|
||||||
|
("phi", "1.61803398"),
|
||||||
|
("ϕ", "1.61803398"),
|
||||||
|
];
|
||||||
|
|
||||||
|
enum FuncType {
|
||||||
|
Trig,
|
||||||
|
InverseTrig,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct UnaryFuncInfo {
|
||||||
|
func: Box<fn(f64) -> f64>,
|
||||||
|
func_type: FuncType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnaryFuncInfo {
|
||||||
|
fn call(&self, x: f64, angle_unit: &Unit) -> f64 {
|
||||||
|
let func = *self.func;
|
||||||
|
match self.func_type {
|
||||||
|
FuncType::Trig => func(self.from_angle_unit(x, angle_unit)),
|
||||||
|
FuncType::InverseTrig => self.to_angle_unit(func(x), angle_unit),
|
||||||
|
FuncType::Other => func(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_angle_unit(&self, x: f64, angle_unit: &Unit) -> f64 {
|
||||||
|
match angle_unit {
|
||||||
|
Unit::Radians => x,
|
||||||
|
Unit::Degrees => x.to_degrees(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_angle_unit(&self, x: f64, angle_unit: &Unit) -> f64 {
|
||||||
|
match angle_unit {
|
||||||
|
Unit::Radians => x,
|
||||||
|
Unit::Degrees => x.to_radians(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Prelude {
|
||||||
|
pub angle_unit: Unit,
|
||||||
|
unary: HashMap<String, UnaryFuncInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Prelude {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Prelude {
|
||||||
|
unary: HashMap::new(),
|
||||||
|
angle_unit: math_parser::DEFAULT_ANGLE_UNIT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call_unary_func(&mut self, name: &str, x: f64) -> Option<f64> {
|
||||||
|
if let Some(func_info) = self.unary.get(name) {
|
||||||
|
Some(func_info.call(x, &self.angle_unit))
|
||||||
|
} else {
|
||||||
|
let trig_func: Option<fn(f64) -> f64> = match name {
|
||||||
|
"cos" => Some(funcs::cos),
|
||||||
|
"cosec" => Some(funcs::cosec),
|
||||||
|
"cosech" => Some(funcs::cosech),
|
||||||
|
"cosh" => Some(funcs::cosh),
|
||||||
|
"cot" => Some(funcs::cot),
|
||||||
|
"coth" => Some(funcs::coth),
|
||||||
|
"sec" => Some(funcs::sec),
|
||||||
|
"sech" => Some(funcs::sech),
|
||||||
|
"sin" => Some(funcs::sin),
|
||||||
|
"sinh" => Some(funcs::sinh),
|
||||||
|
"tan" => Some(funcs::tan),
|
||||||
|
"tanh" => Some(funcs::tanh),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(func) = trig_func {
|
||||||
|
let func_info = UnaryFuncInfo {
|
||||||
|
func: Box::new(func),
|
||||||
|
func_type: FuncType::Trig,
|
||||||
|
};
|
||||||
|
let value = func_info.call(x, &self.angle_unit);
|
||||||
|
self.unary.insert(name.to_string(), func_info);
|
||||||
|
|
||||||
|
return Some(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let inv_trig_func: Option<fn(f64) -> f64> = match name {
|
||||||
|
"acos" => Some(funcs::acos),
|
||||||
|
"acosh" => Some(funcs::acosh),
|
||||||
|
"acot" => Some(funcs::acot),
|
||||||
|
"acoth" => Some(funcs::acoth),
|
||||||
|
"acosec" => Some(funcs::acosec),
|
||||||
|
"asec" => Some(funcs::asec),
|
||||||
|
"asech" => Some(funcs::asech),
|
||||||
|
"asin" => Some(funcs::asin),
|
||||||
|
"asinh" => Some(funcs::asinh),
|
||||||
|
"atan" => Some(funcs::atan),
|
||||||
|
"atanh" => Some(funcs::atanh),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(func) = inv_trig_func {
|
||||||
|
let func_info = UnaryFuncInfo {
|
||||||
|
func: Box::new(func),
|
||||||
|
func_type: FuncType::InverseTrig,
|
||||||
|
};
|
||||||
|
let value = func_info.call(x, &self.angle_unit);
|
||||||
|
self.unary.insert(name.to_string(), func_info);
|
||||||
|
|
||||||
|
return Some(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
let misc_func: Option<fn(f64) -> f64> = match name {
|
||||||
|
"abs" => Some(funcs::abs),
|
||||||
|
"cbrt" => Some(funcs::cbrt),
|
||||||
|
"ceil" => Some(funcs::ceil),
|
||||||
|
"exp" => Some(funcs::exp),
|
||||||
|
"floor" => Some(funcs::floor),
|
||||||
|
"frac" => Some(funcs::frac),
|
||||||
|
"log" => Some(funcs::log),
|
||||||
|
"ln" => Some(funcs::ln),
|
||||||
|
"round" => Some(funcs::round),
|
||||||
|
"sqrt" => Some(funcs::sqrt),
|
||||||
|
"trunc" => Some(funcs::trunc),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(func) = misc_func {
|
||||||
|
let func_info = UnaryFuncInfo {
|
||||||
|
func: Box::new(func),
|
||||||
|
func_type: FuncType::Other,
|
||||||
|
};
|
||||||
|
let value = func_info.call(x, &self.angle_unit);
|
||||||
|
self.unary.insert(name.to_string(), func_info);
|
||||||
|
|
||||||
|
return Some(value);
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod funcs {
|
||||||
|
pub fn abs(x: f64) -> f64 {
|
||||||
|
x.abs()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acos(x: f64) -> f64 {
|
||||||
|
x.acos()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acosh(x: f64) -> f64 {
|
||||||
|
x.acosh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acot(x: f64) -> f64 {
|
||||||
|
(1f64 / x).atan()
|
||||||
|
}
|
||||||
|
pub fn acoth(x: f64) -> f64 {
|
||||||
|
(1f64 / x).atanh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acosec(x: f64) -> f64 {
|
||||||
|
(1f64 / x).sinh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asec(x: f64) -> f64 {
|
||||||
|
(1f64 / x).acos()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asech(x: f64) -> f64 {
|
||||||
|
(1f64 / x).acosh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asin(x: f64) -> f64 {
|
||||||
|
x.asin()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn asinh(x: f64) -> f64 {
|
||||||
|
x.asinh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn atan(x: f64) -> f64 {
|
||||||
|
x.atan()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn atanh(x: f64) -> f64 {
|
||||||
|
x.atanh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cbrt(x: f64) -> f64 {
|
||||||
|
x.cbrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ceil(x: f64) -> f64 {
|
||||||
|
x.ceil()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cos(x: f64) -> f64 {
|
||||||
|
x.cos()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cosh(x: f64) -> f64 {
|
||||||
|
x.cos()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cosec(x: f64) -> f64 {
|
||||||
|
1f64 / x.sin()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cosech(x: f64) -> f64 {
|
||||||
|
1f64 / x.sinh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cot(x: f64) -> f64 {
|
||||||
|
x.cos() / x.sin()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn coth(x: f64) -> f64 {
|
||||||
|
x.cosh() / x.sinh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exp(x: f64) -> f64 {
|
||||||
|
x.exp()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn floor(x: f64) -> f64 {
|
||||||
|
x.floor()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn frac(x: f64) -> f64 {
|
||||||
|
x.fract()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log(x: f64) -> f64 {
|
||||||
|
x.log(10f64)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ln(x: f64) -> f64 {
|
||||||
|
x.ln()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn round(x: f64) -> f64 {
|
||||||
|
x.round()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sec(x: f64) -> f64 {
|
||||||
|
1f64 / x.cos()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sech(x: f64) -> f64 {
|
||||||
|
1f64 / x.cosh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sin(x: f64) -> f64 {
|
||||||
|
x.sin()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sinh(x: f64) -> f64 {
|
||||||
|
x.sinh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sqrt(x: f64) -> f64 {
|
||||||
|
x.sqrt()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tan(x: f64) -> f64 {
|
||||||
|
x.tan()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tanh(x: f64) -> f64 {
|
||||||
|
x.tanh()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trunc(x: f64) -> f64 {
|
||||||
|
x.trunc()
|
||||||
|
}
|
||||||
|
}
|
6
src/visitor.rs
Normal file
6
src/visitor.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
use crate::parser::{Expr, Stmt};
|
||||||
|
|
||||||
|
pub trait Visitor<T, U> {
|
||||||
|
fn visit_stmt(&mut self, stmt: &Stmt) -> T;
|
||||||
|
fn visit_expr(&mut self, expr: &Expr) -> U;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user