164 lines
2.5 KiB
Go
164 lines
2.5 KiB
Go
// Package forge implements sort of 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 fptr = ptr.Ptr[fitem]
|
|
type FPtr = fptr
|
|
|
|
type fstack = stack.Stack[fitem]
|
|
type fvoc = voc.Vocabulary[fitem]
|
|
|
|
type Callable func(*forgeEnv, XT)
|
|
|
|
type forgeEnv struct {
|
|
ds, rs fstack
|
|
ip fptr
|
|
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]
|
|
|
|
// XT (execution token)
|
|
|
|
type XT interface {
|
|
Name() string
|
|
Fct() Callable
|
|
Code() fptr
|
|
}
|
|
|
|
type name string
|
|
|
|
func (n name) Name() string {
|
|
return string(n)
|
|
}
|
|
|
|
func Register(voc *fvoc, it XT) XT {
|
|
voc.Register(it.Name(), it)
|
|
return it
|
|
}
|
|
|
|
// gofunc: XT consisting only of a function written in Go
|
|
|
|
type gofunc struct {
|
|
name
|
|
fct Callable
|
|
}
|
|
|
|
func GoFunc(n string, fct Callable) XT {
|
|
return &gofunc{name(n), fct}
|
|
}
|
|
|
|
func (it *gofunc) Fct() Callable {
|
|
return it.fct
|
|
}
|
|
|
|
func (it *gofunc) Code() fptr {
|
|
return nil
|
|
}
|
|
|
|
// fcode: with forge code
|
|
|
|
type fcode struct {
|
|
name
|
|
code fptr
|
|
}
|
|
|
|
func FCode(n string, c fptr) XT {
|
|
return &fcode{name(n), c}
|
|
}
|
|
|
|
func (it *fcode) Fct() Callable {
|
|
return doFCode
|
|
}
|
|
|
|
func (it *fcode) Code() fptr {
|
|
return it.code
|
|
}
|
|
|
|
// 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, xt)
|
|
} else {
|
|
f.Push(value)
|
|
}
|
|
}
|
|
f.ip = f.rs.Pop().(fptr)
|
|
return f
|
|
}
|
|
|
|
func (f *forgeEnv) Exec(items ...fitem) *forgeEnv {
|
|
return f.Call(f.Code(items...))
|
|
}
|
|
|
|
// core methods
|
|
|
|
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) Peek(d int) fitem {
|
|
return f.ds.Peek(d)
|
|
}
|
|
|
|
// basic functions for executable items
|
|
|
|
func doFCode(f *forgeEnv, xt XT) {
|
|
f.Call(xt.Code())
|
|
}
|