OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [text/] [template/] [funcs.go] - Rev 747

Compare with Previous | Blame | View Log

// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package template

import (
        "bytes"
        "fmt"
        "io"
        "net/url"
        "reflect"
        "strings"
        "unicode"
        "unicode/utf8"
)

// FuncMap is the type of the map defining the mapping from names to functions.
// Each function must have either a single return value, or two return values of
// which the second has type error. In that case, if the second (error)
// argument evaluates to non-nil during execution, execution terminates and
// Execute returns that error.
type FuncMap map[string]interface{}

var builtins = FuncMap{
        "and":      and,
        "html":     HTMLEscaper,
        "index":    index,
        "js":       JSEscaper,
        "len":      length,
        "not":      not,
        "or":       or,
        "print":    fmt.Sprint,
        "printf":   fmt.Sprintf,
        "println":  fmt.Sprintln,
        "urlquery": URLQueryEscaper,
}

var builtinFuncs = createValueFuncs(builtins)

// createValueFuncs turns a FuncMap into a map[string]reflect.Value
func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
        m := make(map[string]reflect.Value)
        addValueFuncs(m, funcMap)
        return m
}

// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
        for name, fn := range in {
                v := reflect.ValueOf(fn)
                if v.Kind() != reflect.Func {
                        panic("value for " + name + " not a function")
                }
                if !goodFunc(v.Type()) {
                        panic(fmt.Errorf("can't handle multiple results from method/function %q", name))
                }
                out[name] = v
        }
}

// addFuncs adds to values the functions in funcs. It does no checking of the input -
// call addValueFuncs first.
func addFuncs(out, in FuncMap) {
        for name, fn := range in {
                out[name] = fn
        }
}

// goodFunc checks that the function or method has the right result signature.
func goodFunc(typ reflect.Type) bool {
        // We allow functions with 1 result or 2 results where the second is an error.
        switch {
        case typ.NumOut() == 1:
                return true
        case typ.NumOut() == 2 && typ.Out(1) == errorType:
                return true
        }
        return false
}

// findFunction looks for a function in the template, and global map.
func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
        if tmpl != nil && tmpl.common != nil {
                if fn := tmpl.execFuncs[name]; fn.IsValid() {
                        return fn, true
                }
        }
        if fn := builtinFuncs[name]; fn.IsValid() {
                return fn, true
        }
        return reflect.Value{}, false
}

// Indexing.

// index returns the result of indexing its first argument by the following
// arguments.  Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
// indexed item must be a map, slice, or array.
func index(item interface{}, indices ...interface{}) (interface{}, error) {
        v := reflect.ValueOf(item)
        for _, i := range indices {
                index := reflect.ValueOf(i)
                var isNil bool
                if v, isNil = indirect(v); isNil {
                        return nil, fmt.Errorf("index of nil pointer")
                }
                switch v.Kind() {
                case reflect.Array, reflect.Slice:
                        var x int64
                        switch index.Kind() {
                        case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
                                x = index.Int()
                        case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
                                x = int64(index.Uint())
                        default:
                                return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
                        }
                        if x < 0 || x >= int64(v.Len()) {
                                return nil, fmt.Errorf("index out of range: %d", x)
                        }
                        v = v.Index(int(x))
                case reflect.Map:
                        if !index.Type().AssignableTo(v.Type().Key()) {
                                return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
                        }
                        if x := v.MapIndex(index); x.IsValid() {
                                v = x
                        } else {
                                v = reflect.Zero(v.Type().Key())
                        }
                default:
                        return nil, fmt.Errorf("can't index item of type %s", index.Type())
                }
        }
        return v.Interface(), nil
}

// Length

// length returns the length of the item, with an error if it has no defined length.
func length(item interface{}) (int, error) {
        v, isNil := indirect(reflect.ValueOf(item))
        if isNil {
                return 0, fmt.Errorf("len of nil pointer")
        }
        switch v.Kind() {
        case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
                return v.Len(), nil
        }
        return 0, fmt.Errorf("len of type %s", v.Type())
}

// Boolean logic.

func truth(a interface{}) bool {
        t, _ := isTrue(reflect.ValueOf(a))
        return t
}

// and computes the Boolean AND of its arguments, returning
// the first false argument it encounters, or the last argument.
func and(arg0 interface{}, args ...interface{}) interface{} {
        if !truth(arg0) {
                return arg0
        }
        for i := range args {
                arg0 = args[i]
                if !truth(arg0) {
                        break
                }
        }
        return arg0
}

