filo

package module
v0.0.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 14, 2026 License: MIT Imports: 12 Imported by: 4

README

Filo language - A Safe, Minimalist Scripting Language for Go Applications

Filo is a lean, secure, and deterministic scripting language designed to be embedded directly into Go applications. It was created for real-world scenarios where end users, including non-programmers, need to write small rules, expressions, and validations that affect application behavior without compromising stability, security, or performance.


Name and pronunciation

The name Filo comes from the Italian word filo (“thread”).

It is pronounced like Italian filo:

  • IPA: /ˈfiː.lo/
  • Rough English approximation: “FEE-lo”

Examples in Italian:

  • Un filo di lana. – “A thread of wool.”
  • Tirare un filo dal maglione. – “To pull a thread from the sweater.”
  • C’è un filo che pende dalla manica. – “There is a thread hanging from the sleeve.”

Please note: it is not pronounced like “Filó” or “FYE-lo” (/ˈfaɪ.loʊ/) or “fee-LOH” (/fiːˈloʊ/), but simply “FEE-lo”.


Motivation

When building complex systems such as:

  • a RAD-style generic application builder,
  • an RPG platform with customizable rules,
  • applications configurable by users or administrators,

a clear need appears:

Allow the user to write custom logic without ever putting the server at risk.

Why create the Filo language?

1. Absolute security

The Filo runtime is designed with:

  • StepLimit - prevents infinite loops.
  • RecursionLimit - blocks stack explosions.
  • Timeout - execution is automatically aborted.
  • context.Context - natural integration with Go.
  • recover() - no script can cause a server panic.
2. Simplicity

Filo uses a minimalist Lisp-like syntax:

(+ 1 2)
(if (< age 18) "minor" "adult")
(map (fn (x) (* x x)) (list 1 2 3))

Small, easy to teach, easy to understand, and extremely predictable.

3. Smooth Go integration
  • Builtins written directly in Go.
  • Global environment passed as map[string]Value.
  • Safe calls made in the backend.
  • Ideal for validations, RPG rules, and configuration scripts.
4. Extensible
  • Go functions can be registered as Filo commands - from simple sums to database queries.

Design and Philosophy

Filo's philosophy is summarized in four principles:

1. Short, declarative scripts

Users should write:

  • mathematical expressions,
  • validations,
  • small business rules.

No modules, long loops, or complex structures.

2. Small, powerful, testable language

Step by step, Filo provides:

  • essential operations (+, -, *, /, %, pow);
  • comparisons (=, !=, <, <=, >, >=);
  • boolean logic (and, or, not);
  • lists and higher-order functions (map, fold, list, length, head, tail, nth, list-append, list-concat);
  • string operations (str-fmt, str-concat, str-join, str-split, str-find, str-trim, str-replace, str-upper, str-lower, str-len, str-sub);
  • basic control flow (if, do);
  • introspection (type-of);
  • validation (is-empty, is-nil);
  • local scope (let, letv);
  • multiple returns (values);
  • functions (fn, def) with recursion limits.
Extension Packages

Filo can be extended with specialized packages:

  • filomath: Advanced math functions (sin, cos, log, to-int, etc).
  • filorand: Non-deterministic functions (rand-float, rand-int, uuid-v4).
  • filojson: JSON helpers for marshal/unmarshal (json-marshal, json-unmarshal, json-null).
Pre-Parse/Execute (Template-style)

For scripts executed multiple times with different data, Filo supports pre-parsing, similar to Go's html/template package:

// Parse once at startup
script := filo.Must(filo.ParseScript("calc", "(+ x y)"))

// Execute many times with different globals
for _, data := range items {
    globals := map[string]filo.Value{
        "x": filo.VNum(data.X),
        "y": filo.VNum(data.Y),
    }
    result, _, err := script.Execute(ctx, engine, globals, cfg)
    fmt.Println(result.Num)
}

API:

Function Description
ParseScript(name, src) Creates and parses a reusable script.
script.Execute(ctx, eng, globals, cfg) Executes with explicit engine/config.
Must(script, err) Panics if error (for init).
Filo.Execute(script, overrides) Executes with Filo's globals + optional overrides.

Performance: Pre-parsing eliminates parsing overhead (~2x faster for repeated executions).

Go Marshal/Unmarshal

Filo provides Marshal and Unmarshal functions to convert Go values to Filo values and vice versa:

