Crates.io | skillet |
lib.rs | skillet |
version | 0.3.0 |
created_at | 2025-08-24 01:55:40.04677+00 |
updated_at | 2025-09-04 01:44:16.782137+00 |
description | Skillet: micro expression language (arithmetic, logical, functions, arrays, conditionals, excel formulas) made in Rust bin cli and server |
homepage | |
repository | |
max_upload_size | |
id | 1808012 |
size | 478,731 |
Skillet is a tiny, embeddable expression engine (written in Rust) inspired by Excel formulas and Ruby-style chaining. It parses expressions into an AST and evaluates them with a small runtime.
This MVP supports numbers, strings, booleans, nulls, arrays, method chaining, functions (built-ins), comparisons, logical ops, ternary, array indexing/slicing, spread ...
, lambdas with named parameters, and basic type casting via ::Type
.
Skilled can be extended with JS, take a look at Documentation
cargo build
cargo test
A minimal CLI is included to evaluate expressions without external variables.
cargo run --bin sk -- "= [30,60,80,100].filter(:x > 50).map(:x * 0.9).sum()"
Notes:
=
is optional (supported for spreadsheet-style familiarity).Add to your Cargo project (from crates.io):
[dependencies]
skillet = "0.2.0"
Or with cargo-edit:
cargo add skillet@0.2.0
Evaluate expressions:
use skillet::{evaluate, evaluate_with, Value};
use std::collections::HashMap;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Numeric
let v = evaluate("= 2 + 3 * 4")?; // -> Value::Number(14.0)
// Variables
let mut vars = HashMap::new();
vars.insert("sales".to_string(), Value::Number(5000.0));
let v = evaluate_with("=SUM(:sales, 1000)", &vars)?; // -> 6000.0
// Strings and chaining
let v = evaluate("= ' john '.trim().upper()")?; // -> "JOHN"
// Arrays + F/M/R
let v = evaluate("= [30,60,80,100].filter(:x > 50).map(:x * 0.9).sum()")?; // -> 216.0
// Named lambda parameters
let v = evaluate("= FILTER([1,2,3,4], :n % 2 == 0, 'n')")?; // -> [2,4]
// Type casting
let v = evaluate("= '42'::Integer")?; // -> 42
Ok(())
}
TRUE
/FALSE
), strings ('...' or "..."), NULL
+ - * / % ^
, > < >= <= == !=
, AND/OR/NOT
(also &&/||/!
), ternary ? :
:name
(provided via evaluate_with
map)SUM
, AVG/AVERAGE
, MIN
, MAX
, ROUND
, CEIL
, FLOOR
, ABS
, SQRT
, POW
ARRAY
, FIRST
, LAST
, CONTAINS
, UNIQUE
, SORT
, REVERSE
, JOIN
, FLATTEN
CONCAT
, UPPER
, LOWER
, TRIM
, LENGTH
, SPLIT
, REPLACE
ISBLANK
FILTER(array, expr, [param])
, MAP(array, expr, [param])
, REDUCE(array, expr, initial, [valParam], [accParam])
SUMIF(array, expr)
, AVGIF(array, expr)
, COUNTIF(array, expr)
.
and predicates ?
.abs() .round(n) .floor() .ceil()
; predicates .positive? .negative? .zero? .even? .odd? .numeric?
.length() .size() .first() .last() .sum() .avg() .min() .max() .sort() .unique() .reverse() .compact() .flatten()
.upper() .lower() .trim() .reverse()
[1, 2, 3]
; indexing arr[i]
(negatives allowed); slicing arr[a:b]
...expr
inside arg listsexpr::Integer|Float|String|Boolean|Array|Currency|DateTime|Json
= 2 + 3 * 4
→ 14
= :score >= 90 ? 'A' : 'B'
= [1,2,3,4].map(:v * 10, 'v')
→ [10,20,30,40]
= [1,2,3].reduce(:a + :v, 0, 'v', 'a')
→ 6
= SUMIF([1,-2,3,-4], :x > 0)
→ 4
= FLATTEN([1,[2,[3]],4])
→ [1,2,3,4]
Value
in src/types.rs
.If you want the binaries such as sk
, sk_server
and sk_client
installed system-wide:
cargo install skillet
Skillet includes a high-performance evaluation server that keeps the interpreter warm and eliminates per-process overhead.
sk_server 8080
(binds to 127.0.0.1:8080)sk_server 8080 -d
(writes PID to skillet-server.pid
in CWD)kill $(cat skillet-server.pid)
sk_server 8080 --host 0.0.0.0
(listen on all interfaces)sk_server 8080 --host 0.0.0.0 --token <secret>
(or set SKILLET_AUTH_TOKEN
)Client and benchmarks:
sk_client localhost:8080 '=2+3*4'
sk_client localhost:8080 '=SUM(:a,:b)' a=10 b=5
sk_client localhost:8080 '=:user.name' --json '{"user":{"name":"Alice"}}'
sk_client localhost:8080 --benchmark '=2+3*4' 10000
sk_client localhost:8080 '=2+3*4' --token <secret>
(or set SKILLET_SERVER_TOKEN
)Scripts:
bash scripts/benchmark_server.sh [port] [iterations] [threads]
take a look at the Server Usage Guide for more details about how to use it and consume in different langauages
SUM
, AVG
/AVERAGE
, MIN
, MAX
, ROUND
, CEIL
, CEILING
, FLOOR
, ABS
, SQRT
, POW
/POWER
, MOD
, INT
AND
, OR
, NOT
, XOR
, IF
, IFS
LENGTH
, CONCAT
, UPPER
, LOWER
, TRIM
, SUBSTRING
, SPLIT
, REPLACE
, REVERSE
, ISBLANK
, ISNUMBER
, ISTEXT
ARRAY
, FLATTEN
, FIRST
, LAST
, CONTAINS
, IN
, COUNT
, UNIQUE
, SORT
, REVERSE
, JOIN
NOW
, DATE
, TIME
, YEAR
, MONTH
, DAY
, DATEADD
, DATEDIFF
PMT
, DB
, FV
, IPMT
MEDIAN
, MODE.SNGL
(MODESNGL
, MODE_SNGL
), STDEV.P
(STDEVP
, STDEV_P
), VAR.P
(VARP
, VAR_P
), PERCENTILE.INC
(PERCENTILEINC
, PERCENTILE_INC
), QUARTILE.INC
(QUARTILEINC
, QUARTILE_INC
)FILTER(array, expr, [param])
, MAP(array, expr, [param])
, REDUCE(array, expr, initial, [valParam], [accParam])
, SUMIF(array, expr)
, AVGIF(array, expr)
, COUNTIF(array, expr)
parse(input: &str) -> Result<Expr, Error>
: parse into ASTevaluate(input: &str) -> Result<Value, Error>
: evaluate without variablesevaluate_with(input: &str, vars: &HashMap<String, Value>) -> Result<Value, Error>
evaluate_with_json(input: &str, json_vars: &str) -> Result<Value, Error>
evaluate_with_custom(input: &str, vars: &HashMap<String, Value>) -> Result<Value, Error>
evaluate_with_json_custom(input: &str, json_vars: &str) -> Result<Value, Error>
register_function(Box<dyn CustomFunction>) -> Result<(), Error>
unregister_function(name: &str) -> bool
list_custom_functions() -> Vec<String>
Value
enum: Number(f64) | Array(Vec<Value>) | Boolean(bool) | String(String) | Null | Currency(f64) | DateTime(i64) | Json(String)
Error
with message
and optional position
Run the test suite:
cargo test
MIT OR Apache-2.0