diff options
Diffstat (limited to 'src/pkg/exp/template/parse.go')
-rw-r--r-- | src/pkg/exp/template/parse.go | 783 |
1 files changed, 0 insertions, 783 deletions
diff --git a/src/pkg/exp/template/parse.go b/src/pkg/exp/template/parse.go deleted file mode 100644 index 8b2d60207..000000000 --- a/src/pkg/exp/template/parse.go +++ /dev/null @@ -1,783 +0,0 @@ -// 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" - "os" - "reflect" - "runtime" - "strconv" - "strings" - "unicode" -) - -// Template is the representation of a parsed template. -type Template struct { - name string - root *listNode - funcs map[string]reflect.Value - // Parsing only; cleared after parse. - set *Set - lex *lexer - token item // token lookahead for parser - havePeek bool -} - -// next returns the next token. -func (t *Template) next() item { - if t.havePeek { - t.havePeek = false - } else { - t.token = t.lex.nextItem() - } - return t.token -} - -// backup backs the input stream up one token. -func (t *Template) backup() { - t.havePeek = true -} - -// peek returns but does not consume the next token. -func (t *Template) peek() item { - if t.havePeek { - return t.token - } - t.token = t.lex.nextItem() - t.havePeek = true - return t.token -} - -// A node is an element in the parse tree. The interface is trivial. -type node interface { - typ() nodeType - String() string -} - -type nodeType int - -func (t nodeType) typ() nodeType { - return t -} - -const ( - nodeText nodeType = iota - nodeAction - nodeCommand - nodeDot - nodeElse - nodeEnd - nodeField - nodeIdentifier - nodeIf - nodeList - nodeNumber - nodeRange - nodeString - nodeTemplate - nodeWith -) - -// Nodes. - -// listNode holds a sequence of nodes. -type listNode struct { - nodeType - nodes []node -} - -func newList() *listNode { - return &listNode{nodeType: nodeList} -} - -func (l *listNode) append(n node) { - l.nodes = append(l.nodes, n) -} - -func (l *listNode) String() string { - b := new(bytes.Buffer) - fmt.Fprint(b, "[") - for _, n := range l.nodes { - fmt.Fprint(b, n) - } - fmt.Fprint(b, "]") - return b.String() -} - -// textNode holds plain text. -type textNode struct { - nodeType - text []byte -} - -func newText(text string) *textNode { - return &textNode{nodeType: nodeText, text: []byte(text)} -} - -func (t *textNode) String() string { - return fmt.Sprintf("(text: %q)", t.text) -} - -// actionNode holds an action (something bounded by delimiters). -type actionNode struct { - nodeType - line int - pipeline []*commandNode -} - -func newAction(line int, pipeline []*commandNode) *actionNode { - return &actionNode{nodeType: nodeAction, line: line, pipeline: pipeline} -} - -func (a *actionNode) append(command *commandNode) { - a.pipeline = append(a.pipeline, command) -} - -func (a *actionNode) String() string { - return fmt.Sprintf("(action: %v)", a.pipeline) -} - -// commandNode holds a command (a pipeline inside an evaluating action). -type commandNode struct { - nodeType - args []node // identifier, string, or number -} - -func newCommand() *commandNode { - return &commandNode{nodeType: nodeCommand} -} - -func (c *commandNode) append(arg node) { - c.args = append(c.args, arg) -} - -func (c *commandNode) String() string { - return fmt.Sprintf("(command: %v)", c.args) -} - -// identifierNode holds an identifier. -type identifierNode struct { - nodeType - ident string -} - -func newIdentifier(ident string) *identifierNode { - return &identifierNode{nodeType: nodeIdentifier, ident: ident} -} - -func (i *identifierNode) String() string { - return fmt.Sprintf("I=%s", i.ident) -} - -// dotNode holds the special identifier '.'. It is represented by a nil pointer. -type dotNode bool - -func newDot() *dotNode { - return nil -} - -func (d *dotNode) typ() nodeType { - return nodeDot -} - -func (d *dotNode) String() string { - return "{{<.>}}" -} - -// fieldNode holds a field (identifier starting with '.'). -// The names may be chained ('.x.y'). -// The period is dropped from each ident. -type fieldNode struct { - nodeType - ident []string -} - -func newField(ident string) *fieldNode { - return &fieldNode{nodeType: nodeField, ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period -} - -func (f *fieldNode) String() string { - return fmt.Sprintf("F=%s", f.ident) -} - -// boolNode holds a boolean constant. -type boolNode struct { - nodeType - true bool -} - -func newBool(true bool) *boolNode { - return &boolNode{nodeType: nodeString, true: true} -} - -func (b *boolNode) String() string { - if b.true { - return fmt.Sprintf("B=true") - } - return fmt.Sprintf("B=false") -} - -// numberNode holds a number, signed or unsigned integer, floating, or complex. -// The value is parsed and stored under all the types that can represent the value. -// This simulates in a small amount of code the behavior of Go's ideal constants. -type numberNode struct { - nodeType - isInt bool // number has an integral value - isUint bool // number has an unsigned integral value - isFloat bool // number has a floating-point value - isComplex bool // number is complex - int64 // the signed integer value - uint64 // the unsigned integer value - float64 // the floating-point value - complex128 // the complex value - text string -} - -func newNumber(text string, isComplex bool) (*numberNode, os.Error) { - n := &numberNode{nodeType: nodeNumber, text: text} - if isComplex { - // fmt.Sscan can parse the pair, so let it do the work. - if _, err := fmt.Sscan(text, &n.complex128); err != nil { - return nil, err - } - n.isComplex = true - n.simplifyComplex() - return n, nil - } - // Imaginary constants can only be complex unless they are zero. - if len(text) > 0 && text[len(text)-1] == 'i' { - f, err := strconv.Atof64(text[:len(text)-1]) - if err == nil { - n.isComplex = true - n.complex128 = complex(0, f) - n.simplifyComplex() - return n, nil - } - } - // Do integer test first so we get 0x123 etc. - u, err := strconv.Btoui64(text, 0) // will fail for -0; fixed below. - if err == nil { - n.isUint = true - n.uint64 = u - } - i, err := strconv.Btoi64(text, 0) - if err == nil { - n.isInt = true - n.int64 = i - if i == 0 { - n.isUint = true // in case of -0. - n.uint64 = u - } - } - // If an integer extraction succeeded, promote the float. - if n.isInt { - n.isFloat = true - n.float64 = float64(n.int64) - } else if n.isUint { - n.isFloat = true - n.float64 = float64(n.uint64) - } else { - f, err := strconv.Atof64(text) - if err == nil { - n.isFloat = true - n.float64 = f - // If a floating-point extraction succeeded, extract the int if needed. - if !n.isInt && float64(int64(f)) == f { - n.isInt = true - n.int64 = int64(f) - } - if !n.isUint && float64(uint64(f)) == f { - n.isUint = true - n.uint64 = uint64(f) - } - } - } - if !n.isInt && !n.isUint && !n.isFloat { - return nil, fmt.Errorf("illegal number syntax: %q", text) - } - return n, nil -} - -// simplifyComplex pulls out any other types that are represented by the complex number. -// These all require that the imaginary part be zero. -func (n *numberNode) simplifyComplex() { - n.isFloat = imag(n.complex128) == 0 - if n.isFloat { - n.float64 = real(n.complex128) - n.isInt = float64(int64(n.float64)) == n.float64 - if n.isInt { - n.int64 = int64(n.float64) - } - n.isUint = float64(uint64(n.float64)) == n.float64 - if n.isUint { - n.uint64 = uint64(n.float64) - } - } -} - -func (n *numberNode) String() string { - return fmt.Sprintf("N=%s", n.text) -} - -// stringNode holds a quoted string. -type stringNode struct { - nodeType - text string -} - -func newString(text string) *stringNode { - return &stringNode{nodeType: nodeString, text: text} -} - -func (s *stringNode) String() string { - return fmt.Sprintf("S=%#q", s.text) -} - -// endNode represents an {{end}} action. It is represented by a nil pointer. -type endNode bool - -func newEnd() *endNode { - return nil -} - -func (e *endNode) typ() nodeType { - return nodeEnd -} - -func (e *endNode) String() string { - return "{{end}}" -} - -// elseNode represents an {{else}} action. -type elseNode struct { - nodeType - line int -} - -func newElse(line int) *elseNode { - return &elseNode{nodeType: nodeElse, line: line} -} - -func (e *elseNode) typ() nodeType { - return nodeElse -} - -func (e *elseNode) String() string { - return "{{else}}" -} -// ifNode represents an {{if}} action and its commands. -// TODO: what should evaluation look like? is a pipeline enough? -type ifNode struct { - nodeType - line int - pipeline []*commandNode - list *listNode - elseList *listNode -} - -func newIf(line int, pipeline []*commandNode, list, elseList *listNode) *ifNode { - return &ifNode{nodeType: nodeIf, line: line, pipeline: pipeline, list: list, elseList: elseList} -} - -func (i *ifNode) String() string { - if i.elseList != nil { - return fmt.Sprintf("({{if %s}} %s {{else}} %s)", i.pipeline, i.list, i.elseList) - } - return fmt.Sprintf("({{if %s}} %s)", i.pipeline, i.list) -} - -// rangeNode represents a {{range}} action and its commands. -type rangeNode struct { - nodeType - line int - pipeline []*commandNode - list *listNode - elseList *listNode -} - -func newRange(line int, pipeline []*commandNode, list, elseList *listNode) *rangeNode { - return &rangeNode{nodeType: nodeRange, line: line, pipeline: pipeline, list: list, elseList: elseList} -} - -func (r *rangeNode) String() string { - if r.elseList != nil { - return fmt.Sprintf("({{range %s}} %s {{else}} %s)", r.pipeline, r.list, r.elseList) - } - return fmt.Sprintf("({{range %s}} %s)", r.pipeline, r.list) -} - -// templateNode represents a {{template}} action. -type templateNode struct { - nodeType - line int - name node - pipeline []*commandNode -} - -func newTemplate(line int, name node, pipeline []*commandNode) *templateNode { - return &templateNode{nodeType: nodeTemplate, line: line, name: name, pipeline: pipeline} -} - -func (t *templateNode) String() string { - return fmt.Sprintf("{{template %s %s}}", t.name, t.pipeline) -} - -// withNode represents a {{with}} action and its commands. -type withNode struct { - nodeType - line int - pipeline []*commandNode - list *listNode - elseList *listNode -} - -func newWith(line int, pipeline []*commandNode, list, elseList *listNode) *withNode { - return &withNode{nodeType: nodeWith, line: line, pipeline: pipeline, list: list, elseList: elseList} -} - -func (w *withNode) String() string { - if w.elseList != nil { - return fmt.Sprintf("({{with %s}} %s {{else}} %s)", w.pipeline, w.list, w.elseList) - } - return fmt.Sprintf("({{with %s}} %s)", w.pipeline, w.list) -} - - -// Parsing. - -// New allocates a new template with the given name. -func New(name string) *Template { - return &Template{ - name: name, - funcs: make(map[string]reflect.Value), - } -} - -// Funcs adds to the template's function map the elements of the -// argument map. It panics if a value in the map is not a function -// with appropriate return type. -// The return value is the template, so calls can be chained. -func (t *Template) Funcs(funcMap FuncMap) *Template { - addFuncs(t.funcs, funcMap) - return t -} - -// errorf formats the error and terminates processing. -func (t *Template) errorf(format string, args ...interface{}) { - t.root = nil - format = fmt.Sprintf("template: %s:%d: %s", t.name, t.lex.lineNumber(), format) - panic(fmt.Errorf(format, args...)) -} - -// error terminates processing. -func (t *Template) error(err os.Error) { - t.errorf("%s", err) -} - -// expect consumes the next token and guarantees it has the required type. -func (t *Template) expect(expected itemType, context string) item { - token := t.next() - if token.typ != expected { - t.errorf("expected %s in %s; got %s", expected, context, token) - } - return token -} - -// unexpected complains about the token and terminates processing. -func (t *Template) unexpected(token item, context string) { - t.errorf("unexpected %s in %s", token, context) -} - -// recover is the handler that turns panics into returns from the top -// level of Parse or Execute. -func (t *Template) recover(errp *os.Error) { - e := recover() - if e != nil { - if _, ok := e.(runtime.Error); ok { - panic(e) - } - t.stopParse() - *errp = e.(os.Error) - } - return -} - -// startParse starts the template parsing from the lexer. -func (t *Template) startParse(set *Set, lex *lexer) { - t.root = nil - t.set = set - t.lex = lex -} - -// stopParse terminates parsing. -func (t *Template) stopParse() { - t.set, t.lex = nil, nil -} - -// atEOF returns true if, possibly after spaces, we're at EOF. -func (t *Template) atEOF() bool { - for { - token := t.peek() - switch token.typ { - case itemEOF: - return true - case itemText: - for _, r := range token.val { - if !unicode.IsSpace(r) { - return false - } - } - t.next() // skip spaces. - continue - } - break - } - return false -} - -// Parse parses the template definition string to construct an internal representation -// of the template for execution. -func (t *Template) Parse(s string) (err os.Error) { - t.startParse(nil, lex(t.name, s)) - defer t.recover(&err) - t.parse(true) - t.stopParse() - return -} - -// ParseInSet parses the template definition string to construct an internal representation -// of the template for execution. Function bindings are checked against those in the set. -func (t *Template) ParseInSet(s string, set *Set) (err os.Error) { - t.startParse(set, lex(t.name, s)) - defer t.recover(&err) - t.parse(true) - t.stopParse() - return -} - -// parse is the helper for Parse. It triggers an error if we expect EOF but don't reach it. -func (t *Template) parse(toEOF bool) (next node) { - t.root, next = t.itemList(true) - if toEOF && next != nil { - t.errorf("unexpected %s", next) - } - return next -} - -// itemList: -// textOrAction* -// Terminates at EOF and at {{end}} or {{else}}, which is returned separately. -// The toEOF flag tells whether we expect to reach EOF. -func (t *Template) itemList(toEOF bool) (list *listNode, next node) { - list = newList() - for t.peek().typ != itemEOF { - n := t.textOrAction() - switch n.typ() { - case nodeEnd, nodeElse: - return list, n - } - list.append(n) - } - if !toEOF { - t.unexpected(t.next(), "input") - } - return list, nil -} - -// textOrAction: -// text | action -func (t *Template) textOrAction() node { - switch token := t.next(); token.typ { - case itemText: - return newText(token.val) - case itemLeftDelim: - return t.action() - default: - t.unexpected(token, "input") - } - return nil -} - -// Action: -// control -// command ("|" command)* -// Left delim is past. Now get actions. -// First word could be a keyword such as range. -func (t *Template) action() (n node) { - switch token := t.next(); token.typ { - case itemElse: - return t.elseControl() - case itemEnd: - return t.endControl() - case itemIf: - return t.ifControl() - case itemRange: - return t.rangeControl() - case itemTemplate: - return t.templateControl() - case itemWith: - return t.withControl() - } - t.backup() - return newAction(t.lex.lineNumber(), t.pipeline("command")) -} - -// Pipeline: -// field or command -// pipeline "|" pipeline -func (t *Template) pipeline(context string) (pipe []*commandNode) { - for { - switch token := t.next(); token.typ { - case itemRightDelim: - if len(pipe) == 0 { - t.errorf("missing value for %s", context) - } - return - case itemBool, itemComplex, itemDot, itemField, itemIdentifier, itemNumber, itemRawString, itemString: - t.backup() - pipe = append(pipe, t.command()) - default: - t.unexpected(token, context) - } - } - return -} - -func (t *Template) parseControl(context string) (lineNum int, pipe []*commandNode, list, elseList *listNode) { - lineNum = t.lex.lineNumber() - pipe = t.pipeline(context) - var next node - list, next = t.itemList(false) - switch next.typ() { - case nodeEnd: //done - case nodeElse: - elseList, next = t.itemList(false) - if next.typ() != nodeEnd { - t.errorf("expected end; found %s", next) - } - elseList = elseList - } - return lineNum, pipe, list, elseList -} - -// If: -// {{if pipeline}} itemList {{end}} -// {{if pipeline}} itemList {{else}} itemList {{end}} -// If keyword is past. -func (t *Template) ifControl() node { - return newIf(t.parseControl("if")) -} - -// Range: -// {{range pipeline}} itemList {{end}} -// {{range pipeline}} itemList {{else}} itemList {{end}} -// Range keyword is past. -func (t *Template) rangeControl() node { - return newRange(t.parseControl("range")) -} - -// With: -// {{with pipeline}} itemList {{end}} -// {{with pipeline}} itemList {{else}} itemList {{end}} -// If keyword is past. -func (t *Template) withControl() node { - return newWith(t.parseControl("with")) -} - - -// End: -// {{end}} -// End keyword is past. -func (t *Template) endControl() node { - t.expect(itemRightDelim, "end") - return newEnd() -} - -// Else: -// {{else}} -// Else keyword is past. -func (t *Template) elseControl() node { - t.expect(itemRightDelim, "else") - return newElse(t.lex.lineNumber()) -} - -// Template: -// {{template stringValue pipeline}} -// Template keyword is past. The name must be something that can evaluate -// to a string. -func (t *Template) templateControl() node { - var name node - switch token := t.next(); token.typ { - case itemIdentifier: - if _, ok := findFunction(token.val, t, t.set); !ok { - t.errorf("function %q not defined", token.val) - } - name = newIdentifier(token.val) - case itemDot: - name = newDot() - case itemField: - name = newField(token.val) - case itemString, itemRawString: - s, err := strconv.Unquote(token.val) - if err != nil { - t.error(err) - } - name = newString(s) - default: - t.unexpected(token, "template invocation") - } - pipeline := t.pipeline("template") - return newTemplate(t.lex.lineNumber(), name, pipeline) -} - -// command: -// space-separated arguments up to a pipeline character or right delimiter. -// we consume the pipe character but leave the right delim to terminate the action. -func (t *Template) command() *commandNode { - cmd := newCommand() -Loop: - for { - switch token := t.next(); token.typ { - case itemRightDelim: - t.backup() - break Loop - case itemPipe: - break Loop - case itemError: - t.errorf("%s", token.val) - case itemIdentifier: - if _, ok := findFunction(token.val, t, t.set); !ok { - t.errorf("function %q not defined", token.val) - } - cmd.append(newIdentifier(token.val)) - case itemDot: - cmd.append(newDot()) - case itemField: - cmd.append(newField(token.val)) - case itemBool: - cmd.append(newBool(token.val == "true")) - case itemComplex, itemNumber: - number, err := newNumber(token.val, token.typ == itemComplex) - if err != nil { - t.error(err) - } - cmd.append(number) - case itemString, itemRawString: - s, err := strconv.Unquote(token.val) - if err != nil { - t.error(err) - } - cmd.append(newString(s)) - default: - t.unexpected(token, "command") - } - } - if len(cmd.args) == 0 { - t.errorf("empty command") - } - return cmd -} |