mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-18 21:11:03 +01:00
279 lines
6.2 KiB
Go
279 lines
6.2 KiB
Go
package zygo
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type StackElem interface {
|
|
IsStackElem()
|
|
}
|
|
|
|
type Stack struct {
|
|
tos int
|
|
elements []StackElem
|
|
env *Zlisp
|
|
|
|
Name string // type name
|
|
|
|
// package support:
|
|
PackageName string
|
|
IsPackage bool
|
|
}
|
|
|
|
func (s *Stack) SexpString(ps *PrintState) string {
|
|
if ps == nil {
|
|
ps = NewPrintState()
|
|
}
|
|
var label string
|
|
head := ""
|
|
if s.IsPackage {
|
|
head = "(package " + s.PackageName
|
|
} else {
|
|
label = "scope " + s.Name
|
|
}
|
|
|
|
str, err := s.Show(s.env, ps, s.Name)
|
|
if err != nil {
|
|
return "(" + label + ")"
|
|
}
|
|
|
|
return head + " " + str + " )"
|
|
}
|
|
|
|
// Type() satisfies the Sexp interface, returning the type of the value.
|
|
func (s *Stack) Type() *RegisteredType {
|
|
return GoStructRegistry.Lookup("packageScopeStack")
|
|
}
|
|
|
|
func (env *Zlisp) NewStack(size int) *Stack {
|
|
return &Stack{
|
|
tos: -1,
|
|
// elements: make([]StackElem, size),
|
|
elements: make([]StackElem, 0),
|
|
env: env,
|
|
}
|
|
}
|
|
|
|
func (stack *Stack) Clone() *Stack {
|
|
ret := &Stack{}
|
|
ret.tos = stack.tos
|
|
ret.env = stack.env
|
|
ret.elements = make([]StackElem, len(stack.elements))
|
|
for i := range stack.elements {
|
|
ret.elements[i] = stack.elements[i]
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func (stack *Stack) Top() int {
|
|
return stack.tos
|
|
}
|
|
|
|
func (stack *Stack) PushAllTo(target *Stack) int {
|
|
if stack.tos < 0 {
|
|
return 0
|
|
}
|
|
|
|
for _, v := range stack.elements[0 : stack.tos+1] {
|
|
target.Push(v)
|
|
}
|
|
|
|
return stack.tos + 1
|
|
}
|
|
|
|
func (stack *Stack) IsEmpty() bool {
|
|
return stack.tos < 0
|
|
}
|
|
|
|
func (stack *Stack) Push(elem StackElem) {
|
|
|
|
// we have to lazily recover from errors here where the stack
|
|
// didn't get unwound...
|
|
n := len(stack.elements)
|
|
|
|
// n-1 is the last legal entry, which should be top of stack too.
|
|
switch {
|
|
case stack.tos == n-1:
|
|
// normal, 99% of the time.
|
|
stack.tos++
|
|
stack.elements = append(stack.elements, elem)
|
|
case stack.tos > n-1:
|
|
// really irretreivably problematic
|
|
panic(fmt.Sprintf("stack %p is really messed up! starting size=%v > "+
|
|
"len(stack.elements)=%v:\n here is stack: '%s'\n",
|
|
stack, stack.tos-1, n, stack.SexpString(nil)))
|
|
default:
|
|
// INVAR stack.tos < n-1
|
|
|
|
// We might get here if an error caused the last operation to abort,
|
|
// resulting in the call stack pop upon returning never happening.
|
|
|
|
// So we'll lazily cleanup now and carry on.
|
|
stack.TruncateToSize(stack.tos + 1)
|
|
stack.tos++
|
|
stack.elements = append(stack.elements, elem)
|
|
}
|
|
}
|
|
|
|
func (stack *Stack) GetTop() StackElem {
|
|
s, err := stack.Get(0)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return s
|
|
}
|
|
func (stack *Stack) Size() int {
|
|
return stack.tos + 1
|
|
}
|
|
|
|
var StackUnderFlowErr = fmt.Errorf("invalid stack access: underflow")
|
|
|
|
func (stack *Stack) Get(n int) (StackElem, error) {
|
|
if stack.tos-n < 0 {
|
|
err := StackUnderFlowErr
|
|
return nil, err
|
|
}
|
|
return stack.elements[stack.tos-n], nil
|
|
}
|
|
|
|
func (stack *Stack) Pop() (StackElem, error) {
|
|
// always make a new array,
|
|
// so we can use for the closure stack-of-scopes.
|
|
|
|
elem, err := stack.Get(0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// invar n > 0
|
|
n := stack.Size()
|
|
if n == 0 {
|
|
return nil, fmt.Errorf("Stack.Pop() on emtpy stack")
|
|
}
|
|
|
|
el := make([]StackElem, n-1)
|
|
copy(el, stack.elements)
|
|
stack.elements = el
|
|
stack.tos--
|
|
return elem, nil
|
|
}
|
|
|
|
func (stack *Stack) IsStackElem() {}
|
|
|
|
func (stack *Stack) Show(env *Zlisp, ps *PrintState, label string) (string, error) {
|
|
//P("debug: Stack.Show starting with stack = %p, ps = %p, Package: '%s', IsPkg: %v", stack, ps, stack.PackageName, stack.IsPackage)
|
|
if ps.GetSeen(stack) {
|
|
return fmt.Sprintf("already-saw Stack %p in Show", stack), nil
|
|
} else {
|
|
ps.SetSeen(stack, "Stack in Show")
|
|
}
|
|
|
|
s := ""
|
|
rep := strings.Repeat(" ", ps.GetIndent())
|
|
s += fmt.Sprintf("%s %s\n", rep, label)
|
|
n := stack.Top()
|
|
for i := 0; i <= n; i++ {
|
|
ele, err := stack.Get(n - i)
|
|
if err != nil {
|
|
panic(fmt.Errorf("stack access error on %v: %v", i, err))
|
|
}
|
|
showme, canshow := ele.(Showable)
|
|
if canshow {
|
|
r, err := showme.Show(env, ps.AddIndent(4),
|
|
fmt.Sprintf("elem %v of %s:", i, label))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
s += r
|
|
}
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// set newsize to 0 to truncate everything
|
|
func (stack *Stack) TruncateToSize(newsize int) {
|
|
el := make([]StackElem, newsize)
|
|
copy(el, stack.elements)
|
|
stack.elements = el
|
|
stack.tos = newsize - 1
|
|
}
|
|
|
|
// nestedPathGetSet does a top-down lookup, as opposed to LexicalLookupSymbol which is bottom up
|
|
func (s *Stack) nestedPathGetSet(env *Zlisp, dotpaths []string, setVal *Sexp) (Sexp, error) {
|
|
|
|
if len(dotpaths) == 0 {
|
|
return SexpNull, fmt.Errorf("internal error: in nestedPathGetSet() dotpaths" +
|
|
" had zero length")
|
|
}
|
|
|
|
curStack := s
|
|
|
|
var ret Sexp = SexpNull
|
|
var err error
|
|
var scop *Scope
|
|
lenpath := len(dotpaths)
|
|
//P("\n in nestedPathGetSet, dotpaths=%#v\n", dotpaths)
|
|
for i := range dotpaths {
|
|
|
|
curSym := env.MakeSymbol(stripAnyDotPrefix(dotpaths[i]))
|
|
if !curStack.IsPackage {
|
|
return SexpNull, fmt.Errorf("error locating symbol '%s': current Stack is not a package", curSym.name)
|
|
}
|
|
|
|
ret, err, scop = curStack.LookupSymbol(curSym, nil)
|
|
if err != nil {
|
|
return SexpNull, fmt.Errorf("could not find symbol '%s' in current package '%v'",
|
|
curSym.name, curStack.PackageName)
|
|
}
|
|
if setVal != nil && i == lenpath-1 {
|
|
// check if private
|
|
err = errIfPrivate(curSym.name, curStack)
|
|
if err != nil {
|
|
return SexpNull, err
|
|
}
|
|
|
|
// assign now
|
|
scop.Map[curSym.number] = *setVal
|
|
// done with SET
|
|
return *setVal, nil
|
|
}
|
|
|
|
if i == lenpath-1 {
|
|
// final element
|
|
switch ret.(type) {
|
|
case *Stack:
|
|
// allow package within package to be inspected.
|
|
// done with GET
|
|
return ret, nil
|
|
default:
|
|
// don't allow private value within package to be inspected.
|
|
err = errIfPrivate(curSym.name, curStack)
|
|
if err != nil {
|
|
return SexpNull, err
|
|
}
|
|
}
|
|
// done with GET
|
|
return ret, nil
|
|
}
|
|
// invar: i < lenpath-1, so go deeper
|
|
switch x := ret.(type) {
|
|
case *SexpHash:
|
|
err = errIfPrivate(curSym.name, curStack)
|
|
if err != nil {
|
|
return SexpNull, err
|
|
}
|
|
//P("\n found hash in x at i=%d, looping to next i\n", i)
|
|
return x.nestedPathGetSet(env, dotpaths[1:], setVal)
|
|
case *Stack:
|
|
curStack = x
|
|
default:
|
|
return SexpNull, fmt.Errorf("not a record or scope: cannot get field '%s'"+
|
|
" out of type %T)", dotpaths[i+1][1:], x)
|
|
}
|
|
|
|
}
|
|
return ret, nil
|
|
}
|