Finx is a fast, lightweight scripting language designed for easy embedding in Rust applications. It features a stack-based virtual machine, lexical scoping with closures, and a simple but powerful syntax.
- Fast execution: Stack-based virtual machine with optimized bytecode
- Easy integration: Simple API for embedding in Rust applications
- Native functions: Register Rust functions to be called from scripts
- Memory safe: Built in Rust with safe memory management
- Rich syntax: Support for functions, closures, loops, conditionals
- Context management: Maintain state between script executions
Add Finx to your Cargo.toml:
[dependencies]
finx = "0.1.0"use finx::Finx;fnmain(){// Create a new language engineletmut engine = Finx::new();// Run a simple expressionlet result = engine.eval("2 + 3 * 4").unwrap();println!("Result: {}", result);// Result: 14// Define variables and functions
engine.execute(r#" let name = "World"; fn greet(person) { return "Hello, " + person + "!"; } "#).unwrap();// Use defined variables and functionslet greeting = engine.eval("greet(name)").unwrap();println!("{}", greeting);// Hello, World!}Register Rust functions to be called from scripts:
use finx::{Finx,Value};letmut engine = Finx::new();// Register a simple math function
engine.register_function("multiply", |args| {iflet[Value::Number(a),Value::Number(b)] = args {Value::Number(a * b)}else{panic!("multiply expects two numbers");}},2);// Use the native function in a scriptlet result = engine.eval("multiply(6, 7)").unwrap();assert_eq!(result.as_num(),Some(42.0));For easier native function registration:
use finx::{Finx, register_function};letmut engine = Finx::new();register_function!(engine,"add",2, |a:f64, b:f64| -> f64{
a + b
});register_function!(engine,"format_name",2, |first:&str, last:&str| -> String{
format!("{}, {}", last, first)});let result = engine.eval(r#"format_name("John", "Doe")"#).unwrap();assert_eq!(result.as_str(),Some("Doe, John"));Finx supports advanced native functions using closures, enabling state capture and more dynamic behavior:
use finx::{Finx,Value};use std::rc::Rc;letmut engine = Finx::new();// Simple closure with captured statelet prefix = "LOG: ".to_string();
engine.register_function("log",Rc::new(move |args| {iflet[Value::Str(msg)] = args {println!("{}{}", prefix, msg);}Value::Null}));
engine.execute(r#"log("Hello from script!");"#).unwrap();// Output: LOG: Hello from script!For shared state between multiple closures, use Rc<RefCell<T>>:
use finx::{Finx,Value};use std::rc::Rc;use std::cell::RefCell;letmut engine = Finx::new();// Shared counter statelet counter = Rc::new(RefCell::new(0_i32));// Increment functionlet counter_clone = counter.clone();
engine.register_function("increment",Rc::new(move |_args| {letmut count = counter_clone.borrow_mut();*count += 1;Value::Number(*count asf64)}));// Get current countlet counter_clone = counter.clone();
engine.register_function("get_count",Rc::new(move |_args| {let count = counter_clone.borrow();Value::Number(*count asf64)}));// Reset counterlet counter_clone = counter.clone();
engine.register_function("reset",Rc::new(move |_args| {letmut count = counter_clone.borrow_mut();*count = 0;Value::Null}));
engine.execute(r#" print(increment()); // 1 print(increment()); // 2 print(get_count()); // 2 reset(); print(get_count()); // 0"#).unwrap();Use Closures When:
- You need to capture configuration or state
- Functions need to share mutable state
- You want to create factory functions for different behaviors
- You need access to external resources (files, network, etc.)
Use Function Pointers When:
- Simple, stateless operations
- Maximum performance is critical
- Functions are pure/mathematical
- Backward compatibility with existing code
Convenience Method:
use finx::Finx;use std::rc::Rc;letmut engine = Finx::new();// For closures
engine.register_closure("add", |args| {// Implementation
finx::Value::Null});Finx supports a familiar, C-like syntax:
letx=42;letname="Alice";letis_valid=true;letempty=null;x=x+1;// Reassignmentfnadd(a,b){returna+b;}fnfactorial(n){ifn<=1{return1;}returnn*factorial(n-1);}fnmake_counter(){letcount=0;fnincrement(){count=count+1;returncount;}returnincrement;}letcounter=make_counter();print(counter());// 1print(counter());// 2// Conditionalsifx>0{print("Positive");}elseifx<0{print("Negative");}else{print("Zero");}// Loopsleti=0;whilei<5{print(i);i=i+1;}foriin0..10{print(i);}When using Finx::new(), you get access to common functions:
print(abs(-42));// 42print(sqrt(16));// 4print(max(10,20));// 20print(min(10,20));// 10print(pow(2,3));// 8print(len("hello"));// 5print(is_num(42));// trueprint(is_str("test"));// trueFinx provides comprehensive error handling:
use finx::{Finx,FinxError};letmut engine = Finx::new();match engine.eval("unknown_variable"){Ok(result) => println!("Result: {}", result),Err(FinxError::RuntimeError(msg)) => println!("Runtime error: {}", msg),Err(FinxError::ParseError(err)) => println!("Parse error: {}", err),Err(err) => println!("Other error: {}", err),}letmut engine = Finx::new();// Execute a script file
engine.execute_file("example_scripts/example.fx")?;// Evaluate an expression from a filelet result = engine.eval_file("example_scripts/example.fx")?;letmut engine = Finx::new();
engine.execute(r#" print("Hello"); print("World");"#)?;// Get all print outputlet output = engine.get_output();assert_eq!(output,&["Hello","World"]);// Clear output for next execution
engine.clear_output();letmut engine = Finx::new();// Set recursion limits
engine.set_max_recursion_depth(500);Finx supports the following data types:
- Numbers: 64-bit floating point (
42,3.14,-1.5) - Strings: UTF-8 strings (
"hello","world") - Booleans:
trueandfalse - Null:
nullvalue - Functions: First-class functions and closures
use finx::{Finx,Value};letmut engine = Finx::new();let result = engine.eval("42").unwrap();match result {Value::Number(n) => println!("Got number: {}", n),Value::Str(s) => println!("Got string: {}", s),Value::Bool(b) => println!("Got boolean: {}", b),Value::Null => println!("Got null"),
_ => println!("Got other value"),}// Or use convenience methodsifletSome(num) = result.as_num(){println!("Number value: {}", num);}