Help us shape the future of Go! Take the 2026 Go Developer Survey

perl

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 31, 2026 License: MIT Imports: 22 Imported by: 0

README

go-perl

CI Go Reference

Perl 5 in pure Go — embed and run Perl anywhere Go runs. No cgo, no external perl, one static binary.

The interpreter is Perl 5.44.0 compiled to WebAssembly and then translated to Go — no wasm runtime is involved at run time — published as the perlwasm2go module this package builds on. The pure-Perl standard library ships inside the package as an embedded zip, so a go build produces a single self-contained binary that can run Perl.

package main

import (
	"context"
	"fmt"

	perl "github.com/goccy/go-perl"
)

func main() {
	p, err := perl.New(perl.Config{})
	if err != nil {
		panic(err)
	}
	defer p.Close()

	r, err := p.Eval(context.Background(), `join(",", map { $_ * 2 } 1..5)`)
	if err != nil {
		panic(err)
	}
	s, _ := perl.As[perl.ScalarValue](r.Value)
	fmt.Println(s.String()) // 2,4,6,8,10
}

Highlights

  • Pure Go, cross-compile anywhere: the interpreter is ordinary Go code — no cgo, no shared libraries, CGO_ENABLED=0 friendly. Plain GOOS/GOARCH cross compilation produces a single static binary for any target Go supports; CI cross-builds every package for linux, macOS, Windows, and even wasip1.
  • Batteries included: libperl plus every static XS extension (List::Util, POSIX, Socket, re, Storable, Encode, ...) and the pure-Perl stdlib are embedded in the binary (served from a private in-memory filesystem by default) — nothing to install or deploy next to your executable.
  • Native XS support, unmodified modules: real CPAN XS distributions compile against go-perl's XS SDK with their own Makefile.PL/Build.PL and load into the running interpreter as shared libraries — no source changes. Moose, Text::Xslate, Devel::NYTProf, DBD::mysql, PerlIO::utf8_strict, and keyword plugins like Syntax::Keyword::Match run as-is (exercised by the test suite). gperl xs build drives the whole pipeline with the embedded interpreter, so no host perl is needed even at build time; at run time loading is a dlopen, no compiler required. Native loading is available on linux/amd64, linux/arm64, and darwin/arm64.
  • Go and Perl call each other in-process: because the interpreter is Go code, the two languages meet at the Go ABI — crossing the boundary is a function call, not FFI or IPC. Call invokes named Perl subs from Go, Bind/BindClass expose Go functions to Perl as ordinary subs (which may call back into Perl, so round trips compose). Every value crosses typed — see Calling between Go and Perl.
  • PSGI: the psgi package serves PSGI applications straight from net/http, so an existing Plack app runs on a Go HTTP server — see examples/plack for a Mojolicious app carrying real traffic over a pool of warm workers.
  • Many interpreters, shared memory: the first New boots one interpreter and snapshots its memory; every later New maps that snapshot copy-on-write, so instances start without re-running interpreter init. Clone does the same from a live instance's current state — modules compiled once are inherited by every clone. Writable state is fully private per instance; the read-only bulk of memory is shared, so one process can hold many interpreters cheaply.
  • Sandboxed by default, capability-controlled: the translation to Go keeps the wasm linear-memory model and the WASI syscall boundary, so every filesystem, network, and process access funnels through hooks you control. Each Perl runs against a PRIVATE in-memory filesystem unless configured otherwise — Config.FS selects the backend from the fs package (fs.NewHostFS() passes through to the OS, matching perl; fs.DirFS scopes to one directory; fs.NewMemFS() or any custom backend plugs in the same way). The capability hooks are FAIL-CLOSED: with a zero Config, outbound connections (Dial), name resolution (Resolve), and subprocess spawns (Exec) are all denied — each capability is granted explicitly.
  • Cancellable: cancelling the context.Context passed to Eval/Call stops a runaway script at the next Perl opcode.
  • Go-style tooling for Perl: the gperl command runs scripts (gperl run, with perl(1) switches), builds self-contained binaries that embed a script and its vendored CPAN modules (gperl build), and compiles XS distributions (gperl xs build) — resolving cpanfile dependencies with an embedded cpanminus. Everything it does is also available programmatically via the gperl library.
  • Supply-chain verified: the Perl-derived artifacts are consumed as attested release binaries, verified against SLSA build provenance on every refresh and re-verified in CI — see Supply-chain verification.

Calling between Go and Perl

p, _ := perl.New(perl.Config{})
defer p.Close()
ctx := context.Background()