// Convert Go struct to Filo Value
type Config struct {
    Name string `filo:"name"`
    Port int    `filo:"port"`
}

cfg := Config{Name: "app", Port: 8080}
val, err := filo.Marshal(cfg)
// val = (list (tuple "name" "app") (tuple "port" 8080))

// Convert Filo Value back to Go struct
var cfg2 Config
err = filo.Unmarshal(val, &cfg2)
// cfg2 = {Name: "app", Port: 8080}

Type mapping:

Go Type Filo Kind
bool KBool
int, float64, etc KNumber
string KString
[]T KList
struct KList of (key, value) tuples
map[K]V KList of (key, value) tuples
nil KTuple (empty)

Note: Expressions in Filo are evaluated before reaching Unmarshal:

  • (list "port" (+ 8000 80)) → port = 8080 (expression evaluated by Filo)
  • (list "port" "(+ 8000 80)") → port = "(+ 8000 80)" (literal string)

Running fuzz tests:

# Run a specific fuzz test for 30 seconds
go test -fuzz=FuzzMarshalUnmarshalString -fuzztime=30s .

# Run fuzz test for ints
go test -fuzz=FuzzMarshalUnmarshalInt -fuzztime=30s .

# Available fuzz tests:
# - FuzzMarshalUnmarshalInt
# - FuzzMarshalUnmarshalFloat
# - FuzzMarshalUnmarshalString
# - FuzzMarshalUnmarshalBool
# - FuzzMarshalUnmarshalBytes
# - FuzzMarshalSliceInt
3. Restricted environment

No:

  • file access,
  • network access,
  • “dangerous” calls.

All advanced integration happens only through explicitly registered Go functions. So the programmer has full control over what the script can do.


Language Reference

Special Forms
Name Syntax Description
if (if cond then [else]) Conditional execution. Returns list() (empty/nil) if else is missing and cond is false.
do (do expr1 expr2 ...) Evaluates expressions in order, returns the last result.
let (let ((n v) ...) body) Defines local variables. Scope is limited to the body.
letv (letv (n1 n2) (values v1 v2) body) Destructures multi-value returns (tuples).
fn (fn (args) body) Creates an anonymous function.
def (def name expr) Defines a global variable or function (always in the root/global scope).
set (set name expr) Updates an existing variable in the nearest scope.
values (values v1 v2 ...) Returns multiple values (a tuple).
exit (exit [value]) Terminates script execution immediately. Returns value or empty list.
return (return [value]) Returns from current function. Returns value or empty list.
Core Builtins

Note: NewEngine() includes the core math/logic/list/type builtins by default. Some builtin sets are intentionally opt-in and must be registered explicitly (see below).

Category Function Description
Math +, -, *, /, % Basic arithmetic.
pow (pow x y) Power function.
Logic =, != Equality checks.
<, <=, >, >= Numeric comparison.
and, or, not Boolean logic.
Types type-of Returns "number", "string", "list", etc.
is-empty Returns true for "" or empty list.
is-nil Returns true for an empty list (closest thing to nil in the current runtime).
Lists list Creates a list (list 1 2 3).
length Returns list length.
head, tail First element / Rest of list.
nth (nth list index) Access element by index (0-based).
list-append (list-append list item) Returns new list with item appended.
list-concat (list-concat l1 l2 ...) Concatenates two or more lists.
map (map fn list) Applies function to each element.
fold (fold fn init list) Reduces list with accumulator.
String Builtins

These string builtins are not enabled by default. To use them, register them explicitly:

eng := filo.NewEngine()
filo.RegisterStringBuiltins(eng)
Function Description
str-fmt (str-fmt format args...) Safe implementation of fmt.Sprintf.
str-concat Concatenates arguments into a string.
str-join (str-join sep list) Joins list elements with separator.
str-split (str-split sep str) Splits string into list.
str-len Returns string length (runes).
str-sub (str-sub str start [end]) Substring operations.
str-find (str-find sub str) Validation/Search.
str-replace (str-replace old new str) Replaces occurrences.
str-trim Trims whitespace.
str-upper Converts to uppercase.
str-lower Converts to lowercase.
Extension: filomath

Requires explicit registration via filomath.RegisterMathBuiltins(eng).

