summaryrefslogtreecommitdiff
path: root/src/pkg/exp/template/parse.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/exp/template/parse.go')
-rw-r--r--src/pkg/exp/template/parse.go783
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
-}