Documentation
¶
Overview ¶
Package kukel embeds the Kukel language in Go programs: compile Kukel source once, run it in as many interpreters as needed, call its functions, and give it Go functions of your own.
app := kukel.NewModule("app").
Func("greet", func(in *kukel.Interp, args []kukel.Value) (kukel.Value, error) {
return kukel.String("hello, " + args[0].String()), nil
})
prog, err := kukel.CompileSource("rules.kkl", `
use app;
discount(total) { return total > 100 ? 0.1 : 0; }
main() { println(app::greet("world")); }`,
kukel.CompileOptions{Modules: []*kukel.Module{app}})
in, err := kukel.New(prog, kukel.Options{Stdout: os.Stdout, StepLimit: 1_000_000})
d, err := in.Call("discount", kukel.Int(250)) // 0.1
The language is described in docs/reference.md of the repository, the virtual machine in docs/vm.md, and this package in docs/embedding.md.
For code that is not trusted, CompileOptions limit what a program may use (modules, built-in functions such as readfile), Options.StepLimit bounds the work of every call, and CallContext stops a call when its context ends. Memory is not limited.
Example ¶
Compile a program once and call its functions from Go.
package main
import (
"fmt"
"github.com/maarten-boot/kukel"
)
func main() {
prog, err := kukel.CompileSource("pricing.kkl", `
const VAT = 0.21;
price(net, qty) {
local total = net * qty;
if (qty >= 10) total *= 0.9; // volume discount
return round(total * (1 + VAT) * 100) / 100.0;
}
main() {}`, kukel.CompileOptions{})
if err != nil {
fmt.Println(err)
return
}
in, err := kukel.New(prog, kukel.Options{})
if err != nil {
fmt.Println(err)
return
}
for _, qty := range []int64{1, 10} {
p, err := in.Call("price", kukel.Float(9.99), kukel.Int(qty))
fmt.Println(qty, p, err)
}
}
Output: 1 12.09 <nil> 10 108.79 <nil>
Index ¶
- Variables
- func Builtins() []string
- func StandardModules() []string
- type Class
- type CompileError
- type CompileOptions
- type Error
- type ExitError
- type Frame
- type Func
- type Interp
- func (in *Interp) Call(name string, args ...Value) (Value, error)
- func (in *Interp) CallContext(ctx context.Context, name string, args ...Value) (Value, error)
- func (in *Interp) CallValue(fn Value, args ...Value) (Value, error)
- func (in *Interp) Close()
- func (in *Interp) Generator(name string, next func() (Value, bool, error)) (Value, error)
- func (in *Interp) Global(name string) (Value, error)
- func (in *Interp) Run(args ...string) (int, error)
- func (in *Interp) RunContext(ctx context.Context, args ...string) (int, error)
- type Method
- type Module
- type Object
- type Options
- type Profile
- type ProfileOptions
- type Program
- type Value
- func (v Value) AsBool() (b, ok bool)
- func (v Value) AsFloat() (float64, bool)
- func (v Value) AsInt() (int64, bool)
- func (v Value) AsString() (string, bool)
- func (v Value) Get(key Value) Value
- func (v Value) Index(i int) Value
- func (v Value) IsNil() bool
- func (v Value) Keys() []Value
- func (v Value) Kind() string
- func (v Value) Len() int
- func (v Value) Object() (*Object, bool)
- func (v Value) Push(e Value) error
- func (v Value) SetKey(key, val Value) error
- func (v Value) String() string
- func (v Value) ToGo() (any, error)
- func (v Value) Truthy() bool
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var Nil = Value{}
Nil is the Kukel value nil.
var SystemBuiltins = []string{"readfile", "writefile", "appendfile", "readline", "getenv", "exit"}
SystemBuiltins are the built-in functions that reach outside the program: files (readfile, writefile, appendfile), standard input (readline), the environment (getenv) and ending the process (exit). CompileOptions{DenyBuiltins: kukel.SystemBuiltins} keeps them away from a program.
Functions ¶
func StandardModules ¶
func StandardModules() []string
StandardModules lists the modules written in Go that come with Kukel (db, events, fs, json, p9, re, strings, time, uuid, worker, zmq, and tk when built with -tags tk), less those that build tags left out.
Types ¶
type Class ¶ added in v0.9.0
type Class struct {
// contains filtered or unexported fields
}
Class is a class written in Go for a host module. Its objects carry a Go value (their state) and have no data members: everything goes through member functions. Kukel code makes objects with new name::C(...) when the class has a constructor, and may then derive classes from it; the derived constructor calls name::C::C(...) to set up the Go value. Without a constructor, objects come from the module's functions (Class.Object).
NewClass, New, Method, Static and Show panic on programming mistakes: a bad name, a nil function, a name used twice, or an addition after the module is in use.
func NewClass ¶ added in v0.9.0
NewClass makes an empty class. name must be an identifier.
Example ¶
A class written in Go: Kukel code uses it and derives a class from it that overrides a step (changed).
package main
import (
"fmt"
"os"
"github.com/maarten-boot/kukel"
)
func main() {
type counter struct{ n int64 }
counterClass := kukel.NewClass("Counter").
New(func(in *kukel.Interp, this *kukel.Object, args []kukel.Value) (kukel.Value, error) {
this.SetState(&counter{}) // the constructor sets the state
return kukel.Nil, nil
}).
Method("inc", func(in *kukel.Interp, this *kukel.Object, args []kukel.Value) (kukel.Value, error) {
c := this.State().(*counter)
c.n++
if _, err := this.Call(in, "changed", kukel.Int(c.n)); err != nil { // overridable
return kukel.Nil, err
}
return this.Value(), nil
}).
Method("changed", func(in *kukel.Interp, this *kukel.Object, args []kukel.Value) (kukel.Value, error) {
return kukel.Nil, nil
}).
Show(func(this *kukel.Object) string {
return fmt.Sprintf("app::Counter(%d)", this.State().(*counter).n)
})
app := kukel.NewModule("app").Class(counterClass)
prog, err := kukel.CompileSource("main.kkl", `use app;
class Loud : app::Counter { Loud(); changed(n); }
Loud::Loud() { app::Counter::Counter(); } // sets up the Go state
Loud::changed(n) { println("now", n); }
main() {
local c = new app::Counter();
c->inc()->inc();
println(c); // app::Counter(2)
new Loud()->inc(); // now 1
}`, kukel.CompileOptions{Modules: []*kukel.Module{app}})
if err != nil {
fmt.Println(err)
return
}
in, err := kukel.New(prog, kukel.Options{Stdout: os.Stdout})
if err != nil {
fmt.Println(err)
return
}
in.Run()
}
Output: app::Counter(2) now 1
func (*Class) New ¶ added in v0.9.0
New sets the constructor: new name::C(args) calls fn with the new object, whose Go value fn sets (this.SetState).
func (*Class) Object ¶ added in v0.9.0
Object makes an object of the class holding state, for a Func to return. The class must be in a module that a compiled program uses.
func (*Class) Show ¶ added in v0.9.0
Show sets how str() and println show an object of the class (without it: "name::C{}").
type CompileError ¶
type CompileError struct {
File string
Line, Col int
Message string
More []*CompileError
}
CompileError is an error in Kukel source. A compile can find several: the first is the CompileError itself, the others are in More (in the order of the source), and Error() gives all of them, one per line.
func (*CompileError) Error ¶
func (e *CompileError) Error() string
type CompileOptions ¶
type CompileOptions struct {
// Modules are the program's own modules, written in Go ("use app;").
// A module here hides a standard module of the same name.
Modules []*Module
// StandardModules lists the standard modules written in Go the program
// may use (nil: all of them; an empty, non-nil slice: none). Those
// written in Kukel (iter) are always there: they can do nothing the
// program's own code could not.
StandardModules []string
// DenyBuiltins lists built-in functions the program may not use
// (using one is a compile error); see SystemBuiltins.
DenyBuiltins []string
// Path lists directories where "use name;" looks for name.kkl (or
// name.kkc, a module compiled on its own), after the directory of the
// file that uses it (CompileFiles only).
Path []string
// NoFileModules forbids reading program files: "use" finds only
// modules written in Go and the standard modules written in Kukel
// (built into kukel), and worker::startfile is refused.
NoFileModules bool
}
CompileOptions say what a program may use beyond its own code.
type Error ¶
type Error struct {
Kind string // "TypeError", "ValueError", ...
Message string // the Error object's message
Value Value // the thrown Error object (Nil for the step limit and cancellation)
Trace []Frame // the calls, innermost first
// contains filtered or unexported fields
}
Error is an exception of a Kukel program that nothing caught, or the end of a call by its step limit (Kind "StepLimitError") or its context (Kind "CancelledError").
Example ¶
An exception that nothing in the program catches is an *Error: its kind, message and the calls it went through.
package main
import (
"errors"
"fmt"
"github.com/maarten-boot/kukel"
)
func main() {
prog, err := kukel.CompileSource("orders.kkl", `
total(orders) { return check(orders[0]) * 2; }
check(order) {
if (order < 0) throw new Error("negative order", "ValueError");
return order;
}
main() {}`, kukel.CompileOptions{})
if err != nil {
fmt.Println(err)
return
}
in, _ := kukel.New(prog, kukel.Options{})
_, err = in.Call("total", kukel.Vector(kukel.Int(-5)))
var e *kukel.Error
if errors.As(err, &e) {
fmt.Println(e.Kind, "-", e.Message)
for _, f := range e.Trace {
fmt.Printf(" %s (%s:%d)\n", f.Func, f.File, f.Line)
}
}
}
Output: ValueError - negative order check (orders.kkl:4) total (orders.kkl:2)
type ExitError ¶
type ExitError struct{ Code int }
ExitError is what Call, CallContext, CallValue and Global give when the program calls exit(code) (Run gives the code instead). A Func may return one to end the program as exit(code) does.
type Func ¶
Func is a Go function that Kukel code calls. in is the interpreter that calls it (to call back into Kukel with in.CallValue). The arguments are valid during the call only; keep copies (ToGo) of what is needed later. An error becomes a Kukel exception: an *Error keeps its kind (see Errorf), any other error has the kind "HostError".
type Interp ¶
type Interp struct {
// contains filtered or unexported fields
}
Interp is an interpreter running a Program: it has its own globals, output and state. Its methods must not be called from several goroutines at once; use one Interp per goroutine (they can share a Program). A Func may call back into the Interp that called it.
func New ¶
New makes an interpreter for p. It fails if p uses a built-in or module that its CompileOptions do not allow (a program from LoadProgram).
func (*Interp) Call ¶
Call calls the program's function name ("discount", or a module's: "util::f", also a private "util::_g", or "json::encode" of a Go module the program may use) with args and gives its result. A Kukel exception that nothing catches is an *Error; exit(code) gives an *ExitError.
func (*Interp) CallContext ¶
CallContext is Call, stopped when ctx ends: the program stops at its next step (a function written in Go that waits, such as time::sleep, returns at once) with an *Error of kind "CancelledError" that it cannot catch. This also holds for a call a Func makes back into Kukel. Workers started during the call are cancelled if ctx ends while it runs; Close cancels those that outlive it.
Example ¶
Run code that is not trusted: no files, no workers, a step budget and a time limit.
package main
import (
"context"
"fmt"
"strings"
"time"
"github.com/maarten-boot/kukel"
)
func main() {
opts := kukel.CompileOptions{
DenyBuiltins: kukel.SystemBuiltins,
StandardModules: []string{"json", "strings"},
NoFileModules: true,
}
if _, err := kukel.CompileSource("evil.kkl", `main() { println(readfile("/etc/passwd")); }`, opts); err != nil {
fmt.Println(err)
}
prog, _ := kukel.CompileSource("slow.kkl", `spin() { while (true) {} } main() {}`, opts)
in, _ := kukel.New(prog, kukel.Options{})
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
defer cancel()
_, err := in.CallContext(ctx, "spin")
fmt.Println(strings.SplitN(err.Error(), "\n", 2)[0])
}
Output: evil.kkl:1:18: built-in function readfile is not available in this program CancelledError: the program was cancelled: context deadline exceeded
func (*Interp) CallValue ¶
CallValue calls a Kukel function value (such as a callback a Func received) with args.
Example ¶
A Go function that gets a Kukel function as a callback calls it with CallValue.
package main
import (
"fmt"
"os"
"github.com/maarten-boot/kukel"
)
func main() {
app := kukel.NewModule("app").Func("each", func(in *kukel.Interp, args []kukel.Value) (kukel.Value, error) {
sum := int64(0)
for _, item := range []string{"pen", "ink"} {
r, err := in.CallValue(args[0], kukel.String(item))
if err != nil {
return kukel.Nil, err // an exception of the callback goes on
}
n, _ := r.AsInt()
sum += n
}
return kukel.Int(sum), nil
})
prog, err := kukel.CompileSource("each.kkl", `use app;
main() {
println(app::each(fn(item) { println("got", item); return len(item); }));
}`, kukel.CompileOptions{Modules: []*kukel.Module{app}})
if err != nil {
fmt.Println(err)
return
}
in, _ := kukel.New(prog, kukel.Options{Stdout: os.Stdout})
in.Run()
}
Output: got pen got ink 6
func (*Interp) Close ¶ added in v0.2.1
func (in *Interp) Close()
Close stops the interpreter for good: workers it started that are still running are cancelled, and what they write later is dropped; files the program opened with fs::open and did not close are closed. Calls after Close give an error. Close may be called more than once.
func (*Interp) Generator ¶ added in v0.37.0
Generator makes a generator (an object of the class Generator) whose values come from next, a Go function: it gives the next value, or false at the end; an error ends the generator and is thrown where the value was asked for, as a Func's error is. A member function iter() of a host class that gives one makes its objects iterable with for. name is how the generator shows ("shop::Cart::iter"). Kukel code steps it as one written in Kukel, but send gives it no value and close only ends it.
func (*Interp) Global ¶
Global gives the value of a global of the program (a variable, function or class), after the global initializers have run.
Example ¶
Read a global variable after the global initializers have run.
package main
import (
"fmt"
"github.com/maarten-boot/kukel"
)
func main() {
prog, err := kukel.CompileSource("config.kkl", `
global limits = {"orders": 10, "items": 3 * 4};
main() {}`, kukel.CompileOptions{})
if err != nil {
fmt.Println(err)
return
}
in, _ := kukel.New(prog, kukel.Options{})
limits, err := in.Global("limits")
fmt.Println(limits.Get(kukel.String("items")), err)
_, err = in.Global("nosuch")
fmt.Println(err)
}
Output: 12 <nil> kukel: the program has no global nosuch
func (*Interp) Run ¶
Run runs the program's main, with args as its parameter (a vector of strings) if it has one, and gives the exit code: main's result if that is an int, else 0; or the code of exit(code). If the program fails, Run gives 1 and the error. The global initializers run first, the first time anything is called; if they throw an exception, every call gives it (a step limit or cancellation lets the next call run them again). Run does not wait for workers the program started (see Close).
type Method ¶ added in v0.9.0
Method is a member function written in Go: this is the object it is called on (also an object of a Kukel class derived from the Go class). For the constructor (Class.New), it sets the object's Go value with this.SetState; its result is not used.
type Module ¶
type Module struct {
// contains filtered or unexported fields
}
Module is a module written in Go for one program: Kukel code that says "use name;" reaches its functions and constants as name::f and name::K. Add everything before the module is first used in CompileOptions.
func NewModule ¶
NewModule makes an empty module. name must be a Kukel identifier that is not a keyword. NewModule, Func and Const panic on such programming mistakes: a bad name, a nil function, a name used twice, a constant that is not nil, a boolean, a number or a string, or an addition after the module is in use.
Example ¶
Give Kukel code functions written in Go, in a module of its own.
package main
import (
"fmt"
"os"
"github.com/maarten-boot/kukel"
)
func main() {
stock := map[string]int64{"pen": 12, "ink": 0}
shop := kukel.NewModule("shop").
Const("currency", kukel.String("EUR")).
Func("stock", func(in *kukel.Interp, args []kukel.Value) (kukel.Value, error) {
item, ok := args[0].AsString()
if !ok {
return kukel.Nil, kukel.Errorf("TypeError", "an item is a string")
}
n, known := stock[item]
if !known {
return kukel.Nil, kukel.Errorf("KeyError", "no item %q", item)
}
return kukel.Int(n), nil
})
prog, err := kukel.CompileSource("shop.kkl", `use shop;
main() {
for (local item in ["pen", "ink", "cap"]) {
try {
println(item, shop::stock(item) > 0 ? "in stock" : "sold out");
} catch (e) {
println(item, e->kind, e->message);
}
}
println("prices in", shop::currency);
}`, kukel.CompileOptions{Modules: []*kukel.Module{shop}})
if err != nil {
fmt.Println(err)
return
}
in, _ := kukel.New(prog, kukel.Options{Stdout: os.Stdout})
in.Run()
}
Output: pen in stock ink sold out cap KeyError no item "cap" prices in EUR
func (*Module) Class ¶ added in v0.9.0
Class adds a class written in Go (see NewClass); Kukel code reaches it as name::C: new name::C(...), class D : name::C, name::C::f(...).
type Object ¶ added in v0.9.0
type Object struct {
// contains filtered or unexported fields
}
Object is an object, as a Method gets it (this) or Value.Object gives it.
func (*Object) Call ¶ added in v0.9.0
Call calls the member function name on the object, as p->name(args) does: a Kukel class derived from a Go class may override it.
func (*Object) ClassName ¶ added in v0.9.0
ClassName gives the name of the object's class ("app::Counter", or the Kukel class derived from it).
type Options ¶
type Options struct {
Stdout io.Writer // what print writes to; nil: discarded
Stderr io.Writer // what eprint writes to; nil: discarded
Stdin io.Reader // what readline reads; nil: empty
// Program is what program() gives: the program's name or path, as
// C's argv[0]; "" by default. Workers started with worker::start get
// it too.
Program string
// StepLimit bounds each call from Go (Run, Call, CallValue): after that
// many steps (backward jumps, calls and throws, so every loop counts)
// the call ends with an *Error of kind "StepLimitError" that the
// program cannot catch. Calls a Func makes back into Kukel share the
// budget of the call that runs the Func. 0: no limit. Workers the
// program starts get the same limit for their whole run.
StepLimit int
// Profile, if not nil, profiles the calls of the interpreter from Go
// (each outermost Run, Call, ...: the time between them does not
// count), and the workers they start.
Profile *Profile
}
Options configure an interpreter.
Example (StepLimit) ¶
A step limit stops code that runs too long (each call from Go gets the whole budget), and a panic in a Go function is a HostError for Kukel.
package main
import (
"errors"
"fmt"
"github.com/maarten-boot/kukel"
)
func main() {
app := kukel.NewModule("app").Func("crash", func(in *kukel.Interp, args []kukel.Value) (kukel.Value, error) {
var m map[string]int
m["x"] = 1 // panics
return kukel.Nil, nil
})
prog, err := kukel.CompileSource("limits.kkl", `use app;
spin() { while (true) {} }
count(n) { local i = 0; while (i < n) i++; return i; }
crash() { try { app::crash(); } catch (e) { return e->kind; } }
main() {}`, kukel.CompileOptions{Modules: []*kukel.Module{app}})
if err != nil {
fmt.Println(err)
return
}
in, _ := kukel.New(prog, kukel.Options{StepLimit: 10000})
_, err = in.Call("spin")
var e *kukel.Error
if errors.As(err, &e) {
fmt.Println(e.Kind + ": " + e.Message)
}
fmt.Println(in.Call("count", kukel.Int(5000))) // a new budget for this call
fmt.Println(in.Call("crash"))
}
Output: StepLimitError: more than 10000 steps 5000 <nil> HostError <nil>
type Profile ¶ added in v0.30.0
type Profile struct {
// contains filtered or unexported fields
}
Profile collects where the time of interpreters' calls goes: which functions and lines of the program, with their calls (also of workers). Give it to New in Options.Profile; then the calls of that interpreter from Go (Run, Call, ...) are profiled, and WriteReport and WritePprof show what they gave (of a call or worker still running: its samples so far, its calls up to its latest sample). Several interpreters may share a Profile (the first is "main", the others "interp 2", ...); lines of their programs stay apart also when two have a file of the same name (the report then names the interpreter). Its methods are safe for concurrent use.
Example ¶
Profile the calls of an interpreter: where their time goes, by function and line, with call counts (the time between the calls does not count).
package main
import (
"os"
"github.com/maarten-boot/kukel"
)
func main() {
prog, _ := kukel.CompileSource("work.kkl", `
work(n) { local t = 0; for (local i = 0; i < n; i++) t += i % 7; return t; }
main() {}`, kukel.CompileOptions{})
prof := kukel.NewProfile(kukel.ProfileOptions{})
in, _ := kukel.New(prog, kukel.Options{Profile: prof})
for range 3 {
in.Call("work", kukel.Int(100000))
}
prof.WriteReport(os.Stderr) // or WritePprof, for go tool pprof
}
Output:
func NewProfile ¶ added in v0.30.0
func NewProfile(opts ProfileOptions) *Profile
NewProfile makes a Profile.
func (*Profile) WritePprof ¶ added in v0.30.0
WritePprof writes the profile in Go's pprof format, for go tool pprof (flame graphs, call graphs, the source with the time of each line).
func (*Profile) WriteReport ¶ added in v0.30.0
WriteReport writes the text report kukel profile writes: the functions by their own time and their total time (with what they call), with their calls; the slowest lines with their source; the calls of built-ins and Go functions.
type ProfileOptions ¶ added in v0.30.0
type ProfileOptions struct {
// Interval is the time between samples (0 or less: a millisecond).
Interval time.Duration
// CPU leaves out the time spent waiting in the standard Go functions
// that wait (time::sleep, events::run(), receive, readline, ...): by
// default each sample weighs the time since the one before, so that
// waiting shows on the line that waits. The time of the Go functions
// of your modules counts in full, also when they wait.
CPU bool
}
ProfileOptions configure a Profile.
type Program ¶
type Program struct {
// contains filtered or unexported fields
}
Program is a compiled program. It does not change when it runs, so any number of interpreters (New) may run it, also at the same time.
func CompileFiles ¶
func CompileFiles(files []string, opts CompileOptions) (*Program, error)
CompileFiles compiles source files as one program, with the modules they use. An error in the source is a *CompileError.
func CompileSource ¶
func CompileSource(name, src string, opts CompileOptions) (*Program, error)
CompileSource compiles a program given as text; name is used in messages. Modules written in Kukel are looked for in opts.Path only.
func LoadProgram ¶
func LoadProgram(data []byte, opts CompileOptions) (*Program, error)
LoadProgram reads a program from the bytes of a .kkc file (Encode, or kukel build). opts say what it may use; New refuses a program that uses more.
Example ¶
Compile once, keep the bytes of a .kkc file, and load them later without compiling. A .kkc file names the functions of host modules but does not contain them: LoadProgram must be given the modules again.
package main
import (
"fmt"
"github.com/maarten-boot/kukel"
)
func main() {
host := func() *kukel.Module {
return kukel.NewModule("host").Func("double", func(in *kukel.Interp, args []kukel.Value) (kukel.Value, error) {
n, _ := args[0].AsInt()
return kukel.Int(2 * n), nil
})
}
prog, err := kukel.CompileSource("calc.kkl", `use host;
calc(n) { return host::double(n) + 1; }
main() {}`, kukel.CompileOptions{Modules: []*kukel.Module{host()}})
if err != nil {
fmt.Println(err)
return
}
data, err := prog.Encode() // to store, as kukel build would
if err != nil {
fmt.Println(err)
return
}
loaded, err := kukel.LoadProgram(data, kukel.CompileOptions{Modules: []*kukel.Module{host()}})
if err != nil {
fmt.Println(err)
return
}
in, _ := kukel.New(loaded, kukel.Options{})
fmt.Println(in.Call("calc", kukel.Int(20)))
}
Output: 41 <nil>
type Value ¶
type Value struct {
// contains filtered or unexported fields
}
Value is a Kukel value: nil, a boolean, an int (64 bits), a float, a string (bytes), a vector, a map, a function, a class or an object. The zero Value is nil.
Vectors, maps and objects are shared, not copied: a Value taken from an interpreter refers to the interpreter's data. Use it only while that interpreter does not run on another goroutine.
func FromGo ¶
FromGo makes a Value of a Go value: nil, any bool, integer, float or string type, []byte (a string), a Value, a slice or array (a vector) or a map with string keys (a map, keys in sorted order), also nested.
Example ¶
Convert between Go and Kukel values.
package main
import (
"fmt"
"github.com/maarten-boot/kukel"
)
func main() {
v, _ := kukel.FromGo(map[string]any{"name": "Kukel", "tags": []string{"small", "fun"}})
fmt.Println(v, v.Get(kukel.String("tags")).Len())
g, _ := v.ToGo()
fmt.Printf("%v\n", g)
}
Output: {"name": "Kukel", "tags": ["small", "fun"]} 2 map[name:Kukel tags:[small fun]]
func MapOf ¶
MapOf makes a new map of m, with the keys in sorted order (Kukel maps keep the order in which keys were added).
func (Value) Index ¶
Index gives element i of a vector (Nil if v is not a vector or i is out of range).
func (Value) Kind ¶
Kind is what typeof gives in Kukel: "nil", "bool", "int", "float", "string", "vector", "map", "function", "class" or "object".
func (Value) Object ¶ added in v0.9.0
Object gives v as an object (of a class written in Kukel or in Go).
func (Value) ToGo ¶
ToGo gives v as a Go value: nil, bool, int64, float64, string, []any for a vector and map[string]any for a map (keys as str() shows them), nested. A function, class or object stays a Value. A vector or map that contains itself is an error, and so is a map with two keys that give the same Go key (1 and "1").
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
kukel
command
Command kukel compiles and runs Kukel programs.
|
Command kukel compiles and runs Kukel programs. |
|
examples
|
|
|
embed
command
Command embed is an example of a Go program that runs Kukel code (see docs/embedding.md): it prices orders, and the discount of each order comes from rules that users write as a Kukel script.
|
Command embed is an example of a Go program that runs Kukel code (see docs/embedding.md): it prices orders, and the discount of each order comes from rules that users write as a Kukel script. |
|
internal
|
|
|
ast
Package ast declares the syntax tree of Kukel programs (syntax.ebnf, sections 1-3).
|
Package ast declares the syntax tree of Kukel programs (syntax.ebnf, sections 1-3). |
|
builtins
Package builtins implements Kukel's built-in functions.
|
Package builtins implements Kukel's built-in functions. |
|
bytecode
Package bytecode defines the instruction set of the Kukel stack machine and the compiled program image.
|
Package bytecode defines the instruction set of the Kukel stack machine and the compiled program image. |
|
compiler
Package compiler translates syntax trees into bytecode for the stack machine.
|
Package compiler translates syntax trees into bytecode for the stack machine. |
|
debugger
Package debugger is the core of Kukel's debuggers (TODO.md Q60, Q77): breakpoints, stepping, the stop at an exception nothing catches, and reading and changing the program's values.
|
Package debugger is the core of Kukel's debuggers (TODO.md Q60, Q77): breakpoints, stepping, the stop at an exception nothing catches, and reading and changing the program's values. |
|
diag
Package diag defines the error type shared by the lexer, parser and compiler: a message at a source position.
|
Package diag defines the error type shared by the lexer, parser and compiler: a message at a source position. |
|
format
Package format formats Kukel source (kukel fmt): it re-indents lines and normalizes the spaces between tokens.
|
Package format formats Kukel source (kukel fmt): it re-indents lines and normalizes the spaces between tokens. |
|
lexer
Package lexer turns Kukel source text into tokens (syntax.ebnf, section 4).
|
Package lexer turns Kukel source text into tokens (syntax.ebnf, section 4). |
|
loader
Package loader reads the source files of a program together with the modules they use ("use name;"), directly or through other modules.
|
Package loader reads the source files of a program together with the modules they use ("use name;"), directly or through other modules. |
|
modules/dbmod
Package dbmod is the native module "db": SQL databases through Go's database/sql: SQLite (modernc.org/sqlite), PostgreSQL (jackc/pgx) and MySQL or MariaDB (go-sql-driver/mysql), all pure Go, so no C library is needed.
|
Package dbmod is the native module "db": SQL databases through Go's database/sql: SQLite (modernc.org/sqlite), PostgreSQL (jackc/pgx) and MySQL or MariaDB (go-sql-driver/mysql), all pure Go, so no C library is needed. |
|
modules/eventsmod
Package eventsmod is the native module "events": an event loop that calls Kukel functions when timers expire or other modules post events.
|
Package eventsmod is the native module "events": an event loop that calls Kukel functions when timers expire or other modules post events. |
|
modules/fsmod
Package fsmod is the native module "fs": directories and files of the local file system (TODO.md Q61).
|
Package fsmod is the native module "fs": directories and files of the local file system (TODO.md Q61). |
|
modules/jsonmod
Package jsonmod is the native module "json": encode Kukel values as JSON text and decode JSON text into Kukel values.
|
Package jsonmod is the native module "json": encode Kukel values as JSON text and decode JSON text into Kukel values. |
|
modules/p9mod
Package p9mod is the native module "p9": the 9P file protocol of Plan 9 and Linux (TODO.md Q62), as a client and as a server, in both versions: 9P2000 and 9P2000.L.
|
Package p9mod is the native module "p9": the 9P file protocol of Plan 9 and Linux (TODO.md Q62), as a client and as a server, in both versions: 9P2000 and 9P2000.L. |
|
modules/remod
Package remod is the native module "re": regular expressions, with Go's RE2 syntax (TODO.md Q59).
|
Package remod is the native module "re": regular expressions, with Go's RE2 syntax (TODO.md Q59). |
|
modules/stringsmod
Package stringsmod is the native module "strings": a Builder class that builds a long string from many pieces.
|
Package stringsmod is the native module "strings": a Builder class that builds a long string from many pieces. |
|
modules/termmod
Package termmod is the native module "term": the terminal a key at a time, and its size (TODO.md Q99), for programs that draw the screen themselves with ANSI escape codes, such as examples/vi.kkl.
|
Package termmod is the native module "term": the terminal a key at a time, and its size (TODO.md Q99), for programs that draw the screen themselves with ANSI escape codes, such as examples/vi.kkl. |
|
modules/timemod
Package timemod is the native module "time": the class time::Time (an instant with a time zone), and sleep.
|
Package timemod is the native module "time": the class time::Time (an instant with a time zone), and sleep. |
|
modules/tkmod
Package tkmod is the native module "tk": desktop windows with Tk widgets whose event handlers are Kukel functions (TODO.md Q67).
|
Package tkmod is the native module "tk": desktop windows with Tk widgets whose event handlers are Kukel functions (TODO.md Q67). |
|
modules/utf8mod
Package utf8mod is the native module "utf8": text as characters (code points) instead of bytes (TODO.md Q93).
|
Package utf8mod is the native module "utf8": text as characters (code points) instead of bytes (TODO.md Q93). |
|
modules/uuidmod
Package uuidmod is the native module "uuid": new UUIDs as strings (TODO.md Q58).
|
Package uuidmod is the native module "uuid": new UUIDs as strings (TODO.md Q58). |
|
modules/workermod
Package workermod is the native module "worker": workers are Kukel interpreters that run in parallel, each on its own goroutine, and talk to the interpreter that started them only through messages.
|
Package workermod is the native module "worker": workers are Kukel interpreters that run in parallel, each on its own goroutine, and talk to the interpreter that started them only through messages. |
|
modules/zmqmod
Package zmqmod is the native module "zmq": ZeroMQ sockets, to talk with other programs (in any language) and other machines.
|
Package zmqmod is the native module "zmq": ZeroMQ sockets, to talk with other programs (in any language) and other machines. |
|
native
Package native lets Go code define Kukel modules: functions, constants and native classes, whose objects carry a Go value and whose member functions are written in Go.
|
Package native lets Go code define Kukel modules: functions, constants and native classes, whose objects carry a Go value and whose member functions are written in Go. |
|
parser
Package parser builds the syntax tree of a Kukel source file (syntax.ebnf, sections 1-3) by recursive descent.
|
Package parser builds the syntax tree of a Kukel source file (syntax.ebnf, sections 1-3) by recursive descent. |
|
profile
Package profile turns what a vm.Profiler collected into reports (TODO.md Q78): a text report of the functions and lines that took the time, for kukel profile and the Go API.
|
Package profile turns what a vm.Profiler collected into reports (TODO.md Q78): a text report of the functions and lines that took the time, for kukel profile and the Go API. |
|
stdlib
Package stdlib holds the standard modules written in Kukel (Q88), built into kukel: "use iter;" finds iter.kkl here, after the modules written in Go and before files on disk.
|
Package stdlib holds the standard modules written in Kukel (Q88), built into kukel: "use iter;" finds iter.kkl here, after the modules written in Go and before files on disk. |
|
token
Package token defines the lexical tokens of Kukel (syntax.ebnf, section 4).
|
Package token defines the lexical tokens of Kukel (syntax.ebnf, section 4). |
|
value
Package value defines Kukel's runtime values and the semantics of the operators on them.
|
Package value defines Kukel's runtime values and the semantics of the operators on them. |
|
vm
Package vm executes compiled Kukel programs on a stack machine.
|
Package vm executes compiled Kukel programs on a stack machine. |