// Go -> Perl: call a named sub. Arguments and results are typed Values.
p.Eval(ctx, `sub add { my ($a, $b) = @_; $a + $b } 1;`)
sum, _ := p.Call(ctx, "add", perl.ValueOf(40), perl.ValueOf(2))
n, _ := perl.As[perl.ScalarValue](sum[0])
fmt.Println(n.Int()) // 42

// Perl objects cross as handles, not copies: the same object, its methods,
// and its state remain live on the Go side.
p.Eval(ctx, `package Counter; sub new { bless {n=>0}, shift } sub inc { $_[0]{n}++ }
             package main; sub counter { our $c ||= Counter->new } 1;`)
res, _ := p.Call(ctx, "counter")
obj, _ := perl.As[perl.RefValue](res[0]) // Class() == ("Counter", true)
obj.MethodCall(ctx, "inc")            // mutates the object Perl sees
p.Call(ctx, "counter")                // returns an Equal handle: same object

// Perl -> Go: bind a Go function as a Perl sub.
p.Bind("go_upper", func(args []perl.Value) ([]perl.Value, error) {
	s, err := perl.As[perl.ScalarValue](args[0])
	if err != nil {
		return nil, err
	}
	return []perl.Value{perl.ValueOf(strings.ToUpper(s.String()))}, nil
})
r, _ := p.Eval(ctx, `go_upper("hello")`)
s, _ := perl.As[perl.ScalarValue](r.Value)
fmt.Println(s.String()) // HELLO

Every value crosses the boundary typed, following Perl's own structure: a Value is one of the sealed concrete types — ScalarValue (SV), RefValue (RV), ArrayValue (AV), HashValue (HV), CodeValue (CV), GlobValue, IOValue — inspected either with a Go type switch or with perl.As[T] when the expected type is known up front; Kind() reports the runtime kind (mirroring reflect.Value.Kind). Nothing is stringified in transit and byte strings cross raw (Bytes() next to String()). Scalars coerce the way Perl would (Bool/Int/Float/String), references dereference with RefValue.Deref (the reflect.Value.Elem analog), and aggregates operate in place through ArrayValue/HashValue/CodeValue — real element accesses in the interpreter, so ties and overloads behave as in plain Perl. Perl references — blessed objects, array/hash/code refs — cross as identity-preserving handles, never serialized, so the same object stays the same object across any number of round trips. NewArray/NewHash materialize Go data as guest aggregates in one crossing, and an ArrayValue/HashValue in an argument list flattens exactly like f(@a, %h). Errors map to *PerlError / Perl die respectively.

Serving PSGI applications

The psgi package supplies the server side of PSGI on top of the bridge: the .psgi file's last evaluated value is the application, exactly as PSGI specifies, and every request builds the environment as a guest hash and calls it — no serialisation format sits between Go and the application. psgi.Server is a fixed pool of warm interpreter workers (the starman/starlet model): prepare ONE instance, and every other worker is a copy-on-write clone that adopts the same loaded application, so the loading work runs once no matter the worker count.

stdlib, _ := perl.ExtractStdlib()
p, _ := perl.New(perl.Config{FS: fs.NewHostFS(), StdlibDir: stdlib})
p.AddInc(ctx, "local/lib/perl5")
p.AddXSDir("local/xs/" + xs.ArchTag())
server, _ := psgi.New(p, 4, "app.psgi")
server.ListenAndServe(":8091") // or http.ListenAndServe(addr, server)

The gperl command

gperl is the perl-meets-go toolchain: it runs Perl programs on the embedded interpreter with go-style workflows.

gperl run script.pl [args...]   # resolve cpanfile deps, then run
gperl run -e '...' [switches]   # any perl(1) switch (-e/-I/-M/-l/...)
gperl build [-o out] script.pl  # self-contained Go binary: script +
                                # vendored modules + interpreter
gperl xs build dist...          # compile XS dists (dir or .tar.gz)
                                # against the native XS SDK into ./local/xs

