Files
tablizer/vendor/github.com/glycerine/zygomys/zygo/builders.go
2024-05-14 12:10:58 +02:00

880 lines
23 KiB
Go

package zygo
import (
"fmt"
"reflect"
"strings"
"time"
)
// package.go: declare package, structs, function types
// A builder is a special kind of function. Like
// a macro it receives the un-evaluated tree
// of symbols from its caller. A builder
// can therefore be used to build new types
// and declarations new functions/methods.
//
// Like a function, a builder is called at
// run/evaluation time, not at definition time.
//
// Since it receives an un-evaluated tree of
// symbols, a builder must manually evaluate
// any arguments it wishes to find bindings for.
//
// The primary use here is to be able to define
// packages, structs, interfaces, functions,
// methods, and type aliases.
//
func (env *Zlisp) ImportPackageBuilder() {
env.AddBuilder("infixExpand", InfixBuilder)
env.AddBuilder("infix", InfixBuilder)
env.AddBuilder(":", ColonAccessBuilder)
env.AddBuilder("sys", SystemBuilder)
env.AddBuilder("struct", StructBuilder)
env.AddBuilder("func", FuncBuilder)
env.AddBuilder("method", FuncBuilder)
env.AddBuilder("interface", InterfaceBuilder)
//env.AddBuilder("package", PackageBuilder)
//env.AddBuilder("import", ImportBuilder)
env.AddBuilder("var", VarBuilder)
env.AddBuilder("expectError", ExpectErrorBuilder)
env.AddBuilder("comma", CommaBuilder)
// env.AddBuilder("&", AddressOfBuilder)
env.AddBuilder("import", ImportPackageBuilder)
env.AddFunction("sliceOf", SliceOfFunction)
env.AddFunction("ptr", PointerToFunction)
}
var sxSliceOf *SexpFunction = MakeUserFunction("sliceOf", SliceOfFunction)
var sxArrayOf *SexpFunction = MakeUserFunction("arrayOf", ArrayOfFunction)
type SexpUserVarDefn struct {
Name string
}
type RecordDefn struct {
Name string
Fields []*SexpField
FieldType map[string]*RegisteredType
}
func NewRecordDefn() *RecordDefn {
return &RecordDefn{
FieldType: make(map[string]*RegisteredType),
}
}
func (r *RecordDefn) SetName(name string) {
r.Name = name
}
func (r *RecordDefn) SetFields(flds []*SexpField) {
r.Fields = flds
for _, f := range flds {
g := (*SexpHash)(f)
rt, err := g.HashGet(nil, f.KeyOrder[0])
panicOn(err)
r.FieldType[g.KeyOrder[0].(*SexpSymbol).name] = rt.(*RegisteredType)
}
}
func (p *RecordDefn) Type() *RegisteredType {
rt := GoStructRegistry.Registry[p.Name]
//Q("RecordDefn) Type() sees rt = %v", rt)
return rt
}
// pretty print a struct
func (p *RecordDefn) SexpString(ps *PrintState) string {
Q("RecordDefn::SexpString() called!")
if len(p.Fields) == 0 {
return fmt.Sprintf("(struct %s)", p.Name)
}
s := fmt.Sprintf("(struct %s [\n", p.Name)
w := make([][]int, len(p.Fields))
maxnfield := 0
for i, f := range p.Fields {
w[i] = f.FieldWidths()
Q("w[i=%v] = %v", i, w[i])
maxnfield = maxi(maxnfield, len(w[i]))
}
biggestCol := make([]int, maxnfield)
Q("\n")
for j := 0; j < maxnfield; j++ {
for i := range p.Fields {
Q("i= %v, j=%v, len(w[i])=%v check=%v", i, j, len(w[i]), len(w[i]) < j)
if j < len(w[i]) {
biggestCol[j] = maxi(biggestCol[j], w[i][j]+1)
}
}
}
Q("RecordDefn::SexpString(): maxnfield = %v, out of %v", maxnfield, len(p.Fields))
Q("RecordDefn::SexpString(): biggestCol = %#v", biggestCol)
// computing padding
// x
// xx xx
// xxxxxxx x
// xxx x x x
//
// becomes
//
// x
// xx xx
// xxxxxxx
// xxx x x x
Q("pad = %#v", biggestCol)
for _, f := range p.Fields {
s += " " + f.AlignString(biggestCol) + "\n"
}
s += " ])\n"
return s
}
func maxi(a, b int) int {
if a > b {
return a
}
return b
}
type SexpField SexpHash
func (r SexpField) Type() *RegisteredType {
return r.GoStructFactory
}
// compute key and value widths to assist alignment
func (f *SexpField) FieldWidths() []int {
hash := (*SexpHash)(f)
wide := []int{}
for _, key := range hash.KeyOrder {
val, err := hash.HashGet(nil, key)
str := ""
if err == nil {
switch s := key.(type) {
case *SexpStr:
str += s.S + ":"
case *SexpSymbol:
str += s.name + ":"
default:
str += key.SexpString(nil) + ":"
}
wide = append(wide, len(str))
wide = append(wide, len(val.SexpString(nil))+1)
} else {
panic(err)
}
}
return wide
}
func (f *SexpField) AlignString(pad []int) string {
hash := (*SexpHash)(f)
str := " (" + hash.TypeName + " "
spc := " "
for i, key := range hash.KeyOrder {
val, err := hash.HashGet(nil, key)
r := ""
if err == nil {
switch s := key.(type) {
case *SexpStr:
r += s.S + ":"
case *SexpSymbol:
r += s.name + ":"
default:
r += key.SexpString(nil) + ":"
}
xtra := pad[i*2] - len(r)
if xtra < 0 {
panic(fmt.Sprintf("xtra = %d, pad[i=%v]=%v, len(r)=%v (r=%v)", xtra, i, pad[i], len(r), r))
}
leftpad := strings.Repeat(" ", xtra)
vs := val.SexpString(nil)
rightpad := strings.Repeat(" ", pad[(i*2)+1]-len(vs))
if i == 0 {
spc = " "
} else {
spc = ""
}
r = leftpad + r + spc + vs + rightpad
} else {
panic(err)
}
str += r
}
if len(hash.Map) > 0 {
return str[:len(str)-1] + ")"
}
return str + ")"
}
func (f *SexpField) SexpString(ps *PrintState) string {
hash := (*SexpHash)(f)
str := " (" + hash.TypeName + " "
for i, key := range hash.KeyOrder {
val, err := hash.HashGet(nil, key)
if err == nil {
switch s := key.(type) {
case *SexpStr:
str += s.S + ":"
case *SexpSymbol:
str += s.name + ":"
default:
str += key.SexpString(nil) + ":"
}
if i > 0 {
str += val.SexpString(nil) + " "
} else {
str += val.SexpString(nil) + " "
}
} else {
panic(err)
}
}
if len(hash.Map) > 0 {
return str[:len(str)-1] + ")"
}
return str + ")"
}
func StructBuilder(env *Zlisp, name string,
args []Sexp) (Sexp, error) {
n := len(args)
if n < 1 {
return SexpNull, fmt.Errorf("struct name is missing. use: " +
"(struct struct-name ...)\n")
}
Q("in struct builder, name = '%s', args = ", name)
for i := range args {
Q("args[%v] = '%s' of type %T", i, args[i].SexpString(nil), args[i])
}
var symN *SexpSymbol
switch b := args[0].(type) {
case *SexpSymbol:
symN = b
case *SexpPair:
sy, isQuo := isQuotedSymbol(b)
if isQuo {
symN = sy.(*SexpSymbol)
} else {
return SexpNull, fmt.Errorf("bad struct name: symbol required")
}
default:
return SexpNull, fmt.Errorf("bad struct name: symbol required")
}
Q("good: have struct name '%v'", symN)
env.datastack.PushExpr(SexpNull)
structName := symN.name
{
// begin enable recursion -- add ourselves to the env early, then
// update later, so that structs can refer to themselves.
udsR := NewRecordDefn()
udsR.SetName(structName)
rtR := NewRegisteredType(func(env *Zlisp, h *SexpHash) (interface{}, error) {
return udsR, nil
})
rtR.UserStructDefn = udsR
rtR.DisplayAs = structName
GoStructRegistry.RegisterUserdef(rtR, false, structName)
// overwrite any existing definition, deliberately ignore any error,
// as there may not be a prior definition present at all.
env.linearstack.DeleteSymbolFromTopOfStackScope(symN)
err := env.LexicalBindSymbol(symN, rtR)
if err != nil {
return SexpNull, fmt.Errorf("struct builder could not bind symbol '%s': '%v'",
structName, err)
}
// end enable recursion
}
var xar []Sexp
var flat []*SexpField
if n > 2 {
return SexpNull, fmt.Errorf("bad struct declaration: more than two arguments." +
"prototype is (struct name [(field ...)*] )")
}
if n == 2 {
Q("in case n == 2")
switch ar := args[1].(type) {
default:
return SexpNull, fmt.Errorf("bad struct declaration '%v': second argument "+
"must be a slice of fields."+
" prototype is (struct name [(field ...)*] )", structName)
case *SexpArray:
arr := ar.Val
if len(arr) == 0 {
// allow this
} else {
// dup to avoid messing with the stack on eval:
//dup := env.Duplicate()
for i, ele := range arr {
Q("about to eval i=%v", i)
//ev, err := dup.EvalExpressions([]Sexp{ele})
ev, err := EvalFunction(env, "evalStructBuilder", []Sexp{ele})
Q("done with eval i=%v. ev=%v", i, ev.SexpString(nil))
if err != nil {
return SexpNull, fmt.Errorf("bad struct declaration '%v': bad "+
"field at array entry %v; error was '%v'", structName, i, err)
}
Q("checking for isHash at i=%v", i)
asHash, isHash := ev.(*SexpField)
if !isHash {
Q("was not hash, instead was %T", ev)
return SexpNull, fmt.Errorf("bad struct declaration '%v': bad "+
"field array at entry %v; a (field ...) is required. Instead saw '%T'/with value = '%v'",
structName, i, ev, ev.SexpString(nil))
}
Q("good eval i=%v, ev=%#v / %v", i, ev, ev.SexpString(nil))
ko := asHash.KeyOrder
if len(ko) == 0 {
return SexpNull, fmt.Errorf("bad struct declaration '%v': bad "+
"field array at entry %v; field had no name",
structName, i)
}
Q("ko = '%#v'", ko)
first := ko[0]
Q("first = '%#v'", first)
xar = append(xar, first)
xar = append(xar, ev)
flat = append(flat, ev.(*SexpField))
}
Q("no err from EvalExpressions, got xar = '%#v'", xar)
}
}
} // end n == 2
uds := NewRecordDefn()
uds.SetName(structName)
uds.SetFields(flat)
Q("good: made typeDefnHash: '%s'", uds.SexpString(nil))
rt := NewRegisteredType(func(env *Zlisp, h *SexpHash) (interface{}, error) {
return uds, nil
})
rt.UserStructDefn = uds
rt.DisplayAs = structName
GoStructRegistry.RegisterUserdef(rt, false, structName)
Q("good: registered new userdefined struct '%s'", structName)
// replace our recursive-reference-enabling symbol with the real one.
err := env.linearstack.DeleteSymbolFromTopOfStackScope(symN)
if err != nil {
return SexpNull, fmt.Errorf("internal error: should have already had symbol '%s' "+
"bound, but DeleteSymbolFromTopOfStackScope returned error: '%v'",
symN.name, err)
}
err = env.LexicalBindSymbol(symN, rt)
if err != nil {
return SexpNull, fmt.Errorf("late: struct builder could not bind symbol '%s': '%v'",
structName, err)
}
Q("good: bound symbol '%s' to RegisteredType '%s'", symN.SexpString(nil), rt.SexpString(nil))
return rt, nil
}
func InterfaceBuilder(env *Zlisp, name string,
args []Sexp) (Sexp, error) {
nargs := len(args)
switch {
case nargs < 1:
return SexpNull, fmt.Errorf("interface name is missing. use: " +
"(interface interface-name [...])\n")
case nargs == 1:
return SexpNull, fmt.Errorf("interface array of methods missing. use: " +
"(interface interface-name [...])\n")
case nargs > 2:
return SexpNull, WrongNargs
}
// P("in interface builder, past arg check")
var iname string
var symN *SexpSymbol
switch sy := args[0].(type) {
case *SexpSymbol:
symN = sy
iname = sy.name
default:
return SexpNull, fmt.Errorf("interface name must be a symbol; we got %T", args[0])
}
// sanity check the name
builtin, builtTyp := env.IsBuiltinSym(symN)
if builtin {
return SexpNull,
fmt.Errorf("already have %s '%s', refusing to overwrite with interface",
builtTyp, symN.name)
}
if env.HasMacro(symN) {
return SexpNull, fmt.Errorf("Already have macro named '%s': refusing"+
" to define interface of same name.", symN.name)
}
// end sanity check the name
var arrMeth *SexpArray
switch ar := args[1].(type) {
case *SexpArray:
arrMeth = ar
default:
return SexpNull, fmt.Errorf("interface method vector expected after name; we got %T", args[1])
}
// P("in interface builder, args = ")
// for i := range args {
// P("args[%v] = '%s'", i, args[i].SexpString(nil))
// }
methods := make([]*SexpFunction, 0)
methodSlice := arrMeth.Val
if len(methodSlice) > 0 {
//dup := env.Duplicate()
for i := range methodSlice {
//ev, err := dup.EvalExpressions([]Sexp{methodSlice[i]})
ev, err := EvalFunction(env, "evalInterface", []Sexp{methodSlice[i]})
if err != nil {
return SexpNull, fmt.Errorf("error parsing the %v-th method in interface definition: '%v'", i, err)
}
me, gotFunc := ev.(*SexpFunction)
if !gotFunc {
return SexpNull,
fmt.Errorf("error parsing the %v-th method in interface: was not function but rather %T",
i, ev)
}
methods = append(methods, me)
}
}
decl := &SexpInterfaceDecl{
name: iname,
methods: methods,
}
return decl, nil
}
func SliceOfFunction(env *Zlisp, name string,
args []Sexp) (Sexp, error) {
if len(args) != 1 {
return SexpNull, fmt.Errorf("argument x to (%s x) is missing. use: "+
"(%s a-regtype)\n", name, name)
}
Q("in SliceOfFunction")
var rt *RegisteredType
switch arg := args[0].(type) {
case *RegisteredType:
rt = arg
case *SexpHash:
rt = arg.GoStructFactory
default:
return SexpNull, fmt.Errorf("argument tx in (%s x) was not regtype, "+
"instead type %T displaying as '%v' ",
name, arg, arg.SexpString(nil))
}
Q("sliceOf arg = '%s' with type %T", args[0].SexpString(nil), args[0])
sliceRt := GoStructRegistry.GetOrCreateSliceType(rt)
Q("in SliceOfFunction: returning sliceRt = '%#v'", sliceRt)
return sliceRt, nil
}
func PointerToFunction(env *Zlisp, name string,
args []Sexp) (Sexp, error) {
if len(args) != 1 {
return SexpNull, fmt.Errorf("argument to pointer-to is missing. use: "+
"(%s a-regtype)\n", name)
}
//P("in PointerToFunction(): args[0] = '%#v'", args[0])
var rt *RegisteredType
switch arg := args[0].(type) {
case *RegisteredType:
rt = arg
case *SexpHash:
rt = arg.GoStructFactory
case *SexpPointer:
// dereference operation, rather than type declaration
//P("dereference operation on *SexpPointer detected, returning target")
if arg == nil || arg.Target == nil {
return SexpNull, fmt.Errorf("illegal to dereference nil pointer")
}
return arg.Target, nil
case *SexpReflect:
Q("dereference operation on SexpReflect detected")
// TODO what goes here?
return SexpNull, fmt.Errorf("illegal to dereference nil pointer")
case *SexpSymbol:
if arg.isDot {
// (* h.a) dereferencing a dot symbol
resolved, err := dotGetSetHelper(env, arg.name, nil)
if err != nil {
return nil, err
}
return resolved, nil
} else {
panic("TODO: what goes here, for (* sym) where sym is a regular symbol")
}
default:
return SexpNull, fmt.Errorf("argument x in (%s x) was not regtype or SexpPointer, "+
"instead type %T displaying as '%v' ",
name, arg, arg.SexpString(nil))
}
Q("pointer-to arg = '%s' with type %T", args[0].SexpString(nil), args[0])
ptrRt := GoStructRegistry.GetOrCreatePointerType(rt)
return ptrRt, nil
}
func StructConstructorFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
Q("in struct ctor, name = '%s', args = %#v", name, args)
return MakeHash(args, name, env)
}
func BaseTypeConstructorFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) {
Q("in base type ctor, args = '%#v'", args)
if len(args) < 1 {
return SexpNull, WrongNargs
}
Q("in base ctor, name = '%s', args = %#v", name, args)
return SexpNull, nil
}
func baseConstruct(env *Zlisp, f *RegisteredType, nargs int) (Sexp, error) {
if nargs > 1 {
return SexpNull, fmt.Errorf("%d is too many arguments for a base type constructor", nargs)
}
v, err := f.Factory(env, nil)
if err != nil {
return SexpNull, err
}
Q("see call to baseConstruct, v = %v/type=%T", v, v)
if nargs == 0 {
switch v.(type) {
case *int, *uint8, *uint16, *uint32, *uint64, *int8, *int16, *int32, *int64:
return &SexpInt{}, nil
case *float32, *float64:
return &SexpFloat{}, nil
case *string:
return &SexpStr{S: ""}, nil
case *bool:
return &SexpBool{Val: false}, nil
case *time.Time:
return &SexpTime{}, nil
default:
return SexpNull, fmt.Errorf("unhandled no-arg case in baseConstruct, v has type=%T", v)
}
}
// get our one argument
args, err := env.datastack.PopExpressions(1)
if err != nil {
return SexpNull, err
}
arg := args[0]
switch v.(type) {
case *int, *uint8, *uint16, *uint32, *uint64, *int8, *int16, *int32, *int64:
myint, ok := arg.(*SexpInt)
if !ok {
return SexpNull, fmt.Errorf("cannot convert %T to int", arg)
}
return myint, nil
case *float32, *float64:
myfloat, ok := arg.(*SexpFloat)
if !ok {
return SexpNull, fmt.Errorf("cannot convert %T to float", arg)
}
return myfloat, nil
case *string:
mystring, ok := arg.(*SexpStr)
if !ok {
return SexpNull, fmt.Errorf("cannot convert %T to string", arg)
}
return mystring, nil
case *bool:
mybool, ok := arg.(*SexpBool)
if !ok {
return SexpNull, fmt.Errorf("cannot convert %T to bool", arg)
}
return mybool, nil
default:
return SexpNull, fmt.Errorf("unhandled case in baseConstruct, arg = %#v/type=%T", arg, arg)
}
//return SexpNull, fmt.Errorf("unhandled no-arg case in baseConstruct, v has type=%T", v)
}
// generate fixed size array
func ArrayOfFunction(env *Zlisp, name string,
args []Sexp) (Sexp, error) {
if len(args) != 2 {
return SexpNull, fmt.Errorf("insufficient arguments to ([size] regtype) array constructor. use: " +
"([size...] a-regtype)\n")
}
sz := 0
Q("args = %#v in ArrayOfFunction", args)
switch ar := args[1].(type) {
case *SexpArray:
if len(ar.Val) == 0 {
return SexpNull, fmt.Errorf("at least one size must be specified in array constructor; e.g. ([size ...] regtype)")
}
asInt, isInt := ar.Val[0].(*SexpInt)
if !isInt {
return SexpNull, fmt.Errorf("size must be an int (not %T) in array constructor; e.g. ([size ...] regtype)", ar.Val[0])
}
sz = int(asInt.Val)
// TODO: implement multiple dimensional arrays (matrixes etc).
default:
return SexpNull, fmt.Errorf("at least one size must be specified in array constructor; e.g. ([size ...] regtype)")
}
var rt *RegisteredType
switch arg := args[0].(type) {
case *RegisteredType:
rt = arg
case *SexpHash:
rt = arg.GoStructFactory
default:
return SexpNull, fmt.Errorf("argument tx in (%s x) was not regtype, "+
"instead type %T displaying as '%v' ",
name, arg, arg.SexpString(nil))
}
//Q("arrayOf arg = '%s' with type %T", args[0].SexpString(nil), args[0])
derivedType := reflect.ArrayOf(sz, rt.TypeCache)
arrayRt := NewRegisteredType(func(env *Zlisp, h *SexpHash) (interface{}, error) {
return reflect.New(derivedType), nil
})
arrayRt.DisplayAs = fmt.Sprintf("(%s %s)", name, rt.DisplayAs)
arrayName := "arrayOf" + rt.RegisteredName
GoStructRegistry.RegisterUserdef(arrayRt, false, arrayName)
return arrayRt, nil
}
func VarBuilder(env *Zlisp, name string,
args []Sexp) (Sexp, error) {
n := len(args)
if n != 2 {
return SexpNull, fmt.Errorf("var name/type missing. use: " +
"(var name regtype)\n")
}
Q("in var builder, name = '%s', args = ", name)
for i := range args {
Q("args[%v] = '%s' of type %T", i, args[i].SexpString(nil), args[i])
}
var symN *SexpSymbol
switch b := args[0].(type) {
case *SexpSymbol:
symN = b
case *SexpPair:
sy, isQuo := isQuotedSymbol(b)
if isQuo {
symN = sy.(*SexpSymbol)
} else {
return SexpNull, fmt.Errorf("bad var name: symbol required")
}
default:
return SexpNull, fmt.Errorf("bad var name: symbol required")
}
Q("good: have var name '%v'", symN)
//dup := env.Duplicate()
Q("about to eval args[1]=%v", args[1])
//ev, err := dup.EvalExpressions(args[1:2])
ev, err := EvalFunction(env, "evalVar", args[1:2])
Q("done with eval, ev=%v / type %T", ev.SexpString(nil), ev)
if err != nil {
return SexpNull, fmt.Errorf("bad var declaration, problem with type '%v': %v", args[1].SexpString(nil), err)
}
var rt *RegisteredType
switch myrt := ev.(type) {
case *RegisteredType:
rt = myrt
default:
return SexpNull, fmt.Errorf("bad var declaration, type '%v' is unknown", rt.SexpString(nil))
}
val, err := rt.Factory(env, nil)
if err != nil {
return SexpNull, fmt.Errorf("var declaration error: could not make type '%s': %v",
rt.SexpString(nil), err)
}
var valSexp Sexp
Q("val is of type %T", val)
switch v := val.(type) {
case Sexp:
valSexp = v
case reflect.Value:
Q("v is of type %T", v.Interface())
switch rd := v.Interface().(type) {
case ***RecordDefn:
Q("we have RecordDefn rd = %#v", *rd)
}
valSexp = &SexpReflect{Val: reflect.ValueOf(v)}
default:
valSexp = &SexpReflect{Val: reflect.ValueOf(v)}
}
Q("var decl: valSexp is '%v'", valSexp.SexpString(nil))
err = env.LexicalBindSymbol(symN, valSexp)
if err != nil {
return SexpNull, fmt.Errorf("var declaration error: could not bind symbol '%s': %v",
symN.name, err)
}
Q("good: var decl bound symbol '%s' to '%s' of type '%s'", symN.SexpString(nil), valSexp.SexpString(nil), rt.SexpString(nil))
env.datastack.PushExpr(valSexp)
return SexpNull, nil
}
func ExpectErrorBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
narg := len(args)
if narg != 2 {
return SexpNull, WrongNargs
}
dup := env.Duplicate()
es, err := dup.EvalExpressions(args[0:1])
if err != nil {
return SexpNull, fmt.Errorf("error evaluating the error string to expect: %s", err)
}
var expectedError *SexpStr
switch e := es.(type) {
case *SexpStr:
expectedError = e
default:
return SexpNull, fmt.Errorf("first arg to expectError must be the string of the error to expect")
}
Q("expectedError = %v", expectedError)
ev, err := dup.EvalExpressions(args[1:2])
Q("done with eval, ev=%v / type %T. err = %v", ev.SexpString(nil), ev, err)
if err != nil {
if err.Error() == expectedError.S {
return SexpNull, nil
}
return SexpNull, fmt.Errorf("expectError expected '%s' but saw '%s'", expectedError.S, err)
}
if expectedError.S == "" {
return SexpNull, nil
}
return SexpNull, fmt.Errorf("expectError expected '%s' but got no error", expectedError.S)
}
func ColonAccessBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
if len(args) < 1 || len(args) > 3 {
return SexpNull, WrongNargs
}
////Q("ColonAccessBuilder, args = %#v", args)
name = "hget"
//dup := env.Duplicate()
//collec, err := dup.EvalExpressions(args[1:2])
collec, err := EvalFunction(env, "evalColonAccess", args[1:2])
if err != nil {
return SexpNull, err
}
swapped := args
swapped[1] = swapped[0]
swapped[0] = collec
if len(args) == 3 {
// have default, needs eval too
//defaul, err := dup.EvalExpressions(args[2:3])
defaul, err := EvalFunction(env, "evalColonDefault", args[2:3])
if err != nil {
return SexpNull, err
}
swapped[2] = defaul
}
switch sx := collec.(type) {
case *SexpHash:
return HashAccessFunction(env, name, swapped)
case *SexpArray:
return ArrayAccessFunction(env, name, swapped)
case *SexpArraySelector:
Q("*SexpSelector seen in : operator form.")
return sx.RHS(env)
}
return SexpNull, fmt.Errorf("second argument to ':' function must be hash or array")
}
// CommaBuilder turns expressions on the LHS and RHS like {a,b,c = 1,2,3}
// into arrays (set [a b c] [1 2 3])
func CommaBuilder(env *Zlisp, name string, args []Sexp) (Sexp, error) {
n := len(args)
if n < 1 {
return SexpNull, nil
}
res := make([]Sexp, 0)
for i := range args {
commaHelper(args[i], &res)
}
return &SexpArray{Val: res}, nil
}
func commaHelper(a Sexp, res *[]Sexp) {
//Q("top of commaHelper with a = '%s'", a.SexpString(nil))
switch x := a.(type) {
case *SexpPair:
sy, isQuo := isQuotedSymbol(x)
if isQuo {
symN := sy.(*SexpSymbol)
//Q("have quoted symbol symN=%v", symN.SexpString(nil))
*res = append(*res, symN)
return
}
ar, err := ListToArray(x)
if err != nil || len(ar) < 1 {
return
}
switch sym := ar[0].(type) {
case *SexpSymbol:
if sym.name == "comma" {
//Q("have recursive comma")
over := ar[1:]
for i := range over {
commaHelper(over[i], res)
}
} else {
//Q("have symbol sym = '%v'", sym.SexpString(nil))
*res = append(*res, a)
}
default:
*res = append(*res, a)
}
default:
*res = append(*res, a)
}
}