semi-functional prototype
diff --git a/jpath/lexer.go b/jpath/lexer.go
new file mode 100644
index 0000000..0d19868
--- /dev/null
+++ b/jpath/lexer.go
@@ -0,0 +1,449 @@
+// TOML JSONPath lexer.
+//
+// Written using the principles developped by Rob Pike in
+// http://www.youtube.com/watch?v=HxaD_trXwRE
+
+package jpath
+
+import (
+  . "github.com/pelletier/go-toml"
+	"fmt"
+	"regexp"
+	"strconv"
+	"strings"
+	"unicode"
+	"unicode/utf8"
+)
+
+var dateRegexp *regexp.Regexp
+
+// Define tokens
+type tokenType int
+
+const (
+	eof = -(iota + 1)
+)
+
+const (
+	tokenError tokenType = iota
+	tokenEOF
+  tokenKey
+  tokenString
+  tokenFloat
+  tokenInteger
+  tokenAtCost
+  tokenDollar
+  tokenLBracket
+  tokenRBracket
+  tokenDot
+  tokenDotDot
+  tokenStar
+  tokenComma
+  tokenColon
+  tokenQuestion
+  tokenLParen
+  tokenRParen
+)
+
+var tokenTypeNames = []string{
+	"EOF",
+  "Key",
+  "String",
+  "Float",
+  "Integer",
+  "@",
+  "$",
+  "[",
+  "]",
+  ".",
+  "..",
+  "*",
+  ",",
+  ":",
+  "?",
+  "(",
+  ")",
+}
+
+type token struct {
+	Position
+	typ tokenType
+	val string
+}
+
+func (tt tokenType) String() string {
+	idx := int(tt)
+	if idx < len(tokenTypeNames) {
+		return tokenTypeNames[idx]
+	}
+	return "Unknown"
+}
+
+func (i token) String() string {
+	switch i.typ {
+	case tokenEOF:
+		return "EOF"
+	case tokenError:
+		return i.val
+	}
+
+	if len(i.val) > 10 {
+		return fmt.Sprintf("%.10q...", i.val)
+	}
+	return fmt.Sprintf("%q", i.val)
+}
+
+func isSpace(r rune) bool {
+	return r == ' ' || r == '\t'
+}
+
+func isAlphanumeric(r rune) bool {
+	return unicode.IsLetter(r) || r == '_'
+}
+
+func isKeyChar(r rune) bool {
+	// "Keys start with the first non-whitespace character and end with the last
+	// non-whitespace character before the equals sign."
+	return !(isSpace(r) || r == '\r' || r == '\n' || r == eof || r == '=')
+}
+
+func isDigit(r rune) bool {
+	return unicode.IsNumber(r)
+}
+
+func isHexDigit(r rune) bool {
+	return isDigit(r) ||
+		r == 'A' || r == 'B' || r == 'C' || r == 'D' || r == 'E' || r == 'F'
+}
+
+// Define lexer
+type lexer struct {
+	input      string
+	start      int
+	pos        int
+	width      int
+	tokens     chan token
+	depth      int
+	line       int
+	col        int
+  stringTerm string
+}
+
+func (l *lexer) run() {
+	for state := lexVoid; state != nil; {
+		state = state(l)
+	}
+	close(l.tokens)
+}
+
+func (l *lexer) nextStart() {
+	// iterate by runes (utf8 characters)
+	// search for newlines and advance line/col counts
+	for i := l.start; i < l.pos; {
+		r, width := utf8.DecodeRuneInString(l.input[i:])
+		if r == '\n' {
+			l.line++
+			l.col = 1
+		} else {
+			l.col++
+		}
+		i += width
+	}
+	// advance start position to next token
+	l.start = l.pos
+}
+
+func (l *lexer) emit(t tokenType) {
+	l.tokens <- token{
+		Position: Position{l.line, l.col},
+		typ:      t,
+		val:      l.input[l.start:l.pos],
+	}
+	l.nextStart()
+}
+
+func (l *lexer) emitWithValue(t tokenType, value string) {
+	l.tokens <- token{
+		Position: Position{l.line, l.col},
+		typ:      t,
+		val:      value,
+	}
+	l.nextStart()
+}
+
+func (l *lexer) next() rune {
+	if l.pos >= len(l.input) {
+		l.width = 0
+		return eof
+	}
+	var r rune
+	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
+	l.pos += l.width
+	return r
+}
+
+func (l *lexer) ignore() {
+	l.nextStart()
+}
+
+func (l *lexer) backup() {
+	l.pos -= l.width
+}
+
+func (l *lexer) errorf(format string, args ...interface{}) stateFn {
+	l.tokens <- token{
+		Position: Position{l.line, l.col},
+		typ:      tokenError,
+		val:      fmt.Sprintf(format, args...),
+	}
+	return nil
+}
+
+func (l *lexer) peek() rune {
+	r := l.next()
+	l.backup()
+	return r
+}
+
+func (l *lexer) accept(valid string) bool {
+	if strings.IndexRune(valid, l.next()) >= 0 {
+		return true
+	}
+	l.backup()
+	return false
+}
+
+func (l *lexer) follow(next string) bool {
+	return strings.HasPrefix(l.input[l.pos:], next)
+}
+
+// Define state functions
+type stateFn func(*lexer) stateFn
+
+func lexVoid(l *lexer) stateFn {
+  for {
+    next := l.peek()
+    switch next {
+    case '$':
+      l.pos++
+      l.emit(tokenDollar)
+      continue
+    case '.':
+      if l.follow("..") {
+        l.pos += 2
+        l.emit(tokenDotDot)
+      } else {
+        l.pos++
+        l.emit(tokenDot)
+      }
+      continue
+    case '@':
+      l.pos++
+      l.emit(tokenAtCost)
+      continue
+    case '[':
+      l.pos++
+      l.emit(tokenLBracket)
+      continue
+    case ']':
+      l.pos++
+      l.emit(tokenRBracket)
+      continue
+    case ',':
+      l.pos++
+      l.emit(tokenComma)
+      continue
+    case '*':
+      l.pos++
+      l.emit(tokenStar)
+      continue
+    case '(':
+      l.pos++
+      l.emit(tokenLParen)
+      continue
+    case ')':
+      l.pos++
+      l.emit(tokenRParen)
+      continue
+    case '?':
+      l.pos++
+      l.emit(tokenQuestion)
+      continue
+    case ':':
+      l.pos++
+      l.emit(tokenColon)
+      continue
+    case '\'':
+      l.ignore()
+      l.stringTerm = string(next)
+      return lexString
+    case '"':
+      l.ignore()
+      l.stringTerm = string(next)
+      return lexString
+    }
+
+		if isAlphanumeric(next) {
+			return lexKey
+		}
+
+		if next == '+' || next == '-' || isDigit(next) {
+			return lexNumber
+		}
+
+    if isAlphanumeric(next) {
+			return lexKey
+		}
+
+		if isSpace(next) {
+			l.ignore()
+		}
+
+    if l.next() == eof {
+			break
+    }
+
+    return l.errorf("unexpected char: '%v'", next)
+  }
+	l.emit(tokenEOF)
+	return nil
+}
+
+func lexKey(l *lexer) stateFn {
+  for {
+    next := l.peek()
+    if !isAlphanumeric(next) {
+      l.emit(tokenKey)
+      return lexVoid
+    }
+
+    if l.next() == eof {
+			break
+    }
+  }
+	l.emit(tokenEOF)
+	return nil
+}
+
+func lexString(l *lexer) stateFn {
+	l.pos++
+	l.ignore()
+	growingString := ""
+
+	for {
+		if l.follow(l.stringTerm) {
+			l.emitWithValue(tokenString, growingString)
+			l.pos++
+			l.ignore()
+			return lexVoid
+		}
+
+		if l.follow("\\\"") {
+			l.pos++
+			growingString += "\""
+		} else if l.follow("\\'") {
+			l.pos++
+			growingString += "'"
+		} else if l.follow("\\n") {
+			l.pos++
+			growingString += "\n"
+		} else if l.follow("\\b") {
+			l.pos++
+			growingString += "\b"
+		} else if l.follow("\\f") {
+			l.pos++
+			growingString += "\f"
+		} else if l.follow("\\/") {
+			l.pos++
+			growingString += "/"
+		} else if l.follow("\\t") {
+			l.pos++
+			growingString += "\t"
+		} else if l.follow("\\r") {
+			l.pos++
+			growingString += "\r"
+		} else if l.follow("\\\\") {
+			l.pos++
+			growingString += "\\"
+		} else if l.follow("\\u") {
+			l.pos += 2
+			code := ""
+			for i := 0; i < 4; i++ {
+				c := l.peek()
+				l.pos++
+				if !isHexDigit(c) {
+					return l.errorf("unfinished unicode escape")
+				}
+				code = code + string(c)
+			}
+			l.pos--
+			intcode, err := strconv.ParseInt(code, 16, 32)
+			if err != nil {
+				return l.errorf("invalid unicode escape: \\u" + code)
+			}
+			growingString += string(rune(intcode))
+		} else if l.follow("\\") {
+			l.pos++
+			return l.errorf("invalid escape sequence: \\" + string(l.peek()))
+		} else {
+			growingString += string(l.peek())
+		}
+
+		if l.next() == eof {
+			break
+		}
+	}
+
+	return l.errorf("unclosed string")
+}
+
+func lexNumber(l *lexer) stateFn {
+	l.ignore()
+	if !l.accept("+") {
+		l.accept("-")
+	}
+	pointSeen := false
+	digitSeen := false
+	for {
+		next := l.next()
+		if next == '.' {
+			if pointSeen {
+				return l.errorf("cannot have two dots in one float")
+			}
+			if !isDigit(l.peek()) {
+				return l.errorf("float cannot end with a dot")
+			}
+			pointSeen = true
+		} else if isDigit(next) {
+			digitSeen = true
+		} else {
+			l.backup()
+			break
+		}
+		if pointSeen && !digitSeen {
+			return l.errorf("cannot start float with a dot")
+		}
+	}
+
+	if !digitSeen {
+		return l.errorf("no digit in that number")
+	}
+	if pointSeen {
+		l.emit(tokenFloat)
+	} else {
+		l.emit(tokenInteger)
+	}
+	return lexVoid
+}
+
+// Entry point
+func lex(input string) (*lexer, chan token) {
+	l := &lexer{
+		input:  input,
+		tokens: make(chan token),
+		line:   1,
+		col:    1,
+	}
+	go l.run()
+	return l, l.tokens
+}
diff --git a/jpath/lexer_test.go b/jpath/lexer_test.go
new file mode 100644
index 0000000..8eb5fb5
--- /dev/null
+++ b/jpath/lexer_test.go
@@ -0,0 +1,70 @@
+
+package jpath
+
+import (
+  . "github.com/pelletier/go-toml"
+  "testing"
+)
+
+func testFlow(t *testing.T, input string, expectedFlow []token) {
+	_, ch := lex(input)
+	for idx, expected := range expectedFlow {
+		token := <-ch
+		if token != expected {
+			t.Log("While testing #", idx, ":", input)
+			t.Log("compared", token, "to", expected)
+			t.Log(token.val, "<->", expected.val)
+			t.Log(token.typ, "<->", expected.typ)
+			t.Log(token.Line, "<->", expected.Line)
+			t.Log(token.Col, "<->", expected.Col)
+			t.FailNow()
+		}
+	}
+
+	tok, ok := <-ch
+	if ok {
+		t.Log("channel is not closed!")
+		t.Log(len(ch)+1, "tokens remaining:")
+
+		t.Log("token ->", tok)
+		for token := range ch {
+			t.Log("token ->", token)
+		}
+		t.FailNow()
+	}
+}
+
+func TestLexSpecialChars(t *testing.T) {
+	testFlow(t, "@.$[]..()?*", []token{
+		token{Position{1, 1}, tokenAtCost, "@"},
+		token{Position{1, 2}, tokenDot, "."},
+		token{Position{1, 3}, tokenDollar, "$"},
+		token{Position{1, 4}, tokenLBracket, "["},
+		token{Position{1, 5}, tokenRBracket, "]"},
+		token{Position{1, 6}, tokenDotDot, ".."},
+		token{Position{1, 8}, tokenLParen, "("},
+		token{Position{1, 9}, tokenRParen, ")"},
+		token{Position{1, 10}, tokenQuestion, "?"},
+		token{Position{1, 11}, tokenStar, "*"},
+		token{Position{1, 12}, tokenEOF, ""},
+	})
+}
+
+func TestLexString(t *testing.T) {
+	testFlow(t, "'foo'", []token{
+		token{Position{1, 2}, tokenString, "foo"},
+		token{Position{1, 6}, tokenEOF, ""},
+	})
+
+	testFlow(t, `"bar"`, []token{
+		token{Position{1, 2}, tokenString, "bar"},
+		token{Position{1, 6}, tokenEOF, ""},
+	})
+}
+
+func TestLexKey(t *testing.T) {
+	testFlow(t, "foo", []token{
+		token{Position{1, 1}, tokenKey, "foo"},
+		token{Position{1, 4}, tokenEOF, ""},
+	})
+}
diff --git a/jpath/match.go b/jpath/match.go
new file mode 100644
index 0000000..6e165c9
--- /dev/null
+++ b/jpath/match.go
@@ -0,0 +1,122 @@
+package jpath
+
+import (
+  . "github.com/pelletier/go-toml"
+)
+
+type PathFn func(context interface{}) []interface{}
+
+func treeValue(tree *TomlTree, key string) interface{} {
+  return tree.GetPath([]string{key})
+}
+
+func matchKeyFn(name string) PathFn {
+  return func(context interface{}) []interface{} {
+    if tree, ok := context.(*TomlTree); ok {
+      item := treeValue(tree, name)
+      if item != nil {
+        return []interface{}{ item }
+      }
+    }
+    return []interface{}{}
+  }
+}
+
+func matchIndexFn(idx int) PathFn {
+  return func(context interface{}) []interface{} {
+    if arr, ok := context.([]interface{}); ok {
+      if idx < len(arr) && idx >= 0 {
+        return arr[idx:idx+1]
+      }
+    }
+    return []interface{}{}
+  }
+}
+
+func matchSliceFn(start, end, step int) PathFn {
+  return func(context interface{}) []interface{} {
+    result := []interface{}{}
+    if arr, ok := context.([]interface{}); ok {
+      // adjust indexes for negative values, reverse ordering
+      realStart, realEnd := start, end
+      if realStart < 0 {
+        realStart = len(arr) + realStart
+      }
+      if realEnd < 0 {
+        realEnd = len(arr) + realEnd
+      }
+      if realEnd < realStart {
+        realEnd, realStart = realStart, realEnd  // swap
+      }
+      // loop and gather
+      for idx := realStart; idx < realEnd; idx += step {
+         result = append(result, arr[idx])
+      }
+    }
+    return result
+  }
+}
+
+func matchAnyFn() PathFn {
+  return func(context interface{}) []interface{} {
+    result := []interface{}{}
+    if tree, ok := context.(*TomlTree); ok {
+      for _, key := range tree.Keys() {
+        item := treeValue(tree, key)
+        result = append(result, item)
+      }
+    }
+    return result
+  }
+}
+
+func matchUnionFn(union []PathFn) PathFn {
+  return func(context interface{}) []interface{} {
+    result := []interface{}{}
+    for _, fn := range union {
+      result = append(result, fn(context)...)
+    }
+    return result
+  }
+}
+
+func matchRecurseFn() PathFn {
+  return func(context interface{}) []interface{} {
+    result := []interface{}{ context }
+
+    if tree, ok := context.(*TomlTree); ok {
+      var visit func(tree *TomlTree)
+      visit = func(tree *TomlTree) {
+        for _, key := range tree.Keys() {
+          item := treeValue(tree, key)
+          result = append(result, item)
+          switch node := item.(type) {
+          case *TomlTree:
+            visit(node)
+          case []*TomlTree:
+            for _, subtree := range node {
+              visit(subtree)
+            }
+          }
+        }
+      }
+      visit(tree)
+    }
+    return result
+  }
+}
+
+func processPath(path []PathFn, context interface{}) []interface{} {
+  result := []interface{}{ context }  // start with the root
+  for _, fn := range path {
+    next := []interface{}{}
+    for _, ctx := range result {
+      next = append(next, fn(ctx)...)
+    }
+    if len(next) == 0 {
+      return next // exit if there is nothing more to search
+    }
+    result = next // prep the next iteration
+  }
+  return result
+}
diff --git a/jpath/parser.go b/jpath/parser.go
new file mode 100644
index 0000000..33f188c
--- /dev/null
+++ b/jpath/parser.go
@@ -0,0 +1,225 @@
+package jpath
+
+import (
+	"fmt"
+  "math"
+	"strconv"
+)
+
+type parser struct {
+	flow          chan token
+	tokensBuffer  []token
+  path          []PathFn
+}
+
+type parserStateFn func(*parser) parserStateFn
+
+// Formats and panics an error message based on a token
+func (p *parser) raiseError(tok *token, msg string, args ...interface{}) {
+	panic(tok.Position.String() + ": " + fmt.Sprintf(msg, args...))
+}
+
+func (p *parser) run() {
+	for state := parseStart; state != nil; {
+		state = state(p)
+	}
+}
+
+func (p *parser) backup(tok *token) {
+	p.tokensBuffer = append(p.tokensBuffer, *tok)
+}
+
+func (p *parser) peek() *token {
+	if len(p.tokensBuffer) != 0 {
+		return &(p.tokensBuffer[0])
+	}
+
+	tok, ok := <-p.flow
+	if !ok {
+		return nil
+	}
+  p.backup(&tok)
+	return &tok
+}
+
+func (p *parser) getToken() *token {
+	if len(p.tokensBuffer) != 0 {
+		tok := p.tokensBuffer[0]
+		p.tokensBuffer = p.tokensBuffer[1:]
+		return &tok
+	}
+	tok, ok := <-p.flow
+	if !ok {
+		return nil
+	}
+	return &tok
+}
+
+
+func (p *parser) appendPath(fn PathFn) {
+  p.path = append(p.path, fn)
+}
+
+func parseStart(p *parser) parserStateFn {
+	tok := p.getToken()
+
+	if tok == nil || tok.typ == tokenEOF {
+		return nil
+	}
+
+  if tok.typ != tokenDollar {
+    p.raiseError(tok, "Expected '$' at start of expression")
+  }
+
+  return parseMatchExpr
+}
+
+func parseMatchExpr(p *parser) parserStateFn {
+	tok := p.getToken()
+	switch tok.typ {
+	case tokenDot:
+    p.appendPath(matchKeyFn(tok.val))
+    return parseMatchExpr
+  case tokenDotDot:
+    p.appendPath(matchRecurseFn())
+		return parseSimpleMatchExpr
+	case tokenLBracket:
+		return parseBracketExpr
+  case tokenStar:
+    p.appendPath(matchAnyFn())
+    return parseMatchExpr
+  case tokenEOF:
+    return nil  // allow EOF at this stage
+	}
+	p.raiseError(tok, "expected match expression")
+	return nil
+}
+
+func parseSimpleMatchExpr(p *parser) parserStateFn {
+	tok := p.getToken()
+	switch tok.typ {
+	case tokenLBracket:
+		return parseBracketExpr
+	case tokenKey:
+    p.appendPath(matchKeyFn(tok.val))
+    return parseMatchExpr
+  case tokenStar:
+    p.appendPath(matchAnyFn())
+    return parseMatchExpr
+	}
+	p.raiseError(tok, "expected match expression")
+	return nil
+}
+
+func parseBracketExpr(p *parser) parserStateFn {
+  tok := p.peek()
+  switch tok.typ {
+  case tokenInteger:
+    // look ahead for a ':'
+    p.getToken()
+    next := p.peek()
+    p.backup(tok)
+    if next.typ == tokenColon {
+      return parseSliceExpr
+    }
+    return parseUnionExpr
+  case tokenColon:
+    return parseSliceExpr
+	}
+	return parseUnionExpr
+}
+
+func parseUnionExpr(p *parser) parserStateFn {
+  union := []PathFn{}
+  for {
+    // parse sub expression
+    tok := p.getToken()
+    switch tok.typ {
+    case tokenInteger:
+      idx, _ := strconv.Atoi(tok.val)
+      union = append(union, matchIndexFn(idx))
+    case tokenKey:
+      union = append(union, matchKeyFn(tok.val))
+    case tokenQuestion:
+      return parseFilterExpr
+    case tokenLParen:
+      return parseScriptExpr
+    default:
+      p.raiseError(tok, "expected union sub expression")
+    }
+    // parse delimiter or terminator
+    tok = p.getToken()
+    switch tok.typ {
+    case tokenComma:
+      continue
+    case tokenRBracket:
+      break
+    default:
+      p.raiseError(tok, "expected ',' or ']'")
+    }
+  }
+  p.appendPath(matchUnionFn(union))
+  return parseMatchExpr
+}
+
+func parseSliceExpr(p *parser) parserStateFn {
+  // init slice to grab all elements
+  start, end, step := 0, math.MaxInt64, 1
+
+  // parse optional start
+  tok := p.getToken()
+  if tok.typ == tokenInteger {
+    start, _ = strconv.Atoi(tok.val)
+    tok = p.getToken()
+  }
+  if tok.typ != tokenColon {
+    p.raiseError(tok, "expected ':'")
+  }
+
+  // parse optional end
+  tok = p.getToken()
+  if tok.typ == tokenInteger {
+    end, _ = strconv.Atoi(tok.val)
+    tok = p.getToken()
+  }
+  if tok.typ != tokenColon || tok.typ != tokenRBracket {
+    p.raiseError(tok, "expected ']' or ':'")
+  }
+
+  // parse optional step
+  tok = p.getToken()
+  if tok.typ == tokenInteger {
+    step, _ = strconv.Atoi(tok.val)
+    if step < 0 {
+      p.raiseError(tok, "step must be a positive value")
+    }
+    tok = p.getToken()
+  }
+  if tok.typ != tokenRBracket {
+    p.raiseError(tok, "expected ']'")
+  }
+
+  p.appendPath(matchSliceFn(start, end, step))
+  return parseMatchExpr
+}
+
+func parseFilterExpr(p *parser) parserStateFn {
+	p.raiseError(p.peek(), "filter expressions are unsupported")
+  return nil
+}
+
+func parseScriptExpr(p *parser) parserStateFn {
+	p.raiseError(p.peek(), "script expressions are unsupported")
+  return nil
+}
+
+func parse(flow chan token) []PathFn {
+	result := []PathFn{}
+	parser := &parser{
+		flow:          flow,
+		tokensBuffer:  []token{},
+    path:          result,
+	}
+	parser.run()
+	return result
+}
diff --git a/jpath/parser_test.go b/jpath/parser_test.go
new file mode 100644
index 0000000..99bc4d0
--- /dev/null
+++ b/jpath/parser_test.go
@@ -0,0 +1,465 @@
+package jpath
+
+import (
+	"fmt"
+	"testing"
+  . "github.com/pelletier/go-toml"
+)
+
+func assertQuery(t *testing.T, toml, query string, ref []interface{}) {
+	tree, err := Load(toml)
+	if err != nil {
+		t.Errorf("Non-nil toml parse error: %v", err)
+    return
+	}
+	_, flow := lex(query)
+	if err != nil {
+		t.Errorf("Non-nil query lex error: %v", err)
+		return
+	}
+	path := parse(flow)
+  result := processPath(path, tree)
+  assertValue(t, result, ref, "")
+}
+
+func assertValue(t *testing.T, result, ref interface{}, location string) {
+  switch node := ref.(type) {
+  case []interface{}:
+    if resultNode, ok := result.([]interface{}); !ok {
+      t.Errorf("{%s} result value not of type %T: %T",
+        location, node, resultNode)
+    } else {
+      for i, v := range node {
+        assertValue(t, resultNode[i], v, fmt.Sprintf("%s[%d]", location, i))
+      }
+    }
+  case map[string]interface{}:
+    if resultNode, ok := result.(*TomlTree); !ok {
+      t.Errorf("{%s} result value not of type %T: %T",
+        location, node, resultNode)
+    } else {
+      for k, v := range node {
+        assertValue(t, resultNode.GetPath([]string{k}), v, location + "." + k)
+      }
+    }
+  case int64:
+    if resultNode, ok := result.(int64); !ok {
+      t.Errorf("{%s} result value not of type %T: %T",
+        location, node, resultNode)
+    } else {
+      if node != resultNode {
+        t.Errorf("{%s} result value does not match", location)
+      }
+    }
+  case string:
+    if resultNode, ok := result.(string); !ok {
+      t.Errorf("{%s} result value not of type %T: %T",
+        location, node, resultNode)
+    } else {
+      if node != resultNode {
+        t.Errorf("{%s} result value does not match", location)
+      }
+    }
+  default:
+    if fmt.Sprintf("%v", node) != fmt.Sprintf("%v", ref) {
+      t.Errorf("{%s} result value does not match: %v != %v",
+        location, node, ref)
+    }
+  }
+}
+
+func TestQueryRoot(t *testing.T) {
+  assertQuery(t,
+    "a = 42",
+    "$",
+	  []interface{}{
+      map[string]interface{}{
+		    "a": int64(42),
+	    },
+    })
+}
+
+/*
+// NOTE: from the BurntSushi test suite
+// NOTE: this test is pure evil due to the embedded '.'
+func TestSpecialKV(t *testing.T) {
+	tree, err := Load("~!@#$^&*()_+-`1234567890[]\\|/?><.,;: = 1")
+	assertTree(t, tree, err, map[string]interface{}{
+		"~!@#$^&*()_+-`1234567890[]\\|/?><.,;:": int64(1),
+	})
+}
+
+func TestSimpleNumbers(t *testing.T) {
+	tree, err := Load("a = +42\nb = -21\nc = +4.2\nd = -2.1")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": int64(42),
+		"b": int64(-21),
+		"c": float64(4.2),
+		"d": float64(-2.1),
+	})
+}
+
+func TestSimpleDate(t *testing.T) {
+	tree, err := Load("a = 1979-05-27T07:32:00Z")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC),
+	})
+}
+
+func TestSimpleString(t *testing.T) {
+	tree, err := Load("a = \"hello world\"")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": "hello world",
+	})
+}
+
+func TestStringEscapables(t *testing.T) {
+	tree, err := Load("a = \"a \\n b\"")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": "a \n b",
+	})
+
+	tree, err = Load("a = \"a \\t b\"")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": "a \t b",
+	})
+
+	tree, err = Load("a = \"a \\r b\"")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": "a \r b",
+	})
+
+	tree, err = Load("a = \"a \\\\ b\"")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": "a \\ b",
+	})
+}
+
+func TestBools(t *testing.T) {
+	tree, err := Load("a = true\nb = false")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": true,
+		"b": false,
+	})
+}
+
+func TestNestedKeys(t *testing.T) {
+	tree, err := Load("[a.b.c]\nd = 42")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": map[string]interface{}{
+			"b": map[string]interface{}{
+				"c": map[string]interface{}{
+					"d": int64(42),
+				},
+			},
+		},
+	})
+}
+
+func TestArrayOne(t *testing.T) {
+	tree, err := Load("a = [1]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": []int64{int64(1)},
+	})
+}
+
+func TestArrayZero(t *testing.T) {
+	tree, err := Load("a = []")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": []interface{}{},
+	})
+}
+
+func TestArraySimple(t *testing.T) {
+	tree, err := Load("a = [42, 21, 10]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": []int64{int64(42), int64(21), int64(10)},
+	})
+
+	tree, _ = Load("a = [42, 21, 10,]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": []int64{int64(42), int64(21), int64(10)},
+	})
+}
+
+func TestArrayMultiline(t *testing.T) {
+	tree, err := Load("a = [42,\n21, 10,]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": []int64{int64(42), int64(21), int64(10)},
+	})
+}
+
+func TestArrayNested(t *testing.T) {
+	tree, err := Load("a = [[42, 21], [10]]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": [][]int64{[]int64{int64(42), int64(21)}, []int64{int64(10)}},
+	})
+}
+
+func TestNestedEmptyArrays(t *testing.T) {
+	tree, err := Load("a = [[[]]]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": [][][]interface{}{[][]interface{}{[]interface{}{}}},
+	})
+}
+
+func TestArrayMixedTypes(t *testing.T) {
+	_, err := Load("a = [42, 16.0]")
+	if err.Error() != "(1, 10): mixed types in array" {
+		t.Error("Bad error message:", err.Error())
+	}
+
+	_, err = Load("a = [42, \"hello\"]")
+	if err.Error() != "(1, 11): mixed types in array" {
+		t.Error("Bad error message:", err.Error())
+	}
+}
+
+func TestArrayNestedStrings(t *testing.T) {
+	tree, err := Load("data = [ [\"gamma\", \"delta\"], [\"Foo\"] ]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"data": [][]string{[]string{"gamma", "delta"}, []string{"Foo"}},
+	})
+}
+
+func TestMissingValue(t *testing.T) {
+	_, err := Load("a = ")
+	if err.Error() != "(1, 4): expecting a value" {
+		t.Error("Bad error message:", err.Error())
+	}
+}
+
+func TestUnterminatedArray(t *testing.T) {
+	_, err := Load("a = [1,")
+	if err.Error() != "(1, 8): unterminated array" {
+		t.Error("Bad error message:", err.Error())
+	}
+}
+
+func TestNewlinesInArrays(t *testing.T) {
+	tree, err := Load("a = [1,\n2,\n3]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": []int64{int64(1), int64(2), int64(3)},
+	})
+}
+
+func TestArrayWithExtraComma(t *testing.T) {
+	tree, err := Load("a = [1,\n2,\n3,\n]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": []int64{int64(1), int64(2), int64(3)},
+	})
+}
+
+func TestArrayWithExtraCommaComment(t *testing.T) {
+	tree, err := Load("a = [1, # wow\n2, # such items\n3, # so array\n]")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": []int64{int64(1), int64(2), int64(3)},
+	})
+}
+
+func TestDuplicateGroups(t *testing.T) {
+	_, err := Load("[foo]\na=2\n[foo]b=3")
+	if err.Error() != "(3, 2): duplicated tables" {
+		t.Error("Bad error message:", err.Error())
+	}
+}
+
+func TestDuplicateKeys(t *testing.T) {
+	_, err := Load("foo = 2\nfoo = 3")
+	if err.Error() != "(2, 1): The following key was defined twice: foo" {
+		t.Error("Bad error message:", err.Error())
+	}
+}
+
+func TestEmptyIntermediateTable(t *testing.T) {
+	_, err := Load("[foo..bar]")
+	if err.Error() != "(1, 2): empty intermediate table" {
+		t.Error("Bad error message:", err.Error())
+	}
+}
+
+func TestImplicitDeclarationBefore(t *testing.T) {
+	tree, err := Load("[a.b.c]\nanswer = 42\n[a]\nbetter = 43")
+	assertTree(t, tree, err, map[string]interface{}{
+		"a": map[string]interface{}{
+			"b": map[string]interface{}{
+				"c": map[string]interface{}{
+					"answer": int64(42),
+				},
+			},
+			"better": int64(43),
+		},
+	})
+}
+
+func TestFloatsWithoutLeadingZeros(t *testing.T) {
+	_, err := Load("a = .42")
+	if err.Error() != "(1, 4): cannot start float with a dot" {
+		t.Error("Bad error message:", err.Error())
+	}
+
+	_, err = Load("a = -.42")
+	if err.Error() != "(1, 5): cannot start float with a dot" {
+		t.Error("Bad error message:", err.Error())
+	}
+}
+
+func TestMissingFile(t *testing.T) {
+	_, err := LoadFile("foo.toml")
+	if err.Error() != "open foo.toml: no such file or directory" {
+		t.Error("Bad error message:", err.Error())
+	}
+}
+
+func TestParseFile(t *testing.T) {
+	tree, err := LoadFile("example.toml")
+
+	assertTree(t, tree, err, map[string]interface{}{
+		"title": "TOML Example",
+		"owner": map[string]interface{}{
+			"name":         "Tom Preston-Werner",
+			"organization": "GitHub",
+			"bio":          "GitHub Cofounder & CEO\nLikes tater tots and beer.",
+			"dob":          time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC),
+		},
+		"database": map[string]interface{}{
+			"server":         "192.168.1.1",
+			"ports":          []int64{8001, 8001, 8002},
+			"connection_max": 5000,
+			"enabled":        true,
+		},
+		"servers": map[string]interface{}{
+			"alpha": map[string]interface{}{
+				"ip": "10.0.0.1",
+				"dc": "eqdc10",
+			},
+			"beta": map[string]interface{}{
+				"ip": "10.0.0.2",
+				"dc": "eqdc10",
+			},
+		},
+		"clients": map[string]interface{}{
+			"data": []interface{}{
+				[]string{"gamma", "delta"},
+				[]int64{1, 2},
+			},
+		},
+	})
+}
+
+func TestParseKeyGroupArray(t *testing.T) {
+	tree, err := Load("[[foo.bar]] a = 42\n[[foo.bar]] a = 69")
+	assertTree(t, tree, err, map[string]interface{}{
+		"foo": map[string]interface{}{
+			"bar": []map[string]interface{}{
+				{"a": int64(42)},
+				{"a": int64(69)},
+			},
+		},
+	})
+}
+
+func TestParseKeyGroupArraySpec(t *testing.T) {
+	tree, err := Load("[[fruit]]\n name=\"apple\"\n [fruit.physical]\n color=\"red\"\n shape=\"round\"\n [[fruit]]\n name=\"banana\"")
+	assertTree(t, tree, err, map[string]interface{}{
+		"fruit": []map[string]interface{}{
+			{"name": "apple", "physical": map[string]interface{}{"color": "red", "shape": "round"}},
+			{"name": "banana"},
+		},
+	})
+}
+
+func TestToTomlValue(t *testing.T) {
+	for idx, item := range []struct {
+		Value  interface{}
+		Expect string
+	}{
+		{int64(12345), "12345"},
+		{float64(123.45), "123.45"},
+		{bool(true), "true"},
+		{"hello world", "\"hello world\""},
+		{"\b\t\n\f\r\"\\", "\"\\b\\t\\n\\f\\r\\\"\\\\\""},
+		{"\x05", "\"\\u0005\""},
+		{time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC),
+			"1979-05-27T07:32:00Z"},
+		{[]interface{}{"gamma", "delta"},
+			"[\n  \"gamma\",\n  \"delta\",\n]"},
+	} {
+		result := toTomlValue(item.Value, 0)
+		if result != item.Expect {
+			t.Errorf("Test %d - got '%s', expected '%s'", idx, result, item.Expect)
+		}
+	}
+}
+
+func TestToString(t *testing.T) {
+	tree, err := Load("[foo]\n\n[[foo.bar]]\na = 42\n\n[[foo.bar]]\na = 69\n")
+	if err != nil {
+		t.Errorf("Test failed to parse: %v", err)
+		return
+	}
+	result := tree.ToString()
+	expected := "\n[foo]\n\n  [[foo.bar]]\n    a = 42\n\n  [[foo.bar]]\n    a = 69\n"
+	if result != expected {
+		t.Errorf("Expected got '%s', expected '%s'", result, expected)
+	}
+}
+
+func assertPosition(t *testing.T, text string, ref map[string]Position) {
+	tree, err := Load(text)
+	if err != nil {
+		t.Errorf("Error loading document text: `%v`", text)
+		t.Errorf("Error: %v", err)
+	}
+	for path, pos := range ref {
+		testPos := tree.GetPosition(path)
+		if testPos.Invalid() {
+			t.Errorf("Failed to query tree path: %s", path)
+		} else if pos != testPos {
+			t.Errorf("Expected position %v, got %v instead", pos, testPos)
+		}
+	}
+}
+
+func TestDocumentPositions(t *testing.T) {
+	assertPosition(t,
+		"[foo]\nbar=42\nbaz=69",
+		map[string]Position{
+			"foo":     Position{1, 1},
+			"foo.bar": Position{2, 1},
+			"foo.baz": Position{3, 1},
+		})
+}
+
+func TestDocumentPositionsWithSpaces(t *testing.T) {
+	assertPosition(t,
+		"  [foo]\n  bar=42\n  baz=69",
+		map[string]Position{
+			"foo":     Position{1, 3},
+			"foo.bar": Position{2, 3},
+			"foo.baz": Position{3, 3},
+		})
+}
+
+func TestDocumentPositionsWithGroupArray(t *testing.T) {
+	assertPosition(t,
+		"[[foo]]\nbar=42\nbaz=69",
+		map[string]Position{
+			"foo":     Position{1, 1},
+			"foo.bar": Position{2, 1},
+			"foo.baz": Position{3, 1},
+		})
+}
+
+func TestDocumentPositionsEmptyPath(t *testing.T) {
+	text := "[foo]\nbar=42\nbaz=69"
+	tree, err := Load(text)
+	if err != nil {
+		t.Errorf("Error loading document text: `%v`", text)
+		t.Errorf("Error: %v", err)
+	}
+	if pos := tree.GetPosition(""); !pos.Invalid() {
+		t.Errorf("Valid position was returned for empty path")
+	}
+}
+*/
diff --git a/test.sh b/test.sh
index 07aa52e..80b27a7 100755
--- a/test.sh
+++ b/test.sh
@@ -21,8 +21,11 @@
 mkdir -p src/github.com/pelletier/go-toml/cmd
 cp *.go *.toml src/github.com/pelletier/go-toml
 cp cmd/*.go src/github.com/pelletier/go-toml/cmd
+mkdir -p src/github.com/pelletier/go-toml/jpath
+cp jpath/*.go src/github.com/pelletier/go-toml/jpath
 go build -o test_program_bin src/github.com/pelletier/go-toml/cmd/test_program.go
 
 # Run basic unit tests and then the BurntSushi test suite
+go test -v github.com/pelletier/go-toml/jpath
 go test -v github.com/pelletier/go-toml
 ./toml-test ./test_program_bin | tee test_out