scriptling

package module
v0.2.9 Latest Latest
Warning

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

Go to latest
Published: Mar 3, 2026 License: MIT Imports: 18 Imported by: 0

README

Scriptling

GitHub License

Scriptling - Python-like Scripting Language for Go

A minimal, sandboxed interpreter for LLM agents to execute code and interact with REST APIs. Python-inspired syntax designed for embedding in Go applications.

Features

  • Python-like syntax with indentation-based blocks
  • Core types: integers, floats, strings, booleans, lists, dictionaries
  • Control flow: if/elif/else, while, for loops, break, continue
  • Object-oriented: Classes with single inheritance, methods, and constructors
  • Advanced features: functions, lambda, list comprehensions, error handling
  • Libraries: including json, regex, math, time, requests, subprocess (load on demand)
  • Go integration: Register functions, exchange variables
  • Fast: Lightweight interpreter, only loads what you need
  • Sandboxed: No filesystem or network access unless enabled via libraries
  • MCP support: Model Context Protocol for LLM integration
  • CLI tool: Run scripts, start HTTP server with MCP support
  • Extensible: Create custom functions, libraries, and classes in Go or Scriptling
  • Linter: Built-in linter for syntax checks
  • OpenAPI support: Generate Scriptling client libraries from OpenAPI specs with o2s tool

Differences from Python

While Scriptling is inspired by Python, it has some key differences:

  • Single Inheritance Only: Classes support single inheritance (e.g., class Dog(Animal):), but not multiple inheritance.
  • No del Statement: Use slice assignment instead — e.g. lst = lst[:n] rather than del lst[n:].
  • No Nested Classes: Classes cannot be defined within other classes.
  • Simplified Scope: nonlocal and global keywords work slightly differently.
  • Go Integration: Designed primarily for embedding in Go, with direct type mapping.
  • Sandboxed: No direct access to filesystem or network unless explicitly enabled via libraries.

Installation

go get github.com/paularlott/scriptling

Quick Start

package main

import (
    "fmt"
    "github.com/paularlott/scriptling"
    "github.com/paularlott/scriptling/stdlib"
)

func main() {
    // Create interpreter
    p := scriptling.New()

    // Register all standard libraries
    stdlib.RegisterAll(p)

    // Execute Scriptling code
    result, err := p.Eval(`
# Variables and types
x = 42
name = "Alice"
numbers = [1, 2, 3]

# Functions
def greet(n):
    return "Hello " + n

# Output
print(greet(name))
print("Sum:", x + len(numbers))
`)
    if err != nil {
        fmt.Println("Error:", err)
    }
}

CLI Tool

Scriptling includes a command-line interface for running scripts directly:

# Install Task (build tool)
brew install go-task/tap/go-task

# Build CLI for current platform
task build

# Run scripts
./bin/scriptling script.py
echo 'print("Hello")' | ./bin/scriptling
./bin/scriptling --interactive

# Build for all platforms
task build-all
HTTP & MCP Server

The CLI can also run as an HTTP server with MCP (Model Context Protocol) support for LLM integration:

# Start HTTP server on port 8000
./bin/scriptling --server :8000 script.py

# With MCP tools for LLM integration
./bin/scriptling --server :8000 --mcp-tools ./tools script.py

# With TLS and authentication
./bin/scriptling --server :8443 --tls-generate --bearer-token secret script.py

See scriptling-cli/README.md for details.

Go API

Basic Usage
import (
    "github.com/paularlott/scriptling"
    "github.com/paularlott/scriptling/libloader"
    "github.com/paularlott/scriptling/stdlib"
)

p := scriptling.New()

// Register libraries as needed
stdlib.RegisterAll(p)  // Register all standard libraries
// Or register individual libraries:
// p.RegisterLibrary(stdlib.JSONLibraryName, stdlib.JSONLibrary)
// p.RegisterLibrary(stdlib.MathLibraryName, stdlib.MathLibrary)

// Execute code
result, err := p.Eval("x = 5 + 3")

// Exchange variables
p.SetVar("name", "Alice")
value, objErr := p.GetVarAsString("name")

// Register Go functions
p.RegisterFunc("custom", func(ctx context.Context, kwargs map[string]object.Object, args ...object.Object) object.Object {
    return &object.String{Value: "result"}
})

// Register Scriptling functions
p.RegisterScriptFunc("my_func", `
def my_func(x):
    return x * 2
my_func
`)

// Register Scriptling libraries
p.RegisterScriptLibrary("mylib", `
def add(a, b):
    return a + b
PI = 3.14159
`)

