github.com/NeowayLabs/nash@v0.2.2-0.20200127205349-a227041ffd50/scanner/lex_test.go (about) 1 package scanner 2 3 import ( 4 "fmt" 5 "strconv" 6 7 "github.com/madlambda/nash/token" 8 ) 9 import "testing" 10 11 func testTable(name, content string, expected []Token, t *testing.T) { 12 l := Lex(name, content) 13 14 if l == nil { 15 t.Errorf("Failed to initialize lexer") 16 return 17 } 18 19 if l.Tokens == nil { 20 t.Errorf("Failed to initialize lexer") 21 return 22 } 23 24 result := make([]Token, 0, 1024) 25 26 for i := range l.Tokens { 27 result = append(result, i) 28 } 29 30 if len(result) != len(expected) { 31 t.Errorf("Failed to parse commands, length differs %d != %d", 32 len(result), len(expected)) 33 34 fmt.Printf("Parsing content: %s\n", content) 35 36 for _, res := range result { 37 fmt.Printf("parsed: %+v\n", res) 38 } 39 40 fmt.Printf("\n") 41 42 for _, exp := range expected { 43 fmt.Printf("expect: %+v\n", exp) 44 } 45 46 return 47 } 48 49 for i := 0; i < len(expected); i++ { 50 if expected[i].typ != result[i].typ { 51 t.Errorf("'%s (%s)' != '%s (%s)'", expected[i].typ, expected[i].val, result[i].typ, result[i].val) 52 return 53 } 54 55 if expected[i].val != result[i].val { 56 t.Errorf("Parsing '%s':\n\terror: '%s' != '%s'", content, expected[i].val, result[i].val) 57 return 58 } 59 } 60 } 61 62 func TestLexerCommandStringArgs(t *testing.T) { 63 expected := []Token{ 64 {typ: token.Ident, val: "echo"}, 65 {typ: token.Ident, val: "hello"}, 66 {typ: token.Semicolon, val: ";"}, 67 {typ: token.Ident, val: "echo"}, 68 {typ: token.String, val: "hello"}, 69 {typ: token.Semicolon, val: ";"}, 70 {typ: token.EOF}, 71 } 72 73 testTable("test args", `echo hello 74 echo "hello"`, expected, t) 75 } 76 77 func TestLexerTokenToString(t *testing.T) { 78 it := Token{ 79 typ: token.EOF, 80 } 81 82 if it.String() != "EOF" { 83 t.Errorf("Wrong eof string: %s", it.String()) 84 } 85 86 it = Token{ 87 typ: token.Illegal, 88 val: "some error", 89 } 90 91 if it.String() != "ERROR: some error" { 92 t.Errorf("wrong error string: %s", it.String()) 93 } 94 95 it = Token{ 96 typ: token.Ident, 97 val: "echo", 98 } 99 100 if it.String() != "IDENT" { 101 t.Errorf("wrong command name: %s", it.String()) 102 } 103 104 it = Token{ 105 typ: token.Ident, 106 val: "echoooooooooooooooooooooooo", 107 } 108 109 // test if long names are truncated 110 if it.String() != "IDENT" { 111 t.Errorf("wrong command name: %s", it.String()) 112 } 113 } 114 115 func TestLexerShebangOnly(t *testing.T) { 116 expected := []Token{ 117 { 118 typ: token.Comment, 119 val: "#!/bin/nash", 120 }, 121 { 122 typ: token.EOF, 123 }, 124 } 125 126 testTable("testShebangonly", "#!/bin/nash\n", expected, t) 127 } 128 129 func TestLexerSimpleSetEnvAssignment(t *testing.T) { 130 expected := []Token{ 131 {typ: token.SetEnv, val: "setenv"}, 132 {typ: token.Ident, val: "name"}, 133 {typ: token.Semicolon, val: ";"}, 134 {typ: token.EOF}, 135 } 136 137 testTable("testSet", `setenv name`, expected, t) 138 } 139 140 func TestLexerSimpleAssignment(t *testing.T) { 141 expected := []Token{ 142 {typ: token.Ident, val: "test"}, 143 {typ: token.Assign, val: "="}, 144 {typ: token.String, val: "value"}, 145 {typ: token.Semicolon, val: ";"}, 146 {typ: token.EOF}, 147 } 148 149 testTable("testAssignment", `test="value"`, expected, t) 150 testTable("testAssignment spacy", `test = "value"`, expected, t) 151 testTable("testAssignment spacy", `test ="value"`, expected, t) 152 testTable("testAssignment spacy", `test= "value"`, expected, t) 153 testTable("testAssignment spacy", `test ="value"`, expected, t) 154 testTable("testAssignment spacy", `test ="value"`, expected, t) 155 testTable("testAssignment spacy", `test = "value"`, expected, t) 156 157 expected = []Token{ 158 {typ: token.Ident, val: "test"}, 159 {typ: token.Assign, val: "="}, 160 {typ: token.String, val: "value"}, 161 {typ: token.Semicolon, val: ";"}, 162 {typ: token.Ident, val: "other"}, 163 {typ: token.Assign, val: "="}, 164 {typ: token.String, val: "other"}, 165 {typ: token.Semicolon, val: ";"}, 166 {typ: token.EOF}, 167 } 168 169 testTable("test multiple separate assignments", ` 170 test="value" 171 other="other"`, expected, t) 172 173 expected = []Token{ 174 {typ: token.Ident, val: "test"}, 175 {typ: token.Assign, val: "="}, 176 {typ: token.String, val: "value"}, 177 {typ: token.Semicolon, val: ";"}, 178 {typ: token.Ident, val: "other"}, 179 {typ: token.Assign, val: "="}, 180 {typ: token.Variable, val: "$test"}, 181 {typ: token.Semicolon, val: ";"}, 182 {typ: token.Ident, val: "echo"}, 183 {typ: token.Variable, val: "$other"}, 184 {typ: token.Semicolon, val: ";"}, 185 {typ: token.EOF}, 186 } 187 188 testTable("test multiple separate assignments", ` 189 test="value" 190 other=$test 191 echo $other`, expected, t) 192 193 expected = []Token{ 194 {typ: token.Ident, val: "STALI_SRC"}, 195 {typ: token.Assign, val: "="}, 196 {typ: token.Variable, val: "$PWD"}, 197 {typ: token.Plus, val: "+"}, 198 {typ: token.String, val: "/src"}, 199 {typ: token.Semicolon, val: ";"}, 200 {typ: token.EOF}, 201 } 202 203 testTable("test underscore", `STALI_SRC = $PWD + "/src"`, expected, t) 204 205 expected = []Token{ 206 {typ: token.Ident, val: "PROMPT"}, 207 {typ: token.Assign, val: "="}, 208 {typ: token.String, val: "("}, 209 {typ: token.Plus, val: "+"}, 210 {typ: token.Variable, val: "$path"}, 211 {typ: token.Plus, val: "+"}, 212 {typ: token.String, val: ")"}, 213 {typ: token.Plus, val: "+"}, 214 {typ: token.Variable, val: "$PROMPT"}, 215 {typ: token.Semicolon, val: ";"}, 216 {typ: token.EOF}, 217 } 218 219 testTable("test concat with parenthesis", `PROMPT = "("+$path+")"+$PROMPT`, expected, t) 220 221 expected = []Token{ 222 {typ: token.Ident, val: "a"}, 223 {typ: token.LBrack, val: "["}, 224 {typ: token.Number, val: "0"}, 225 {typ: token.RBrack, val: "]"}, 226 {typ: token.Assign, val: "="}, 227 {typ: token.String, val: "test"}, 228 {typ: token.Semicolon, val: ";"}, 229 {typ: token.EOF}, 230 } 231 232 testTable("test index assignment", `a[0] = "test"`, expected, t) 233 234 } 235 236 func TestLexerListAssignment(t *testing.T) { 237 expected := []Token{ 238 {typ: token.Ident, val: "test"}, 239 {typ: token.Assign, val: "="}, 240 {typ: token.LParen, val: "("}, 241 {typ: token.Ident, val: "plan9"}, 242 {typ: token.Ident, val: "from"}, 243 {typ: token.Ident, val: "bell"}, 244 {typ: token.Ident, val: "labs"}, 245 {typ: token.RParen, val: ")"}, 246 {typ: token.Semicolon, val: ";"}, 247 {typ: token.EOF}, 248 } 249 250 testTable("testListAssignment", "test=( plan9 from bell labs )", expected, t) 251 testTable("testListAssignment no space", "test=(plan9 from bell labs)", expected, t) 252 testTable("testListAssignment multiline", `test = ( 253 plan9 254 from 255 bell 256 labs 257 )`, expected, t) 258 259 expected = []Token{ 260 {typ: token.Ident, val: "test"}, 261 {typ: token.Assign, val: "="}, 262 {typ: token.LParen, val: "("}, 263 {typ: token.String, val: "plan9"}, 264 {typ: token.Ident, val: "from"}, 265 {typ: token.String, val: "bell"}, 266 {typ: token.Ident, val: "labs"}, 267 {typ: token.RParen, val: ")"}, 268 {typ: token.Semicolon, val: ";"}, 269 {typ: token.EOF}, 270 } 271 272 testTable("testListAssignment mixed args", `test=( "plan9" from "bell" labs )`, expected, t) 273 testTable("testListAssignment mixed args", `test=("plan9" from "bell" labs)`, expected, t) 274 testTable("testListAssignment mixed args", `test = ( 275 "plan9" 276 from 277 "bell" 278 labs 279 )`, expected, t) 280 281 expected = []Token{ 282 {typ: token.Ident, val: "test"}, 283 {typ: token.Assign, val: "="}, 284 {typ: token.LParen, val: "("}, 285 {typ: token.Variable, val: "$plan9"}, 286 {typ: token.Ident, val: "from"}, 287 {typ: token.Variable, val: "$bell"}, 288 {typ: token.Ident, val: "labs"}, 289 {typ: token.RParen, val: ")"}, 290 {typ: token.Semicolon, val: ";"}, 291 {typ: token.EOF}, 292 } 293 294 testTable("testListAssignment mixed args", `test=( $plan9 from $bell labs )`, expected, t) 295 testTable("testListAssignment mixed args", `test=($plan9 from $bell labs)`, expected, t) 296 testTable("testListAssignment mixed args", `test = ( 297 $plan9 298 from 299 $bell 300 labs 301 )`, expected, t) 302 } 303 304 func TestLexerListOfLists(t *testing.T) { 305 expected := []Token{ 306 {typ: token.Ident, val: "l"}, 307 {typ: token.Assign, val: "="}, 308 {typ: token.LParen, val: "("}, 309 {typ: token.LParen, val: "("}, 310 {typ: token.RParen, val: ")"}, 311 {typ: token.RParen, val: ")"}, 312 {typ: token.Semicolon, val: ";"}, 313 {typ: token.EOF}, 314 } 315 316 testTable("testlistoflists", `l = (())`, expected, t) 317 testTable("testlistoflists", `l = ( 318 () 319 )`, expected, t) 320 321 expected = []Token{ 322 {typ: token.Ident, val: "l"}, 323 {typ: token.Assign, val: "="}, 324 {typ: token.LParen, val: "("}, 325 326 {typ: token.LParen, val: "("}, 327 {typ: token.Ident, val: "plan9"}, 328 {typ: token.Ident, val: "from"}, 329 {typ: token.Ident, val: "bell"}, 330 {typ: token.Ident, val: "labs"}, 331 {typ: token.RParen, val: ")"}, 332 333 {typ: token.LParen, val: "("}, 334 {typ: token.Ident, val: "linux"}, 335 {typ: token.RParen, val: ")"}, 336 337 {typ: token.RParen, val: ")"}, 338 {typ: token.Semicolon, val: ";"}, 339 {typ: token.EOF}, 340 } 341 342 testTable("testlistoflists", `l = ((plan9 from bell labs) (linux))`, expected, t) 343 testTable("testlistoflists", `l = ( 344 (plan9 from bell labs) 345 (linux) 346 )`, expected, t) 347 348 } 349 350 func TestLexerInvalidAssignments(t *testing.T) { 351 expected := []Token{ 352 {typ: token.Ident, val: "test"}, 353 {typ: token.Assign, val: "="}, 354 {typ: token.String, val: "value"}, 355 {typ: token.Ident, val: "other"}, 356 {typ: token.Semicolon, val: ";"}, 357 {typ: token.EOF}, 358 } 359 360 testTable("testInvalidAssignments", `test="value" other`, expected, t) 361 } 362 363 func TestLexerSimpleCommand(t *testing.T) { 364 expected := []Token{ 365 {typ: token.Ident, val: "echo"}, 366 {typ: token.String, val: "hello world"}, 367 {typ: token.Semicolon, val: ";"}, 368 {typ: token.EOF}, 369 } 370 371 testTable("testSimpleCommand", `echo "hello world"`, expected, t) 372 373 expected = []Token{ 374 {typ: token.Ident, val: "echo"}, 375 {typ: token.Arg, val: "rootfs-x86_64"}, 376 {typ: token.Semicolon, val: ";"}, 377 {typ: token.EOF}, 378 } 379 380 testTable("testSimpleCommand", `echo rootfs-x86_64`, expected, t) 381 382 expected = []Token{ 383 {typ: token.Ident, val: "git"}, 384 {typ: token.Ident, val: "clone"}, 385 {typ: token.Arg, val: "--depth=1"}, 386 {typ: token.Arg, val: "http://git.sta.li/toolchain"}, 387 {typ: token.Semicolon, val: ";"}, 388 {typ: token.EOF}, 389 } 390 391 testTable("testSimpleCommand", `git clone --depth=1 http://git.sta.li/toolchain`, expected, t) 392 393 expected = []Token{ 394 {typ: token.Ident, val: "ls"}, 395 {typ: token.Variable, val: "$GOPATH"}, 396 {typ: token.Semicolon, val: ";"}, 397 {typ: token.EOF}, 398 } 399 400 testTable("testSimpleCommand", `ls $GOPATH`, expected, t) 401 402 expected = []Token{ 403 {typ: token.Ident, val: "ls"}, 404 {typ: token.Variable, val: "$GOPATH"}, 405 {typ: token.Plus, val: "+"}, 406 {typ: token.String, val: "/src/github.com"}, 407 {typ: token.Semicolon, val: ";"}, 408 {typ: token.EOF}, 409 } 410 411 testTable("testSimpleCommand", `ls $GOPATH+"/src/github.com"`, expected, t) 412 413 expected = []Token{ 414 {typ: token.Ident, val: "ls"}, 415 {typ: token.String, val: "/src/github.com"}, 416 {typ: token.Plus, val: "+"}, 417 {typ: token.Variable, val: "$GOPATH"}, 418 {typ: token.Semicolon, val: ";"}, 419 {typ: token.EOF}, 420 } 421 422 testTable("testSimpleCommand", `ls "/src/github.com"+$GOPATH`, expected, t) 423 424 expected = []Token{ 425 {typ: token.Ident, val: "ls"}, 426 {typ: token.String, val: "/home/user"}, 427 {typ: token.Plus, val: "+"}, 428 {typ: token.String, val: "/.gvm/pkgsets/global/src"}, 429 {typ: token.Semicolon, val: ";"}, 430 {typ: token.EOF}, 431 } 432 433 testTable("testSimpleCommand", `ls "/home/user" + "/.gvm/pkgsets/global/src"`, expected, t) 434 435 expected = []Token{ 436 {typ: token.Arg, val: "./rkt"}, 437 {typ: token.Semicolon, val: ";"}, // automatic semicolon insertion 438 {typ: token.EOF}, 439 } 440 441 testTable("test local command", "./rkt", expected, t) 442 443 } 444 445 func TestLexerPipe(t *testing.T) { 446 expected := []Token{ 447 {typ: token.Ident, val: "ls"}, 448 {typ: token.Pipe, val: "|"}, 449 {typ: token.Ident, val: "wc"}, 450 {typ: token.Arg, val: "-l"}, 451 {typ: token.Semicolon, val: ";"}, 452 {typ: token.EOF}, 453 } 454 455 testTable("testPipe", `ls | wc -l`, expected, t) 456 457 expected = []Token{ 458 {typ: token.Ident, val: "ls"}, 459 {typ: token.Arg, val: "-l"}, 460 {typ: token.Pipe, val: "|"}, 461 {typ: token.Ident, val: "wc"}, 462 {typ: token.Pipe, val: "|"}, 463 {typ: token.Ident, val: "awk"}, 464 {typ: token.String, val: "{print $1}"}, 465 {typ: token.Semicolon, val: ";"}, 466 {typ: token.EOF}, 467 } 468 469 testTable("testPipe", `ls -l | wc | awk "{print $1}"`, expected, t) 470 471 expected = []Token{ 472 {typ: token.Ident, val: "go"}, 473 {typ: token.Ident, val: "tool"}, 474 {typ: token.Ident, val: "vet"}, 475 {typ: token.Arg, val: "-h"}, 476 {typ: token.Gt, val: ">"}, 477 {typ: token.LBrack, val: "["}, 478 {typ: token.Number, val: "2"}, 479 {typ: token.Assign, val: "="}, 480 {typ: token.Number, val: "1"}, 481 {typ: token.RBrack, val: "]"}, 482 {typ: token.Pipe, val: "|"}, 483 {typ: token.Ident, val: "grep"}, 484 {typ: token.Ident, val: "log"}, 485 {typ: token.Semicolon, val: ";"}, 486 {typ: token.EOF}, 487 } 488 489 testTable("testPipe with redirection", `go tool vet -h >[2=1] | grep log`, expected, t) 490 491 expected = []Token{ 492 {typ: token.Ident, val: "go"}, 493 {typ: token.Ident, val: "tool"}, 494 {typ: token.Ident, val: "vet"}, 495 {typ: token.Arg, val: "-h"}, 496 {typ: token.Gt, val: ">"}, 497 {typ: token.Arg, val: "out.log"}, 498 {typ: token.Pipe, val: "|"}, 499 {typ: token.Ident, val: "grep"}, 500 {typ: token.Ident, val: "log"}, 501 {typ: token.Semicolon, val: ";"}, 502 {typ: token.EOF}, 503 } 504 505 testTable("testPipe with redirection", `go tool vet -h > out.log | grep log`, expected, t) 506 } 507 508 func TestPipeFunctions(t *testing.T) { 509 expected := []Token{ 510 {typ: token.Ident, val: "echo"}, 511 {typ: token.String, val: "some thing"}, 512 {typ: token.Pipe, val: "|"}, 513 {typ: token.Ident, val: "replace"}, 514 {typ: token.LParen, val: "("}, 515 {typ: token.String, val: " "}, 516 {typ: token.Comma, val: ","}, 517 {typ: token.String, val: "|"}, 518 {typ: token.RParen, val: ")"}, 519 {typ: token.Semicolon, val: ";"}, 520 {typ: token.EOF}, 521 } 522 523 testTable("test pipe with function", 524 `echo "some thing" | replace(" ", "|")`, 525 expected, t) 526 } 527 528 func TestLexerUnquoteArg(t *testing.T) { 529 expected := []Token{ 530 {typ: token.Ident, val: "echo"}, 531 {typ: token.Ident, val: "hello"}, 532 {typ: token.Semicolon, val: ";"}, 533 {typ: token.EOF}, 534 } 535 536 testTable("testSimpleCommand", `echo hello`, expected, t) 537 538 expected = []Token{ 539 {typ: token.Ident, val: "echo"}, 540 {typ: token.Arg, val: "hello-world"}, 541 {typ: token.Semicolon, val: ";"}, 542 {typ: token.EOF}, 543 } 544 545 testTable("testSimpleCommand", `echo hello-world`, expected, t) 546 547 expected = []Token{ 548 {typ: token.Ident, val: "echo"}, 549 {typ: token.Comment, val: "#hello-world"}, 550 {typ: token.Semicolon, val: ";"}, 551 {typ: token.EOF}, 552 } 553 554 testTable("testSimpleCommand", `echo #hello-world`, expected, t) 555 } 556 557 func TestLexerDashedCommand(t *testing.T) { 558 expected := []Token{ 559 {typ: token.Arg, val: "google-chrome"}, 560 {typ: token.Semicolon, val: ";"}, 561 {typ: token.EOF}, 562 } 563 564 testTable("testDashedCommand", `google-chrome`, expected, t) 565 } 566 567 func TestLexerPathCommand(t *testing.T) { 568 expected := []Token{ 569 {typ: token.Arg, val: "/bin/echo"}, 570 {typ: token.String, val: "hello world"}, 571 {typ: token.Semicolon, val: ";"}, 572 {typ: token.EOF}, 573 } 574 575 testTable("testPathCommand", `/bin/echo "hello world"`, expected, t) 576 } 577 578 func TestLexerInvalidBlock(t *testing.T) { 579 expected := []Token{ 580 {typ: token.LBrace, val: "{"}, 581 {typ: token.EOF}, 582 } 583 584 testTable("testInvalidBlock", "{", expected, t) 585 } 586 587 func TestLexerQuotedStringNotFinished(t *testing.T) { 588 expected := []Token{ 589 { 590 typ: token.Ident, 591 val: "echo", 592 }, 593 { 594 typ: token.Illegal, 595 val: "testQuotedstringnotfinished:1:17: Quoted string not finished: hello world", 596 }, 597 { 598 typ: token.EOF, 599 }, 600 } 601 602 testTable("testQuotedstringnotfinished", "echo \"hello world", expected, t) 603 } 604 605 func TestLexerVariousCommands(t *testing.T) { 606 content := ` 607 echo "hello world" 608 mount -t proc proc /proc 609 ` 610 611 expected := []Token{ 612 {typ: token.Ident, val: "echo"}, 613 {typ: token.String, val: "hello world"}, 614 {typ: token.Semicolon, val: ";"}, 615 616 {typ: token.Ident, val: "mount"}, 617 {typ: token.Arg, val: "-t"}, 618 {typ: token.Ident, val: "proc"}, 619 {typ: token.Ident, val: "proc"}, 620 {typ: token.Arg, val: "/proc"}, 621 {typ: token.Semicolon, val: ";"}, 622 {typ: token.EOF}, 623 } 624 625 testTable("testVariouscommands", content, expected, t) 626 } 627 628 func TestLexerRfork(t *testing.T) { 629 expected := []Token{ 630 {typ: token.Rfork, val: "rfork"}, 631 {typ: token.Ident, val: "u"}, 632 {typ: token.Semicolon, val: ";"}, 633 {typ: token.EOF}, 634 } 635 636 testTable("testRfork", "rfork u\n", expected, t) 637 638 expected = []Token{ 639 {typ: token.Rfork, val: "rfork"}, 640 {typ: token.Ident, val: "usnm"}, 641 {typ: token.LBrace, val: "{"}, 642 {typ: token.Ident, val: "echo"}, 643 {typ: token.String, val: "inside namespace :)"}, 644 {typ: token.Semicolon, val: ";"}, 645 {typ: token.RBrace, val: "}"}, 646 {typ: token.EOF}, 647 } 648 649 testTable("testRforkWithBlock", ` 650 rfork usnm { 651 echo "inside namespace :)" 652 } 653 `, expected, t) 654 655 } 656 657 func TestLexerSomethingIdontcareanymore(t *testing.T) { 658 // maybe oneliner rfork isnt a good idea 659 expected := []Token{ 660 {typ: token.Rfork, val: "rfork"}, 661 {typ: token.Ident, val: "u"}, 662 {typ: token.LBrace, val: "{"}, 663 {typ: token.Ident, val: "ls"}, 664 {typ: token.RBrace, val: "}"}, 665 {typ: token.EOF}, 666 } 667 668 testTable("test whatever", "rfork u { ls }", expected, t) 669 } 670 671 func TestLexerBuiltinCd(t *testing.T) { 672 expected := []Token{ 673 {typ: token.Ident, val: "cd"}, 674 {typ: token.String, val: "some place"}, 675 {typ: token.Semicolon, val: ";"}, 676 {typ: token.EOF}, 677 } 678 679 testTable("testBuiltinCd", `cd "some place"`, expected, t) 680 681 expected = []Token{ 682 {typ: token.Ident, val: "cd"}, 683 {typ: token.Arg, val: "/proc"}, 684 {typ: token.Semicolon, val: ";"}, 685 {typ: token.EOF}, 686 } 687 688 testTable("testBuiltinCdNoQuote", `cd /proc`, expected, t) 689 690 expected = []Token{ 691 {typ: token.Ident, val: "cd"}, 692 {typ: token.Semicolon, val: ";"}, 693 {typ: token.EOF}, 694 } 695 696 testTable("testBuiltincd home", `cd`, expected, t) 697 698 expected = []Token{ 699 {typ: token.Ident, val: "HOME"}, 700 {typ: token.Assign, val: "="}, 701 {typ: token.String, val: "/"}, 702 {typ: token.Semicolon, val: ";"}, 703 {typ: token.SetEnv, val: "setenv"}, 704 {typ: token.Ident, val: "HOME"}, 705 {typ: token.Semicolon, val: ";"}, 706 {typ: token.Ident, val: "cd"}, 707 {typ: token.Semicolon, val: ";"}, 708 {typ: token.Ident, val: "pwd"}, 709 {typ: token.Semicolon, val: ";"}, 710 {typ: token.EOF}, 711 } 712 713 testTable("testBuiltin cd bug", ` 714 HOME="/" 715 setenv HOME 716 cd 717 pwd 718 `, expected, t) 719 720 expected = []Token{ 721 {typ: token.Ident, val: "cd"}, 722 {typ: token.Variable, val: "$GOPATH"}, 723 {typ: token.Semicolon, val: ";"}, 724 {typ: token.EOF}, 725 } 726 727 testTable("test builtin cd into variable", `cd $GOPATH`, expected, t) 728 729 expected = []Token{ 730 {typ: token.Ident, val: "cd"}, 731 {typ: token.Variable, val: "$GOPATH"}, 732 {typ: token.Plus, val: "+"}, 733 {typ: token.String, val: "/src/github.com"}, 734 {typ: token.Semicolon, val: ";"}, 735 {typ: token.EOF}, 736 } 737 738 testTable("test cd with concat", `cd $GOPATH+"/src/github.com"`, expected, t) 739 } 740 741 func TestLexerRedirectSimple(t *testing.T) { 742 expected := []Token{ 743 {typ: token.Ident, val: "cmd"}, 744 {typ: token.Gt, val: ">"}, 745 {typ: token.Arg, val: "file.out"}, 746 {typ: token.Semicolon, val: ";"}, 747 {typ: token.EOF}, 748 } 749 750 testTable("test simple redirect", "cmd > file.out", expected, t) 751 752 expected = []Token{ 753 {typ: token.Ident, val: "cmd"}, 754 {typ: token.Gt, val: ">"}, 755 {typ: token.String, val: "tcp://localhost:8888"}, 756 {typ: token.Semicolon, val: ";"}, 757 {typ: token.EOF}, 758 } 759 760 testTable("test simple redirect", `cmd > "tcp://localhost:8888"`, expected, t) 761 762 expected = []Token{ 763 {typ: token.Ident, val: "cmd"}, 764 {typ: token.Gt, val: ">"}, 765 {typ: token.String, val: "udp://localhost:8888"}, 766 {typ: token.Semicolon, val: ";"}, 767 {typ: token.EOF}, 768 } 769 770 testTable("test simple redirect", `cmd > "udp://localhost:8888"`, expected, t) 771 772 expected = []Token{ 773 {typ: token.Ident, val: "cmd"}, 774 {typ: token.Gt, val: ">"}, 775 {typ: token.String, val: "unix:///tmp/sock.txt"}, 776 {typ: token.Semicolon, val: ";"}, 777 {typ: token.EOF}, 778 } 779 780 testTable("test simple redirect", `cmd > "unix:///tmp/sock.txt"`, expected, t) 781 782 } 783 784 func TestLexerRedirectMap(t *testing.T) { 785 786 // Suppress stderr output 787 expected := []Token{ 788 {typ: token.Ident, val: "cmd"}, 789 {typ: token.Gt, val: ">"}, 790 {typ: token.LBrack, val: "["}, 791 {typ: token.Number, val: "2"}, 792 {typ: token.Assign, val: "="}, 793 {typ: token.RBrack, val: "]"}, 794 {typ: token.Semicolon, val: ";"}, 795 {typ: token.EOF}, 796 } 797 798 testTable("test suppress stderr", "cmd >[2=]", expected, t) 799 800 // points stderr to stdout 801 expected = []Token{ 802 {typ: token.Ident, val: "cmd"}, 803 {typ: token.Gt, val: ">"}, 804 {typ: token.LBrack, val: "["}, 805 {typ: token.Number, val: "2"}, 806 {typ: token.Assign, val: "="}, 807 {typ: token.Number, val: "1"}, 808 {typ: token.RBrack, val: "]"}, 809 {typ: token.Semicolon, val: ";"}, 810 {typ: token.EOF}, 811 } 812 813 testTable("test stderr=stdout", "cmd >[2=1]", expected, t) 814 } 815 816 func TestLexerRedirectMapToLocation(t *testing.T) { 817 // Suppress stderr output 818 expected := []Token{ 819 {typ: token.Ident, val: "cmd"}, 820 {typ: token.Gt, val: ">"}, 821 {typ: token.LBrack, val: "["}, 822 {typ: token.Number, val: "2"}, 823 {typ: token.Assign, val: "="}, 824 {typ: token.RBrack, val: "]"}, 825 {typ: token.Arg, val: "file.out"}, 826 {typ: token.Semicolon, val: ";"}, 827 {typ: token.EOF}, 828 } 829 830 testTable("test suppress stderr", "cmd >[2=] file.out", expected, t) 831 832 // points stderr to stdout 833 expected = []Token{ 834 {typ: token.Ident, val: "cmd"}, 835 {typ: token.Gt, val: ">"}, 836 {typ: token.LBrack, val: "["}, 837 {typ: token.Number, val: "2"}, 838 {typ: token.Assign, val: "="}, 839 {typ: token.Number, val: "1"}, 840 {typ: token.RBrack, val: "]"}, 841 {typ: token.Arg, val: "file.out"}, 842 {typ: token.Semicolon, val: ";"}, 843 {typ: token.EOF}, 844 } 845 846 testTable("test stderr=stdout", "cmd >[2=1] file.out", expected, t) 847 848 expected = []Token{ 849 {typ: token.Ident, val: "cmd"}, 850 {typ: token.Gt, val: ">"}, 851 {typ: token.LBrack, val: "["}, 852 {typ: token.Number, val: "2"}, 853 {typ: token.RBrack, val: "]"}, 854 {typ: token.Arg, val: "/var/log/service.log"}, 855 {typ: token.Semicolon, val: ";"}, 856 {typ: token.EOF}, 857 } 858 859 testTable("test suppress stderr", `cmd >[2] /var/log/service.log`, expected, t) 860 } 861 862 func TestLexerRedirectMultipleMaps(t *testing.T) { 863 expected := []Token{ 864 {typ: token.Ident, val: "cmd"}, 865 {typ: token.Gt, val: ">"}, 866 {typ: token.LBrack, val: "["}, 867 {typ: token.Number, val: "1"}, 868 {typ: token.RBrack, val: "]"}, 869 {typ: token.Arg, val: "file.out"}, 870 {typ: token.Gt, val: ">"}, 871 {typ: token.LBrack, val: "["}, 872 {typ: token.Number, val: "2"}, 873 {typ: token.RBrack, val: "]"}, 874 {typ: token.Arg, val: "file.err"}, 875 {typ: token.Semicolon, val: ";"}, 876 {typ: token.EOF}, 877 } 878 879 testTable("test suppress stderr", `cmd >[1] file.out >[2] file.err`, expected, t) 880 881 expected = []Token{ 882 {typ: token.Ident, val: "cmd"}, 883 {typ: token.Gt, val: ">"}, 884 {typ: token.LBrack, val: "["}, 885 {typ: token.Number, val: "2"}, 886 {typ: token.Assign, val: "="}, 887 {typ: token.Number, val: "1"}, 888 {typ: token.RBrack, val: "]"}, 889 {typ: token.Gt, val: ">"}, 890 {typ: token.LBrack, val: "["}, 891 {typ: token.Number, val: "1"}, 892 {typ: token.RBrack, val: "]"}, 893 {typ: token.Arg, val: "/var/log/service.log"}, 894 {typ: token.Semicolon, val: ";"}, 895 {typ: token.EOF}, 896 } 897 898 testTable("test suppress stderr", `cmd >[2=1] >[1] /var/log/service.log`, expected, t) 899 900 expected = []Token{ 901 {typ: token.Ident, val: "cmd"}, 902 {typ: token.Gt, val: ">"}, 903 {typ: token.LBrack, val: "["}, 904 {typ: token.Number, val: "1"}, 905 {typ: token.Assign, val: "="}, 906 {typ: token.Number, val: "2"}, 907 {typ: token.RBrack, val: "]"}, 908 {typ: token.Gt, val: ">"}, 909 {typ: token.LBrack, val: "["}, 910 {typ: token.Number, val: "2"}, 911 {typ: token.Assign, val: "="}, 912 {typ: token.RBrack, val: "]"}, 913 {typ: token.Semicolon, val: ";"}, 914 {typ: token.EOF}, 915 } 916 917 testTable("test suppress stderr", `cmd >[1=2] >[2=]`, expected, t) 918 } 919 920 func TestLexerImport(t *testing.T) { 921 expected := []Token{ 922 {typ: token.Import, val: "import"}, 923 {typ: token.Arg, val: "env.sh"}, 924 {typ: token.Semicolon, val: ";"}, 925 {typ: token.EOF}, 926 } 927 928 testTable("test import", `import env.sh`, expected, t) 929 } 930 931 func TestLexerSimpleIf(t *testing.T) { 932 expected := []Token{ 933 {typ: token.If, val: "if"}, 934 {typ: token.String, val: "test"}, 935 {typ: token.Equal, val: "=="}, 936 {typ: token.String, val: "other"}, 937 {typ: token.LBrace, val: "{"}, 938 {typ: token.Ident, val: "rm"}, 939 {typ: token.Arg, val: "-rf"}, 940 {typ: token.Arg, val: "/"}, 941 {typ: token.RBrace, val: "}"}, 942 {typ: token.EOF}, 943 } 944 945 testTable("test simple if", `if "test" == "other" { rm -rf / }`, expected, t) 946 947 expected = []Token{ 948 {typ: token.If, val: "if"}, 949 {typ: token.String, val: "test"}, 950 {typ: token.NotEqual, val: "!="}, 951 {typ: token.Variable, val: "$test"}, 952 {typ: token.LBrace, val: "{"}, 953 {typ: token.Ident, val: "rm"}, 954 {typ: token.Arg, val: "-rf"}, 955 {typ: token.Arg, val: "/"}, 956 {typ: token.Semicolon, val: ";"}, 957 {typ: token.RBrace, val: "}"}, 958 {typ: token.EOF}, 959 } 960 961 testTable("test simple if", `if "test" != $test { 962 rm -rf / 963 }`, expected, t) 964 965 testTable("test simple if", ` 966 967 if "test" != $test { 968 rm -rf / 969 }`, expected, t) 970 } 971 972 func TestLexerIfWithConcat(t *testing.T) { 973 expected := []Token{ 974 {typ: token.If, val: "if"}, 975 {typ: token.Variable, val: "$test"}, 976 {typ: token.Plus, val: "+"}, 977 {typ: token.String, val: "001"}, 978 {typ: token.NotEqual, val: "!="}, 979 {typ: token.String, val: "value001"}, 980 {typ: token.LBrace, val: "{"}, 981 {typ: token.Ident, val: "rm"}, 982 {typ: token.Arg, val: "-rf"}, 983 {typ: token.Arg, val: "/"}, 984 {typ: token.Semicolon, val: ";"}, 985 {typ: token.RBrace, val: "}"}, 986 {typ: token.EOF}, 987 } 988 989 testTable("test if concat", `if $test + "001" != "value001" { 990 rm -rf / 991 }`, expected, t) 992 } 993 994 func TestLexerIfWithFuncInvocation(t *testing.T) { 995 expected := []Token{ 996 {typ: token.If, val: "if"}, 997 {typ: token.Ident, val: "test"}, 998 {typ: token.LParen, val: "("}, 999 {typ: token.String, val: "some val"}, 1000 {typ: token.RParen, val: ")"}, 1001 {typ: token.NotEqual, val: "!="}, 1002 {typ: token.String, val: "value001"}, 1003 {typ: token.LBrace, val: "{"}, 1004 {typ: token.Ident, val: "rm"}, 1005 {typ: token.Arg, val: "-rf"}, 1006 {typ: token.Arg, val: "/"}, 1007 {typ: token.Semicolon, val: ";"}, 1008 {typ: token.RBrace, val: "}"}, 1009 {typ: token.EOF}, 1010 } 1011 1012 testTable("test if concat", `if test("some val") != "value001" { 1013 rm -rf / 1014 }`, expected, t) 1015 } 1016 1017 func TestLexerIfElse(t *testing.T) { 1018 expected := []Token{ 1019 {typ: token.If, val: "if"}, 1020 {typ: token.String, val: "test"}, 1021 {typ: token.Equal, val: "=="}, 1022 {typ: token.String, val: "other"}, 1023 {typ: token.LBrace, val: "{"}, 1024 {typ: token.Ident, val: "rm"}, 1025 {typ: token.Arg, val: "-rf"}, 1026 {typ: token.Arg, val: "/"}, 1027 {typ: token.RBrace, val: "}"}, 1028 {typ: token.Else, val: "else"}, 1029 {typ: token.LBrace, val: "{"}, 1030 {typ: token.Ident, val: "pwd"}, 1031 {typ: token.RBrace, val: "}"}, 1032 {typ: token.EOF}, 1033 } 1034 1035 testTable("test simple if", `if "test" == "other" { rm -rf / } else { pwd }`, expected, t) 1036 } 1037 1038 func TestLexerIfElseIf(t *testing.T) { 1039 expected := []Token{ 1040 {typ: token.If, val: "if"}, 1041 {typ: token.String, val: "test"}, 1042 {typ: token.Equal, val: "=="}, 1043 {typ: token.String, val: "other"}, 1044 {typ: token.LBrace, val: "{"}, 1045 {typ: token.Ident, val: "rm"}, 1046 {typ: token.Arg, val: "-rf"}, 1047 {typ: token.Arg, val: "/"}, 1048 {typ: token.Semicolon, val: ";"}, 1049 {typ: token.RBrace, val: "}"}, 1050 {typ: token.Else, val: "else"}, 1051 {typ: token.If, val: "if"}, 1052 {typ: token.String, val: "test"}, 1053 {typ: token.Equal, val: "=="}, 1054 {typ: token.Variable, val: "$var"}, 1055 {typ: token.LBrace, val: "{"}, 1056 {typ: token.Ident, val: "pwd"}, 1057 {typ: token.Semicolon, val: ";"}, 1058 {typ: token.RBrace, val: "}"}, 1059 {typ: token.Else, val: "else"}, 1060 {typ: token.LBrace, val: "{"}, 1061 {typ: token.Ident, val: "exit"}, 1062 {typ: token.Number, val: "1"}, 1063 {typ: token.Semicolon, val: ";"}, 1064 {typ: token.RBrace, val: "}"}, 1065 {typ: token.EOF}, 1066 } 1067 1068 testTable("test simple if", ` 1069 if "test" == "other" { 1070 rm -rf / 1071 } else if "test" == $var { 1072 pwd 1073 } else { 1074 exit 1 1075 }`, expected, t) 1076 } 1077 1078 func TestLexerFnBasic(t *testing.T) { 1079 expected := []Token{ 1080 {typ: token.Fn, val: "fn"}, 1081 {typ: token.Ident, val: "build"}, 1082 {typ: token.LParen, val: "("}, 1083 {typ: token.RParen, val: ")"}, 1084 {typ: token.LBrace, val: "{"}, 1085 {typ: token.RBrace, val: "}"}, 1086 {typ: token.EOF}, 1087 } 1088 1089 testTable("test empty fn", `fn build() {}`, expected, t) 1090 1091 // lambda 1092 expected = []Token{ 1093 {typ: token.Fn, val: "fn"}, 1094 {typ: token.LParen, val: "("}, 1095 {typ: token.RParen, val: ")"}, 1096 {typ: token.LBrace, val: "{"}, 1097 {typ: token.RBrace, val: "}"}, 1098 {typ: token.EOF}, 1099 } 1100 1101 testTable("test empty fn", `fn () {}`, expected, t) 1102 1103 // IIFE 1104 expected = []Token{ 1105 {typ: token.Fn, val: "fn"}, 1106 {typ: token.LParen, val: "("}, 1107 {typ: token.RParen, val: ")"}, 1108 {typ: token.LBrace, val: "{"}, 1109 {typ: token.RBrace, val: "}"}, 1110 {typ: token.LParen, val: "("}, 1111 {typ: token.RParen, val: ")"}, 1112 {typ: token.Semicolon, val: ";"}, 1113 {typ: token.EOF}, 1114 } 1115 1116 testTable("test empty fn", `fn () {}()`, expected, t) 1117 1118 expected = []Token{ 1119 {typ: token.Fn, val: "fn"}, 1120 {typ: token.Ident, val: "build"}, 1121 {typ: token.LParen, val: "("}, 1122 {typ: token.Ident, val: "image"}, 1123 {typ: token.Comma, val: ","}, 1124 {typ: token.Ident, val: "debug"}, 1125 {typ: token.RParen, val: ")"}, 1126 {typ: token.LBrace, val: "{"}, 1127 {typ: token.RBrace, val: "}"}, 1128 {typ: token.EOF}, 1129 } 1130 1131 testTable("test empty fn with args", `fn build(image, debug) {}`, expected, t) 1132 1133 expected = []Token{ 1134 {typ: token.Fn, val: "fn"}, 1135 {typ: token.Ident, val: "build"}, 1136 {typ: token.LParen, val: "("}, 1137 {typ: token.Ident, val: "image"}, 1138 {typ: token.Comma, val: ","}, 1139 {typ: token.Ident, val: "debug"}, 1140 {typ: token.RParen, val: ")"}, 1141 {typ: token.LBrace, val: "{"}, 1142 {typ: token.Ident, val: "ls"}, 1143 {typ: token.Semicolon, val: ";"}, 1144 {typ: token.Ident, val: "tar"}, 1145 {typ: token.Semicolon, val: ";"}, 1146 {typ: token.RBrace, val: "}"}, 1147 {typ: token.EOF}, 1148 } 1149 1150 testTable("test empty fn with args and body", `fn build(image, debug) { 1151 ls 1152 tar 1153 }`, expected, t) 1154 1155 expected = []Token{ 1156 {typ: token.Fn, val: "fn"}, 1157 {typ: token.Ident, val: "cd"}, 1158 {typ: token.LParen, val: "("}, 1159 {typ: token.Ident, val: "path"}, 1160 {typ: token.RParen, val: ")"}, 1161 {typ: token.LBrace, val: "{"}, 1162 {typ: token.Ident, val: "cd"}, 1163 {typ: token.Variable, val: "$path"}, 1164 {typ: token.Semicolon, val: ";"}, 1165 {typ: token.Ident, val: "PROMPT"}, 1166 {typ: token.Assign, val: "="}, 1167 {typ: token.String, val: "("}, 1168 {typ: token.Plus, val: "+"}, 1169 {typ: token.Variable, val: "$path"}, 1170 {typ: token.Plus, val: "+"}, 1171 {typ: token.String, val: ")"}, 1172 {typ: token.Plus, val: "+"}, 1173 {typ: token.Variable, val: "$PROMPT"}, 1174 {typ: token.Semicolon, val: ";"}, 1175 {typ: token.SetEnv, val: "setenv"}, 1176 {typ: token.Ident, val: "PROMPT"}, 1177 {typ: token.Semicolon, val: ";"}, 1178 {typ: token.RBrace, val: "}"}, 1179 {typ: token.EOF}, 1180 } 1181 1182 testTable("test cd fn with PROMPT update", `fn cd(path) { 1183 cd $path 1184 PROMPT="(" + $path + ")"+$PROMPT 1185 setenv PROMPT 1186 }`, expected, t) 1187 } 1188 1189 func TestLexerFuncall(t *testing.T) { 1190 expected := []Token{ 1191 {typ: token.Ident, val: "build"}, 1192 {typ: token.LParen, val: "("}, 1193 {typ: token.RParen, val: ")"}, 1194 {typ: token.Semicolon, val: ";"}, 1195 {typ: token.EOF}, 1196 } 1197 1198 testTable("test fn invocation", `build()`, expected, t) 1199 1200 expected = []Token{ 1201 {typ: token.Ident, val: "build"}, 1202 {typ: token.LParen, val: "("}, 1203 {typ: token.String, val: "ubuntu"}, 1204 {typ: token.RParen, val: ")"}, 1205 {typ: token.Semicolon, val: ";"}, 1206 {typ: token.EOF}, 1207 } 1208 1209 testTable("test fn invocation", `build("ubuntu")`, expected, t) 1210 1211 expected = []Token{ 1212 {typ: token.Ident, val: "build"}, 1213 {typ: token.LParen, val: "("}, 1214 {typ: token.String, val: "ubuntu"}, 1215 {typ: token.Comma, val: ","}, 1216 {typ: token.Variable, val: "$debug"}, 1217 1218 {typ: token.RParen, val: ")"}, 1219 {typ: token.Semicolon, val: ";"}, 1220 {typ: token.EOF}, 1221 } 1222 1223 testTable("test fn invocation", `build("ubuntu", $debug)`, expected, t) 1224 1225 expected = []Token{ 1226 {typ: token.Ident, val: "build"}, 1227 {typ: token.LParen, val: "("}, 1228 {typ: token.Variable, val: "$debug"}, 1229 1230 {typ: token.RParen, val: ")"}, 1231 {typ: token.Semicolon, val: ";"}, 1232 {typ: token.EOF}, 1233 } 1234 1235 testTable("test fn invocation", `build($debug)`, expected, t) 1236 1237 expected = []Token{ 1238 {typ: token.Ident, val: "a"}, 1239 {typ: token.LParen, val: "("}, 1240 {typ: token.Ident, val: "b"}, 1241 {typ: token.LParen, val: "("}, 1242 {typ: token.RParen, val: ")"}, 1243 {typ: token.RParen, val: ")"}, 1244 {typ: token.Semicolon, val: ";"}, 1245 {typ: token.EOF}, 1246 } 1247 1248 testTable("test fn composition", `a(b())`, expected, t) 1249 1250 expected = []Token{ 1251 {typ: token.Ident, val: "ids_luns"}, 1252 {typ: token.AssignCmd, val: "<="}, 1253 {typ: token.Ident, val: "append"}, 1254 {typ: token.LParen, val: "("}, 1255 {typ: token.Variable, val: "$ids_luns"}, 1256 {typ: token.Comma, val: ","}, 1257 {typ: token.LParen, val: "("}, 1258 {typ: token.Variable, val: "$id"}, 1259 {typ: token.Variable, val: "$lun"}, 1260 {typ: token.RParen, val: ")"}, 1261 {typ: token.RParen, val: ")"}, 1262 {typ: token.Semicolon, val: ";"}, 1263 {typ: token.EOF}, 1264 } 1265 1266 testTable("test katcipis bad mood", `ids_luns <= append($ids_luns, ($id $lun))`, 1267 expected, t) 1268 } 1269 1270 func TestLexerAssignCmdOut(t *testing.T) { 1271 expected := []Token{ 1272 {typ: token.Ident, val: "ipaddr"}, 1273 {typ: token.AssignCmd, val: "<="}, 1274 {typ: token.Ident, val: "someprogram"}, 1275 {typ: token.Semicolon, val: ";"}, 1276 {typ: token.EOF}, 1277 } 1278 1279 testTable("test assignCmdOut", `ipaddr <= someprogram`, expected, t) 1280 } 1281 1282 func TestLexerMultipleAssignCmdOut(t *testing.T) { 1283 expected := []Token{ 1284 {typ: token.Ident, val: "ret"}, 1285 {typ: token.Comma, val: ","}, 1286 {typ: token.Ident, val: "status"}, 1287 {typ: token.AssignCmd, val: "<="}, 1288 {typ: token.Ident, val: "someprogram"}, 1289 {typ: token.Semicolon, val: ";"}, 1290 {typ: token.EOF}, 1291 } 1292 1293 testTable("test multiple return assignCmdOut", `ret, status <= someprogram`, expected, t) 1294 1295 expected = []Token{ 1296 {typ: token.Ident, val: "ret"}, 1297 {typ: token.Comma, val: ","}, 1298 {typ: token.Ident, val: "_"}, 1299 {typ: token.AssignCmd, val: "<="}, 1300 {typ: token.Ident, val: "someprogram"}, 1301 {typ: token.Semicolon, val: ";"}, 1302 {typ: token.EOF}, 1303 } 1304 1305 testTable("test multiple return assignCmdOut", `ret, _ <= someprogram`, expected, t) 1306 1307 expected = []Token{ 1308 {typ: token.Ident, val: "ret"}, 1309 {typ: token.Comma, val: ","}, 1310 {typ: token.Ident, val: "obj"}, 1311 {typ: token.Comma, val: ","}, 1312 {typ: token.Ident, val: "err"}, 1313 {typ: token.AssignCmd, val: "<="}, 1314 {typ: token.Ident, val: "somefn"}, 1315 {typ: token.LParen, val: "("}, 1316 {typ: token.RParen, val: ")"}, 1317 {typ: token.Semicolon, val: ";"}, 1318 {typ: token.EOF}, 1319 } 1320 1321 testTable("test multiple return assignCmdOut", `ret, obj, err <= somefn()`, expected, t) 1322 } 1323 1324 func TestMultipleAssignments(t *testing.T) { 1325 expected := []Token{ 1326 {typ: token.Ident, val: "a"}, 1327 {typ: token.Comma, val: ","}, 1328 {typ: token.Ident, val: "b"}, 1329 {typ: token.Assign, val: "="}, 1330 {typ: token.String, val: "1"}, 1331 {typ: token.Comma, val: ","}, 1332 {typ: token.String, val: "2"}, 1333 {typ: token.Semicolon, val: ";"}, 1334 {typ: token.EOF}, 1335 } 1336 1337 testTable("test multiple assign", `a, b = "1", "2"`, expected, t) 1338 } 1339 1340 func TestLexerBindFn(t *testing.T) { 1341 expected := []Token{ 1342 {typ: token.BindFn, val: "bindfn"}, 1343 {typ: token.Ident, val: "cd"}, 1344 {typ: token.Ident, val: "cd2"}, 1345 {typ: token.Semicolon, val: ";"}, 1346 {typ: token.EOF}, 1347 } 1348 1349 testTable("test bindfn", `bindfn cd cd2`, expected, t) 1350 1351 } 1352 1353 func TestLexerRedirectionNetwork(t *testing.T) { 1354 expected := []Token{ 1355 {typ: token.Ident, val: "echo"}, 1356 {typ: token.String, val: "hello world"}, 1357 {typ: token.Gt, val: ">"}, 1358 {typ: token.LBrack, val: "["}, 1359 {typ: token.Number, val: "1"}, 1360 {typ: token.RBrack, val: "]"}, 1361 {typ: token.String, val: "tcp://localhost:6667"}, 1362 {typ: token.Semicolon, val: ";"}, 1363 {typ: token.EOF}, 1364 } 1365 1366 testTable("test redirection network", `echo "hello world" >[1] "tcp://localhost:6667"`, expected, t) 1367 } 1368 1369 func TestLexerDump(t *testing.T) { 1370 expected := []Token{ 1371 {typ: token.Dump, val: "dump"}, 1372 {typ: token.Semicolon, val: ";"}, 1373 {typ: token.EOF}, 1374 } 1375 1376 testTable("test dump", `dump`, expected, t) 1377 1378 expected = []Token{ 1379 {typ: token.Dump, val: "dump"}, 1380 {typ: token.Ident, val: "out"}, 1381 {typ: token.Semicolon, val: ";"}, 1382 {typ: token.EOF}, 1383 } 1384 1385 testTable("test dump", `dump out`, expected, t) 1386 1387 expected = []Token{ 1388 {typ: token.Dump, val: "dump"}, 1389 {typ: token.Variable, val: "$out"}, 1390 {typ: token.Semicolon, val: ";"}, 1391 {typ: token.EOF}, 1392 } 1393 1394 testTable("test dump", `dump $out`, expected, t) 1395 } 1396 1397 func TestLexerReturn(t *testing.T) { 1398 expected := []Token{ 1399 {typ: token.Return, val: "return"}, 1400 {typ: token.Semicolon, val: ";"}, 1401 {typ: token.EOF}, 1402 } 1403 1404 testTable("test return", "return", expected, t) 1405 1406 expected = []Token{ 1407 {typ: token.Fn, val: "fn"}, 1408 {typ: token.Ident, val: "test"}, 1409 {typ: token.LParen, val: "("}, 1410 {typ: token.RParen, val: ")"}, 1411 {typ: token.LBrace, val: "{"}, 1412 {typ: token.Return, val: "return"}, 1413 {typ: token.RBrace, val: "}"}, 1414 {typ: token.EOF}, 1415 } 1416 1417 testTable("test return", "fn test() { return }", expected, t) 1418 1419 expected = []Token{ 1420 {typ: token.Fn, val: "fn"}, 1421 {typ: token.Ident, val: "test"}, 1422 {typ: token.LParen, val: "("}, 1423 {typ: token.RParen, val: ")"}, 1424 {typ: token.LBrace, val: "{"}, 1425 {typ: token.Return, val: "return"}, 1426 {typ: token.Semicolon, val: ";"}, 1427 {typ: token.RBrace, val: "}"}, 1428 {typ: token.EOF}, 1429 } 1430 1431 testTable("test return", `fn test() { 1432 return 1433 }`, expected, t) 1434 1435 expected = []Token{ 1436 {typ: token.Fn, val: "fn"}, 1437 {typ: token.Ident, val: "test"}, 1438 {typ: token.LParen, val: "("}, 1439 {typ: token.RParen, val: ")"}, 1440 {typ: token.LBrace, val: "{"}, 1441 {typ: token.Return, val: "return"}, 1442 {typ: token.String, val: "some value"}, 1443 {typ: token.RBrace, val: "}"}, 1444 {typ: token.EOF}, 1445 } 1446 1447 testTable("test return", `fn test() { return "some value"}`, expected, t) 1448 1449 expected = []Token{ 1450 {typ: token.Fn, val: "fn"}, 1451 {typ: token.Ident, val: "test"}, 1452 {typ: token.LParen, val: "("}, 1453 {typ: token.RParen, val: ")"}, 1454 {typ: token.LBrace, val: "{"}, 1455 {typ: token.Return, val: "return"}, 1456 {typ: token.String, val: "some value"}, 1457 {typ: token.Semicolon, val: ";"}, 1458 {typ: token.RBrace, val: "}"}, 1459 {typ: token.EOF}, 1460 } 1461 1462 testTable("test return", `fn test() { 1463 return "some value" 1464 }`, expected, t) 1465 1466 expected = []Token{ 1467 {typ: token.Fn, val: "fn"}, 1468 {typ: token.Ident, val: "test"}, 1469 {typ: token.LParen, val: "("}, 1470 {typ: token.RParen, val: ")"}, 1471 {typ: token.LBrace, val: "{"}, 1472 {typ: token.Ident, val: "value"}, 1473 {typ: token.Assign, val: "="}, 1474 {typ: token.String, val: "some value"}, 1475 {typ: token.Semicolon, val: ";"}, 1476 {typ: token.Return, val: "return"}, 1477 {typ: token.Variable, val: "$value"}, 1478 {typ: token.Semicolon, val: ";"}, 1479 {typ: token.RBrace, val: "}"}, 1480 {typ: token.EOF}, 1481 } 1482 1483 testTable("test return", `fn test() { 1484 value = "some value" 1485 return $value 1486 }`, expected, t) 1487 1488 expected = []Token{ 1489 {typ: token.Fn, val: "fn"}, 1490 {typ: token.Ident, val: "test"}, 1491 {typ: token.LParen, val: "("}, 1492 {typ: token.RParen, val: ")"}, 1493 {typ: token.LBrace, val: "{"}, 1494 {typ: token.Return, val: "return"}, 1495 {typ: token.LParen, val: "("}, 1496 {typ: token.String, val: "test"}, 1497 {typ: token.String, val: "test2"}, 1498 {typ: token.RParen, val: ")"}, 1499 {typ: token.Semicolon, val: ";"}, 1500 {typ: token.RBrace, val: "}"}, 1501 {typ: token.EOF}, 1502 } 1503 1504 testTable("test return", `fn test() { 1505 return ("test" "test2") 1506 }`, expected, t) 1507 1508 expected = []Token{ 1509 {typ: token.Fn, val: "fn"}, 1510 {typ: token.Ident, val: "test"}, 1511 {typ: token.LParen, val: "("}, 1512 {typ: token.RParen, val: ")"}, 1513 {typ: token.LBrace, val: "{"}, 1514 {typ: token.Return, val: "return"}, 1515 {typ: token.Variable, val: "$PWD"}, 1516 {typ: token.Semicolon, val: ";"}, 1517 {typ: token.RBrace, val: "}"}, 1518 {typ: token.EOF}, 1519 } 1520 1521 testTable("test return", `fn test() { 1522 return $PWD 1523 }`, expected, t) 1524 } 1525 1526 func TestLexerFor(t *testing.T) { 1527 expected := []Token{ 1528 {typ: token.For, val: "for"}, 1529 {typ: token.LBrace, val: "{"}, 1530 {typ: token.RBrace, val: "}"}, 1531 {typ: token.EOF}, 1532 } 1533 1534 testTable("test inf loop", `for {}`, expected, t) 1535 1536 expected = []Token{ 1537 {typ: token.For, val: "for"}, 1538 {typ: token.Ident, val: "f"}, 1539 {typ: token.Ident, val: "in"}, 1540 {typ: token.Variable, val: "$files"}, 1541 {typ: token.LBrace, val: "{"}, 1542 {typ: token.RBrace, val: "}"}, 1543 {typ: token.EOF}, 1544 } 1545 1546 testTable("test inf loop", `for f in $files {}`, expected, t) 1547 1548 expected = []Token{ 1549 {typ: token.For, val: "for"}, 1550 {typ: token.Ident, val: "f"}, 1551 {typ: token.Ident, val: "in"}, 1552 {typ: token.Ident, val: "getfiles"}, 1553 {typ: token.LParen, val: "("}, 1554 {typ: token.String, val: "/"}, 1555 {typ: token.RParen, val: ")"}, 1556 {typ: token.LBrace, val: "{"}, 1557 {typ: token.RBrace, val: "}"}, 1558 {typ: token.EOF}, 1559 } 1560 1561 testTable("test inf loop", `for f in getfiles("/") {}`, expected, t) 1562 1563 expected = []Token{ 1564 {typ: token.For, val: "for"}, 1565 {typ: token.Ident, val: "f"}, 1566 {typ: token.Ident, val: "in"}, 1567 {typ: token.LParen, val: "("}, 1568 {typ: token.Number, val: "1"}, 1569 {typ: token.Number, val: "2"}, 1570 {typ: token.Number, val: "3"}, 1571 {typ: token.Number, val: "4"}, 1572 {typ: token.Number, val: "5"}, 1573 {typ: token.RParen, val: ")"}, 1574 {typ: token.LBrace, val: "{"}, 1575 {typ: token.RBrace, val: "}"}, 1576 {typ: token.EOF}, 1577 } 1578 1579 testTable("test inf loop", `for f in (1 2 3 4 5) {}`, expected, t) 1580 } 1581 1582 func TestLexerFnAsFirstClass(t *testing.T) { 1583 expected := []Token{ 1584 {typ: token.Fn, val: "fn"}, 1585 {typ: token.Ident, val: "printer"}, 1586 {typ: token.LParen, val: "("}, 1587 {typ: token.Ident, val: "val"}, 1588 {typ: token.RParen, val: ")"}, 1589 {typ: token.LBrace, val: "{"}, 1590 {typ: token.Ident, val: "echo"}, 1591 {typ: token.Arg, val: "-n"}, 1592 {typ: token.Variable, val: "$val"}, 1593 {typ: token.Semicolon, val: ";"}, 1594 {typ: token.RBrace, val: "}"}, 1595 {typ: token.Fn, val: "fn"}, 1596 {typ: token.Ident, val: "success"}, 1597 {typ: token.LParen, val: "("}, 1598 {typ: token.Ident, val: "print"}, 1599 {typ: token.Comma, val: ","}, 1600 {typ: token.Ident, val: "val"}, 1601 {typ: token.RParen, val: ")"}, 1602 {typ: token.LBrace, val: "{"}, 1603 {typ: token.Variable, val: "$print"}, 1604 {typ: token.LParen, val: "("}, 1605 {typ: token.String, val: "[SUCCESS] "}, 1606 {typ: token.Plus, val: "+"}, 1607 {typ: token.Variable, val: "$val"}, 1608 {typ: token.RParen, val: ")"}, 1609 {typ: token.Semicolon, val: ";"}, 1610 {typ: token.RBrace, val: "}"}, 1611 {typ: token.Ident, val: "success"}, 1612 {typ: token.LParen, val: "("}, 1613 {typ: token.Variable, val: "$printer"}, 1614 {typ: token.Comma, val: ","}, 1615 {typ: token.String, val: "Command executed!"}, 1616 {typ: token.RParen, val: ")"}, 1617 {typ: token.Semicolon, val: ";"}, 1618 1619 {typ: token.EOF}, 1620 } 1621 1622 testTable("test fn as first class", ` 1623 fn printer(val) { 1624 echo -n $val 1625 } 1626 1627 fn success(print, val) { 1628 $print("[SUCCESS] " + $val) 1629 } 1630 1631 success($printer, "Command executed!") 1632 `, expected, t) 1633 } 1634 1635 func TestLexerListIndexing(t *testing.T) { 1636 expected := []Token{ 1637 {typ: token.Ident, val: "cmd"}, 1638 {typ: token.Assign, val: "="}, 1639 {typ: token.Variable, val: "$commands"}, 1640 {typ: token.LBrack, val: "["}, 1641 {typ: token.Number, val: "0"}, 1642 {typ: token.RBrack, val: "]"}, 1643 {typ: token.Semicolon, val: ";"}, 1644 {typ: token.EOF}, 1645 } 1646 1647 for i := 0; i < 1000; i++ { 1648 expected[4] = Token{ 1649 typ: token.Number, 1650 val: strconv.Itoa(i), 1651 } 1652 1653 testTable("test variable indexing", `cmd = $commands[`+strconv.Itoa(i)+`]`, expected, t) 1654 } 1655 1656 expected = []Token{ 1657 {typ: token.Ident, val: "cmd"}, 1658 {typ: token.Assign, val: "="}, 1659 {typ: token.Variable, val: "$commands"}, 1660 {typ: token.LBrack, val: "["}, 1661 {typ: token.Arg, val: "a"}, 1662 {typ: token.RBrack, val: "]"}, 1663 {typ: token.Semicolon, val: ";"}, 1664 {typ: token.EOF}, 1665 } 1666 1667 testTable("test invalid number", `cmd = $commands[a]`, expected, t) 1668 1669 expected = []Token{ 1670 {typ: token.Ident, val: "cmd"}, 1671 {typ: token.Assign, val: "="}, 1672 {typ: token.Variable, val: "$commands"}, 1673 {typ: token.LBrack, val: "["}, 1674 {typ: token.RBrack, val: "]"}, 1675 {typ: token.Semicolon, val: ";"}, 1676 {typ: token.EOF}, 1677 } 1678 1679 testTable("test invalid number", `cmd = $commands[]`, expected, t) 1680 1681 expected = []Token{ 1682 {typ: token.Ident, val: "echo"}, 1683 {typ: token.Ident, val: "test"}, 1684 {typ: token.Variable, val: "$names"}, 1685 {typ: token.LBrack, val: "["}, 1686 {typ: token.Number, val: "666"}, 1687 {typ: token.RBrack, val: "]"}, 1688 {typ: token.Semicolon, val: ";"}, 1689 {typ: token.EOF}, 1690 } 1691 1692 testTable("test variable index on commands", `echo test $names[666]`, expected, t) 1693 1694 expected = []Token{ 1695 {typ: token.If, val: "if"}, 1696 {typ: token.Variable, val: "$crazies"}, 1697 {typ: token.LBrack, val: "["}, 1698 {typ: token.Number, val: "0"}, 1699 {typ: token.RBrack, val: "]"}, 1700 {typ: token.Equal, val: "=="}, 1701 {typ: token.String, val: "patito"}, 1702 {typ: token.LBrace, val: "{"}, 1703 {typ: token.Ident, val: "echo"}, 1704 {typ: token.String, val: ":D"}, 1705 {typ: token.RBrace, val: "}"}, 1706 {typ: token.EOF}, 1707 } 1708 1709 testTable("test if with indexing", `if $crazies[0] == "patito" { echo ":D" }`, expected, t) 1710 } 1711 1712 func TestLexerMultilineCmdExecution(t *testing.T) { 1713 expected := []Token{ 1714 {typ: token.LParen, val: "("}, 1715 {typ: token.RParen, val: ")"}, 1716 {typ: token.Semicolon, val: ";"}, 1717 {typ: token.EOF}, 1718 } 1719 1720 testTable("multiline", `()`, expected, t) 1721 1722 expected = []Token{ 1723 {typ: token.LParen, val: "("}, 1724 {typ: token.Ident, val: "echo"}, 1725 {typ: token.RParen, val: ")"}, 1726 {typ: token.Semicolon, val: ";"}, 1727 {typ: token.EOF}, 1728 } 1729 1730 testTable("multiline", `(echo)`, expected, t) 1731 1732 expected = []Token{ 1733 {typ: token.LParen, val: "("}, 1734 {typ: token.Ident, val: "echo"}, 1735 {typ: token.Ident, val: "AAAAA"}, 1736 {typ: token.Ident, val: "AAAAA"}, 1737 {typ: token.Ident, val: "AAAAA"}, 1738 {typ: token.Ident, val: "AAAAA"}, 1739 {typ: token.Ident, val: "AAAAA"}, 1740 {typ: token.Ident, val: "AAAAA"}, 1741 {typ: token.Ident, val: "BBBBB"}, 1742 {typ: token.Ident, val: "BBBBB"}, 1743 {typ: token.RParen, val: ")"}, 1744 {typ: token.Semicolon, val: ";"}, 1745 {typ: token.EOF}, 1746 } 1747 1748 testTable("test multiline cmd execution", `(echo AAAAA AAAAA 1749 AAAAA AAAAA 1750 AAAAA AAAAA 1751 BBBBB BBBBB)`, expected, t) 1752 } 1753 1754 func TestLexerMultilineCmdAssign(t *testing.T) { 1755 expected := []Token{ 1756 {typ: token.Ident, val: "some"}, 1757 {typ: token.AssignCmd, val: "<="}, 1758 {typ: token.LParen, val: "("}, 1759 {typ: token.RParen, val: ")"}, 1760 {typ: token.Semicolon, val: ";"}, 1761 {typ: token.EOF}, 1762 } 1763 1764 testTable("multiline", `some <= ()`, expected, t) 1765 1766 expected = []Token{ 1767 {typ: token.Ident, val: "some"}, 1768 {typ: token.AssignCmd, val: "<="}, 1769 {typ: token.LParen, val: "("}, 1770 {typ: token.Ident, val: "echo"}, 1771 {typ: token.RParen, val: ")"}, 1772 {typ: token.Semicolon, val: ";"}, 1773 {typ: token.EOF}, 1774 } 1775 1776 testTable("multiline", `some <= (echo)`, expected, t) 1777 1778 expected = []Token{ 1779 {typ: token.Ident, val: "some"}, 1780 {typ: token.AssignCmd, val: "<="}, 1781 {typ: token.LParen, val: "("}, 1782 {typ: token.Ident, val: "echo"}, 1783 {typ: token.Ident, val: "AAAAA"}, 1784 {typ: token.Ident, val: "AAAAA"}, 1785 {typ: token.Ident, val: "AAAAA"}, 1786 {typ: token.Ident, val: "AAAAA"}, 1787 {typ: token.Ident, val: "AAAAA"}, 1788 {typ: token.Ident, val: "AAAAA"}, 1789 {typ: token.Ident, val: "BBBBB"}, 1790 {typ: token.Ident, val: "BBBBB"}, 1791 {typ: token.RParen, val: ")"}, 1792 {typ: token.Semicolon, val: ";"}, 1793 {typ: token.EOF}, 1794 } 1795 1796 testTable("multiline", `some <= (echo AAAAA AAAAA 1797 AAAAA AAAAA 1798 AAAAA AAAAA 1799 BBBBB BBBBB)`, expected, t) 1800 1801 testTable("multiline", `some <= ( 1802 echo AAAAA AAAAA 1803 AAAAA AAAAA 1804 AAAAA AAAAA 1805 BBBBB BBBBB 1806 )`, expected, t) 1807 } 1808 1809 func TestLexerCommandDelimiter(t *testing.T) { 1810 expected := []Token{ 1811 {typ: token.Ident, val: "echo"}, 1812 {typ: token.String, val: "hello"}, 1813 {typ: token.Semicolon, val: ";"}, 1814 {typ: token.Ident, val: "echo"}, 1815 {typ: token.String, val: "world"}, 1816 {typ: token.Semicolon, val: ";"}, 1817 {typ: token.EOF}, 1818 } 1819 1820 testTable("semicolons to separate commands", 1821 `echo "hello"; echo "world"`, expected, t) 1822 } 1823 1824 func TestLexerLongAssignment(t *testing.T) { 1825 expected := []Token{ 1826 {typ: token.Ident, val: "grpid"}, 1827 {typ: token.AssignCmd, val: "<="}, 1828 {typ: token.LParen, val: "("}, 1829 {typ: token.Ident, val: "aws"}, 1830 {typ: token.Ident, val: "ec2"}, 1831 {typ: token.Arg, val: "create-security-group"}, 1832 {typ: token.Arg, val: "--group-name"}, 1833 {typ: token.Variable, val: "$name"}, 1834 {typ: token.Arg, val: "--description"}, 1835 {typ: token.Variable, val: "$desc"}, 1836 {typ: token.Variable, val: "$vpcarg"}, 1837 {typ: token.Pipe, val: "|"}, 1838 {typ: token.Ident, val: "jq"}, 1839 {typ: token.String, val: ".GroupId"}, 1840 {typ: token.Pipe, val: "|"}, 1841 {typ: token.Ident, val: "xargs"}, 1842 {typ: token.Ident, val: "echo"}, 1843 {typ: token.Arg, val: "-n"}, 1844 {typ: token.RParen, val: ")"}, 1845 {typ: token.Semicolon, val: ";"}, 1846 {typ: token.EOF}, 1847 } 1848 1849 testTable("test xxx", `grpid <= ( 1850 aws ec2 create-security-group 1851 --group-name $name 1852 --description $desc 1853 $vpcarg | 1854 jq ".GroupId" | 1855 xargs echo -n)`, expected, t) 1856 } 1857 1858 func TestLexerVarArgs(t *testing.T) { 1859 expected := []Token{ 1860 {typ: token.Ident, val: "println"}, 1861 {typ: token.LParen, val: "("}, 1862 {typ: token.Ident, val: "fmt"}, 1863 {typ: token.Comma, val: ","}, 1864 {typ: token.Ident, val: "args"}, 1865 {typ: token.Dotdotdot, val: "..."}, 1866 {typ: token.RParen, val: ")"}, 1867 {typ: token.LBrace, val: "{"}, 1868 {typ: token.Ident, val: "print"}, 1869 {typ: token.LParen, val: "("}, 1870 {typ: token.Variable, val: "$fmt"}, 1871 {typ: token.Comma, val: ","}, 1872 {typ: token.Variable, val: "$args"}, 1873 {typ: token.Dotdotdot, val: "..."}, 1874 {typ: token.RParen, val: ")"}, 1875 {typ: token.Semicolon, val: ";"}, 1876 {typ: token.RBrace, val: "}"}, 1877 {typ: token.EOF}, 1878 } 1879 1880 testTable("test var args", `println(fmt, args...) { 1881 print($fmt, $args...) 1882 }`, expected, t) 1883 testTable("test var args", `println(fmt, args ...) { 1884 print($fmt, $args...) 1885 }`, expected, t) 1886 1887 expected = []Token{ 1888 {typ: token.Ident, val: "print"}, 1889 {typ: token.LParen, val: "("}, 1890 {typ: token.String, val: "%s:%s:%s"}, 1891 {typ: token.Comma, val: ","}, 1892 {typ: token.LParen, val: "("}, 1893 {typ: token.String, val: "a"}, 1894 {typ: token.String, val: "b"}, 1895 {typ: token.String, val: "c"}, 1896 {typ: token.RParen, val: ")"}, 1897 {typ: token.Dotdotdot, val: "..."}, 1898 {typ: token.RParen, val: ")"}, 1899 {typ: token.Semicolon, val: ";"}, 1900 {typ: token.EOF}, 1901 } 1902 1903 testTable("test literal expansion", `print("%s:%s:%s", ("a" "b" "c")...)`, 1904 expected, t) 1905 testTable("test literal expansion", `print("%s:%s:%s", ("a" "b" "c") ...)`, 1906 expected, t) 1907 } 1908 1909 func TestLexerVar(t *testing.T) { 1910 expected := []Token{ 1911 {typ: token.Var, val: "var"}, 1912 {typ: token.Ident, val: "a"}, 1913 {typ: token.Assign, val: "="}, 1914 {typ: token.String, val: "hello world"}, 1915 {typ: token.Semicolon, val: ";"}, 1916 {typ: token.EOF}, 1917 } 1918 1919 testTable("test simple var decl", `var a = "hello world"`, expected, t) 1920 }