Beispiel #1
0
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
}
Beispiel #2
0
// 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
}
Beispiel #3
0
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
}
Beispiel #4
0
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
}
Beispiel #5
0
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
}
Beispiel #6
0
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
}
Beispiel #7
0
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
}
Beispiel #8
0
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
}
Beispiel #9
0
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
}
Beispiel #10
0
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
}
Beispiel #11
0
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
}
Beispiel #12
0
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
}
Beispiel #13
0
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
}
Beispiel #14
0
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
}
Beispiel #15
0
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
}
Beispiel #16
0
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
}
Beispiel #17
0
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
}
Beispiel #18
0
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
}
Beispiel #19
0
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
}
Beispiel #20
0
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
}
Beispiel #21
0
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
}
Beispiel #22
0
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
}
Beispiel #23
0
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
}
Beispiel #24
0
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
}
Beispiel #25
0
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
}
Beispiel #26
0
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
}
Beispiel #27
0
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
}
Beispiel #28
0
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
}
Beispiel #29
0
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
}
Beispiel #30
0
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
}