Dynamic Evaluation
Dynamic Evaluation
Section titled “Dynamic Evaluation”Execute code dynamically at runtime with sandboxed environments and controlled module access.
Two Systems
Section titled “Two Systems”Wippy provides two evaluation systems:
| System | Purpose | Use Case |
|---|---|---|
expr | Expression evaluation | Config, templates, simple calculations |
eval_runner | Full Lua execution | Plugins, user scripts, dynamic code |
expr Module
Section titled “expr Module”Lightweight expression evaluation using the expr-lang syntax.
local expr = require("expr")
local result, err = expr.eval("x + y * 2", {x = 10, y = 5})-- result = 20Compiling Expressions
Section titled “Compiling Expressions”Compile once, run many times:
local program, err = expr.compile("price * quantity")
local total1 = program:run({price = 10, quantity = 5})local total2 = program:run({price = 20, quantity = 3})Supported Syntax
Section titled “Supported Syntax”-- Arithmeticexpr.eval("1 + 2 * 3") -- 7expr.eval("10 / 2 - 1") -- 4expr.eval("10 % 3") -- 1
-- Comparisonexpr.eval("x > 5", {x = 10}) -- trueexpr.eval("x == y", {x = 1, y = 1}) -- true
-- Booleanexpr.eval("a && b", {a = true, b = false}) -- falseexpr.eval("a || b", {a = true, b = false}) -- trueexpr.eval("!a", {a = false}) -- true
-- Ternaryexpr.eval("x > 0 ? 'positive' : 'negative'", {x = 5})
-- Functionsexpr.eval("max(1, 5, 3)") -- 5expr.eval("min(1, 5, 3)") -- 1expr.eval("len([1, 2, 3])") -- 3
-- Arraysexpr.eval("[1, 2, 3][0]") -- 1
-- String concatenationexpr.eval("'hello' + ' ' + 'world'")eval_runner Module
Section titled “eval_runner Module”Full Lua execution with security controls.
local runner = require("eval_runner")
local result, err = runner.run({ source = [[ local function double(x) return x * 2 end return { double = double } ]], method = "double", args = {21}})-- result = 42Configuration
Section titled “Configuration”| Parameter | Type | Description |
|---|---|---|
source | string | Lua source code (required) |
method | string | Function to call in returned table |
args | any[] | Arguments passed to function |
modules | string[] | Allowed builtin modules |
imports | table | Registry entries to import |
context | table | Values available as ctx |
allow_classes | string[] | Additional module classes |
custom_modules | table | Custom tables as modules |
Module Access
Section titled “Module Access”Whitelist allowed modules:
runner.run({ source = [[ local json = require("json") return json.encode({hello = "world"}) ]], modules = {"json"}})Modules not in the list cannot be required.
Registry Imports
Section titled “Registry Imports”Import entries from the registry:
runner.run({ source = [[ local data = ... local utils = require("utils") return utils.format(data) ]], imports = { utils = "app.lib:utilities" }, args = {{key = "value"}}})Custom Modules
Section titled “Custom Modules”Inject custom tables:
runner.run({ source = [[ return sdk.version ]], custom_modules = { sdk = {version = "1.0.0", api_key = "xxx"} }})Context Values
Section titled “Context Values”Pass data accessible as ctx:
runner.run({ source = [[ return "Hello, " .. ctx.get("user") ]], context = {user = "Alice"}})Compiling Programs
Section titled “Compiling Programs”runner.compile validates source and reports its entrypoint and modules without running it:
local program, err = runner.compile([[ local function process(x) return x * 2 end return { process = process }]], "process", {modules = {"json"}})
program:method() -- "process" (string)program:modules() -- {"json"} (string[])The compiled program is informational; execute by calling runner.run with the source and method.
Security Model
Section titled “Security Model”Module Classes
Section titled “Module Classes”Modules are categorized by capability:
| Class | Description | Default |
|---|---|---|
deterministic | Pure functions | Allowed |
encoding | Data encoding | Allowed |
time | Time operations | Allowed |
nondeterministic | Random, etc. | Allowed |
process | Spawn, registry | Blocked |
storage | File, database | Blocked |
network | HTTP, sockets | Blocked |
Enabling Blocked Classes
Section titled “Enabling Blocked Classes”runner.run({ source = [[ local http = require("http_client") return http.get("https://api.example.com") ]], modules = {"http_client"}, allow_classes = {"network"}})Permission Checks
Section titled “Permission Checks”The system checks permissions for:
eval.compile- Before compilationeval.run- Before executioneval.module- For each module in whitelisteval.import- For each registry importeval.class- For each allowed class
Configure in security policies.
Error Handling
Section titled “Error Handling”local result, err = runner.run({...})if err then if err:kind() == errors.PERMISSION_DENIED then -- Access denied by security policy elseif err:kind() == errors.INVALID then -- Invalid source or configuration elseif err:kind() == errors.INTERNAL then -- Execution or compilation error endendUse Cases
Section titled “Use Cases”Plugin System
Section titled “Plugin System”local plugins = registry.find({meta = {type = "plugin"}})
for _, plugin in ipairs(plugins) do local source = plugin:data().source runner.run({ source = source, method = "init", modules = {"json", "time"}, context = {config = app_config} })endTemplate Evaluation
Section titled “Template Evaluation”local template = "Hello, {{name}}! You have {{count}} messages."local compiled = expr.compile("name")
-- Fast repeated evaluationfor _, user in ipairs(users) do local greeting = compiled:run({name = user.name})endUser Scripts
Section titled “User Scripts”local user_code = request:body()
local result, err = runner.run({ source = user_code, modules = {"json", "text"}, -- Safe modules only context = {data = input_data}})See Also
Section titled “See Also”- Expression - Expression language reference
- Exec - System command execution
- Security - Security policies