mirror of
https://github.com/PaddiM8/kalker.git
synced 2025-01-20 20:18:36 +01: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…
Reference in New Issue
Block a user