1package toml 2 3import ( 4 "strings" 5 "testing" 6) 7 8func testFlow(t *testing.T, input string, expectedFlow []token) { 9 ch := lexToml(strings.NewReader(input)) 10 for _, expected := range expectedFlow { 11 token := <-ch 12 if token != expected { 13 t.Log("While testing: ", input) 14 t.Log("compared (got)", token, "to (expected)", expected) 15 t.Log("\tvalue:", token.val, "<->", expected.val) 16 t.Log("\tvalue as bytes:", []byte(token.val), "<->", []byte(expected.val)) 17 t.Log("\ttype:", token.typ.String(), "<->", expected.typ.String()) 18 t.Log("\tline:", token.Line, "<->", expected.Line) 19 t.Log("\tcolumn:", token.Col, "<->", expected.Col) 20 t.Log("compared", token, "to", expected) 21 t.FailNow() 22 } 23 } 24 25 tok, ok := <-ch 26 if ok { 27 t.Log("channel is not closed!") 28 t.Log(len(ch)+1, "tokens remaining:") 29 30 t.Log("token ->", tok) 31 for token := range ch { 32 t.Log("token ->", token) 33 } 34 t.FailNow() 35 } 36} 37 38func TestValidKeyGroup(t *testing.T) { 39 testFlow(t, "[hello world]", []token{ 40 {Position{1, 1}, tokenLeftBracket, "["}, 41 {Position{1, 2}, tokenKeyGroup, "hello world"}, 42 {Position{1, 13}, tokenRightBracket, "]"}, 43 {Position{1, 14}, tokenEOF, ""}, 44 }) 45} 46 47func TestNestedQuotedUnicodeKeyGroup(t *testing.T) { 48 testFlow(t, `[ j . "ʞ" . l ]`, []token{ 49 {Position{1, 1}, tokenLeftBracket, "["}, 50 {Position{1, 2}, tokenKeyGroup, ` j . "ʞ" . l `}, 51 {Position{1, 15}, tokenRightBracket, "]"}, 52 {Position{1, 16}, tokenEOF, ""}, 53 }) 54} 55 56func TestUnclosedKeyGroup(t *testing.T) { 57 testFlow(t, "[hello world", []token{ 58 {Position{1, 1}, tokenLeftBracket, "["}, 59 {Position{1, 2}, tokenError, "unclosed table key"}, 60 }) 61} 62 63func TestComment(t *testing.T) { 64 testFlow(t, "# blahblah", []token{ 65 {Position{1, 11}, tokenEOF, ""}, 66 }) 67} 68 69func TestKeyGroupComment(t *testing.T) { 70 testFlow(t, "[hello world] # blahblah", []token{ 71 {Position{1, 1}, tokenLeftBracket, "["}, 72 {Position{1, 2}, tokenKeyGroup, "hello world"}, 73 {Position{1, 13}, tokenRightBracket, "]"}, 74 {Position{1, 25}, tokenEOF, ""}, 75 }) 76} 77 78func TestMultipleKeyGroupsComment(t *testing.T) { 79 testFlow(t, "[hello world] # blahblah\n[test]", []token{ 80 {Position{1, 1}, tokenLeftBracket, "["}, 81 {Position{1, 2}, tokenKeyGroup, "hello world"}, 82 {Position{1, 13}, tokenRightBracket, "]"}, 83 {Position{2, 1}, tokenLeftBracket, "["}, 84 {Position{2, 2}, tokenKeyGroup, "test"}, 85 {Position{2, 6}, tokenRightBracket, "]"}, 86 {Position{2, 7}, tokenEOF, ""}, 87 }) 88} 89 90func TestSimpleWindowsCRLF(t *testing.T) { 91 testFlow(t, "a=4\r\nb=2", []token{ 92 {Position{1, 1}, tokenKey, "a"}, 93 {Position{1, 2}, tokenEqual, "="}, 94 {Position{1, 3}, tokenInteger, "4"}, 95 {Position{2, 1}, tokenKey, "b"}, 96 {Position{2, 2}, tokenEqual, "="}, 97 {Position{2, 3}, tokenInteger, "2"}, 98 {Position{2, 4}, tokenEOF, ""}, 99 }) 100} 101 102func TestBasicKey(t *testing.T) { 103 testFlow(t, "hello", []token{ 104 {Position{1, 1}, tokenKey, "hello"}, 105 {Position{1, 6}, tokenEOF, ""}, 106 }) 107} 108 109func TestBasicKeyWithUnderscore(t *testing.T) { 110 testFlow(t, "hello_hello", []token{ 111 {Position{1, 1}, tokenKey, "hello_hello"}, 112 {Position{1, 12}, tokenEOF, ""}, 113 }) 114} 115 116func TestBasicKeyWithDash(t *testing.T) { 117 testFlow(t, "hello-world", []token{ 118 {Position{1, 1}, tokenKey, "hello-world"}, 119 {Position{1, 12}, tokenEOF, ""}, 120 }) 121} 122 123func TestBasicKeyWithUppercaseMix(t *testing.T) { 124 testFlow(t, "helloHELLOHello", []token{ 125 {Position{1, 1}, tokenKey, "helloHELLOHello"}, 126 {Position{1, 16}, tokenEOF, ""}, 127 }) 128} 129 130func TestBasicKeyWithInternationalCharacters(t *testing.T) { 131 testFlow(t, "héllÖ", []token{ 132 {Position{1, 1}, tokenKey, "héllÖ"}, 133 {Position{1, 6}, tokenEOF, ""}, 134 }) 135} 136 137func TestBasicKeyAndEqual(t *testing.T) { 138 testFlow(t, "hello =", []token{ 139 {Position{1, 1}, tokenKey, "hello"}, 140 {Position{1, 7}, tokenEqual, "="}, 141 {Position{1, 8}, tokenEOF, ""}, 142 }) 143} 144 145func TestKeyWithSharpAndEqual(t *testing.T) { 146 testFlow(t, "key#name = 5", []token{ 147 {Position{1, 1}, tokenError, "keys cannot contain # character"}, 148 }) 149} 150 151func TestKeyWithSymbolsAndEqual(t *testing.T) { 152 testFlow(t, "~!@$^&*()_+-`1234567890[]\\|/?><.,;:' = 5", []token{ 153 {Position{1, 1}, tokenError, "keys cannot contain ~ character"}, 154 }) 155} 156 157func TestKeyEqualStringEscape(t *testing.T) { 158 testFlow(t, `foo = "hello\""`, []token{ 159 {Position{1, 1}, tokenKey, "foo"}, 160 {Position{1, 5}, tokenEqual, "="}, 161 {Position{1, 8}, tokenString, "hello\""}, 162 {Position{1, 16}, tokenEOF, ""}, 163 }) 164} 165 166func TestKeyEqualStringUnfinished(t *testing.T) { 167 testFlow(t, `foo = "bar`, []token{ 168 {Position{1, 1}, tokenKey, "foo"}, 169 {Position{1, 5}, tokenEqual, "="}, 170 {Position{1, 8}, tokenError, "unclosed string"}, 171 }) 172} 173 174func TestKeyEqualString(t *testing.T) { 175 testFlow(t, `foo = "bar"`, []token{ 176 {Position{1, 1}, tokenKey, "foo"}, 177 {Position{1, 5}, tokenEqual, "="}, 178 {Position{1, 8}, tokenString, "bar"}, 179 {Position{1, 12}, tokenEOF, ""}, 180 }) 181} 182 183func TestKeyEqualTrue(t *testing.T) { 184 testFlow(t, "foo = true", []token{ 185 {Position{1, 1}, tokenKey, "foo"}, 186 {Position{1, 5}, tokenEqual, "="}, 187 {Position{1, 7}, tokenTrue, "true"}, 188 {Position{1, 11}, tokenEOF, ""}, 189 }) 190} 191 192func TestKeyEqualFalse(t *testing.T) { 193 testFlow(t, "foo = false", []token{ 194 {Position{1, 1}, tokenKey, "foo"}, 195 {Position{1, 5}, tokenEqual, "="}, 196 {Position{1, 7}, tokenFalse, "false"}, 197 {Position{1, 12}, tokenEOF, ""}, 198 }) 199} 200 201func TestArrayNestedString(t *testing.T) { 202 testFlow(t, `a = [ ["hello", "world"] ]`, []token{ 203 {Position{1, 1}, tokenKey, "a"}, 204 {Position{1, 3}, tokenEqual, "="}, 205 {Position{1, 5}, tokenLeftBracket, "["}, 206 {Position{1, 7}, tokenLeftBracket, "["}, 207 {Position{1, 9}, tokenString, "hello"}, 208 {Position{1, 15}, tokenComma, ","}, 209 {Position{1, 18}, tokenString, "world"}, 210 {Position{1, 24}, tokenRightBracket, "]"}, 211 {Position{1, 26}, tokenRightBracket, "]"}, 212 {Position{1, 27}, tokenEOF, ""}, 213 }) 214} 215 216func TestArrayNestedInts(t *testing.T) { 217 testFlow(t, "a = [ [42, 21], [10] ]", []token{ 218 {Position{1, 1}, tokenKey, "a"}, 219 {Position{1, 3}, tokenEqual, "="}, 220 {Position{1, 5}, tokenLeftBracket, "["}, 221 {Position{1, 7}, tokenLeftBracket, "["}, 222 {Position{1, 8}, tokenInteger, "42"}, 223 {Position{1, 10}, tokenComma, ","}, 224 {Position{1, 12}, tokenInteger, "21"}, 225 {Position{1, 14}, tokenRightBracket, "]"}, 226 {Position{1, 15}, tokenComma, ","}, 227 {Position{1, 17}, tokenLeftBracket, "["}, 228 {Position{1, 18}, tokenInteger, "10"}, 229 {Position{1, 20}, tokenRightBracket, "]"}, 230 {Position{1, 22}, tokenRightBracket, "]"}, 231 {Position{1, 23}, tokenEOF, ""}, 232 }) 233} 234 235func TestArrayInts(t *testing.T) { 236 testFlow(t, "a = [ 42, 21, 10, ]", []token{ 237 {Position{1, 1}, tokenKey, "a"}, 238 {Position{1, 3}, tokenEqual, "="}, 239 {Position{1, 5}, tokenLeftBracket, "["}, 240 {Position{1, 7}, tokenInteger, "42"}, 241 {Position{1, 9}, tokenComma, ","}, 242 {Position{1, 11}, tokenInteger, "21"}, 243 {Position{1, 13}, tokenComma, ","}, 244 {Position{1, 15}, tokenInteger, "10"}, 245 {Position{1, 17}, tokenComma, ","}, 246 {Position{1, 19}, tokenRightBracket, "]"}, 247 {Position{1, 20}, tokenEOF, ""}, 248 }) 249} 250 251func TestMultilineArrayComments(t *testing.T) { 252 testFlow(t, "a = [1, # wow\n2, # such items\n3, # so array\n]", []token{ 253 {Position{1, 1}, tokenKey, "a"}, 254 {Position{1, 3}, tokenEqual, "="}, 255 {Position{1, 5}, tokenLeftBracket, "["}, 256 {Position{1, 6}, tokenInteger, "1"}, 257 {Position{1, 7}, tokenComma, ","}, 258 {Position{2, 1}, tokenInteger, "2"}, 259 {Position{2, 2}, tokenComma, ","}, 260 {Position{3, 1}, tokenInteger, "3"}, 261 {Position{3, 2}, tokenComma, ","}, 262 {Position{4, 1}, tokenRightBracket, "]"}, 263 {Position{4, 2}, tokenEOF, ""}, 264 }) 265} 266 267func TestNestedArraysComment(t *testing.T) { 268 toml := ` 269someArray = [ 270# does not work 271["entry1"] 272]` 273 testFlow(t, toml, []token{ 274 {Position{2, 1}, tokenKey, "someArray"}, 275 {Position{2, 11}, tokenEqual, "="}, 276 {Position{2, 13}, tokenLeftBracket, "["}, 277 {Position{4, 1}, tokenLeftBracket, "["}, 278 {Position{4, 3}, tokenString, "entry1"}, 279 {Position{4, 10}, tokenRightBracket, "]"}, 280 {Position{5, 1}, tokenRightBracket, "]"}, 281 {Position{5, 2}, tokenEOF, ""}, 282 }) 283} 284 285func TestKeyEqualArrayBools(t *testing.T) { 286 testFlow(t, "foo = [true, false, true]", []token{ 287 {Position{1, 1}, tokenKey, "foo"}, 288 {Position{1, 5}, tokenEqual, "="}, 289 {Position{1, 7}, tokenLeftBracket, "["}, 290 {Position{1, 8}, tokenTrue, "true"}, 291 {Position{1, 12}, tokenComma, ","}, 292 {Position{1, 14}, tokenFalse, "false"}, 293 {Position{1, 19}, tokenComma, ","}, 294 {Position{1, 21}, tokenTrue, "true"}, 295 {Position{1, 25}, tokenRightBracket, "]"}, 296 {Position{1, 26}, tokenEOF, ""}, 297 }) 298} 299 300func TestKeyEqualArrayBoolsWithComments(t *testing.T) { 301 testFlow(t, "foo = [true, false, true] # YEAH", []token{ 302 {Position{1, 1}, tokenKey, "foo"}, 303 {Position{1, 5}, tokenEqual, "="}, 304 {Position{1, 7}, tokenLeftBracket, "["}, 305 {Position{1, 8}, tokenTrue, "true"}, 306 {Position{1, 12}, tokenComma, ","}, 307 {Position{1, 14}, tokenFalse, "false"}, 308 {Position{1, 19}, tokenComma, ","}, 309 {Position{1, 21}, tokenTrue, "true"}, 310 {Position{1, 25}, tokenRightBracket, "]"}, 311 {Position{1, 33}, tokenEOF, ""}, 312 }) 313} 314 315func TestDateRegexp(t *testing.T) { 316 if dateRegexp.FindString("1979-05-27T07:32:00Z") == "" { 317 t.Error("basic lexing") 318 } 319 if dateRegexp.FindString("1979-05-27T00:32:00-07:00") == "" { 320 t.Error("offset lexing") 321 } 322 if dateRegexp.FindString("1979-05-27T00:32:00.999999-07:00") == "" { 323 t.Error("nano precision lexing") 324 } 325} 326 327func TestKeyEqualDate(t *testing.T) { 328 testFlow(t, "foo = 1979-05-27T07:32:00Z", []token{ 329 {Position{1, 1}, tokenKey, "foo"}, 330 {Position{1, 5}, tokenEqual, "="}, 331 {Position{1, 7}, tokenDate, "1979-05-27T07:32:00Z"}, 332 {Position{1, 27}, tokenEOF, ""}, 333 }) 334 testFlow(t, "foo = 1979-05-27T00:32:00-07:00", []token{ 335 {Position{1, 1}, tokenKey, "foo"}, 336 {Position{1, 5}, tokenEqual, "="}, 337 {Position{1, 7}, tokenDate, "1979-05-27T00:32:00-07:00"}, 338 {Position{1, 32}, tokenEOF, ""}, 339 }) 340 testFlow(t, "foo = 1979-05-27T00:32:00.999999-07:00", []token{ 341 {Position{1, 1}, tokenKey, "foo"}, 342 {Position{1, 5}, tokenEqual, "="}, 343 {Position{1, 7}, tokenDate, "1979-05-27T00:32:00.999999-07:00"}, 344 {Position{1, 39}, tokenEOF, ""}, 345 }) 346} 347 348func TestFloatEndingWithDot(t *testing.T) { 349 testFlow(t, "foo = 42.", []token{ 350 {Position{1, 1}, tokenKey, "foo"}, 351 {Position{1, 5}, tokenEqual, "="}, 352 {Position{1, 7}, tokenError, "float cannot end with a dot"}, 353 }) 354} 355 356func TestFloatWithTwoDots(t *testing.T) { 357 testFlow(t, "foo = 4.2.", []token{ 358 {Position{1, 1}, tokenKey, "foo"}, 359 {Position{1, 5}, tokenEqual, "="}, 360 {Position{1, 7}, tokenError, "cannot have two dots in one float"}, 361 }) 362} 363 364func TestFloatWithExponent1(t *testing.T) { 365 testFlow(t, "a = 5e+22", []token{ 366 {Position{1, 1}, tokenKey, "a"}, 367 {Position{1, 3}, tokenEqual, "="}, 368 {Position{1, 5}, tokenFloat, "5e+22"}, 369 {Position{1, 10}, tokenEOF, ""}, 370 }) 371} 372 373func TestFloatWithExponent2(t *testing.T) { 374 testFlow(t, "a = 5E+22", []token{ 375 {Position{1, 1}, tokenKey, "a"}, 376 {Position{1, 3}, tokenEqual, "="}, 377 {Position{1, 5}, tokenFloat, "5E+22"}, 378 {Position{1, 10}, tokenEOF, ""}, 379 }) 380} 381 382func TestFloatWithExponent3(t *testing.T) { 383 testFlow(t, "a = -5e+22", []token{ 384 {Position{1, 1}, tokenKey, "a"}, 385 {Position{1, 3}, tokenEqual, "="}, 386 {Position{1, 5}, tokenFloat, "-5e+22"}, 387 {Position{1, 11}, tokenEOF, ""}, 388 }) 389} 390 391func TestFloatWithExponent4(t *testing.T) { 392 testFlow(t, "a = -5e-22", []token{ 393 {Position{1, 1}, tokenKey, "a"}, 394 {Position{1, 3}, tokenEqual, "="}, 395 {Position{1, 5}, tokenFloat, "-5e-22"}, 396 {Position{1, 11}, tokenEOF, ""}, 397 }) 398} 399 400func TestFloatWithExponent5(t *testing.T) { 401 testFlow(t, "a = 6.626e-34", []token{ 402 {Position{1, 1}, tokenKey, "a"}, 403 {Position{1, 3}, tokenEqual, "="}, 404 {Position{1, 5}, tokenFloat, "6.626e-34"}, 405 {Position{1, 14}, tokenEOF, ""}, 406 }) 407} 408 409func TestInvalidEsquapeSequence(t *testing.T) { 410 testFlow(t, `foo = "\x"`, []token{ 411 {Position{1, 1}, tokenKey, "foo"}, 412 {Position{1, 5}, tokenEqual, "="}, 413 {Position{1, 8}, tokenError, "invalid escape sequence: \\x"}, 414 }) 415} 416 417func TestNestedArrays(t *testing.T) { 418 testFlow(t, "foo = [[[]]]", []token{ 419 {Position{1, 1}, tokenKey, "foo"}, 420 {Position{1, 5}, tokenEqual, "="}, 421 {Position{1, 7}, tokenLeftBracket, "["}, 422 {Position{1, 8}, tokenLeftBracket, "["}, 423 {Position{1, 9}, tokenLeftBracket, "["}, 424 {Position{1, 10}, tokenRightBracket, "]"}, 425 {Position{1, 11}, tokenRightBracket, "]"}, 426 {Position{1, 12}, tokenRightBracket, "]"}, 427 {Position{1, 13}, tokenEOF, ""}, 428 }) 429} 430 431func TestKeyEqualNumber(t *testing.T) { 432 testFlow(t, "foo = 42", []token{ 433 {Position{1, 1}, tokenKey, "foo"}, 434 {Position{1, 5}, tokenEqual, "="}, 435 {Position{1, 7}, tokenInteger, "42"}, 436 {Position{1, 9}, tokenEOF, ""}, 437 }) 438 439 testFlow(t, "foo = +42", []token{ 440 {Position{1, 1}, tokenKey, "foo"}, 441 {Position{1, 5}, tokenEqual, "="}, 442 {Position{1, 7}, tokenInteger, "+42"}, 443 {Position{1, 10}, tokenEOF, ""}, 444 }) 445 446 testFlow(t, "foo = -42", []token{ 447 {Position{1, 1}, tokenKey, "foo"}, 448 {Position{1, 5}, tokenEqual, "="}, 449 {Position{1, 7}, tokenInteger, "-42"}, 450 {Position{1, 10}, tokenEOF, ""}, 451 }) 452 453 testFlow(t, "foo = 4.2", []token{ 454 {Position{1, 1}, tokenKey, "foo"}, 455 {Position{1, 5}, tokenEqual, "="}, 456 {Position{1, 7}, tokenFloat, "4.2"}, 457 {Position{1, 10}, tokenEOF, ""}, 458 }) 459 460 testFlow(t, "foo = +4.2", []token{ 461 {Position{1, 1}, tokenKey, "foo"}, 462 {Position{1, 5}, tokenEqual, "="}, 463 {Position{1, 7}, tokenFloat, "+4.2"}, 464 {Position{1, 11}, tokenEOF, ""}, 465 }) 466 467 testFlow(t, "foo = -4.2", []token{ 468 {Position{1, 1}, tokenKey, "foo"}, 469 {Position{1, 5}, tokenEqual, "="}, 470 {Position{1, 7}, tokenFloat, "-4.2"}, 471 {Position{1, 11}, tokenEOF, ""}, 472 }) 473 474 testFlow(t, "foo = 1_000", []token{ 475 {Position{1, 1}, tokenKey, "foo"}, 476 {Position{1, 5}, tokenEqual, "="}, 477 {Position{1, 7}, tokenInteger, "1_000"}, 478 {Position{1, 12}, tokenEOF, ""}, 479 }) 480 481 testFlow(t, "foo = 5_349_221", []token{ 482 {Position{1, 1}, tokenKey, "foo"}, 483 {Position{1, 5}, tokenEqual, "="}, 484 {Position{1, 7}, tokenInteger, "5_349_221"}, 485 {Position{1, 16}, tokenEOF, ""}, 486 }) 487 488 testFlow(t, "foo = 1_2_3_4_5", []token{ 489 {Position{1, 1}, tokenKey, "foo"}, 490 {Position{1, 5}, tokenEqual, "="}, 491 {Position{1, 7}, tokenInteger, "1_2_3_4_5"}, 492 {Position{1, 16}, tokenEOF, ""}, 493 }) 494 495 testFlow(t, "flt8 = 9_224_617.445_991_228_313", []token{ 496 {Position{1, 1}, tokenKey, "flt8"}, 497 {Position{1, 6}, tokenEqual, "="}, 498 {Position{1, 8}, tokenFloat, "9_224_617.445_991_228_313"}, 499 {Position{1, 33}, tokenEOF, ""}, 500 }) 501 502 testFlow(t, "foo = +", []token{ 503 {Position{1, 1}, tokenKey, "foo"}, 504 {Position{1, 5}, tokenEqual, "="}, 505 {Position{1, 7}, tokenError, "no digit in that number"}, 506 }) 507} 508 509func TestMultiline(t *testing.T) { 510 testFlow(t, "foo = 42\nbar=21", []token{ 511 {Position{1, 1}, tokenKey, "foo"}, 512 {Position{1, 5}, tokenEqual, "="}, 513 {Position{1, 7}, tokenInteger, "42"}, 514 {Position{2, 1}, tokenKey, "bar"}, 515 {Position{2, 4}, tokenEqual, "="}, 516 {Position{2, 5}, tokenInteger, "21"}, 517 {Position{2, 7}, tokenEOF, ""}, 518 }) 519} 520 521func TestKeyEqualStringUnicodeEscape(t *testing.T) { 522 testFlow(t, `foo = "hello \u2665"`, []token{ 523 {Position{1, 1}, tokenKey, "foo"}, 524 {Position{1, 5}, tokenEqual, "="}, 525 {Position{1, 8}, tokenString, "hello ♥"}, 526 {Position{1, 21}, tokenEOF, ""}, 527 }) 528 testFlow(t, `foo = "hello \U000003B4"`, []token{ 529 {Position{1, 1}, tokenKey, "foo"}, 530 {Position{1, 5}, tokenEqual, "="}, 531 {Position{1, 8}, tokenString, "hello δ"}, 532 {Position{1, 25}, tokenEOF, ""}, 533 }) 534 testFlow(t, `foo = "\uabcd"`, []token{ 535 {Position{1, 1}, tokenKey, "foo"}, 536 {Position{1, 5}, tokenEqual, "="}, 537 {Position{1, 8}, tokenString, "\uabcd"}, 538 {Position{1, 15}, tokenEOF, ""}, 539 }) 540 testFlow(t, `foo = "\uABCD"`, []token{ 541 {Position{1, 1}, tokenKey, "foo"}, 542 {Position{1, 5}, tokenEqual, "="}, 543 {Position{1, 8}, tokenString, "\uABCD"}, 544 {Position{1, 15}, tokenEOF, ""}, 545 }) 546 testFlow(t, `foo = "\U000bcdef"`, []token{ 547 {Position{1, 1}, tokenKey, "foo"}, 548 {Position{1, 5}, tokenEqual, "="}, 549 {Position{1, 8}, tokenString, "\U000bcdef"}, 550 {Position{1, 19}, tokenEOF, ""}, 551 }) 552 testFlow(t, `foo = "\U000BCDEF"`, []token{ 553 {Position{1, 1}, tokenKey, "foo"}, 554 {Position{1, 5}, tokenEqual, "="}, 555 {Position{1, 8}, tokenString, "\U000BCDEF"}, 556 {Position{1, 19}, tokenEOF, ""}, 557 }) 558 testFlow(t, `foo = "\u2"`, []token{ 559 {Position{1, 1}, tokenKey, "foo"}, 560 {Position{1, 5}, tokenEqual, "="}, 561 {Position{1, 8}, tokenError, "unfinished unicode escape"}, 562 }) 563 testFlow(t, `foo = "\U2"`, []token{ 564 {Position{1, 1}, tokenKey, "foo"}, 565 {Position{1, 5}, tokenEqual, "="}, 566 {Position{1, 8}, tokenError, "unfinished unicode escape"}, 567 }) 568} 569 570func TestKeyEqualStringNoEscape(t *testing.T) { 571 testFlow(t, "foo = \"hello \u0002\"", []token{ 572 {Position{1, 1}, tokenKey, "foo"}, 573 {Position{1, 5}, tokenEqual, "="}, 574 {Position{1, 8}, tokenError, "unescaped control character U+0002"}, 575 }) 576 testFlow(t, "foo = \"hello \u001F\"", []token{ 577 {Position{1, 1}, tokenKey, "foo"}, 578 {Position{1, 5}, tokenEqual, "="}, 579 {Position{1, 8}, tokenError, "unescaped control character U+001F"}, 580 }) 581} 582 583func TestLiteralString(t *testing.T) { 584 testFlow(t, `foo = 'C:\Users\nodejs\templates'`, []token{ 585 {Position{1, 1}, tokenKey, "foo"}, 586 {Position{1, 5}, tokenEqual, "="}, 587 {Position{1, 8}, tokenString, `C:\Users\nodejs\templates`}, 588 {Position{1, 34}, tokenEOF, ""}, 589 }) 590 testFlow(t, `foo = '\\ServerX\admin$\system32\'`, []token{ 591 {Position{1, 1}, tokenKey, "foo"}, 592 {Position{1, 5}, tokenEqual, "="}, 593 {Position{1, 8}, tokenString, `\\ServerX\admin$\system32\`}, 594 {Position{1, 35}, tokenEOF, ""}, 595 }) 596 testFlow(t, `foo = 'Tom "Dubs" Preston-Werner'`, []token{ 597 {Position{1, 1}, tokenKey, "foo"}, 598 {Position{1, 5}, tokenEqual, "="}, 599 {Position{1, 8}, tokenString, `Tom "Dubs" Preston-Werner`}, 600 {Position{1, 34}, tokenEOF, ""}, 601 }) 602 testFlow(t, `foo = '<\i\c*\s*>'`, []token{ 603 {Position{1, 1}, tokenKey, "foo"}, 604 {Position{1, 5}, tokenEqual, "="}, 605 {Position{1, 8}, tokenString, `<\i\c*\s*>`}, 606 {Position{1, 19}, tokenEOF, ""}, 607 }) 608 testFlow(t, `foo = 'C:\Users\nodejs\unfinis`, []token{ 609 {Position{1, 1}, tokenKey, "foo"}, 610 {Position{1, 5}, tokenEqual, "="}, 611 {Position{1, 8}, tokenError, "unclosed string"}, 612 }) 613} 614 615func TestMultilineLiteralString(t *testing.T) { 616 testFlow(t, `foo = '''hello 'literal' world'''`, []token{ 617 {Position{1, 1}, tokenKey, "foo"}, 618 {Position{1, 5}, tokenEqual, "="}, 619 {Position{1, 10}, tokenString, `hello 'literal' world`}, 620 {Position{1, 34}, tokenEOF, ""}, 621 }) 622 623 testFlow(t, "foo = '''\nhello\n'literal'\nworld'''", []token{ 624 {Position{1, 1}, tokenKey, "foo"}, 625 {Position{1, 5}, tokenEqual, "="}, 626 {Position{2, 1}, tokenString, "hello\n'literal'\nworld"}, 627 {Position{4, 9}, tokenEOF, ""}, 628 }) 629 testFlow(t, "foo = '''\r\nhello\r\n'literal'\r\nworld'''", []token{ 630 {Position{1, 1}, tokenKey, "foo"}, 631 {Position{1, 5}, tokenEqual, "="}, 632 {Position{2, 1}, tokenString, "hello\r\n'literal'\r\nworld"}, 633 {Position{4, 9}, tokenEOF, ""}, 634 }) 635} 636 637func TestMultilineString(t *testing.T) { 638 testFlow(t, `foo = """hello "literal" world"""`, []token{ 639 {Position{1, 1}, tokenKey, "foo"}, 640 {Position{1, 5}, tokenEqual, "="}, 641 {Position{1, 10}, tokenString, `hello "literal" world`}, 642 {Position{1, 34}, tokenEOF, ""}, 643 }) 644 645 testFlow(t, "foo = \"\"\"\r\nhello\\\r\n\"literal\"\\\nworld\"\"\"", []token{ 646 {Position{1, 1}, tokenKey, "foo"}, 647 {Position{1, 5}, tokenEqual, "="}, 648 {Position{2, 1}, tokenString, "hello\"literal\"world"}, 649 {Position{4, 9}, tokenEOF, ""}, 650 }) 651 652 testFlow(t, "foo = \"\"\"\\\n \\\n \\\n hello\\\nmultiline\\\nworld\"\"\"", []token{ 653 {Position{1, 1}, tokenKey, "foo"}, 654 {Position{1, 5}, tokenEqual, "="}, 655 {Position{1, 10}, tokenString, "hellomultilineworld"}, 656 {Position{6, 9}, tokenEOF, ""}, 657 }) 658 659 testFlow(t, "key2 = \"\"\"\nThe quick brown \\\n\n\n fox jumps over \\\n the lazy dog.\"\"\"", []token{ 660 {Position{1, 1}, tokenKey, "key2"}, 661 {Position{1, 6}, tokenEqual, "="}, 662 {Position{2, 1}, tokenString, "The quick brown fox jumps over the lazy dog."}, 663 {Position{6, 21}, tokenEOF, ""}, 664 }) 665 666 testFlow(t, "key2 = \"\"\"\\\n The quick brown \\\n fox jumps over \\\n the lazy dog.\\\n \"\"\"", []token{ 667 {Position{1, 1}, tokenKey, "key2"}, 668 {Position{1, 6}, tokenEqual, "="}, 669 {Position{1, 11}, tokenString, "The quick brown fox jumps over the lazy dog."}, 670 {Position{5, 11}, tokenEOF, ""}, 671 }) 672 673 testFlow(t, `key2 = "Roses are red\nViolets are blue"`, []token{ 674 {Position{1, 1}, tokenKey, "key2"}, 675 {Position{1, 6}, tokenEqual, "="}, 676 {Position{1, 9}, tokenString, "Roses are red\nViolets are blue"}, 677 {Position{1, 41}, tokenEOF, ""}, 678 }) 679 680 testFlow(t, "key2 = \"\"\"\nRoses are red\nViolets are blue\"\"\"", []token{ 681 {Position{1, 1}, tokenKey, "key2"}, 682 {Position{1, 6}, tokenEqual, "="}, 683 {Position{2, 1}, tokenString, "Roses are red\nViolets are blue"}, 684 {Position{3, 20}, tokenEOF, ""}, 685 }) 686} 687 688func TestUnicodeString(t *testing.T) { 689 testFlow(t, `foo = "hello ♥ world"`, []token{ 690 {Position{1, 1}, tokenKey, "foo"}, 691 {Position{1, 5}, tokenEqual, "="}, 692 {Position{1, 8}, tokenString, "hello ♥ world"}, 693 {Position{1, 22}, tokenEOF, ""}, 694 }) 695} 696func TestEscapeInString(t *testing.T) { 697 testFlow(t, `foo = "\b\f\/"`, []token{ 698 {Position{1, 1}, tokenKey, "foo"}, 699 {Position{1, 5}, tokenEqual, "="}, 700 {Position{1, 8}, tokenString, "\b\f/"}, 701 {Position{1, 15}, tokenEOF, ""}, 702 }) 703} 704 705func TestKeyGroupArray(t *testing.T) { 706 testFlow(t, "[[foo]]", []token{ 707 {Position{1, 1}, tokenDoubleLeftBracket, "[["}, 708 {Position{1, 3}, tokenKeyGroupArray, "foo"}, 709 {Position{1, 6}, tokenDoubleRightBracket, "]]"}, 710 {Position{1, 8}, tokenEOF, ""}, 711 }) 712} 713 714func TestQuotedKey(t *testing.T) { 715 testFlow(t, "\"a b\" = 42", []token{ 716 {Position{1, 1}, tokenKey, "\"a b\""}, 717 {Position{1, 7}, tokenEqual, "="}, 718 {Position{1, 9}, tokenInteger, "42"}, 719 {Position{1, 11}, tokenEOF, ""}, 720 }) 721} 722 723func TestKeyNewline(t *testing.T) { 724 testFlow(t, "a\n= 4", []token{ 725 {Position{1, 1}, tokenError, "keys cannot contain new lines"}, 726 }) 727} 728 729func TestInvalidFloat(t *testing.T) { 730 testFlow(t, "a=7e1_", []token{ 731 {Position{1, 1}, tokenKey, "a"}, 732 {Position{1, 2}, tokenEqual, "="}, 733 {Position{1, 3}, tokenFloat, "7e1_"}, 734 {Position{1, 7}, tokenEOF, ""}, 735 }) 736} 737 738func TestLexUnknownRvalue(t *testing.T) { 739 testFlow(t, `a = !b`, []token{ 740 {Position{1, 1}, tokenKey, "a"}, 741 {Position{1, 3}, tokenEqual, "="}, 742 {Position{1, 5}, tokenError, "no value can start with !"}, 743 }) 744 745 testFlow(t, `a = \b`, []token{ 746 {Position{1, 1}, tokenKey, "a"}, 747 {Position{1, 3}, tokenEqual, "="}, 748 {Position{1, 5}, tokenError, `no value can start with \`}, 749 }) 750} 751