Skip to content

Embedding in Go

Nubo’s root Go package exposes the interpreter without requiring imports from internal/.

Terminal window
go get github.com/nubolang/nubo@latest
package main
import (
"fmt"
"log"
"github.com/nubolang/nubo"
)
func main() {
app := nubo.New()
err := app.AddFunc("Double", nubo.GoFunc(func(value int) int {
return value * 2
}))
if err != nil {
log.Fatal(err)
}
result, err := app.ExecString(`return Double(21)`)
if err != nil {
log.Fatal(err)
}
if result != nil {
fmt.Println(result.Value())
}
}

The script calls Double as a normal function. The bridge builds argument and return metadata and converts supported values automatically.

APIPurpose
nubo.New()Create an application context using host files for source loading.
nubo.NewWithFS(files)Load scripts from an fs.FS.
app.ExecString(code)Execute a source string.
app.Exec(reader)Execute an io.Reader.
app.ExecFile(name)Execute a file through the source loader.
app.AddFunc(name, nubo.GoFunc(fn))Register a host function.
app.AddPackage(name, pkg)Register a Go-only or hybrid module.

Execution returns (language.Object, error). Check the error and check for a nil result before calling object methods. For primitive results, Value() gives the underlying Go value. It is not a universal recursive conversion of any object graph.

Functions may return no ordinary value or one ordinary value, optionally followed by error. A leading context.Context is injected from the Nubo call context.

Supported values include booleans, strings, signed and unsigned integers except uintptr, floats, arrays, slices, maps, and supported values inside any. Containers are converted rather than shared as live Go maps or slices. Numeric compatibility, integer overflow, array lengths, and conversion depth are checked.

Variadic functions, arbitrary pointers, Go structs, and nonempty interface APIs are not automatically exposed. Wrap host operations in functions accepting supported values. A general Go object-handle bridge is not currently part of this public conversion API.

Go reflection does not retain parameter identifiers: reflected arguments are named arg1, arg2, and so on. Prefer positional calls, or write a Nubo wrapper for meaningful named arguments and defaults.

err := app.AddFunc("LoadText", nubo.GoFunc(func(name string) (string, error) {
data, err := os.ReadFile(name)
return string(data), err
}))

This Go fragment additionally imports os and requires checking the registration error. Nubo callers can handle failures normally:

catch err {
println(LoadText("settings.txt"))
}
if !isNil(err) {
println(err.message)
}

The bridge also turns recoverable callback panics into Nubo errors. A host process failure or runtime-fatal Go condition is not a catchable script exception.

In a Go application containing scripts/main.nubo and any relative helpers:

package main
import (
"embed"
"log"
"github.com/nubolang/nubo"
)
//go:embed scripts
var files embed.FS
func main() {
app := nubo.NewWithFS(files)
if _, err := app.ExecFile("scripts/main.nubo"); err != nil {
log.Fatal(err)
}
}

Paths inside an fs.FS are slash-separated relative paths. Imports resolve relative to their importing source file. os.DirFS(...) works too. The chosen source filesystem does not redirect host callbacks or the OS/IO standard libraries, so it is not itself a sandbox.

For HTTP integration, see Go HTTP APIs. Do not assume a shared application context or arbitrary object graph supports concurrent request mutation without your own ownership and synchronization design.