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