// Set up library loader for dynamic loading from filesystem
// Supports Python-style folder structure: libs/knot/groups.py → import knot.groups
loader := libloader.NewFilesystem("/app/libs")
p.SetLibraryLoader(loader)
Libraries
import (
    "github.com/paularlott/scriptling"
    "github.com/paularlott/scriptling/stdlib"
    "github.com/paularlott/scriptling/extlibs"
)

// Create interpreter
p := scriptling.New()

// Register all standard libraries
stdlib.RegisterAll(p)

// Or register individual standard libraries
p.RegisterLibrary(stdlib.JSONLibrary)
p.RegisterLibrary(stdlib.MathLibrary)

// Register additional custom libraries
p.RegisterLibrary(extlibs.RequestsLibrary)

// Register os and pathlib with security restrictions
extlibs.RegisterOSLibrary(p, []string{"/tmp", "/home/user/data"})
extlibs.RegisterPathlibLibrary(p, []string{"/tmp", "/home/user/data"})

// Import libraries programmatically (no need for import statements in scripts)
p.Import("json")

// Use in scripts
p.Eval(`
import requests
import os

response = requests.get("https://api.example.com/data")
data = json.parse(response["body"])  # json already imported via p.Import()
files = os.listdir("/tmp")
`)

Examples

See examples/ directory:

  • scripts/ - Script examples and Go integration
  • openai/scriptlingcoder/ - AI coding assistant with custom tools (inspired by nanocode)
  • mcp-tools/ - MCP tools for use with CLI MCP server

Run examples:

cd examples/scripts
go run main.go test_basics.py

# AI coding assistant (⚠️ executes AI-generated code)
cd examples/openai/scriptlingcoder
../../../bin/scriptling scriptlingcoder.py

Tools

  • o2s - OpenAPI to Scriptling converter. Generate HTTP client libraries from OpenAPI v3 specs

Documentation

Full documentation is available at scriptling.dev

Testing

# Run all tests
go test ./...

# Run benchmarks
go test -bench=. -run=Benchmark

License

MIT

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Get

func Get(script string) (*ast.Program, bool)

Get retrieves a cached program by script content

func Set

func Set(script string, program *ast.Program)

Set stores a program in the cache by script content

Types

type Kwargs

type Kwargs map[string]interface{}

Kwargs is a wrapper type to explicitly pass keyword arguments to CallFunction. Use this to distinguish between a map being passed as a dict argument vs kwargs.

type LibraryLoader added in v0.2.9

type LibraryLoader interface {
	Load(name string) (source string, found bool, err error)
	Description() string
}

LibraryLoader is the interface for loading libraries from various sources. This is an alias for the libloader.LibraryLoader interface to avoid import cycles.

type Scriptling

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

func New

func New() *Scriptling

func (*Scriptling) CallFunction

func (p *Scriptling) CallFunction(name string, args ...interface{}) (object.Object, error)

CallFunction calls a registered function by name with Go arguments. Args are Go types (int, string, etc.) that will be converted to Object. Returns object.Object - use .AsInt(), .AsString(), etc. to extract value.

Works with both Go-registered functions (via RegisterFunc) and script-defined functions.

To pass a map as a dict argument, use map[string]interface{} directly. To pass keyword arguments, wrap the map in Kwargs{}.

Example:

p.RegisterFunc("add", addFunc)
result, err := p.CallFunction("add", 10, 32)
sum, _ := result.AsInt()

// Pass a map as a dict argument
dataMap := map[string]interface{}{"key": "value"}
result, err := p.CallFunction("process", dataMap)

// With keyword arguments (use Kwargs wrapper)
result, err := p.CallFunction("format", "value", Kwargs{"prefix": ">>"})

func (*Scriptling) CallFunctionWithContext

func (p *Scriptling) CallFunctionWithContext(ctx context.Context, name string, args ...interface{}) (object.Object, error)

CallFunctionWithContext calls a registered function by name with Go arguments and a context. The context can be used for cancellation or timeouts. Args are Go types (int, string, etc.) that will be converted to Object. Returns object.Object - use .AsInt(), .AsString(), etc. to extract value.

Works with both Go-registered functions (via RegisterFunc) and script-defined functions.

To pass a map as a dict argument, use map[string]interface{} directly. To pass keyword arguments, wrap the map in Kwargs{}.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
result, err := p.CallFunctionWithContext(ctx, "add", 10, 32)
sum, _ := result.AsInt()

// Pass a map as a dict argument
dataMap := map[string]interface{}{"key": "value"}
result, err := p.CallFunctionWithContext(ctx, "process", dataMap)