// or computes the Boolean OR of its arguments, returning
// the first true argument it encounters, or the last argument.
func or(arg0 interface{}, args ...interface{}) interface{} {
        if truth(arg0) {
                return arg0
        }
        for i := range args {
                arg0 = args[i]
                if truth(arg0) {
                        break
                }
        }
        return arg0
}

// not returns the Boolean negation of its argument.
func not(arg interface{}) (truth bool) {
        truth, _ = isTrue(reflect.ValueOf(arg))
        return !truth
}

// HTML escaping.

var (
        htmlQuot = []byte("&#34;") // shorter than "&quot;"
        htmlApos = []byte("&#39;") // shorter than "&apos;"
        htmlAmp  = []byte("&amp;")
        htmlLt   = []byte("&lt;")
        htmlGt   = []byte("&gt;")
)

// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
func HTMLEscape(w io.Writer, b []byte) {
        last := 0
        for i, c := range b {
                var html []byte
                switch c {
                case '"':
                        html = htmlQuot
                case '\'':
                        html = htmlApos
                case '&':
                        html = htmlAmp
                case '<':
                        html = htmlLt
                case '>':
                        html = htmlGt
                default:
                        continue
                }
                w.Write(b[last:i])
                w.Write(html)
                last = i + 1
        }
        w.Write(b[last:])
}

// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
func HTMLEscapeString(s string) string {
        // Avoid allocation if we can.
        if strings.IndexAny(s, `'"&<>`) < 0 {
                return s
        }
        var b bytes.Buffer
        HTMLEscape(&b, []byte(s))
        return b.String()
}

// HTMLEscaper returns the escaped HTML equivalent of the textual
// representation of its arguments.
func HTMLEscaper(args ...interface{}) string {
        ok := false
        var s string
        if len(args) == 1 {
                s, ok = args[0].(string)
        }
        if !ok {
                s = fmt.Sprint(args...)
        }
        return HTMLEscapeString(s)
}

// JavaScript escaping.

var (
        jsLowUni = []byte(`\u00`)
        hex      = []byte("0123456789ABCDEF")

        jsBackslash = []byte(`\\`)
        jsApos      = []byte(`\'`)
        jsQuot      = []byte(`\"`)
        jsLt        = []byte(`\x3C`)
        jsGt        = []byte(`\x3E`)
)

// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
func JSEscape(w io.Writer, b []byte) {
        last := 0
        for i := 0; i < len(b); i++ {
                c := b[i]

                if !jsIsSpecial(rune(c)) {
                        // fast path: nothing to do
                        continue
                }
                w.Write(b[last:i])

                if c < utf8.RuneSelf {
                        // Quotes, slashes and angle brackets get quoted.
                        // Control characters get written as \u00XX.
                        switch c {
                        case '\\':
                                w.Write(jsBackslash)
                        case '\'':
                                w.Write(jsApos)
                        case '"':
                                w.Write(jsQuot)
                        case '<':
                                w.Write(jsLt)
                        case '>':
                                w.Write(jsGt)
                        default:
                                w.Write(jsLowUni)
                                t, b := c>>4, c&0x0f
                                w.Write(hex[t : t+1])
                                w.Write(hex[b : b+1])
                        }
                } else {
                        // Unicode rune.
                        r, size := utf8.DecodeRune(b[i:])
                        if unicode.IsPrint(r) {
                                w.Write(b[i : i+size])
                        } else {
                                fmt.Fprintf(w, "\\u%04X", r)
                        }
                        i += size - 1
                }
                last = i + 1
        }
        w.Write(b[last:])
}

// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
func JSEscapeString(s string) string {
        // Avoid allocation if we can.
        if strings.IndexFunc(s, jsIsSpecial) < 0 {
                return s
        }
        var b bytes.Buffer
        JSEscape(&b, []byte(s))
        return b.String()
}

func jsIsSpecial(r rune) bool {
        switch r {
        case '\\', '\'', '"', '<', '>':
                return true
        }
        return r < ' ' || utf8.RuneSelf <= r
}

// JSEscaper returns the escaped JavaScript equivalent of the textual
// representation of its arguments.
func JSEscaper(args ...interface{}) string {
        ok := false
        var s string
        if len(args) == 1 {
                s, ok = args[0].(string)
        }
        if !ok {
                s = fmt.Sprint(args...)
        }
        return JSEscapeString(s)
}

// URLQueryEscaper returns the escaped value of the textual representation of
// its arguments in a form suitable for embedding in a URL query.
func URLQueryEscaper(args ...interface{}) string {
        s, ok := "", false
        if len(args) == 1 {
                s, ok = args[0].(string)
        }
        if !ok {
                s = fmt.Sprint(args...)
        }
        return url.QueryEscape(s)
}

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.