github.com/NeowayLabs/nash@v0.2.2-0.20200127205349-a227041ffd50/parser/parse.go (about) 1 package parser 2 3 import ( 4 "fmt" 5 "runtime" 6 7 "strconv" 8 9 "github.com/madlambda/nash/ast" 10 "github.com/madlambda/nash/errors" 11 "github.com/madlambda/nash/scanner" 12 "github.com/madlambda/nash/token" 13 ) 14 15 type ( 16 // Parser parses an nash file 17 Parser struct { 18 name string // filename or name of the buffer 19 content string 20 l *scanner.Lexer 21 tok *scanner.Token // token saved for lookahead 22 openblocks int 23 24 insidePipe bool 25 26 keywordParsers map[token.Token]parserFn 27 } 28 29 parserFn func(tok scanner.Token) (ast.Node, error) 30 31 exprConfig struct { 32 allowArg bool 33 allowVariadic bool 34 allowFuncall bool 35 allowConcat bool 36 } 37 ) 38 39 // NewParser creates a new parser 40 func NewParser(name, content string) *Parser { 41 p := &Parser{ 42 name: name, 43 content: content, 44 l: scanner.Lex(name, content), 45 } 46 47 p.keywordParsers = map[token.Token]parserFn{ 48 token.For: p.parseFor, 49 token.If: p.parseIf, 50 token.Fn: p.parseFnDecl, 51 token.Var: p.parseVar, 52 token.Return: p.parseReturn, 53 token.Import: p.parseImport, 54 token.SetEnv: p.parseSetenv, 55 token.Rfork: p.parseRfork, 56 token.BindFn: p.parseBindFn, 57 token.Comment: p.parseComment, 58 token.Illegal: p.parseError, 59 } 60 61 return p 62 } 63 64 // Parse starts the parsing. 65 func (p *Parser) Parse() (tr *ast.Tree, err error) { 66 var root *ast.BlockNode 67 68 defer func() { 69 if r := recover(); r != nil { 70 if _, ok := r.(runtime.Error); ok { 71 panic(r) 72 } 73 74 err = r.(error) 75 } 76 }() 77 78 root, err = p.parseBlock(1, 0) 79 80 if err != nil { 81 return nil, err 82 } 83 84 tr = ast.NewTree(p.name) 85 tr.Root = root 86 87 return tr, nil 88 } 89 90 // next returns the next item from lookahead buffer if not empty or 91 // from the Lexer 92 func (p *Parser) next() scanner.Token { 93 if p.tok != nil { 94 t := p.tok 95 p.tok = nil 96 return *t 97 } 98 99 tok := <-p.l.Tokens 100 101 if tok.Type() == token.Illegal { 102 panic(errors.NewError(tok.Value())) 103 } 104 105 return tok 106 } 107 108 // backup puts the item into the lookahead buffer 109 func (p *Parser) backup(it scanner.Token) error { 110 if p.tok != nil { 111 panic(errors.NewError("only one slot for backup/lookahead: %s", it)) 112 } 113 114 p.tok = &it 115 116 return nil 117 } 118 119 // ignores the next item 120 func (p *Parser) ignore() { 121 if p.tok != nil { 122 p.tok = nil 123 } else { 124 <-p.l.Tokens 125 } 126 } 127 128 // peek gets but do not discards the next item (lookahead) 129 func (p *Parser) peek() scanner.Token { 130 i := p.next() 131 p.tok = &i 132 return i 133 } 134 135 func (p *Parser) parseBlock(lineStart, columnStart int) (*ast.BlockNode, error) { 136 ln := ast.NewBlockNode(token.NewFileInfo(lineStart, columnStart)) 137 138 for { 139 it := p.peek() 140 141 switch it.Type() { 142 case token.EOF: 143 goto finish 144 case token.LBrace: 145 p.ignore() 146 147 return nil, newParserError(it, p.name, 148 "Unexpected '{'") 149 case token.RBrace: 150 p.ignore() 151 152 if p.openblocks <= 0 { 153 return nil, newParserError(it, p.name, 154 "No block open for close") 155 } 156 157 p.openblocks-- 158 return ln, nil 159 default: 160 n, err := p.parseStatement() 161 162 if err != nil { 163 return nil, err 164 } 165 166 ln.Push(n) 167 } 168 } 169 170 finish: 171 if p.openblocks != 0 { 172 return nil, errors.NewUnfinishedBlockError(p.name, p.peek()) 173 } 174 175 return ln, nil 176 } 177 178 func (p *Parser) parseStatement() (ast.Node, error) { 179 it := p.next() 180 next := p.peek() 181 182 if fn, ok := p.keywordParsers[it.Type()]; ok { 183 return fn(it) 184 } 185 186 // statement starting with ident: 187 // - fn call 188 // - variable assignment 189 // - variable exec assignment 190 // - Command 191 192 if isFuncall(it.Type(), next.Type()) { 193 return p.parseFnInv(it, true) 194 } 195 196 if it.Type() == token.Ident { 197 if isAssignment(next.Type()) { 198 return p.parseAssignment(it) 199 } 200 201 return p.parseCommand(it) 202 } else if it.Type() == token.Arg { 203 return p.parseCommand(it) 204 } 205 206 // statement starting with '(' 207 // -multiline command (echo hello) 208 if it.Type() == token.LParen { 209 return p.parseCommand(it) 210 } 211 212 return nil, newParserError(it, p.name, "Unexpected token parsing statement '%+v'", it) 213 } 214 215 func (p *Parser) parseIndexing() (ast.Expr, error) { 216 it := p.next() 217 218 if it.Type() != token.Number && it.Type() != token.Variable { 219 return nil, newParserError(it, p.name, 220 "Expected number or variable in index. Found %v", it) 221 } 222 223 var ( 224 index ast.Expr 225 err error 226 ) 227 228 if it.Type() == token.Number { 229 // only supports base10 230 intval, err := strconv.Atoi(it.Value()) 231 232 if err != nil { 233 return nil, err 234 } 235 236 index = ast.NewIntExpr(it.FileInfo, intval) 237 } else { 238 index, err = p.parseVariable(&it, false) 239 240 if err != nil { 241 return nil, err 242 } 243 } 244 245 it = p.next() 246 247 if it.Type() != token.RBrack { 248 return nil, newParserError(it, p.name, 249 "Unexpected token %v. Expecting ']'", it) 250 } 251 252 return index, nil 253 } 254 255 func (p *Parser) parseVariable(tok *scanner.Token, allowVararg bool) (ast.Expr, error) { 256 var it scanner.Token 257 258 if tok == nil { 259 it = p.next() 260 } else { 261 it = *tok 262 } 263 264 if it.Type() != token.Variable { 265 return nil, newParserError(it, p.name, 266 "Unexpected token %v. Expected VARIABLE", it) 267 } 268 269 variadicErr := func(tok scanner.Token) (ast.Node, error) { 270 return nil, newParserError(it, p.name, 271 "Unexpected token '...'. Varargs allowed only in fn call and fn decl") 272 } 273 274 varTok := it 275 it = p.peek() 276 if it.Type() == token.LBrack { 277 variable := ast.NewVarExpr(varTok.FileInfo, varTok.Value()) 278 p.ignore() 279 index, err := p.parseIndexing() 280 if err != nil { 281 return nil, err 282 } 283 284 isVariadic := p.peek().Type() == token.Dotdotdot 285 if isVariadic && !allowVararg { 286 return variadicErr(p.peek()) 287 } 288 indexedVar := ast.NewIndexVariadicExpr(variable.FileInfo, variable, index, isVariadic) 289 if isVariadic { 290 p.ignore() 291 } 292 return indexedVar, nil 293 } 294 295 isVariadic := p.peek().Type() == token.Dotdotdot 296 if isVariadic { 297 if !allowVararg { 298 return variadicErr(p.peek()) 299 } 300 p.ignore() 301 } 302 303 return ast.NewVarVariadicExpr(varTok.FileInfo, varTok.Value(), isVariadic), nil 304 } 305 306 func (p *Parser) parsePipe(first *ast.CommandNode) (ast.Node, error) { 307 it := p.next() 308 309 n := ast.NewPipeNode(it.FileInfo, first.IsMulti()) 310 first.SetMulti(false) 311 312 n.AddCmd(first) 313 314 for it = p.peek(); it.Type() == token.Ident || it.Type() == token.Arg; it = p.peek() { 315 p.next() 316 cmd, err := p.parseCommand(it) 317 318 if err != nil { 319 return nil, err 320 } 321 322 n.AddCmd(cmd.(*ast.CommandNode)) 323 324 if !p.insidePipe { 325 break 326 } 327 } 328 329 if n.IsMulti() { 330 it = p.peek() 331 if it.Type() != token.RParen { 332 if it.Type() == token.EOF { 333 return nil, errors.NewUnfinishedCmdError(p.name, it) 334 } 335 336 return nil, newParserError(it, p.name, "Unexpected symbol '%s'", it) 337 } 338 339 p.ignore() 340 } 341 342 it = p.peek() 343 344 if it.Type() == token.RBrace { 345 return n, nil 346 } 347 348 if it.Type() != token.Semicolon { 349 return nil, newParserError(it, p.name, "Unexpected symbol %s", it) 350 } 351 352 p.ignore() 353 354 return n, nil 355 } 356 357 func (p *Parser) parseCommand(it scanner.Token) (ast.Node, error) { 358 isMulti := false 359 360 if it.Type() == token.LParen { 361 // multiline command 362 isMulti = true 363 364 it = p.next() 365 } 366 367 if it.Type() != token.Ident && it.Type() != token.Arg { 368 if isMulti && it.Type() == token.EOF { 369 return nil, errors.NewUnfinishedCmdError(p.name, it) 370 } 371 372 return nil, newParserError(it, p.name, "Unexpected token %v. Expecting IDENT or ARG", it) 373 } 374 375 n := ast.NewCommandNode(it.FileInfo, it.Value(), isMulti) 376 377 cmdLoop: 378 for { 379 it = p.peek() 380 381 switch typ := it.Type(); { 382 case typ == token.RBrace: 383 if p.openblocks > 0 { 384 if p.insidePipe { 385 p.insidePipe = false 386 } 387 388 return n, nil 389 } 390 391 break cmdLoop 392 case isValidArgument(it): 393 arg, err := p.getArgument(nil, exprConfig{ 394 allowConcat: true, 395 allowArg: true, 396 allowVariadic: true, 397 allowFuncall: false, 398 }) 399 400 if err != nil { 401 return nil, err 402 } 403 404 n.AddArg(arg) 405 case typ == token.Plus: 406 return nil, newParserError(it, p.name, 407 "Unexpected '+'") 408 case typ == token.Gt: 409 p.next() 410 redir, err := p.parseRedirection(it) 411 412 if err != nil { 413 return nil, err 414 } 415 416 n.AddRedirect(redir) 417 case typ == token.Pipe: 418 if p.insidePipe { 419 p.next() 420 // TODO(i4k): test against pipes and multiline cmds 421 return n, nil 422 } 423 424 p.insidePipe = true 425 return p.parsePipe(n) 426 case typ == token.EOF: 427 break cmdLoop 428 case typ == token.Illegal: 429 return nil, errors.NewError(it.Value()) 430 default: 431 break cmdLoop 432 } 433 } 434 435 it = p.peek() 436 437 if isMulti { 438 if it.Type() != token.RParen { 439 if it.Type() == token.EOF { 440 return nil, errors.NewUnfinishedCmdError(p.name, it) 441 } 442 443 return nil, newParserError(it, p.name, "Unexpected symbol '%s'", it) 444 } 445 446 p.ignore() 447 448 it = p.peek() 449 } 450 451 if p.insidePipe { 452 p.insidePipe = false 453 return n, nil 454 } 455 456 if it.Type() != token.Semicolon { 457 return nil, newParserError(it, p.name, "Unexpected symbol '%s'", it) 458 } 459 460 p.ignore() 461 462 return n, nil 463 } 464 465 func (p *Parser) parseRedirection(it scanner.Token) (*ast.RedirectNode, error) { 466 var ( 467 lval, rval int = ast.RedirMapNoValue, ast.RedirMapNoValue 468 err error 469 ) 470 471 redir := ast.NewRedirectNode(it.FileInfo) 472 473 it = p.peek() 474 475 if !isValidArgument(it) && it.Type() != token.LBrack { 476 return nil, newParserError(it, p.name, "Unexpected token: %v", it) 477 } 478 479 // [ 480 if it.Type() == token.LBrack { 481 p.next() 482 it = p.peek() 483 484 if it.Type() != token.Number { 485 return nil, newParserError(it, p.name, "Expected lefthand side of redirection map, but found '%s'", 486 it.Value()) 487 } 488 489 lval, err = strconv.Atoi(it.Value()) 490 491 if err != nil { 492 return nil, newParserError(it, p.name, "Redirection map expects integers. Found: %s", 493 it.Value()) 494 } 495 496 p.next() 497 it = p.peek() 498 499 if it.Type() != token.Assign && it.Type() != token.RBrack { 500 return nil, newParserError(it, p.name, "Unexpected token %v. Expecting ASSIGN or ]", 501 it) 502 } 503 504 // [xxx= 505 if it.Type() == token.Assign { 506 p.next() 507 it = p.peek() 508 509 if it.Type() != token.Number && it.Type() != token.RBrack { 510 return nil, newParserError(it, p.name, "Unexpected token %v. Expecting REDIRMAPRSIDE or ]", it) 511 } 512 513 if it.Type() == token.Number { 514 rval, err = strconv.Atoi(it.Value()) 515 516 if err != nil { 517 return nil, newParserError(it, p.name, "Redirection map expects integers. Found: %s", it.Value()) 518 } 519 520 p.next() 521 it = p.peek() 522 } else { 523 rval = ast.RedirMapSupress 524 } 525 } 526 527 if it.Type() != token.RBrack { 528 return nil, newParserError(it, p.name, "Unexpected token %v. Expecting ]", it) 529 } 530 531 // [xxx=yyy] 532 533 redir.SetMap(lval, rval) 534 535 p.next() 536 it = p.peek() 537 } 538 539 if !isValidArgument(it) { 540 if rval != ast.RedirMapNoValue || lval != ast.RedirMapNoValue { 541 return redir, nil 542 } 543 544 return nil, newParserError(it, p.name, "Unexpected token %v. Expecting STRING or ARG or VARIABLE", it) 545 } 546 547 arg, err := p.getArgument(nil, exprConfig{ 548 allowConcat: true, 549 allowArg: true, 550 allowVariadic: false, 551 allowFuncall: false, 552 }) 553 if err != nil { 554 return nil, err 555 } 556 557 redir.SetLocation(arg) 558 559 return redir, nil 560 } 561 562 func (p *Parser) parseImport(importToken scanner.Token) (ast.Node, error) { 563 it := p.next() 564 565 if it.Type() != token.Arg && it.Type() != token.String && it.Type() != token.Ident { 566 return nil, newParserError(it, p.name, "Unexpected token %v. Expecting ARG or STRING", it) 567 } 568 569 var arg *ast.StringExpr 570 571 if it.Type() == token.String { 572 arg = ast.NewStringExpr(it.FileInfo, it.Value(), true) 573 } else if it.Type() == token.Arg || it.Type() == token.Ident { 574 arg = ast.NewStringExpr(it.FileInfo, it.Value(), false) 575 } else { 576 return nil, newParserError(it, p.name, "Parser error: Invalid token '%v' for import path", it) 577 } 578 579 if p.peek().Type() == token.Semicolon { 580 p.ignore() 581 } 582 583 return ast.NewImportNode(importToken.FileInfo, arg), nil 584 } 585 586 func (p *Parser) parseSetenv(it scanner.Token) (ast.Node, error) { 587 var ( 588 setenv *ast.SetenvNode 589 assign ast.Node 590 err error 591 fileInfo = it.FileInfo 592 ) 593 594 it = p.next() 595 next := p.peek() 596 597 if it.Type() != token.Ident { 598 return nil, newParserError(it, p.name, "Unexpected token %v, expected identifier", it) 599 } 600 601 if next.Type() == token.Assign || next.Type() == token.AssignCmd { 602 assign, err = p.parseAssignment(it) 603 604 if err != nil { 605 return nil, err 606 } 607 608 setenv, err = ast.NewSetenvNode(fileInfo, it.Value(), assign) 609 } else { 610 setenv, err = ast.NewSetenvNode(fileInfo, it.Value(), nil) 611 612 if p.peek().Type() != token.Semicolon { 613 return nil, newParserError(p.peek(), 614 p.name, 615 "Unexpected token %v, expected semicolon (;) or EOL", 616 p.peek()) 617 } 618 619 p.ignore() 620 } 621 622 if err != nil { 623 return nil, err 624 } 625 626 return setenv, nil 627 } 628 629 func (p *Parser) getArgument(tok *scanner.Token, cfg exprConfig) (ast.Expr, error) { 630 var ( 631 err error 632 it scanner.Token 633 isFuncall bool 634 ) 635 636 if tok != nil { 637 it = *tok 638 } else { 639 it = p.next() 640 } 641 if !isValidArgument(it) { 642 return nil, newParserError(it, p.name, "Unexpected token %v. Expected %s, %s, %s or %s", 643 it, token.Ident, token.String, token.Variable, token.Arg) 644 } 645 646 firstToken := it 647 var arg ast.Expr 648 649 if firstToken.Type() == token.Variable { 650 next := p.peek() 651 652 if cfg.allowFuncall && next.Type() == token.LParen { 653 arg, err = p.parseFnInv(firstToken, false) 654 isFuncall = true 655 } else { 656 // makes "echo $list" == "echo $list..." 657 arg, err = p.parseVariable(&firstToken, cfg.allowVariadic) 658 } 659 } else if firstToken.Type() == token.String { 660 arg = ast.NewStringExpr(firstToken.FileInfo, firstToken.Value(), true) 661 } else { 662 // Arg, Ident, Number, Dotdotdot, etc 663 664 next := p.peek() 665 666 if cfg.allowFuncall && next.Type() == token.LParen { 667 arg, err = p.parseFnInv(firstToken, false) 668 isFuncall = true 669 } else { 670 arg = ast.NewStringExpr(firstToken.FileInfo, firstToken.Value(), false) 671 } 672 } 673 674 if err != nil { 675 return nil, err 676 } 677 678 it = p.peek() 679 if it.Type() == token.Plus && cfg.allowConcat { 680 return p.getConcatArg(arg) 681 } 682 683 if (firstToken.Type() == token.Arg || firstToken.Type() == token.Ident) && (!cfg.allowArg && !isFuncall) { 684 return nil, newParserError(it, p.name, "Unquoted string not allowed at pos %d (%s)", it.FileInfo, it.Value()) 685 } 686 687 return arg, nil 688 } 689 690 func (p *Parser) getConcatArg(firstArg ast.Expr) (ast.Expr, error) { 691 var ( 692 it scanner.Token 693 parts []ast.Expr 694 ) 695 696 parts = append(parts, firstArg) 697 698 hasConcat: 699 it = p.peek() 700 701 if it.Type() == token.Plus { 702 p.ignore() 703 704 arg, err := p.getArgument(nil, exprConfig{ 705 allowArg: false, 706 allowConcat: false, 707 allowFuncall: true, 708 allowVariadic: false, 709 }) 710 if err != nil { 711 return nil, err 712 } 713 714 parts = append(parts, arg) 715 goto hasConcat 716 } 717 718 return ast.NewConcatExpr(token.NewFileInfo(firstArg.Line(), firstArg.Column()), parts), nil 719 } 720 721 func (p *Parser) parseAssignment(ident scanner.Token) (ast.Node, error) { 722 // we're here 723 // | 724 // V 725 // ident = ... 726 // ident <= ... 727 // ident, ident2, ..., identN = ... 728 // ident, ident2, ..., identN <= ... 729 it := p.next() 730 731 if !isAssignment(it.Type()) { 732 return nil, newParserError(it, p.name, 733 "Unexpected token %v, expected '=' ,'<=', ',' or '['", it) 734 } 735 736 var ( 737 index ast.Expr 738 err error 739 ) 740 741 if it.Type() == token.LBrack { 742 index, err = p.parseIndexing() 743 744 if err != nil { 745 return nil, err 746 } 747 748 it = p.next() 749 } 750 751 names := []*ast.NameNode{ 752 ast.NewNameNode(ident.FileInfo, ident.Value(), index), 753 } 754 755 if it.Type() != token.Comma { 756 goto assignOp 757 } 758 759 for it = p.next(); it.Type() == token.Ident; it = p.next() { 760 var index ast.Expr 761 762 name := it 763 it = p.next() 764 765 if it.Type() == token.LBrack { 766 index, err = p.parseIndexing() 767 768 if err != nil { 769 return nil, err 770 } 771 772 it = p.next() 773 } 774 775 names = append(names, ast.NewNameNode(name.FileInfo, name.Value(), index)) 776 777 if it.Type() != token.Comma { 778 break 779 } 780 } 781 782 assignOp: 783 if it.Type() != token.AssignCmd && it.Type() != token.Assign { 784 return nil, newParserError(it, p.name, "Unexpected token %v, expected ',' '=' or '<='", it) 785 } 786 787 if it.Type() == token.AssignCmd { 788 return p.parseAssignCmdOut(names) 789 } 790 791 return p.parseAssignValues(names) 792 } 793 794 func (p *Parser) parseList(tok *scanner.Token) (ast.Node, error) { 795 var ( 796 arg ast.Expr 797 err error 798 lit scanner.Token 799 ) 800 801 if tok != nil { 802 lit = *tok 803 } else { 804 lit = p.next() 805 } 806 807 if lit.Type() != token.LParen { 808 return nil, newParserError(lit, p.name, "Unexpected token %v. Expecting (", lit) 809 } 810 811 var values []ast.Expr 812 813 it := p.peek() 814 815 for isValidArgument(it) || it.Type() == token.LParen { 816 if it.Type() == token.LParen { 817 arg, err = p.parseList(nil) 818 } else { 819 arg, err = p.getArgument(nil, exprConfig{ 820 allowArg: true, 821 allowConcat: true, 822 allowFuncall: false, 823 allowVariadic: false, 824 }) 825 } 826 827 if err != nil { 828 return nil, err 829 } 830 831 it = p.peek() 832 833 values = append(values, arg) 834 } 835 836 if it.Type() != token.RParen { 837 if it.Type() == token.EOF { 838 return nil, errors.NewUnfinishedListError(p.name, it) 839 } 840 841 return nil, newParserError(it, p.name, "Expected ) but found %s", it) 842 } 843 844 p.ignore() 845 846 var isVariadic bool 847 if p.peek().Type() == token.Dotdotdot { 848 isVariadic = true 849 p.ignore() 850 } 851 return ast.NewListVariadicExpr(lit.FileInfo, values, isVariadic), nil 852 } 853 854 func (p *Parser) parseAssignValues(names []*ast.NameNode) (ast.Node, error) { 855 var values []ast.Expr 856 857 if len(names) == 0 { 858 return nil, newParserError(p.peek(), p.name, "parser error: expect names non nil") 859 } 860 861 for it := p.peek(); isExpr(it.Type()); it = p.peek() { 862 var ( 863 value ast.Expr 864 err error 865 ) 866 867 if it.Type() == token.Variable || it.Type() == token.String { 868 value, err = p.getArgument(nil, exprConfig{ 869 allowArg: false, 870 allowFuncall: true, 871 allowVariadic: false, 872 allowConcat: true, 873 }) 874 } else if it.Type() == token.LParen { // list 875 value, err = p.parseList(nil) 876 } else { 877 return nil, newParserError(it, p.name, "Unexpected token %v. Expecting VARIABLE or STRING or (", it) 878 } 879 880 if err != nil { 881 return nil, err 882 } 883 884 values = append(values, value) 885 886 if p.peek().Type() != token.Comma { 887 break 888 } 889 890 p.ignore() 891 } 892 893 if len(values) == 0 { 894 return nil, newParserError(p.peek(), p.name, "Unexpected token %v. Expecting VARIABLE, STRING or (", p.peek()) 895 } else if len(values) != len(names) { 896 return nil, newParserError(p.peek(), p.name, "assignment count mismatch: %d = %d", 897 len(names), len(values)) 898 } 899 900 if p.peek().Type() == token.Semicolon { 901 p.ignore() 902 } 903 904 return ast.NewAssignNode(names[0].FileInfo, names, values), nil 905 } 906 907 func (p *Parser) parseAssignCmdOut(identifiers []*ast.NameNode) (ast.Node, error) { 908 var ( 909 exec ast.Node 910 err error 911 ) 912 913 it := p.next() 914 915 if it.Type() != token.Ident && it.Type() != token.Arg && it.Type() != token.Variable && it.Type() != token.LParen { 916 return nil, newParserError(it, p.name, 917 "Invalid token %v. Expected command or function invocation", it) 918 } 919 920 if it.Type() == token.LParen { 921 // command invocation 922 exec, err = p.parseCommand(it) 923 } else { 924 nextIt := p.peek() 925 926 if nextIt.Type() != token.LParen { 927 // it == (Ident || Arg) 928 exec, err = p.parseCommand(it) 929 } else { 930 // <ident>() 931 // <arg>() 932 // <var>() 933 exec, err = p.parseFnInv(it, true) 934 } 935 } 936 937 if err != nil { 938 return nil, err 939 } 940 941 if len(identifiers) == 0 { 942 // should not happen... pray 943 panic("internal error parsing assignment") 944 } 945 946 return ast.NewExecAssignNode(identifiers[0].FileInfo, identifiers, exec) 947 } 948 949 func (p *Parser) parseRfork(it scanner.Token) (ast.Node, error) { 950 n := ast.NewRforkNode(it.FileInfo) 951 952 it = p.next() 953 954 if it.Type() != token.Ident { 955 return nil, newParserError(it, p.name, "rfork requires one or more of the following flags: %s", ast.RforkFlags) 956 } 957 958 arg := ast.NewStringExpr(it.FileInfo, it.Value(), false) 959 n.SetFlags(arg) 960 961 it = p.peek() 962 963 if it.Type() == token.LBrace { 964 blockPos := it.FileInfo 965 966 p.ignore() // ignore lookaheaded symbol 967 p.openblocks++ 968 969 tree := ast.NewTree("rfork block") 970 r, err := p.parseBlock(blockPos.Line(), blockPos.Column()) 971 972 if err != nil { 973 return nil, err 974 } 975 976 tree.Root = r 977 978 n.SetTree(tree) 979 } 980 981 if p.peek().Type() == token.Semicolon { 982 p.ignore() 983 } 984 985 return n, nil 986 } 987 988 func (p *Parser) parseIfExpr() (ast.Node, error) { 989 it := p.peek() 990 if it.Type() != token.Ident && it.Type() != token.String && 991 it.Type() != token.Variable { 992 return nil, newParserError(it, p.name, "if requires lhs/rhs of type string, variable or function invocation. Found %v", it) 993 } 994 995 return p.getArgument(nil, exprConfig{ 996 allowArg: false, 997 allowVariadic: false, 998 allowFuncall: true, 999 allowConcat: true, 1000 }) 1001 } 1002 1003 func (p *Parser) parseIf(it scanner.Token) (ast.Node, error) { 1004 n := ast.NewIfNode(it.FileInfo) 1005 1006 lvalue, err := p.parseIfExpr() 1007 if err != nil { 1008 return nil, err 1009 } 1010 1011 n.SetLvalue(lvalue) 1012 1013 it = p.next() 1014 1015 if it.Type() != token.Equal && it.Type() != token.NotEqual { 1016 return nil, newParserError(it, p.name, "Expected comparison, but found %v", it) 1017 } 1018 1019 if it.Value() != "==" && it.Value() != "!=" { 1020 return nil, newParserError(it, p.name, "Invalid if operator '%s'. Valid comparison operators are '==' and '!='", 1021 it.Value()) 1022 } 1023 1024 n.SetOp(it.Value()) 1025 1026 rvalue, err := p.parseIfExpr() 1027 1028 if err != nil { 1029 return nil, err 1030 } 1031 1032 n.SetRvalue(rvalue) 1033 1034 it = p.next() 1035 1036 if it.Type() != token.LBrace { 1037 return nil, newParserError(it, p.name, "Expected '{' but found %v", it) 1038 } 1039 1040 p.openblocks++ 1041 1042 r, err := p.parseBlock(it.Line(), it.Column()) 1043 1044 if err != nil { 1045 return nil, err 1046 } 1047 1048 ifTree := ast.NewTree("if block") 1049 ifTree.Root = r 1050 n.SetIfTree(ifTree) 1051 1052 it = p.peek() 1053 1054 if it.Type() == token.Else { 1055 p.next() 1056 1057 elseBlock, elseIf, err := p.parseElse() 1058 1059 if err != nil { 1060 return nil, err 1061 } 1062 1063 elseTree := ast.NewTree("else tree") 1064 elseTree.Root = elseBlock 1065 1066 n.SetElseif(elseIf) 1067 n.SetElseTree(elseTree) 1068 } 1069 1070 return n, nil 1071 } 1072 1073 func (p *Parser) parseFnArgs() ([]*ast.FnArgNode, error) { 1074 var args []*ast.FnArgNode 1075 1076 if p.peek().Type() == token.RParen { 1077 // no argument 1078 p.ignore() 1079 return args, nil 1080 } 1081 1082 for { 1083 it := p.next() 1084 if it.Type() == token.Ident { 1085 argName := it.Value() 1086 isVariadic := false 1087 if p.peek().Type() == token.Dotdotdot { 1088 isVariadic = true 1089 p.ignore() 1090 } 1091 args = append(args, ast.NewFnArgNode(it.FileInfo, 1092 argName, isVariadic)) 1093 } else { 1094 return nil, newParserError(it, p.name, "Unexpected token %v. Expected identifier or ')'", it) 1095 } 1096 1097 it = p.peek() 1098 if it.Type() == token.Comma { 1099 p.ignore() 1100 it = p.peek() 1101 1102 if it.Type() == token.RParen { 1103 break 1104 } 1105 continue 1106 } 1107 1108 if it.Type() != token.RParen { 1109 return nil, newParserError(it, p.name, "Unexpected '%v'. Expected ')'", it) 1110 } 1111 1112 p.ignore() 1113 break 1114 } 1115 1116 return args, nil 1117 } 1118 1119 func (p *Parser) parseVar(it scanner.Token) (ast.Node, error) { 1120 var varTok = it 1121 1122 it = p.next() 1123 next := p.peek() 1124 1125 if it.Type() != token.Ident { 1126 return nil, newParserError(it, p.name, 1127 "Unexpected token %v. Expected IDENT", 1128 next, 1129 ) 1130 } 1131 1132 if !isAssignment(next.Type()) { 1133 return nil, newParserError(next, p.name, 1134 "Unexpected token %v. Expected '=' or ','", 1135 next, 1136 ) 1137 } 1138 1139 assign, err := p.parseAssignment(it) 1140 if err != nil { 1141 return nil, err 1142 } 1143 1144 switch assign.Type() { 1145 case ast.NodeAssign: 1146 return ast.NewVarAssignDecl( 1147 varTok.FileInfo, 1148 assign.(*ast.AssignNode), 1149 ), nil 1150 case ast.NodeExecAssign: 1151 return ast.NewVarExecAssignDecl( 1152 varTok.FileInfo, 1153 assign.(*ast.ExecAssignNode), 1154 ), nil 1155 } 1156 1157 return nil, newParserError(next, p.name, 1158 "Unexpected token %v. Expected ASSIGN or EXECASSIGN", 1159 next, 1160 ) 1161 } 1162 1163 func (p *Parser) parseFnDecl(it scanner.Token) (ast.Node, error) { 1164 var n *ast.FnDeclNode 1165 1166 it = p.next() 1167 if it.Type() == token.Ident { 1168 n = ast.NewFnDeclNode(it.FileInfo, it.Value()) 1169 it = p.next() 1170 } else { 1171 n = ast.NewFnDeclNode(it.FileInfo, "") 1172 } 1173 1174 if it.Type() != token.LParen { 1175 return nil, newParserError(it, p.name, 1176 "Unexpected token %v. Expected '('", it) 1177 } 1178 1179 args, err := p.parseFnArgs() 1180 if err != nil { 1181 return nil, err 1182 } 1183 1184 for _, arg := range args { 1185 n.AddArg(arg) 1186 } 1187 1188 it = p.next() 1189 if it.Type() != token.LBrace { 1190 return nil, newParserError(it, p.name, 1191 "Unexpected token %v. Expected '{'", it) 1192 } 1193 1194 p.openblocks++ 1195 1196 tree := ast.NewTree(fmt.Sprintf("fn %s body", n.Name())) 1197 r, err := p.parseBlock(it.Line(), it.Column()) 1198 1199 if err != nil { 1200 return nil, err 1201 } 1202 1203 tree.Root = r 1204 n.SetTree(tree) 1205 return n, nil 1206 } 1207 1208 func (p *Parser) parseFnInv(ident scanner.Token, allowSemicolon bool) (ast.Node, error) { 1209 n := ast.NewFnInvNode(ident.FileInfo, ident.Value()) 1210 1211 it := p.next() 1212 if it.Type() != token.LParen { 1213 return nil, newParserError(it, p.name, "Invalid token %v. Expected '('", it) 1214 } 1215 1216 for { 1217 it = p.next() 1218 next := p.peek() 1219 if isFuncall(it.Type(), next.Type()) || 1220 isValidArgument(it) { 1221 arg, err := p.getArgument(&it, exprConfig{ 1222 allowArg: false, 1223 allowFuncall: true, 1224 allowConcat: true, 1225 allowVariadic: true, 1226 }) 1227 if err != nil { 1228 return nil, err 1229 } 1230 1231 n.AddArg(arg) 1232 } else if it.Type() == token.LParen { 1233 listArg, err := p.parseList(&it) 1234 if err != nil { 1235 return nil, err 1236 } 1237 n.AddArg(listArg) 1238 } else if it.Type() == token.RParen { 1239 // p.next() 1240 break 1241 } else if it.Type() == token.EOF { 1242 goto parseError 1243 } 1244 1245 it = p.peek() 1246 if it.Type() == token.Comma { 1247 p.ignore() 1248 1249 continue 1250 } 1251 1252 if it.Type() == token.RParen { 1253 p.next() 1254 break 1255 } 1256 1257 goto parseError 1258 } 1259 1260 // semicolon is optional here 1261 if allowSemicolon && p.peek().Type() == token.Semicolon { 1262 p.next() 1263 } 1264 1265 return n, nil 1266 1267 parseError: 1268 return nil, newParserError(it, p.name, 1269 "Unexpected token %v. Expecting STRING, VARIABLE or )", it) 1270 } 1271 1272 func (p *Parser) parseElse() (*ast.BlockNode, bool, error) { 1273 it := p.next() 1274 1275 if it.Type() == token.LBrace { 1276 p.openblocks++ 1277 1278 elseBlock, err := p.parseBlock(it.Line(), it.Column()) 1279 1280 if err != nil { 1281 return nil, false, err 1282 } 1283 1284 return elseBlock, false, nil 1285 } 1286 1287 if it.Type() == token.If { 1288 ifNode, err := p.parseIf(it) 1289 1290 if err != nil { 1291 return nil, false, err 1292 } 1293 1294 block := ast.NewBlockNode(it.FileInfo) 1295 block.Push(ifNode) 1296 1297 return block, true, nil 1298 } 1299 1300 return nil, false, newParserError(it, p.name, "Unexpected token: %v", it) 1301 } 1302 1303 func (p *Parser) parseBindFn(bindIt scanner.Token) (ast.Node, error) { 1304 nameIt := p.next() 1305 1306 if nameIt.Type() != token.Ident { 1307 return nil, newParserError(nameIt, p.name, 1308 "Expected identifier, but found '%v'", nameIt) 1309 } 1310 1311 cmdIt := p.next() 1312 1313 if cmdIt.Type() != token.Ident { 1314 return nil, newParserError(cmdIt, p.name, "Expected identifier, but found '%v'", cmdIt) 1315 } 1316 1317 if p.peek().Type() == token.Semicolon { 1318 p.ignore() 1319 } 1320 1321 n := ast.NewBindFnNode(bindIt.FileInfo, nameIt.Value(), cmdIt.Value()) 1322 return n, nil 1323 } 1324 1325 func (p *Parser) parseReturn(retTok scanner.Token) (ast.Node, error) { 1326 ret := ast.NewReturnNode(retTok.FileInfo) 1327 1328 tok := p.peek() 1329 1330 // return; 1331 // return } 1332 // return $v 1333 // return "<some>" 1334 // return ( ... values ... ) 1335 // return <fn name>() 1336 // return "val1", "val2", $val3, test() 1337 if tok.Type() != token.Semicolon && 1338 tok.Type() != token.RBrace && 1339 tok.Type() != token.Variable && 1340 tok.Type() != token.String && 1341 tok.Type() != token.LParen && 1342 tok.Type() != token.Ident { 1343 return nil, newParserError(tok, p.name, 1344 "Expected ';', STRING, VARIABLE, FUNCALL or LPAREN, but found %v", 1345 tok) 1346 } 1347 1348 var returnExprs []ast.Expr 1349 1350 for { 1351 tok = p.peek() 1352 if tok.Type() == token.Semicolon { 1353 p.ignore() 1354 break 1355 } 1356 1357 if tok.Type() == token.RBrace { 1358 break 1359 } 1360 1361 if tok.Type() == token.LParen { 1362 listArg, err := p.parseList(nil) 1363 if err != nil { 1364 return nil, err 1365 } 1366 returnExprs = append(returnExprs, listArg) 1367 } else if tok.Type() == token.Ident { 1368 p.next() 1369 next := p.peek() 1370 1371 if next.Type() != token.LParen { 1372 return nil, newParserError(tok, p.name, 1373 "Expected FUNCALL, STRING, VARIABLE or LPAREN, but found '%v' %v", 1374 tok.Value(), next) 1375 } 1376 1377 arg, err := p.parseFnInv(tok, true) 1378 if err != nil { 1379 return nil, err 1380 } 1381 1382 returnExprs = append(returnExprs, arg) 1383 } else { 1384 arg, err := p.getArgument(nil, exprConfig{ 1385 allowArg: false, 1386 allowConcat: true, 1387 allowFuncall: true, 1388 allowVariadic: false, 1389 }) 1390 if err != nil { 1391 return nil, err 1392 } 1393 1394 returnExprs = append(returnExprs, arg) 1395 } 1396 1397 next := p.peek() 1398 1399 if next.Type() == token.Comma { 1400 p.ignore() 1401 continue 1402 } 1403 1404 if next.Type() == token.Semicolon { 1405 p.ignore() 1406 } 1407 1408 break 1409 } 1410 1411 ret.Returns = returnExprs 1412 1413 return ret, nil 1414 } 1415 1416 func (p *Parser) parseFor(it scanner.Token) (ast.Node, error) { 1417 var ( 1418 inExpr ast.Expr 1419 err error 1420 next scanner.Token 1421 ) 1422 1423 forStmt := ast.NewForNode(it.FileInfo) 1424 1425 it = p.peek() 1426 1427 if it.Type() != token.Ident { 1428 goto forBlockParse 1429 } 1430 1431 p.next() 1432 1433 forStmt.SetIdentifier(it.Value()) 1434 1435 it = p.next() 1436 1437 if it.Type() != token.Ident || it.Value() != "in" { 1438 return nil, newParserError(it, p.name, 1439 "Expected 'in' but found %q", it) 1440 } 1441 1442 // ignores 'in' keyword 1443 // TODO: make 'in' a real keyword 1444 1445 it = p.next() 1446 next = p.peek() 1447 1448 if it.Type() != token.Variable && 1449 (it.Type() != token.Ident || (it.Type() == token.Ident && next.Type() != token.LParen)) && 1450 it.Type() != token.LParen { 1451 return nil, newParserError(it, p.name, 1452 "Expected (variable, list or fn invocation) but found %q", it) 1453 } 1454 1455 if (it.Type() == token.Ident || it.Type() == token.Variable) && next.Type() == token.LParen { 1456 inExpr, err = p.parseFnInv(it, false) 1457 } else if it.Type() == token.Variable { 1458 inExpr, err = p.parseVariable(&it, false) 1459 } else if it.Type() == token.LParen { 1460 inExpr, err = p.parseList(&it) 1461 } 1462 1463 if err != nil { 1464 return nil, err 1465 } 1466 1467 forStmt.SetInExpr(inExpr) 1468 forBlockParse: 1469 it = p.peek() 1470 1471 if it.Type() != token.LBrace { 1472 return nil, newParserError(it, p.name, 1473 "Expected '{' but found %q", it) 1474 } 1475 1476 blockPos := it.FileInfo 1477 1478 p.ignore() // ignore lookaheaded symbol 1479 p.openblocks++ 1480 1481 tree := ast.NewTree("for block") 1482 1483 r, err := p.parseBlock(blockPos.Line(), blockPos.Column()) 1484 1485 if err != nil { 1486 return nil, err 1487 } 1488 1489 tree.Root = r 1490 forStmt.SetTree(tree) 1491 1492 return forStmt, nil 1493 } 1494 1495 func (p *Parser) parseComment(it scanner.Token) (ast.Node, error) { 1496 return ast.NewCommentNode(it.FileInfo, it.Value()), nil 1497 } 1498 1499 func (p *Parser) parseError(it scanner.Token) (ast.Node, error) { 1500 return nil, errors.NewError(it.Value()) 1501 } 1502 1503 func newParserError(item scanner.Token, name, format string, args ...interface{}) error { 1504 if item.Type() == token.Illegal { 1505 // scanner error 1506 return errors.NewError(item.Value()) 1507 } 1508 1509 errstr := fmt.Sprintf(format, args...) 1510 1511 return errors.NewError("%s:%d:%d: %s", name, item.Line(), item.Column(), errstr) 1512 } 1513 1514 func isValidArgument(t scanner.Token) bool { 1515 if t.Type() == token.String || 1516 t.Type() == token.Number || 1517 t.Type() == token.Arg || 1518 t.Type() == token.Dotdotdot || 1519 t.Type() == token.Ident || 1520 token.IsKeyword(t.Type()) || 1521 t.Type() == token.Variable { 1522 return true 1523 } 1524 1525 return false 1526 } 1527 1528 func isFuncall(tok, next token.Token) bool { 1529 return (tok == token.Ident || tok == token.Variable) && 1530 next == token.LParen 1531 } 1532 1533 func isAssignment(tok token.Token) bool { 1534 return tok == token.Assign || 1535 tok == token.AssignCmd || 1536 tok == token.LBrack || 1537 tok == token.Comma 1538 } 1539 1540 func isExpr(tok token.Token) bool { 1541 return tok == token.Variable || 1542 tok == token.String || 1543 tok == token.LParen 1544 }