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