mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-19 05:21:03 +01:00
added
This commit is contained in:
339
vendor/github.com/glycerine/zygomys/zygo/scopes.go
generated
vendored
Normal file
339
vendor/github.com/glycerine/zygomys/zygo/scopes.go
generated
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
package zygo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Scopes map names to values. Scope nesting avoids variable name collisions and
|
||||
// allows namespace maintainance. Most scopes (inside loops, inside functions)
|
||||
// are implicitly created. Packages are scopes that the user can manipulate
|
||||
// explicitly.
|
||||
type Scope struct {
|
||||
Map map[int]Sexp
|
||||
IsGlobal bool
|
||||
Name string
|
||||
PackageName string
|
||||
Parent *Scope
|
||||
IsFunction bool // if true, read-only.
|
||||
MyFunction *SexpFunction // so we can query captured closure scopes.
|
||||
IsPackage bool
|
||||
env *Zlisp
|
||||
}
|
||||
|
||||
// SexpString satisfies the Sexp interface, producing a string presentation of the value.
|
||||
func (s *Scope) SexpString(ps *PrintState) string {
|
||||
var label string
|
||||
head := ""
|
||||
if s.IsPackage {
|
||||
head = "(package " + s.PackageName
|
||||
} else {
|
||||
label = "scope " + s.Name
|
||||
if s.IsGlobal {
|
||||
label += " (global)"
|
||||
}
|
||||
}
|
||||
|
||||
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 *Scope) Type() *RegisteredType {
|
||||
return GoStructRegistry.Lookup("packageScope")
|
||||
}
|
||||
|
||||
func (env *Zlisp) NewScope() *Scope {
|
||||
return &Scope{
|
||||
Map: make(map[int]Sexp),
|
||||
env: env,
|
||||
}
|
||||
}
|
||||
|
||||
func (env *Zlisp) NewNamedScope(name string) *Scope {
|
||||
return &Scope{
|
||||
Map: make(map[int]Sexp),
|
||||
Name: name,
|
||||
env: env,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scope) CloneScope() *Scope {
|
||||
n := s.env.NewScope()
|
||||
for k, v := range s.Map {
|
||||
n.Map[k] = v
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (s Scope) IsStackElem() {}
|
||||
|
||||
func (stack *Stack) PushScope() {
|
||||
s := stack.env.NewScope()
|
||||
if stack.Size() > 0 {
|
||||
s.Parent = stack.GetTop().(*Scope)
|
||||
}
|
||||
stack.Push(s)
|
||||
}
|
||||
|
||||
func (stack *Stack) PopScope() error {
|
||||
_, err := stack.Pop()
|
||||
return err
|
||||
}
|
||||
|
||||
// dynamic scoping lookup. See env.LexicalLookupSymbol() for the lexically
|
||||
// scoped equivalent.
|
||||
// If setVal is not nil, and if we find the symbol, we set it in the scope
|
||||
// where it was found. This is equivalent to scope.UpdateSymbolInScope.
|
||||
//
|
||||
func (stack *Stack) lookupSymbol(sym *SexpSymbol, minFrame int, setVal *Sexp) (Sexp, error, *Scope) {
|
||||
if !stack.IsEmpty() {
|
||||
for i := 0; i <= stack.tos-minFrame; i++ {
|
||||
//P("lookupSymbol checking stack %v of %v", i, (stack.tos-minFrame)+1)
|
||||
elem, err := stack.Get(i)
|
||||
if err != nil {
|
||||
//P("lookupSymbol bailing (early?) at i=%v on err='%v'", i, err)
|
||||
return SexpNull, err, nil
|
||||
}
|
||||
switch scope := elem.(type) {
|
||||
case (*Scope):
|
||||
expr, ok := scope.Map[sym.number]
|
||||
if ok {
|
||||
//P("lookupSymbol at stack scope# i=%v, we found sym '%s' with value '%s'", i, sym.name, expr.SexpString(0))
|
||||
if setVal != nil {
|
||||
scope.Map[sym.number] = *setVal
|
||||
}
|
||||
return expr, nil, scope
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//P("lookupSymbol finished stack scan without finding it")
|
||||
if stack.env != nil && stack.env.debugSymbolNotFound {
|
||||
stack.env.ShowStackStackAndScopeStack()
|
||||
}
|
||||
return SexpNull, fmt.Errorf("alas, symbol `%s` not found", sym.name), nil
|
||||
}
|
||||
|
||||
func (stack *Stack) LookupSymbol(sym *SexpSymbol, setVal *Sexp) (Sexp, error, *Scope) {
|
||||
return stack.lookupSymbol(sym, 0, setVal)
|
||||
}
|
||||
|
||||
// LookupSymbolNonGlobal - closures use this to only find symbols below the global scope, to avoid copying globals it'll always be-able to ref
|
||||
func (stack *Stack) LookupSymbolNonGlobal(sym *SexpSymbol) (Sexp, error, *Scope) {
|
||||
return stack.lookupSymbol(sym, 1, nil)
|
||||
}
|
||||
|
||||
var SymNotFound = errors.New("symbol not found")
|
||||
|
||||
// lookup symbols, but don't go beyond a function boundary -- a user-defined
|
||||
// function boundary that is. We certainly have to go up beyond
|
||||
// all built-in operators like '+' and '-', '*' and '/'.
|
||||
func (stack *Stack) LookupSymbolUntilFunction(sym *SexpSymbol, setVal *Sexp, maximumFuncToSearch int, checkCaptures bool) (Sexp, error, *Scope) {
|
||||
|
||||
funcCount := 0
|
||||
if !stack.IsEmpty() {
|
||||
doneSearching:
|
||||
for i := 0; i <= stack.tos; i++ {
|
||||
elem, err := stack.Get(i)
|
||||
if err != nil {
|
||||
return SexpNull, err, nil
|
||||
}
|
||||
switch scope := elem.(type) {
|
||||
case (*Scope):
|
||||
VPrintf(" ...looking up in scope '%s'\n", scope.Name)
|
||||
expr, ok := scope.Map[sym.number]
|
||||
if ok {
|
||||
if setVal != nil {
|
||||
scope.UpdateSymbolInScope(sym, *setVal)
|
||||
}
|
||||
return expr, nil, scope
|
||||
}
|
||||
if scope.IsFunction {
|
||||
funcCount++
|
||||
//P(" ...scope '%s' was a function, halting up search and checking captured closures\n", scope.Name)
|
||||
|
||||
if checkCaptures {
|
||||
// check the captured closure scope stack
|
||||
|
||||
exp, err, whichScope := scope.MyFunction.ClosingLookupSymbol(sym, setVal)
|
||||
switch err {
|
||||
case nil:
|
||||
//P("LookupSymbolUntilFunction('%s') found in scope '%s'\n", sym.name, whichScope.Name)
|
||||
return exp, err, whichScope
|
||||
}
|
||||
}
|
||||
|
||||
// no luck inside the captured closure scopes.
|
||||
if funcCount >= maximumFuncToSearch {
|
||||
break doneSearching
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if stack != nil && stack.env != nil && stack.env.debugSymbolNotFound {
|
||||
fmt.Printf("debugSymbolNotFound is true, here are scopes:\n")
|
||||
stack.env.ShowStackStackAndScopeStack()
|
||||
}
|
||||
return SexpNull, SymNotFound, nil
|
||||
}
|
||||
|
||||
func (stack *Stack) BindSymbol(sym *SexpSymbol, expr Sexp) error {
|
||||
if stack.IsEmpty() {
|
||||
panic("empty stack!!")
|
||||
}
|
||||
cur, already := stack.elements[stack.tos].(*Scope).Map[sym.number]
|
||||
if already {
|
||||
Q("BindSymbol already sees symbol %v, currently bound to '%v'", sym.name, cur.SexpString(nil))
|
||||
|
||||
lhsTy := cur.Type()
|
||||
rhsTy := expr.Type()
|
||||
if lhsTy == nil {
|
||||
// for backcompat with closure.zy, just do the binding for now if the LHS isn't typed.
|
||||
//return fmt.Errorf("left-hand-side had nil type")
|
||||
// TODO: fix this? or require removal of previous symbol binding to avoid type errors?
|
||||
stack.elements[stack.tos].(*Scope).Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
if rhsTy == nil {
|
||||
// meh, we need to be able to assign nil to stuff without freaking out,
|
||||
// so force type match
|
||||
rhsTy = lhsTy
|
||||
|
||||
//return fmt.Errorf("right-hand-side had nil type back from Type() call; val = '%s'/%T", expr.SexpString(nil), expr)
|
||||
}
|
||||
|
||||
// both sides have type
|
||||
Q("BindSymbol: both sides have type. rhs=%v, lhs=%v", rhsTy.SexpString(nil), lhsTy.SexpString(nil))
|
||||
|
||||
if lhsTy == rhsTy {
|
||||
Q("BindSymbol: YES types match exactly. Good.")
|
||||
stack.elements[stack.tos].(*Scope).Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
|
||||
if rhsTy.UserStructDefn != nil && rhsTy.UserStructDefn != lhsTy.UserStructDefn {
|
||||
return fmt.Errorf("cannot assign %v to %v", rhsTy.ShortName(), lhsTy.ShortName())
|
||||
}
|
||||
|
||||
if lhsTy.UserStructDefn != nil && lhsTy.UserStructDefn != rhsTy.UserStructDefn {
|
||||
return fmt.Errorf("cannot assign %v to %v", rhsTy.ShortName(), lhsTy.ShortName())
|
||||
}
|
||||
|
||||
// TODO: problem with this implementation is that it may narrow the possible
|
||||
// types assignments to this variable. To fix we'll need to keep around the
|
||||
// type of the symbol in the symbol table, separately from the value currently
|
||||
// bound to it.
|
||||
if lhsTy.TypeCache != nil && rhsTy.TypeCache != nil {
|
||||
if rhsTy.TypeCache.AssignableTo(lhsTy.TypeCache) {
|
||||
Q("BindSymbol: YES: rhsTy.TypeCache (%v) is AssigntableTo(lhsTy.TypeCache) (%v). Good.", rhsTy.TypeCache, lhsTy.TypeCache)
|
||||
stack.elements[stack.tos].(*Scope).Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Q("BindSymbol: at end, defaulting to deny")
|
||||
return fmt.Errorf("cannot assign %v to %v", rhsTy.ShortName(), lhsTy.ShortName())
|
||||
} else {
|
||||
Q("BindSymbol: new symbol %v", sym.name)
|
||||
}
|
||||
stack.elements[stack.tos].(*Scope).Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stack *Stack) DeleteSymbolFromTopOfStackScope(sym *SexpSymbol) error {
|
||||
if stack.IsEmpty() {
|
||||
panic("empty stack!!")
|
||||
//return errors.New("no scope available")
|
||||
}
|
||||
_, present := stack.elements[stack.tos].(*Scope).Map[sym.number]
|
||||
if !present {
|
||||
return fmt.Errorf("symbol `%s` not found", sym.name)
|
||||
}
|
||||
delete(stack.elements[stack.tos].(*Scope).Map, sym.number)
|
||||
return nil
|
||||
}
|
||||
|
||||
// used to implement (set v 10)
|
||||
func (scope *Scope) UpdateSymbolInScope(sym *SexpSymbol, expr Sexp) error {
|
||||
|
||||
_, found := scope.Map[sym.number]
|
||||
if !found {
|
||||
return fmt.Errorf("symbol `%s` not found", sym.name)
|
||||
}
|
||||
scope.Map[sym.number] = expr
|
||||
return nil
|
||||
}
|
||||
|
||||
func (scope *Scope) DeleteSymbolInScope(sym *SexpSymbol) error {
|
||||
|
||||
_, found := scope.Map[sym.number]
|
||||
if !found {
|
||||
return fmt.Errorf("symbol `%s` not found", sym.name)
|
||||
}
|
||||
delete(scope.Map, sym.number)
|
||||
return nil
|
||||
}
|
||||
|
||||
type SymtabE struct {
|
||||
Key string
|
||||
Val string
|
||||
}
|
||||
|
||||
type SymtabSorter []*SymtabE
|
||||
|
||||
func (a SymtabSorter) Len() int { return len(a) }
|
||||
func (a SymtabSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a SymtabSorter) Less(i, j int) bool { return a[i].Key < a[j].Key }
|
||||
|
||||
func (scop *Scope) Show(env *Zlisp, ps *PrintState, label string) (s string, err error) {
|
||||
//P("scop %p Show() starting, PackageName: '%s' IsGlobal: %v", scop, scop.PackageName, scop.IsGlobal)
|
||||
if ps == nil {
|
||||
ps = NewPrintState()
|
||||
}
|
||||
if ps.GetSeen(scop) {
|
||||
// This check is critical to prevent infinite looping in a cycle.
|
||||
// Scopes like global are referenced by every package, and
|
||||
// nested scopes refer to their paranets, so nesting
|
||||
// two packages will loop forever without this check.
|
||||
|
||||
// debug version: return fmt.Sprintf("already-saw Scope %p with scop.PackageName='%s'\n", scop, scop.PackageName), nil
|
||||
return "", nil
|
||||
} else {
|
||||
ps.SetSeen(scop, "Scope")
|
||||
}
|
||||
indent := ps.GetIndent()
|
||||
rep := strings.Repeat(" ", indent)
|
||||
rep4 := strings.Repeat(" ", indent+4)
|
||||
s += fmt.Sprintf("%s %s %s (%p)\n", rep, label, scop.Name, scop)
|
||||
if scop.IsGlobal && !env.showGlobalScope {
|
||||
s += fmt.Sprintf("%s (global scope - omitting content for brevity)\n", rep4)
|
||||
return
|
||||
}
|
||||
if len(scop.Map) == 0 {
|
||||
s += fmt.Sprintf("%s empty-scope: no symbols\n", rep4)
|
||||
return
|
||||
}
|
||||
sortme := []*SymtabE{}
|
||||
for symbolNumber, val := range scop.Map {
|
||||
symbolName := env.revsymtable[symbolNumber]
|
||||
sortme = append(sortme, &SymtabE{Key: symbolName, Val: val.SexpString(ps)})
|
||||
}
|
||||
sort.Sort(SymtabSorter(sortme))
|
||||
for i := range sortme {
|
||||
s += fmt.Sprintf("%s %s -> %s\n", rep4,
|
||||
sortme[i].Key, sortme[i].Val)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type Showable interface {
|
||||
Show(env *Zlisp, ps *PrintState, label string) (string, error)
|
||||
}
|
||||
Reference in New Issue
Block a user