// Package forge implements sort of a stack-based interpreter. package forge import ( "io" "os" "git.sr.ht/~cco/go-scopes/common/ptr" "git.sr.ht/~cco/go-scopes/common/stack" "git.sr.ht/~cco/go-scopes/common/voc" ) type DataItem interface{} type Ptr = ptr.Ptr[DataItem] type Stack = stack.Stack[DataItem] type Voc = voc.Vocabulary[XT] type Callable func(*ForgeEnv, XT) type Item struct { name string immediate bool fct Callable body Ptr } type XT = *Item type ForgeEnv struct { ds, rs Stack cp, ip, dp Ptr voc *Voc latestXT XT cstate bool output io.Writer } func NewVoc(parent *Voc) *Voc { return voc.NewVoc[XT](parent) } func NewFE() *ForgeEnv { return newFE(NewVoc(nil)) } func (f *ForgeEnv) ChildFE() *ForgeEnv { return newFE(NewVoc(f.voc)) } func newFE(voc *Voc) *ForgeEnv { return &ForgeEnv{ ds: stack.NewStack[DataItem](), rs: stack.NewStack[DataItem](), ip: ptr.NewSlice[DataItem](), voc: voc, output: os.Stdout, } } // basic functions and methods func Register(voc *Voc, name string, fct Callable) *Item { it := Item{ name: name, fct: fct, } voc.Register(name, &it) return &it } func (it *Item) IsImmediate() bool { return it.immediate } func (it *Item) Immediate() { it.immediate = true } func (it *Item) Body() Ptr { if it.body == nil { it.body = ptr.NewSlice[DataItem]() } return it.body } func (it *Item) Name() string { return it.name } // ForgeEnv methods func (f *ForgeEnv) Voc() *Voc { return f.voc } func (f *ForgeEnv) IP() Ptr { return f.ip } func (f *ForgeEnv) Exec(items ...DataItem) { f.ip = ptr.NewSlice[DataItem](items...) for f.ip.Next() != nil { it := f.ip.Value().(XT) it.fct(f, it) } } func (f *ForgeEnv) Push(it DataItem) { f.ds.Push(it) } func (f *ForgeEnv) Pop() DataItem { return f.ds.Pop() } // dummy stuff for testing var builtins = struct{ add, sub XT }{} var work = struct{ square XT }{} func init() { builtins.add = &Item{} builtins.sub = &Item{} }