Function Description
abs, sqrt Absolute value, Square root.
floor, ceil, round Rounding operations.
to-int (to-int n) Truncates float to integer.
sin, cos, tan Trigonometric functions (radians).
log, log10, exp Logarithmic functions.
math-min, math-max Min/Max of arguments.
pi, e Constants.
Extension: filorand

Requires explicit registration via filorand.RegisterRandomBuiltins(eng). These functions are intentionally non-deterministic.

Function Description
rand-float Random number [0.0, 1.0).
rand-int (rand-int n) Random integer [0, n).
uuid-v4 Generates a standard UUID string.

Practical examples

1. Calculated field
(let ((forca field:for) (bonus field:bonus))
  (+ (* forca 2) bonus))
2. Dynamic configuration
(let ((env ENV))
  (set Address "http://localhost:3210")
  (if (= env "prod")
      (set Address "https://app.example.com")
      (set Address "http://localhost:3210")))
3. Controlled recursion
(let ()
  (def fact (fn (n)
    (if (<= n 1)
        1
        (* n (fact (- n 1))))))
  (fact 5)) ; 120
4. Auto-level helpers
(let ()
  (def thresholds (list 0 300 900 2700 6500 15000))

  (def auto-level (fn (xp thresholds)
    (fold (fn (lvl threshold)
            (if (>= xp threshold) (+ lvl 1) lvl))
         0
         thresholds)))

  (def auto-level-progress (fn (xp thresholds)
    (let ((lvl (auto-level xp thresholds))
          (total (length thresholds)))
      (if (>= lvl total)
          (values lvl 0)
          (let ((next (nth thresholds lvl)))
            (values lvl (- next xp))))))

  (auto-level-progress 1200 thresholds))

This script returns (tuple 3 1500), meaning the character is at level three and needs 1,500 XP to reach the next tier.


Integration example with Go

Register a simple builtin

The following helper sums two numbers provided as positional parameters a and b.

func addTwo(ctx context.Context, args []filo.Value) (filo.Value, error) {
    if len(args) != 2 {
        return filo.Value{}, errors.New("expected two numbers")
    }

    a, err := args[0].AsNumber()
    if err != nil {
        return filo.Value{}, err
    }

    b, err := args[1].AsNumber()
    if err != nil {
        return filo.Value{}, err
    }

    return filo.VNum(a + b), nil
}

func registerMathBuiltins(eng *filo.Engine) {
    eng.MustRegisterBuiltin("add-two", addTwo)
}

eng := filo.NewEngine()
registerMathBuiltins(eng)

res, _, err := eng.RunScript(ctx, "(add-two 10 32)", nil, cfg)
if err != nil {
    return err
}
// res = 42
Register a string formatter

full-name expects two string parameters: first and last. It trims blanks and returns a single string value.

func fullName(ctx context.Context, args []filo.Value) (filo.Value, error) {
    if len(args) != 2 {
        return filo.Value{}, errors.New("expected first and last name")
    }

    first, err := args[0].AsString()
    if err != nil {
        return filo.Value{}, err
    }

    last, err := args[1].AsString()
    if err != nil {
        return filo.Value{}, err
    }

    combined := strings.TrimSpace(first + " " + last)
    return filo.VString(combined), nil
}

func registerStringBuiltins(eng *filo.Engine) {
    eng.MustRegisterBuiltin("full-name", fullName)
}

eng := filo.NewEngine()
registerStringBuiltins(eng)

res, _, err := eng.RunScript(ctx, "(full-name \"Ada\" \"Lovelace\")", nil, cfg)
if err != nil {
    return err
}
// res = "Ada Lovelace"
Register a custom aggregator

This builtin receives a list of numbers xs and returns a tuple with the minimum and maximum values.

func minMax(ctx context.Context, args []filo.Value) (filo.Value, error) {
    if len(args) != 1 {
        return filo.Value{}, errors.New("expected one list")
    }

    list, err := args[0].AsList()
    if err != nil {
        return filo.Value{}, err
    }

    if len(list) == 0 {
        return filo.Value{}, errors.New("list cannot be empty")
    }

    minVal, err := list[0].AsNumber()
    if err != nil {
        return filo.Value{}, err
    }

    maxVal := minVal
    for i := 1; i < len(list); i++ {
        current, convErr := list[i].AsNumber()
        if convErr != nil {
            return filo.Value{}, convErr
        }

        if current < minVal {
            minVal = current
        }

        if current > maxVal {
            maxVal = current
        }
    }

    return filo.VList([]filo.Value{filo.VNum(minVal), filo.VNum(maxVal)}), nil
}

func registerAggregatorBuiltins(eng *filo.Engine) {
    eng.MustRegisterBuiltin("min-max", minMax)
}

eng := filo.NewEngine()
registerAggregatorBuiltins(eng)

res, _, err := eng.RunScript(ctx, "(min-max (list 4 7 1 9))", nil, cfg)
if err != nil {
    return err
}
// res = (list 1 9)
Example with globals
globals := map[string]filo.Value{
    "field:a": filo.VNum(10),
    "field:b": filo.VNum(5),
}

res, _, err := eng.RunScript(ctx, "(+ field:a field:b)", globals, cfg)
if err != nil {
    return err
}
// res = 15

give power to the user without giving up security.

It is a small, elegant, deterministic language that integrates easily into the Go ecosystem. It lets users write helpful rules without putting the server at risk.

Documentation

Overview

Package filo provides Marshal and Unmarshal functions for converting between Go values and Filo Value types.

Marshal converts any Go value supported by Filo (bool, numbers, string, slices, structs, maps) into a Filo Value.

Unmarshal converts a Filo Value into a Go value. The target must be a pointer to any Filo-supported type.

Struct fields use the "filo" tag to specify the field name. If no tag is present, the lowercased field name is used. Fields with tag "-" are skipped.

Example:

type Person struct {
    Name string `filo:"name"`
    Age  int    `filo:"age"`
}

p := Person{Name: "Alice", Age: 30}
val, err := filo.Marshal(p)
// val is a list of tuples: [("name", "Alice"), ("age", 30)]

var p2 Person
err = filo.Unmarshal(val, &p2)
// p2 is now {Name: "Alice", Age: 30}

Package filo provides Lisp-style formatting for Filo code.

The formatting follows common Lisp conventions for readability:

  • Short expressions stay on one line: (+ 1 2)
  • Function arguments align vertically when multiline
  • Special forms (let, if, fn, def) have specific indentation rules
  • Strings with escapes are preserved exactly: "test \" test"
  • Comments and blank lines are preserved
Example (Do)
// The 'do' special form evaluates multiple expressions in sequence
// and returns the value of the last one.
// Useful for side effects or grouping commands.

eng := NewEngine()
script := `
(do
  (def x 10)
  (def y 20)
  (+ x y))`
val, _, _ := eng.RunScript(context.Background(), script, nil, EvalConfig{})
fmt.Printf("Result: %v\n", val.Num)
Output:

Result: 30
Example (TypeOf)
// 'type-of' returns the type of a value as a string.
// Possible values: "number", "string", "bool", "list", "tuple", "map", "func".

eng := NewEngine()
script := `
(let ((n 42)
      (s "hello"))
  (list (type-of n) (type-of s)))`
val, _, _ := eng.RunScript(context.Background(), script, nil, EvalConfig{})
// Manually inspect list to print
l, _ := val.AsList()
fmt.Printf("Types: %s, %s\n", l[0].Str, l[1].Str)
Output:

Types: number, string

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrorFunctionNotFound = errors.New("function not found")
	ErrorNotAllowedType   = errors.New("not allowed return type")
)