// With keyword arguments (use Kwargs wrapper)
result, err := p.CallFunctionWithContext(ctx, "format", "value", Kwargs{"prefix": ">>"})

func (*Scriptling) CallMethod

func (p *Scriptling) CallMethod(obj object.Object, methodName string, args ...interface{}) (object.Object, error)

CallMethod calls a method on a Scriptling object (typically an Instance). The obj should be an object.Object (usually obtained from CreateInstance or script evaluation). Args are Go types that will be converted to Object. Returns object.Object - use .AsInt(), .AsString(), etc. to extract value.

Example:

instance, _ := p.CreateInstance("Counter", 10)
result, err := p.CallMethod(instance, "increment")
value, _ := result.AsInt()

func (*Scriptling) CallMethodWithContext

func (p *Scriptling) CallMethodWithContext(ctx context.Context, obj object.Object, methodName string, args ...interface{}) (object.Object, error)

CallMethodWithContext calls a method on a Scriptling object with a context. The context can be used for cancellation or timeouts.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
instance, _ := p.CreateInstance("Counter", 10)
result, err := p.CallMethodWithContext(ctx, instance, "increment")

func (*Scriptling) Clone added in v0.2.0

func (p *Scriptling) Clone() *Scriptling

Clone creates a new Scriptling interpreter that shares library registrations (both Go and script libraries) with the parent but starts with a fresh, isolated environment. Already-imported libraries are NOT copied; each clone re-evaluates script libraries on first import, so no mutable state is shared. Useful for per-request / multi-tenant isolation.

func (*Scriptling) CreateInstance

func (p *Scriptling) CreateInstance(className string, args ...interface{}) (object.Object, error)

CreateInstance creates an instance of a Scriptling class and returns it as an object.Object. The className should be the name of a class defined in the script or registered via a library. Args are Go types that will be converted to Object and passed to __init__.

Example:

p.Eval("class Counter:\n    def __init__(self, start=0):\n        self.value = start")
instance, err := p.CreateInstance("Counter", 10)
if err != nil {
    // handle error
}
// Now you can use CallMethod on this instance

func (*Scriptling) CreateInstanceWithContext

func (p *Scriptling) CreateInstanceWithContext(ctx context.Context, className string, args ...interface{}) (object.Object, error)

CreateInstanceWithContext creates an instance of a Scriptling class with a context. The context can be used for cancellation or timeouts.

Example:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
instance, err := p.CreateInstanceWithContext(ctx, "Counter", 10)

func (*Scriptling) EnableOutputCapture

func (p *Scriptling) EnableOutputCapture()

EnableOutputCapture enables capturing print output instead of sending to stdout

func (*Scriptling) Eval

func (p *Scriptling) Eval(input string) (object.Object, error)

Eval executes script without timeout (backwards compatible)

func (*Scriptling) EvalFile added in v0.2.0

func (p *Scriptling) EvalFile(path string) (object.Object, error)

EvalFile reads a file from disk and evaluates it.

func (*Scriptling) EvalWithContext

func (p *Scriptling) EvalWithContext(ctx context.Context, input string) (result object.Object, err error)

EvalWithContext executes script with context for timeout/cancellation. This method is safe against deep recursion (via call depth tracking) and recovers from panics during script execution.

func (*Scriptling) EvalWithTimeout

func (p *Scriptling) EvalWithTimeout(timeout time.Duration, input string) (object.Object, error)

EvalWithTimeout executes script with timeout

func (*Scriptling) GetOutput

func (p *Scriptling) GetOutput() string

GetOutput returns captured output and clears the buffer

func (*Scriptling) GetVar

func (p *Scriptling) GetVar(name string) (interface{}, object.Object)

func (*Scriptling) GetVarAsBool

func (p *Scriptling) GetVarAsBool(name string) (bool, object.Object)

func (*Scriptling) GetVarAsDict

func (p *Scriptling) GetVarAsDict(name string) (map[string]object.Object, object.Object)

func (*Scriptling) GetVarAsFloat

func (p *Scriptling) GetVarAsFloat(name string) (float64, object.Object)

func (*Scriptling) GetVarAsInt

func (p *Scriptling) GetVarAsInt(name string) (int64, object.Object)

func (*Scriptling) GetVarAsList

func (p *Scriptling) GetVarAsList(name string) ([]object.Object, object.Object)

func (*Scriptling) GetVarAsObject

func (p *Scriptling) GetVarAsObject(name string) (object.Object, error)

GetVarAsObject retrieves a variable from the environment as a scriptling Object.

func (*Scriptling) GetVarAsSet added in v0.2.0

func (p *Scriptling) GetVarAsSet(name string) (*object.Set, object.Object)

