Example #1
0
// LineNumber computes the line number in the input source for the given node
// within the given template.
func (r *Registry) LineNumber(templateName string, node ast.Node) int {
	var src, ok = r.sourceByTemplateName[templateName]
	if !ok {
		log.Println("template not found:", templateName)
		return 0
	}
	return 1 + strings.Count(src[:node.Position()], "\n")
}
Example #2
0
File: parse.go Project: leobcn/soy
// parseTernary parses the ternary operator within an expression.
// itemTernIf has already been read, and the condition is provided.
func (t *tree) parseTernary(cond ast.Node) ast.Node {
	n1 := t.parseExpr(0)
	t.expect(itemColon, "ternary")
	n2 := t.parseExpr(0)
	result := &ast.TernNode{cond.Position(), cond, n1, n2}
	if t.peek().typ == itemColon {
		t.next()
		return t.parseTernary(result)
	}
	return result
}
Example #3
0
File: exec.go Project: leobcn/soy
// walk recursively goes through each node and translates the nodes to
// javascript, writing the result to s.wr
func (s *state) walk(node ast.Node) {
	s.at(node)
	switch node := node.(type) {
	case *ast.SoyFileNode:
		s.visitSoyFile(node)
	case *ast.NamespaceNode:
		s.visitNamespace(node)
	case *ast.SoyDocNode:
		return
	case *ast.TemplateNode:
		s.visitTemplate(node)
	case *ast.ListNode:
		s.visitChildren(node)

		// Output nodes ----------
	case *ast.RawTextNode:
		s.writeRawText(node.Text)
	case *ast.PrintNode:
		s.visitPrint(node)
	case *ast.MsgNode:
		s.walk(node.Body)
	case *ast.CssNode:
		if node.Expr != nil {
			s.jsln(s.bufferName, " += ", node.Expr, " + '-';")
		}
		s.writeRawText([]byte(node.Suffix))
	case *ast.DebuggerNode:
		s.jsln("debugger;")
	case *ast.LogNode:
		s.bufferName += "_"
		s.jsln("var ", s.bufferName, " = '';")
		s.walk(node.Body)
		s.jsln("console.log(", s.bufferName, ");")
		s.bufferName = s.bufferName[:len(s.bufferName)-1]

	// Control flow ----------
	case *ast.IfNode:
		s.visitIf(node)
	case *ast.ForNode:
		s.visitFor(node)
	case *ast.SwitchNode:
		s.visitSwitch(node)
	case *ast.CallNode:
		s.visitCall(node)
	case *ast.LetValueNode:
		s.jsln("var ", s.scope.makevar(node.Name), " = ", node.Expr, ";")
	case *ast.LetContentNode:
		var oldBufferName = s.bufferName
		s.bufferName = s.scope.makevar(node.Name)
		s.jsln("var ", s.bufferName, " = '';")
		s.walk(node.Body)
		s.bufferName = oldBufferName

	// Values ----------
	case *ast.NullNode:
		s.js("null")
	case *ast.StringNode:
		s.js("'")
		template.JSEscape(s.wr, []byte(node.Value))
		s.js("'")
	case *ast.IntNode:
		s.js(node.String())
	case *ast.FloatNode:
		s.js(node.String())
	case *ast.BoolNode:
		s.js(node.String())
	case *ast.GlobalNode:
		s.visitGlobal(node)
	case *ast.ListLiteralNode:
		s.js("[")
		for i, item := range node.Items {
			if i != 0 {
				s.js(",")
			}
			s.walk(item)
		}
		s.js("]")
	case *ast.MapLiteralNode:
		s.js("{")
		var first = true
		for k, v := range node.Items {
			if !first {
				s.js(",")
			}
			first = false
			s.js(k, ":")
			s.walk(v)
		}
		s.js("}")
	case *ast.FunctionNode:
		s.visitFunction(node)
	case *ast.DataRefNode:
		s.visitDataRef(node)

	// Arithmetic operators ----------
	case *ast.NegateNode:
		s.js("(-", node.Arg, ")")
	case *ast.AddNode:
		s.op("+", node)
	case *ast.SubNode:
		s.op("-", node)
	case *ast.DivNode:
		s.op("/", node)
	case *ast.MulNode:
		s.op("*", node)
	case *ast.ModNode:
		s.op("%", node)

		// Arithmetic comparisons ----------
	case *ast.EqNode:
		s.op("==", node)
	case *ast.NotEqNode:
		s.op("!=", node)
	case *ast.LtNode:
		s.op("<", node)
	case *ast.LteNode:
		s.op("<=", node)
	case *ast.GtNode:
		s.op(">", node)
	case *ast.GteNode:
		s.op(">=", node)

	// Boolean operators ----------
	case *ast.NotNode:
		s.js("!(", node.Arg, ")")
	case *ast.AndNode:
		s.op("&&", node)
	case *ast.OrNode:
		s.op("||", node)
	case *ast.ElvisNode:
		// ?: is specified to check for null.
		s.js("(", node.Arg1, " != null ? ", node.Arg1, " : ", node.Arg2, ")")
	case *ast.TernNode:
		s.js("(", node.Arg1, "?", node.Arg2, ":", node.Arg3, ")")

	default:
		s.errorf("unknown node (%T): %v", node, node)
	}
}
Example #4
0
File: exec.go Project: leobcn/soy
// walk recursively goes through each node and executes the indicated logic and
// writes the output
func (s *state) walk(node ast.Node) {
	s.val = data.Undefined{}
	s.at(node)
	switch node := node.(type) {
	case *ast.SoyFileNode:
		for _, node := range node.Body {
			s.walk(node)
		}
	case *ast.TemplateNode:
		if node.Autoescape != ast.AutoescapeUnspecified {
			s.autoescape = node.Autoescape
		}
		s.walk(node.Body)
	case *ast.ListNode:
		for _, node := range node.Nodes {
			s.walk(node)
		}

		// Output nodes ----------
	case *ast.PrintNode:
		s.evalPrint(node)
	case *ast.RawTextNode:
		if _, err := s.wr.Write(node.Text); err != nil {
			s.errorf("%s", err)
		}
	case *ast.MsgNode:
		s.walk(node.Body)
	case *ast.CssNode:
		var prefix = ""
		if node.Expr != nil {
			prefix = s.eval(node.Expr).String() + "-"
		}
		if _, err := io.WriteString(s.wr, prefix+node.Suffix); err != nil {
			s.errorf("%s", err)
		}
	case *ast.DebuggerNode:
		// nothing to do
	case *ast.LogNode:
		Logger.Print(string(s.renderBlock(node.Body)))

		// Control flow ----------
	case *ast.IfNode:
		for _, cond := range node.Conds {
			if cond.Cond == nil || s.eval(cond.Cond).Truthy() {
				s.walk(cond.Body)
				break
			}
		}
	case *ast.ForNode:
		var list, ok = s.eval(node.List).(data.List)
		if !ok {
			s.errorf("In for loop %q, %q does not resolve to a list.",
				node.String(), node.List.String())
		}
		if len(list) == 0 {
			if node.IfEmpty != nil {
				s.walk(node.IfEmpty)
			}
			break
		}
		s.context.push()
		for i, item := range list {
			s.context.set(node.Var, item)
			s.context.set(node.Var+"__index", data.Int(i))
			s.context.set(node.Var+"__lastIndex", data.Int(len(list)-1))
			s.walk(node.Body)
		}
		s.context.pop()
	case *ast.SwitchNode:
		var switchValue = s.eval(node.Value)
		for _, caseNode := range node.Cases {
			for _, caseValueNode := range caseNode.Values {
				if switchValue.Equals(s.eval(caseValueNode)) {
					s.walk(caseNode.Body)
					return
				}
			}
			if len(caseNode.Values) == 0 { // default/last case
				s.walk(caseNode.Body)
				return
			}
		}
	case *ast.CallNode:
		s.evalCall(node)
	case *ast.LetValueNode:
		s.context.set(node.Name, s.eval(node.Expr))
	case *ast.LetContentNode:
		s.context.set(node.Name, data.String(s.renderBlock(node.Body)))

		// Values ----------
	case *ast.NullNode:
		s.val = data.Null{}
	case *ast.StringNode:
		s.val = data.String(node.Value)
	case *ast.IntNode:
		s.val = data.Int(node.Value)
	case *ast.FloatNode:
		s.val = data.Float(node.Value)
	case *ast.BoolNode:
		s.val = data.Bool(node.True)
	case *ast.GlobalNode:
		s.val = node.Value
	case *ast.ListLiteralNode:
		var items = make(data.List, len(node.Items))
		for i, item := range node.Items {
			items[i] = s.eval(item)
		}
		s.val = data.List(items)
	case *ast.MapLiteralNode:
		var items = make(data.Map, len(node.Items))
		for k, v := range node.Items {
			items[k] = s.eval(v)
		}
		s.val = data.Map(items)
	case *ast.FunctionNode:
		s.val = s.evalFunc(node)
	case *ast.DataRefNode:
		s.val = s.evalDataRef(node)

		// Arithmetic operators ----------
	case *ast.NegateNode:
		switch arg := s.evaldef(node.Arg).(type) {
		case data.Int:
			s.val = data.Int(-arg)
		case data.Float:
			s.val = data.Float(-arg)
		default:
			s.errorf("can not negate non-number: %q", arg.String())
		}
	case *ast.AddNode:
		var arg1, arg2 = s.eval2def(node.Arg1, node.Arg2)
		switch {
		case isInt(arg1) && isInt(arg2):
			s.val = data.Int(arg1.(data.Int) + arg2.(data.Int))
		case isString(arg1) || isString(arg2):
			s.val = data.String(arg1.String() + arg2.String())
		default:
			s.val = data.Float(toFloat(arg1) + toFloat(arg2))
		}
	case *ast.SubNode:
		var arg1, arg2 = s.eval2def(node.Arg1, node.Arg2)
		switch {
		case isInt(arg1) && isInt(arg2):
			s.val = data.Int(arg1.(data.Int) - arg2.(data.Int))
		default:
			s.val = data.Float(toFloat(arg1) - toFloat(arg2))
		}
	case *ast.DivNode:
		var arg1, arg2 = s.eval2def(node.Arg1, node.Arg2)
		s.val = data.Float(toFloat(arg1) / toFloat(arg2))
	case *ast.MulNode:
		var arg1, arg2 = s.eval2def(node.Arg1, node.Arg2)
		switch {
		case isInt(arg1) && isInt(arg2):
			s.val = data.Int(arg1.(data.Int) * arg2.(data.Int))
		default:
			s.val = data.Float(toFloat(arg1) * toFloat(arg2))
		}
	case *ast.ModNode:
		var arg1, arg2 = s.eval2def(node.Arg1, node.Arg2)
		s.val = data.Int(arg1.(data.Int) % arg2.(data.Int))

		// Arithmetic comparisons ----------
	case *ast.EqNode:
		s.val = data.Bool(s.eval(node.Arg1).Equals(s.eval(node.Arg2)))
	case *ast.NotEqNode:
		s.val = data.Bool(!s.eval(node.Arg1).Equals(s.eval(node.Arg2)))
	case *ast.LtNode:
		s.val = data.Bool(toFloat(s.evaldef(node.Arg1)) < toFloat(s.evaldef(node.Arg2)))
	case *ast.LteNode:
		s.val = data.Bool(toFloat(s.evaldef(node.Arg1)) <= toFloat(s.evaldef(node.Arg2)))
	case *ast.GtNode:
		s.val = data.Bool(toFloat(s.evaldef(node.Arg1)) > toFloat(s.evaldef(node.Arg2)))
	case *ast.GteNode:
		s.val = data.Bool(toFloat(s.evaldef(node.Arg1)) >= toFloat(s.evaldef(node.Arg2)))

		// Boolean operators ----------
	case *ast.NotNode:
		s.val = data.Bool(!s.eval(node.Arg).Truthy())
	case *ast.AndNode:
		s.val = data.Bool(s.eval(node.Arg1).Truthy() && s.eval(node.Arg2).Truthy())
	case *ast.OrNode:
		s.val = data.Bool(s.eval(node.Arg1).Truthy() || s.eval(node.Arg2).Truthy())
	case *ast.ElvisNode:
		var arg1 = s.eval(node.Arg1)
		if arg1 != (data.Null{}) && arg1 != (data.Undefined{}) {
			s.val = arg1
		} else {
			s.val = s.eval(node.Arg2)
		}
	case *ast.TernNode:
		var arg1 = s.eval(node.Arg1)
		if arg1.Truthy() {
			s.val = s.eval(node.Arg2)
		} else {
			s.val = s.eval(node.Arg3)
		}

	default:
		s.errorf("unknown node: %T", node)
	}
}