gperl run and the binaries gperl build produces behave like perl: host filesystem, host environment, no sandbox (the library's zero Config is the opposite — deny by default). XS builds ride each distribution's own Makefile.PL/Build.PL with every perl in the pipeline being the embedded interpreter, so the only tools required are make and a C compiler; the artifacts land in local/xs/<goos>_<goarch>/ and load automatically.

Supply-chain verification

Two files in this repository are release artifacts of perl-wasm, not hand-written code: internal/perl.go (the generated bridge) and stdlib.zip (the embeddable stdlib). They are refreshed with:

make perl PERL_WASM_VERSION=v0.2.1

which downloads both from the perl-wasm release and verifies each against the release's SLSA build-provenance attestation (gh attestation verify). CI re-verifies them on every push and pull request. The interpreter itself comes in through the perlwasm2go Go module dependency, which applies the same attestation-verified vendoring on its side.

License

  • The Go source code of this repository is licensed under MIT.
  • stdlib.zip is not MIT: it is a repackaged subset of the Perl 5.44.0 standard library — a derivative work of Perl 5 — and keeps Perl's own dual license: the GNU General Public License version 1 or (at your option) any later version (LICENSE-GPL), or the "Artistic License" (LICENSE-ARTISTIC), at your choice. Both texts are vendored verbatim from the pinned Perl 5.44.0 sources.
  • gperl/cpanm is not MIT: it is the fatpacked cpanminus program, vendored verbatim from the App-cpanminus 1.7049 CPAN release and embedded so the XS build pipeline can resolve CPAN dependencies without downloading or consulting a host installation. It stays under its own terms — the same dual license as Perl itself (see the script's POD).
  • The perlwasm2go dependency (the translated interpreter) is likewise dual-licensed under Perl's terms in its own repository.
Using go-perl in your own project
  • As a library dependency (source distribution): your repository contains no Perl-derived bytes — only an import path and a go.mod entry. License your code however you like (MIT, proprietary, ...); no Perl license text needs to accompany it. Your users receive go-perl and perlwasm2go from their own origins, under their own licenses.
  • Shipping a compiled binary: the binary embeds the translated interpreter and stdlib.zip. The Artistic License expressly permits this: linking the complete interpreter into your executable is "a mere form of aggregation" (§5), and embedded use inside a (commercial) distribution "shall not be construed as a distribution of this Package" (§8) — so your own code does not inherit Perl's license and no Perl license text is required to accompany the binary, provided the interpreter is embedded complete and unmodified, you do not overtly expose Perl's interfaces to your end users, and you do not advertise Perl as your own product (§5, §9). If your product does expose Perl to end users (say, a Perl plugin system), add a short third-party notice pointing at Perl 5 and its dual license — customary and costless in any case.
  • Perl code you run on the interpreter, and its output, remain yours (§6).

This summary is not legal advice; the license texts govern.

Documentation

Overview

Package perl embeds the Perl interpreter — compiled to wasm and transpiled to pure Go — behind a sandboxed, multi-instance API. Each Perl value is an isolated interpreter: its own memory, filesystem view, and environment. No cgo, no system perl.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Adopt added in v0.2.0

func Adopt[T PerlValue](c *Perl, v T) (T, error)

Adopt returns v as seen by c, a clone of the instance v was obtained from. Cloning copies the guest memory wholesale — the handle registry included — so a reference obtained in the prototype BEFORE its first Clone designates the same (copied) value in every clone under the same handle. Scalars are instance-free and pass through unchanged.

func As added in v0.2.0

func As[T PerlValue](v Value) (T, error)

As extracts the concrete type T from v, for call sites that know the expected type up front; a kind mismatch is an error, never a panic. For runtime inspection use a type switch over the concrete types instead.

func ExitCode added in v0.2.0

func ExitCode(err error) (int, bool)

ExitCode reports whether err (from Eval, Call, or RunFile) is the guest terminating itself with a Perl-level exit(), and returns the exit status. An embedder running a whole script typically forwards the status to os.Exit after Closing the instance — a Call-surfaced exit leaves the interpreter cleanly unwound, so Close still flushes PerlIO and runs END blocks. (An exit during Eval reaches the host as a raw wasi proc_exit instead; that instance should only be Closed.)

func ExtractStdlib

func ExtractStdlib() (string, error)

ExtractStdlib unpacks the embedded standard library into a temporary directory (once per process) and returns its path. The result is cached, so repeated calls — e.g. one per Interpreter — are cheap. The returned directory is the value to pass as Config.StdlibDir (it becomes @INC via perl_new).

Types

type ArrayValue added in v0.2.0

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

ArrayValue is a Perl array (AV), viewed through the reference that reached Go. Operations run real Perl element accesses, so ties and overloads behave as in plain Perl; a die comes back as *PerlError.

func (ArrayValue) Index added in v0.2.0

func (v ArrayValue) Index(ctx context.Context, i int) (Value, error)

Index returns $array[i] (Perl indexing: negative counts from the end).

func (ArrayValue) Kind added in v0.2.0

func (v ArrayValue) Kind() Kind

Kind returns KindArray.

func (ArrayValue) Len added in v0.2.0

func (v ArrayValue) Len(ctx context.Context) (int, error)

Len returns scalar @array.

func (ArrayValue) Push added in v0.2.0

func (v ArrayValue) Push(ctx context.Context, vals ...Value) error

Push appends vals to the array. An ArrayValue or HashValue among vals flattens into its contents, exactly like push @a, @b, %h in Perl.

func (ArrayValue) Ref added in v0.2.0

func (v ArrayValue) Ref() RefValue

Ref returns the reference to this array (\@a).

func (ArrayValue) SetIndex added in v0.2.0

func (v ArrayValue) SetIndex(ctx context.Context, i int, val Value) error

SetIndex performs $array[i] = val.

func (ArrayValue) Values added in v0.2.0

func (v ArrayValue) Values(ctx context.Context) ([]Value, error)

Values returns every element in one crossing.

type CodeValue added in v0.2.0

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

CodeValue is a Perl subroutine (CV), viewed through the code reference that reached Go.

func (CodeValue) Call added in v0.2.0

func (v CodeValue) Call(ctx context.Context, args ...Value) ([]Value, error)

Call invokes the subroutine in list context and returns its return list. An ArrayValue or HashValue among args flattens into the argument list, exactly like f(@a, %h) in Perl; pass Ref() to pass the reference itself. A die comes back as *PerlError.

func (CodeValue) CallScalar added in v0.2.0

func (v CodeValue) CallScalar(ctx context.Context, args ...Value) (Value, error)

CallScalar invokes the subroutine in scalar context and returns its single result.

func (CodeValue) Kind added in v0.2.0

func (v CodeValue) Kind() Kind

Kind returns KindCode.

func (CodeValue) Ref added in v0.2.0

func (v CodeValue) Ref() RefValue

Ref returns the reference to this subroutine (\&f).

type Config

type Config struct {
	// StdlibDir is the path INSIDE the instance's filesystem (Config.FS)
	// holding the Perl standard library (the assembled lib/ tree); it
	// becomes @INC via perl_new. Empty defaults to "/" — right for the
	// library default and for NewStdlibMemFS. An instance on the host
	// filesystem (fs.NewHostFS) points this at a host directory, e.g. the
	// one ExtractStdlib returns.
	StdlibDir string
	// Env is the environment the guest sees (%ENV). nil means an empty
	// environment — the host process os.Environ() is NOT leaked.
	Env []string
	// Dial is the outbound-connection whitelist. It is called before each
	// connect with the network ("tcp"), the host NAME the guest asked for
	// (empty when it connected to a literal address), the resolved
	// dotted-quad IP, and the port; returning false denies the connection.
	// Receiving both the name and the IP lets policy match them jointly, and
	// the connection is made to the exact approved IP (no DNS rebinding).
	// FAIL-CLOSED: a nil Dial denies ALL outbound connections.
	Dial func(network, host, ip string, port int) bool
	// Resolve is the name-resolution whitelist. It is called with the host
	// being resolved before each lookup; returning false denies it.
	// FAIL-CLOSED: a nil Resolve denies ALL lookups (so a hostname
	// connection needs both Resolve and Dial; a literal-IP connection needs
	// only Dial).
	Resolve func(host string) bool
	// Stdin, when non-nil, backs the guest's fd 0 (STDIN). Defaults to an
	// empty stream (the host process stdin is NOT used).
	Stdin io.Reader
	// Stdout, when non-nil, receives the guest's fd 1 writes (os-level
	// stdout). Note: Perl-level print output is also captured into
	// Result.Stdout by the bridge; Stdout here is the raw fd sink.
	Stdout io.Writer
	// Stderr, when non-nil, receives the guest's fd 2 writes.
	Stderr io.Writer
	// MaxMemoryBytes, when > 0, caps this instance's wasm linear memory.
	// A guest allocation that would grow memory past this limit fails
	// (memory.grow returns -1) instead of growing the host process unbounded.
	// Rounded down to a multiple of the 64 KiB wasm page size; values below
	// the module's initial memory are ignored.
	MaxMemoryBytes int
	// MemoryReserveBytes, when > 0, is the initial linear-memory slice
	// capacity reserved for this instance. Reserving capacity up front makes
	// boot-time grows zero-copy reslices, dropping a freshly-booted
	// instance's resident memory. The reservation is virtual address space,
	// not resident memory. Only consulted when the instance boots privately;
	// an instance built on the copy-on-write process snapshot maps the
	// snapshot instead. When 0, a default headroom is used.
	MemoryReserveBytes int
	// Exec is the subprocess whitelist. It is called before every process
	// spawn with the executable path and full argv; returning false denies
	// the spawn. Spawning runs a HOST binary, so a sandbox that allows
	// subprocess should set this to a strict allow-list. FAIL-CLOSED: a nil
	// Exec denies ALL spawns.
	Exec func(path string, argv []string) bool
	// FS, when non-nil, is the filesystem backend this instance sees as its
	// entire guest filesystem ("/"). Every file operation is routed to it, so
	// giving two instances separate FS values isolates them completely.
	//
	// The go-perl/fs package provides the backends: fs.NewMemFS (private
	// in-memory), fs.NewHostFS (pass-through to the operating system's
	// filesystem - how the perl command behaves, and what gperl uses),
	// fs.DirFS (the host filesystem scoped to one directory), or any other
	// fs.FS implementation.
	//
	// When nil, the instance gets a PRIVATE in-memory filesystem
	// pre-loaded with the standard library (NewStdlibMemFS), so a library
	// embedding never touches the host disk unless explicitly asked to.
	FS FS
}

Config configures a new Perl instance created by New. The zero value is a usable, FULLY SANDBOXED interpreter: a private in-memory filesystem pre-loaded with the embedded standard library, an empty environment (the host process env is not leaked), sandboxed stdio, and every capability hook denying — no outbound connections, no name resolution, no subprocess spawns. Each capability is granted explicitly by setting its field.

type FS

type FS = goperlfs.FS

FS is the read/write filesystem backend a Perl instance is given via Config.FS. It is a write-capable superset of io/fs.FS; implementations live in the go-perl/fs package.

type File

type File = goperlfs.File

File is an open file returned by FS.OpenFile.

type GlobValue added in v0.2.0

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

GlobValue is a typeglob (GV) — a filehandle in most user code. Opaque for now: it can be inspected, held, and passed back to Perl; operations are added as needs arise.

func (GlobValue) Kind added in v0.2.0

func (v GlobValue) Kind() Kind

Kind returns KindGlob.

func (GlobValue) Ref added in v0.2.0

func (v GlobValue) Ref() RefValue

Ref returns the reference to this glob (\*g).

type GoFunc added in v0.2.0

type GoFunc func(args []Value) ([]Value, error)

GoFunc is a Go function callable from Perl. args is the Perl call's argument list as typed Values (references arrive as handle-bearing Values); the returned slice becomes the Perl call's return list. Returning an error makes the Perl call die with the error text.

fn may call back into the instance (Eval, Call, value operations) — the invoke lock is released for the callback's duration, and a nested entry from the handler's own goroutine is safe. What deadlocks is HOST-side lock cycles: a handler that blocks on something only the code that initiated the outer call can provide (a mutex it holds, a channel it will only service after the call returns) waits forever. Keep handlers self-contained or hand work to other goroutines without waiting on the outer caller.

type HashValue added in v0.2.0

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

HashValue is a Perl hash (HV), viewed through the reference that reached Go. Operations run real Perl accesses, so ties behave as in plain Perl.

func (HashValue) Delete added in v0.2.0

func (v HashValue) Delete(ctx context.Context, key string) error

Delete performs delete $hash{key}.

func (HashValue) Get added in v0.2.0

func (v HashValue) Get(ctx context.Context, key string) (Value, bool, error)

Get returns $hash{key} and whether the key exists.

func (HashValue) Keys added in v0.2.0

func (v HashValue) Keys(ctx context.Context) ([]string, error)

Keys returns keys %hash (Perl's hash order: unordered).

func (HashValue) Kind added in v0.2.0

func (v HashValue) Kind() Kind

Kind returns KindHash.

func (HashValue) Ref added in v0.2.0

func (v HashValue) Ref() RefValue

Ref returns the reference to this hash (\%h).

func (HashValue) Set added in v0.2.0

func (v HashValue) Set(ctx context.Context, key string, val Value) error

Set performs $hash{key} = val.

type IOValue added in v0.2.0

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

IOValue is a filehandle body (IO). Opaque for now, like GlobValue.

func (IOValue) Kind added in v0.2.0

func (v IOValue) Kind() Kind

Kind returns KindIO.

func (IOValue) Ref added in v0.2.0

func (v IOValue) Ref() RefValue

Ref returns the reference to this IO (*g{IO} as a reference).

type Kind added in v0.2.0

type Kind uint8

Kind is the runtime kind of a Value, one level finer than the concrete type for scalars (mirroring reflect.Value.Kind): a ScalarValue reports which representation its scalar holds, every other concrete type reports its single kind.

const (
	// KindUndef is an undefined scalar (SV: undef).
	KindUndef Kind = iota
	// KindBool is a core boolean scalar (SV: !!1 / !!0).
	KindBool
	// KindInt is an integer scalar (SV: IV).
	KindInt
	// KindFloat is a floating-point scalar (SV: NV).
	KindFloat
	// KindString is a string scalar (SV: PV — a byte string).
	KindString
	// KindRef is a reference (RV).
	KindRef
	// KindArray is an array (AV).
	KindArray
	// KindHash is a hash (HV).
	KindHash
	// KindCode is a subroutine (CV).
	KindCode
	// KindGlob is a typeglob (GV).
	KindGlob
	// KindIO is a filehandle body (IO).
	KindIO
)

func (Kind) String added in v0.2.0

func (k Kind) String() string

type MemFS

type MemFS = goperlfs.MemFS

MemFS is an in-memory read/write FS (see the go-perl/fs package).

func NewStdlibMemFS

func NewStdlibMemFS() (*MemFS, error)

NewStdlibMemFS returns an in-memory filesystem pre-loaded with the embedded Perl standard library at the root, ready to back an Interpreter:

fs := perl.NewStdlibMemFS()
interp, _ := perl.NewInterpreter(perl.Config{FS: fs}) // StdlibDir = "/"

Each call returns an independent FS, so interpreters built from separate NewStdlibMemFS() values share no filesystem state. This is also the filesystem an instance gets by default (Config with a nil FS).

type Pair added in v0.2.0

type Pair struct {
	K string
	V Value
}

Pair is one key/value entry for NewHash.

type Perl added in v0.2.0

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

Perl is one isolated Perl interpreter instance.

func New added in v0.2.0

func New(cfg Config) (*Perl, error)

New builds a fresh Perl instance, applies the sandbox config, and returns it ready to Eval. When the config permits, the instance is mapped copy-on-write from a process-wide snapshot of an already-initialized interpreter, so construction is cheap and the instances share the read-only bulk of their memory.

func (*Perl) AddInc added in v0.2.0

func (p *Perl) AddInc(ctx context.Context, dirs ...string) error

AddInc prepends dirs to the instance's @INC, so `use`/`require` resolve modules from them (a vendored local/lib/perl5 tree, an application's lib directory). Paths cross via the function bridge, so no Perl-source quoting is involved.

func (*Perl) AddXSDir added in v0.2.0

func (p *Perl) AddXSDir(dir string) error

AddXSDir registers the native XS modules in dir with the instance, the way AddInc registers a module tree. Each <Module-Name>.so in dir (the package separator spelled "-", the `gperl xs build` output naming) is registered under its module name; the architecture-specific project layout is dir = local/xs/<goos>_<goarch> (see the xs package's ArchTag). Registration is cheap (each module boots lazily on first `use`), and a missing directory simply registers nothing.

The loader itself lives in the go-perl/xs package; using AddXSDir from a binary that links neither psgi nor gperl requires importing it (`import _ "github.com/goccy/go-perl/xs"`).

func (*Perl) Bind added in v0.2.0

func (p *Perl) Bind(name string, fn GoFunc) error

Bind makes fn callable from Perl as the subroutine name (fully qualified, or defined into main:: when unqualified). The Perl side sees an ordinary sub: arguments arrive in Go as typed Values, the returned slice becomes the return list, and a Go error — or a contained panic — becomes a Perl die. Binding the same name again replaces the sub (the previous function is unreferenced but its id stays allocated).

func (*Perl) BindClass added in v0.2.0

func (p *Perl) BindClass(name string, methods map[string]GoFunc) error

BindClass defines a Perl class whose methods are implemented in Go: each entry becomes the sub name::method, so Perl's own method resolution — inheritance via @ISA, class methods, SUPER:: — applies unchanged. Every method receives the invocant as args[0]: the class name (a string scalar) for a class-method call, the blessed instance (a RefValue) for an instance call.

Unless methods defines "new", a default constructor is provided:

sub new { my $class = shift; bless { @_ }, $class }

so instances are ordinary Perl hash-based objects (readable from Go through the value operations, extensible by subclassing in Perl). A Go method that needs per-instance Go state can key a Go-side map by an id stored in the hash.

func (*Perl) Call added in v0.2.0

func (p *Perl) Call(ctx context.Context, name string, args ...Value) ([]Value, error)

Call invokes the named Perl subroutine in list context and returns its return list. name is a fully qualified sub name ("My::App::handler") or a main:: sub name ("handler"). An ArrayValue or HashValue among args flattens into the argument list, exactly like f(@a, %h) in Perl (pass Ref() to pass the reference itself). A Perl-level die (including calling a sub that does not exist) is returned as a *PerlError; other errors are host/transport failures or a context cancellation, which behaves exactly like Eval's.

func (*Perl) Clone added in v0.2.0

func (p *Perl) Clone() (*Perl, error)

Clone returns a new instance mapped copy-on-write from this instance's CURRENT state: every module compiled and every value that exists in p exists in the clone, and from here the two diverge privately, sharing the read-only bulk of their memory.

Take clones at rest — between requests, never while an Eval/Call is running in p. The first Clone builds the image (a full memory copy); subsequent clones of the same instance reuse it, so p must not run anything between clones of one batch either. The clone inherits p's configuration (filesystem, environment, hooks) and its Go bindings.

func (*Perl) Close added in v0.2.0

func (p *Perl) Close() error

Close finalizes the instance. The Perl must not be used afterward: every later Eval/Call errors, and outstanding *Ref handles become inert (their finalizers only touch host-side state). Idempotent.

func (*Perl) Eval added in v0.2.0

func (p *Perl) Eval(ctx context.Context, src string) (Result, error)

Eval compiles and runs src in this instance's persistent package (its main::) and returns the structured result. A Perl-level die is reported via Result.Error (*PerlError), not as Eval's own error; a Go error indicates a host/transport failure (a wasm trap, encoding problem, ...) or a context cancellation.

Cancelling ctx while the eval runs stops it at the next Perl opcode (the interpreter runs a pluggable run loop that tests a host-writable flag; see perl-wasm's bridge) and Eval returns ctx.Err(). A single long-running opcode — a pathological regex, a big sort — is not preempted until it returns to the run loop.

func (*Perl) NewArray added in v0.2.0

func (p *Perl) NewArray(ctx context.Context, vals ...Value) (ArrayValue, error)

NewArray materialises a fresh array in the guest holding vals (one crossing) and returns it. An ArrayValue or HashValue among vals flattens, like [@a, %h]; pass Ref() to nest a reference.

func (*Perl) NewHash added in v0.2.0

func (p *Perl) NewHash(ctx context.Context, pairs ...Pair) (HashValue, error)

NewHash materialises a fresh hash in the guest holding pairs (one crossing) and returns it.

func (*Perl) RunFile added in v0.2.0

func (p *Perl) RunFile(ctx context.Context, path string, incDirs []string, args []string) error

RunFile executes the Perl program at path — a guest path against Config.FS when one is set, a host path otherwise — with incDirs prepended to @INC and args as @ARGV. The program's output goes to the instance's configured stdio (not captured), and its package state persists on the instance afterwards.

An uncaught die comes back as *PerlError; a Perl-level exit() comes back as the error ExitCode recognizes, after which the instance must only be Closed. Both conventions let a command-line front end translate the outcome into a process exit status.

type PerlError added in v0.2.0

type PerlError struct {
	Message string
}

PerlError is a Perl-level die surfaced by Eval, Call, and the value operations: Message holds $@.

func (*PerlError) Error added in v0.2.0

func (e *PerlError) Error() string

type PerlValue added in v0.2.0

type PerlValue interface {
	ScalarValue | RefValue | ArrayValue | HashValue | CodeValue | GlobValue | IOValue
}

PerlValue enumerates the concrete Value types, as the constraint behind the typed extraction path (As and friends).

type RefValue added in v0.2.0

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

RefValue is a reference (RV): a handle to a Perl reference held alive in the interpreter's registry. Identity is preserved — the same Perl reference always surfaces with the same handle, and sending it back dereferences to the same referent.

func (RefValue) Can added in v0.2.0

func (v RefValue) Can(ctx context.Context, method string) (bool, error)

Can reports $ref->can(method) — whether the class provides the method.

func (RefValue) Class added in v0.2.0

func (v RefValue) Class() (string, bool)

Class returns the package the reference is blessed into, and whether it is blessed at all.

func (RefValue) Deref added in v0.2.0

func (v RefValue) Deref(ctx context.Context) (Value, error)

Deref dereferences the reference — the reflect.Value.Elem analog:

\$x  -> the ScalarValue $x (or a fresh RefValue for a ref-to-ref)
\@a  -> the ArrayValue @a
\%h  -> the HashValue %h
\&f  -> the CodeValue &f
\*g  -> the GlobValue *g

Aggregate derefs are view constructions (no guest call); a scalar deref reads the referent from the guest.

func (RefValue) Equal added in v0.2.0

func (v RefValue) Equal(o RefValue) bool

Equal reports whether two references designate the same Perl reference. The guest deduplicates handles by referent address, so identity comparison is handle comparison.

func (RefValue) Isa added in v0.2.0

func (v RefValue) Isa(ctx context.Context, class string) (bool, error)

Isa reports $ref->isa(class) — whether the reference's class is or inherits from class.

func (RefValue) Kind added in v0.2.0

func (v RefValue) Kind() Kind

Kind returns KindRef.

func (RefValue) MethodCall added in v0.2.0

func (v RefValue) MethodCall(ctx context.Context, method string, args ...Value) ([]Value, error)

MethodCall invokes $ref->method(args...) in list context and returns the return list, dispatched by Perl's own method resolution (inheritance, AUTOLOAD). A die comes back as *PerlError.

type Result added in v0.2.0

type Result struct {
	// Value is the evaluated expression's value (the last statement, in
	// scalar context), valid when Error is nil. See Value for the Perl
	// coercions its accessors apply.
	Value Value
	// Error is non-nil when the eval died: a Perl-level die/croak or a
	// compile error, carrying $@ as a *PerlError.
	Error error
	// Stdout / Stderr capture what the eval printed to STDOUT / STDERR
	// (the bridge redirects both onto in-memory scalars for the duration).
	Stdout string
	Stderr string
}

Result is the outcome of one Eval: the expression's value and the Perl-level error state. A die/croak/compile error is a fact ABOUT the evaluation, so it lives here as Error (*PerlError, $@'s text), not in Eval's own error return — that one reports host and transport failures.

type ScalarType added in v0.2.0

type ScalarType interface {
	bool | int | int32 | int64 | uint32 | float64 | string | []byte
}

ScalarType constrains the Go types ValueOf accepts as one Perl scalar.

type ScalarValue added in v0.2.0

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

ScalarValue is one Perl scalar: undef, a boolean, an integer, a float, or a byte string. The accessors coerce the way Perl itself would, totally — they never panic. Kind reports which representation the scalar holds.

func Undef added in v0.2.0

func Undef() ScalarValue

Undef is the undefined scalar.

func ValueOf added in v0.2.0

func ValueOf[T ScalarType](v T) ScalarValue

ValueOf builds the scalar for a Go value: a bool crosses as a Perl core boolean, integers as IVs, floats as NVs, a string as a utf8 character string, and a []byte as a raw byte string.

func (ScalarValue) Bool added in v0.2.0

func (v ScalarValue) Bool() bool

Bool applies Perl truth: undef, "", "0", and zero are false, everything else is true.

func (ScalarValue) Bytes added in v0.2.0

func (v ScalarValue) Bytes() []byte

Bytes is the scalar's raw byte string: for a string scalar the exact PV bytes (no re-encoding), otherwise the bytes of String().

func (ScalarValue) Float added in v0.2.0

func (v ScalarValue) Float() float64

Float numifies the scalar the way Perl does (a leading numeric prefix counts, anything else is 0).

func (ScalarValue) Int added in v0.2.0

func (v ScalarValue) Int() int64

Int numifies the scalar the way Perl does and truncates toward zero.

func (ScalarValue) Kind added in v0.2.0

func (v ScalarValue) Kind() Kind

Kind reports the scalar's representation: KindUndef, KindBool, KindInt, KindFloat, or KindString.

func (ScalarValue) String added in v0.2.0

func (v ScalarValue) String() string

String is the scalar's string form, as Perl would stringify it (a boolean true is "1", false is ""; undef is "").

type Value added in v0.2.0

type Value interface {
	Kind() Kind
	// contains filtered or unexported methods
}

Value is any Perl value. Sealed: the concrete types — ScalarValue, RefValue, ArrayValue, HashValue, CodeValue, GlobValue, IOValue — are the only implementations, so a type switch over them is exhaustive.

Directories

Path Synopsis
cmd
gperl command
gperl is the perl-meets-go command for go-perl: it runs Perl programs on the embedded (wasm2go-transpiled) interpreter with go-style tooling.
gperl is the perl-meets-go command for go-perl: it runs Perl programs on the embedded (wasm2go-transpiled) interpreter with go-style tooling.
Package fs provides the filesystem backends a Perl instance runs on.
Package fs provides the filesystem backends a Perl instance runs on.
Package gperl is the library behind the gperl command: the perl-meets-go toolchain for go-perl.
Package gperl is the library behind the gperl command: the perl-meets-go toolchain for go-perl.
Package internal holds the machinery under the public go-perl API: the generated wasm2go binding (perl.go), the per-instance bridge transport, the copy-on-write snapshot/clone plumbing, and the native-XS hook surface the xs subpackage drives.
Package internal holds the machinery under the public go-perl API: the generated wasm2go binding (perl.go), the per-instance bridge transport, the copy-on-write snapshot/clone plumbing, and the native-XS hook surface the xs subpackage drives.
Package psgi serves PSGI Perl web applications from Go's net/http.
Package psgi serves PSGI Perl web applications from Go's net/http.
Package xs loads host-native XS modules — shared libraries compiled with an ordinary C compiler against go-perl's XS SDK headers (sdk/include) — into a running Perl instance.
Package xs loads host-native XS modules — shared libraries compiled with an ordinary C compiler against go-perl's XS SDK headers (sdk/include) — into a running Perl instance.

Jump to

Keyboard shortcuts

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