Embedding in Go
Logos provides a clean embedding API that lets you run Logos scripts from Go applications. This makes it perfect for scripting, configuration, plugins, and game scripting.
Installation
Add Logos to your Go project:
go get github.com/codetesla51/logosQuick Start
Import the logos package and create a VM:
package main
import (
"fmt"
"github.com/codetesla51/logos/logos"
)
func main() {
vm := logos.New()
err := vm.Run(`
let message = "Hello from Logos!"
print(message)
`)
if err != nil {
fmt.Println("Error:", err)
}
}API Reference
logos.New()
Creates a new Logos VM with all capabilities enabled:
vm := logos.New()logos.NewWithConfig()
Creates a sandboxed VM with specific capabilities:
vm := logos.NewWithConfig(logos.SandboxConfig{
AllowFileIO: false, // Block file read/write/delete
AllowNetwork: false, // Block HTTP requests
AllowShell: false, // Block shell() command
AllowExit: false, // Block exit()
})vm.Run()
Evaluates a Logos script string. Returns an error if parsing fails or the script produces an error:
err := vm.Run(`
let x = 10
let y = 20
print(toStr(x + y))
`)
if err != nil {
fmt.Println("Script error:", err)
}vm.Register()
Registers a Go function callable from Logos scripts:
vm.Register("greet", func(args ...logos.Object) logos.Object {
name := args[0].(*logos.String).Value
return &logos.String{Value: "Hello, " + name + "!"}
})
vm.Register("add", func(args ...logos.Object) logos.Object {
a := args[0].(*logos.Integer).Value
b := args[1].(*logos.Integer).Value
return &logos.Integer{Value: a + b}
})
// Use them in Logos
vm.Run(`
print(greet("World")) // Hello, World!
print(add(5, 10)) // 15
`)vm.SetVar()
Sets a variable in the Logos environment. Supports Go types: int, int64, float64, string, bool, []interface{}, map[string]interface{}, nil:
vm.SetVar("count", 42)
vm.SetVar("name", "Logos")
vm.SetVar("enabled", true)
vm.SetVar("items", []interface{}{1, 2, 3})
vm.SetVar("config", map[string]interface{}{
"host": "localhost",
"port": 8080,
})
vm.Run(`
print("Count:", count)
print("Name:", name)
print("Items:", items)
`)vm.GetVar()
Gets a variable from the Logos environment, converted back to a Go value:
vm.Run(`
let result = 42 * 2
let greeting = "Hello"
`)
result := vm.GetVar("result")
fmt.Println(result) // 84
greeting := vm.GetVar("greeting")
fmt.Println(greeting) // Hello
missing := vm.GetVar("undefined")
fmt.Println(missing) // nilvm.Call()
Calls a Logos function by name with Go arguments:
vm.Run(`
let double = fn(x) {
return x * 2
}
let greet = fn(name) {
return "Hello, " + name + "!"
}
`)
// Call Logos functions from Go
result, err := vm.Call("double", 21)
fmt.Println(result) // 42
greeting, err := vm.Call("greet", "World")
fmt.Println(greeting) // Hello, World!Object Types
When registering functions, you work with Logos object types:
// Available types (re-exported from interpreter)
logos.Object // Interface for all types
logos.Integer // int64 value
logos.Float // float64 value
logos.String // string value
logos.Bool // bool value
logos.Array // []Object elements
logos.Table // map[string]Object pairs
logos.Null // null value
logos.Function // Logos function
logos.Builtin // Go builtin functionAccessing values:
vm.Register("processData", func(args ...logos.Object) logos.Object {
// Type assertions to access values
num := args[0].(*logos.Integer).Value // int64
text := args[1].(*logos.String).Value // string
flag := args[2].(*logos.Bool).Value // bool
// Return a table
return &logos.Table{Pairs: map[string]logos.Object{
"STRING:result": &logos.String{Value: text},
"STRING:count": &logos.Integer{Value: num * 2},
}}
})Sandboxing
Use NewWithConfig to create restricted VMs for untrusted scripts:
// Fully sandboxed - no file, network, shell, or exit access
vm := logos.NewWithConfig(logos.SandboxConfig{
AllowFileIO: false,
AllowNetwork: false,
AllowShell: false,
AllowExit: false,
})
// This will fail - file access is disabled
err := vm.Run(`
let data = fileRead("secrets.txt")
`)
// Error: file operations are disabled in sandbox mode
// This will fail - network access is disabled
err = vm.Run(`
let resp = httpGet("https://api.example.com")
`)
// Error: network operations are disabled in sandbox modeComplete Example: Basic Embedding
package main
import (
"fmt"
"github.com/codetesla51/logos/logos"
)
func main() {
// Create sandboxed VM
vm := logos.NewWithConfig(logos.SandboxConfig{
AllowFileIO: false,
AllowNetwork: false,
AllowShell: false,
AllowExit: false,
})
// Register custom functions
vm.Register("greet", func(args ...logos.Object) logos.Object {
name := args[0].(*logos.String).Value
return &logos.String{Value: "Hello from Go, " + name + "!"}
})
vm.Register("add", func(args ...logos.Object) logos.Object {
a := args[0].(*logos.Integer).Value
b := args[1].(*logos.Integer).Value
return &logos.Integer{Value: a + b}
})
// Run script
script := `
let name = "World"
print(greet(name))
let sum = add(5, 10)
print("Sum:", sum)
let final = "done"
`
err := vm.Run(script)
if err != nil {
fmt.Println("Error:", err)
return
}
// Get result from script
finalValue := vm.GetVar("final")
fmt.Println("Final value:", finalValue)
// Try blocked operation
err = vm.Run(`let data = fileRead("secret.txt")`)
if err != nil {
fmt.Println("Blocked:", err)
}
}Complete Example: Game Scripting
Logos is great for game scripting. Here's an example of exposing game state to scripts:
package main
import (
"fmt"
"github.com/codetesla51/logos/logos"
)
type Player struct {
Name string
Health int
Level int
}
var player = Player{Name: "Hero", Health: 100, Level: 1}
func main() {
vm := logos.NewWithConfig(logos.SandboxConfig{
AllowFileIO: false,
AllowNetwork: false,
AllowShell: false,
AllowExit: false,
})
// Expose player data to scripts
vm.Register("getPlayer", func(args ...logos.Object) logos.Object {
return &logos.Table{Pairs: map[string]logos.Object{
"STRING:name": &logos.String{Value: player.Name},
"STRING:health": &logos.Integer{Value: int64(player.Health)},
"STRING:level": &logos.Integer{Value: int64(player.Level)},
}}
})
vm.Register("heal", func(args ...logos.Object) logos.Object {
amount := int(args[0].(*logos.Integer).Value)
player.Health += amount
if player.Health > 100 {
player.Health = 100
}
return &logos.String{Value: fmt.Sprintf("Healed! Health: %d", player.Health)}
})
vm.Register("setName", func(args ...logos.Object) logos.Object {
player.Name = args[0].(*logos.String).Value
return &logos.Null{}
})
// Run game script
vm.Run(`
let p = getPlayer()
print("Player: " + p["name"] + " (HP: " + toStr(p["health"]) + ")")
setName("Warrior")
print(heal(20))
let updated = getPlayer()
print("Now: " + updated["name"] + " (HP: " + toStr(updated["health"]) + ")")
`)
}Complete Example: Environment Config
Use Logos for configuration scripts that read environment variables:
package main
import (
"os"
"github.com/codetesla51/logos/logos"
)
func main() {
vm := logos.NewWithConfig(logos.SandboxConfig{
AllowFileIO: false,
AllowNetwork: false,
AllowShell: false,
AllowExit: false,
})
// Register env access
vm.Register("env", func(args ...logos.Object) logos.Object {
key := args[0].(*logos.String).Value
value := os.Getenv(key)
if value == "" && len(args) > 1 {
return args[1] // Return default
}
if value == "" {
return &logos.Null{}
}
return &logos.String{Value: value}
})
// Set some env vars for demo
os.Setenv("APP_NAME", "MyApp")
os.Setenv("APP_ENV", "development")
vm.Run(`
let appName = env("APP_NAME", "DefaultApp")
let appEnv = env("APP_ENV", "production")
print("App: " + appName)
print("Environment: " + appEnv)
if appEnv == "development" {
print("Running in dev mode")
}
`)
}Error Handling
All methods that can fail return errors:
// vm.Run() returns error on parse or runtime errors
err := vm.Run(`let x = 10 / 0`)
if err != nil {
fmt.Println("Script error:", err)
}
// vm.Call() returns (result, error)
result, err := vm.Call("nonexistent", 1)
if err != nil {
fmt.Println("Call error:", err)
}Multiple VMs
Each VM is independent with its own environment:
vm1 := logos.New()
vm2 := logos.New()
vm1.SetVar("x", 42)
vm2.SetVar("x", 100)
// Each VM has its own state
fmt.Println(vm1.GetVar("x")) // 42
fmt.Println(vm2.GetVar("x")) // 100
// Functions registered on one VM don't affect others
vm1.Register("getValue", func(args ...logos.Object) logos.Object {
return &logos.Integer{Value: 1}
})
vm2.Register("getValue", func(args ...logos.Object) logos.Object {
return &logos.Integer{Value: 2}
})
result1, _ := vm1.Call("getValue") // 1
result2, _ := vm2.Call("getValue") // 2