work in progress: add some 'Forge' (Forth by Go) functionality
This commit is contained in:
parent
3f16eb44ca
commit
ec6172f07e
4 changed files with 141 additions and 0 deletions
2
forge/forge.go
Normal file
2
forge/forge.go
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// Package forge implements sort of a stack-based interpreter.
|
||||||
|
package forge
|
53
forge/stack/stack.go
Normal file
53
forge/stack/stack.go
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// package stack provides a generic first-in / first-out stack.
|
||||||
|
package stack
|
||||||
|
|
||||||
|
// stack interface
|
||||||
|
|
||||||
|
type Stack[V any] interface {
|
||||||
|
Depth() int
|
||||||
|
Push(V)
|
||||||
|
Pop() V
|
||||||
|
Peek(int) V
|
||||||
|
Replace(int, V)
|
||||||
|
}
|
||||||
|
|
||||||
|
// simple slice implementation
|
||||||
|
|
||||||
|
type stack[V any] struct {
|
||||||
|
tos int
|
||||||
|
data []V
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStack[V any]() *stack[V] {
|
||||||
|
return &stack[V]{
|
||||||
|
tos: -1,
|
||||||
|
data: make([]V, 0, 5),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *stack[V]) Depth() int {
|
||||||
|
return st.tos + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *stack[V]) Push(v V) {
|
||||||
|
st.tos += 1
|
||||||
|
if st.tos >= len(st.data) {
|
||||||
|
st.data = append(st.data, v)
|
||||||
|
} else {
|
||||||
|
st.data[st.tos] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *stack[V]) Pop() V {
|
||||||
|
v := st.data[st.tos]
|
||||||
|
st.tos -= 1
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *stack[V]) Peek(i int) V {
|
||||||
|
return st.data[st.tos-i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *stack[V]) Replace(i int, v V) {
|
||||||
|
st.data[st.tos-i] = v
|
||||||
|
}
|
42
forge/voc/voc.go
Normal file
42
forge/voc/voc.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Package voc implements a generic vocabulary. Vocabularies may be nested
|
||||||
|
// and are searched upwards using the parent vocabulary until this is nil.
|
||||||
|
// Individual entries contain slices of values; the last item in a
|
||||||
|
// slice is the current value.
|
||||||
|
package voc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.sr.ht/~cco/go-scopes/lib/funky"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VocData[V any] map[string][]V
|
||||||
|
|
||||||
|
type Vocabulary[V any] struct {
|
||||||
|
parent *Vocabulary[V]
|
||||||
|
data VocData[V]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVoc[V any](parent *Vocabulary[V]) *Vocabulary[V] {
|
||||||
|
return &Vocabulary[V]{
|
||||||
|
parent: parent,
|
||||||
|
data: VocData[V]{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (voc *Vocabulary[V]) Register(name string, it V) {
|
||||||
|
vi, ok := voc.data[name]
|
||||||
|
if ok {
|
||||||
|
voc.data[name] = append(vi, it)
|
||||||
|
} else {
|
||||||
|
voc.data[name] = []V{it}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (voc *Vocabulary[V]) Lookup(name string) funky.Maybe[V] {
|
||||||
|
vi, ok := voc.data[name]
|
||||||
|
if ok {
|
||||||
|
return funky.Just(vi[len(vi)-1])
|
||||||
|
}
|
||||||
|
if voc.parent != nil {
|
||||||
|
return voc.parent.Lookup(name)
|
||||||
|
}
|
||||||
|
return funky.Nothing[V]()
|
||||||
|
}
|
44
tests/forge_test.go
Normal file
44
tests/forge_test.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package scopes_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
tbase "testing"
|
||||||
|
|
||||||
|
"git.sr.ht/~cco/go-scopes/forge/stack"
|
||||||
|
"git.sr.ht/~cco/go-scopes/forge/voc"
|
||||||
|
"git.sr.ht/~cco/go-scopes/testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestForge(tb *tbase.T) {
|
||||||
|
t := testing.SetUp(tb)
|
||||||
|
t.Run("stack", StackTest)
|
||||||
|
t.Run("voc", VocTest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StackTest(t *testing.T) {
|
||||||
|
var st stack.Stack[int] = stack.NewStack[int]()
|
||||||
|
st.Push(3)
|
||||||
|
st.Push(4)
|
||||||
|
t.AssertEqual(st.Depth(), 2)
|
||||||
|
st.Push(5)
|
||||||
|
t.AssertEqual(st.Peek(0), 5)
|
||||||
|
st.Replace(1, 99)
|
||||||
|
st.Push(6)
|
||||||
|
t.AssertEqual(st.Peek(2), 99)
|
||||||
|
st.Push(7)
|
||||||
|
st.Push(8)
|
||||||
|
st.Push(9)
|
||||||
|
t.AssertEqual(st.Peek(2), 7)
|
||||||
|
st.Pop()
|
||||||
|
t.AssertEqual(st.Pop(), 8)
|
||||||
|
t.AssertEqual(st.Peek(st.Depth()-1), 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VocTest(t *testing.T) {
|
||||||
|
v1 := voc.NewVoc[int](nil)
|
||||||
|
v1.Register("one", 1)
|
||||||
|
m := v1.Lookup("one")
|
||||||
|
t.AssertEqual(m.IsNothing(), false)
|
||||||
|
t.AssertEqual(m.Value(), 1)
|
||||||
|
m = v1.Lookup("two")
|
||||||
|
t.AssertEqual(m.IsNothing(), true)
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue