// 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 fitem interface{} type fptr = ptr.Ptr[fitem] type fstack = stack.Stack[fitem] type fvoc = voc.Vocabulary[XT] type Callable func(*forgeEnv, XT) type xitem struct { name string immediate bool fct Callable body fptr } type XT = *xitem type forgeEnv struct { ds, rs fstack cp, ip, dp fptr voc *fvoc latestXT XT cstate bool output io.Writer } type FE = *forgeEnv func NewVoc(parent *fvoc) *fvoc { 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 *fvoc) *forgeEnv { return &forgeEnv{ ds: stack.NewStack[fitem](), rs: stack.NewStack[fitem](), ip: ptr.NewSlice[fitem](), voc: voc, output: os.Stdout, } } // basic functions and methods func Register(voc *fvoc, name string, fct Callable) *xitem { it := xitem{ name: name, fct: fct, } voc.Register(name, &it) return &it } func (it *xitem) IsImmediate() bool { return it.immediate } func (it *xitem) Immediate() { it.immediate = true } func (it *xitem) Body() fptr { if it.body == nil { it.body = ptr.NewSlice[fitem]() } return it.body } func (it *xitem) Name() string { return it.name } // ForgeEnv methods func (f *forgeEnv) Voc() *fvoc { return f.voc } func (f *forgeEnv) IP() fptr { return f.ip } func (f *forgeEnv) Exec(items ...fitem) { f.ip = ptr.NewSlice[fitem](items...) for f.ip.Next() != nil { it := f.ip.Value().(XT) it.fct(f, it) } } func (f *forgeEnv) Push(it fitem) { f.ds.Push(it) } func (f *forgeEnv) Pop() fitem { return f.ds.Pop() }