Skip to content

Commit

Permalink
0.5.1
Browse files Browse the repository at this point in the history
  • Loading branch information
rscarson committed Mar 26, 2022
1 parent 8cbc90e commit 6e9a3ee
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 113 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.0"
version = "0.5.1"
edition = "2021"

[dependencies]
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,11 +168,12 @@ The following functions are supported by default:
```javascript
// String functions
concat("s1", "s2", ...) | strlen("string") | substr("string", start, [length])
uppercase("s1") | lowercase("S1") | trim(" s1 ")

// Rounding functions
ceil(n) | floor(n) | round(n, precision)

// Trigonometric functions
// Trigonometric functions - values are in radians, use to_radians to convert
tan(r), cos(r), sin(r), atan(r), acos(r), asin(r), tanh(r), cosh(r), sinh(r)

// Rounding functions
Expand All @@ -186,7 +187,7 @@ choose("argument 1", 2, 3.0, ...) | rand() | rand(min, max)
get(url, ["header-name=value", ...]) | post(url, ["header-name=value", ...]) | resolve(hostname)

// Misc. functions
to_radians(degree_value) | abs(n) | tail(filename, [lines]) | time()
to_radians(degree_value) | to_degrees(radian_value) | abs(n) | tail(filename, [lines]) | time()
```

Lavendeux can be extended with javascript. Extensions are run in a sandboxed environment, with no network or host access.
Expand Down
3 changes: 2 additions & 1 deletion examples/adding_functionality.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use lavendeux_parser::{ParserState, FunctionNArgError, ParserError, Token, Value};
use lavendeux_parser::{ParserState, ParserError, Token, Value};
use lavendeux_parser::errors::FunctionNArgError;

fn echo_function(args: &[Value]) -> Result<Value, ParserError> {
if args.len() != 1 {
Expand Down
104 changes: 7 additions & 97 deletions src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ use dev::*;
mod network;
use network::*;

mod str;
use self::str::*;

#[derive(Clone)]
pub struct FunctionTable(HashMap<String, FunctionHandler>);
impl FunctionTable {
Expand All @@ -27,6 +30,7 @@ impl FunctionTable {

// Conversion functions
table.register("to_radians", builtin_to_radians);
table.register("to_degrees", builtin_to_degrees);
table.register("abs", builtin_abs);

// Trig functions
Expand All @@ -49,6 +53,9 @@ impl FunctionTable {

// 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);
Expand Down Expand Up @@ -242,61 +249,6 @@ fn builtin_root(args: &[Value]) -> Result<Value, ParserError> {
}
}

fn builtin_contains(args: &[Value]) -> Result<Value, ParserError> {
if args.len() != 2 {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("contains(source, s)", 1, 1)));
}

Ok(Value::Boolean(args[0].as_string().contains(&args[1].as_string())))
}

fn builtin_strlen(args: &[Value]) -> Result<Value, ParserError> {
if args.len() != 1 {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("strlen(s)", 1, 1)));
}

match &args[0] {
Value::String(s) => Ok(Value::Integer(s.len() as IntegerType)),
_ => Err(ParserError::FunctionArgType(FunctionArgTypeError::new("strlen(s)", 1, ExpectedTypes::String)))
}
}

fn builtin_concat(args: &[Value]) -> Result<Value, ParserError> {
if args.is_empty() {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("strlen(s)", 1, 1)));
}

Ok(Value::String(args.iter().map(|v|v.as_string()).collect::<String>()))
}

fn builtin_substr(args: &[Value]) -> Result<Value, ParserError> {
if args.len() != 2 && args.len() != 3 {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("substr(s, start, [length])", 2, 3)));
}

let start = match args[1].as_int() {
Some(n) => n,
None => return Err(ParserError::FunctionArgType(FunctionArgTypeError::new("substr(s, start, [length])", 2, ExpectedTypes::IntOrFloat)))
};

match &args[0] {
Value::String(s) => {
let length = if args.len() == 3 { match args[2].as_int() {
Some(n) => n,
None => return Err(ParserError::FunctionArgType(FunctionArgTypeError::new("substr(s, start, [length])", 3, ExpectedTypes::IntOrFloat)))
} } else { s.len() as IntegerType - start };
if start >= s.len() as IntegerType || start < 0 {
return Err(ParserError::FunctionArgOverFlow(FunctionArgOverFlowError::new("substr(s, start, [length])", 2)));
} else if length < 0 || length > (s.len() - start as usize) as IntegerType {
return Err(ParserError::FunctionArgOverFlow(FunctionArgOverFlowError::new("substr(s, start, [length])", 3)));
}

Ok(Value::String(s.chars().skip(start as usize).take(length as usize).collect()))
},
_ => Err(ParserError::FunctionArgType(FunctionArgTypeError::new("substr(s, start, [length])", 1, ExpectedTypes::String)))
}
}