Functions

func Format added in v0.0.5

func Format(src string) (string, error)

Format formats Filo source code with Lisp-style indentation. It preserves comments and blank lines.

func FormatAST added in v0.0.5

func FormatAST(node Node, cfg FormatConfig) (string, error)

FormatAST formatting an AST node back to string. This is used when the AST has been modified (e.g. by constant folding) and we can't use the token-based formatter.

func FormatValue added in v0.0.5

func FormatValue(v Value) string

FormatValue formats a Filo Value as a readable string.

func FormatValueIndent added in v0.0.5

func FormatValueIndent(v Value, prefix, indent string) string

FormatValueIndent formats a Value with prefix and indent strings. Similar to json.MarshalIndent behavior.

func FormatWithConfig added in v0.0.5

func FormatWithConfig(src string, cfg FormatConfig) (string, error)

FormatWithConfig formats Filo source with custom configuration. Comments and intentional blank lines are preserved.

func Marshal added in v0.0.5

func Marshal(v any) (string, error)

Marshal converts a Go value to a compact Filo string. To modify the formatting, use MarshalIndent.

func MarshalIndent added in v0.0.5

func MarshalIndent(v any, prefix, indent string) (string, error)

MarshalIndent converts a Go value to an indented Filo string. The prefix is prepended to each line (including the first), and indent is added per nesting level.

