Piko interpreter provider
Bytecode interpreter that powers Piko's dev-i (interpreted development) mode. It reads your .pk template source, compiles it to bytecode in memory, and runs it without invoking go build.
Overview
The interpreter implements templater_domain.InterpreterProviderPort against Piko's internal bytecode VM. The server accepts that port, not the concrete type, so you can swap the dev-i engine instead of hard-wiring one. In normal dev mode, every source change triggers a full Go rebuild. In dev-i mode, the interpreter reads the source, compiles to bytecode, and executes it directly, so parse and compile time bound the feedback loop instead of Go's link step.
The provider exposes Piko's stdlib and runtime symbols to interpreted programs, so templates call into the framework as if the compiler had built them. To reach interpreted code, your own application functions and types go through server.WithSymbols(...), which feeds the provider's RegisterSymbols.
This is an optional module. Only dev-i needs it. Compiled dev and prod builds do not import or wire it.
The provider pools and pre-warms interpreters. It builds one golden service with every symbol loaded, then each request takes a cheap clone from a pool instead of rebuilding the symbol surface.
Configuration
NewProvider takes optional functional options instead of a config struct. There is no Debug or WatchMode field. Piko's watchdog handles hot reload at a higher layer. Most setups need no options.
import (
"piko.sh/piko/wdk/interp/interp_provider_piko"
)
provider := interp_provider_piko.NewProvider(
interp_provider_piko.WithBytecodeEmission(".piko/bytecode"), // optional
)
WithBytecodeEmission(directory) enables an experimental dump of source and compiled bytecode to disk during compilation, useful when debugging register overflow or other VM-level compilation issues. Leave it disabled in normal use.
WithRestrictedSymbolSurface() locks down which packages an interpreted script may import. A script may import only the host's own registered namespaces, the script's local packages, and the language builtins. The compiler rejects every other import. The vendored stdlib (os, net, os/exec, syscall, reflect) and the Piko framework packages become unimportable, and the option denies unsafe outright. Reach for this option when interpreted templates come from an untrusted source.
provider := interp_provider_piko.NewProvider(
interp_provider_piko.WithRestrictedSymbolSurface(),
)
Bootstrap
Wiring is a single line with no glue. A method on *SSRServer attaches the interpreter, not a piko.With* option. WithInterpreterProvider(provider templater_domain.InterpreterProviderPort) takes the port, so the concrete provider satisfies it. Construct the server first, attach the provider, then drive dev-i mode.
The interpreter harness uses Generate, which builds the daemon infrastructure without starting it.
import (
"context"
"piko.sh/piko"
"piko.sh/piko/wdk/interp/interp_provider_piko"
)
server := piko.New(/* options */)
server.WithInterpreterProvider(interp_provider_piko.NewProvider())
err := server.Generate(context.Background(), piko.RunModeDevInterpreted)
Generate(ctx context.Context, runMode string) returns an error and leaves the daemon stopped. To serve HTTP requests directly, call the high-level Run instead, which takes the run mode alone.
server := piko.New(/* options */)
server.WithInterpreterProvider(interp_provider_piko.NewProvider())
err := server.Run(piko.RunModeDevInterpreted)
Run(runMode string) starts the server and blocks until a shutdown signal arrives. piko.RunModeDevInterpreted carries the value dev-i.
WithInterpreterProvider matters when the server runs in RunModeDevInterpreted (the dev-i mode). Skip the call and dev-i startup fails with an error that states that dev-i mode requires an interpreter provider and that the caller must invoke WithInterpreterProvider first.
See also
Framework docs:
- About interpreted mode, what
dev-iis for and when to reach for it. - About the watchdog, the file-change loop that drives reloads in development.
- About WASM, the same bytecode VM also runs in WebAssembly contexts behind a
js && wasmbuild tag.