work in progress: add some 'Forge' (Forth by Go) functionality

This commit is contained in:
Helmut Merz 2023-07-20 19:51:43 +02:00
parent 3f16eb44ca
commit ec6172f07e
4 changed files with 141 additions and 0 deletions

2
forge/forge.go Normal file
View file

@ -0,0 +1,2 @@
// Package forge implements sort of a stack-based interpreter.
package forge

53
forge/stack/stack.go Normal file
View 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
View 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
View 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)
}