This is similar to json.MarshalIndent but produces Filo syntax.

func MarshalWithOptions added in v0.0.5

func MarshalWithOptions(v any, opts MarshalOptions) (string, error)

MarshalWithOptions converts a Go value to a Filo string with options.

func Unmarshal added in v0.0.5

func Unmarshal(data string, target any) error

Unmarshal converts a Filo string to a Go value. It parses the string, evaluates it, and unmarshals the result into target.

func UnmarshalFromValue added in v0.0.5

func UnmarshalFromValue(val Value, target any) error

UnmarshalFromValue converts a Filo Value to a Go value. The target must be a non-nil pointer to any Filo-supported type.

Type mapping:

  • KBool -> bool
  • KNumber -> int, int64, float64, etc.
  • KString -> string
  • KList -> []T or struct (if list of tuples with string keys)
  • KTuple (empty) -> nil pointer

For structs, the list must contain tuples of (string_key, value). Field matching uses the "filo" tag or lowercased field name.

Types

type BoolLit

type BoolLit struct {
	Value bool
}

type Builtin

type Builtin func(ctx context.Context, args []Value) (Value, error)

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

func NewEngine

func NewEngine() *Engine

func (*Engine) Compile added in v0.0.5

func (e *Engine) Compile(src string) (*Program, error)

Compile parses and compiles the source code into a reuseable Program.

func (*Engine) CompileAST added in v0.0.5

func (e *Engine) CompileAST(ast Node) (*Program, error)

CompileAST compiles a parsed AST into a reusable Program bound to this Engine.

func (*Engine) ExecuteAST added in v0.0.5

func (e *Engine) ExecuteAST(ctx context.Context, ast Node, globals map[string]Value, cfg EvalConfig) (result Value, newGlobals map[string]Value, err error)

ExecuteAST executes a pre-parsed (and compiled) AST with the given globals. This is the core execution method used by both RunScript and Script.Execute.

func (*Engine) MustRegisterBuiltin

func (e *Engine) MustRegisterBuiltin(name string, fn Builtin)

func (*Engine) RegisterBuiltin

func (e *Engine) RegisterBuiltin(name string, fn Builtin) error

func (*Engine) RunScript

func (e *Engine) RunScript(ctx context.Context, src string, globals map[string]Value, cfg EvalConfig) (result Value, newGlobals map[string]Value, err error)

type EvalConfig

type EvalConfig struct {
	StepLimit      int
	RecursionLimit int
	Timeout        time.Duration
}

type Filo

type Filo struct {
	// contains filtered or unexported fields
}

func New

func New() *Filo

New creates a new Filo instance for configuration loading.

func (*Filo) CallFunction

func (f *Filo) CallFunction(name string, args ...any) (Value, error)

CallFunction invokes a Filo-defined function by name with the given arguments. Arguments are converted from Go types to Filo Values. Returns the result Value or an error if the function doesn't exist or fails.

func (*Filo) CallFunctionString

func (f *Filo) CallFunctionString(name string, fallback string, args ...any) string

CallFunctionString is a convenience wrapper that calls a function and converts the result to a string. If the function doesn't exist or returns a non-string, the fallback string is returned.

func (*Filo) Close

func (f *Filo) Close()

Close is a no-op for Filo but provided for API compatibility.

func (*Filo) DoString

func (f *Filo) DoString(filoScript string) error

DoString executes a Filo script and updates globals with any (set ...) statements. This is a convenience method that parses and executes in one call. For scripts executed multiple times, use ParseScript + Execute for better performance.

func (*Filo) Execute added in v0.0.5

func (f *Filo) Execute(script *Script, overrideGlobals map[string]Value) error

Execute executes a pre-parsed script with optional override globals. Override globals take precedence over instance globals for this execution only. After execution, instance globals are updated with any (set ...) statements.

func (*Filo) GetBool

func (f *Filo) GetBool(vGlobal string) (bool, error)

func (*Filo) GetEngine

func (f *Filo) GetEngine() *Engine

GetEngine returns the underlying Filo engine for advanced operations.

func (*Filo) GetInt

func (f *Filo) GetInt(vGlobal string) (int, error)

func (*Filo) GetMap

