func xml_Text_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { input := scope.Value.(string) doc, err := xml.Parse([]byte(input), nil, nil, xml.DefaultParseOption, nil) if err != nil { LogEngineError(ctx, "xml err: "+err.Error()) returnValue = "false" return } if doc == nil { LogEngineError(ctx, "xml err: nil doc") returnValue = "false" return } ctx.AddMemoryObject(doc) ctx.CurrentDoc = doc ns := &Scope{Value: doc} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } scope.Value = doc.String() ctx.CurrentDoc = nil returnValue = scope.Value //doc.Free() return }
// Buggy, look at inject_at_v2 for fixed version. // kept for backwards compatibility. func inject_at_Position_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { node := scope.Value.(xml.Node) position := args[0].(Position) input := args[1].(string) nodes, err := node.Coerce(input) if err == nil { for _, n := range nodes { MoveFunc(n, node, position) } } if len(nodes) > 0 { first := nodes[0] if first.NodeType() == xml.XML_ELEMENT_NODE { // successfully ran scope returnValue = "true" ns := &Scope{Value: first} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } } } else { returnValue = "false" } return }
func replace_Regexp(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { regexp := args[0].(*rubex.Regexp) scope.Value = regexp.GsubFunc(scope.Value.(string), func(match string, captures map[string]string) string { usesGlobal := (ctx.GetEnv("use_global_replace_vars") == "true") for name, capture := range captures { if usesGlobal { //println("setting $", name, "to", capture) ctx.SetEnv(name, capture) } ctx.SetVar(name, capture) } replacementScope := &Scope{Value: match} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(replacementScope, child) } //println(ins.String()) //println("Replacement:", replacementScope.Value.(string)) return ctx.GetInnerReplacer().GsubFunc(replacementScope.Value.(string), func(_ string, numeric_captures map[string]string) string { capture := numeric_captures["1"] var val string if usesGlobal { val = ctx.GetEnv(capture) } else { val = ctx.GetVar(capture).(string) } return val }) }) returnValue = scope.Value return }
func html_fragment_doc_Text_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { ctx.HtmlParsed = true inputEncoding := args[0].(string) inputEncodingBytes := []byte(inputEncoding) outputEncoding := args[1].(string) outputEncodingBytes := []byte(outputEncoding) input := scope.Value.(string) fragment, err := html.ParseFragment([]byte(input), inputEncodingBytes, nil, html.DefaultParseOption, outputEncodingBytes) if err != nil { LogEngineError(ctx, "html_fragment err: "+err.Error()) returnValue = "false" return } if fragment == nil { LogEngineError(ctx, "html_fragment err: nil fragment") returnValue = "false" return } ctx.AddMemoryObject(fragment.Node.MyDocument()) ctx.CurrentDoc = fragment ns := &Scope{Value: fragment} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } //output is always utf-8 because the content is internal to Doc. scope.Value = ns.Value.(*xml.DocumentFragment).String() //TODO(NOJ): Why are we setting currentdoc to nil instead of what it used to be? ctx.CurrentDoc = nil returnValue = scope.Value //fragment.Node.MyDocument().Free() return }
func header_comp_v1_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { header := scope.Value.(string) attr := args[0].(string) headersRegex := regexp.MustCompile(`(?m)^(\S+?):\s+(.+?)$`) headerParsed := headersRegex.FindStringSubmatch(header) replaceMe := "" if len(headerParsed) != 3 { return "" } ns := &Scope{} switch attr { case "name": replaceMe = headerParsed[1] case "value": replaceMe = headerParsed[2] if strings.HasSuffix(replaceMe, "\r") { replaceMe = replaceMe[:len(replaceMe)-1] } case "this": replaceMe = header } ns.Value = replaceMe if ns.Value != "" { for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } } // set the value of the container scope to the header replaced with the new ns.Value scope.Value = strings.Replace(header, replaceMe, ns.Value.(string), -1) // return the resultant ns.Value returnValue = ns.Value return }
func parse_headers_v1(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { httpStr := scope.Value.(string) // remove \r (will add them back later) httpStr = strings.Replace(httpStr, "\r", "", -1) headersRegex := regexp.MustCompile(`(?m)^\S+?:\s?[^\n]+`) // replace headers with result of eval'd instructions newHttpStr := headersRegex.ReplaceAllStringFunc(httpStr, func(header string) string { ns := &Scope{Value: header} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } return ns.Value.(string) }) // remove empty lines (in the case that a header is blanked) replaceEmptyLinesRegex := regexp.MustCompile(`\n+`) newHttpStr = replaceEmptyLinesRegex.ReplaceAllString(newHttpStr, "\n") // trim leading/trailing spaces (e.g. if last header is removed) newHttpStr = strings.TrimRight(newHttpStr, "\r\n") // replace all \n with \r\n scope.Value = strings.Replace(newHttpStr, "\n", "\r\n", -1) // return the entire http returnValue = scope.Value return }
func fetch_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { node := scope.Value.(xml.Node) xpathStr := args[0].(string) expr := ctx.GetXpathExpr(xpathStr) if expr == nil { returnValue = "false" return } nodes, err := node.SearchByDeadline(expr, &ctx.Deadline) if err == nil && len(nodes) > 0 { node := nodes[0] returnValue = node.String() } if returnValue == nil { returnValue = "" } if ins.INumChildren() > 0 { ts := &Scope{Value: returnValue} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ts, child) } returnValue = ts.Value } return }
func html_doc_Text_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { ctx.HtmlParsed = true inputEncoding := args[0].(string) inputEncodingBytes := []byte(inputEncoding) outputEncoding := args[1].(string) outputEncodingBytes := []byte(outputEncoding) input := scope.Value.(string) doc, err := html.Parse([]byte(input), inputEncodingBytes, nil, html.DefaultParseOption, outputEncodingBytes) if err != nil { LogEngineError(ctx, "html_doc err: "+err.Error()) returnValue = "false" return } if doc == nil { LogEngineError(ctx, "html_doc err: nil doc") returnValue = "false" return } ctx.AddMemoryObject(doc) ctx.CurrentDoc = doc ns := &Scope{Value: doc} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } if err := doc.SetMetaEncoding(outputEncoding); err != nil { //ctx.Log.Warn("executing html:" + err.Error()) } scope.Value = doc.String() ctx.CurrentDoc = nil returnValue = scope.Value //doc.Free() return }
func replace_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { ts := &Scope{Value: ""} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ts, child) } scope.Value = strings.Replace(scope.Value.(string), args[0].(string), ts.Value.(string), -1) return }
func comp_v1_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { component := args[0].(string) u := scope.Value.(*url.URL) ns := &Scope{} switch component { case "scheme": ns.Value = u.Scheme case "host": ns.Value = u.Host case "path": ns.Value = u.Path case "fragment": ns.Value = u.Fragment case "userinfo": if u.User != nil { ns.Value = u.User.String() } else { ns.Value = "" } } if ns.Value != nil { for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } // write value back to URL (as long as it's a string) if newVal, ok := ns.Value.(string); ok { switch component { case "scheme": u.Scheme = newVal case "host": u.Host = newVal case "path": u.Path = newVal case "fragment": u.Fragment = newVal case "userinfo": if newVal == "" { // remove the userinfo u.User = nil } else { info := strings.Split(newVal, ":") newUserinfo := url.User(info[0]) if len(info) == 2 { newUserinfo = url.UserPassword(info[0], info[1]) } u.User = newUserinfo } } returnValue = newVal } } return }
func name(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { node := scope.Value.(xml.Node) ts := &Scope{Value: node.Name()} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ts, child) } node.SetName(ts.Value.(string)) returnValue = ts.Value.(string) return }
func time_(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { start := time.Now() for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(scope, child) } duration := time.Since(start) // I only seem to get 6 significant digits, so output in microseconds returnValue = strconv.FormatInt(duration.Nanoseconds()/1000, 10) + "µs" return }
func else_(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { returnValue = "false" if ctx.ShouldContinue() { ctx.SetShouldContinue(false) for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(scope, child) } returnValue = "true" } return }
func inner_text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { node := scope.Value.(xml.Node) ts := &Scope{Value: node.Content()} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ts, child) } val := ts.Value.(string) node.SetInnerHtml(val) returnValue = val return }
func export_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { val := make([]string, 2) val[0] = args[0].(string) ts := &Scope{Value: ""} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ts, child) } val[1] = ts.Value.(string) ctx.AddExport(val) return }
func dup(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { node := scope.Value.(xml.Node) newNode := node.Duplicate(1) if newNode.NodeType() == xml.XML_ELEMENT_NODE { MoveFunc(newNode, node, AFTER) } ns := &Scope{Value: newNode} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } return }
func with_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { returnValue = "false" if ctx.ShouldContinue() { if args[0].(string) == ctx.MatchTarget() { for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(scope, child) } ctx.SetShouldContinue(false) returnValue = "true" } } return }
func insert_at_Position_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { node := scope.Value.(xml.Node) position := args[0].(Position) tagName := args[1].(string) element := node.MyDocument().CreateElementNode(tagName) MoveFunc(element, node, position) ns := &Scope{Value: element} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } returnValue = "true" return }
func var_Text_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { ctx.SetEnv(args[0].(string), args[1].(string)) returnValue = args[1].(string) if ins.INumChildren() > 0 { ts := &Scope{Value: returnValue} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ts, child) } returnValue = ts.Value ctx.SetEnv(args[0].(string), returnValue.(string)) } return }
func value(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { node := scope.Value.(xml.Node) ts := &Scope{Value: node.Content()} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ts, child) } val := ts.Value.(string) if attr, ok := node.(*xml.AttributeNode); ok { attr.SetValue(val) } returnValue = val return }
func not_Regexp(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { returnValue = "false" if ctx.ShouldContinue() { //println(matcher.MatchAgainst, matchWith) if !(args[0].(*rubex.Regexp)).Match([]uint8(ctx.MatchTarget())) { ctx.SetShouldContinue(false) for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(scope, child) } returnValue = "true" } } return }
func attribute_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { node := scope.Value.(xml.Node) name := args[0].(string) attr := node.Attribute(name) if attr == nil { node.SetAttr(name, "") attr = node.Attribute(name) } if attr != nil { as := &Scope{Value: attr} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(as, child) } if attr.Value() == "" { attr.Remove() } returnValue = "true" } return }
func param_v1_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { parameter := args[0].(string) u := scope.Value.(*url.URL) params := u.Query() ns := &Scope{Value: params.Get(parameter)} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } // if child instructions result in a string: if newVal, ok := ns.Value.(string); ok { // write the ns.Value back to params params.Set(parameter, newVal) // write params back to u.RawQuery u.RawQuery = params.Encode() returnValue = newVal } return }
func wrap_text_children_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { returnValue = "false" node := scope.Value.(xml.Node) if textNodes, err := node.SearchByDeadline("./text()", &ctx.Deadline); err == nil { tagName := args[0].(string) tag := fmt.Sprintf("<%s />", tagName) for index, textNode := range textNodes { textNode.Wrap(tag) parent := textNode.Parent() if parent == nil { continue } ns := &Scope{parent, index} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } } } return }
func match_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { // Setup stacks against := args[0].(string) ctx.PushMatchStack(against) ctx.PushShouldContinueStack(true) // Run children for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(scope, child) } if ctx.ShouldContinue() { returnValue = "false" } else { returnValue = "true" } // Clear ctx.PopShouldContinueStack() ctx.PopMatchStack() return }
func url_v1_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { urlStr := args[0].(string) urlParsed, err := url.Parse(urlStr) if err != nil { LogEngineError(ctx, "url parse err: "+err.Error()) returnValue = "false" return } ns := &Scope{Value: urlParsed} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } // return the modified URL string (allow strings as well?) if urlVal, isURL := ns.Value.(*url.URL); isURL { returnValue = urlVal.String() } else if urlStr, isStr := ns.Value.(string); isStr { returnValue = urlStr } return }
func json_to_xml_v1(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { // unmarshal the json jsonSrc := scope.Value.(string) var jsonVal interface{} err := json.Unmarshal([]byte(jsonSrc), &jsonVal) if err != nil { // invalid JSON -- log an error message and keep going LogEngineError(ctx, "json_decoding err: "+err.Error()) returnValue = "null" return } // convert to an xml doc and run the supplied block on it newDoc := xml.CreateEmptyDocument(nil, nil) ctx.AddMemoryObject(newDoc) jsonNodes := json_to_node(jsonVal, newDoc) // put the jsonNodes under a new root node to get the xpath searches to be correctly scoped jsonRoot := newDoc.CreateElementNode("json") jsonRoot.AddChild(jsonNodes) newScope := &Scope{Value: jsonRoot} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(newScope, child) } // convert back to native Go data structures and re-marshal jsonVal = node_to_json(jsonRoot.FirstChild()) jsonOut, err := json.MarshalIndent(jsonVal, "", " ") if err != nil { // invalid JSON -- log an error message and keep going LogEngineError(ctx, "json_encoding err: "+err.Error()) returnValue = "null" return } scope.Value = string(jsonOut) returnValue = string(jsonOut) return }
func capture_Regexp(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { regexp := args[0].(*rubex.Regexp) scope.Value = regexp.GsubFunc(scope.Value.(string), func(match string, captures map[string]string) string { usesGlobal := (ctx.GetEnv("use_global_replace_vars") == "true") for name, capture := range captures { if usesGlobal { ctx.SetEnv(name, capture) } ctx.SetVar(name, capture) } replacementScope := &Scope{Value: match} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(replacementScope, child) } return replacementScope.Value.(string) }) returnValue = scope.Value return }
func base64_v1_Text_Text(ctx *EngineContext, scope *Scope, ins protoface.Instruction, args []interface{}) (returnValue interface{}) { method := args[0].(string) str := args[1].(string) ns := &Scope{Value: str} switch method { case "decode": data, err := base64.StdEncoding.DecodeString(str) if err != nil { LogEngineError(ctx, "String decode error: "+err.Error()) ns.Value = "" } else { ns.Value = string(data) } case "encode": data := []byte(str) ns.Value = base64.StdEncoding.EncodeToString(data) } for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ns, child) } returnValue = ns.Value return }
func (ctx *EngineContext) RunInstruction(scope *Scope, ins protoface.Instruction) (returnValue interface{}) { thisFile := ctx.Filename defer func() { //TODO Stack traces seem to get truncated on syslog... if x := recover(); x != nil { err, ok := x.(error) errString := "" if ok { errString = err.Error() } else { errString = x.(string) } if errString != TimeoutError { if ctx.HadError == false { ctx.HadError = true errString = errString + "\n" + constants.Instruction_InstructionType_name[ins.IGetType()] + " " + ins.IGetValue() + "\n\n\nTritium Stack\n=========\n\n" } // errString = errString + ctx.FileAndLine(ins) + "\n" if len(thisFile) > 0 && thisFile != "__rewriter__" { switch ins.IGetType() { case constants.Instruction_IMPORT: errString = errString + fmt.Sprintf("%s:%d", thisFile, ins.IGetLineNumber()) errString = errString + fmt.Sprintf(":\t@import %s\n", ctx.Transform.IGetNthObject(int(ins.IGetObjectId())).IGetName()) case constants.Instruction_FUNCTION_CALL: errString = errString + fmt.Sprintf("%s:%d", thisFile, ins.IGetLineNumber()) if callee := ins.IGetValue(); len(callee) > 0 { errString = errString + fmt.Sprintf(":\t%s\n", callee) } default: // do nothing } } } panic(errString) } }() ctx.Whale.Debugger.TrapInstruction(ctx.MessagePath, ctx.Filename, ctx.Env, ins, scope.Value, scope.Index, ctx.CurrentDoc) if time.Now().After(ctx.Deadline) && !ctx.InDebug { panic(TimeoutError) } indent := "" for i := 0; i < len(ctx.Yields); i++ { indent += "\t" } returnValue = "" switch ins.IGetType() { case constants.Instruction_BLOCK: for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) returnValue = ctx.RunInstruction(scope, child) } case constants.Instruction_TEXT: returnValue = ins.IGetValue() case constants.Instruction_LOCAL_VAR: name := ins.IGetValue() vars := ctx.Vars() if ins.INumArgs() > 0 { vars[name] = ctx.RunInstruction(scope, ins.IGetNthArgument(0)) } if ins.INumChildren() > 0 { ts := &Scope{Value: ctx.Vars()[name]} for i := 0; i < ins.INumChildren(); i++ { child := ins.IGetNthChild(i) ctx.RunInstruction(ts, child) } vars[name] = ts.Value } returnValue = vars[name] case constants.Instruction_IMPORT: obj := ctx.IGetNthObject(int(ins.IGetObjectId())) curFile := ctx.Filename ctx.Filename = obj.IGetName() ctx.Whale.Debugger.LogImport(ctx.MessagePath, ctx.Filename, curFile, int(ins.IGetLineNumber())) root := obj.IGetRoot() // TODO(layer-track): Currently we keep track of which layer we're in, but we // don't expose that to the user. If performance becomes an issue, we might // want to consider removing this bit. It would encompass removing the layer // meta-data from the protobuf object as well. The feature was introduce // for the case where maybe we'd want styles/assets to be included at a per // layer basis, but it was then decided that we don't want to do that anymore, // so this feature is left usecase-less. pushedLayer := false if layer := obj.IGetLayer(); layer != "" && layer != ctx.CurrentLayer() { ctx.PushLayer(layer) pushedLayer = true } for i := 0; i < root.INumChildren(); i++ { child := root.IGetNthChild(i) ctx.RunInstruction(scope, child) } //TODO(layer-track) -- see above if pushedLayer { ctx.PopLayer() } ctx.Whale.Debugger.LogImportDone(ctx.MessagePath, ctx.Filename, curFile, int(ins.IGetLineNumber())) ctx.Filename = curFile case constants.Instruction_FUNCTION_CALL: fun := ctx.Functions[int(ins.IGetFunctionId())] args := make([]interface{}, ins.INumArgs()) for i := 0; i < len(args); i++ { argIns := ins.IGetNthArgument(i) args[i] = ctx.RunInstruction(scope, argIns) } if fun.IGetBuiltIn() { curFile := ctx.Filename if f := builtInFunctions[fun.Name]; f != nil { returnValue = f(ctx, scope, ins, args) if returnValue == nil { returnValue = "" } } else { panic("missing function: " + fun.IGetName()) } ctx.Filename = curFile } else { // We are using a user-defined function //println("Resetting localvar") // Setup the new local var vars := make(map[string]interface{}, len(args)) for i := 0; i < fun.INumArgs(); i++ { arg := fun.IGetNthArg(i) vars[arg.IGetName()] = args[i] } yieldBlock := &YieldBlock{ Ins: ins, Vars: vars, Filename: ctx.Filename, } // PUSH! ctx.PushYieldBlock(yieldBlock) curFile := ctx.Filename ctx.Filename = fun.IGetFilename() // if it's a user-called function, save the curfile:linenumber // Are we going to need a stack here? --A.L. if !ctx.Debugger.IsProd() && ins.IGetIsUserCalled() == true { ctx.Env[isUserCalledEnvKey] = fmt.Sprintf("%s:%d", curFile, ins.IGetLineNumber()) } for i := 0; i < fun.IGetInstruction().INumChildren(); i++ { child := fun.IGetInstruction().IGetNthChild(i) returnValue = ctx.RunInstruction(scope, child) } if ins.IGetIsUserCalled() == true { delete(ctx.Env, isUserCalledEnvKey) } ctx.Filename = curFile // POP! ctx.PopYieldBlock() } } return }