Skip to content

Commit

Permalink
Overhaul functions to add help, and add regex
Browse files Browse the repository at this point in the history
  • Loading branch information
rscarson committed May 1, 2022
1 parent 54120df commit 5b50bec
Show file tree
Hide file tree
Showing 16 changed files with 1,143 additions and 614 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ default = ["extensions"]
extensions = ["js-sandbox"]

[dependencies]
regex = "1.5.5"
pest = "2.1.3"
pest_derive = "2.1.0"
serde = { version = "1.0.136", features = ["derive"] }
Expand Down
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,8 @@ fn main() -> Result<(), ParserError> {

A number of functions and @decorators are available for expressions to use - add more using the state:
```rust
use lavendeux_parser::{ParserState, ParserError, Value};

// Functions take in an array of values, and return a single value
fn new_function_handler(args: &[Value]) -> Result<Value, ParserError> {
Ok(Value::Integer(0))
}
use lavendeux_parser::{ParserState, ParserError, FunctionDefinition, FunctionArgument, Value};
use lavendeux_parser::errors::*;

// Decorators take in a single value, and return a string representation
fn new_decorator_handler(arg: &Value) -> Result<String, ParserError> {
Expand All @@ -68,7 +64,18 @@ fn new_decorator_handler(arg: &Value) -> Result<String, ParserError> {

let mut state : ParserState = ParserState::new();
state.decorators.register("new_decorator", new_decorator_handler);
state.functions.register("new_function", new_function_handler);

// Functions take in an array of values, and return a single value
state.functions.register(FunctionDefinition {
name: "echo",
description: "Echo back the provided input",
arguments: || vec![
FunctionArgument::new_required("input", ExpectedTypes::String),
],
handler: |_, args: &[Value]| {
Ok(Value::String(args[0].as_string()))
}
});

// Expressions being parsed can now call new_function(), and use the @new_decorator
```rust
Expand Down Expand Up @@ -191,6 +198,9 @@ Decorators can be put at the end of a line to change the output format. Valid de

The following functions are supported by default:
```javascript
help() // List all functions and decorators
help("strlen") // Get help for a specific function by name

// String functions
concat("s1", "s2", ...) | strlen("string") | substr("string", start, [length])
uppercase("s1") | lowercase("S1") | trim(" s1 ")
Expand Down
18 changes: 11 additions & 7 deletions examples/adding_functionality.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
use lavendeux_parser::{ParserState, ParserError, Token, Value};
use lavendeux_parser::errors::FunctionNArgError;
use lavendeux_parser::{FunctionDefinition, FunctionArgument};
use lavendeux_parser::errors::*;

fn echo_function(args: &[Value]) -> Result<Value, ParserError> {
if args.len() != 1 {
Err(ParserError::FunctionNArg(FunctionNArgError::new("echo(input)", 1, 1)))
} else {
const ECHO : FunctionDefinition = FunctionDefinition {
name: "echo",
description: "Echo back the provided input",
arguments: || vec![
FunctionArgument::new_required("input", ExpectedTypes::String),
],
handler: |_, args: &[Value]| {
Ok(Value::String(args[0].as_string()))
}
}
};

fn main() -> Result<(), ParserError> {
// Load the extensions into our parser
let mut state : ParserState = ParserState::new();
state.functions.register("echo", echo_function);
state.functions.register(ECHO);

// Now we can use the new functions and @decorators
let token = Token::new("echo(5)", &mut state)?;
Expand Down
5 changes: 5 additions & 0 deletions src/decorators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ impl DecoratorTable {
self.0.contains_key(name)
}

/// Get a collection of all included decorators
pub fn all(&self) -> Vec<String> {
self.0.keys().cloned().collect()
}

/// Call a decorator
///
/// # Arguments
Expand Down
10 changes: 9 additions & 1 deletion src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,13 @@ pub enum ExpectedTypes {
IntOrFloat,

/// String value
String
String,

/// Boolean value
Boolean,

/// Any type of value
Any
}
impl fmt::Display for ExpectedTypes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Expand All @@ -130,6 +136,8 @@ impl fmt::Display for ExpectedTypes {
ExpectedTypes::Float => write!(f, "float"),
ExpectedTypes::IntOrFloat => write!(f, "int or float"),
ExpectedTypes::String => write!(f, "string"),
ExpectedTypes::Boolean => write!(f, "boolean"),
ExpectedTypes::Any => write!(f, "any"),
}
}
}
Expand Down
59 changes: 54 additions & 5 deletions src/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@ impl ExtensionTable {
Self(HashMap::new())
}

/// Add an extension
///
/// # Arguments
/// * `filename` - File name
/// * `extension` - Extension to add
pub fn add(&mut self, filename: &str, extension: Extension) {
self.0.insert(filename.to_string(), extension);
}

/// Load an extension from a filename
///
/// # Arguments
/// * `name` - Function name
/// * `filename` - File name
pub fn load(&mut self, filename: &str) -> Result<Extension, ParserError> {
let e = Extension::new(filename)?;
self.0.insert(filename.to_string(), e.clone());
Expand Down Expand Up @@ -95,6 +104,10 @@ impl Default for ExtensionTable {
}
}

fn default_name() -> String { "Unnamed Extension".to_string() }
fn default_author() -> String { "Anonymous".to_string() }
fn default_version() -> String { "0.0.0".to_string() }

/// Represents a single loaded extension. It describes the functions and decorators it adds,
/// as well as metadata about the extension and it's author.
///
Expand All @@ -105,13 +118,13 @@ pub struct Extension {
#[serde(default)]
filename: String,

#[serde(default)]
#[serde(default = "default_name")]
name: String,

#[serde(default)]
#[serde(default = "default_author")]
author: String,

#[serde(default)]
#[serde(default = "default_version")]
version: String,

#[serde(default)]
Expand All @@ -135,7 +148,7 @@ impl Extension {
/// Load an extension from a filename
///
/// # Arguments
/// * `name` - Function name
/// * `filename` - Source filename
pub fn new(filename: &str) -> Result<Extension, std::io::Error> {
match fs::read_to_string(filename) {
Ok(s) => {
Expand All @@ -148,6 +161,32 @@ impl Extension {
}
}

/// Create a new dummy extension that cannot be called or used
///
/// # Arguments
/// * `name` - Extension name
/// * `author` - Extension author
/// * `author` - Extension author
/// * `version` - Extension version
/// * `functions` - Extension functions
/// * `decorators` - Extension decorators
pub fn new_stub(name: Option<&str>, author: Option<&str>, version: Option<&str>, functions: Vec<String>, decorators: Vec<String>) -> Self {
let mut stub = Self {
name: name.unwrap_or(&default_name()).to_string(),
author: author.unwrap_or(&default_author()).to_string(),
version: version.unwrap_or(&default_version()).to_string(),
contents: "".to_string(),
filename: "".to_string(),
functions: HashMap::new(),
decorators: HashMap::new()
};

for f in functions { stub.functions.insert(f.clone(), f); }
for d in decorators { stub.decorators.insert(d.clone(), d); }

stub
}

/// Attempt to load all extensions in a directory
pub fn load_all(directory: &str) -> Result<Vec<Extension>, std::io::Error> {
let mut extensions : Vec<Extension> = Vec::new();
Expand Down Expand Up @@ -253,6 +292,16 @@ impl Extension {
pub fn version(&self) -> &str {
&self.version
}

/// Return the list of all functions in the extension
pub fn functions(&self) -> Vec<String> {
self.functions.keys().cloned().collect()
}

/// Return the list of all decorators in the extension
pub fn decorators(&self) -> Vec<String> {
self.decorators.keys().cloned().collect()
}
}

/// Load a script from a string
Expand Down
Loading

0 comments on commit 5b50bec

Please sign in to comment.