package zygo import ( "bytes" "fmt" "os" "reflect" "runtime" "unicode" ) var WrongNargs error = fmt.Errorf("wrong number of arguments") type ZlispFunction []Instruction type ZlispUserFunction func(*Zlisp, string, []Sexp) (Sexp, error) func CompareFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, WrongNargs } res, err := env.Compare(args[0], args[1]) if err != nil { return SexpNull, err } if res > 1 { //fmt.Printf("CompareFunction, res = %v\n", res) // 2 => one NaN found // 3 => two NaN found // NaN != NaN needs to return true. // NaN != 3.0 needs to return true. if name == "!=" { return &SexpBool{Val: true}, nil } return &SexpBool{Val: false}, nil } cond := false switch name { case "<": cond = res < 0 case ">": cond = res > 0 case "<=": cond = res <= 0 case ">=": cond = res >= 0 case "==": cond = res == 0 case "!=": cond = res != 0 } return &SexpBool{Val: cond}, nil } func BinaryIntFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, WrongNargs } var op IntegerOp switch name { case "sll": op = ShiftLeft case "sra": op = ShiftRightArith case "srl": op = ShiftRightLog case "mod": op = Modulo } return IntegerDo(op, args[0], args[1]) } func BitwiseFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, WrongNargs } var op IntegerOp switch name { case "bitAnd": op = BitAnd case "bitOr": op = BitOr case "bitXor": op = BitXor } accum := args[0] var err error for _, expr := range args[1:] { accum, err = IntegerDo(op, accum, expr) if err != nil { return SexpNull, err } } return accum, nil } func ComplementFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } switch t := args[0].(type) { case *SexpInt: return &SexpInt{Val: ^t.Val}, nil case *SexpChar: return &SexpChar{Val: ^t.Val}, nil } return SexpNull, fmt.Errorf("Argument to bitNot should be integer") } func PointerOrNumericFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { n := len(args) if n == 0 { return SexpNull, WrongNargs } if n >= 2 { return NumericFunction(env, name, args) } return PointerToFunction(env, name, args) } func NumericFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) < 1 { return SexpNull, WrongNargs } var err error args, err = env.SubstituteRHS(args) if err != nil { return SexpNull, err } accum := args[0] var op NumericOp switch name { case "+": op = Add case "-": op = Sub case "*": op = Mult case "/": op = Div case "**": op = Pow } for _, expr := range args[1:] { accum, err = NumericDo(op, accum, expr) if err != nil { return SexpNull, err } } return accum, nil } func ConsFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, WrongNargs } return Cons(args[0], args[1]), nil } func FirstFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } switch expr := args[0].(type) { case *SexpPair: return expr.Head, nil case *SexpArray: if len(expr.Val) > 0 { return expr.Val[0], nil } return SexpNull, fmt.Errorf("first called on empty array") } return SexpNull, WrongType } func RestFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } switch expr := args[0].(type) { case *SexpPair: return expr.Tail, nil case *SexpArray: if len(expr.Val) == 0 { return expr, nil } return &SexpArray{Val: expr.Val[1:], Env: env, Typ: expr.Typ}, nil case *SexpSentinel: if expr == SexpNull { return SexpNull, nil } } return SexpNull, WrongType } func SecondFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } switch expr := args[0].(type) { case *SexpPair: tail := expr.Tail switch p := tail.(type) { case *SexpPair: return p.Head, nil } return SexpNull, fmt.Errorf("list too small for second") case *SexpArray: if len(expr.Val) >= 2 { return expr.Val[1], nil } return SexpNull, fmt.Errorf("array too small for second") } return SexpNull, WrongType } func ArrayAccessFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { narg := len(args) if narg < 2 || narg > 3 { return SexpNull, WrongNargs } var arr *SexpArray switch t := args[0].(type) { case *SexpArray: arr = t default: return SexpNull, fmt.Errorf("First argument of aget must be array") } var i int switch t := args[1].(type) { case *SexpInt: i = int(t.Val) case *SexpChar: i = int(t.Val) default: // can we evaluate it? res, err := EvalFunction(env, "eval-aget-index", []Sexp{args[1]}) if err != nil { return SexpNull, fmt.Errorf("error during eval of "+ "array-access position argument: %s", err) } switch j := res.(type) { case *SexpInt: i = int(j.Val) default: return SexpNull, fmt.Errorf("Second argument of aget could not be evaluated to integer; got j = '%#v'/type = %T", j, j) } } switch name { case "hget": fallthrough case "aget": if i < 0 || i >= len(arr.Val) { // out of bounds -- do we have a default? if narg == 3 { return args[2], nil } return SexpNull, fmt.Errorf("Array index out of bounds") } return arr.Val[i], nil case "aset": if len(args) != 3 { return SexpNull, WrongNargs } if i < 0 || i >= len(arr.Val) { return SexpNull, fmt.Errorf("Array index out of bounds") } arr.Val[i] = args[2] } return SexpNull, nil } func SgetFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, WrongNargs } var str *SexpStr switch t := args[0].(type) { case *SexpStr: str = t default: return SexpNull, fmt.Errorf("First argument of sget must be string") } var i int switch t := args[1].(type) { case *SexpInt: i = int(t.Val) case *SexpChar: i = int(t.Val) default: return SexpNull, fmt.Errorf("Second argument of sget must be integer") } return &SexpChar{Val: rune(str.S[i])}, nil } func HashAccessFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) < 1 || len(args) > 3 { return SexpNull, WrongNargs } // handle *SexpSelector container := args[0] var err error if ptr, isPtrLike := container.(Selector); isPtrLike { container, err = ptr.RHS(env) if err != nil { return SexpNull, err } } var hash *SexpHash switch e := container.(type) { case *SexpHash: hash = e default: return SexpNull, fmt.Errorf("first argument to h* function must be hash") } switch name { case "hget": if len(args) == 3 { return hash.HashGetDefault(env, args[1], args[2]) } return hash.HashGet(env, args[1]) case "hset": if len(args) != 3 { return SexpNull, WrongNargs } err := hash.HashSet(args[1], args[2]) return SexpNull, err case "hdel": if len(args) != 2 { return SexpNull, WrongNargs } err := hash.HashDelete(args[1]) return SexpNull, err case "keys": if len(args) != 1 { return SexpNull, WrongNargs } keys := make([]Sexp, 0) n := len(hash.KeyOrder) arr := &SexpArray{Env: env} for i := 0; i < n; i++ { keys = append(keys, (hash.KeyOrder)[i]) // try to get a .Typ value going too... from the first available. if arr.Typ == nil { arr.Typ = (hash.KeyOrder)[i].Type() } } arr.Val = keys return arr, nil case "hpair": if len(args) != 2 { return SexpNull, WrongNargs } switch posreq := args[1].(type) { case *SexpInt: pos := int(posreq.Val) if pos < 0 || pos >= len(hash.KeyOrder) { return SexpNull, fmt.Errorf("hpair position request %d out of bounds", pos) } return hash.HashPairi(pos) default: return SexpNull, fmt.Errorf("hpair position request must be an integer") } } return SexpNull, nil } func HashColonFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) < 2 || len(args) > 3 { return SexpNull, WrongNargs } var hash *SexpHash switch e := args[1].(type) { case *SexpHash: hash = e default: return SexpNull, fmt.Errorf("second argument of (:field hash) must be a hash") } if len(args) == 3 { return hash.HashGetDefault(env, args[0], args[2]) } return hash.HashGet(env, args[0]) } func SliceFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 3 { return SexpNull, WrongNargs } var start int var end int switch t := args[1].(type) { case *SexpInt: start = int(t.Val) case *SexpChar: start = int(t.Val) default: return SexpNull, fmt.Errorf("Second argument of slice must be integer") } switch t := args[2].(type) { case *SexpInt: end = int(t.Val) case *SexpChar: end = int(t.Val) default: return SexpNull, fmt.Errorf("Third argument of slice must be integer") } switch t := args[0].(type) { case *SexpArray: return &SexpArray{Val: t.Val[start:end], Env: env, Typ: t.Typ}, nil case *SexpStr: return &SexpStr{S: t.S[start:end]}, nil } return SexpNull, fmt.Errorf("First argument of slice must be array or string") } func LenFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } var err error args, err = env.ResolveDotSym(args) if err != nil { return SexpNull, err } switch t := args[0].(type) { case *SexpSentinel: if t == SexpNull { return &SexpInt{}, nil } break case *SexpArray: return &SexpInt{Val: int64(len(t.Val))}, nil case *SexpStr: return &SexpInt{Val: int64(len(t.S))}, nil case *SexpHash: return &SexpInt{Val: int64(HashCountKeys(t))}, nil case *SexpPair: n, err := ListLen(t) return &SexpInt{Val: int64(n)}, err default: P("in LenFunction with args[0] of type %T", t) } return &SexpInt{}, fmt.Errorf("argument must be string, list, hash, or array") } func AppendFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, WrongNargs } switch t := args[0].(type) { case *SexpArray: switch name { case "append": return &SexpArray{Val: append(t.Val, args[1]), Env: env, Typ: t.Typ}, nil case "appendslice": switch sl := args[1].(type) { case *SexpArray: return &SexpArray{Val: append(t.Val, sl.Val...), Env: env, Typ: t.Typ}, nil default: return SexpNull, fmt.Errorf("Second argument of appendslice must be slice") } default: return SexpNull, fmt.Errorf("unrecognized append variant: '%s'", name) } case *SexpStr: return AppendStr(t, args[1]) } return SexpNull, fmt.Errorf("First argument of append must be array or string") } func ConcatFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) < 1 { return SexpNull, WrongNargs } var err error args, err = env.ResolveDotSym(args) if err != nil { return SexpNull, err } switch t := args[0].(type) { case *SexpArray: return ConcatArray(t, args[1:]) case *SexpStr: return ConcatStr(t, args[1:]) case *SexpPair: n := len(args) switch { case n == 1: return t, nil default: return ConcatLists(t, args[1:]) } } return SexpNull, fmt.Errorf("expected strings, lists or arrays") } func ReadFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } str := "" switch t := args[0].(type) { case *SexpStr: str = t.S default: return SexpNull, WrongType } env.parser.ResetAddNewInput(bytes.NewBuffer([]byte(str))) exp, err := env.parser.ParseExpression(0) return exp, err } func OldEvalFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } P("EvalFunction() called, name = '%s'; args = %#v", name, args) newenv := env.Duplicate() err := newenv.LoadExpressions(args) if err != nil { return SexpNull, fmt.Errorf("failed to compile expression") } newenv.pc = 0 return newenv.Run() } // EvalFunction: new version doesn't use a duplicated environment, // allowing eval to create closures under the lexical scope and // to allow proper scoping in a package. func EvalFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) < 1 { return SexpNull, WrongNargs } //P("EvalFunction() called, name = '%s'; args = %#v", name, (&SexpArray{Val: args}).SexpString(0)) // Instead of LoadExpressions: args = env.FilterArray(args, RemoveCommentsFilter) args = env.FilterArray(args, RemoveEndsFilter) startingDataStackSize := env.datastack.Size() gen := NewGenerator(env) err := gen.GenerateBegin(args) if err != nil { return SexpNull, err } newfunc := ZlispFunction(gen.instructions) orig := &SexpArray{Val: args} sfun := env.MakeFunction("evalGeneratedFunction", 0, false, newfunc, orig) err = env.CallFunction(sfun, 0) if err != nil { return SexpNull, err } var resultSexp Sexp resultSexp, err = env.Run() if err != nil { return SexpNull, err } err = env.ReturnFromFunction() // some sanity checks if env.datastack.Size() > startingDataStackSize { /* xtra := env.datastack.Size() - startingDataStackSize panic(fmt.Sprintf("we've left some extra stuff (xtra = %v) on the datastack "+ "during eval, don't be sloppy, fix it now! env.datastack.Size()=%v, startingDataStackSize = %v", xtra, env.datastack.Size(), startingDataStackSize)) P("warning: truncating datastack back to startingDataStackSize %v", startingDataStackSize) */ env.datastack.TruncateToSize(startingDataStackSize) } if env.datastack.Size() < startingDataStackSize { P("about panic, since env.datastack.Size() < startingDataStackSize, here is env dump:") env.DumpEnvironment() panic(fmt.Sprintf("we've shrunk the datastack during eval, don't be sloppy, fix it now! env.datastack.Size()=%v. startingDataStackSize=%v", env.datastack.Size(), startingDataStackSize)) } return resultSexp, err } func TypeQueryFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } var result bool switch name { case "type?": return TypeOf(args[0]), nil case "list?": result = IsList(args[0]) case "null?": result = (args[0] == SexpNull) case "array?": result = IsArray(args[0]) case "number?": result = IsNumber(args[0]) case "float?": result = IsFloat(args[0]) case "int?": result = IsInt(args[0]) case "char?": result = IsChar(args[0]) case "symbol?": result = IsSymbol(args[0]) case "string?": result = IsString(args[0]) case "hash?": result = IsHash(args[0]) case "zero?": result = IsZero(args[0]) case "empty?": result = IsEmpty(args[0]) case "func?": result = IsFunc(args[0]) } return &SexpBool{Val: result}, nil } func PrintFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) < 1 { return SexpNull, WrongNargs } var str string switch expr := args[0].(type) { case *SexpStr: str = expr.S default: str = expr.SexpString(nil) } switch name { case "println": fmt.Println(str) case "print": fmt.Print(str) case "printf", "sprintf": if len(args) == 1 && name == "printf" { fmt.Printf(str) } else { ar := make([]interface{}, len(args)-1) for i := 0; i < len(ar); i++ { switch x := args[i+1].(type) { case *SexpInt: ar[i] = x.Val case *SexpBool: ar[i] = x.Val case *SexpFloat: ar[i] = x.Val case *SexpChar: ar[i] = x.Val case *SexpStr: ar[i] = x.S case *SexpTime: ar[i] = x.Tm.In(NYC) default: ar[i] = args[i+1] } } if name == "printf" { fmt.Printf(str, ar...) } else { // sprintf return &SexpStr{S: fmt.Sprintf(str, ar...)}, nil } } } return SexpNull, nil } func NotFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } result := &SexpBool{Val: !IsTruthy(args[0])} return result, nil } func ApplyFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, WrongNargs } var fun *SexpFunction var funargs []Sexp switch e := args[0].(type) { case *SexpFunction: fun = e default: return SexpNull, fmt.Errorf("first argument must be function") } switch e := args[1].(type) { case *SexpArray: funargs = e.Val case *SexpPair: var err error funargs, err = ListToArray(e) if err != nil { return SexpNull, err } default: return SexpNull, fmt.Errorf("second argument must be array or list") } return env.Apply(fun, funargs) } func MapFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, WrongNargs } var fun *SexpFunction VPrintf("\n debug Map: args = '%#v'\n", args) switch e := args[0].(type) { case *SexpFunction: fun = e default: return SexpNull, fmt.Errorf("first argument must be function, but we had %T / val = '%#v'", e, e) } switch e := args[1].(type) { case *SexpArray: return MapArray(env, fun, e) case *SexpPair: x, err := MapList(env, fun, e) return x, err default: return SexpNull, fmt.Errorf("second argument must be array or list; we saw %T / val = %#v", e, e) } } func MakeArrayFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) < 1 { return SexpNull, WrongNargs } var size int switch e := args[0].(type) { case *SexpInt: size = int(e.Val) default: return SexpNull, fmt.Errorf("first argument must be integer") } var fill Sexp if len(args) == 2 { fill = args[1] } else { fill = SexpNull } arr := make([]Sexp, size) for i := range arr { arr[i] = fill } return env.NewSexpArray(arr), nil } func ConstructorFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { switch name { case "array": return env.NewSexpArray(args), nil case "list": return MakeList(args), nil case "hash": return MakeHash(args, "hash", env) case "raw": return MakeRaw(args) case "field": Q("making hash for field") h, err := MakeHash(args, "field", env) if err != nil { return SexpNull, err } fld := (*SexpField)(h) Q("hash for field is: '%v'", fld.SexpString(nil)) return fld, nil case "struct": return MakeHash(args, "struct", env) case "msgmap": switch len(args) { case 0: return MakeHash(args, name, env) default: var arr []Sexp var err error if len(args) > 1 { arr, err = ListToArray(args[1]) if err != nil { return SexpNull, fmt.Errorf("error converting "+ "'%s' arguments to an array: '%v'", name, err) } } else { arr = args[1:] } switch nm := args[0].(type) { case *SexpStr: return MakeHash(arr, nm.S, env) case *SexpSymbol: return MakeHash(arr, nm.name, env) default: return MakeHash(arr, name, env) } } } return SexpNull, fmt.Errorf("invalid constructor") } func SymnumFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } switch t := args[0].(type) { case *SexpSymbol: return &SexpInt{Val: int64(t.number)}, nil } return SexpNull, fmt.Errorf("argument must be symbol") } var MissingFunction = &SexpFunction{name: "__missing", user: true} func (env *Zlisp) MakeFunction(name string, nargs int, varargs bool, fun ZlispFunction, orig Sexp) *SexpFunction { var sfun SexpFunction sfun.name = name sfun.user = false sfun.nargs = nargs sfun.varargs = varargs sfun.fun = fun sfun.orig = orig sfun.SetClosing(NewClosing(name, env)) // snapshot the create env as of now. return &sfun } func MakeUserFunction(name string, ufun ZlispUserFunction) *SexpFunction { var sfun SexpFunction sfun.name = name sfun.user = true sfun.userfun = ufun return &sfun } func MakeBuilderFunction(name string, ufun ZlispUserFunction) *SexpFunction { sfun := MakeUserFunction(name, ufun) sfun.isBuilder = true return sfun } // MergeFuncMap returns the union of the two given maps func MergeFuncMap(funcs ...map[string]ZlispUserFunction) map[string]ZlispUserFunction { n := make(map[string]ZlispUserFunction) for _, f := range funcs { for k, v := range f { // disallow dups, avoiding possible security implications and confusion generally. if _, dup := n[k]; dup { panic(fmt.Sprintf(" duplicate function '%s' not allowed", k)) } n[k] = v } } return n } // SandboxSafeFuncs returns all functions that are safe to run in a sandbox func SandboxSafeFunctions() map[string]ZlispUserFunction { return MergeFuncMap( CoreFunctions(), StrFunctions(), EncodingFunctions(), ) } // AllBuiltinFunctions returns all built in functions func AllBuiltinFunctions() map[string]ZlispUserFunction { return MergeFuncMap( CoreFunctions(), StrFunctions(), EncodingFunctions(), SystemFunctions(), ReflectionFunctions(), ) } // CoreFunctions returns all of the core logic func CoreFunctions() map[string]ZlispUserFunction { return map[string]ZlispUserFunction{ "pretty": SetPrettyPrintFlag, "<": CompareFunction, ">": CompareFunction, "<=": CompareFunction, ">=": CompareFunction, "==": CompareFunction, "!=": CompareFunction, "isnan": IsNaNFunction, "isNaN": IsNaNFunction, "sll": BinaryIntFunction, "sra": BinaryIntFunction, "srl": BinaryIntFunction, "mod": BinaryIntFunction, "+": NumericFunction, "-": NumericFunction, "*": PointerOrNumericFunction, "**": NumericFunction, "/": NumericFunction, "bitAnd": BitwiseFunction, "bitOr": BitwiseFunction, "bitXor": BitwiseFunction, "bitNot": ComplementFunction, "read": ReadFunction, "cons": ConsFunction, "first": FirstFunction, "second": SecondFunction, "rest": RestFunction, "car": FirstFunction, "cdr": RestFunction, "type?": TypeQueryFunction, "list?": TypeQueryFunction, "null?": TypeQueryFunction, "array?": TypeQueryFunction, "hash?": TypeQueryFunction, "number?": TypeQueryFunction, "int?": TypeQueryFunction, "float?": TypeQueryFunction, "char?": TypeQueryFunction, "symbol?": TypeQueryFunction, "string?": TypeQueryFunction, "zero?": TypeQueryFunction, "empty?": TypeQueryFunction, "func?": TypeQueryFunction, "not": NotFunction, "apply": ApplyFunction, "map": MapFunction, "makeArray": MakeArrayFunction, "aget": ArrayAccessFunction, "aset": ArrayAccessFunction, "sget": SgetFunction, "hget": GenericAccessFunction, // handles arrays or hashes //":": ColonAccessFunction, "hset": HashAccessFunction, "hdel": HashAccessFunction, "keys": HashAccessFunction, "hpair": GenericHpairFunction, "slice": SliceFunction, "len": LenFunction, "append": AppendFunction, "appendslice": AppendFunction, "concat": ConcatFunction, "field": ConstructorFunction, "struct": ConstructorFunction, "array": ConstructorFunction, "list": ConstructorFunction, "hash": ConstructorFunction, "raw": ConstructorFunction, "str": StringifyFunction, "->": ThreadMapFunction, "flatten": FlattenToWordsFunction, "quotelist": QuoteListFunction, "=": AssignmentFunction, ":=": AssignmentFunction, "fieldls": GoFieldListFunction, "defined?": DefinedFunction, "stop": StopFunction, "joinsym": JoinSymFunction, "GOOS": GOOSFunction, "&": AddressOfFunction, "derefSet": DerefFunction, "deref": DerefFunction, ".": DotFunction, "arrayidx": ArrayIndexFunction, "hashidx": HashIndexFunction, "asUint64": AsUint64Function, } } func StrFunctions() map[string]ZlispUserFunction { return map[string]ZlispUserFunction{ "nsplit": SplitStringOnNewlinesFunction, "split": SplitStringFunction, "chomp": StringUtilFunction, "trim": StringUtilFunction, "println": PrintFunction, "print": PrintFunction, "printf": PrintFunction, "sprintf": PrintFunction, "raw2str": RawToStringFunction, "str2sym": Str2SymFunction, "sym2str": Sym2StrFunction, "gensym": GensymFunction, "symnum": SymnumFunction, } } func EncodingFunctions() map[string]ZlispUserFunction { return map[string]ZlispUserFunction{ "json": JsonFunction, "unjson": JsonFunction, "msgpack": JsonFunction, "unmsgpack": JsonFunction, "gob": GobEncodeFunction, "msgmap": ConstructorFunction, } } func ReflectionFunctions() map[string]ZlispUserFunction { return map[string]ZlispUserFunction{ "methodls": GoMethodListFunction, "_method": CallGoMethodFunction, "registerDemoFunctions": ScriptFacingRegisterDemoStructs, } } func SystemFunctions() map[string]ZlispUserFunction { return map[string]ZlispUserFunction{ "source": SourceFileFunction, "togo": ToGoFunction, "fromgo": FromGoFunction, "dump": GoonDumpFunction, "slurpf": SlurpfileFunction, "writef": WriteToFileFunction, "save": WriteToFileFunction, "bload": ReadGreenpackFromFileFunction, "bsave": WriteShadowGreenpackToFileFunction, "greenpack": WriteShadowGreenpackToFileFunction, "owritef": WriteToFileFunction, "system": SystemFunction, "exit": ExitFunction, "_closdump": DumpClosureEnvFunction, "rmsym": RemoveSymFunction, "typelist": TypeListFunction, "setenv": GetEnvFunction, "getenv": GetEnvFunction, // not done "_call": CallZMethodOnRecordFunction, } } func ThreadMapFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) < 2 { return SexpNull, WrongNargs } h, isHash := args[0].(*SexpHash) if !isHash { return SexpNull, fmt.Errorf("-> error: first argument must be a hash or defmap") } field, err := threadingHelper(env, h, args[1:]) if err != nil { return SexpNull, err } return field, nil } func threadingHelper(env *Zlisp, hash *SexpHash, args []Sexp) (Sexp, error) { if len(args) == 0 { panic("should not recur without arguments") } field, err := hash.HashGet(env, args[0]) if err != nil { return SexpNull, fmt.Errorf("-> error: field '%s' not found", args[0].SexpString(nil)) } if len(args) > 1 { h, isHash := field.(*SexpHash) if !isHash { return SexpNull, fmt.Errorf("request for field '%s' was "+ "not on a hash or defmap; instead type %T with value '%#v'", args[1].SexpString(nil), field, field) } return threadingHelper(env, h, args[1:]) } return field, nil } func StringifyFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } return &SexpStr{S: args[0].SexpString(nil)}, nil } func Sym2StrFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } switch t := args[0].(type) { case *SexpSymbol: r := &SexpStr{S: t.name} return r, nil } return SexpNull, fmt.Errorf("argument must be symbol") } func Str2SymFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } switch t := args[0].(type) { case *SexpStr: return env.MakeSymbol(t.S), nil } return SexpNull, fmt.Errorf("argument must be string") } func GensymFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { n := len(args) switch { case n == 0: return env.GenSymbol("__gensym"), nil case n == 1: switch t := args[0].(type) { case *SexpStr: return env.GenSymbol(t.S), nil } return SexpNull, fmt.Errorf("argument must be string") default: return SexpNull, WrongNargs } } func ExitFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } switch e := args[0].(type) { case *SexpInt: os.Exit(int(e.Val)) } return SexpNull, fmt.Errorf("argument must be int (the exit code)") } // handles arrays or hashes func GenericAccessFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) < 1 || len(args) > 3 { return SexpNull, WrongNargs } // handle *SexpSelector container := args[0] var err error if ptr, isPtrLike := container.(Selector); isPtrLike { container, err = ptr.RHS(env) if err != nil { return SexpNull, err } } switch container.(type) { case *SexpHash: return HashAccessFunction(env, name, args) case *SexpArray: return ArrayAccessFunction(env, name, args) } return SexpNull, fmt.Errorf("first argument to hget function must be hash or array") } var stopErr error = fmt.Errorf("stop") func StopFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { narg := len(args) if narg > 1 { return SexpNull, WrongNargs } if narg == 0 { return SexpNull, stopErr } switch s := args[0].(type) { case *SexpStr: return SexpNull, fmt.Errorf(s.S) } return SexpNull, stopErr } // the assignment function, = func AssignmentFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { Q("\n AssignmentFunction called with name ='%s'. args='%s'\n", name, env.NewSexpArray(args).SexpString(nil)) narg := len(args) if narg != 2 { return SexpNull, fmt.Errorf("assignment requires two arguments: a left-hand-side and a right-hand-side argument") } var sym *SexpSymbol switch s := args[0].(type) { case *SexpSymbol: sym = s case Selector: err := s.AssignToSelection(env, args[1]) return args[1], err default: return SexpNull, fmt.Errorf("assignment needs left-hand-side"+ " argument to be a symbol; we got %T", s) } if !sym.isDot { Q("assignment sees LHS symbol but is not dot, binding '%s' to '%s'\n", sym.name, args[1].SexpString(nil)) err := env.LexicalBindSymbol(sym, args[1]) if err != nil { return SexpNull, err } return args[1], nil } Q("assignment calling dotGetSetHelper()\n") return dotGetSetHelper(env, sym.name, &args[1]) } func JoinSymFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { narg := len(args) if narg == 0 { return SexpNull, nil } j := "" for k := range args { switch a := args[k].(type) { case *SexpPair: arr, err := ListToArray(args[k]) if err != nil { return SexpNull, fmt.Errorf("error converting "+ "joinsym arguments to an array: '%v'", err) } s, err := joinSymHelper(arr) if err != nil { return SexpNull, err } j += s case *SexpSymbol: j = j + a.name case *SexpArray: s, err := joinSymHelper(a.Val) if err != nil { return SexpNull, err } j += s default: return SexpNull, fmt.Errorf("error cannot joinsym type '%T' / val = '%s'", a, a.SexpString(nil)) } } return env.MakeSymbol(j), nil } func joinSymHelper(arr []Sexp) (string, error) { j := "" for i := 0; i < len(arr); i++ { switch s := arr[i].(type) { case *SexpSymbol: j = j + s.name default: return "", fmt.Errorf("not a symbol: '%s'", arr[i].SexpString(nil)) } } return j, nil } // '(a b c) -> ('a 'b 'c) func QuoteListFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { narg := len(args) if narg != 1 { return SexpNull, WrongNargs } pair, ok := args[0].(*SexpPair) if !ok { return SexpNull, fmt.Errorf("list required") } arr, err := ListToArray(pair) if err != nil { return SexpNull, fmt.Errorf("error converting "+ "quotelist arguments to an array: '%v'", err) } arr2 := []Sexp{} for _, v := range arr { arr2 = append(arr2, MakeList([]Sexp{env.MakeSymbol("quote"), v})) } return MakeList(arr2), nil } // helper used by dotGetSetHelper and sub-calls to check for private func errIfPrivate(pathPart string, pkg *Stack) error { noDot := stripAnyDotPrefix(pathPart) // references through a package must be Public if !unicode.IsUpper([]rune(noDot)[0]) { return fmt.Errorf("Cannot access private member '%s' of package '%s'", noDot, pkg.PackageName) } return nil } // if setVal is nil, only get and return the lookup. // Otherwise set and return the value we set. func dotGetSetHelper(env *Zlisp, name string, setVal *Sexp) (Sexp, error) { path := DotPartsRegex.FindAllString(name, -1) //P("\n in dotGetSetHelper(), name = '%s', path = '%#v', setVal = '%#v'\n", name, path, setVal) if len(path) == 0 { return SexpNull, fmt.Errorf("internal error: DotFunction" + " path had zero length") } var ret Sexp = SexpNull var err error lenpath := len(path) if lenpath == 1 && setVal != nil { // single path element set, bind it now. a := stripAnyDotPrefix(path[0]) asym := env.MakeSymbol(a) // check conflict //Q("asym = %#v\n", asym) builtin, typ := env.IsBuiltinSym(asym) if builtin { return SexpNull, fmt.Errorf("'%s' is a %s, cannot assign to it with dot-symbol", asym.name, typ) } err := env.LexicalBindSymbol(asym, *setVal) if err != nil { return SexpNull, err } return *setVal, nil } // handle multiple paths that index into hashes after the // the first key := stripAnyDotPrefix(path[0]) //Q("\n in dotGetSetHelper(), looking up '%s'\n", key) keySym := env.MakeSymbol(key) ret, err, _ = env.LexicalLookupSymbol(keySym, nil) if err != nil { //Q("\n in dotGetSetHelper(), '%s' not found\n", key) return SexpNull, err } if lenpath == 1 { // single path element get, return it. return ret, err } // INVAR: lenpath > 1 // package or hash? check for package pkg, isStack := ret.(*Stack) if isStack && pkg.IsPackage { //P("found a package: '%s'", pkg.SexpString(nil)) exp, err := pkg.nestedPathGetSet(env, path[1:], setVal) if err != nil { return SexpNull, err } return exp, nil } // at least .a.b if not a.b.c. etc: multiple elements, // where .b and after // will index into hashes (.a must refer to a hash); // proceed deeper into the hashes. var h *SexpHash switch x := ret.(type) { case *SexpHash: h = x case *SexpReflect: // at least allow reading, if we can. if setVal != nil { return SexpNull, fmt.Errorf("can't set on an SexpReflect: on request for "+ "field '%s' in non-record (instead of type %T)", stripAnyDotPrefix(path[1]), ret) } //P("functions.go DEBUG! SexpReflect value h is type: '%v', '%T', kind: '%v'", x.Val.Type(), x.Val.Interface(), x.Val.Type().Kind()) if x.Val.Type().Kind() == reflect.Struct { //P("We have a struct! path[1]='%v', path='%#v'", path[1], path) if len(path) >= 2 && len(path[1]) > 0 { fieldName := stripAnyDotPrefix(path[1]) //P("We have a struct! with dot request for member '%s'", fieldName) fld := x.Val.FieldByName(fieldName) if reflect.DeepEqual(fld, reflect.Value{}) { return SexpNull, fmt.Errorf("no such field '%s'", fieldName) } // ex: We got back fld='20' of type int, kind=int //P("We got back fld='%v' of type %v, kind=%v", fld, fld.Type(), fld.Type().Kind()) return GoToSexp(fld.Interface(), env) } } return SexpNull, fmt.Errorf("SexpReflect is not a struct: cannot get "+ "field '%s' in non-struct (instead of type %T)", stripAnyDotPrefix(path[1]), ret) default: return SexpNull, fmt.Errorf("not a record: cannot get "+ "field '%s' in non-record (instead of type %T)", stripAnyDotPrefix(path[1]), ret) } // have hash: rest of path handled in hashutils.go in nestedPathGet() //Q("\n in dotGetSetHelper(), about to call nestedPathGetSet() with"+ // "dotpaths = path[i+1:]='%#v\n", path[1:]) exp, err := h.nestedPathGetSet(env, path[1:], setVal) if err != nil { return SexpNull, err } return exp, nil } func RemoveSymFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { narg := len(args) if narg != 1 { return SexpNull, WrongNargs } sym, ok := args[0].(*SexpSymbol) if !ok { return SexpNull, fmt.Errorf("symbol required, but saw %T/%v", args[0], args[0].SexpString(nil)) } err := env.linearstack.DeleteSymbolFromTopOfStackScope(sym) return SexpNull, err } func GOOSFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { narg := len(args) if narg != 0 { return SexpNull, WrongNargs } return &SexpStr{S: runtime.GOOS}, nil } // check is a symbol/string/value is defined func DefinedFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { //P("in DefinedFunction, args = '%#v'", args) narg := len(args) if narg != 1 { return SexpNull, WrongNargs } var checkme string switch nm := args[0].(type) { case *SexpStr: checkme = nm.S case *SexpSymbol: checkme = nm.name case *SexpFunction: return &SexpBool{Val: true}, nil default: return &SexpBool{Val: false}, nil } _, err, _ := env.LexicalLookupSymbol(env.MakeSymbol(checkme), nil) if err != nil { return &SexpBool{Val: false}, nil } return &SexpBool{Val: true}, nil } func AddressOfFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { narg := len(args) if narg != 1 { return SexpNull, WrongNargs } return NewSexpPointer(args[0]), nil } func DerefFunction(env *Zlisp, name string, args []Sexp) (result Sexp, err error) { result = SexpNull defer func() { e := recover() if e != nil { //Q("in recover() of DerefFunction, e = '%#v'", e) switch ve := e.(type) { case *reflect.ValueError: err = ve default: err = fmt.Errorf("unknown typecheck error during %s: %v", name, ve) } } }() narg := len(args) if narg != 1 && narg != 2 { return SexpNull, WrongNargs } var ptr *SexpPointer switch e := args[0].(type) { case *SexpPointer: ptr = e case *SexpReflect: ptr = NewSexpPointer(e) default: return SexpNull, fmt.Errorf("%s only operates on pointers (*SexpPointer); we saw %T instead", name, e) } switch name { case "deref": if narg != 1 { return SexpNull, WrongNargs } return ptr.Target, nil case "derefSet": if narg != 2 { return SexpNull, WrongNargs } // delegate as much as we can to the Go type system // and reflection rhs := reflect.ValueOf(args[1]) rhstype := rhs.Type() lhstype := ptr.ReflectTarget.Type() //P("rhstype = %#v, lhstype = %#v", rhstype, lhstype) if lhstype == rhstype { // have to exclude *SexpHash and *SexpReflect from this switch args[1].(type) { case *SexpHash: // handle below //case *SexpReflect: // handle here or below? default: //P("we have a reflection capable type match!") ptr.ReflectTarget.Elem().Set(rhs.Elem()) return } } //P("derefSet: arg0 is %T and arg1 is %T, ptr.Target = %#v", args[0], args[1], ptr.Target) //P("args[0] has ptr.ReflectTarget = '%#v'", ptr.ReflectTarget) switch payload := args[1].(type) { case *SexpInt: Q("ptr = '%#v'", ptr) Q("ptr.ReflectTarget = '%#v'", ptr.ReflectTarget) Q("ptr.ReflectTarget.CanAddr() = '%#v'", ptr.ReflectTarget.Elem().CanAddr()) Q("ptr.ReflectTarget.CanSet() = '%#v'", ptr.ReflectTarget.Elem().CanSet()) Q("*SexpInt case: payload = '%#v'", payload) vo := reflect.ValueOf(payload.Val) vot := vo.Type() if !vot.AssignableTo(ptr.ReflectTarget.Elem().Type()) { return SexpNull, fmt.Errorf("type mismatch: value of type '%s' is not assignable to type '%v'", vot, ptr.ReflectTarget.Elem().Type()) } ptr.ReflectTarget.Elem().Set(vo) return case *SexpStr: vo := reflect.ValueOf(payload.S) vot := vo.Type() //P("payload is *SexpStr") //tele := ptr.ReflectTarget.Elem() //P("ptr = %#v", ptr) tele := ptr.ReflectTarget //P("got past tele : %#v", tele) if !reflect.PtrTo(vot).AssignableTo(tele.Type()) { return SexpNull, fmt.Errorf("type mismatch: value of type '%v' is not assignable to '%v'", vot, ptr.PointedToType.RegisteredName) // tele.Type()) } //P("payload is *SexpStr, got past type check") ptr.ReflectTarget.Elem().Set(vo) return case *SexpHash: //P("ptr.PointedToType = '%#v'", ptr.PointedToType) pt := payload.Type() tt := ptr.PointedToType if tt == pt && tt.RegisteredName == pt.RegisteredName { //P("have matching type!: %v", tt.RegisteredName) ptr.Target.(*SexpHash).CloneFrom(payload) return } else { return SexpNull, fmt.Errorf("cannot assign type '%v' to type '%v'", payload.Type().RegisteredName, ptr.PointedToType.RegisteredName) } case *SexpReflect: Q("good, e2 is SexpReflect with Val='%#v'", payload.Val) Q("ptr.Target = '%#v'. ... trying SexpToGoStructs()", ptr.Target) iface, err := SexpToGoStructs(payload, ptr.Target, env, nil) if err != nil { return SexpNull, err } Q("got back iface = '%#v'", iface) panic("not done yet with this implementation of args[1] of type *SexpReflect") } return SexpNull, fmt.Errorf("derefSet doesn't handle assignment of type %T at present", args[1]) default: return SexpNull, fmt.Errorf("unimplemented operation '%s' in DerefFunction", name) } } // "." dot operator func DotFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 2 { return SexpNull, WrongNargs } P("in DotFunction(), name='%v', args[0] = '%v', args[1]= '%v'", name, args[0].SexpString(nil), args[1].SexpString(nil)) return SexpNull, nil /* var ret Sexp = SexpNull var err error lenpath := len(path) if lenpath == 1 && setVal != nil { // single path element set, bind it now. a := path[0][1:] // strip off the dot asym := env.MakeSymbol(a) // check conflict //Q("asym = %#v\n", asym) builtin, typ := env.IsBuiltinSym(asym) if builtin { return SexpNull, fmt.Errorf("'%s' is a %s, cannot assign to it with dot-symbol", asym.name, typ) } err := env.LexicalBindSymbol(asym, *setVal) if err != nil { return SexpNull, err } return *setVal, nil } // handle multiple paths that index into hashes after the // the first key := path[0][1:] // strip off the dot //Q("\n in dotGetSetHelper(), looking up '%s'\n", key) ret, err, _ = env.LexicalLookupSymbol(env.MakeSymbol(key), false) if err != nil { Q("\n in dotGetSetHelper(), '%s' not found\n", key) return SexpNull, err } return ret, err */ } func stripAnyDotPrefix(s string) string { if len(s) > 0 && s[0] == '.' { return s[1:] } return s } // SubstituteRHS locates any SexpSelector(s) (Selector implementers, really) // and substitutes // the value of x.RHS() for each x in args. func (env *Zlisp) SubstituteRHS(args []Sexp) ([]Sexp, error) { for i := range args { obj, hasRhs := args[i].(Selector) if hasRhs { sx, err := obj.RHS(env) if err != nil { return args, err } args[i] = sx } } return args, nil } func ScriptFacingRegisterDemoStructs(env *Zlisp, name string, args []Sexp) (Sexp, error) { RegisterDemoStructs() return SexpNull, nil } func GetEnvFunction(env *Zlisp, name string, args []Sexp) (Sexp, error) { narg := len(args) //fmt.Printf("GetEnv name='%s' called with narg = %v\n", name, narg) if name == "getenv" { if narg != 1 { return SexpNull, WrongNargs } } else { if name != "setenv" { panic("only getenv or setenv allowed here") } if narg != 2 { return SexpNull, WrongNargs } } nm := make([]string, narg) for i := 0; i < narg; i++ { switch x := args[i].(type) { case *SexpSymbol: nm[i] = x.name case *SexpStr: nm[i] = x.S default: return SexpNull, fmt.Errorf("symbol or string required, but saw %T/%v for i=%v arg", args[i], args[i].SexpString(nil), i) } } if name == "getenv" { return &SexpStr{S: os.Getenv(nm[0])}, nil } //fmt.Printf("calling setenv with nm[0]='%s', nm[1]='%s'\n", nm[0], nm[1]) return SexpNull, os.Setenv(nm[0], nm[1]) } // coerce numbers to uint64 func AsUint64Function(env *Zlisp, name string, args []Sexp) (Sexp, error) { if len(args) != 1 { return SexpNull, WrongNargs } var val uint64 switch x := args[0].(type) { case *SexpInt: val = uint64(x.Val) case *SexpFloat: val = uint64(x.Val) default: return SexpNull, fmt.Errorf("Cannot convert %s to uint64", TypeOf(args[0]).SexpString(nil)) } return &SexpUint64{Val: val}, nil }