Documentation
¶
Overview ¶
Package feather provides an embeddable TCL interpreter for Go applications.
Architecture ¶
feather has a layered architecture:
- C core: The parsing and evaluation engine written in C
- Handle layer: Internal numeric handles (FeatherObj, ObjHandle) for C interop
- Obj layer: The public Go API using *Obj values
As a user of this package, you work exclusively with *Obj values. The Handle types exist only for internal implementation and may change between versions.
Quick Start ¶
interp := feather.New()
defer interp.Close()
// Evaluate TCL scripts
result, err := interp.Eval("expr {2 + 2}")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.String()) // "4"
// Register Go functions as TCL commands
interp.Register("env", func(name string) string {
return os.Getenv(name)
})
result, _ = interp.Eval(`env HOME`)
fmt.Println(result.String()) // "/home/user"
Thread Safety ¶
An *Interp is NOT safe for concurrent use from multiple goroutines. Each goroutine that needs to evaluate TCL must have its own interpreter:
// WRONG: sharing interpreter between goroutines
interp := feather.New()
go func() { interp.Eval("...") }() // data race!
go func() { interp.Eval("...") }() // data race!
// CORRECT: one interpreter per goroutine
go func() {
interp := feather.New()
defer interp.Close()
interp.Eval("...")
}()
For server applications, use a pool of interpreters or create one per request. *Obj values are also tied to their interpreter and must not be shared.
Supported TCL Commands ¶
feather implements a substantial subset of TCL 8.6. Available commands:
Control flow:
if, while, for, foreach, switch, break, continue, return, tailcall
Procedures and evaluation:
proc, apply, eval, uplevel, upvar, catch, try, throw, error
Variables and namespaces:
set, unset, incr, append, global, variable, namespace, rename, trace
Lists:
list, llength, lindex, lrange, lappend, lset, linsert, lreplace, lreverse, lrepeat, lsort, lsearch, lmap, lassign, split, join, concat
Dictionaries:
dict (with subcommands: create, get, set, exists, keys, values, etc.)
Strings:
string (with subcommands: length, index, range, equal, compare,
match, map, tolower, toupper, trim, replace, first, last, etc.)
format, scan, subst
Introspection:
info (with subcommands: exists, commands, procs, vars, body, args,
level, frame, script, etc.)
Math functions (via expr):
sqrt, exp, log, log10, sin, cos, tan, asin, acos, atan, atan2, sinh, cosh, tanh, floor, ceil, round, abs, pow, fmod, hypot, double, int, wide, isnan, isinf
NOT implemented: file I/O, sockets, regex, clock, encoding, interp (safe interps), and most Tk-related commands. Use Interp.Register to add these if needed.
Error Handling ¶
Errors from Interp.Eval are returned as *EvalError:
result, err := interp.Eval("expr {1/0}")
if err != nil {
// err is *EvalError, err.Error() returns the message
fmt.Println("Error:", err)
}
To return errors from Go commands, use Error or Errorf:
interp.RegisterCommand("fail", func(i *feather.Interp, cmd *feather.Obj, args []*feather.Obj) feather.Result {
// For Go errors, use err.Error() to get the string
_, err := os.Open("/nonexistent")
if err != nil {
return feather.Error(err.Error())
}
return feather.OK("success")
})
For functions registered with Interp.Register, return an error as the last value:
interp.Register("openfile", func(path string) (string, error) {
data, err := os.ReadFile(path)
return string(data), err // error automatically becomes TCL error
})
In TCL, use catch or try to handle errors:
if {[catch {openfile /nonexistent} errmsg]} {
puts "Error: $errmsg"
}
Note: feather does not currently provide stack traces or line numbers in errors. The error message is the only diagnostic information available.
Working with Results ¶
Interp.Eval returns (*Obj, error). The result is the value of the last command executed. Extract values using methods on *Obj or the As* functions:
result, _ := interp.Eval("expr {2 + 2}")
// As string (always works)
s := result.String() // "4"
// As typed values (may error if not convertible)
n, err := result.Int() // 4, nil
f, err := result.Double() // 4.0, nil
b, err := result.Bool() // true, nil
// For lists, first check if it's already a list or parse it
result, _ = interp.Eval("list a b c")
items, err := result.List() // []*Obj{"a", "b", "c"}
// Or parse a string as a list:
items, err = interp.ParseList("a b {c d}")
The Result type is only used when implementing commands with Interp.RegisterCommand. Create results with OK, Error, or Errorf.
Memory and Lifetime ¶
*Obj values are managed by Go's garbage collector. You don't need to explicitly free them. However:
- After Interp.Close, all *Obj values from that interpreter become invalid
- Don't store *Obj values beyond the interpreter's lifetime
- Don't share *Obj values between interpreters
For long-lived applications, be aware that string representations are cached. An object that shimmers between int and string keeps both representations until garbage collected.
The Obj Type System ¶
TCL values are represented by *Obj. Each Obj has two representations:
- String representation: The TCL string form (always available)
- Internal representation: An efficient native form (optional)
The internal representation is managed through the ObjType interface. Conversion between representations happens lazily through "shimmering": when you request a value as an integer, it parses the string and caches the int; when you later request the string, it's regenerated from the int.
Use the As* functions to convert between types:
n, err := feather.AsInt(obj) // Get as int64 f, err := feather.AsDouble(obj) // Get as float64 b, err := feather.AsBool(obj) // Get as bool list, err := feather.AsList(obj) // Get as []*Obj (requires list rep) dict, err := feather.AsDict(obj) // Get as *DictType (requires dict rep)
Note: AsList and AsDict only work on objects that already have list/dict representations. To parse a string as a list or dict, use the interpreter:
list, err := interp.ParseList("a b {c d}") // Parse string to list
dict, err := interp.ParseDict("name Alice") // Parse string to dict
Custom Object Types ¶
Implement ObjType to create types that participate in shimmering. This is useful when you have a Go type that's expensive to create from its string form, so you want to cache the parsed representation.
The interface has three methods:
type ObjType interface {
Name() string // Type name for debugging (e.g., "regex")
UpdateString() string // Convert internal rep back to string
Dup() ObjType // Clone the internal rep (for Copy)
}
Example: A regex type that caches compiled patterns:
type RegexType struct {
pattern string
re *regexp.Regexp
}
func (r *RegexType) Name() string { return "regex" }
func (r *RegexType) UpdateString() string { return r.pattern }
func (r *RegexType) Dup() feather.ObjType { return r } // Immutable, share it
func NewRegex(pattern string) (*feather.Obj, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
return feather.NewObj(&RegexType{pattern: pattern, re: re}), nil
}
// Extract the compiled regex from any Obj
func GetRegex(obj *feather.Obj) (*regexp.Regexp, bool) {
if rt, ok := obj.InternalRep().(*RegexType); ok {
return rt.re, true
}
return nil, false
}
Conversion Interfaces ¶
Custom types can implement conversion interfaces to participate in automatic type coercion. When AsInt is called on an Obj, it first checks if the internal representation implements IntoInt:
type IntoInt interface {
IntoInt() (int64, bool)
}
If implemented and returns (value, true), that value is used directly without parsing the string representation. This enables efficient conversions between related types.
Available conversion interfaces:
IntoInt - Convert to int64 IntoDouble - Convert to float64 IntoBool - Convert to bool IntoList - Convert to []*Obj IntoDict - Convert to (map[string]*Obj, []string)
Example: A timestamp type that converts to int (Unix epoch):
type TimestampType struct {
t time.Time
}
func (ts *TimestampType) Name() string { return "timestamp" }
func (ts *TimestampType) UpdateString() string { return ts.t.Format(time.RFC3339) }
func (ts *TimestampType) Dup() feather.ObjType { return ts }
// Implement IntoInt to support "expr {$timestamp + 3600}"
func (ts *TimestampType) IntoInt() (int64, bool) {
return ts.t.Unix(), true
}
// Implement IntoDouble for sub-second precision
func (ts *TimestampType) IntoDouble() (float64, bool) {
return float64(ts.t.UnixNano()) / 1e9, true
}
Foreign Objects ¶
For exposing Go structs with methods to TCL, use RegisterType. Unlike ObjType (which is about caching parsed representations), foreign types create objects that act as TCL commands with methods:
type DB struct {
conn *sql.DB
}
feather.RegisterType[*DB](interp, "DB", feather.TypeDef[*DB]{
New: func() *DB {
conn, _ := sql.Open("sqlite3", ":memory:")
return &DB{conn: conn}
},
Methods: map[string]any{
"exec": func(db *DB, sql string) error { _, err := db.conn.Exec(sql); return err },
"query": func(db *DB, sql string) ([]string, error) { /* ... */ },
},
Destroy: func(db *DB) { db.conn.Close() },
})
// In TCL:
// set db [DB new]
// $db exec "CREATE TABLE users (name TEXT)"
// $db destroy
Registering Commands ¶
For simple functions, use Interp.Register with automatic type conversion:
// String arguments
interp.Register("upper", strings.ToUpper)
// Multiple parameters with error return
interp.Register("readfile", func(path string) (string, error) {
data, err := os.ReadFile(path)
return string(data), err
})
// Variadic functions
interp.Register("sum", func(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
})
For full control over argument handling, use Interp.RegisterCommand:
interp.RegisterCommand("mycommand", func(i *feather.Interp, cmd *feather.Obj, args []*feather.Obj) feather.Result {
if len(args) < 1 {
return feather.Errorf("usage: %s value", cmd.String())
}
n, err := feather.AsInt(args[0])
if err != nil {
return feather.Error(err.Error())
}
return feather.OK(n * 2)
})
Configuration ¶
Set the recursion limit to prevent stack overflow from deeply nested calls:
interp.SetRecursionLimit(500) // Default is 1000
Parsing Without Evaluation ¶
Use Interp.Parse to check if a script is syntactically complete without evaluating it. This is useful for implementing REPLs:
pr := interp.Parse("set x {")
switch pr.Status {
case feather.ParseOK:
// Complete, ready to evaluate
case feather.ParseIncomplete:
// Unclosed brace/bracket/quote, prompt for more input
case feather.ParseError:
// Syntax error, pr.Message has details
}
Internal Types (Do Not Use) ¶
The following types are internal implementation details for C interop. They are exported only because Go requires it for cgo. Do not use them in application code:
- Handle, FeatherInterp, FeatherObj - Raw numeric handles
- ObjHandle - Handle wrapper with interpreter reference
- InternalCommandFunc - Low-level command signature
- InternalParseStatus, ParseResultInternal - Internal parsing types
- CallFrame, Namespace, Procedure, Command - Interpreter internals
- ForeignRegistry, ForeignType - Interpreter internals
- ListSortContext - Internal sorting state
- FeatherResult, InternalCommandType - C interop constants
These types may change or be removed in any version.
Example (RegexType) ¶
This example shows how to create and use a custom ObjType for caching compiled regular expressions.
package main
import (
"fmt"
"regexp"
"github.com/feather-lang/feather"
)
// RegexType caches a compiled regular expression.
// This demonstrates the core use case for custom ObjType: avoiding repeated
// parsing of the same value.
type RegexType struct {
pattern string
re *regexp.Regexp
}
func (r *RegexType) Name() string { return "regex" }
func (r *RegexType) UpdateString() string { return r.pattern }
func (r *RegexType) Dup() feather.ObjType { return r }
// NewRegexObj compiles a pattern and wraps it in an Obj.
func NewRegexObj(pattern string) (*feather.Obj, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
return feather.NewObj(&RegexType{pattern: pattern, re: re}), nil
}
// GetRegex extracts the compiled regex from an Obj.
// Returns nil, false if the Obj doesn't contain a RegexType.
func GetRegex(obj *feather.Obj) (*regexp.Regexp, bool) {
if rt, ok := obj.InternalRep().(*RegexType); ok {
return rt.re, true
}
return nil, false
}
// registerRegexCommands adds "regex" and "match" commands to an interpreter.
// Factored out so multiple examples can use it.
func registerRegexCommands(interp *feather.Interp) {
interp.RegisterCommand("regex", func(i *feather.Interp, cmd *feather.Obj, args []*feather.Obj) feather.Result {
if len(args) < 1 {
return feather.Errorf("wrong # args: should be \"regex pattern\"")
}
obj, err := NewRegexObj(args[0].String())
if err != nil {
return feather.Error(err.Error())
}
return feather.OK(obj)
})
interp.RegisterCommand("match", func(i *feather.Interp, cmd *feather.Obj, args []*feather.Obj) feather.Result {
if len(args) < 2 {
return feather.Errorf("wrong # args: should be \"match regex string\"")
}
re, ok := GetRegex(args[0])
if !ok {
var err error
re, err = regexp.Compile(args[0].String())
if err != nil {
return feather.Error(err.Error())
}
}
if re.MatchString(args[1].String()) {
return feather.OK(1)
}
return feather.OK(0)
})
}
func main() {
interp := feather.New()
defer interp.Close()
registerRegexCommands(interp)
// Use the regex - the pattern is compiled once and reused
result, _ := interp.Eval(`
set re [regex {^\d+$}]
match $re "12345"
`)
fmt.Println(result.String())
}
Output: 1
Example (RegexTypeError) ¶
This example shows how errors from Go are propagated back to TCL. When regexp.Compile fails, the error becomes a TCL error that can be caught with "catch".
package main
import (
"fmt"
"regexp"
"github.com/feather-lang/feather"
)
// RegexType caches a compiled regular expression.
// This demonstrates the core use case for custom ObjType: avoiding repeated
// parsing of the same value.
type RegexType struct {
pattern string
re *regexp.Regexp
}
func (r *RegexType) Name() string { return "regex" }
func (r *RegexType) UpdateString() string { return r.pattern }
func (r *RegexType) Dup() feather.ObjType { return r }
// NewRegexObj compiles a pattern and wraps it in an Obj.
func NewRegexObj(pattern string) (*feather.Obj, error) {
re, err := regexp.Compile(pattern)
if err != nil {
return nil, err
}
return feather.NewObj(&RegexType{pattern: pattern, re: re}), nil
}
// GetRegex extracts the compiled regex from an Obj.
// Returns nil, false if the Obj doesn't contain a RegexType.
func GetRegex(obj *feather.Obj) (*regexp.Regexp, bool) {
if rt, ok := obj.InternalRep().(*RegexType); ok {
return rt.re, true
}
return nil, false
}
// registerRegexCommands adds "regex" and "match" commands to an interpreter.
// Factored out so multiple examples can use it.
func registerRegexCommands(interp *feather.Interp) {
interp.RegisterCommand("regex", func(i *feather.Interp, cmd *feather.Obj, args []*feather.Obj) feather.Result {
if len(args) < 1 {
return feather.Errorf("wrong # args: should be \"regex pattern\"")
}
obj, err := NewRegexObj(args[0].String())
if err != nil {
return feather.Error(err.Error())
}
return feather.OK(obj)
})
interp.RegisterCommand("match", func(i *feather.Interp, cmd *feather.Obj, args []*feather.Obj) feather.Result {
if len(args) < 2 {
return feather.Errorf("wrong # args: should be \"match regex string\"")
}
re, ok := GetRegex(args[0])
if !ok {
var err error
re, err = regexp.Compile(args[0].String())
if err != nil {
return feather.Error(err.Error())
}
}
if re.MatchString(args[1].String()) {
return feather.OK(1)
}
return feather.OK(0)
})
}
func main() {
interp := feather.New()
defer interp.Close()
registerRegexCommands(interp)
// Invalid regex pattern - repetition operator at start
_, err := interp.Eval(`regex {*invalid}`)
fmt.Println("Direct error:", err)
// Using catch to handle the error in TCL
result, _ := interp.Eval(`
if {[catch {regex {+also-invalid}} errmsg]} {
set errmsg
} else {
set errmsg "no error"
}
`)
fmt.Println("Caught error:", result.String())
}
Output: Direct error: error parsing regexp: missing argument to repetition operator: `*` Caught error: error parsing regexp: missing argument to repetition operator: `+`
Example (TimestampType) ¶
This example shows a custom type that implements conversion interfaces, allowing it to work with expr and other numeric contexts.
package main
import (
"fmt"
"time"
"github.com/feather-lang/feather"
)
// TimestampType wraps time.Time and implements conversion interfaces.
// This shows how to make a custom type participate in TCL's type system.
type TimestampType struct {
t time.Time
}
func (ts *TimestampType) Name() string { return "timestamp" }
func (ts *TimestampType) UpdateString() string { return ts.t.Format(time.RFC3339) }
func (ts *TimestampType) Dup() feather.ObjType { return ts }
// IntoInt returns Unix timestamp, enabling arithmetic in expr.
func (ts *TimestampType) IntoInt() (int64, bool) {
return ts.t.Unix(), true
}
// IntoDouble returns Unix timestamp with nanosecond precision.
func (ts *TimestampType) IntoDouble() (float64, bool) {
return float64(ts.t.UnixNano()) / 1e9, true
}
// NewTimestampObj creates a timestamp object from a time.Time.
func NewTimestampObj(t time.Time) *feather.Obj {
return feather.NewObj(&TimestampType{t: t})
}
func main() {
interp := feather.New()
defer interp.Close()
// Create a timestamp for a fixed point in time
ts := NewTimestampObj(time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC))
// Store it in a variable
interp.SetVar("ts", ts.String())
// The string representation is RFC3339
result, _ := interp.Eval("set ts")
fmt.Println("String:", result.String())
// But we can also get the Unix timestamp via IntoInt
n, _ := feather.AsInt(ts)
fmt.Println("Unix:", n)
}
Output: String: 2024-01-15T10:30:00Z Unix: 1705314600
Index ¶
- Constants
- func AsBool(o *Obj) (bool, error)
- func AsDouble(o *Obj) (float64, error)
- func AsInt(o *Obj) (int64, error)
- func ObjDictKeys(o *Obj) []string
- func ObjDictLen(o *Obj) int
- func ObjDictSet(o *Obj, key string, val *Obj)
- func ObjListAppend(o *Obj, elem *Obj)
- func ObjListLen(o *Obj) int
- func ObjListSet(o *Obj, i int, elem *Obj)
- func RegisterType[T any](i *Interp, typeName string, def TypeDef[T]) error
- type CallFrame
- type Command
- type CommandFunc
- type DictType
- type DoubleType
- type EvalError
- type FeatherInterp
- type FeatherObj
- type FeatherResult
- type ForeignRegistry
- type ForeignType
- type Handle
- type IntType
- type InternalCommandFunc
- type InternalCommandType
- type InternalParseStatus
- type Interp
- func (i *Interp) Bool(v bool) *Obj
- func (i *Interp) Call(cmd string, args ...any) (*Obj, error)
- func (i *Interp) Close()
- func (i *Interp) Dict() *Obj
- func (i *Interp) DictFrom(m map[string]any) *Obj
- func (i *Interp) DictGet(h FeatherObj, key string) (FeatherObj, bool)
- func (i *Interp) DictGetObj(dict FeatherObj, key string) (FeatherObj, bool)
- func (i *Interp) DictKV(kvs ...any) *Obj
- func (i *Interp) DictKeys(h FeatherObj) []string
- func (i *Interp) DictSetObj(dict FeatherObj, key string, val FeatherObj) FeatherObj
- func (i *Interp) Eval(script string) (*Obj, error)
- func (i *Interp) EvalObj(obj *Obj) (*Obj, error)
- func (i *Interp) Float(v float64) *Obj
- func (i *Interp) GetForeignMethods(typeName string) []string
- func (i *Interp) GetForeignStringRep(obj FeatherObj) string
- func (i *Interp) GetVar(name string) string
- func (i *Interp) GetVarHandle(name string) FeatherObj
- func (i *Interp) GetVars(names ...string) map[string]*Obj
- func (i *Interp) Handle() FeatherInterp
- func (i *Interp) Int(v int64) *Obj
- func (i *Interp) InternString(s string) FeatherObj
- func (i *Interp) IsForeignHandle(h FeatherObj) bool
- func (i *Interp) IsNativeDict(h FeatherObj) bool
- func (i *Interp) IsNativeList(h FeatherObj) bool
- func (i *Interp) List(items ...*Obj) *Obj
- func (i *Interp) ListAppendObj(list FeatherObj, item FeatherObj) FeatherObj
- func (i *Interp) ListFrom(slice any) *Obj
- func (i *Interp) ListIndex(h FeatherObj, idx int) FeatherObj
- func (i *Interp) ListLen(h FeatherObj) int
- func (i *Interp) NewDictObj() FeatherObj
- func (i *Interp) NewDoubleObj(val float64) FeatherObj
- func (i *Interp) NewForeignHandle(typeName string, value any) FeatherObj
- func (i *Interp) NewForeignObj(typeName string, value any) FeatherObj
- func (i *Interp) NewIntObj(val int64) FeatherObj
- func (i *Interp) NewListObj() FeatherObj
- func (i *Interp) Parse(script string) ParseResult
- func (i *Interp) ParseDict(s string) (*DictType, error)
- func (i *Interp) ParseInternal(script string) ParseResultInternal
- func (i *Interp) ParseList(s string) ([]*Obj, error)
- func (i *Interp) Register(name string, fn any)
- func (i *Interp) RegisterCommand(name string, fn CommandFunc)
- func (i *Interp) Result() string
- func (i *Interp) ResultHandle() FeatherObj
- func (i *Interp) SetError(obj FeatherObj)
- func (i *Interp) SetErrorString(s string)
- func (i *Interp) SetRecursionLimit(limit int)
- func (i *Interp) SetResult(obj FeatherObj)
- func (i *Interp) SetResultObj(obj *Obj)
- func (i *Interp) SetResultString(s string)
- func (i *Interp) SetUnknownHandler(fn CommandFunc)
- func (i *Interp) SetVar(name string, val any)
- func (i *Interp) SetVars(vars map[string]any)
- func (i *Interp) String(s string) *Obj
- func (i *Interp) Type(h FeatherObj) string
- func (i *Interp) Var(name string) *Obj
- func (i *Interp) Wrap(h FeatherObj) ObjHandle
- type IntoBool
- type IntoDict
- type IntoDouble
- type IntoInt
- type IntoList
- type ListSortContext
- type ListType
- type Namespace
- type Obj
- func AsList(o *Obj) ([]*Obj, error)
- func NewDictObj() *Obj
- func NewDoubleObj(v float64) *Obj
- func NewForeignObj(typeName string, value any) *Obj
- func NewIntObj(v int64) *Obj
- func NewListObj(items ...*Obj) *Obj
- func NewObj(intrep ObjType) *Obj
- func NewStringObj(s string) *Obj
- func ObjDictGet(o *Obj, key string) (*Obj, bool)
- func ObjListAt(o *Obj, i int) *Obj
- func (o *Obj) Bool() (bool, error)
- func (o *Obj) Copy() *Obj
- func (o *Obj) Dict() (*DictType, error)
- func (o *Obj) Double() (float64, error)
- func (o *Obj) Int() (int64, error)
- func (o *Obj) InternalRep() ObjType
- func (o *Obj) List() ([]*Obj, error)
- func (o *Obj) String() string
- func (o *Obj) Type() string
- type ObjHandle
- func (o ObjHandle) Dict() (map[string]ObjHandle, []string, error)
- func (o ObjHandle) Double() (float64, error)
- func (o ObjHandle) ForeignType() string
- func (o ObjHandle) ForeignValue() any
- func (o ObjHandle) Int() (int64, error)
- func (o ObjHandle) IsForeign() bool
- func (o ObjHandle) List() ([]ObjHandle, error)
- func (o ObjHandle) Raw() FeatherObj
- func (o ObjHandle) String() string
- type ObjType
- type ParseResult
- type ParseResultInternal
- type ParseStatus
- type Procedure
- type Result
- type TypeDef
Examples ¶
Constants ¶
const ( // EvalLocal evaluates the script in the current call frame. // Variables are resolved in the local scope first, then in enclosing scopes. // This is the default behavior for most TCL commands. EvalLocal = C.TCL_EVAL_LOCAL // EvalGlobal evaluates the script in the global (top-level) scope. // Variables are resolved only in the global namespace, ignoring local frames. // Use this when a script should not see or modify local variables. EvalGlobal = C.TCL_EVAL_GLOBAL )
Eval flags control variable resolution scope during script evaluation.
const DefaultRecursionLimit = 1000
DefaultRecursionLimit is the default maximum call stack depth.
Variables ¶
This section is empty.
Functions ¶
func ObjDictKeys ¶
ObjDictKeys returns the keys of a dict object in insertion order. Returns nil if the object is not a dict.
func ObjDictLen ¶
ObjDictLen returns the number of keys in a dict object. Returns 0 if the object is nil or not a dict.
func ObjDictSet ¶
ObjDictSet sets a key-value pair in a dict object. Creates the dict if needed (for nil intrep). Invalidates the string representation.
func ObjListAppend ¶
ObjListAppend appends an element to a list object. Converts the object to a list if it has a list-compatible representation. Invalidates the string representation.
func ObjListLen ¶
ObjListLen returns the length of a list object. Returns 0 if the object is nil or not a list.
func ObjListSet ¶
ObjListSet sets the element at index i in a list object. Does nothing if the object is not a list or index is out of bounds. Invalidates the string representation.
func RegisterType ¶
RegisterType registers a foreign type with the interpreter.
After registration, the type name becomes a command that supports "new" to create instances. Instances can then call methods using $obj method args.
Example:
type Counter struct {
value int
}
feather.RegisterType[*Counter](interp, "Counter", feather.TypeDef[*Counter]{
New: func() *Counter { return &Counter{} },
Methods: map[string]any{
"get": func(c *Counter) int { return c.value },
"set": func(c *Counter, v int) { c.value = v },
"incr": func(c *Counter) int { c.value++; return c.value },
},
})
// In TCL:
// set c [Counter new]
// $c set 10
// $c incr ;# returns 11
Types ¶
type CallFrame ¶
type CallFrame struct {
// contains filtered or unexported fields
}
CallFrame represents an execution frame on the call stack. Each frame has its own variable environment.
type Command ¶
type Command struct {
// contains filtered or unexported fields
}
Command represents an entry in the unified command table
type CommandFunc ¶
CommandFunc is the signature for custom commands registered with Interp.RegisterCommand.
The function receives:
- i: the interpreter (for creating objects, accessing variables, etc.)
- cmd: the command name as invoked
- args: the arguments passed to the command
type DictType ¶
DictType is the internal representation for dictionary values.
func AsDict ¶
AsDict converts o to a dictionary if it has a dict-compatible internal representation. For string-to-dict conversion, use the interpreter's parsing facilities.
func (*DictType) UpdateString ¶
type DoubleType ¶
type DoubleType float64
DoubleType is the internal representation for floating-point values.
func (DoubleType) Dup ¶
func (t DoubleType) Dup() ObjType
func (DoubleType) IntoDouble ¶
func (t DoubleType) IntoDouble() (float64, bool)
func (DoubleType) IntoInt ¶
func (t DoubleType) IntoInt() (int64, bool)
func (DoubleType) Name ¶
func (t DoubleType) Name() string
func (DoubleType) UpdateString ¶
func (t DoubleType) UpdateString() string
type FeatherResult ¶
type FeatherResult uint
const ( ResultOK FeatherResult = C.TCL_OK ResultError FeatherResult = C.TCL_ERROR ResultReturn FeatherResult = C.TCL_RETURN ResultBreak FeatherResult = C.TCL_BREAK ResultContinue FeatherResult = C.TCL_CONTINUE )
Result codes matching FeatherResult enum
type ForeignRegistry ¶
type ForeignRegistry struct {
// contains filtered or unexported fields
}
ForeignRegistry manages foreign type definitions and object instances.
type ForeignType ¶
ForeignType is the internal representation for foreign (host-language) objects.
func (*ForeignType) Dup ¶
func (t *ForeignType) Dup() ObjType
func (*ForeignType) Name ¶
func (t *ForeignType) Name() string
func (*ForeignType) UpdateString ¶
func (t *ForeignType) UpdateString() string
type IntType ¶
type IntType int64
IntType is the internal representation for integer values.
func (IntType) IntoDouble ¶
func (IntType) UpdateString ¶
type InternalCommandFunc ¶
type InternalCommandFunc func(i *Interp, cmd FeatherObj, args []FeatherObj) FeatherResult
InternalCommandFunc is the signature for host command implementations. Commands receive the interpreter, the command name and a list of argument objects.
In case of an error, the command should set the interpreter's error information and return ResultError ¶
To return a value, the command should set the interpreter's result value and return ResultOK ¶
Low-level API. May change between versions.
type InternalCommandType ¶
type InternalCommandType int
CommandType indicates the type of a command
const ( CmdNone InternalCommandType = 0 // command doesn't exist CmdBuiltin InternalCommandType = 1 // it's a builtin command CmdProc InternalCommandType = 2 // it's a user-defined procedure )
type InternalParseStatus ¶
type InternalParseStatus uint
InternalParseStatus matching FeatherParseStatus enum
const ( InternalParseOK InternalParseStatus = C.TCL_PARSE_OK InternalParseIncomplete InternalParseStatus = C.TCL_PARSE_INCOMPLETE InternalParseError InternalParseStatus = C.TCL_PARSE_ERROR )
type Interp ¶
type Interp struct {
// Commands holds registered Go command implementations.
// Low-level API. May change between versions.
Commands map[string]InternalCommandFunc
// ForeignRegistry stores foreign type definitions for the high-level API.
ForeignRegistry *ForeignRegistry
// contains filtered or unexported fields
}
Interp is a TCL interpreter instance.
Create a new interpreter with New and always call Interp.Close when done. An interpreter is not safe for concurrent use from multiple goroutines.
interp := feather.New()
defer interp.Close()
result, err := interp.Eval("expr 2 + 2")
func New ¶
func New() *Interp
New creates a new TCL interpreter with all standard commands registered.
The interpreter must be closed with Interp.Close when no longer needed to release resources.
interp := feather.New() defer interp.Close()
func (*Interp) Bool ¶
Bool creates a boolean object, stored as int 1 (true) or 0 (false).
TCL has no native boolean type; booleans are represented as integers.
b := interp.Bool(true) b.Type() // "int" b.String() // "1"
func (*Interp) Call ¶
Call invokes a single TCL command with the given arguments.
Arguments are automatically converted from Go types to TCL values. This is a convenience wrapper around Interp.Eval for single command invocation.
result, err := interp.Call("expr", "2 + 2")
result, err := interp.Call("llength", myList)
result, err := interp.Call("myns::proc", arg1, arg2)
func (*Interp) Close ¶
func (i *Interp) Close()
Close releases resources associated with the interpreter.
After Close is called, the interpreter and all *Obj values created from it become invalid. Always use defer to ensure Close is called.
func (*Interp) Dict ¶
Dict creates an empty dict object.
Use dict helper functions to add key-value pairs, or use Interp.DictKV or Interp.DictFrom to create a populated dict.
dict := interp.Dict()
feather.ObjDictSet(dict, "key", interp.String("value"))
func (*Interp) DictFrom ¶
DictFrom creates a dict object from a Go map.
Values are auto-converted based on their Go type. Note: Go maps have undefined iteration order, so dict key order may vary.
dict := interp.DictFrom(map[string]any{
"name": "Alice",
"age": 30,
})
func (*Interp) DictGet ¶
func (i *Interp) DictGet(h FeatherObj, key string) (FeatherObj, bool)
DictGet retrieves the value for a key in a dict object.
If the object is not already a dict, it attempts to convert from list representation (must have even number of elements).
Returns (handle, true) if the key exists, (0, false) otherwise. Also returns (0, false) if the object cannot be converted to a dict.
func (*Interp) DictGetObj ¶
func (i *Interp) DictGetObj(dict FeatherObj, key string) (FeatherObj, bool)
DictGetObj retrieves a value from a dict by key. Returns the handle and true if found, or 0 and false if not found.
func (*Interp) DictKV ¶
DictKV creates a dict object from alternating key-value pairs.
Keys should be strings (non-strings are converted via fmt.Sprintf). Values are auto-converted based on their Go type.
dict := interp.DictKV("name", "Alice", "age", 30, "active", true)
dict.String() // "name Alice age 30 active 1"
func (*Interp) DictKeys ¶
func (i *Interp) DictKeys(h FeatherObj) []string
DictKeys returns all keys of a dict object in insertion order.
If the object is not already a dict, it attempts to convert from list representation.
Returns nil if the object cannot be converted to a dict.
func (*Interp) DictSetObj ¶
func (i *Interp) DictSetObj(dict FeatherObj, key string, val FeatherObj) FeatherObj
DictSetObj sets a key-value pair in a dict and returns the dict. If the object is not a dict, returns it unchanged.
func (*Interp) Eval ¶
Eval evaluates a TCL script and returns the result.
Multiple commands can be separated by semicolons or newlines. Returns an error if the script has a syntax error or a command fails.
result, err := interp.Eval("set x 10; expr {$x * 2}")
if err != nil {
log.Fatal(err)
}
fmt.Println(result.String()) // "20"
func (*Interp) EvalObj ¶
EvalObj evaluates a TCL script contained in an object.
This is equivalent to calling Interp.Eval with obj.String(), but may be more convenient when working with objects that already contain TCL code.
script := interp.String("expr 2 + 2")
result, err := interp.EvalObj(script)
func (*Interp) Float ¶
Float creates a floating-point object.
f := interp.Float(3.14) f.Type() // "double" f.String() // "3.14"
func (*Interp) GetForeignMethods ¶
GetForeignMethods returns the method names for a foreign type. Used by the goForeignMethods callback.
func (*Interp) GetForeignStringRep ¶
func (i *Interp) GetForeignStringRep(obj FeatherObj) string
GetForeignStringRep returns a custom string representation for a foreign object. Used by the goForeignStringRep callback.
func (*Interp) GetVar ¶
GetVar returns the string value of a variable from the current frame, or empty string if not found.
func (*Interp) GetVarHandle ¶
func (i *Interp) GetVarHandle(name string) FeatherObj
GetVarHandle returns the object handle for a variable, preserving its type. Returns 0 if the variable is not found.
func (*Interp) GetVars ¶
GetVars returns multiple variables as a map.
This is a convenience method equivalent to calling Interp.Var for each name. Variables that don't exist will have empty string values in the result.
vars := interp.GetVars("x", "y", "z")
func (*Interp) Handle ¶
func (i *Interp) Handle() FeatherInterp
Handle returns the interpreter's handle
func (*Interp) Int ¶
Int creates an integer object.
n := interp.Int(42) n.Type() // "int" n.String() // "42"
func (*Interp) InternString ¶
func (i *Interp) InternString(s string) FeatherObj
InternString stores a string and returns its handle.
func (*Interp) IsForeignHandle ¶
func (i *Interp) IsForeignHandle(h FeatherObj) bool
IsForeignHandle returns true if the object is a foreign object. Also checks if the string representation is a foreign handle name.
func (*Interp) IsNativeDict ¶
func (i *Interp) IsNativeDict(h FeatherObj) bool
IsNativeDict returns true if the object has a native dict representation (not just convertible to dict via shimmering).
func (*Interp) IsNativeList ¶
func (i *Interp) IsNativeList(h FeatherObj) bool
IsNativeList returns true if the object has a native list representation (not just convertible to list via shimmering).
func (*Interp) List ¶
List creates a list object from the given items.
list := interp.List(interp.String("a"), interp.Int(1), interp.Bool(true))
list.Type() // "list"
list.String() // "a 1 1"
func (*Interp) ListAppendObj ¶
func (i *Interp) ListAppendObj(list FeatherObj, item FeatherObj) FeatherObj
ListAppendObj appends an item to a list and returns the list. If the object is not a list, returns it unchanged.
func (*Interp) ListFrom ¶
ListFrom creates a list object from a Go slice.
Supported slice types:
- []string - each element becomes a string object
- []int - each element becomes an int object
- []int64 - each element becomes an int object
- []float64 - each element becomes a double object
- []any - each element is auto-converted based on its type
Example:
list := interp.ListFrom([]string{"a", "b", "c"})
list.String() // "a b c"
nums := interp.ListFrom([]int{1, 2, 3})
nums.String() // "1 2 3"
func (*Interp) ListIndex ¶
func (i *Interp) ListIndex(h FeatherObj, idx int) FeatherObj
ListIndex returns the element at the given zero-based index in a list.
If the object is not already a list, it parses the string representation. Returns 0 (invalid handle) if:
- The index is negative or out of bounds
- The object cannot be converted to a list
- The object handle is invalid
func (*Interp) ListLen ¶
func (i *Interp) ListLen(h FeatherObj) int
ListLen returns the number of elements in a list object.
If the object is not already a list, it parses the string representation. Returns 0 if the object is nil, empty, or cannot be converted to a list.
Note: For dicts, this returns the number of key-value pairs times 2 (the list representation length).
func (*Interp) NewDictObj ¶
func (i *Interp) NewDictObj() FeatherObj
NewDictObj creates an empty dict object.
func (*Interp) NewDoubleObj ¶
func (i *Interp) NewDoubleObj(val float64) FeatherObj
NewDoubleObj creates a floating-point object.
func (*Interp) NewForeignHandle ¶
func (i *Interp) NewForeignHandle(typeName string, value any) FeatherObj
NewForeignHandle creates a new foreign object with the given type name and Go value. The string representation is generated as "<TypeName:id>". Returns the handle to the new foreign object. Foreign objects are stored in permanent storage (not scratch) for explicit lifecycle.
func (*Interp) NewForeignObj ¶
func (i *Interp) NewForeignObj(typeName string, value any) FeatherObj
NewForeignObj creates a foreign object with the given type name and value.
func (*Interp) NewIntObj ¶
func (i *Interp) NewIntObj(val int64) FeatherObj
NewIntObj creates an integer object.
func (*Interp) NewListObj ¶
func (i *Interp) NewListObj() FeatherObj
NewListObj creates an empty list object.
func (*Interp) Parse ¶
func (i *Interp) Parse(script string) ParseResult
Parse checks if a script is syntactically complete.
This is useful for implementing REPLs that need to detect incomplete input (unclosed braces, brackets, or quotes).
pr := interp.Parse("set x {")
if pr.Status == feather.ParseIncomplete {
// Prompt for more input
}
func (*Interp) ParseDict ¶
ParseDict parses a string into a dict.
Use this when you have a string that needs to be parsed as a TCL dict. For objects that are already dicts, use AsDict instead.
d, err := interp.ParseDict("name Alice age 30")
// d.Items["name"].String() == "Alice"
func (*Interp) ParseInternal ¶
func (i *Interp) ParseInternal(script string) ParseResultInternal
ParseInternal parses a script string and returns the parse status and result. Low-level API. May change between versions.
func (*Interp) ParseList ¶
ParseList parses a string into a list.
Use this when you have a string that needs to be parsed as a TCL list. For objects that are already lists, use AsList instead.
items, err := interp.ParseList("{a b} c d")
// items = []*Obj{"a b", "c", "d"}
func (*Interp) Register ¶
Register adds a command with automatic argument conversion.
The function's signature determines how arguments are converted:
- string parameters receive the string representation
- int/int64 parameters parse the argument as an integer
- float64 parameters parse as a floating-point number
- bool parameters use TCL boolean rules
- []string parameters receive remaining args as a list
- Variadic parameters (...string, ...int) consume remaining arguments
Return types are also auto-converted:
- string, int, int64, float64, bool become the command result
- error causes the command to fail with the error message
- (T, error) returns T on success or fails on error
Examples:
// Simple function
interp.Register("greet", func(name string) string {
return "Hello, " + name
})
// With error handling
interp.Register("divide", func(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
})
// Variadic
interp.Register("join", func(sep string, parts ...string) string {
return strings.Join(parts, sep)
})
func (*Interp) RegisterCommand ¶
func (i *Interp) RegisterCommand(name string, fn CommandFunc)
RegisterCommand adds a command using the low-level CommandFunc interface.
Use this when you need full control over argument handling, access to the interpreter, or custom error messages. For simpler cases, use Interp.Register.
interp.RegisterCommand("sum", func(i *feather.Interp, cmd *feather.Obj, args []*feather.Obj) feather.Result {
if len(args) < 2 {
return feather.Errorf("wrong # args: should be \"%s a b\"", cmd.String())
}
a, err := feather.AsInt(args[0])
if err != nil {
return feather.Error(err.Error())
}
b, err := feather.AsInt(args[1])
if err != nil {
return feather.Error(err.Error())
}
return feather.OK(a + b)
})
func (*Interp) ResultHandle ¶
func (i *Interp) ResultHandle() FeatherObj
ResultHandle returns the current result object handle. Creates a scratch handle for C code to use.
func (*Interp) SetError ¶
func (i *Interp) SetError(obj FeatherObj)
SetError sets the interpreter's result to the given object handle (for error results). This is symmetric with SetResult but semantically indicates an error value.
func (*Interp) SetErrorString ¶
SetErrorString sets the interpreter's result to an error message.
func (*Interp) SetRecursionLimit ¶
SetRecursionLimit sets the maximum call stack depth. If limit is 0 or negative, the default limit (1000) is used.
func (*Interp) SetResult ¶
func (i *Interp) SetResult(obj FeatherObj)
SetResult sets the interpreter's result to the given object handle. The object is retrieved from the arena and stored directly.
func (*Interp) SetResultObj ¶
SetResultObj sets the interpreter's result to the given *Obj directly.
func (*Interp) SetResultString ¶
SetResultString sets the interpreter's result to a string value.
func (*Interp) SetUnknownHandler ¶
func (i *Interp) SetUnknownHandler(fn CommandFunc)
SetUnknownHandler sets a handler called when a command is not found.
The handler receives the unknown command name and its arguments. It can:
- Implement the command dynamically
- Delegate to another system
- Return an error for truly unknown commands
Set to nil to restore default behavior (return "invalid command" error).
interp.SetUnknownHandler(func(i *feather.Interp, cmd *feather.Obj, args []*feather.Obj) feather.Result {
// Try to auto-load the command
if loaded := tryLoadCommand(cmd.String()); loaded {
return i.Call(cmd.String(), args...)
}
return feather.Errorf("unknown command: %s", cmd.String())
})
func (*Interp) SetVar ¶
SetVar sets a variable to a value.
The value is automatically converted from Go types to TCL:
string, int, int64, float64, bool are converted directly
[]string becomes a TCL list
Other types use fmt.Sprintf("%v", val)
interp.SetVar("name", "Alice") interp.SetVar("count", 42) interp.SetVar("items", []string{"a", "b", "c"})
func (*Interp) SetVars ¶
SetVars sets multiple variables at once from a map.
This is a convenience method equivalent to calling Interp.SetVar for each entry.
interp.SetVars(map[string]any{
"x": 1,
"y": 2,
"name": "Alice",
})
func (*Interp) String ¶
String creates a string object.
s := interp.String("hello world")
s.Type() // "string"
s.String() // "hello world"
func (*Interp) Type ¶
func (i *Interp) Type(h FeatherObj) string
Type returns the native type of an object.
func (*Interp) Var ¶
Var returns the value of a variable as a *Obj.
Returns an empty string object if the variable does not exist. The returned object preserves the variable's type (int, list, foreign, etc.).
interp.SetVar("x", 42)
v := interp.Var("x")
feather.AsInt(v) // 42, nil
v.Type() // "int" (if SetVar preserved type) or "string"
func (*Interp) Wrap ¶
func (i *Interp) Wrap(h FeatherObj) ObjHandle
Wrap creates an ObjHandle for method-based access to handle data.
Low-level API. May change between versions.
type IntoDouble ¶
IntoDouble can convert directly to float64.
type ListSortContext ¶
type ListSortContext struct {
// contains filtered or unexported fields
}
ListSortContext holds context for list sorting
type ListType ¶
type ListType []*Obj
ListType is the internal representation for list values.
func (ListType) UpdateString ¶
type Namespace ¶
type Namespace struct {
// contains filtered or unexported fields
}
Namespace represents a namespace in the hierarchy
type Obj ¶
type Obj struct {
// contains filtered or unexported fields
}
Obj is a Feather value. It follows TCL semantics where values have both a string representation and an optional internal representation that can be lazily computed.
func AsList ¶
AsList converts o to a list if it has a list-compatible internal representation. For string-to-list conversion, use the interpreter's parsing facilities.
func NewDoubleObj ¶
NewDoubleObj creates a new object with a floating-point value.
func NewForeignObj ¶
NewForeignObj creates a new foreign object with the given type name and Go value.
func NewListObj ¶
NewListObj creates a new object with a list value.
func NewObj ¶
NewObj creates a new object with a custom ObjType internal representation. Use this when implementing custom shimmering types.
Example:
type RegexType struct {
pattern string
re *regexp.Regexp
}
func (t *RegexType) Name() string { return "regex" }
func (t *RegexType) UpdateString() string { return t.pattern }
func (t *RegexType) Dup() feather.ObjType { return t }
func NewRegex(pattern string, re *regexp.Regexp) *feather.Obj {
return feather.NewObj(&RegexType{pattern: pattern, re: re})
}
func NewStringObj ¶
NewStringObj creates a new object with a string value.
func ObjDictGet ¶
ObjDictGet returns the value for a key in a dict object. Returns nil, false if the object is not a dict or key doesn't exist.
func ObjListAt ¶
ObjListAt returns the element at index i in a list object. Returns nil if the object is not a list or index is out of bounds.
func (*Obj) Copy ¶
Copy creates a shallow copy of the object. If the object has an internal representation, it is duplicated via Dup().
func (*Obj) Dict ¶
Dict returns the dict representation of this object. Note: This only works on objects that already have a dict representation. To parse a string as a dict, use Interp.ParseDict().
func (*Obj) InternalRep ¶
InternalRep returns the internal representation of the object. Returns nil for pure string objects.
Use type assertion to access custom ObjType implementations:
if myType, ok := obj.InternalRep().(*MyType); ok {
// use myType
}
func (*Obj) List ¶
List returns the list elements of this object. Note: This only works on objects that already have a list representation. To parse a string as a list, use Interp.ParseList().
type ObjHandle ¶
type ObjHandle struct {
// contains filtered or unexported fields
}
ObjHandle wraps a FeatherObj handle with its interpreter for method access. This type exists only in Go and never crosses the CGo boundary.
Use Interp.Handle to create an ObjHandle from a FeatherObj.
Low-level API. May change between versions.
func (ObjHandle) Dict ¶
Dict returns dict as map with ObjHandle values, or error if not a dict. Returns the items map, key order slice, and any error.
func (ObjHandle) ForeignType ¶
ForeignType returns the type name if foreign, empty string otherwise.
func (ObjHandle) ForeignValue ¶
ForeignValue returns the Go value if foreign, nil otherwise.
func (ObjHandle) Raw ¶
func (o ObjHandle) Raw() FeatherObj
Raw returns the underlying FeatherObj handle.
type ObjType ¶
type ObjType interface {
// Name returns the type name (e.g., "int", "list").
Name() string
// UpdateString regenerates string representation from this internal rep.
UpdateString() string
// Dup creates a copy of this internal representation.
Dup() ObjType
}
ObjType defines the core behavior for an internal representation.
type ParseResult ¶
type ParseResult struct {
// Status indicates whether parsing succeeded, found incomplete input, or failed.
Status ParseStatus
// Message contains an error message if Status is ParseError.
Message string
}
ParseResult holds the result of parsing a script.
type ParseResultInternal ¶
type ParseResultInternal struct {
Status InternalParseStatus
Result string // The interpreter's result string (e.g., "{INCOMPLETE 5 20}")
ErrorMessage string // For ParseError, the error message from the result list
}
ParseResultInternal holds the result of parsing a script
type ParseStatus ¶
type ParseStatus int
ParseStatus indicates the result of parsing a script.
const ( // ParseOK indicates the script is syntactically complete and valid. ParseOK ParseStatus = ParseStatus(InternalParseOK) // ParseIncomplete indicates the script has unclosed braces, brackets, or quotes. ParseIncomplete ParseStatus = ParseStatus(InternalParseIncomplete) // ParseError indicates a syntax error in the script. ParseError ParseStatus = ParseStatus(InternalParseError) )
type Procedure ¶
type Procedure struct {
// contains filtered or unexported fields
}
Procedure represents a user-defined procedure
type Result ¶
type Result struct {
// contains filtered or unexported fields
}
Result represents the result of a command execution.
Create results using OK, Error, or Errorf.
func Error ¶
Error returns an error result with a message or *Obj.
Pass a string for simple error messages, or a *Obj for structured errors.
return feather.Error("something went wrong")
return feather.Error(errDict) // structured error
func Errorf ¶
Errorf returns a formatted error result.
return feather.Errorf("expected %d args, got %d", want, got)
func OK ¶
OK returns a successful result with a value.
The value is auto-converted to a TCL string representation. Pass a *Obj directly to preserve its internal type (int, list, dict, etc.).
return feather.OK("success")
return feather.OK(42)
return feather.OK([]string{"a", "b"})
return feather.OK(myObj) // preserves *Obj type
type TypeDef ¶
type TypeDef[T any] struct { // New is the constructor function, called when "TypeName new" is evaluated. // Required. New func() T // Methods maps method names to Go functions. // Each function's first parameter must be the receiver type T. // Additional parameters and return values are auto-converted. Methods map[string]any // String optionally provides a custom string representation. // If nil, a default "<TypeName:address>" format is used. String func(T) string // Destroy is called when the object is garbage collected or explicitly destroyed. // Use for cleanup (closing files, connections, etc.). Destroy func(T) }
TypeDef defines a foreign type that can be exposed to TCL.
Foreign types allow Go structs to be used as TCL objects with methods. See RegisterType for usage.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
feather-httpd
command
feather-httpd is an example HTTP server configurable via the feather TCL interpreter.
|
feather-httpd is an example HTTP server configurable via the feather TCL interpreter. |
|
feather-memory-tester
command
|
|
|
feather-tester
command
feather-tester is the interpreter used by the test harness.
|
feather-tester is the interpreter used by the test harness. |
