Go
Feather is written in Go and designed for easy embedding in Go applications.
Installation
bash
go get github.com/feather-lang/featherQuick Start
go
package main
import (
"fmt"
"github.com/feather-lang/feather"
)
func main() {
interp := feather.New()
defer interp.Close()
// Register a host command with automatic type conversion
interp.Register("greet", func(name string) string {
return "Hello, " + name + "!"
})
// Evaluate TCL code
result, err := interp.Eval(`greet "Feather"`)
if err != nil {
panic(err)
}
fmt.Println(result) // Hello, Feather!
}Registering Commands
Use Register to add commands with automatic type conversion:
go
// Simple function
interp.Register("greet", func(name string) string {
return "Hello, " + name + "!"
})
// Multiple arguments with different types
interp.Register("add", func(a, b int) int {
return a + b
})
// With error return
interp.Register("divide", func(a, b int) (int, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
})
// Variadic arguments
interp.Register("sum", func(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
})Supported parameter types: string, int, int64, float64, bool, []string, and variadic versions.
Low-Level Interface
For full control over argument handling, use RegisterCommand:
go
interp.RegisterCommand("mysum", 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 := args[0].Int()
if err != nil {
return feather.Error(err.Error())
}
b, err := args[1].Int()
if err != nil {
return feather.Error(err.Error())
}
return feather.OK(a + b)
})Working with Values
Feather values (*feather.Obj) can be converted to Go types:
go
// String (always available)
s := obj.String()
// Integer
i, err := obj.Int()
// Float
f, err := obj.Double()
// Boolean (TCL boolean rules)
b, err := obj.Bool()
// List (if already has list representation)
items, err := obj.List()
// Dict (shimmers to dict if needed)
dictObj, err := obj.Dict()Create new values with constructor functions:
go
feather.NewStringObj("hello")
feather.NewIntObj(42)
feather.NewDoubleObj(3.14)
feather.NewListObj(item1, item2, item3)
feather.NewDictObj()Or use interpreter convenience methods:
go
interp.String("hello")
interp.Int(42)
interp.Float(3.14)
interp.Dict()
interp.DictKV("name", "Alice", "age", 30)
interp.DictFrom(map[string]any{"name": "Alice", "age": 30})Foreign Types
Expose Go structs as TCL objects with methods:
go
type Counter struct {
value int64
}
interp.RegisterType("Counter", feather.TypeDef[*Counter]{
New: func() *Counter {
return &Counter{value: 0}
},
Methods: feather.Methods{
"get": func(c *Counter) int64 {
return c.value
},
"set": func(c *Counter, val int64) {
c.value = val
},
"incr": func(c *Counter) int64 {
c.value++
return c.value
},
},
String: func(c *Counter) string {
return fmt.Sprintf("<Counter:%d>", c.value)
},
})Use from TCL:
tcl
set c [Counter new]
$c set 10
$c incr
puts [$c get] ;# 11Parsing Input
Check if input is complete before evaluating (useful for REPLs):
go
result := interp.Parse(script)
switch result.Status {
case feather.ParseOK:
// Script is complete, safe to eval
case feather.ParseIncomplete:
// Unclosed braces/quotes, wait for more input
case feather.ParseError:
// Syntax error
fmt.Println(result.Message)
}API Reference
Full documentation is available on pkg.go.dev.
Example Project
See feather-httpd for a complete example: an HTTP server scriptable via Feather with a telnet REPL for live inspection and modification.
