go-scopes/forge/forge.go

125 lines
2 KiB
Go

// Package `forge` implements a stack-based interpreter.
package forge
import (
"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 FItem = fitem
type fptr = ptr.Ptr[fitem]
type FPtr = fptr
type fstack = stack.Stack[fitem]
type fvoc = voc.Vocabulary[fitem]
type Callable func(*forgeEnv)
type forgeEnv struct {
ds, rs fstack
ip fptr
latestxt XT
voc *fvoc
}
type FE = *forgeEnv
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,
}
}
var newVoc = voc.NewVoc[fitem]
var newScalar = ptr.NewScalar[fitem]
var newPtr = ptr.NewSlice[fitem]
// forgeEnv methods
func (f *forgeEnv) Code(items ...fitem) fptr {
code := newPtr(items...)
// ... pre-process (compile) code ...
return code
}
func (f *forgeEnv) Call(code fptr) *forgeEnv {
f.rs.Push(f.ip)
f.ip = code.Clone()
for f.ip.Next() != nil {
value := f.ip.Value()
if xt, ok := value.(XT); ok {
xt.Fct()(f)
} else {
f.Push(value)
}
}
f.ip = f.RPop()
return f
}
func (f *forgeEnv) Exec(items ...fitem) *forgeEnv {
return f.Call(f.Code(items...))
}
func (f *forgeEnv) NewVar() fptr {
return newScalar()
}
func (f *forgeEnv) Literal() fitem {
return f.ip.Next().Value()
}
func (f *forgeEnv) Voc() *fvoc {
return f.voc
}
func (f *forgeEnv) Push(it fitem) {
f.ds.Push(it)
}
func (f *forgeEnv) Pop() fitem {
return f.ds.Pop()
}
func (f *forgeEnv) PopI() int {
return f.ds.Pop().(int)
}
func (f *forgeEnv) Peek(d int) fitem {
return f.ds.Peek(d)
}
func (f *forgeEnv) RPop() fptr {
if f.rs.Depth() > 0 {
return f.rs.Pop().(fptr)
}
return f.Code()
}
func (f *forgeEnv) Reset() fptr {
return f.ip.Reset()
}
func (f *forgeEnv) LatestXT() XT {
return f.latestxt
}
func (f *forgeEnv) Register(xt XT) {
Register(f.Voc(), xt)
f.latestxt = xt
}