func (f *Filo) GetMap(vGlobal string) (map[string]string, error)

func (*Filo) GetMapOfLists

func (f *Filo) GetMapOfLists(vGlobal string) (map[string][]string, error)

func (*Filo) GetString

func (f *Filo) GetString(vGlobal string) (string, error)

func (*Filo) GetTable

func (f *Filo) GetTable(vGlobal string) ([]string, error)

func (*Filo) HasFunction

func (f *Filo) HasFunction(name string) bool

HasFunction checks if a function with the given name is defined in globals.

func (*Filo) MustGetBool

func (f *Filo) MustGetBool(vGlobal string) bool

MustGetBool retrieves a global variable as a bool or fatals.

func (*Filo) MustGetInt

func (f *Filo) MustGetInt(vGlobal string) int

MustGetInt retrieves a global variable as an int or fatals.

func (*Filo) MustGetMap

func (f *Filo) MustGetMap(vGlobal string) map[string]string

MustGetMap retrieves a global variable as a map[string]string or fatals.

func (*Filo) MustGetMapOfLists

func (f *Filo) MustGetMapOfLists(vGlobal string) map[string][]string

MustGetMapOfLists retrieves a global variable as a map[string][]string or fatals. Expects the structure: (list (list "key" (list "val1" "val2")) ...)

func (*Filo) MustGetString

func (f *Filo) MustGetString(vGlobal string) string

MustGetString retrieves a global variable as a string or fatals.

func (*Filo) MustGetTable

func (f *Filo) MustGetTable(vGlobal string) []string

MustGetTable retrieves a global variable as a []string or fatals.

func (*Filo) RegisterBuiltin

func (f *Filo) RegisterBuiltin(name string, fn Builtin) error

RegisterBuiltin registers a custom builtin function in the Filo engine.

func (*Filo) SetGlobal

func (f *Filo) SetGlobal(name string, value any)

SetGlobal sets a global variable that will be available in the Filo script.

func (*Filo) SetGlobalMapOfLists

func (f *Filo) SetGlobalMapOfLists(name string, m map[string][]string)

SetGlobalMapOfLists sets a global variable from a map[string][]string. The structure is stored as a list of (key, list-of-values) tuples. Example: {"a": ["x", "y"]} becomes (list (list "a" (list "x" "y")))

type FormatConfig added in v0.0.5

type FormatConfig struct {
	// Indent is the string used for each indentation level (default: 2 spaces).
	Indent string
	// MaxLineWidth is the preferred maximum line width (default: 80).
	MaxLineWidth int
}

FormatConfig controls formatting behavior.

func DefaultFormatConfig added in v0.0.5

func DefaultFormatConfig() FormatConfig

DefaultFormatConfig returns the default formatting configuration.

type Frame added in v0.0.5

type Frame struct {
	// contains filtered or unexported fields
}

Frame represents a static lexical scope (activation record). It replaces the map-based Env for local variables.

type Func

type Func struct {
	Params []string
	Body   []Node
	Frame  *Frame
}

type GlobalEnv added in v0.0.5

type GlobalEnv struct {
	// contains filtered or unexported fields
}

GlobalEnv represents the global environment (map-based). Unresolved symbols (globals) are looked up here. GlobalEnv represents the global environment using a SymbolTable and array storage.

func NewGlobalEnv added in v0.0.5

func NewGlobalEnv(symbols *SymbolTable) *GlobalEnv

NewGlobalEnv creates a new global environment linked to a symbol table. It allocates backing storage based on symbol table size.

func (*GlobalEnv) Define added in v0.0.5

func (g *GlobalEnv) Define(name string, v Value)

func (*GlobalEnv) DefineID added in v0.0.5

func (g *GlobalEnv) DefineID(id int, v Value)

func (*GlobalEnv) Get added in v0.0.5

func (g *GlobalEnv) Get(name string) (Value, bool)

func (*GlobalEnv) GetByID added in v0.0.5

func (g *GlobalEnv) GetByID(id int) (Value, bool)

func (*GlobalEnv) Reset added in v0.0.5

func (g *GlobalEnv) Reset(symbols *SymbolTable)

Reset clears the global environment for reuse. It keeps the backing array but resets values and defined status.

func (*GlobalEnv) ToMap added in v0.0.5

func (g *GlobalEnv) ToMap() map[string]Value

ToMap returns a copy of globals (compatibility helper)

