Merge branch 'master' into pelletier/inline-tables
diff --git a/lexer.go b/lexer.go
index 95beb09..25635b4 100644
--- a/lexer.go
+++ b/lexer.go
@@ -158,13 +158,17 @@
 		case '.':
 			return l.errorf("cannot start float with a dot")
 		case '=':
-			return l.errorf("cannot have multiple equals for the same key")
+			return l.lexEqual
 		case '[':
 			l.depth++
 			return l.lexLeftBracket
 		case ']':
 			l.depth--
 			return l.lexRightBracket
+		case '{':
+			return l.lexLeftCurlyBrace
+		case '}':
+			return l.lexRightCurlyBrace
 		case '#':
 			return l.lexComment
 		case '"':
@@ -218,6 +222,20 @@
 	return nil
 }
 
+func (l *tomlLexer) lexLeftCurlyBrace() tomlLexStateFn {
+	l.ignore()
+	l.pos++
+	l.emit(tokenLeftCurlyBrace)
+	return l.lexRvalue
+}
+
+func (l *tomlLexer) lexRightCurlyBrace() tomlLexStateFn {
+	l.ignore()
+	l.pos++
+	l.emit(tokenRightCurlyBrace)
+	return l.lexRvalue
+}
+
 func (l *tomlLexer) lexDate() tomlLexStateFn {
 	l.emit(tokenDate)
 	return l.lexRvalue
diff --git a/lexer_test.go b/lexer_test.go
index d8abb35..df2cac9 100644
--- a/lexer_test.go
+++ b/lexer_test.go
@@ -8,11 +8,12 @@
 		token := <-ch
 		if token != expected {
 			t.Log("While testing: ", input)
+			t.Log("compared (got)", token, "to (expected)", expected)
+			t.Log("\tvalue:", token.val, "<->", expected.val)
+			t.Log("\ttype:", token.typ.String(), "<->", expected.typ.String())
+			t.Log("\tline:", token.Line, "<->", expected.Line)
+			t.Log("\tcolumn:", token.Col, "<->", expected.Col)
 			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()
 		}
 	}
@@ -371,14 +372,6 @@
 	})
 }
 
-func TestDoubleEqualKey(t *testing.T) {
-	testFlow(t, "foo= = 2", []token{
-		token{Position{1, 1}, tokenKey, "foo"},
-		token{Position{1, 4}, tokenEqual, "="},
-		token{Position{1, 5}, tokenError, "cannot have multiple equals for the same key"},
-	})
-}
-
 func TestInvalidEsquapeSequence(t *testing.T) {
 	testFlow(t, `foo = "\x"`, []token{
 		token{Position{1, 1}, tokenKey, "foo"},
diff --git a/parser.go b/parser.go
index bd33e0d..c3afa04 100644
--- a/parser.go
+++ b/parser.go
@@ -171,6 +171,7 @@
 func (p *tomlParser) parseAssign() tomlParserStateFn {
 	key := p.getToken()
 	p.assume(tokenEqual)
+
 	value := p.parseRvalue()
 	var groupKey []string
 	if len(p.currentGroup) > 0 {
@@ -245,6 +246,10 @@
 		return val
 	case tokenLeftBracket:
 		return p.parseArray()
+	case tokenLeftCurlyBrace:
+		return p.parseInlineTable()
+	case tokenEqual:
+		p.raiseError(tok, "cannot have multiple equals for the same key")
 	case tokenError:
 		p.raiseError(tok, "%s", tok)
 	}
@@ -254,7 +259,51 @@
 	return nil
 }
 
-func (p *tomlParser) parseArray() []interface{} {
+func tokenIsComma(t *token) bool {
+	return t != nil && t.typ == tokenComma
+}
+
+func (p *tomlParser) parseInlineTable() *TomlTree {
+	tree := newTomlTree()
+	var previous *token
+Loop:
+	for {
+		follow := p.peek()
+		if follow == nil || follow.typ == tokenEOF {
+			p.raiseError(follow, "unterminated inline table")
+		}
+		switch follow.typ {
+		case tokenRightCurlyBrace:
+			p.getToken()
+			break Loop
+		case tokenKey:
+			if !tokenIsComma(previous) && previous != nil {
+				p.raiseError(follow, "comma expected between fields in inline table")
+			}
+			key := p.getToken()
+			p.assume(tokenEqual)
+			value := p.parseRvalue()
+			tree.Set(key.val, value)
+		case tokenComma:
+			if previous == nil {
+				p.raiseError(follow, "inline table cannot start with a comma")
+			}
+			if tokenIsComma(previous) {
+				p.raiseError(follow, "need field between two commas in inline table")
+			}
+			p.getToken()
+		default:
+			p.raiseError(follow, "unexpected token type in inline table: %s", follow.typ.String())
+		}
+		previous = follow
+	}
+	if tokenIsComma(previous) {
+		p.raiseError(previous, "trailing comma at the end of inline table")
+	}
+	return tree
+}
+
+func (p *tomlParser) parseArray() interface{} {
 	var array []interface{}
 	arrayType := reflect.TypeOf(nil)
 	for {
@@ -285,6 +334,17 @@
 			p.getToken()
 		}
 	}
+	// An array of TomlTrees is actually an array of inline
+	// tables, which is a shorthand for a table array. If the
+	// array was not converted from []interface{} to []*TomlTree,
+	// the two notations would not be equivalent.
+	if arrayType == reflect.TypeOf(newTomlTree()) {
+		tomlArray := make([]*TomlTree, len(array))
+		for i, v := range array {
+			tomlArray[i] = v.(*TomlTree)
+		}
+		return tomlArray
+	}
 	return array
 }
 
diff --git a/parser_test.go b/parser_test.go
index 9b827b1..365c247 100644
--- a/parser_test.go
+++ b/parser_test.go
@@ -330,6 +330,52 @@
 	})
 }
 
