Skip to content

Commit 7f50e4c

Browse files
committed
Merge pull request #51 from pelletier/pelletier/fix-crlf-support
Fix support for CRLF line ending
2 parents a402e61 + 2df0835 commit 7f50e4c

File tree

4 files changed

+94
-3
lines changed

4 files changed

+94
-3
lines changed

example-crlf.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# This is a TOML document. Boom.
2+
3+
title = "TOML Example"
4+
5+
[owner]
6+
name = "Tom Preston-Werner"
7+
organization = "GitHub"
8+
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
9+
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
10+
11+
[database]
12+
server = "192.168.1.1"
13+
ports = [ 8001, 8001, 8002 ]
14+
connection_max = 5000
15+
enabled = true
16+
17+
[servers]
18+
19+
# You can indent as you please. Tabs or spaces. TOML don't care.
20+
[servers.alpha]
21+
ip = "10.0.0.1"
22+
dc = "eqdc10"
23+
24+
[servers.beta]
25+
ip = "10.0.0.2"
26+
dc = "eqdc10"
27+
28+
[clients]
29+
data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it

lexer.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ func (l *tomlLexer) lexVoid() tomlLexStateFn {
132132
return l.lexComment
133133
case '=':
134134
return l.lexEqual
135+
case '\r':
136+
fallthrough
135137
case '\n':
136138
l.skip()
137139
continue
@@ -185,6 +187,8 @@ func (l *tomlLexer) lexRvalue() tomlLexStateFn {
185187
return l.lexLiteralString
186188
case ',':
187189
return l.lexComma
190+
case '\r':
191+
fallthrough
188192
case '\n':
189193
l.skip()
190194
if l.depth == 0 {
@@ -277,7 +281,7 @@ func (l *tomlLexer) lexComma() tomlLexStateFn {
277281

278282
func (l *tomlLexer) lexKey() tomlLexStateFn {
279283
inQuotes := false
280-
for r := l.peek(); isKeyChar(r) || r == '\n'; r = l.peek() {
284+
for r := l.peek(); isKeyChar(r) || r == '\n' || r == '\r'; r = l.peek() {
281285
if r == '"' {
282286
inQuotes = !inQuotes
283287
} else if r == '\n' {
@@ -295,6 +299,9 @@ func (l *tomlLexer) lexKey() tomlLexStateFn {
295299

296300
func (l *tomlLexer) lexComment() tomlLexStateFn {
297301
for next := l.peek(); next != '\n' && next != eof; next = l.peek() {
302+
if (next == '\r' && l.follow("\r\n")) {
303+
break
304+
}
298305
l.next()
299306
}
300307
l.ignore()
@@ -319,7 +326,10 @@ func (l *tomlLexer) lexLiteralString() tomlLexStateFn {
319326
terminator = "'''"
320327

321328
// special case: discard leading newline
322-
if l.peek() == '\n' {
329+
if l.follow("\r\n") {
330+
l.skip()
331+
l.skip()
332+
} else if l.peek() == '\n' {
323333
l.skip()
324334
}
325335
}
@@ -355,7 +365,10 @@ func (l *tomlLexer) lexString() tomlLexStateFn {
355365
terminator = "\"\"\""
356366

357367
// special case: discard leading newline
358-
if l.peek() == '\n' {
368+
if l.follow("\r\n") {
369+
l.skip()
370+
l.skip()
371+
} else if l.peek() == '\n' {
359372
l.skip()
360373
}
361374
}

lexer_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,19 @@ func TestMultipleKeyGroupsComment(t *testing.T) {
8787
})
8888
}
8989

90+
91+
func TestSimpleWindowsCRLF(t *testing.T) {
92+
testFlow(t, "a=4\r\nb=2", []token{
93+
token{Position{1, 1}, tokenKey, "a"},
94+
token{Position{1, 2}, tokenEqual, "="},
95+
token{Position{1, 3}, tokenInteger, "4"},
96+
token{Position{2, 1}, tokenKey, "b"},
97+
token{Position{2, 2}, tokenEqual, "="},
98+
token{Position{2, 3}, tokenInteger, "2"},
99+
token{Position{2, 4}, tokenEOF, ""},
100+
})
101+
}
102+
90103
func TestBasicKey(t *testing.T) {
91104
testFlow(t, "hello", []token{
92105
token{Position{1, 1}, tokenKey, "hello"},

parser_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,42 @@ func TestParseFile(t *testing.T) {
494494
})
495495
}
496496

497+
func TestParseFileCRLF(t *testing.T) {
498+
tree, err := LoadFile("example-crlf.toml")
499+
500+
assertTree(t, tree, err, map[string]interface{}{
501+
"title": "TOML Example",
502+
"owner": map[string]interface{}{
503+
"name": "Tom Preston-Werner",
504+
"organization": "GitHub",
505+
"bio": "GitHub Cofounder & CEO\nLikes tater tots and beer.",
506+
"dob": time.Date(1979, time.May, 27, 7, 32, 0, 0, time.UTC),
507+
},
508+
"database": map[string]interface{}{
509+
"server": "192.168.1.1",
510+
"ports": []int64{8001, 8001, 8002},
511+
"connection_max": 5000,
512+
"enabled": true,
513+
},
514+
"servers": map[string]interface{}{
515+
"alpha": map[string]interface{}{
516+
"ip": "10.0.0.1",
517+
"dc": "eqdc10",
518+
},
519+
"beta": map[string]interface{}{
520+
"ip": "10.0.0.2",
521+
"dc": "eqdc10",
522+
},
523+
},
524+
"clients": map[string]interface{}{
525+
"data": []interface{}{
526+
[]string{"gamma", "delta"},
527+
[]int64{1, 2},
528+
},
529+
},
530+
})
531+
}
532+
497533
func TestParseKeyGroupArray(t *testing.T) {
498534
tree, err := Load("[[foo.bar]] a = 42\n[[foo.bar]] a = 69")
499535
assertTree(t, tree, err, map[string]interface{}{

0 commit comments

Comments
 (0)