type Kind

type Kind int
const (
	KNumber Kind = iota
	KBool
	KString
	KList
	KTuple
	KFunc
)

type List

type List struct {
	Elems []Node
}

type MarshalOptions added in v0.0.5

type MarshalOptions struct {
	// Prefix is prepended to each line.
	Prefix string
	// Indent is added per nesting level.
	Indent string
}

Marshal converts a Go value to a Filo Value.

Supported types:

  • bool -> KBool
  • int, int8, int16, int32, int64 -> KNumber
  • uint, uint8, uint16, uint32, uint64 -> KNumber
  • float32, float64 -> KNumber
  • string -> KString
  • []T -> KList (elements converted recursively)
  • struct -> KList of (field_name, value) tuples
  • map[K]V -> KList of (key, value) tuples (sorted by key for determinism)
  • *T -> same as T (nil becomes empty tuple)

Functions and channels are not supported and return an error. MarshalIndent converts a Go value to an indented Filo string. The prefix is prepended to each line (including the first), and indent is added per nesting level.

This is similar to json.MarshalIndent but produces Filo syntax. MarshalOptions configures the marshaling process.

type Node

type Node any

func Compile added in v0.0.5

func Compile(node Node, builtins map[string]builtinFunc, symbols *SymbolTable) (Node, error)

Compile performs static analysis on the AST to resolve symbols. It returns a new AST with Symbol nodes replaced by ResolvedSymbol where possible, and ResolvedBuiltin for known builtins. Global symbols are resolved to ResolvedGlobal if a SymbolTable is provided.

func FoldConstants added in v0.0.5

func FoldConstants(node Node) (Node, bool)

FoldConstants recursively folds constant expressions in the AST. It returns the folded node and true if any change was made, or false otherwise.

Rules: 1. Recursively fold children. 2. If node is a pure function call with all-literal arguments, evaluate it. 3. If node is (if cond then else) and cond is literal bool:

  • If true, replace with folded 'then'.
  • If false, replace with folded 'else' (or empty if missing).

func Parse added in v0.0.5

func Parse(input string) (Node, error)

Parse parses the input string and returns the AST (List of expressions)

type NumberLit

type NumberLit struct {
	Value float64
}

type ParseError

type ParseError struct {
	Pos     int
	Near    string
	Message string
}

ParseError represents a syntax/lexing error with a byte position in the source. Position is 0-based and refers to a byte offset in the original string. Near contains a small snippet around the error location to aid debugging.

func (*ParseError) Error

func (e *ParseError) Error() string

type Program added in v0.0.5

type Program struct {
	// contains filtered or unexported fields
}

Program represents a compiled Filo script bound to an Engine instance.

func (*Program) Execute added in v0.0.5

func (p *Program) Execute(ctx context.Context, globals map[string]Value, cfg EvalConfig) (result Value, newGlobals map[string]Value, err error)

Execute runs the compiled program.

type ResolvedBuiltin added in v0.0.5

type ResolvedBuiltin struct {
	Name string
	// contains filtered or unexported fields
}

type ResolvedGlobal added in v0.0.5

type ResolvedGlobal struct {
	Name string
	ID   int
}

type ResolvedSymbol added in v0.0.5

type ResolvedSymbol struct {
	Name  string
	Depth int // How many frames up to look (0 = local)
	Index int // Index in the frame's slot array
}

ResolvedSymbol represents a symbol resolved to a static frame location.

type Scope added in v0.0.5

type Scope struct {
	// contains filtered or unexported fields
}

Scope represents a lexical scope during compilation.

type Script added in v0.0.5

type Script struct {
	// contains filtered or unexported fields
}

Script represents a pre-parsed Filo script that can be executed multiple times with different globals. This is similar to Go's html/template pattern.

func Must added in v0.0.5

func Must(s *Script, err error) *Script

Must is a helper that wraps a call to ParseScript and panics if the error is non-nil. It is intended for use in variable initializations.

func ParseScript added in v0.0.5

func ParseScript(name, src string) (*Script, error)

ParseScript is a convenience function that creates a new Script and parses it.

func (*Script) Execute added in v0.0.5

func (s *Script) Execute(ctx context.Context, eng *Engine, globals map[string]Value, cfg EvalConfig) (result Value, newGlobals map[string]Value, err error)

Execute runs the pre-parsed script with the given engine, globals, and config.

