go-scopes/forge/forge.go

126 lines
2 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 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{}
}