From 2ff26da4343e06b8875b857d762ecc5914127674 Mon Sep 17 00:00:00 2001 From: Richard Carson Date: Mon, 28 Mar 2022 12:13:15 -0400 Subject: [PATCH 1/8] fix how functions are registered --- Cargo.lock | 10 ++++----- Cargo.toml | 2 +- README.md | 2 +- src/errors.rs | 2 +- src/functions.rs | 46 +++++----------------------------------- src/functions/dev.rs | 42 ++++++++++++++++++++++++++---------- src/functions/network.rs | 14 +++++++++--- src/functions/str.rs | 26 +++++++++++++++++------ src/functions/trig.rs | 39 ++++++++++++++++++++++++---------- src/handlers.rs | 1 + src/lib.rs | 16 ++++++-------- src/state.rs | 8 +++++-- 12 files changed, 115 insertions(+), 93 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66cceb4..8566d0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -331,9 +331,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" +checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" dependencies = [ "cfg-if", "libc", @@ -509,7 +509,7 @@ dependencies = [ [[package]] name = "lavendeux-parser" -version = "0.5.1" +version = "0.5.2" dependencies = [ "chrono", "derive_more", @@ -531,9 +531,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.119" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" [[package]] name = "log" diff --git a/Cargo.toml b/Cargo.toml index fa1aadd..fe87099 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ categories = ["parser-implementations", "development-tools", "command-line-utili homepage = "https://rscarson.github.io/Lavendeux/" repository = "https://github.com/rscarson/lavendeux-parser" readme = "readme.md" -version = "0.5.1" +version = "0.5.2" edition = "2021" [dependencies] diff --git a/README.md b/README.md index a87d839..067e9c8 100644 --- a/README.md +++ b/README.md @@ -150,7 +150,7 @@ f(x) = 2*x**2 + 3*x + 5 f(2.3) // Recursive functions work too! -factorial(x) = x==1 ? x : (x * factorial(x - 1) ) +factorial(x) = x==0 ? 1 : (x * factorial(x - 1) ) factorial(5) ``` diff --git a/src/errors.rs b/src/errors.rs index a2473af..c0ab720 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -9,7 +9,7 @@ mod values; pub use values::*; /// Represents all possible errors during expression handling -#[derive(From, Debug, Clone)] +#[derive(Debug, Clone)] pub enum ParserError { /// An error with an unknown cause General(String), diff --git a/src/functions.rs b/src/functions.rs index 8bc5d25..8fa878c 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -5,16 +5,9 @@ use std::collections::HashMap; pub type FunctionHandler = fn(&[Value]) -> Result; mod trig; -use trig::*; - mod dev; -use dev::*; - mod network; -use network::*; - mod str; -use self::str::*; #[derive(Clone)] pub struct FunctionTable(HashMap); @@ -27,23 +20,8 @@ impl FunctionTable { table.register("ceil", builtin_ceil); table.register("floor", builtin_floor); table.register("round", builtin_round); - - // Conversion functions - table.register("to_radians", builtin_to_radians); - table.register("to_degrees", builtin_to_degrees); table.register("abs", builtin_abs); - // Trig functions - table.register("tan", builtin_tan); - table.register("cos", builtin_cos); - table.register("sin", builtin_sin); - table.register("atan", builtin_atan); - table.register("acos", builtin_acos); - table.register("asin", builtin_asin); - table.register("tanh", builtin_tanh); - table.register("cosh", builtin_cosh); - table.register("sinh", builtin_sinh); - // Roots and logs table.register("ln", builtin_ln); table.register("log10", builtin_log10); @@ -51,25 +29,11 @@ impl FunctionTable { table.register("sqrt", builtin_sqrt); table.register("root", builtin_root); - // String functions - table.register("concat", builtin_concat); - table.register("uppercase", builtin_uppercase); - table.register("lowercase", builtin_lowercase); - table.register("trim", builtin_trim); - table.register("strlen", builtin_strlen); - table.register("substr", builtin_substr); - table.register("contains", builtin_contains); - - // Developper functions - table.register("choose", builtin_choose); - table.register("rand", builtin_rand); - table.register("time", builtin_time); - table.register("tail", builtin_tail); - - // Networking functions - table.register("get", builtin_get); - table.register("post", builtin_post); - table.register("resolve", builtin_resolve); + // Other builtins + str::register_functions(&mut table); + dev::register_functions(&mut table); + network::register_functions(&mut table); + trig::register_functions(&mut table); table } diff --git a/src/functions/dev.rs b/src/functions/dev.rs index c9d0755..91742dd 100644 --- a/src/functions/dev.rs +++ b/src/functions/dev.rs @@ -2,21 +2,30 @@ use std::time::{SystemTime, UNIX_EPOCH}; use std::fs::File; use std::io::{BufRead, BufReader}; use rand::prelude::*; +use super::FunctionTable; use crate::value::{Value, IntegerType}; use crate::errors::*; -pub fn builtin_choose(args: &[Value]) -> Result { +/// Register developper functions +pub fn register_functions(table: &mut FunctionTable) { + table.register("choose", builtin_choose); + table.register("rand", builtin_rand); + table.register("time", builtin_time); + table.register("tail", builtin_tail); +} + +fn builtin_choose(args: &[Value]) -> Result { let mut rng = rand::thread_rng(); if args.is_empty() { Err(ParserError::FunctionNArg(FunctionNArgError::new("choose(..)", 1, 100))) } else { - let arg = rng.gen_range(0..(args.len() - 1)); + let arg = rng.gen_range(0..args.len()); Ok(args[arg].clone()) } } -pub fn builtin_rand(args: &[Value]) -> Result { +fn builtin_rand(args: &[Value]) -> Result { let mut rng = rand::thread_rng(); if args.is_empty() { // Generate a float between 0 and 1 @@ -32,14 +41,14 @@ pub fn builtin_rand(args: &[Value]) -> Result { } } -pub fn builtin_time(_args: &[Value]) -> Result { +fn builtin_time(_args: &[Value]) -> Result { match SystemTime::now().duration_since(UNIX_EPOCH) { Ok(n) => Ok(Value::Integer(n.as_secs() as IntegerType)), Err(_) => Ok(Value::Integer(0)) } } -pub fn builtin_tail(args: &[Value]) -> Result { +fn builtin_tail(args: &[Value]) -> Result { if args.len() != 1 && args.len() != 2 { return Err(ParserError::FunctionNArg(FunctionNArgError::new("tail(file, [n_lines])", 1, 2))); } @@ -71,17 +80,28 @@ mod test_builtin_table { #[test] fn test_choose() { - let result = builtin_choose(&[Value::String("test".to_string()), Value::Integer(5)]).unwrap(); - assert_eq!(true, result == Value::String("test".to_string()) || result == Value::Integer(5)); + let mut result; + for _ in 0..30 { + result = builtin_choose(&[Value::String("test".to_string()), Value::Integer(5)]).unwrap(); + assert_eq!(true, result.is_string() || result == Value::Integer(5).is_int()); + } } #[test] fn test_rand() { - let mut result = builtin_rand(&[]).unwrap(); - assert_eq!(true, result.as_float().unwrap() >= 0.0 && result.as_float().unwrap() <= 1.0); + let mut result; - result = builtin_rand(&[Value::Integer(5), Value::Integer(10)]).unwrap(); - assert_eq!(true, result.as_int().unwrap() >= 5 && result.as_int().unwrap() <= 10); + for _ in 0..30 { + result = builtin_rand(&[]).unwrap(); + println!("{}", result); + assert_eq!(true, result.as_float().unwrap() >= 0.0 && result.as_float().unwrap() <= 1.0); + } + + for _ in 0..30 { + result = builtin_rand(&[Value::Integer(5), Value::Integer(10)]).unwrap(); + println!("{}", result); + assert_eq!(true, result.as_int().unwrap() >= 5 && result.as_int().unwrap() <= 10); + } } #[test] diff --git a/src/functions/network.rs b/src/functions/network.rs index cf00c3a..9ecace2 100644 --- a/src/functions/network.rs +++ b/src/functions/network.rs @@ -1,9 +1,17 @@ use std::net::ToSocketAddrs; use std::time::Duration; +use super::FunctionTable; use crate::value::{Value}; use crate::errors::*; -pub fn builtin_resolve(args: &[Value]) -> Result { +/// Register network functions +pub fn register_functions(table: &mut FunctionTable) { + table.register("get", builtin_get); + table.register("post", builtin_post); + table.register("resolve", builtin_resolve); +} + +fn builtin_resolve(args: &[Value]) -> Result { if args.len() != 1 { return Err(ParserError::FunctionNArg(FunctionNArgError::new("resolve(hostname)", 1, 1))); } @@ -24,7 +32,7 @@ pub fn builtin_resolve(args: &[Value]) -> Result { } } -pub fn builtin_get(args: &[Value]) -> Result { +fn builtin_get(args: &[Value]) -> Result { if args.is_empty() { return Err(ParserError::FunctionNArg(FunctionNArgError::new("get(url, [\"header-name=value\", ...])", 1, 1))); } @@ -54,7 +62,7 @@ pub fn builtin_get(args: &[Value]) -> Result { } } -pub fn builtin_post(args: &[Value]) -> Result { +fn builtin_post(args: &[Value]) -> Result { if args.len() != 2 { return Err(ParserError::FunctionNArg(FunctionNArgError::new("post(url, body)", 1, 1))); } diff --git a/src/functions/str.rs b/src/functions/str.rs index 54d7c9a..3323182 100644 --- a/src/functions/str.rs +++ b/src/functions/str.rs @@ -1,7 +1,19 @@ +use super::FunctionTable; use crate::value::{Value, IntegerType}; use crate::errors::*; -pub fn builtin_contains(args: &[Value]) -> Result { +/// Register string functions +pub fn register_functions(table: &mut FunctionTable) { + table.register("concat", builtin_concat); + table.register("uppercase", builtin_uppercase); + table.register("lowercase", builtin_lowercase); + table.register("trim", builtin_trim); + table.register("strlen", builtin_strlen); + table.register("substr", builtin_substr); + table.register("contains", builtin_contains); +} + +fn builtin_contains(args: &[Value]) -> Result { if args.len() != 2 { return Err(ParserError::FunctionNArg(FunctionNArgError::new("contains(source, s)", 1, 1))); } @@ -9,7 +21,7 @@ pub fn builtin_contains(args: &[Value]) -> Result { Ok(Value::Boolean(args[0].as_string().contains(&args[1].as_string()))) } -pub fn builtin_strlen(args: &[Value]) -> Result { +fn builtin_strlen(args: &[Value]) -> Result { if args.len() != 1 { return Err(ParserError::FunctionNArg(FunctionNArgError::new("strlen(s)", 1, 1))); } @@ -20,7 +32,7 @@ pub fn builtin_strlen(args: &[Value]) -> Result { } } -pub fn builtin_concat(args: &[Value]) -> Result { +fn builtin_concat(args: &[Value]) -> Result { if args.is_empty() { return Err(ParserError::FunctionNArg(FunctionNArgError::new("concat(s, s2, ...)", 1, 1))); } @@ -28,7 +40,7 @@ pub fn builtin_concat(args: &[Value]) -> Result { Ok(Value::String(args.iter().map(|v|v.as_string()).collect::())) } -pub fn builtin_uppercase(args: &[Value]) -> Result { +fn builtin_uppercase(args: &[Value]) -> Result { if args.is_empty() { return Err(ParserError::FunctionNArg(FunctionNArgError::new("uppercase(s)", 1, 1))); } @@ -36,7 +48,7 @@ pub fn builtin_uppercase(args: &[Value]) -> Result { Ok(Value::String(args[0].as_string().to_uppercase())) } -pub fn builtin_lowercase(args: &[Value]) -> Result { +fn builtin_lowercase(args: &[Value]) -> Result { if args.is_empty() { return Err(ParserError::FunctionNArg(FunctionNArgError::new("lowercase(s)", 1, 1))); } @@ -44,7 +56,7 @@ pub fn builtin_lowercase(args: &[Value]) -> Result { Ok(Value::String(args[0].as_string().to_lowercase())) } -pub fn builtin_trim(args: &[Value]) -> Result { +fn builtin_trim(args: &[Value]) -> Result { if args.is_empty() { return Err(ParserError::FunctionNArg(FunctionNArgError::new("trim(s)", 1, 1))); } @@ -52,7 +64,7 @@ pub fn builtin_trim(args: &[Value]) -> Result { Ok(Value::String(args[0].as_string().trim().to_string())) } -pub fn builtin_substr(args: &[Value]) -> Result { +fn builtin_substr(args: &[Value]) -> Result { if args.len() != 2 && args.len() != 3 { return Err(ParserError::FunctionNArg(FunctionNArgError::new("substr(s, start, [length])", 2, 3))); } diff --git a/src/functions/trig.rs b/src/functions/trig.rs index afbfd15..c4f84ad 100644 --- a/src/functions/trig.rs +++ b/src/functions/trig.rs @@ -1,7 +1,24 @@ +use super::FunctionTable; use crate::value::{Value, FloatType}; use crate::errors::*; -pub fn builtin_to_radians(args: &[Value]) -> Result { +/// Register trig functions +pub fn register_functions(table: &mut FunctionTable) { + table.register("to_radians", builtin_to_radians); + table.register("to_degrees", builtin_to_degrees); + + table.register("tan", builtin_tan); + table.register("cos", builtin_cos); + table.register("sin", builtin_sin); + table.register("atan", builtin_atan); + table.register("acos", builtin_acos); + table.register("asin", builtin_asin); + table.register("tanh", builtin_tanh); + table.register("cosh", builtin_cosh); + table.register("sinh", builtin_sinh); +} + +fn builtin_to_radians(args: &[Value]) -> Result { if args.len() != 1 { return Err(ParserError::FunctionNArg(FunctionNArgError::new("to_radians(n)", 1, 1))); } @@ -13,7 +30,7 @@ pub fn builtin_to_radians(args: &[Value]) -> Result { } } -pub fn builtin_to_degrees(args: &[Value]) -> Result { +fn builtin_to_degrees(args: &[Value]) -> Result { if args.len() != 1 { return Err(ParserError::FunctionNArg(FunctionNArgError::new("to_degrees(n)", 1, 1))); } @@ -37,39 +54,39 @@ fn builtin_trig(sig: &str, method: fn(FloatType) -> FloatType, args: &[Value]) - } } -pub fn builtin_tan(args: &[Value]) -> Result { +fn builtin_tan(args: &[Value]) -> Result { builtin_trig("tan(n)", FloatType::tan, args) } -pub fn builtin_cos(args: &[Value]) -> Result { +fn builtin_cos(args: &[Value]) -> Result { builtin_trig("cos(n)", FloatType::cos, args) } -pub fn builtin_sin(args: &[Value]) -> Result { +fn builtin_sin(args: &[Value]) -> Result { builtin_trig("sin(n)", FloatType::sin, args) } -pub fn builtin_atan(args: &[Value]) -> Result { +fn builtin_atan(args: &[Value]) -> Result { builtin_trig("atan(n)", FloatType::atan, args) } -pub fn builtin_acos(args: &[Value]) -> Result { +fn builtin_acos(args: &[Value]) -> Result { builtin_trig("acos(n)", FloatType::acos, args) } -pub fn builtin_asin(args: &[Value]) -> Result { +fn builtin_asin(args: &[Value]) -> Result { builtin_trig("asin(n)", FloatType::asin, args) } -pub fn builtin_tanh(args: &[Value]) -> Result { +fn builtin_tanh(args: &[Value]) -> Result { builtin_trig("tanh(n)", FloatType::tanh, args) } -pub fn builtin_cosh(args: &[Value]) -> Result { +fn builtin_cosh(args: &[Value]) -> Result { builtin_trig("cosh(n)", FloatType::cosh, args) } -pub fn builtin_sinh(args: &[Value]) -> Result { +fn builtin_sinh(args: &[Value]) -> Result { builtin_trig("sinh(n)", FloatType::sinh, args) } diff --git a/src/handlers.rs b/src/handlers.rs index 6bd12bd..74b8559 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -43,6 +43,7 @@ fn expression_handler(token: &mut Token, state: &mut ParserState) -> Option token.set_text(&s), Err(e) => { + // Extension decorators if state.extensions.has_decorator(name) { match state.extensions.call_decorator(name, &token.value()) { Ok(s) => { diff --git a/src/lib.rs b/src/lib.rs index cc555af..547c98a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,32 +91,28 @@ //! } //! ``` //! Extensions give a more flexible way of adding functionality at runtime. Extensions are written in javascript. -#![doc(html_root_url = "https://docs.rs/lavendeux-parser/0.5.1")] +#![doc(html_root_url = "https://docs.rs/lavendeux-parser/0.5.2")] #![warn(missing_docs)] #![warn(rustdoc::missing_doc_code_examples)] -// Mostly for error type derivisions -#[macro_use] -extern crate derive_more; - -mod handlers; -mod functions; -mod decorators; mod extensions; +mod decorators; +mod functions; +mod handlers; mod token; mod value; mod state; + /// Module defining errors that can occur during parsing pub mod errors; +pub use extensions::Extension; pub use errors::ParserError; - pub use token::Token; pub use state::ParserState; pub use value::Value; pub use value::IntegerType; pub use value::FloatType; -pub use extensions::Extension; #[cfg(test)] mod test_token { diff --git a/src/state.rs b/src/state.rs index 4950a30..ea1ad8a 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,10 @@ use super::value::Value; -use super::{functions, decorators, extensions}; use std::collections::HashMap; +use super::functions; +use super::decorators; +use super::extensions; + const MAX_STACK_DEPTH: usize = 100; /// Holds the properties of a function assigned inside an expression @@ -52,10 +55,11 @@ impl ParserState { variables: HashMap::new(), constants: HashMap::new(), - extensions: extensions::ExtensionTable::new(), functions: functions::FunctionTable::new(), user_functions: HashMap::new(), decorators: decorators::DecoratorTable::new(), + + extensions: extensions::ExtensionTable::new(), }; // Set up constants From b24db763aaf8b404b538b96f4e952aee19e52286 Mon Sep 17 00:00:00 2001 From: Richard Carson Date: Mon, 28 Mar 2022 14:16:42 -0400 Subject: [PATCH 2/8] Make extensions optional --- Cargo.toml | 8 +++++++- src/handlers.rs | 1 + src/handlers/callable.rs | 1 + src/lib.rs | 5 +++-- src/state.rs | 4 ++++ 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fe87099..299a638 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,10 @@ readme = "readme.md" version = "0.5.2" edition = "2021" +[features] +default = ["extensions"] +extensions = ["js-sandbox"] + [dependencies] pest = "2.1.3" pest_derive = "2.1.0" @@ -19,7 +23,9 @@ chrono = "0.4.19" derive_more = "0.99.17" serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.79" -js-sandbox = "0.1.6" + +# Feature deps +js-sandbox = { version = "0.1.6", optional = true } [dev-dependencies] version-sync = "0.9" \ No newline at end of file diff --git a/src/handlers.rs b/src/handlers.rs index 74b8559..2018b90 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -44,6 +44,7 @@ fn expression_handler(token: &mut Token, state: &mut ParserState) -> Option token.set_text(&s), Err(e) => { // Extension decorators + #[cfg(feature = "extensions")] if state.extensions.has_decorator(name) { match state.extensions.call_decorator(name, &token.value()) { Ok(s) => { diff --git a/src/handlers/callable.rs b/src/handlers/callable.rs index ef62b19..b422b57 100644 --- a/src/handlers/callable.rs +++ b/src/handlers/callable.rs @@ -30,6 +30,7 @@ pub fn call_expression_handler(token: &mut Token, state: &mut ParserState) -> Op } } else { // Extension functions + #[cfg(feature = "extensions")] if state.extensions.has_function(name) { match state.extensions.call_function(name, &args[..]) { Ok(v) => { diff --git a/src/lib.rs b/src/lib.rs index 547c98a..3ec7949 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,7 @@ #![warn(missing_docs)] #![warn(rustdoc::missing_doc_code_examples)] -mod extensions; +//mod extensions; mod decorators; mod functions; mod handlers; @@ -103,10 +103,11 @@ mod token; mod value; mod state; +#[cfg(feature = "extensions")] +pub use extensions::Extension; /// Module defining errors that can occur during parsing pub mod errors; -pub use extensions::Extension; pub use errors::ParserError; pub use token::Token; pub use state::ParserState; diff --git a/src/state.rs b/src/state.rs index ea1ad8a..23ce027 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; use super::functions; use super::decorators; + +#[cfg(feature = "extensions")] use super::extensions; const MAX_STACK_DEPTH: usize = 100; @@ -38,6 +40,7 @@ pub struct ParserState { pub decorators: decorators::DecoratorTable, /// Currently loaded extensions + #[cfg(feature = "extensions")] pub extensions: extensions::ExtensionTable, } @@ -59,6 +62,7 @@ impl ParserState { user_functions: HashMap::new(), decorators: decorators::DecoratorTable::new(), + #[cfg(feature = "extensions")] extensions: extensions::ExtensionTable::new(), }; From 4b309f575fddc551a43fd87846e6f2cde47982a3 Mon Sep 17 00:00:00 2001 From: Richard Carson Date: Mon, 28 Mar 2022 14:30:13 -0400 Subject: [PATCH 3/8] refactor functions --- Cargo.toml | 6 +++--- src/functions.rs | 33 ++++++++++++++++++--------------- src/lib.rs | 4 +++- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 299a638..e61953f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,12 +17,12 @@ extensions = ["js-sandbox"] [dependencies] pest = "2.1.3" pest_derive = "2.1.0" -rand = "0.8.4" +serde = { version = "1.0.136", features = ["derive"] } +serde_json = "1.0.79" reqwest = { version = "0.11.10", features = ["blocking"] } chrono = "0.4.19" +rand = "0.8.4" derive_more = "0.99.17" -serde = { version = "1.0.136", features = ["derive"] } -serde_json = "1.0.79" # Feature deps js-sandbox = { version = "0.1.6", optional = true } diff --git a/src/functions.rs b/src/functions.rs index 8fa878c..989cea5 100644 --- a/src/functions.rs +++ b/src/functions.rs @@ -15,27 +15,30 @@ impl FunctionTable { /// Initialize a new function table, complete with default builtin functions pub fn new() -> FunctionTable { let mut table : FunctionTable = FunctionTable(HashMap::new()); + table.register_builtins(); + table + } + /// Register builtin functions + fn register_builtins(&mut self) { // Rounding functions - table.register("ceil", builtin_ceil); - table.register("floor", builtin_floor); - table.register("round", builtin_round); - table.register("abs", builtin_abs); + self.register("ceil", builtin_ceil); + self.register("floor", builtin_floor); + self.register("round", builtin_round); + self.register("abs", builtin_abs); // Roots and logs - table.register("ln", builtin_ln); - table.register("log10", builtin_log10); - table.register("log", builtin_log); - table.register("sqrt", builtin_sqrt); - table.register("root", builtin_root); + self.register("ln", builtin_ln); + self.register("log10", builtin_log10); + self.register("log", builtin_log); + self.register("sqrt", builtin_sqrt); + self.register("root", builtin_root); // Other builtins - str::register_functions(&mut table); - dev::register_functions(&mut table); - network::register_functions(&mut table); - trig::register_functions(&mut table); - - table + str::register_functions(self); + dev::register_functions(self); + network::register_functions(self); + trig::register_functions(self); } /// Register a function in the table diff --git a/src/lib.rs b/src/lib.rs index 3ec7949..53d0b09 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -95,7 +95,6 @@ #![warn(missing_docs)] #![warn(rustdoc::missing_doc_code_examples)] -//mod extensions; mod decorators; mod functions; mod handlers; @@ -103,6 +102,9 @@ mod token; mod value; mod state; +#[cfg(feature = "extensions")] +mod extensions; + #[cfg(feature = "extensions")] pub use extensions::Extension; From 86a2d86a2d47914baf7f84bcfc7c6433a92d5dde Mon Sep 17 00:00:00 2001 From: Richard Carson Date: Mon, 28 Mar 2022 14:35:05 -0400 Subject: [PATCH 4/8] update docs --- README.md | 16 +++++++++------- src/lib.rs | 2 ++ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 067e9c8..0fb7e08 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,15 @@ [![Build Status](https://github.com/rscarson/lavendeux-parser/workflows/Rust/badge.svg)](https://github.com/rscarson/lavendeux-parser/actions?workflow=Rust) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/rscarson/lavendeux-parser/master/LICENSE) -lavendeux-parser is an exensible parsing engine for mathematical expressions. -It supports variable and function assignments, a variety of datatypes, and can -be extended easily at runtime through extensions written in javascript. +lavendeux-parser is an exensible parsing engine for mathematical expressions. +It supports variable and function assignments, a variety of datatypes, and can +be extended easily at runtime through extensions written in javascript. -Extensions are run in a sandboxed environment with no host or network access. -This project is the engine behind [Lavendeux](https://rscarson.github.io/lavendeux/). +Extensions are run in a sandboxed environment with no host or network access. +This project is the engine behind [Lavendeux](https://rscarson.github.io/lavendeux/). -## Getting Started -To use it, create a `ParserState` object, and use it to tokenize input with `Token::new`: +## Getting Started +To use it, create a `ParserState` object, and use it to tokenize input with `Token::new`: ```rust use lavendeux_parser::{ParserState, ParserError, Token, Value}; @@ -92,6 +92,8 @@ fn main() -> Result<(), ParserError> { ``` Extensions give a more flexible way of adding functionality at runtime. Extensions are written in javascript. +Extensions are enabled by default, and can be excluded by disabling the crate's "extensions" feature + ## Syntax Expressions can be composed of integers, floats, strings, as well as numbers of various bases: ```javascript diff --git a/src/lib.rs b/src/lib.rs index 53d0b09..b4d30e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,8 @@ //! ``` //! //! ## Using Extensions +//! Extensions are enabled by default, and can be excluded by disabling the crate's "extensions" feature +//! //! Extensions can be loaded as follows: //! ```rust //! use lavendeux_parser::{ParserState, ParserError, Value, Token}; From 18f80620fc14f4977644f780ae96eab2c65fc3a0 Mon Sep 17 00:00:00 2001 From: Richard Carson Date: Mon, 28 Mar 2022 14:36:16 -0400 Subject: [PATCH 5/8] add more auto tests --- .github/workflows/rust.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3c13d1b..7e8f7d4 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,3 +20,7 @@ jobs: run: cargo build --verbose - name: Run tests run: cargo test --verbose + - name: Run barebones tests + run: cargo test --no-default-features --lib + - name: Run clippy + run: cargo clippy From 03d39df56e91eeb300d9b02454a19a80b9e42b4d Mon Sep 17 00:00:00 2001 From: Richard Carson Date: Mon, 28 Mar 2022 14:38:14 -0400 Subject: [PATCH 6/8] fix readme --- README.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0fb7e08..1e855d2 100644 --- a/README.md +++ b/README.md @@ -90,10 +90,31 @@ fn main() -> Result<(), ParserError> { Ok(()) } ``` -Extensions give a more flexible way of adding functionality at runtime. Extensions are written in javascript. +## Using Extensions Extensions are enabled by default, and can be excluded by disabling the crate's "extensions" feature +Extensions can be loaded as follows: +```rust +use lavendeux_parser::{ParserState, ParserError, Value, Token}; + +fn main() -> Result<(), ParserError> { + let mut state : ParserState = ParserState::new(); + + // Load one extension + state.extensions.load("example_extensions/colour_utils.js")?; + + // Load a whole directory + state.extensions.load_all("./example_extensions")?; + + // Once loaded, functions and @decorators decribed in the extensions + // can be called in expressions being parsed + let token = Token::new("complement(0xFF0000) @colour", &mut state)?; + assert_eq!(token.text(), "#ffff00"); + Ok(()) +} +``` + ## Syntax Expressions can be composed of integers, floats, strings, as well as numbers of various bases: ```javascript From ae1382593253d2e1658228eae53fc8129099d914 Mon Sep 17 00:00:00 2001 From: Richard Carson Date: Mon, 28 Mar 2022 14:41:01 -0400 Subject: [PATCH 7/8] documentation update --- README.md | 2 ++ src/lib.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 1e855d2..597f002 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,8 @@ fn main() -> Result<(), ParserError> { ``` ## Using Extensions +Extensions give a more flexible way of adding functionality at runtime. Extensions are written in javascript. + Extensions are enabled by default, and can be excluded by disabling the crate's "extensions" feature Extensions can be loaded as follows: diff --git a/src/lib.rs b/src/lib.rs index b4d30e9..da627f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,8 @@ //! ``` //! //! ## Using Extensions +//! Extensions give a more flexible way of adding functionality at runtime. Extensions are written in javascript. +//! //! Extensions are enabled by default, and can be excluded by disabling the crate's "extensions" feature //! //! Extensions can be loaded as follows: From 39d611398d5878bce1188f73779b12e0fd9b0435 Mon Sep 17 00:00:00 2001 From: Richard Carson Date: Mon, 28 Mar 2022 15:09:31 -0400 Subject: [PATCH 8/8] update changelog --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index a5b4bac..ac1d100 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +0.5.2 +----- +Make extensions a feature that can be disabled + 0.5.1 ----- Change to how errors are exported