func (*Scriptling) GetVarAsString

func (p *Scriptling) GetVarAsString(name string) (string, object.Object)

Convenience methods for type-safe variable access

func (*Scriptling) GetVarAsTuple added in v0.2.0

func (p *Scriptling) GetVarAsTuple(name string) ([]object.Object, object.Object)

func (*Scriptling) Import

func (p *Scriptling) Import(names interface{}) error

Import imports a library into the current environment, making it available for use without needing an import statement in scripts

func (*Scriptling) ListVars added in v0.2.0

func (p *Scriptling) ListVars() []string

ListVars returns a sorted list of variable names in the current environment, excluding internal names (import builtin).

func (*Scriptling) LoadLibraryIntoEnv

func (p *Scriptling) LoadLibraryIntoEnv(name string, env *object.Environment) error

LoadLibraryIntoEnv loads a library into the specified environment. This is useful for loading libraries into cloned environments for background tasks. Returns an error if the library cannot be loaded.

func (*Scriptling) RegisterFunc

func (p *Scriptling) RegisterFunc(name string, fn func(ctx context.Context, kwargs object.Kwargs, args ...object.Object) object.Object, helpText ...string)

func (*Scriptling) RegisterLibrary

func (p *Scriptling) RegisterLibrary(lib *object.Library)

RegisterLibrary registers a new library that can be imported by scripts The library name is extracted from the library itself

func (*Scriptling) RegisterScriptFunc

func (p *Scriptling) RegisterScriptFunc(name string, script string) error

RegisterScriptFunc registers a function written in Scriptling The script should define a function and this method will extract it and register it by name

func (*Scriptling) RegisterScriptLibrary

func (p *Scriptling) RegisterScriptLibrary(name string, script string) error

RegisterScriptLibrary registers a library written in Scriptling The script should define functions/values that will be available when the library is imported

func (*Scriptling) ResetEnv added in v0.2.7

func (p *Scriptling) ResetEnv(keepKeys ...string)

ResetEnv clears all user-defined variables from the environment, keeping only the listed keys (plus the "import" builtin which is always preserved). Imported library dicts remain if their names are passed in keepKeys. This allows VM reuse across requests without re-registering libraries.

func (*Scriptling) SetInputReader

func (p *Scriptling) SetInputReader(r io.Reader)

SetInputReader sets a custom reader for input (e.g., for reading from a websocket)

func (*Scriptling) SetLibraryLoader added in v0.2.9

func (p *Scriptling) SetLibraryLoader(loader LibraryLoader)

SetLibraryLoader sets a loader for dynamically loading libraries. This is the preferred way to load libraries from various sources (filesystem, API, etc.) using the libloader package.

Example:

loader := libloader.NewChain(
    libloader.NewFilesystem("/app/libs"),
    libloader.NewAPI("https://api.example.com/libs"),
)
p.SetLibraryLoader(loader)

func (*Scriptling) SetObjectVar

func (p *Scriptling) SetObjectVar(name string, obj object.Object) error

SetObjectVar sets a variable in the environment from a scriptling Object. This is useful when you already have a scriptling object (like an Instance) and want to set it directly without converting from Go types.

func (*Scriptling) SetOutputWriter

func (p *Scriptling) SetOutputWriter(w io.Writer)

SetOutputWriter sets a custom writer for output (e.g., for streaming to a websocket or logger)

func (*Scriptling) SetSourceFile

func (p *Scriptling) SetSourceFile(name string)

SetSourceFile sets the source file name used in error messages. When set, errors will include the file name and line number for better debugging.

func (*Scriptling) SetVar

func (p *Scriptling) SetVar(name string, value interface{}) error

func (*Scriptling) UnsetVar added in v0.2.0

func (p *Scriptling) UnsetVar(name string)

UnsetVar removes a variable from the current environment.

Directories

Path Synopsis
Package build contains build-time version information for Scriptling
Package build contains build-time version information for Scriptling
Package evaliface provides an interface for calling functions from libraries without creating circular dependencies
Package evaliface provides an interface for calling functions from libraries without creating circular dependencies
examples
call_method command
extending command
logging command
openai/instance command
openai/shared command
scripts command
Package extlibs provides external libraries that need explicit registration
Package extlibs provides external libraries that need explicit registration
ai
mcp
Package libloader provides a flexible library loading system for Scriptling.
Package libloader provides a flexible library loading system for Scriptling.
Package lint provides code analysis functionality for Scriptling scripts.
Package lint provides code analysis functionality for Scriptling scripts.
mcp
scripts
tools
echo command
getversion command

Jump to

Keyboard shortcuts

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