#[cfg(test)]
mod test_builtin_table {
use super::*;
Expand Down Expand Up @@ -373,46 +325,4 @@ mod test_builtin_functions {
fn test_root() {
assert_eq!(Value::Float(3.0), builtin_root(&[Value::Float(27.0), Value::Integer(3)]).unwrap());
}

#[test]
fn test_strlen() {
assert_eq!(Value::Integer(0), builtin_strlen(&[Value::String("".to_string())]).unwrap());
assert_eq!(Value::Integer(3), builtin_strlen(&[Value::String(" ".to_string())]).unwrap());
}

#[test]
fn test_concat() {
assert_eq!(Value::String(" ".to_string()), builtin_concat(
&[Value::String("".to_string()),
Value::String(" ".to_string())
]).unwrap());
assert_eq!(Value::String("test4false".to_string()), builtin_concat(
&[Value::String("test".to_string()),
Value::Integer(4),
Value::Boolean(false)
]).unwrap());
}

#[test]
fn test_substr() {
assert_eq!(Value::String("t".to_string()), builtin_substr(
&[Value::String("test".to_string()), Value::Integer(3)]

).unwrap());
assert_eq!(Value::String("tes".to_string()), builtin_substr(
&[Value::String("test".to_string()), Value::Integer(0), Value::Integer(3)],
).unwrap());
}

#[test]
fn test_contains() {
assert_eq!(Value::Boolean(true), builtin_contains(
&[Value::String("test".to_string()), Value::String("e".to_string())]

).unwrap());
assert_eq!(Value::Boolean(false), builtin_contains(
&[Value::String("test".to_string()), Value::String("fff".to_string())]

).unwrap());
}
}
34 changes: 26 additions & 8 deletions src/functions/trig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,22 @@ pub fn builtin_to_radians(args: &[Value]) -> Result<Value, ParserError> {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("to_radians(n)", 1, 1)));
}

match args[0] {
Value::Integer(n) => Ok(Value::Float((n as FloatType) * (std::f64::consts::PI / 180.0))),
Value::Float(n) => Ok(Value::Float(n * (std::f64::consts::PI / 180.0))),
_ => Err(ParserError::FunctionArgType(FunctionArgTypeError::new("to_radians(n)", 1, ExpectedTypes::IntOrFloat)))
if let Some(n) = args[0].as_float() {
Ok(Value::Float(n * (std::f64::consts::PI / 180.0)))
} else {
Err(ParserError::FunctionArgType(FunctionArgTypeError::new("to_radians(n)", 1, ExpectedTypes::IntOrFloat)))
}
}

pub fn builtin_to_degrees(args: &[Value]) -> Result<Value, ParserError> {
if args.len() != 1 {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("to_degrees(n)", 1, 1)));
}

if let Some(n) = args[0].as_float() {
Ok(Value::Float(n * 180.0 / std::f64::consts::PI))
} else {
Err(ParserError::FunctionArgType(FunctionArgTypeError::new("to_degrees(n)", 1, ExpectedTypes::IntOrFloat)))
}
}

Expand All @@ -18,10 +30,10 @@ fn builtin_trig(sig: &str, method: fn(FloatType) -> FloatType, args: &[Value]) -
return Err(ParserError::FunctionNArg(FunctionNArgError::new(sig, 1, 1)));
}

match args[0] {
Value::Integer(n) => Ok(Value::Float(method(n as FloatType))),
Value::Float(n) => Ok(Value::Float(method(n))),
_ => Err(ParserError::FunctionArgType(FunctionArgTypeError::new(sig, 1, ExpectedTypes::IntOrFloat)))
if let Some(n) = args[0].as_float() {
Ok(Value::Float(method(n)))
} else {
Err(ParserError::FunctionArgType(FunctionArgTypeError::new(sig, 1, ExpectedTypes::IntOrFloat)))
}
}

Expand Down Expand Up @@ -70,6 +82,12 @@ mod test_builtin_functions {
assert_eq!(Value::Float(std::f64::consts::PI), builtin_to_radians(&[Value::Integer(180)]).unwrap());
assert_eq!(Value::Float(std::f64::consts::PI * 4.0), builtin_to_radians(&[Value::Integer(720)]).unwrap());
}

#[test]
fn test_to_degrees() {
assert_eq!(Value::Float(180.0), builtin_to_degrees(&[Value::Float(std::f64::consts::PI)]).unwrap());
assert_eq!(Value::Float(90.0), builtin_to_degrees(&[Value::Float(std::f64::consts::PI / 2.0)]).unwrap());
}

#[test]
fn test_tan() {
Expand Down
8 changes: 5 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
//! }
//! ```
//! 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.0")]
#![doc(html_root_url = "https://docs.rs/lavendeux-parser/0.5.1")]
#![warn(missing_docs)]
#![warn(rustdoc::missing_doc_code_examples)]

Expand All @@ -106,9 +106,11 @@ mod extensions;
mod token;
mod value;
mod state;
mod errors;

pub use errors::*;
/// Module defining errors that can occur during parsing
pub mod errors;
pub use errors::ParserError;

pub use token::Token;
pub use state::ParserState;
pub use value::Value;
Expand Down

0 comments on commit 6e9a3ee

Please sign in to comment.