Skip to content

Commit

Permalink
fix how functions are registered
Browse files Browse the repository at this point in the history
  • Loading branch information
rscarson committed Mar 28, 2022
1 parent 29161f7 commit 2ff26da
Show file tree
Hide file tree
Showing 12 changed files with 115 additions and 93 deletions.
10 changes: 5 additions & 5 deletions 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.1"
version = "0.5.2"
edition = "2021"

[dependencies]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
```

Expand Down
2 changes: 1 addition & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
46 changes: 5 additions & 41 deletions src/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,9 @@ use std::collections::HashMap;
pub type FunctionHandler = fn(&[Value]) -> Result<Value, ParserError>;

mod trig;
use trig::*;

mod dev;
use dev::*;

mod network;
use network::*;

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

#[derive(Clone)]
pub struct FunctionTable(HashMap<String, FunctionHandler>);
Expand All @@ -27,49 +20,20 @@ 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);
table.register("log", builtin_log);
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
}
Expand Down
42 changes: 31 additions & 11 deletions src/functions/dev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value, ParserError> {
/// 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<Value, ParserError> {
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<Value, ParserError> {
fn builtin_rand(args: &[Value]) -> Result<Value, ParserError> {
let mut rng = rand::thread_rng();
if args.is_empty() {
// Generate a float between 0 and 1
Expand All @@ -32,14 +41,14 @@ pub fn builtin_rand(args: &[Value]) -> Result<Value, ParserError> {
}
}

pub fn builtin_time(_args: &[Value]) -> Result<Value, ParserError> {
fn builtin_time(_args: &[Value]) -> Result<Value, ParserError> {
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<Value, ParserError> {
fn builtin_tail(args: &[Value]) -> Result<Value, ParserError> {
if args.len() != 1 && args.len() != 2 {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("tail(file, [n_lines])", 1, 2)));
}
Expand Down Expand Up @@ -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]
Expand Down
14 changes: 11 additions & 3 deletions src/functions/network.rs
Original file line number Diff line number Diff line change
@@ -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<Value, ParserError> {
/// 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<Value, ParserError> {
if args.len() != 1 {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("resolve(hostname)", 1, 1)));
}
Expand All @@ -24,7 +32,7 @@ pub fn builtin_resolve(args: &[Value]) -> Result<Value, ParserError> {
}
}

pub fn builtin_get(args: &[Value]) -> Result<Value, ParserError> {
fn builtin_get(args: &[Value]) -> Result<Value, ParserError> {
if args.is_empty() {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("get(url, [\"header-name=value\", ...])", 1, 1)));
}
Expand Down Expand Up @@ -54,7 +62,7 @@ pub fn builtin_get(args: &[Value]) -> Result<Value, ParserError> {
}
}

pub fn builtin_post(args: &[Value]) -> Result<Value, ParserError> {
fn builtin_post(args: &[Value]) -> Result<Value, ParserError> {
if args.len() != 2 {
return Err(ParserError::FunctionNArg(FunctionNArgError::new("post(url, body)", 1, 1)));
}
Expand Down
26 changes: 19 additions & 7 deletions src/functions/str.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
use super::FunctionTable;
use crate::value::{Value, IntegerType};
use crate::errors::*;

pub fn builtin_contains(args: &[Value]) -> Result<Value, ParserError> {
/// 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<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())))
}

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

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

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

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

Ok(Value::String(args[0].as_string().to_uppercase()))
}

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

Ok(Value::String(args[0].as_string().to_lowercase()))
}

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

Ok(Value::String(args[0].as_string().trim().to_string()))
}

pub fn builtin_substr(args: &[Value]) -> Result<Value, ParserError> {
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)));
}
Expand Down
Loading

0 comments on commit 2ff26da

Please sign in to comment.