+func TestSimpleInlineGroup(t *testing.T) {
+	tree, err := Load("key = {a = 42}")
+	assertTree(t, tree, err, map[string]interface{}{
+		"key": map[string]interface{}{
+			"a": int64(42),
+		},
+	})
+}
+
+func TestDoubleInlineGroup(t *testing.T) {
+	tree, err := Load("key = {a = 42, b = \"foo\"}")
+	assertTree(t, tree, err, map[string]interface{}{
+		"key": map[string]interface{}{
+			"a": int64(42),
+			"b": "foo",
+		},
+	})
+}
+
+func TestExampleInlineGroup(t *testing.T) {
+	tree, err := Load(`name = { first = "Tom", last = "Preston-Werner" }
+point = { x = 1, y = 2 }`)
+	assertTree(t, tree, err, map[string]interface{}{
+		"name": map[string]interface{}{
+			"first": "Tom",
+			"last":  "Preston-Werner",
+		},
+		"point": map[string]interface{}{
+			"x": int64(1),
+			"y": int64(2),
+		},
+	})
+}
+
+func TestExampleInlineGroupInArray(t *testing.T) {
+	tree, err := Load(`points = [{ x = 1, y = 2 }]`)
+	assertTree(t, tree, err, map[string]interface{}{
+		"points": []map[string]interface{}{
+			map[string]interface{}{
+				"x": int64(1),
+				"y": int64(2),
+			},
+		},
+	})
+}
+
 func TestDuplicateGroups(t *testing.T) {
 	_, err := Load("[foo]\na=2\n[foo]b=3")
 	if err.Error() != "(3, 2): duplicated tables" {
@@ -545,3 +591,10 @@
 		t.Error("Should error")
 	}
 }
+
+func TestDoubleEqual(t *testing.T) {
+	_, err := Load("foo= = 2")
+	if err.Error() != "(1, 6): cannot have multiple equals for the same key" {
+		t.Error("Bad error message:", err.Error())
+	}
+}
diff --git a/token.go b/token.go
index f0fbac9..ebbf960 100644
--- a/token.go
+++ b/token.go
@@ -26,6 +26,8 @@
 	tokenEqual
 	tokenLeftBracket
 	tokenRightBracket
+	tokenLeftCurlyBrace
+	tokenRightCurlyBrace
 	tokenLeftParen
 	tokenRightParen
 	tokenDoubleLeftBracket
@@ -44,6 +46,7 @@
 )
 
 var tokenTypeNames = []string{
+	"Error",
 	"EOF",
 	"Comment",
 	"Key",
@@ -54,7 +57,9 @@
 	"Float",
 	"=",
 	"[",
-	"[",
+	"]",
+	"{",
+	"}",
 	"(",
 	")",
 	"]]",