Skip to content

Commit

Permalink
add some parser bits from text/template
Browse files Browse the repository at this point in the history
  • Loading branch information
jmoiron committed May 11, 2014
1 parent 3d59271 commit 5fd79de
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 2 deletions.
1 change: 1 addition & 0 deletions neo/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Node interface {

const (
NodeList NodeType = iota
NodeText
)

// ListNode holds a sequence of nodes.
Expand Down
109 changes: 107 additions & 2 deletions neo/parse.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,120 @@
package jigo

import (
"fmt"
"strings"
)

// Tree is the representation of a single parsed template.
type Tree struct {
Name string // name of the template represented by the tree.
ParseName string // name of the top-level template during parsing, for error messages.
Root *ListNode // top-level root of the tree.
text string // text parsed to create the template (or its parent)
// Parsing only; cleared after parse.
funcs []map[string]interface{}
lex *lexer
funcs []map[string]interface{}
lex *lexer
// FIXME: how much do we need?
token [3]item // three-token lookahead for parser.
peekCount int
vars []string // variables defined at the moment.
}

// 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 tokens.
// The zeroth token is already there.
func (t *Tree) backup2(t1 item) {
t.token[1] = t1
t.peekCount = 2
}

// backup3 backs the input stream up three tokens
// The zeroth token is already there.
func (t *Tree) backup3(t2, t1 item) { // Reverse order: we're pushing back.
t.token[1] = t1
t.token[2] = t2
t.peekCount = 3
}

// 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 = 1
t.token[0] = t.lex.nextItem()
return t.token[0]
}

// nextNonSpace returns the next non-space token.
func (t *Tree) nextNonSpace() (token item) {
for {
token = t.next()
if token.typ != tokenWhitespace {
break
}
}
return token
}

// peekNonSpace returns but does not consume the next non-space token.
func (t *Tree) peekNonSpace() (token item) {
for {
token = t.next()
if token.typ != tokenWhitespace {
break
}
}
t.backup()
return token
}

// 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,
}
}

// ErrorContext returns a textual representation of the location of the node in the input text.
func (t *Tree) ErrorContext(n Node) (location, context string) {
pos := int(n.Position())
text := t.text[:pos]
byteNum := strings.LastIndex(text, "\n")
if byteNum == -1 {
byteNum = pos // On first line.
} else {
byteNum++ // After the newline.
byteNum = pos - byteNum
}
lineNum := 1 + strings.Count(text, "\n")
context = n.String()
if len(context) > 20 {
context = fmt.Sprintf("%.20s...", context)
}
return fmt.Sprintf("%s:%d:%d", t.ParseName, lineNum, byteNum), context
}

// errorf formats the error and terminates processing.
func (t *Tree) errorf(format string, args ...interface{}) {
t.Root = nil
format = fmt.Sprintf("template: %s:%d: %s", t.ParseName, t.lex.lineNumber(), format)
panic(fmt.Errorf(format, args...))
}

0 comments on commit 5fd79de

Please sign in to comment.