mirror of
https://codeberg.org/scip/tablizer.git
synced 2025-12-18 04:51:05 +01:00
880 lines
23 KiB
Go
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)
|
|
}
|
|
}
|