diff --git a/Cargo.lock b/Cargo.lock index ba6f210..811ff9d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -113,6 +113,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" name = "lek" version = "0.1.0" dependencies = [ + "phf", "rustyline", ] @@ -150,6 +151,131 @@ dependencies = [ "void", ] +[[package]] +name = "phf" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" +dependencies = [ + "siphasher", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea" + +[[package]] +name = "proc-macro-hack" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4" + +[[package]] +name = "proc-macro2" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", + "rand_pcg", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_pcg" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" +dependencies = [ + "rand_core", +] + [[package]] name = "redox_syscall" version = "0.1.56" @@ -204,6 +330,23 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "siphasher" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7" + +[[package]] +name = "syn" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e33a62f20d3dc02a1bc9c1d385f92b459bbf35e4dc325eed20c53db5b90c03" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "unicode-segmentation" version = "1.6.0" @@ -216,6 +359,12 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479" +[[package]] +name = "unicode-xid" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" + [[package]] name = "utf8parse" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 3e127fe..4f9ea5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,4 @@ panic = "abort" [dependencies] rustyline = "6.1.2" +phf = { version = "0.8", features = ["macros"] } diff --git a/src/interpreter.rs b/src/interpreter.rs index 2704715..90e99a1 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1,22 +1,21 @@ -use std::{collections::HashMap, mem}; +use std::mem; use crate::lexer::TokenKind; use crate::parser::{Expr, Stmt, Unit}; -use crate::prelude::{self, Prelude}; -use crate::visitor::Visitor; +use crate::prelude::{self}; +use crate::{symbol_table::SymbolTable, visitor::Visitor}; pub struct Interpreter<'a> { - symbol_table: &'a mut HashMap, + symbol_table: &'a mut SymbolTable, angle_unit: Unit, - prelude: Prelude, } impl<'a> Interpreter<'a> { - pub fn new(angle_unit: Unit, symbol_table: &'a mut HashMap) -> Self { + pub fn new(angle_unit: Unit, symbol_table: &'a mut SymbolTable) -> Self { //let mut hashmap: HashMap = HashMap::new(); for constant in prelude::CONSTANTS { symbol_table.insert( - constant.0.to_string(), + constant.0, Stmt::VarDecl( constant.0.to_string(), Box::new(Expr::Literal(constant.1.to_string())), @@ -27,7 +26,6 @@ impl<'a> Interpreter<'a> { Interpreter { angle_unit: angle_unit.clone(), symbol_table, - prelude: Prelude::new(angle_unit), } } @@ -67,23 +65,10 @@ impl<'a> Visitor for Interpreter<'a> { 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, arguments, _) => { - // Initialise each of the arguments as their own variable. - for argument in arguments { - self.visit_stmt(&Stmt::VarDecl( - argument.clone(), - Box::new(Expr::Literal(String::from("0"))), - )); - } - - // Add the function to the symbol table. - self.symbol_table - .insert(format!("{}()", identifier.clone()), stmt.clone()); + self.symbol_table.insert(&identifier, stmt.clone()); 0f64 } + Stmt::FnDecl(_, _, _) => 0f64, // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table. Stmt::Expr(expr) => self.visit_expr(&expr), } } @@ -138,12 +123,12 @@ impl<'a> Visitor for Interpreter<'a> { let prelude_func = match expressions.len() { 1 => { let x = self.visit_expr(&expressions[0]); - self.prelude.call_unary_func(identifier, x) + prelude::call_unary_func(identifier, x, &self.angle_unit) } 2 => { let x = self.visit_expr(&expressions[0]); let y = self.visit_expr(&expressions[1]); - self.prelude.call_binary_func(identifier, x, y) + prelude::call_binary_func(identifier, x, y, &self.angle_unit) } _ => None, }; diff --git a/src/lexer.rs b/src/lexer.rs index 29e04a6..8e00b93 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -184,5 +184,5 @@ impl<'a> Lexer<'a> { } fn is_valid_identifier(c: char) -> bool { - c.is_alphabetic() || c == '°' || c == '√' || c == '\'' + c.is_alphabetic() || c == '°' || c == '√' || c == '\'' || c == '¨' } diff --git a/src/main.rs b/src/main.rs index 961ca28..a701bb2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ mod interpreter; mod lexer; mod parser; mod prelude; +mod symbol_table; mod visitor; use parser::{Parser, Unit}; diff --git a/src/parser.rs b/src/parser.rs index d841fb1..673da06 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,9 +1,10 @@ -use std::{collections::HashMap, mem}; +use std::mem; use crate::{ interpreter::Interpreter, lexer::{Lexer, Token, TokenKind}, prelude, + symbol_table::SymbolTable, }; #[derive(Debug, Clone)] @@ -34,7 +35,7 @@ pub struct Parser { pub angle_unit: Unit, tokens: Vec, pos: usize, - symbol_table: HashMap, + symbol_table: SymbolTable, } impl TokenKind { @@ -55,7 +56,7 @@ impl Parser { Parser { tokens: Vec::new(), pos: 0, - symbol_table: HashMap::new(), + symbol_table: SymbolTable::new(), angle_unit: prelude::DEFAULT_ANGLE_UNIT, } } @@ -106,7 +107,15 @@ impl Parser { } } - return Stmt::FnDecl(identifier, parameter_identifiers, Box::new(expr)); + let fn_decl = + Stmt::FnDecl(identifier.clone(), parameter_identifiers, Box::new(expr)); + + // Insert the function declaration into the symbol table during parsing + // so that the parser can find out if particular functions exist. + self.symbol_table + .insert(&format!("{}()", identifier), fn_decl.clone()); + + return fn_decl; } panic!("Unexpected error."); @@ -221,6 +230,16 @@ impl Parser { fn parse_identifier(&mut self) -> Expr { let identifier = self.advance().clone(); + // Eg. sqrt64 + if self.match_token(TokenKind::Literal) { + // If there is a function with this name, parse it as a function, with the next token as the argument. + if self.symbol_table.contains_func(&identifier.value) { + let parameter = Expr::Literal(self.advance().value.clone()); + return Expr::FnCall(identifier.value, vec![parameter]); + } + } + + // Eg. sqrt(64) if self.match_token(TokenKind::OpenParenthesis) { self.advance(); @@ -234,10 +253,11 @@ impl Parser { self.consume(TokenKind::ClosedParenthesis); - Expr::FnCall(identifier.value, parameters) - } else { - Expr::Var(identifier.value) + return Expr::FnCall(identifier.value, parameters); } + + // Eg. x + Expr::Var(identifier.value) } fn peek(&self) -> &Token { diff --git a/src/prelude.rs b/src/prelude.rs index 7234d75..810f582 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,5 +1,5 @@ use crate::parser::Unit; -use std::collections::HashMap; +use FuncType::*; pub const DEFAULT_ANGLE_UNIT: Unit = Unit::Radians; pub const CONSTANTS: &[(&str, &str)] = &[ @@ -12,36 +12,77 @@ pub const CONSTANTS: &[(&str, &str)] = &[ ("ϕ", "1.61803398"), ]; +use funcs::*; +pub const UNARY_FUNCS: phf::Map<&'static str, UnaryFuncInfo> = phf::phf_map! { + "cos" => UnaryFuncInfo(cos, Trig), + "cosec" => UnaryFuncInfo(cosec, Trig), + "cosech" => UnaryFuncInfo(cosech, Trig), + "cosh" => UnaryFuncInfo(cosh, Trig), + "cot" => UnaryFuncInfo(cot, Trig), + "coth" => UnaryFuncInfo(coth, Trig), + "sec" => UnaryFuncInfo(sec, Trig), + "sech" => UnaryFuncInfo(sech, Trig), + "sin" => UnaryFuncInfo(sin, Trig), + "sinh" => UnaryFuncInfo(sinh, Trig), + "tan" => UnaryFuncInfo(tan, Trig), + "tanh" => UnaryFuncInfo(tanh, Trig), + + "acos" => UnaryFuncInfo(acos, InverseTrig), + "acosec" => UnaryFuncInfo(acosec, InverseTrig), + "acosech" => UnaryFuncInfo(acosech, InverseTrig), + "acosh" => UnaryFuncInfo(acosh, InverseTrig), + "acot" => UnaryFuncInfo(acot, InverseTrig), + "acoth" => UnaryFuncInfo(acoth, InverseTrig), + "asec" => UnaryFuncInfo(asec, InverseTrig), + "asech" => UnaryFuncInfo(asech, InverseTrig), + "asin" => UnaryFuncInfo(asin, InverseTrig), + "asinh" => UnaryFuncInfo(asinh, InverseTrig), + "atan" => UnaryFuncInfo(atan, InverseTrig), + "atanh" => UnaryFuncInfo(atanh, InverseTrig), + + "abs" => UnaryFuncInfo(abs, Other), + "cbrt" => UnaryFuncInfo(cbrt, Other), + "ceil" => UnaryFuncInfo(ceil, Other), + "exp" => UnaryFuncInfo(exp, Other), + "floor" => UnaryFuncInfo(floor, Other), + "frac" => UnaryFuncInfo(frac, Other), + "log" => UnaryFuncInfo(log, Other), + "ln" => UnaryFuncInfo(ln, Other), + "round" => UnaryFuncInfo(round, Other), + "sqrt" => UnaryFuncInfo(sqrt, Other), + "trunc" => UnaryFuncInfo(trunc, Other), +}; +pub const BINARY_FUNCS: phf::Map<&'static str, BinaryFuncInfo> = phf::phf_map! { + "max" => BinaryFuncInfo(max, Other), + "min" => BinaryFuncInfo(min, Other), +}; + enum FuncType { Trig, InverseTrig, Other, } -struct UnaryFuncInfo { - func: Box f64>, - func_type: FuncType, -} +// Unary functions +pub struct UnaryFuncInfo(fn(f64) -> f64, FuncType); + +pub struct BinaryFuncInfo(fn(f64, f64) -> f64, FuncType); impl UnaryFuncInfo { fn call(&self, x: f64, angle_unit: &Unit) -> f64 { - let func = *self.func; - match self.func_type { + let func = self.0; + match self.1 { FuncType::Trig => func(from_angle_unit(x, angle_unit)), FuncType::InverseTrig => to_angle_unit(func(x), angle_unit), FuncType::Other => func(x), } } } -struct BinaryFuncInfo { - func: Box f64>, - func_type: FuncType, -} impl BinaryFuncInfo { fn call(&self, x: f64, y: f64, angle_unit: &Unit) -> f64 { - let func = *self.func; - match self.func_type { + let func = self.0; + match self.1 { FuncType::Trig => func( from_angle_unit(x, angle_unit), from_angle_unit(y, angle_unit), @@ -52,6 +93,22 @@ impl BinaryFuncInfo { } } +pub fn call_unary_func(name: &str, x: f64, angle_unit: &Unit) -> Option { + if let Some(func_info) = UNARY_FUNCS.get(name) { + Some(func_info.call(x, &angle_unit)) + } else { + None + } +} + +pub fn call_binary_func(name: &str, x: f64, y: f64, angle_unit: &Unit) -> Option { + if let Some(func_info) = BINARY_FUNCS.get(name) { + Some(func_info.call(x, y, angle_unit)) + } else { + None + } +} + fn to_angle_unit(x: f64, angle_unit: &Unit) -> f64 { match angle_unit { Unit::Radians => x, @@ -66,130 +123,6 @@ fn from_angle_unit(x: f64, angle_unit: &Unit) -> f64 { } } -pub struct Prelude { - angle_unit: Unit, - unary: HashMap, - binary: HashMap, -} - -impl Prelude { - pub fn new(angle_unit: Unit) -> Self { - Prelude { - angle_unit, - unary: HashMap::new(), - binary: HashMap::new(), - } - } - - pub fn call_unary_func(&mut self, name: &str, x: f64) -> Option { - if let Some(func_info) = self.unary.get(name) { - Some(func_info.call(x, &self.angle_unit)) - } else { - let trig_func: Option 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 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 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 - } - } - } - - pub fn call_binary_func(&mut self, name: &str, x: f64, y: f64) -> Option { - let misc_func: Option f64> = match name { - "max" => Some(funcs::max), - "min" => Some(funcs::min), - _ => None, - }; - - if let Some(func) = misc_func { - let func_info = BinaryFuncInfo { - func: Box::new(func), - func_type: FuncType::Other, - }; - let value = func_info.call(x, y, &self.angle_unit); - self.binary.insert(name.to_string(), func_info); - - return Some(value); - } else { - None - } - } -} - mod funcs { pub fn abs(x: f64) -> f64 { x.abs() @@ -211,7 +144,11 @@ mod funcs { } pub fn acosec(x: f64) -> f64 { - (1f64 / x).sinh() + (1f64 / x).asin() + } + + pub fn acosech(x: f64) -> f64 { + (1f64 / x).asinh() } pub fn asec(x: f64) -> f64 { diff --git a/src/symbol_table.rs b/src/symbol_table.rs new file mode 100644 index 0000000..aff7846 --- /dev/null +++ b/src/symbol_table.rs @@ -0,0 +1,28 @@ +use crate::{parser::Stmt, prelude}; +use std::collections::HashMap; + +pub struct SymbolTable { + hashmap: HashMap, +} + +impl SymbolTable { + pub fn new() -> Self { + SymbolTable { + hashmap: HashMap::new(), + } + } + + pub fn insert(&mut self, key: &str, value: Stmt) { + self.hashmap.insert(key.into(), value); + } + + pub fn get(&self, key: &str) -> Option<&Stmt> { + self.hashmap.get(key) + } + + pub fn contains_func(&self, key: &str) -> bool { + prelude::UNARY_FUNCS.contains_key(key) + || prelude::UNARY_FUNCS.contains_key(key) + || self.hashmap.contains_key(&format!("{}()", key)) + } +}