URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [text/] [template/] [parse/] [parse.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 parse builds parse trees for templates. The grammar is defined// in the documents for the template package.package parseimport ("bytes""fmt""runtime""strconv""unicode")// Tree is the representation of a single parsed template.type Tree struct {Name string // name of the template represented by the tree.Root *ListNode // top-level root of the tree.// Parsing only; cleared after parse.funcs []map[string]interface{}lex *lexertoken [2]item // two-token lookahead for parser.peekCount intvars []string // variables defined at the moment.}// Parse returns a map from template name to parse.Tree, created by parsing the// templates described in the argument string. The top-level template will be// given the specified name. If an error is encountered, parsing stops and an// empty map is returned with the error.func Parse(name, text, leftDelim, rightDelim string, funcs ...map[string]interface{}) (treeSet map[string]*Tree, err error) {treeSet = make(map[string]*Tree)_, err = New(name).Parse(text, leftDelim, rightDelim, treeSet, funcs...)return}// next returns the next token.func (t *Tree) next() item {if t.peekCount > 0 {t.peekCount--} else {t.token[0] = t.lex.nextItem()}return t.token[t.peekCount]}// backup backs the input stream up one token.func (t *Tree) backup() {t.peekCount++}// backup2 backs the input stream up two tokensfunc (t *Tree) backup2(t1 item) {t.token[1] = t1t.peekCount = 2}// peek returns but does not consume the next token.func (t *Tree) peek() item {if t.peekCount > 0 {return t.token[t.peekCount-1]}t.peekCount = 1t.token[0] = t.lex.nextItem()return t.token[0]}// Parsing.// New allocates a new parse tree with the given name.func New(name string, funcs ...map[string]interface{}) *Tree {return &Tree{Name: name,funcs: funcs,}}// errorf formats the error and terminates processing.func (t *Tree) errorf(format string, args ...interface{}) {t.Root = nilformat = fmt.Sprintf("template: %s:%d: %s", t.Name, t.lex.lineNumber(), format)panic(fmt.Errorf(format, args...))}// error terminates processing.func (t *Tree) error(err error) {t.errorf("%s", err)}// expect consumes the next token and guarantees it has the required type.func (t *Tree) 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}// expectEither consumes the next token and guarantees it has one of the required types.func (t *Tree) expectOneOf(expected1, expected2 itemType, context string) item {token := t.next()if token.typ != expected1 && token.typ != expected2 {t.errorf("expected %s or %s in %s; got %s", expected1, expected2, context, token)}return token}// unexpected complains about the token and terminates processing.func (t *Tree) 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.func (t *Tree) recover(errp *error) {e := recover()if e != nil {if _, ok := e.(runtime.Error); ok {panic(e)}if t != nil {t.stopParse()}*errp = e.(error)}return}// startParse initializes the parser, using the lexer.func (t *Tree) startParse(funcs []map[string]interface{}, lex *lexer) {t.Root = nilt.lex = lext.vars = []string{"$"}t.funcs = funcs}// stopParse terminates parsing.func (t *Tree) stopParse() {t.lex = nilt.vars = nilt.funcs = nil}// atEOF returns true if, possibly after spaces, we're at EOF.func (t *Tree) atEOF() bool {for {token := t.peek()switch token.typ {case itemEOF:return truecase 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 a representation of// the template for execution. If either action delimiter string is empty, the// default ("{{" or "}}") is used. Embedded template definitions are added to// the treeSet map.func (t *Tree) Parse(s, leftDelim, rightDelim string, treeSet map[string]*Tree, funcs ...map[string]interface{}) (tree *Tree, err error) {defer t.recover(&err)t.startParse(funcs, lex(t.Name, s, leftDelim, rightDelim))t.parse(treeSet)t.add(treeSet)t.stopParse()return t, nil}// add adds tree to the treeSet.func (t *Tree) add(treeSet map[string]*Tree) {tree := treeSet[t.Name]if tree == nil || IsEmptyTree(tree.Root) {treeSet[t.Name] = treturn}if !IsEmptyTree(t.Root) {t.errorf("template: multiple definition of template %q", t.Name)}}// IsEmptyTree reports whether this tree (node) is empty of everything but space.func IsEmptyTree(n Node) bool {switch n := n.(type) {case *ActionNode:case *IfNode:case *ListNode:for _, node := range n.Nodes {if !IsEmptyTree(node) {return false}}return truecase *RangeNode:case *TemplateNode:case *TextNode:return len(bytes.TrimSpace(n.Text)) == 0case *WithNode:default:panic("unknown node: " + n.String())}return false}// parse is the top-level parser for a template, essentially the same// as itemList except it also parses {{define}} actions.// It runs to EOF.func (t *Tree) parse(treeSet map[string]*Tree) (next Node) {t.Root = newList()for t.peek().typ != itemEOF {if t.peek().typ == itemLeftDelim {delim := t.next()if t.next().typ == itemDefine {newT := New("definition") // name will be updated once we know it.newT.startParse(t.funcs, t.lex)newT.parseDefinition(treeSet)continue}t.backup2(delim)}n := t.textOrAction()if n.Type() == nodeEnd {t.errorf("unexpected %s", n)}t.Root.append(n)}return nil}// parseDefinition parses a {{define}} ... {{end}} template definition and// installs the definition in the treeSet map. The "define" keyword has already// been scanned.func (t *Tree) parseDefinition(treeSet map[string]*Tree) {const context = "define clause"name := t.expectOneOf(itemString, itemRawString, context)var err errort.Name, err = strconv.Unquote(name.val)if err != nil {t.error(err)}t.expect(itemRightDelim, context)var end Nodet.Root, end = t.itemList()if end.Type() != nodeEnd {t.errorf("unexpected %s in %s", end, context)}t.stopParse()t.add(treeSet)}// itemList:// textOrAction*// Terminates at {{end}} or {{else}}, returned separately.func (t *Tree) itemList() (list *ListNode, next Node) {list = newList()for t.peek().typ != itemEOF {n := t.textOrAction()switch n.Type() {case nodeEnd, nodeElse:return list, n}list.append(n)}t.errorf("unexpected EOF")return}// textOrAction:// text | actionfunc (t *Tree) 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 *Tree) 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()// Do not pop variables; they persist until "end".return newAction(t.lex.lineNumber(), t.pipeline("command"))}// Pipeline:// field or command// pipeline "|" pipelinefunc (t *Tree) pipeline(context string) (pipe *PipeNode) {var decl []*VariableNode// Are there declarations?for {if v := t.peek(); v.typ == itemVariable {t.next()if next := t.peek(); next.typ == itemColonEquals || next.typ == itemChar {t.next()variable := newVariable(v.val)if len(variable.Ident) != 1 {t.errorf("illegal variable in declaration: %s", v.val)}decl = append(decl, variable)t.vars = append(t.vars, v.val)if next.typ == itemChar && next.val == "," {if context == "range" && len(decl) < 2 {continue}t.errorf("too many declarations in %s", context)}} else {t.backup2(v)}}break}pipe = newPipeline(t.lex.lineNumber(), decl)for {switch token := t.next(); token.typ {case itemRightDelim:if len(pipe.Cmds) == 0 {t.errorf("missing value for %s", context)}returncase itemBool, itemCharConstant, itemComplex, itemDot, itemField, itemIdentifier,itemVariable, itemNumber, itemRawString, itemString:t.backup()pipe.append(t.command())default:t.unexpected(token, context)}}return}func (t *Tree) parseControl(context string) (lineNum int, pipe *PipeNode, list, elseList *ListNode) {lineNum = t.lex.lineNumber()defer t.popVars(len(t.vars))pipe = t.pipeline(context)var next Nodelist, next = t.itemList()switch next.Type() {case nodeEnd: //donecase nodeElse:elseList, next = t.itemList()if next.Type() != 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 *Tree) ifControl() Node {return newIf(t.parseControl("if"))}// Range:// {{range pipeline}} itemList {{end}}// {{range pipeline}} itemList {{else}} itemList {{end}}// Range keyword is past.func (t *Tree) rangeControl() Node {return newRange(t.parseControl("range"))}// With:// {{with pipeline}} itemList {{end}}// {{with pipeline}} itemList {{else}} itemList {{end}}// If keyword is past.func (t *Tree) withControl() Node {return newWith(t.parseControl("with"))}// End:// {{end}}// End keyword is past.func (t *Tree) endControl() Node {t.expect(itemRightDelim, "end")return newEnd()}// Else:// {{else}}// Else keyword is past.func (t *Tree) 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 *Tree) templateControl() Node {var name stringswitch token := t.next(); token.typ {case itemString, itemRawString:s, err := strconv.Unquote(token.val)if err != nil {t.error(err)}name = sdefault:t.unexpected(token, "template invocation")}var pipe *PipeNodeif t.next().typ != itemRightDelim {t.backup()// Do not pop variables; they persist until "end".pipe = t.pipeline("template")}return newTemplate(t.lex.lineNumber(), name, pipe)}// 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 *Tree) command() *CommandNode {cmd := newCommand()Loop:for {switch token := t.next(); token.typ {case itemRightDelim:t.backup()break Loopcase itemPipe:break Loopcase itemError:t.errorf("%s", token.val)case itemIdentifier:if !t.hasFunction(token.val) {t.errorf("function %q not defined", token.val)}cmd.append(NewIdentifier(token.val))case itemDot:cmd.append(newDot())case itemVariable:cmd.append(t.useVar(token.val))case itemField:cmd.append(newField(token.val))case itemBool:cmd.append(newBool(token.val == "true"))case itemCharConstant, itemComplex, itemNumber:number, err := newNumber(token.val, token.typ)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(token.val, s))default:t.unexpected(token, "command")}}if len(cmd.Args) == 0 {t.errorf("empty command")}return cmd}// hasFunction reports if a function name exists in the Tree's maps.func (t *Tree) hasFunction(name string) bool {for _, funcMap := range t.funcs {if funcMap == nil {continue}if funcMap[name] != nil {return true}}return false}// popVars trims the variable list to the specified lengthfunc (t *Tree) popVars(n int) {t.vars = t.vars[:n]}// useVar returns a node for a variable reference. It errors if the// variable is not defined.func (t *Tree) useVar(name string) Node {v := newVariable(name)for _, varName := range t.vars {if varName == v.Ident[0] {return v}}t.errorf("undefined variable %q", v.Ident[0])return nil}
