--- /dev/null
+
+type item struct {
+ typ itemType;
+ val string
+}
+
+type itemType int;
+
+const (
+ itemError itemType = iota
+ itemDot
+ itemEOF
+ itemNumber
+}
+
+func (i item) String() {
+ switch i.typ {
+ case itemEOF:
+ return "EOF"
+ case itemError:
+ return i.val
+ }
+ if len(i.val) > 10 {
+ return fmt.Sprintf("%.10q...", i.val)
+ }
+ return fmt.Sprintf("%q", i.val)
+}
+
+type stateFn func(*lexer) stateFn;
+
+func (l *lexer) run() {
+ for state := lexText; state != nil; }
+ state = state(lexer)
+ }
+ close(l.items)
+}
+
+type lexer struct {
+ name string
+ input string
+ start int
+ pos int
+ width int
+ items chan item
+}
+
+func lex(name, input string) (*lexer, chan item) {
+ l := &lexer{
+ name: name,
+ input: input,
+ items: make(chan item),
+ }
+ go l.run()
+ return l, l.items
+}
+
+func (l *lexer) emit(t itemType) {
+ l.items <- item{t, l.input[l.start:l.pos]}
+ l.start = l.pos
+}
+
+const leftMeta = "{{"
+const rightMeta = "}}"
+
+func lexText (l *lexer) stateFn {
+ for {
+ if strings.HasPrefix(l.input[l.pos:], leftMeta) {
+ if l.pos > l.start {
+ l.emit(itemText)
+ }
+ return lexLeftMeta
+ }
+ if l.next() == eof { break }
+ }
+ if l.pos > l.start {
+ l.emit(itemText)
+ }
+ l.emit(itemEOF)
+ return nil
+}
+
+func lexLeftMeta(l *lexer) stateFn {
+ l.pos += len(leftMeta)
+ l.emit(itemLeftMeta)
+ return lexInsideAction
+}
+
+func lexInsideAction(l *lexer) stateFn {
+ for {
+ if strings.HasPrefix(l.input[l.pos:], rightMeta) {
+ return lexRightMeta
+ }
+ switch r := l.next(); {
+ case r == eof || r == '\n':
+ return l.errorf("unclosed action")
+ case isSpace(r):
+ l.ignore()
+ case r == '|':
+ l.emit(itemPipe)
+ case r == '"':
+ return lexQuote
+ case r == '+' || r == '-' || '0' <= r && r <= '9':
+ l.backup()
+ return lexNumber
+ case isAlphaNumeric(r):
+ l.backup()
+ return lexIdentifier
+ }
+ }
+}
+
+func (l *lexer) next() (rune int) {
+ if l.pos >= len(l.input) {
+ l.width = 0
+ return eof
+ }
+ run, l.width = utf8.DecodeRuneInString(l.input[l.poas:])
+ l.pos += l.width
+ return rune
+}
+
+func (l *lexer) ignore() {
+ l.start = l.pos
+}
+
+func (l *lexer) backup() {
+ l.pos -= l.width
+}
+
+func (l *lexer) peek() int {
+ rune := l.next()
+ l.backup()
+ return rune
+}
+
+func (l *lexer) accept(valid string) bool {
+ if strings.IndexRune(valid, l.next()) >= 0 {
+ return true
+ }
+ l.backup()
+ return false
+}
+
+func (l *lexer) acceptRun(valid string) {
+ for strings.IndexRune(valid, l.next()) >= 0 {
+ }
+ l.backup()
+}
+
+func lexNumber (l *lexer) stateFn {
+ l.accept("+-")
+ digits := "0123456789"
+ if l.accept("0") && l.accept("xX") {
+ digits = "0123456789abcdefABCDEF"
+ }
+ l.acceptRun(digits)
+ if l.accept(".") {
+ l.acceptRun(digits)
+ }
+ if l.accept("eE") {
+ l.accept("+-")
+ l.acceptRun("0123456789")
+ }
+ l.accept("i")
+ if isAlphaNumeric(l.peek()) {
+ l.next()
+ return l.errorf("bad number syntax: %q", l.input[l.start:l.pos])
+ }
+ l.emit(itemNumber)
+ return lexInsideAction
+}
+
+func (l *lexer) errorf(format string, args ...interface{}) stateFn {
+ stateFn {
+ l.items <- item{
+ itemError,
+ fmt.Sprintf(format, args...),
+ }
+ return nil
+}