github.com/clarenceb/holochain-proto@v0.0.1-alpha.0.20180918132555-dff351593ded/zygosome.go (about)

     1  // Copyright (C) 2013-2018, The MetaCurrency Project (Eric Harris-Braun, Arthur Brock, et. al.)
     2  // Use of this source code is governed by GPLv3 found in the LICENSE file
     3  //----------------------------------------------------------------------------------------
     4  // ZygoRibosome implements a zygomys use of the Ribosome interface
     5  
     6  package holochain
     7  
     8  import (
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	zygo "github.com/glycerine/zygomys/zygo"
    13  	. "github.com/holochain/holochain-proto/hash"
    14  	peer "github.com/libp2p/go-libp2p-peer"
    15  	"math"
    16  	"regexp"
    17  	"strconv"
    18  	"strings"
    19  	"time"
    20  )
    21  
    22  const (
    23  	ZygoRibosomeType = "zygo"
    24  )
    25  
    26  // ZygoRibosome holds data needed for the Zygo VM
    27  type ZygoRibosome struct {
    28  	h          *Holochain
    29  	zome       *Zome
    30  	env        *zygo.Zlisp
    31  	lastResult zygo.Sexp
    32  	library    string
    33  }
    34  
    35  // Type returns the string value under which this ribosome is registered
    36  func (z *ZygoRibosome) Type() string { return ZygoRibosomeType }
    37  
    38  // ChainGenesis runs the application genesis function
    39  // this function gets called after the genesis entries are added to the chain
    40  func (z *ZygoRibosome) ChainGenesis() (err error) {
    41  	err = z.boolFn("genesis", "")
    42  	return
    43  }
    44  
    45  // BridgeGenesis runs the bridging genesis function
    46  // this function gets called on both sides of the bridging
    47  func (z *ZygoRibosome) BridgeGenesis(side int, dnaHash Hash, data string) (err error) {
    48  	err = z.boolFn("bridgeGenesis", fmt.Sprintf(`%d "%s" "%s"`, side, dnaHash.String(), sanitizeZyString(data)))
    49  	return
    50  }
    51  
    52  func (z *ZygoRibosome) boolFn(fnName string, args string) (err error) {
    53  	err = z.env.LoadString("(" + fnName + " " + args + ")")
    54  	if err != nil {
    55  		return
    56  	}
    57  	result, err := z.env.Run()
    58  	if err != nil {
    59  		err = fmt.Errorf("Error executing %s: %v", fnName, err)
    60  		return
    61  	}
    62  	switch result.(type) {
    63  	case *zygo.SexpBool:
    64  		r := result.(*zygo.SexpBool).Val
    65  		if !r {
    66  			err = fmt.Errorf("%s failed", fnName)
    67  		}
    68  	case *zygo.SexpSentinel:
    69  		err = fmt.Errorf("%s should return boolean, got nil", fnName)
    70  
    71  	default:
    72  		err = fmt.Errorf("%s should return boolean, got: %v", fnName, result)
    73  	}
    74  	return
    75  
    76  }
    77  
    78  // Receive calls the app receive function for node-to-node messages
    79  func (z *ZygoRibosome) Receive(from string, msg string) (response string, err error) {
    80  	var code string
    81  	fnName := "receive"
    82  
    83  	code = fmt.Sprintf(`(json (%s "%s" (unjson (raw "%s"))))`, fnName, from, sanitizeZyString(msg))
    84  	z.h.Debug(code)
    85  	err = z.env.LoadString(code)
    86  	if err != nil {
    87  		return
    88  	}
    89  	var result interface{}
    90  	result, err = z.env.Run()
    91  	if err == nil {
    92  		switch t := result.(type) {
    93  		case *zygo.SexpStr:
    94  			response = t.S
    95  		case *zygo.SexpInt:
    96  			response = fmt.Sprintf("%d", t.Val)
    97  		case *zygo.SexpRaw:
    98  			response = cleanZygoJson(string(t.Val))
    99  		default:
   100  			result = fmt.Sprintf("%v", result)
   101  		}
   102  	}
   103  	return
   104  }
   105  
   106  // BundleCancel calls the app bundleCanceled function
   107  func (z *ZygoRibosome) BundleCanceled(reason string) (response string, err error) {
   108  	return
   109  }
   110  
   111  // ValidatePackagingRequest calls the app for a validation packaging request for an action
   112  func (z *ZygoRibosome) ValidatePackagingRequest(action ValidatingAction, def *EntryDef) (req PackagingReq, err error) {
   113  	var code string
   114  	fnName := "validate" + strings.Title(action.Name()) + "Pkg"
   115  	code = fmt.Sprintf(`(%s "%s")`, fnName, def.Name)
   116  	z.h.Debug(code)
   117  	err = z.env.LoadString(code)
   118  	if err != nil {
   119  		return
   120  	}
   121  	result, err := z.env.Run()
   122  	if err != nil {
   123  		err = fmt.Errorf("Error executing %s: %v", fnName, err)
   124  		return
   125  	}
   126  	switch v := result.(type) {
   127  	case *zygo.SexpHash:
   128  		j := cleanZygoJson(zygo.SexpToJson(v))
   129  		m := make(PackagingReq)
   130  		err = json.Unmarshal([]byte(j), &m)
   131  		if err != nil {
   132  			return
   133  		}
   134  		delete(m, "zKeyOrder")
   135  		delete(m, "Atype")
   136  		req = m
   137  	case *zygo.SexpSentinel:
   138  	default:
   139  		err = fmt.Errorf("%s should return nil or hash, got: %v", fnName, v)
   140  	}
   141  
   142  	return
   143  }
   144  
   145  func prepareZyEntryArgs(def *EntryDef, entry Entry, header *Header) (args string, err error) {
   146  	entryStr := entry.Content().(string)
   147  	switch def.DataFormat {
   148  	case DataFormatRawZygo:
   149  		args = entryStr
   150  	case DataFormatString:
   151  		args = "\"" + sanitizeZyString(entryStr) + "\""
   152  	case DataFormatLinks:
   153  		fallthrough
   154  	case DataFormatJSON:
   155  		args = fmt.Sprintf(`(unjson (raw "%s"))`, sanitizeZyString(entryStr))
   156  	default:
   157  		err = errors.New("data format not implemented: " + def.DataFormat)
   158  		return
   159  	}
   160  
   161  	var hdr string
   162  	if header != nil {
   163  		hdr = fmt.Sprintf(
   164  			`(hash EntryLink:"%s" Type:"%s" Time:"%s")`,
   165  			header.EntryLink.String(),
   166  			header.Type,
   167  			header.Time.UTC().Format(time.RFC3339),
   168  		)
   169  	} else {
   170  		hdr = `""`
   171  	}
   172  
   173  	args += " " + hdr
   174  	return
   175  }
   176  
   177  func prepareZyValidateArgs(action Action, def *EntryDef) (args string, err error) {
   178  	switch t := action.(type) {
   179  	case *ActionCommit:
   180  		args, err = prepareZyEntryArgs(def, t.entry, t.header)
   181  	case *ActionPut:
   182  		args, err = prepareZyEntryArgs(def, t.entry, t.header)
   183  	case *ActionMod:
   184  		args, err = prepareZyEntryArgs(def, t.entry, t.header)
   185  		if err == nil {
   186  			args += fmt.Sprintf(` "%s"`, t.replaces.String())
   187  		}
   188  	case *ActionDel:
   189  		args = fmt.Sprintf(`"%s"`, t.entry.Hash.String())
   190  	case *ActionLink:
   191  		var j []byte
   192  		j, err = json.Marshal(t.links)
   193  		if err == nil {
   194  			args = fmt.Sprintf(`"%s" (unjson (raw "%s"))`, t.validationBase.String(), sanitizeZyString(string(j)))
   195  		}
   196  	default:
   197  		err = fmt.Errorf("can't prepare args for %T: ", t)
   198  		return
   199  	}
   200  	return
   201  }
   202  
   203  func buildZyValidateAction(action Action, def *EntryDef, pkg *ValidationPackage, sources []string) (code string, err error) {
   204  	fnName := "validate" + strings.Title(action.Name())
   205  	var args string
   206  	args, err = prepareZyValidateArgs(action, def)
   207  	if err != nil {
   208  		return
   209  	}
   210  	srcs := mkZySources(sources)
   211  
   212  	var pkgObj string
   213  	if pkg == nil || pkg.Chain == nil {
   214  		pkgObj = "(hash)"
   215  	} else {
   216  		var j []byte
   217  		j, err = json.Marshal(pkg.Chain)
   218  		if err != nil {
   219  			return
   220  		}
   221  		pkgObj = fmt.Sprintf(`(unjson (raw "%s"))`, sanitizeZyString(string(j)))
   222  	}
   223  
   224  	code = fmt.Sprintf(`(%s "%s" %s %s %s)`, fnName, def.Name, args, pkgObj, srcs)
   225  
   226  	return
   227  }
   228  
   229  // ValidateAction builds the correct validation function based on the action an calls it
   230  func (z *ZygoRibosome) ValidateAction(action Action, def *EntryDef, pkg *ValidationPackage, sources []string) (err error) {
   231  	var code string
   232  	code, err = buildZyValidateAction(action, def, pkg, sources)
   233  	if err != nil {
   234  		return
   235  	}
   236  	z.h.Debug(code)
   237  	err = z.runValidate(action.Name(), code)
   238  	return
   239  }
   240  
   241  func mkZySources(sources []string) (srcs string) {
   242  	var err error
   243  	var b []byte
   244  	b, err = json.Marshal(sources)
   245  	if err != nil {
   246  		return
   247  	}
   248  	srcs = fmt.Sprintf(`(unjson (raw "%s"))`, sanitizeZyString(string(b)))
   249  	return
   250  }
   251  
   252  func (z *ZygoRibosome) prepareValidateArgs(def *EntryDef, entry Entry, sources []string) (e string, srcs string, err error) {
   253  	c := entry.Content().(string)
   254  	// @todo handle JSON if schema type is different
   255  	switch def.DataFormat {
   256  	case DataFormatRawZygo:
   257  		e = c
   258  	case DataFormatString:
   259  		e = "\"" + sanitizeZyString(c) + "\""
   260  	case DataFormatLinks:
   261  		fallthrough
   262  	case DataFormatJSON:
   263  		e = fmt.Sprintf(`(unjson (raw "%s"))`, sanitizeZyString(c))
   264  	default:
   265  		err = errors.New("data format not implemented: " + def.DataFormat)
   266  		return
   267  	}
   268  	srcs = mkZySources(sources)
   269  	return
   270  }
   271  
   272  func (z *ZygoRibosome) runValidate(fnName string, code string) (err error) {
   273  	err = z.env.LoadString(code)
   274  	if err != nil {
   275  		return
   276  	}
   277  	result, err := z.env.Run()
   278  	if err != nil {
   279  		err = fmt.Errorf("Error executing %s: %v", fnName, err)
   280  		return
   281  	}
   282  	switch v := result.(type) {
   283  	case *zygo.SexpBool:
   284  		r := v.Val
   285  		if !r {
   286  			err = ValidationFailed()
   287  		}
   288  	case *zygo.SexpStr:
   289  		if v.S != "" {
   290  			err = ValidationFailed(v.S)
   291  		}
   292  	case *zygo.SexpSentinel:
   293  		err = fmt.Errorf("%s should return boolean or string, got nil", fnName)
   294  
   295  	default:
   296  		err = fmt.Errorf("%s should return boolean or string, got: %v", fnName, result)
   297  	}
   298  	return
   299  }
   300  
   301  func (z *ZygoRibosome) validateEntry(fnName string, def *EntryDef, entry Entry, header *Header, sources []string) (err error) {
   302  	e, srcs, err := z.prepareValidateArgs(def, entry, sources)
   303  	if err != nil {
   304  		return
   305  	}
   306  
   307  	var hdr string
   308  	if header != nil {
   309  		hdr = fmt.Sprintf(
   310  			`(hash EntryLink:"%s" Type:"%s" Time:"%s")`,
   311  			header.EntryLink.String(),
   312  			header.Type,
   313  			header.Time.UTC().Format(time.RFC3339),
   314  		)
   315  	} else {
   316  		hdr = `""`
   317  	}
   318  
   319  	code := fmt.Sprintf(`(%s "%s" %s %s %s)`, fnName, def.Name, e, hdr, srcs)
   320  	z.h.Debugf("%s: %s", fnName, code)
   321  
   322  	err = z.runValidate(fnName, code)
   323  	return
   324  }
   325  
   326  // sanitizeZyString makes sure all quotes are quoted
   327  func sanitizeZyString(s string) string {
   328  	s = strings.Replace(s, "\"", "\\\"", -1)
   329  	return s
   330  }
   331  
   332  // Call calls the zygo function that was registered with expose
   333  func (z *ZygoRibosome) Call(fn *FunctionDef, params interface{}) (result interface{}, err error) {
   334  	var code string
   335  	switch fn.CallingType {
   336  	case STRING_CALLING:
   337  		code = fmt.Sprintf(`(%s "%s")`, fn.Name, sanitizeZyString(params.(string)))
   338  	case JSON_CALLING:
   339  		if params.(string) == "" {
   340  			code = fmt.Sprintf(`(json (%s (raw "%s")))`, fn.Name, sanitizeZyString(params.(string)))
   341  		} else {
   342  			code = fmt.Sprintf(`(json (%s (unjson (raw "%s"))))`, fn.Name, sanitizeZyString(params.(string)))
   343  		}
   344  	default:
   345  		err = errors.New("params type not implemented")
   346  		return
   347  	}
   348  	z.h.Debugf("Zygo Call: %s", code)
   349  	err = z.env.LoadString(code)
   350  	if err != nil {
   351  		return
   352  	}
   353  	result, err = z.env.Run()
   354  	if err == nil {
   355  		switch fn.CallingType {
   356  		case STRING_CALLING:
   357  			switch t := result.(type) {
   358  			case *zygo.SexpStr:
   359  				result = t.S
   360  			case *zygo.SexpInt:
   361  				result = fmt.Sprintf("%d", t.Val)
   362  			case *zygo.SexpRaw:
   363  				result = string(t.Val)
   364  			default:
   365  				result = fmt.Sprintf("%v", result)
   366  			}
   367  		case JSON_CALLING:
   368  			// type should always be SexpRaw
   369  			switch t := result.(type) {
   370  			case *zygo.SexpRaw:
   371  				result = cleanZygoJson(string(t.Val))
   372  			default:
   373  				err = errors.New("expected SexpRaw return type")
   374  			}
   375  		}
   376  
   377  	}
   378  	return
   379  }
   380  
   381  // These are the zygo implementations of the library functions that must available in
   382  // all Ribosome implementations.
   383  const (
   384  	ZygoLibrary = `(def HC_Version "` + VersionStr + `")` +
   385  		`(def HC_HashNotFound nil)` +
   386  		`(def HC_Status_Live ` + StatusLiveVal + ")" +
   387  		`(def HC_Status_Rejected ` + StatusRejectedVal + ")" +
   388  		`(def HC_Status_Deleted ` + StatusDeletedVal + ")" +
   389  		`(def HC_Status_Modified ` + StatusModifiedVal + ")" +
   390  		`(def HC_Status_Any ` + StatusAnyVal + ")" +
   391  		`(def HC_GetMask_Default ` + GetMaskDefaultStr + ")" +
   392  		`(def HC_GetMask_Entry ` + GetMaskEntryStr + ")" +
   393  		`(def HC_GetMask_EntryType ` + GetMaskEntryTypeStr + ")" +
   394  		`(def HC_GetMask_Sources ` + GetMaskSourcesStr + ")" +
   395  		`(def HC_GetMask_All ` + GetMaskAllStr + ")" +
   396  
   397  		`(def HC_Bridge_From ` + BridgeFromStr + ")" +
   398  		`(def HC_Bridge_To ` + BridgeToStr + ")" +
   399  
   400  		`(def HC_LinkAction_Add "` + AddAction + "\")" +
   401  		`(def HC_LinkAction_Del "` + DelAction + "\")" +
   402  		`(def HC_PkgReq_Chain "` + PkgReqChain + "\")" +
   403  		`(def HC_PkgReq_ChainOpt_None "` + PkgReqChainOptNoneStr + "\")" +
   404  		`(def HC_PkgReq_ChainOpt_Headers "` + PkgReqChainOptHeadersStr + "\")" +
   405  		`(def HC_PkgReq_ChainOpt_Entries "` + PkgReqChainOptEntriesStr + "\")" +
   406  		`(def HC_PkgReq_ChainOpt_Full "` + PkgReqChainOptFullStr + "\")"
   407  )
   408  
   409  func makeResult(env *zygo.Zlisp, resultValue zygo.Sexp, resultError error) (zygo.Sexp, error) {
   410  	result, err := zygo.MakeHash(nil, "hash", env)
   411  	if err != nil {
   412  		return nil, err
   413  	}
   414  	if resultError != nil {
   415  		err = result.HashSet(env.MakeSymbol("error"), &zygo.SexpStr{S: resultError.Error()})
   416  	} else {
   417  		err = result.HashSet(env.MakeSymbol("result"), resultValue)
   418  	}
   419  	return result, err
   420  }
   421  
   422  // cleanZygoJson removes zygos crazy crap
   423  func cleanZygoJson(s string) string {
   424  	s = strings.Replace(s, `"Atype":"hash", `, "", -1)
   425  	re := regexp.MustCompile(`, "zKeyOrder":\[[^\]]+\]`)
   426  	s = string(re.ReplaceAll([]byte(s), []byte("")))
   427  	s = strings.Replace(s, `", "`, `","`, -1)
   428  	return s
   429  }
   430  
   431  func zyProcessArgs(z *ZygoRibosome, args []Arg, zyArgs []zygo.Sexp) (err error) {
   432  	err = checkArgCount(args, len(zyArgs))
   433  	if err != nil {
   434  		return err
   435  	}
   436  
   437  	// check arg types
   438  	for i, a := range zyArgs {
   439  		switch args[i].Type {
   440  		case StringArg:
   441  			var str string
   442  			switch t := a.(type) {
   443  			case *zygo.SexpStr:
   444  				str = t.S
   445  				args[i].value = str
   446  			default:
   447  				return argErr("string", i+1, args[i])
   448  			}
   449  		case HashArg:
   450  			switch t := a.(type) {
   451  			case *zygo.SexpStr:
   452  				var hash Hash
   453  				hash, err = NewHash(t.S)
   454  				if err != nil {
   455  					return
   456  				}
   457  				args[i].value = hash
   458  			default:
   459  				return argErr("string", i+1, args[i])
   460  			}
   461  		case IntArg:
   462  			var integer int64
   463  			switch t := a.(type) {
   464  			case *zygo.SexpInt:
   465  				integer = t.Val
   466  				args[i].value = integer
   467  			default:
   468  				return argErr("int", i+1, args[i])
   469  			}
   470  		case BoolArg:
   471  			var boolean bool
   472  			switch t := a.(type) {
   473  			case *zygo.SexpBool:
   474  				boolean = t.Val
   475  				args[i].value = boolean
   476  			default:
   477  				return argErr("boolean", i+1, args[i])
   478  			}
   479  		case ArgsArg:
   480  			switch t := a.(type) {
   481  			case *zygo.SexpStr:
   482  				args[i].value = t.S
   483  			case *zygo.SexpHash:
   484  				args[i].value = cleanZygoJson(zygo.SexpToJson(t))
   485  			default:
   486  				return argErr("string or hash", i+1, args[i])
   487  			}
   488  		case EntryArg:
   489  			// this a special case in that all EntryArgs must be preceeded by
   490  			// string arg that specifies the entry type
   491  
   492  			// don't have to do checking because the previous time through the loop
   493  			// should have done it
   494  			entryType := zyArgs[i-1].(*zygo.SexpStr).S
   495  			_, def, err := z.h.GetEntryDef(entryType)
   496  			if err != nil {
   497  				return err
   498  			}
   499  
   500  			var entry string
   501  			switch def.DataFormat {
   502  			case DataFormatRawZygo:
   503  				fallthrough
   504  			case DataFormatRawJS:
   505  				fallthrough
   506  			case DataFormatString:
   507  				switch t := a.(type) {
   508  				case *zygo.SexpStr:
   509  					entry = t.S
   510  				default:
   511  					return argErr("string", i+1, args[i])
   512  				}
   513  			case DataFormatLinks:
   514  				switch t := a.(type) {
   515  				case *zygo.SexpHash:
   516  					entry = cleanZygoJson(zygo.SexpToJson(t))
   517  				default:
   518  					return argErr("hash", i+1, args[i])
   519  				}
   520  			case DataFormatJSON:
   521  				switch a.(type) {
   522  				case *zygo.SexpSentinel:
   523  					entry = "undefined"
   524  				default:
   525  					entry = cleanZygoJson(zygo.SexpToJson(a))
   526  				}
   527  			default:
   528  				err = errors.New("data format not implemented: " + def.DataFormat)
   529  				return err
   530  			}
   531  			args[i].value = entry
   532  
   533  		case MapArg:
   534  			switch t := a.(type) {
   535  			case *zygo.SexpHash:
   536  				j := cleanZygoJson(zygo.SexpToJson(t))
   537  				m := make(map[string]interface{})
   538  				var err = json.Unmarshal([]byte(j), &m)
   539  				if err != nil {
   540  					return err
   541  				}
   542  				args[i].value = m
   543  			default:
   544  				return argErr("hash", i+1, args[i])
   545  			}
   546  		case ToStrArg:
   547  			var str string
   548  
   549  			switch t := a.(type) {
   550  			case *zygo.SexpStr:
   551  				str = t.S
   552  			case *zygo.SexpInt:
   553  				str = fmt.Sprintf("%d", t.Val)
   554  			case *zygo.SexpBool:
   555  				if t.Val {
   556  					str = "true"
   557  				} else {
   558  					str = "false"
   559  				}
   560  			case *zygo.SexpHash:
   561  				str = cleanZygoJson(zygo.SexpToJson(t))
   562  			case *zygo.SexpArray:
   563  				str = cleanZygoJson(zygo.SexpToJson(t))
   564  			default:
   565  				return argErr("int, boolean, string, array or hash", i+1, args[i])
   566  			}
   567  			args[i].value = str
   568  		}
   569  	}
   570  
   571  	return
   572  }
   573  
   574  // NewZygoRibosome factory function to build a zygo execution environment for a zome
   575  func NewZygoRibosome(h *Holochain, zome *Zome) (n Ribosome, err error) {
   576  	z := ZygoRibosome{
   577  		h:    h,
   578  		zome: zome,
   579  		env:  zygo.NewZlispSandbox(),
   580  	}
   581  
   582  	z.env.AddFunction("version",
   583  		func(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
   584  			return &zygo.SexpStr{S: VersionStr}, nil
   585  		})
   586  
   587  	addExtras(&z)
   588  
   589  	var appKeyHash, appAgentStr, appAgentHash, appAgentTopHash zygo.SexpStr
   590  	if h != nil {
   591  		appKeyHash = zygo.SexpStr{S: h.nodeIDStr}
   592  		appAgentStr = zygo.SexpStr{S: sanitizeZyString(string(h.Agent().Identity()))}
   593  		appAgentHash = zygo.SexpStr{S: h.agentHash.String()}
   594  		appAgentTopHash = zygo.SexpStr{S: h.agentTopHash.String()}
   595  	}
   596  
   597  	// use a closure so that the registered zygo function can call Expose on the correct ZygoRibosome obj
   598  
   599  	z.env.AddFunction("property",
   600  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   601  			a := &ActionProperty{}
   602  			args := a.Args()
   603  			err := zyProcessArgs(&z, args, zyargs)
   604  			if err != nil {
   605  				return zygo.SexpNull, err
   606  			}
   607  
   608  			a.prop = args[0].value.(string)
   609  
   610  			var p interface{}
   611  			p, err = a.Do(h)
   612  
   613  			if err != nil {
   614  				return zygo.SexpNull, err
   615  			}
   616  			result := zygo.SexpStr{S: p.(string)}
   617  			return &result, err
   618  		})
   619  
   620  	z.env.AddFunction("debug",
   621  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   622  			a := &ActionDebug{}
   623  			args := a.Args()
   624  			err := zyProcessArgs(&z, args, zyargs)
   625  			if err != nil {
   626  				return zygo.SexpNull, err
   627  			}
   628  			a.msg = args[0].value.(string)
   629  			a.Do(h)
   630  			return zygo.SexpNull, err
   631  		})
   632  
   633  	z.env.AddFunction("makeHash",
   634  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   635  			a := &ActionMakeHash{}
   636  			args := a.Args()
   637  			err := zyProcessArgs(&z, args, zyargs)
   638  			if err != nil {
   639  				return zygo.SexpNull, err
   640  			}
   641  			a.entryType = args[0].value.(string)
   642  			a.entry = &GobEntry{C: args[1].value.(string)}
   643  			var r interface{}
   644  			r, err = a.Do(h)
   645  			if err != nil {
   646  				return zygo.SexpNull, err
   647  			}
   648  			var entryHash Hash
   649  			if r != nil {
   650  				entryHash = r.(Hash)
   651  			}
   652  			var result = zygo.SexpStr{S: entryHash.String()}
   653  			return &result, nil
   654  		})
   655  
   656  	z.env.AddFunction("getBridges",
   657  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   658  			a := &ActionGetBridges{}
   659  			args := a.Args()
   660  			err := zyProcessArgs(&z, args, zyargs)
   661  			if err != nil {
   662  				return zygo.SexpNull, err
   663  			}
   664  			r, err := a.Do(h)
   665  			if err != nil {
   666  				return zygo.SexpNull, err
   667  			}
   668  			var zbridges *zygo.SexpArray
   669  
   670  			var bridges []zygo.Sexp
   671  			for _, b := range r.([]Bridge) {
   672  				var bridge *zygo.SexpHash
   673  				bridge, err = zygo.MakeHash(nil, "hash", env)
   674  				if err != nil {
   675  					return zygo.SexpNull, err
   676  				}
   677  				if b.Side == BridgeTo {
   678  					err = bridge.HashSet(env.MakeSymbol("Side"), &zygo.SexpInt{Val: int64(b.Side)})
   679  					if err != nil {
   680  						return zygo.SexpNull, err
   681  					}
   682  					err = bridge.HashSet(env.MakeSymbol("Token"), &zygo.SexpStr{S: b.Token})
   683  					if err != nil {
   684  						return zygo.SexpNull, err
   685  					}
   686  				} else {
   687  					err = bridge.HashSet(env.MakeSymbol("Side"), &zygo.SexpInt{Val: int64(b.Side)})
   688  					if err != nil {
   689  						return zygo.SexpNull, err
   690  					}
   691  					err = bridge.HashSet(env.MakeSymbol("ToApp"), &zygo.SexpStr{S: b.ToApp.String()})
   692  					if err != nil {
   693  						return zygo.SexpNull, err
   694  					}
   695  				}
   696  				bridges = append(bridges, bridge)
   697  			}
   698  			zbridges = env.NewSexpArray(bridges)
   699  			return zbridges, err
   700  		})
   701  
   702  	z.env.AddFunction("send",
   703  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   704  			a := &ActionSend{}
   705  			args := a.Args()
   706  			err := zyProcessArgs(&z, args, zyargs)
   707  			if err != nil {
   708  				return zygo.SexpNull, err
   709  			}
   710  
   711  			a.to, err = peer.IDB58Decode(args[0].value.(Hash).String())
   712  			if err != nil {
   713  				return zygo.SexpNull, err
   714  			}
   715  
   716  			msg := args[1].value.(map[string]interface{})
   717  			var j []byte
   718  			j, err = json.Marshal(msg)
   719  			if err != nil {
   720  				return zygo.SexpNull, err
   721  			}
   722  
   723  			a.msg.ZomeType = z.zome.Name
   724  			a.msg.Body = string(j)
   725  
   726  			if args[2].value != nil {
   727  				a.options = &SendOptions{}
   728  				opts := args[2].value.(map[string]interface{})
   729  				cbmap, ok := opts["Callback"]
   730  				if ok {
   731  					callback := Callback{zomeType: zome.Name}
   732  					v, ok := cbmap.(map[string]interface{})["Function"]
   733  					if !ok {
   734  						return zygo.SexpNull, errors.New("callback option requires Function")
   735  					}
   736  					callback.Function = v.(string)
   737  					v, ok = cbmap.(map[string]interface{})["ID"]
   738  					if !ok {
   739  						return zygo.SexpNull, errors.New("callback option requires ID")
   740  					}
   741  					callback.ID = v.(string)
   742  					a.options.Callback = &callback
   743  				}
   744  				timeout, ok := opts["Timeout"]
   745  				if ok {
   746  					a.options.Timeout = int(timeout.(int64))
   747  				}
   748  			}
   749  
   750  			var r interface{}
   751  			r, err = a.Do(h)
   752  			var resp zygo.Sexp
   753  			if err == nil {
   754  				switch t := r.(type) {
   755  				case string:
   756  					resp = &zygo.SexpStr{S: t}
   757  				case nil:
   758  					resp = zygo.SexpNull
   759  				default:
   760  					return zygo.SexpNull, errors.New("send should return nil or string")
   761  				}
   762  			}
   763  			return makeResult(env, resp, err)
   764  		})
   765  
   766  	z.env.AddFunction("call",
   767  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   768  			a := &ActionCall{}
   769  			args := a.Args()
   770  			err := zyProcessArgs(&z, args, zyargs)
   771  			if err != nil {
   772  				return zygo.SexpNull, err
   773  			}
   774  			a.zome = args[0].value.(string)
   775  			var zome *Zome
   776  			zome, err = h.GetZome(a.zome)
   777  			if err != nil {
   778  				return zygo.SexpNull, err
   779  			}
   780  			a.function = args[1].value.(string)
   781  			var fn *FunctionDef
   782  			fn, err = zome.GetFunctionDef(a.function)
   783  			if err != nil {
   784  				return zygo.SexpNull, err
   785  			}
   786  			if fn.CallingType == JSON_CALLING {
   787  				switch zyargs[2].(type) {
   788  				case *zygo.SexpHash:
   789  					a.args = args[2].value
   790  				default:
   791  					return zygo.SexpNull, errors.New("function calling type requires hash argument type")
   792  				}
   793  
   794  			} else {
   795  				a.args = args[2].value.(string)
   796  			}
   797  			var r interface{}
   798  			r, err = a.Do(h)
   799  			if err != nil {
   800  				return zygo.SexpNull, err
   801  			}
   802  			return &zygo.SexpStr{S: r.(string)}, err
   803  		})
   804  
   805  	z.env.AddFunction("bridge",
   806  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   807  			a := &ActionBridge{}
   808  			args := a.Args()
   809  			err := zyProcessArgs(&z, args, zyargs)
   810  			if err != nil {
   811  				return zygo.SexpNull, err
   812  			}
   813  			hash := args[0].value.(Hash)
   814  			a.token, a.url, err = h.GetBridgeToken(hash)
   815  			if err != nil {
   816  				return zygo.SexpNull, err
   817  			}
   818  
   819  			a.zome = args[1].value.(string)
   820  			a.function = args[2].value.(string)
   821  			a.args = args[3].value.(string)
   822  
   823  			var r interface{}
   824  			r, err = a.Do(h)
   825  			if err != nil {
   826  				return zygo.SexpNull, err
   827  			}
   828  
   829  			return &zygo.SexpStr{S: r.(string)}, err
   830  		})
   831  
   832  	z.env.AddFunction("commit",
   833  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   834  			var a Action = &ActionCommit{}
   835  			args := a.Args()
   836  			err := zyProcessArgs(&z, args, zyargs)
   837  			if err != nil {
   838  				return zygo.SexpNull, err
   839  			}
   840  			entryType := args[0].value.(string)
   841  			entry := args[1].value.(string)
   842  			var r interface{}
   843  			e := GobEntry{C: entry}
   844  			r, err = NewCommitAction(entryType, &e).Do(h)
   845  			if err != nil {
   846  				return zygo.SexpNull, err
   847  			}
   848  			var entryHash Hash
   849  			if r != nil {
   850  				entryHash = r.(Hash)
   851  			}
   852  			var result = zygo.SexpStr{S: entryHash.String()}
   853  			return &result, nil
   854  		})
   855  
   856  	z.env.AddFunction("query",
   857  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   858  			a := &ActionQuery{}
   859  			args := a.Args()
   860  			err := zyProcessArgs(&z, args, zyargs)
   861  			if err != nil {
   862  				return zygo.SexpNull, err
   863  			}
   864  
   865  			if len(zyargs) == 1 {
   866  				options := QueryOptions{}
   867  				var j []byte
   868  				j, err = json.Marshal(args[0].value)
   869  				if err != nil {
   870  					return zygo.SexpNull, err
   871  				}
   872  				z.h.Debugf("Query options: %s", string(j))
   873  				err = json.Unmarshal(j, &options)
   874  				if err != nil {
   875  					return zygo.SexpNull, err
   876  				}
   877  				a.options = &options
   878  			}
   879  
   880  			r, err := a.Do(h)
   881  			if err != nil {
   882  				return zygo.SexpNull, err
   883  			}
   884  			qr := r.([]QueryResult)
   885  
   886  			defs := make(map[string]*EntryDef)
   887  			results := make([]zygo.Sexp, len(qr))
   888  			for i, result := range qr {
   889  				var sexp zygo.Sexp
   890  				var hashSexp, entrySexp *zygo.SexpStr
   891  				var headerSexp *zygo.SexpHash
   892  				var returnCount int
   893  				if a.options.Return.Hashes {
   894  					returnCount += 1
   895  					hashSexp = &zygo.SexpStr{S: result.Header.EntryLink.String()}
   896  					sexp = hashSexp
   897  				}
   898  				if a.options.Return.Headers {
   899  					returnCount += 1
   900  					headerSexp, err = zygo.MakeHash(nil, "hash", env)
   901  					if err != nil {
   902  						return zygo.SexpNull, err
   903  					}
   904  					sexp = headerSexp
   905  					// TODO REFACTOR!!
   906  					err = headerSexp.HashSet(env.MakeSymbol("Time"), &zygo.SexpStr{S: fmt.Sprintf("%v", result.Header.Time)})
   907  					if err != nil {
   908  						return zygo.SexpNull, err
   909  					}
   910  					err = headerSexp.HashSet(env.MakeSymbol("Type"), &zygo.SexpStr{S: result.Header.Type})
   911  					if err != nil {
   912  						return zygo.SexpNull, err
   913  					}
   914  					err = headerSexp.HashSet(env.MakeSymbol("EntryLink"), &zygo.SexpStr{S: result.Header.EntryLink.String()})
   915  					if err != nil {
   916  						return zygo.SexpNull, err
   917  					}
   918  					err = headerSexp.HashSet(env.MakeSymbol("HeaderLink"), &zygo.SexpStr{S: result.Header.HeaderLink.String()})
   919  					if err != nil {
   920  						return zygo.SexpNull, err
   921  					}
   922  					err = headerSexp.HashSet(env.MakeSymbol("TypeLink"), &zygo.SexpStr{S: result.Header.TypeLink.String()})
   923  					if err != nil {
   924  						return zygo.SexpNull, err
   925  					}
   926  				}
   927  
   928  				if a.options.Return.Entries {
   929  					returnCount += 1
   930  
   931  					var def *EntryDef
   932  					var ok bool
   933  					def, ok = defs[result.Header.Type]
   934  					if !ok {
   935  						_, def, err = h.GetEntryDef(result.Header.Type)
   936  						if err != nil {
   937  							return zygo.SexpNull, err
   938  						}
   939  						defs[result.Header.Type] = def
   940  					}
   941  					var content string
   942  					switch def.DataFormat {
   943  					case DataFormatRawZygo:
   944  						fallthrough
   945  					case DataFormatRawJS:
   946  						fallthrough
   947  					case DataFormatString:
   948  						fallthrough
   949  					case DataFormatLinks:
   950  						fallthrough
   951  					case DataFormatJSON:
   952  						content = result.Entry.Content().(string)
   953  					default:
   954  						return zygo.SexpNull, fmt.Errorf("data format not implemented: %s", def.DataFormat)
   955  					}
   956  					entrySexp = &zygo.SexpStr{S: content}
   957  					sexp = entrySexp
   958  
   959  				}
   960  				if returnCount > 1 {
   961  					var result *zygo.SexpHash
   962  					result, err = zygo.MakeHash(nil, "hash", env)
   963  					if err == nil && headerSexp != nil {
   964  						err = result.HashSet(env.MakeSymbol("Header"), headerSexp)
   965  					}
   966  					if err == nil && hashSexp != nil {
   967  						err = result.HashSet(env.MakeSymbol("Hash"), hashSexp)
   968  					}
   969  					if err == nil && entrySexp != nil {
   970  						err = result.HashSet(env.MakeSymbol("Entry"), entrySexp)
   971  					}
   972  					if err != nil {
   973  						return zygo.SexpNull, err
   974  					}
   975  					sexp = result
   976  				}
   977  				results[i] = sexp
   978  			}
   979  
   980  			return env.NewSexpArray(results), nil
   981  		})
   982  
   983  	z.env.AddFunction("get",
   984  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
   985  			var a Action = &ActionGet{}
   986  			args := a.Args()
   987  			err := zyProcessArgs(&z, args, zyargs)
   988  			if err != nil {
   989  				return zygo.SexpNull, err
   990  			}
   991  			options := GetOptions{StatusMask: StatusDefault, GetMask: GetMaskDefault}
   992  			if len(zyargs) == 2 {
   993  				opts := args[1].value.(map[string]interface{})
   994  				mask, ok := opts["StatusMask"]
   995  				if ok {
   996  					maskval, ok := mask.(float64)
   997  					if !ok {
   998  						return zygo.SexpNull,
   999  							fmt.Errorf("expecting int StatusMask attribute, got %T", mask)
  1000  					}
  1001  					options.StatusMask = int(maskval)
  1002  				}
  1003  				mask, ok = opts["GetMask"]
  1004  				if ok {
  1005  					maskval, ok := mask.(float64)
  1006  					if !ok {
  1007  						return zygo.SexpNull,
  1008  							fmt.Errorf("expecting int GetMask attribute, got %T", mask)
  1009  					}
  1010  					options.GetMask = int(maskval)
  1011  				}
  1012  				local, ok := opts["Local"]
  1013  				if ok {
  1014  					options.Local = local.(bool)
  1015  				}
  1016  
  1017  			}
  1018  			req := GetReq{H: args[0].value.(Hash), StatusMask: options.StatusMask, GetMask: options.GetMask}
  1019  
  1020  			var r interface{}
  1021  			r, err = NewGetAction(req, &options).Do(h)
  1022  			mask := options.GetMask
  1023  			if mask == GetMaskDefault {
  1024  				mask = GetMaskEntry
  1025  			}
  1026  			var resultValue zygo.Sexp
  1027  			resultValue = zygo.SexpNull
  1028  			if err == ErrHashNotFound {
  1029  				// if the hash wasn't found this isn't actually an error
  1030  				// so return nil which is the same as HC_HashNotFound
  1031  				err = nil
  1032  				return zygo.SexpNull, err
  1033  			} else if err == nil {
  1034  				getResp := r.(GetResp)
  1035  				var entryStr string
  1036  				var singleValueReturn bool
  1037  				if mask&GetMaskEntry != 0 {
  1038  					j, err := json.Marshal(getResp.Entry.Content())
  1039  					if err == nil {
  1040  						if GetMaskEntry == mask {
  1041  							singleValueReturn = true
  1042  							resultValue = &zygo.SexpStr{S: string(j)}
  1043  						} else {
  1044  							entryStr = string(j)
  1045  						}
  1046  					}
  1047  				}
  1048  				if mask&GetMaskEntryType != 0 {
  1049  					if GetMaskEntryType == mask {
  1050  						singleValueReturn = true
  1051  						resultValue = &zygo.SexpStr{S: getResp.EntryType}
  1052  					}
  1053  				}
  1054  				var zSources *zygo.SexpArray
  1055  				if mask&GetMaskSources != 0 {
  1056  					sources := make([]zygo.Sexp, len(getResp.Sources))
  1057  					for i := range getResp.Sources {
  1058  						sources[i] = &zygo.SexpStr{S: getResp.Sources[i]}
  1059  					}
  1060  					zSources = env.NewSexpArray(sources)
  1061  					if GetMaskSources == mask {
  1062  						singleValueReturn = true
  1063  						resultValue = zSources
  1064  					}
  1065  				}
  1066  				if err == nil && !singleValueReturn {
  1067  					// build the return object
  1068  					var respObj *zygo.SexpHash
  1069  					respObj, err = zygo.MakeHash(nil, "hash", env)
  1070  					if err == nil {
  1071  						resultValue = respObj
  1072  						if mask&GetMaskEntry != 0 {
  1073  							err = respObj.HashSet(env.MakeSymbol("Entry"), &zygo.SexpStr{S: entryStr})
  1074  						}
  1075  						if err == nil && mask&GetMaskEntryType != 0 {
  1076  							err = respObj.HashSet(env.MakeSymbol("EntryType"), &zygo.SexpStr{S: getResp.EntryType})
  1077  						}
  1078  						if err == nil && mask&GetMaskSources != 0 {
  1079  							err = respObj.HashSet(env.MakeSymbol("Sources"), zSources)
  1080  						}
  1081  					}
  1082  				}
  1083  			}
  1084  			return makeResult(env, resultValue, err)
  1085  		})
  1086  
  1087  	z.env.AddFunction("update",
  1088  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
  1089  			var a Action = &ActionMod{}
  1090  			args := a.Args()
  1091  			err := zyProcessArgs(&z, args, zyargs)
  1092  			if err != nil {
  1093  				return zygo.SexpNull, err
  1094  			}
  1095  			entryType := args[0].value.(string)
  1096  			entryStr := args[1].value.(string)
  1097  			replaces := args[2].value.(Hash)
  1098  
  1099  			entry := GobEntry{C: entryStr}
  1100  			resp, err := NewModAction(entryType, &entry, replaces).Do(h)
  1101  			if err != nil {
  1102  				return zygo.SexpNull, err
  1103  			}
  1104  			var entryHash Hash
  1105  			if resp != nil {
  1106  				entryHash = resp.(Hash)
  1107  			}
  1108  			var result = zygo.SexpStr{S: entryHash.String()}
  1109  			return &result, nil
  1110  		})
  1111  
  1112  	z.env.AddFunction("updateAgent",
  1113  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
  1114  			a := &ActionModAgent{}
  1115  			//		var a Action = &ActionModAgent{}
  1116  			args := a.Args()
  1117  			err := zyProcessArgs(&z, args, zyargs)
  1118  			if err != nil {
  1119  				return zygo.SexpNull, err
  1120  			}
  1121  
  1122  			opts := args[0].value.(map[string]interface{})
  1123  			id, idok := opts["Identity"]
  1124  			if idok {
  1125  				a.Identity = AgentIdentity(id.(string))
  1126  			}
  1127  			rev, revok := opts["Revocation"]
  1128  			if revok {
  1129  				a.Revocation = rev.(string)
  1130  			}
  1131  
  1132  			resp, err := a.Do(h)
  1133  			if err != nil {
  1134  				return zygo.SexpNull, err
  1135  			}
  1136  			var agentEntryHash Hash
  1137  			if resp != nil {
  1138  				agentEntryHash = resp.(Hash)
  1139  			}
  1140  
  1141  			if revok {
  1142  				appKeyHash.S = h.nodeIDStr
  1143  			}
  1144  
  1145  			// there's always a new agent entry
  1146  			appAgentTopHash.S = h.agentTopHash.String()
  1147  
  1148  			// but not always a new identity to update
  1149  			if idok {
  1150  				appAgentStr.S = string(a.Identity)
  1151  			}
  1152  			var result = zygo.SexpStr{S: agentEntryHash.String()}
  1153  			return &result, nil
  1154  		})
  1155  
  1156  	z.env.AddFunction("remove",
  1157  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
  1158  			var a Action = &ActionDel{}
  1159  			args := a.Args()
  1160  			err := zyProcessArgs(&z, args, zyargs)
  1161  			if err != nil {
  1162  				return zygo.SexpNull, err
  1163  			}
  1164  			entry := DelEntry{
  1165  				Hash:    args[0].value.(Hash),
  1166  				Message: args[1].value.(string),
  1167  			}
  1168  			header, err := h.chain.GetEntryHeader(entry.Hash)
  1169  			if err == nil {
  1170  				resp, err := NewDelAction(header.Type, entry).Do(h)
  1171  				if err != nil {
  1172  					return zygo.SexpNull, err
  1173  				}
  1174  				var entryHash Hash
  1175  				if resp != nil {
  1176  					entryHash = resp.(Hash)
  1177  				}
  1178  				return &zygo.SexpStr{S: entryHash.String()}, err
  1179  			}
  1180  			return zygo.SexpNull, err
  1181  		})
  1182  
  1183  	z.env.AddFunction("getLinks",
  1184  		func(env *zygo.Zlisp, name string, zyargs []zygo.Sexp) (zygo.Sexp, error) {
  1185  			var a Action = &ActionGetLinks{}
  1186  			args := a.Args()
  1187  			err := zyProcessArgs(&z, args, zyargs)
  1188  			if err != nil {
  1189  				return zygo.SexpNull, err
  1190  			}
  1191  			base := args[0].value.(Hash)
  1192  			tag := args[1].value.(string)
  1193  
  1194  			options := GetLinksOptions{Load: false, StatusMask: StatusLive}
  1195  			if len(zyargs) == 3 {
  1196  				opts := args[2].value.(map[string]interface{})
  1197  				load, ok := opts["Load"]
  1198  				if ok {
  1199  					loadval, ok := load.(bool)
  1200  					if !ok {
  1201  						return zygo.SexpNull,
  1202  							fmt.Errorf("expecting boolean Load attribute in object, got %T", load)
  1203  					}
  1204  					options.Load = loadval
  1205  				}
  1206  				mask, ok := opts["StatusMask"]
  1207  				if ok {
  1208  					maskval, ok := mask.(float64)
  1209  					if !ok {
  1210  						return zygo.SexpNull,
  1211  							fmt.Errorf("expecting int StatusMask attribute in object, got %T", mask)
  1212  					}
  1213  					options.StatusMask = int(maskval)
  1214  				}
  1215  			}
  1216  
  1217  			var r interface{}
  1218  			r, err = NewGetLinksAction(&LinkQuery{Base: base, T: tag, StatusMask: options.StatusMask}, &options).Do(h)
  1219  			var resultValue zygo.Sexp
  1220  			if err == nil {
  1221  				response := r.(*LinkQueryResp)
  1222  				resultValue = zygo.SexpNull
  1223  				var j []byte
  1224  				j, err = json.Marshal(response.Links)
  1225  				if err == nil {
  1226  					resultValue = &zygo.SexpStr{S: string(j)}
  1227  				}
  1228  			}
  1229  			return makeResult(env, resultValue, err)
  1230  		})
  1231  
  1232  	l := ZygoLibrary
  1233  	if h != nil {
  1234  		z.env.AddGlobal("App_Name", &zygo.SexpStr{S: h.Name()})
  1235  		z.env.AddGlobal("App_DNA_Hash", &zygo.SexpStr{S: h.dnaHash.String()})
  1236  		z.env.AddGlobal("App_Key_Hash", &appKeyHash)
  1237  		z.env.AddGlobal("App_Agent_String", &appAgentStr)
  1238  		z.env.AddGlobal("App_Agent_Hash", &appAgentHash)
  1239  		z.env.AddGlobal("App_Agent_TopHash", &appAgentTopHash)
  1240  	}
  1241  	z.library = l
  1242  
  1243  	_, err = z.Run(l + zome.Code)
  1244  	if err != nil {
  1245  		return
  1246  	}
  1247  	n = &z
  1248  	return
  1249  }
  1250  
  1251  // Run executes zygo code
  1252  func (z *ZygoRibosome) Run(code string) (result interface{}, err error) {
  1253  	c := fmt.Sprintf("(begin %s %s)", z.library, code)
  1254  	err = z.env.LoadString(c)
  1255  	if err != nil {
  1256  		err = errors.New("Zygomys load error: " + err.Error())
  1257  		return
  1258  	}
  1259  	var sexp zygo.Sexp
  1260  	sexp, err = z.env.Run()
  1261  	if err != nil {
  1262  		err = errors.New("Zygomys exec error: " + err.Error())
  1263  		return
  1264  	}
  1265  	z.lastResult = sexp
  1266  	result = sexp
  1267  	return
  1268  }
  1269  
  1270  // extra functions we want to have available for app developers in zygo
  1271  
  1272  func isPrime(t int64) bool {
  1273  
  1274  	// math.Mod requires floats.
  1275  	x := float64(t)
  1276  
  1277  	// 1 or less aren't primes.
  1278  	if x <= 1 {
  1279  		return false
  1280  	}
  1281  
  1282  	// Solve half of the integer set directly
  1283  	if math.Mod(x, 2) == 0 {
  1284  		return x == 2
  1285  	}
  1286  
  1287  	// Main loop. i needs to be float because of math.Mod.
  1288  	for i := 3.0; i <= math.Floor(math.Sqrt(x)); i += 2.0 {
  1289  		if math.Mod(x, i) == 0 {
  1290  			return false
  1291  		}
  1292  	}
  1293  
  1294  	// It's a prime!
  1295  	return true
  1296  }
  1297  
  1298  func addExtras(z *ZygoRibosome) {
  1299  	z.env.AddFunction("isprime",
  1300  		func(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
  1301  
  1302  			switch t := args[0].(type) {
  1303  			case *zygo.SexpInt:
  1304  				return &zygo.SexpBool{Val: isPrime(t.Val)}, nil
  1305  			default:
  1306  				return zygo.SexpNull,
  1307  					errors.New("argument to isprime should be int")
  1308  			}
  1309  		})
  1310  	z.env.AddFunction("atoi",
  1311  		func(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
  1312  
  1313  			var i int64
  1314  			var e error
  1315  			switch t := args[0].(type) {
  1316  			case *zygo.SexpStr:
  1317  				i, e = strconv.ParseInt(t.S, 10, 64)
  1318  				if e != nil {
  1319  					return zygo.SexpNull, e
  1320  				}
  1321  			default:
  1322  				return zygo.SexpNull,
  1323  					errors.New("argument to atoi should be string")
  1324  			}
  1325  
  1326  			return &zygo.SexpInt{Val: i}, nil
  1327  		})
  1328  }
  1329  
  1330  func (z *ZygoRibosome) RunAsyncSendResponse(response AppMsg, callback string, callbackID string) (result interface{}, err error) {
  1331  	code := fmt.Sprintf(`(%s (unjson (raw "%s")) "%s")`, callback, sanitizeZyString(response.Body), sanitizeZyString(callbackID))
  1332  	z.h.Debugf("Calling %s\n", code)
  1333  	result, err = z.Run(code)
  1334  	return
  1335  }