func (*Script) Name added in v0.0.5

func (s *Script) Name() string

Name returns the script's name.

func (*Script) Parse added in v0.0.5

func (s *Script) Parse(src string) (*Script, error)

Parse parses the source code and stores the AST in the Script. Returns the Script for method chaining.

type StringLit

type StringLit struct {
	Value string
}

type Symbol

type Symbol struct {
	Name string
}

type SymbolTable added in v0.0.5

type SymbolTable struct {
	// contains filtered or unexported fields
}

SymbolTable manages the mapping between global variable names and their integer IDs. It is thread-safe to allow concurrent compilation/execution sharing the same engine state.

func NewSymbolTable added in v0.0.5

func NewSymbolTable() *SymbolTable

NewSymbolTable creates a new empty SymbolTable.

func (*SymbolTable) GetID added in v0.0.5

func (st *SymbolTable) GetID(name string) (int, bool)

GetID returns the ID for a name if it exists, and false otherwise.

func (*SymbolTable) GetName added in v0.0.5

func (st *SymbolTable) GetName(id int) (string, bool)

GetName returns the name for a given ID if it exists.

func (*SymbolTable) Resolve added in v0.0.5

func (st *SymbolTable) Resolve(name string) int

Resolve returns the ID for a given symbol name. If the symbol does not exist, it defines it and assigns a new ID.

func (*SymbolTable) Size added in v0.0.5

func (st *SymbolTable) Size() int

Size returns the number of symbols currently registered.

func (*SymbolTable) Snapshot added in v0.0.5

func (st *SymbolTable) Snapshot() map[string]int

Snapshot returns a copy of the current symbol map.

type Value

type Value struct {
	Kind Kind
	Num  float64
	Bool bool
	Str  string
	List []Value
	Tup  []Value
	Fn   *Func
}

func MarshalToValue added in v0.0.5

func MarshalToValue(v any) (Value, error)

MarshalToValue converts a Go value to a Filo Value.

Supported types:

  • bool -> KBool
  • int, int8, int16, int32, int64 -> KNumber
  • uint, uint8, uint16, uint32, uint64 -> KNumber
  • float32, float64 -> KNumber
  • string -> KString
  • []T -> KList (elements converted recursively)
  • struct -> KList of (field_name, value) tuples
  • map[K]V -> KList of (key, value) tuples (sorted by key for determinism)
  • *T -> same as T (nil becomes empty tuple)

Functions and channels are not supported and return an error.

func VBool

func VBool(v bool) Value

func VFunc

func VFunc(fn *Func) Value

func VList

func VList(v []Value) Value

func VNum

func VNum(v float64) Value

func VString

func VString(v string) Value

func VTuple

func VTuple(v []Value) Value

func (Value) AsBool

func (v Value) AsBool() (bool, error)

func (Value) AsList

func (v Value) AsList() ([]Value, error)

func (Value) AsNumber

func (v Value) AsNumber() (float64, error)

func (Value) AsString

func (v Value) AsString() (string, error)

func (Value) AsTuple

func (v Value) AsTuple() ([]Value, error)

func (Value) String

func (v Value) String() string

Directories

Path Synopsis
cmd
filo-repl command
Command filo-repl provides an interactive REPL for the Filo language.
Command filo-repl provides an interactive REPL for the Filo language.
filofmt command
Command filofmt formats Filo source code files.
Command filofmt formats Filo source code files.
examples
autolevel command
config command
controlflow command
custombuiltin command
filter command
globals command
hello command
json command
listops command
marshal command
Example marshal demonstrates converting Go structs to Filo values and back.
Example marshal demonstrates converting Go structs to Filo values and back.
math command
printf command
random command
scoring command
script command
Example: Pre-Parse/Execute pattern for efficient script reuse
Example: Pre-Parse/Execute pattern for efficient script reuse
sequence command
strings command
transform command
types command
validation command
Package filojson provides JSON marshal/unmarshal builtins for the Filo language.
Package filojson provides JSON marshal/unmarshal builtins for the Filo language.
Package filomath provides advanced math builtins for the Filo scripting language.
Package filomath provides advanced math builtins for the Filo scripting language.
Package filoprint provides simple print builtins for Filo scripts.
Package filoprint provides simple print builtins for Filo scripts.
Package filorand provides non-deterministic builtins for Filo.
Package filorand provides non-deterministic builtins for Filo.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL