go-scopes/forge/forge.go

117 lines
1.8 KiB
Go

// 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()
}