Skip to content

Repository files navigation

Finx - Embeddable Scripting Language

Finx Logo

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.

Features

  • 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

Quick Start

Add Finx to your Cargo.toml:

[dependencies]
finx = "0.1.0"

Basic Usage

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!}

Native Functions

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));

Using the Convenience Macro

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"));

Native Functions with Closures

Finx supports advanced native functions using closures, enabling state capture and more dynamic behavior:

Basic Closure Registration

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!

Shared Mutable State

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();

When to Use Closures vs Function Pointers

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});

Language Syntax

Finx supports a familiar, C-like syntax:

Variables and Assignment

letx=42;letname="Alice";letis_valid=true;letempty=null;x=x+1;// Reassignment

Functions

fnadd(a,b){returna+b;}fnfactorial(n){ifn<=1{return1;}returnn*factorial(n-1);}

Closures

fnmake_counter(){letcount=0;fnincrement(){count=count+1;returncount;}returnincrement;}letcounter=make_counter();print(counter());// 1print(counter());// 2

Control Flow

// Conditionalsifx>0{print("Positive");}elseifx<0{print("Negative");}else{print("Zero");}// Loopsleti=0;whilei<5{print(i);i=i+1;}foriin0..10{print(i);}

Built-in Functions

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"));// true

Error Handling

Finx 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),}

Advanced Usage

Running Scripts from Files

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")?;

Managing Output

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();

Performance Tuning

letmut engine = Finx::new();// Set recursion limits
engine.set_max_recursion_depth(500);

Value Types

Finx supports the following data types:

  • Numbers: 64-bit floating point (42, 3.14, -1.5)
  • Strings: UTF-8 strings ("hello", "world")
  • Booleans: true and false
  • Null: null value
  • Functions: First-class functions and closures

Working with Values

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);}

About

Embeddable Scripting Language for Rust

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages