github.com/NeowayLabs/nash@v0.2.2-0.20200127205349-a227041ffd50/ast/node.go (about) 1 package ast 2 3 import ( 4 "errors" 5 "fmt" 6 7 "github.com/madlambda/nash/token" 8 ) 9 10 const ( 11 // RedirMapNoValue indicates the pipe has not redirection 12 RedirMapNoValue = -1 13 // RedirMapSupress indicates the rhs of map was suppressed 14 RedirMapSupress = -2 15 16 RforkFlags = "umnips" 17 ) 18 19 type ( 20 // Node represents nodes in the grammar 21 Node interface { 22 Type() NodeType 23 IsEqual(Node) bool 24 25 // Line of node in the file 26 Line() int 27 // Column of the node in the file 28 Column() int 29 30 // String representation of the node. 31 // Note that it could not match the correspondent node in 32 // the source code. 33 String() string 34 } 35 36 assignable interface { 37 names() []*NameNode 38 setEqSpace(int) 39 getEqSpace() int 40 string() (string, bool) 41 } 42 43 egalitarian struct{} 44 45 // Expr is the interface of expression nodes. 46 Expr Node 47 48 // NodeType is the types of grammar 49 NodeType int 50 51 // BlockNode is the block 52 BlockNode struct { 53 NodeType 54 token.FileInfo 55 egalitarian 56 57 Nodes []Node 58 } 59 60 // An ImportNode represents the node for an "import" keyword. 61 ImportNode struct { 62 NodeType 63 token.FileInfo 64 egalitarian 65 66 Path *StringExpr // Import path 67 } 68 69 // A SetenvNode represents the node for a "setenv" keyword. 70 SetenvNode struct { 71 NodeType 72 token.FileInfo 73 egalitarian 74 75 Name string 76 assign Node 77 } 78 79 NameNode struct { 80 NodeType 81 token.FileInfo 82 egalitarian 83 84 Ident string 85 Index Expr 86 } 87 88 // AssignNode is a node for variable assignments 89 AssignNode struct { 90 NodeType 91 token.FileInfo 92 egalitarian 93 94 Names []*NameNode 95 Values []Expr 96 eqSpace int 97 } 98 99 // ExecAssignNode represents the node for execution assignment. 100 ExecAssignNode struct { 101 NodeType 102 token.FileInfo 103 egalitarian 104 105 Names []*NameNode 106 cmd Node 107 eqSpace int 108 } 109 110 // A CommandNode is a node for commands 111 CommandNode struct { 112 NodeType 113 token.FileInfo 114 egalitarian 115 116 name string 117 args []Expr 118 redirs []*RedirectNode 119 120 multi bool 121 } 122 123 // PipeNode represents the node for a command pipeline. 124 PipeNode struct { 125 NodeType 126 token.FileInfo 127 egalitarian 128 129 cmds []*CommandNode 130 multi bool 131 } 132 133 // StringExpr is a string argument 134 StringExpr struct { 135 NodeType 136 token.FileInfo 137 egalitarian 138 139 str string 140 quoted bool 141 } 142 143 // IntExpr is a integer used at indexing 144 IntExpr struct { 145 NodeType 146 token.FileInfo 147 egalitarian 148 149 val int 150 } 151 152 // ListExpr is a list argument 153 ListExpr struct { 154 NodeType 155 token.FileInfo 156 egalitarian 157 158 List []Expr 159 IsVariadic bool 160 } 161 162 // ConcatExpr is a concatenation of arguments 163 ConcatExpr struct { 164 NodeType 165 token.FileInfo 166 egalitarian 167 168 concat []Expr 169 } 170 171 // VarExpr is a variable argument 172 VarExpr struct { 173 NodeType 174 token.FileInfo 175 egalitarian 176 177 Name string 178 IsVariadic bool 179 } 180 181 // IndexExpr is a indexed variable 182 IndexExpr struct { 183 NodeType 184 token.FileInfo 185 egalitarian 186 187 Var *VarExpr 188 Index Expr 189 IsVariadic bool 190 } 191 192 // RedirectNode represents the output redirection part of a command 193 RedirectNode struct { 194 NodeType 195 token.FileInfo 196 egalitarian 197 198 rmap RedirMap 199 location Expr 200 } 201 202 // RforkNode is a builtin node for rfork 203 RforkNode struct { 204 NodeType 205 token.FileInfo 206 egalitarian 207 208 arg *StringExpr 209 tree *Tree 210 } 211 212 // CommentNode is the node for comments 213 CommentNode struct { 214 NodeType 215 token.FileInfo 216 egalitarian 217 218 val string 219 } 220 221 // RedirMap is the map of file descriptors of the redirection 222 RedirMap struct { 223 lfd int 224 rfd int 225 } 226 227 // IfNode represents the node for the "if" keyword. 228 IfNode struct { 229 NodeType 230 token.FileInfo 231 egalitarian 232 233 lvalue Expr 234 rvalue Expr 235 op string 236 elseIf bool 237 238 ifTree *Tree 239 elseTree *Tree 240 } 241 242 // VarAssignDeclNode is a "var" declaration to assign values 243 VarAssignDeclNode struct { 244 NodeType 245 token.FileInfo 246 egalitarian 247 248 Assign *AssignNode 249 } 250 251 // VarExecAssignDeclNode is a var declaration to assign output of fn/cmd 252 VarExecAssignDeclNode struct { 253 NodeType 254 token.FileInfo 255 egalitarian 256 257 ExecAssign *ExecAssignNode 258 } 259 260 // FnArgNode represents function arguments 261 FnArgNode struct { 262 NodeType 263 token.FileInfo 264 egalitarian 265 266 Name string 267 IsVariadic bool 268 } 269 270 // A FnDeclNode represents a function declaration. 271 FnDeclNode struct { 272 NodeType 273 token.FileInfo 274 egalitarian 275 276 name string 277 args []*FnArgNode 278 tree *Tree 279 } 280 281 // A FnInvNode represents a function invocation statement. 282 FnInvNode struct { 283 NodeType 284 token.FileInfo 285 egalitarian 286 287 name string 288 args []Expr 289 } 290 291 // A ReturnNode represents the "return" keyword. 292 ReturnNode struct { 293 NodeType 294 token.FileInfo 295 egalitarian 296 297 Returns []Expr 298 } 299 300 // A BindFnNode represents the "bindfn" keyword. 301 BindFnNode struct { 302 NodeType 303 token.FileInfo 304 egalitarian 305 306 name string 307 cmdname string 308 } 309 310 // A ForNode represents the "for" keyword. 311 ForNode struct { 312 NodeType 313 token.FileInfo 314 egalitarian 315 316 identifier string 317 inExpr Expr 318 tree *Tree 319 } 320 ) 321 322 //go:generate stringer -type=NodeType 323 324 const ( 325 // NodeSetenv is the type for "setenv" builtin keyword 326 NodeSetenv NodeType = iota + 1 327 328 // NodeBlock represents a program scope. 329 NodeBlock 330 331 // NodeName represents an identifier 332 NodeName 333 334 // NodeAssign is the type for variable assignment 335 NodeAssign 336 337 // NodeExecAssign is the type for command/function assignment 338 NodeExecAssign 339 340 // NodeImport is the type for "import" builtin keyword 341 NodeImport 342 343 execBegin 344 345 // NodeCommand is the type for command execution 346 NodeCommand 347 348 // NodePipe is the type for pipeline execution 349 NodePipe 350 351 // NodeRedirect is the type for redirection nodes 352 NodeRedirect 353 354 // NodeFnInv is the type for function invocation 355 NodeFnInv 356 357 execEnd 358 359 expressionBegin 360 361 // NodeStringExpr is the type of string expression (quoted or not). 362 NodeStringExpr 363 364 // NodeIntExpr is the type of integer expression (commonly list indexing) 365 NodeIntExpr 366 367 // NodeVarExpr is the type of variable expressions. 368 NodeVarExpr 369 370 // NodeListExpr is the type of list expression. 371 NodeListExpr 372 373 // NodeIndexExpr is the type of indexing expressions. 374 NodeIndexExpr 375 376 // NodeConcatExpr is the type of concatenation expressions. 377 NodeConcatExpr 378 379 expressionEnd 380 381 // NodeString are nodes for argument strings 382 NodeString 383 384 // NodeRfork is the type for rfork statement 385 NodeRfork 386 387 // NodeRforkFlags are nodes for rfork flags 388 NodeRforkFlags 389 390 // NodeIf is the type for if statements 391 NodeIf 392 393 // NodeComment are nodes for comment 394 NodeComment 395 396 NodeFnArg 397 398 // NodeVarAssignDecl is the type for var declaration of values 399 NodeVarAssignDecl 400 401 // NodeVarExecAssignDecl 402 NodeVarExecAssignDecl 403 404 // NodeFnDecl is the type for function declaration 405 NodeFnDecl 406 407 // NodeReturn is the type for return statement 408 NodeReturn 409 410 // NodeBindFn is the type for bindfn statements 411 NodeBindFn 412 413 // NodeFor is the type for "for" statements 414 NodeFor 415 ) 416 417 var ( 418 DebugCmp bool 419 ) 420 421 func debug(format string, args ...interface{}) { 422 if DebugCmp { 423 fmt.Printf("[debug] "+format+"\n", args...) 424 } 425 } 426 427 // Type returns the type of the node 428 func (t NodeType) Type() NodeType { 429 return t 430 } 431 432 // IsExpr returns if the node is an expression. 433 func (t NodeType) IsExpr() bool { 434 return t > expressionBegin && t < expressionEnd 435 } 436 437 // IsExecutable returns if the node is executable 438 func (t NodeType) IsExecutable() bool { 439 return t > execBegin && t < execEnd 440 } 441 442 func (e egalitarian) equal(node, other Node) bool { 443 if node == other { 444 return true 445 } 446 447 if node == nil { 448 return false 449 } 450 451 if !cmpInfo(node, other) { 452 return false 453 } 454 455 return true 456 } 457 458 // NewBlockNode creates a new block 459 func NewBlockNode(info token.FileInfo) *BlockNode { 460 return &BlockNode{ 461 NodeType: NodeBlock, 462 FileInfo: info, 463 } 464 } 465 466 // Push adds a new node for a block of nodes 467 func (l *BlockNode) Push(n Node) { 468 l.Nodes = append(l.Nodes, n) 469 } 470 471 // IsEqual returns if it is equal to the other node. 472 func (l *BlockNode) IsEqual(other Node) bool { 473 if !l.equal(l, other) { 474 return false 475 } 476 477 o, ok := other.(*BlockNode) 478 479 if !ok { 480 debug("Failed to cast other node to BlockNode") 481 return false 482 } 483 484 if len(l.Nodes) != len(o.Nodes) { 485 debug("Nodes differs in length") 486 return false 487 } 488 489 for i := 0; i < len(l.Nodes); i++ { 490 if !l.Nodes[i].IsEqual(o.Nodes[i]) { 491 debug("List entry %d differ... '%s' != '%s'", i, l.Nodes[i], o.Nodes[i]) 492 return false 493 } 494 } 495 496 return true 497 } 498 499 // NewImportNode creates a new ImportNode object 500 func NewImportNode(info token.FileInfo, path *StringExpr) *ImportNode { 501 return &ImportNode{ 502 NodeType: NodeImport, 503 FileInfo: info, 504 505 Path: path, 506 } 507 } 508 509 // IsEqual returns if it is equal to the other node. 510 func (n *ImportNode) IsEqual(other Node) bool { 511 if !n.equal(n, other) { 512 return false 513 } 514 515 o, ok := other.(*ImportNode) 516 517 if !ok { 518 debug("Failed to cast to ImportNode") 519 return false 520 } 521 522 if n.Path != o.Path { 523 if n.Path != nil { 524 return n.Path.IsEqual(o.Path) 525 } 526 } 527 528 return false 529 } 530 531 // NewSetenvNode creates a new assignment node 532 func NewSetenvNode(info token.FileInfo, name string, assign Node) (*SetenvNode, error) { 533 if assign != nil && assign.Type() != NodeAssign && 534 assign.Type() != NodeExecAssign { 535 return nil, errors.New("Invalid assignment in setenv") 536 } 537 538 return &SetenvNode{ 539 NodeType: NodeSetenv, 540 FileInfo: info, 541 542 Name: name, 543 assign: assign, 544 }, nil 545 } 546 547 // Assignment returns the setenv assignment (if any) 548 func (n *SetenvNode) Assignment() Node { return n.assign } 549 550 // IsEqual returns if it is equal to the other node. 551 func (n *SetenvNode) IsEqual(other Node) bool { 552 if !n.equal(n, other) { 553 return false 554 } 555 556 o, ok := other.(*SetenvNode) 557 558 if !ok { 559 debug("Failed to convert to SetenvNode") 560 return false 561 } 562 563 if n.assign != o.assign { 564 if !n.assign.IsEqual(o.assign) { 565 return false 566 } 567 } 568 569 return n.Name == o.Name 570 } 571 572 func NewNameNode(info token.FileInfo, ident string, index Expr) *NameNode { 573 return &NameNode{ 574 NodeType: NodeName, 575 FileInfo: info, 576 Ident: ident, 577 Index: index, 578 } 579 } 580 581 func (n *NameNode) IsEqual(other Node) bool { 582 if !n.equal(n, other) { 583 return false 584 } 585 586 o, ok := other.(*NameNode) 587 588 if !ok { 589 debug("Failed to convert to NameNode") 590 return false 591 } 592 593 if n.Ident != o.Ident { 594 return false 595 } 596 597 if n.Index == o.Index { 598 return true 599 } 600 601 if n.Index != nil { 602 return n.Index.IsEqual(o.Index) 603 } 604 605 return false 606 } 607 608 // NewAssignNode creates a new tuple assignment (multiple variable 609 // assigned in a single statement). 610 // For single assignment see NewSingleAssignNode. 611 func NewAssignNode(info token.FileInfo, names []*NameNode, values []Expr) *AssignNode { 612 return &AssignNode{ 613 NodeType: NodeAssign, 614 FileInfo: info, 615 eqSpace: -1, 616 617 Names: names, 618 Values: values, 619 } 620 } 621 622 // NewSingleAssignNode creates an assignment of a single variable. Eg.: 623 // name = "hello" 624 // To make an assignment of multiple variables in the same statement 625 // use `NewAssignNode`. 626 func NewSingleAssignNode(info token.FileInfo, name *NameNode, value Expr) *AssignNode { 627 return NewAssignNode(info, []*NameNode{name}, []Expr{value}) 628 } 629 630 // TODO(i4k): fix that 631 func (n *AssignNode) names() []*NameNode { return n.Names } 632 func (n *AssignNode) getEqSpace() int { return n.eqSpace } 633 func (n *AssignNode) setEqSpace(value int) { n.eqSpace = value } 634 635 // IsEqual returns if it is equal to the other node. 636 func (n *AssignNode) IsEqual(other Node) bool { 637 if !n.equal(n, other) { 638 return false 639 } 640 641 o, ok := other.(*AssignNode) 642 643 if !ok { 644 debug("Failed to convert to AssignNode") 645 return false 646 } 647 648 if len(n.Names) == len(o.Names) { 649 for i := 0; i < len(n.Names); i++ { 650 if !n.Names[i].IsEqual(o.Names[i]) { 651 debug("Assignment identifier doesn't match: '%s' != '%s'", 652 n.Names[i], o.Names[i]) 653 return false 654 } 655 } 656 } else { 657 return false 658 } 659 660 if len(n.Values) == len(o.Values) { 661 for i := 0; i < len(n.Values); i++ { 662 if !n.Values[i].IsEqual(o.Values[i]) { 663 return false 664 } 665 } 666 } else { 667 return false 668 } 669 670 return true 671 } 672 673 // NewExecAssignNode creates a new node for executing something and store the 674 // result on a new variable. The assignment could be made using an operating system 675 // command, a pipe of commands or a function invocation. 676 // It returns a *ExecAssignNode ready to be executed or error when n is not a valid 677 // node for execution. 678 // TODO(i4k): Change the API to specific node types. Eg.: NewExecAssignCmdNode and 679 // so on. 680 func NewExecAssignNode(info token.FileInfo, names []*NameNode, n Node) (*ExecAssignNode, error) { 681 if !n.Type().IsExecutable() { 682 return nil, errors.New("NewExecAssignNode expects a CommandNode, PipeNode or FninvNode") 683 } 684 685 return &ExecAssignNode{ 686 NodeType: NodeExecAssign, 687 FileInfo: info, 688 689 Names: names, 690 cmd: n, 691 eqSpace: -1, 692 }, nil 693 } 694 695 func (n *ExecAssignNode) names() []*NameNode { return n.Names } 696 func (n *ExecAssignNode) getEqSpace() int { return n.eqSpace } 697 func (n *ExecAssignNode) setEqSpace(value int) { n.eqSpace = value } 698 699 // Command returns the command (or r-value). Command could be a CommandNode or FnNode 700 func (n *ExecAssignNode) Command() Node { 701 return n.cmd 702 } 703 704 // SetCommand set the command part (NodeCommand or NodeFnDecl) 705 func (n *ExecAssignNode) SetCommand(c Node) { 706 n.cmd = c 707 } 708 709 func (n *ExecAssignNode) IsEqual(other Node) bool { 710 if !n.equal(n, other) { 711 return false 712 } 713 714 o, ok := other.(*ExecAssignNode) 715 716 if !ok { 717 debug("Failed to convert to ExecAssignNode") 718 return false 719 } 720 721 if len(n.Names) != len(o.Names) { 722 return false 723 } 724 725 for i := 0; i < len(n.Names); i++ { 726 if n.Names[i] != nil { 727 if !n.Names[i].IsEqual(o.Names[i]) { 728 debug("Exec assignment name differs") 729 return false 730 } 731 } 732 } 733 734 if n.cmd == o.cmd { 735 return true 736 } else if n.cmd != nil { 737 return n.cmd.IsEqual(o.cmd) 738 } 739 740 return false 741 } 742 743 // NewCommandNode creates a new node for commands 744 func NewCommandNode(info token.FileInfo, name string, multiline bool) *CommandNode { 745 return &CommandNode{ 746 NodeType: NodeCommand, 747 FileInfo: info, 748 749 name: name, 750 multi: multiline, 751 } 752 } 753 754 func (n *CommandNode) IsMulti() bool { return n.multi } 755 func (n *CommandNode) SetMulti(b bool) { n.multi = b } 756 757 // AddArg adds a new argument to the command 758 func (n *CommandNode) AddArg(a Expr) { 759 n.args = append(n.args, a) 760 } 761 762 // SetArgs sets an array of args to command 763 func (n *CommandNode) SetArgs(args []Expr) { 764 n.args = args 765 } 766 767 // Args returns the list of arguments supplied to command. 768 func (n *CommandNode) Args() []Expr { return n.args } 769 770 // AddRedirect adds a new redirect node to command 771 func (n *CommandNode) AddRedirect(redir *RedirectNode) { 772 n.redirs = append(n.redirs, redir) 773 } 774 775 // Redirects return the list of redirect maps of the command. 776 func (n *CommandNode) Redirects() []*RedirectNode { return n.redirs } 777 778 // Name returns the program name 779 func (n *CommandNode) Name() string { return n.name } 780 781 // IsEqual returns if it is equal to the other node. 782 func (n *CommandNode) IsEqual(other Node) bool { 783 if !n.equal(n, other) { 784 return false 785 } 786 787 o, ok := other.(*CommandNode) 788 789 if !ok { 790 debug("Failed to convert to CommandNode") 791 return false 792 } 793 794 if n.multi != o.multi { 795 debug("Command multiline differs.") 796 return false 797 } 798 799 if len(n.args) != len(o.args) { 800 debug("Command argument length differs: %d (%+v) != %d (%+v)", 801 len(n.args), n.args, len(o.args), o.args) 802 return false 803 } 804 805 for i := 0; i < len(n.args); i++ { 806 if !n.args[i].IsEqual(o.args[i]) { 807 debug("Argument %d differs. '%s' != '%s'", i, n.args[i], 808 o.args[i]) 809 return false 810 } 811 } 812 813 if len(n.redirs) != len(o.redirs) { 814 debug("Number of redirects differs. %d != %d", len(n.redirs), 815 len(o.redirs)) 816 return false 817 } 818 819 for i := 0; i < len(n.redirs); i++ { 820 if n.redirs[i] == o.redirs[i] { 821 continue 822 } else if n.redirs[i] != nil && 823 !n.redirs[i].IsEqual(o.redirs[i]) { 824 debug("Redirect differs... %s != %s", n.redirs[i], 825 o.redirs[i]) 826 return false 827 } 828 } 829 830 return n.name == o.name 831 } 832 833 // NewPipeNode creates a new command pipeline 834 func NewPipeNode(info token.FileInfo, multi bool) *PipeNode { 835 return &PipeNode{ 836 NodeType: NodePipe, 837 FileInfo: info, 838 839 multi: multi, 840 } 841 } 842 843 func (n *PipeNode) IsMulti() bool { return n.multi } 844 func (n *PipeNode) SetMulti(b bool) { n.multi = b } 845 846 // AddCmd add another command to end of the pipeline 847 func (n *PipeNode) AddCmd(c *CommandNode) { 848 n.cmds = append(n.cmds, c) 849 } 850 851 // Commands returns the list of pipeline commands 852 func (n *PipeNode) Commands() []*CommandNode { 853 return n.cmds 854 } 855 856 // IsEqual returns if it is equal to the other node. 857 func (n *PipeNode) IsEqual(other Node) bool { 858 if !n.equal(n, other) { 859 return false 860 } 861 862 o, ok := other.(*PipeNode) 863 864 if !ok { 865 debug("Failed to convert to PipeNode") 866 return false 867 } 868 869 if len(n.cmds) != len(o.cmds) { 870 debug("Number of pipe commands differ: %d != %d", 871 len(n.cmds), len(o.cmds)) 872 return false 873 } 874 875 for i := 0; i < len(n.cmds); i++ { 876 if !n.cmds[i].IsEqual(o.cmds[i]) { 877 debug("Command differs. '%s' != '%s'", n.cmds[i], 878 o.cmds[i]) 879 return false 880 } 881 } 882 883 return true 884 } 885 886 // NewRedirectNode creates a new redirection node for commands 887 func NewRedirectNode(info token.FileInfo) *RedirectNode { 888 return &RedirectNode{ 889 NodeType: NodeRedirect, 890 FileInfo: info, 891 892 rmap: RedirMap{ 893 lfd: -1, 894 rfd: -1, 895 }, 896 } 897 } 898 899 // SetMap sets the redirection map. Eg.: [2=1] 900 func (r *RedirectNode) SetMap(lfd int, rfd int) { 901 r.rmap.lfd = lfd 902 r.rmap.rfd = rfd 903 } 904 905 // LeftFD return the lhs of the redirection map. 906 func (r *RedirectNode) LeftFD() int { return r.rmap.lfd } 907 908 // RightFD return the rhs of the redirection map. 909 func (r *RedirectNode) RightFD() int { return r.rmap.rfd } 910 911 // SetLocation of the output 912 func (r *RedirectNode) SetLocation(s Expr) { r.location = s } 913 914 // Location return the location of the redirection. 915 func (r *RedirectNode) Location() Expr { return r.location } 916 917 // IsEqual return if it is equal to the other node. 918 func (r *RedirectNode) IsEqual(other Node) bool { 919 if !r.equal(r, other) { 920 return false 921 } 922 923 o, ok := other.(*RedirectNode) 924 925 if !ok { 926 return false 927 } 928 929 if r.rmap.lfd != o.rmap.lfd || 930 r.rmap.rfd != o.rmap.rfd { 931 return false 932 } 933 934 if r.location == o.location { 935 return true 936 } else if r.location != nil { 937 return r.location.IsEqual(o.location) 938 } 939 940 return false 941 } 942 943 // NewRforkNode creates a new node for rfork 944 func NewRforkNode(info token.FileInfo) *RforkNode { 945 return &RforkNode{ 946 NodeType: NodeRfork, 947 FileInfo: info, 948 } 949 } 950 951 // Arg return the string argument of the rfork. 952 func (n *RforkNode) Arg() *StringExpr { 953 return n.arg 954 } 955 956 // SetFlags sets the rfork flags 957 func (n *RforkNode) SetFlags(a *StringExpr) { 958 n.arg = a 959 } 960 961 // Tree returns the child tree of node 962 func (n *RforkNode) Tree() *Tree { 963 return n.tree 964 } 965 966 // SetTree set the body of the rfork block. 967 func (n *RforkNode) SetTree(t *Tree) { 968 n.tree = t 969 } 970 971 func (n *RforkNode) IsEqual(other Node) bool { 972 if !n.equal(n, other) { 973 return false 974 } 975 976 o, ok := other.(*RforkNode) 977 978 if !ok { 979 return false 980 } 981 982 if n.arg == o.arg { 983 return true 984 } 985 986 if n.arg != nil { 987 if !n.arg.IsEqual(o.arg) { 988 return false 989 } 990 } 991 992 return n.tree.IsEqual(o.tree) 993 } 994 995 // NewCommentNode creates a new node for comments 996 func NewCommentNode(info token.FileInfo, val string) *CommentNode { 997 return &CommentNode{ 998 NodeType: NodeComment, 999 FileInfo: info, 1000 1001 val: val, 1002 } 1003 } 1004 1005 func (n *CommentNode) IsEqual(other Node) bool { 1006 if !n.equal(n, other) { 1007 return false 1008 } 1009 1010 if n.Type() != other.Type() { 1011 return false 1012 } 1013 1014 o, ok := other.(*CommentNode) 1015 1016 if !ok { 1017 return false 1018 } 1019 1020 return n.val == o.val 1021 } 1022 1023 // NewIfNode creates a new if block statement 1024 func NewIfNode(info token.FileInfo) *IfNode { 1025 return &IfNode{ 1026 NodeType: NodeIf, 1027 FileInfo: info, 1028 } 1029 } 1030 1031 // Lvalue returns the lefthand part of condition 1032 func (n *IfNode) Lvalue() Expr { 1033 return n.lvalue 1034 } 1035 1036 // Rvalue returns the righthand side of condition 1037 func (n *IfNode) Rvalue() Expr { 1038 return n.rvalue 1039 } 1040 1041 // SetLvalue set the lefthand side of condition 1042 func (n *IfNode) SetLvalue(arg Expr) { 1043 n.lvalue = arg 1044 } 1045 1046 // SetRvalue set the righthand side of condition 1047 func (n *IfNode) SetRvalue(arg Expr) { 1048 n.rvalue = arg 1049 } 1050 1051 // Op returns the condition operation 1052 func (n *IfNode) Op() string { return n.op } 1053 1054 // SetOp set the condition operation 1055 func (n *IfNode) SetOp(op string) { 1056 n.op = op 1057 } 1058 1059 // IsElseIf tells if the if is an else-if statement 1060 func (n *IfNode) IsElseIf() bool { 1061 return n.elseIf 1062 } 1063 1064 // SetElseif sets the else-if part 1065 func (n *IfNode) SetElseif(b bool) { 1066 n.elseIf = b 1067 } 1068 1069 // SetIfTree sets the block of statements of the if block 1070 func (n *IfNode) SetIfTree(t *Tree) { 1071 n.ifTree = t 1072 } 1073 1074 // SetElseTree sets the block of statements of the else block 1075 func (n *IfNode) SetElseTree(t *Tree) { 1076 n.elseTree = t 1077 } 1078 1079 // IfTree returns the if block 1080 func (n *IfNode) IfTree() *Tree { return n.ifTree } 1081 1082 // ElseTree returns the else block 1083 func (n *IfNode) ElseTree() *Tree { return n.elseTree } 1084 1085 // IsEqual returns if it is equal to the other node. 1086 func (n *IfNode) IsEqual(other Node) bool { 1087 if !n.equal(n, other) { 1088 return false 1089 } 1090 1091 o, ok := other.(*IfNode) 1092 1093 if !ok { 1094 debug("Failed to convert to ifNode") 1095 return false 1096 } 1097 1098 elvalue := n.Lvalue() 1099 ervalue := n.Rvalue() 1100 vlvalue := o.Lvalue() 1101 vrvalue := o.Rvalue() 1102 1103 if !elvalue.IsEqual(vlvalue) { 1104 debug("Lvalue differs: '%s' != '%s'", elvalue, vlvalue) 1105 return false 1106 } 1107 1108 if !ervalue.IsEqual(vrvalue) { 1109 debug("Rvalue differs: '%s' != '%s'", ervalue, vrvalue) 1110 return false 1111 } 1112 1113 if n.Op() != o.Op() { 1114 debug("Operation differs: %s != %s", n.Op(), o.Op()) 1115 return false 1116 } 1117 1118 expectedTree := n.IfTree() 1119 valueTree := o.IfTree() 1120 1121 if !expectedTree.IsEqual(valueTree) { 1122 debug("If tree differs: '%s' != '%s'", expectedTree, 1123 valueTree) 1124 return false 1125 } 1126 1127 expectedTree = n.ElseTree() 1128 valueTree = o.ElseTree() 1129 1130 return expectedTree.IsEqual(valueTree) 1131 } 1132 1133 func NewFnArgNode(info token.FileInfo, name string, isVariadic bool) *FnArgNode { 1134 return &FnArgNode{ 1135 NodeType: NodeFnArg, 1136 FileInfo: info, 1137 1138 Name: name, 1139 IsVariadic: isVariadic, 1140 } 1141 } 1142 1143 func (a *FnArgNode) IsEqual(other Node) bool { 1144 if !a.equal(a, other) { 1145 return false 1146 } 1147 o, ok := other.(*FnArgNode) 1148 if !ok { 1149 return false 1150 } 1151 if a.Name != o.Name || 1152 a.IsVariadic != o.IsVariadic { 1153 return false 1154 } 1155 return true 1156 } 1157 1158 func NewVarAssignDecl(info token.FileInfo, assignNode *AssignNode) *VarAssignDeclNode { 1159 return &VarAssignDeclNode{ 1160 NodeType: NodeVarAssignDecl, 1161 Assign: assignNode, 1162 } 1163 } 1164 1165 func (n *VarAssignDeclNode) IsEqual(other Node) bool { 1166 if !n.equal(n, other) { 1167 return false 1168 } 1169 1170 o, ok := other.(*VarAssignDeclNode) 1171 if !ok { 1172 return false 1173 } 1174 1175 return n.Assign.IsEqual(o.Assign) 1176 } 1177 1178 func NewVarExecAssignDecl(info token.FileInfo, assignNode *ExecAssignNode) *VarExecAssignDeclNode { 1179 return &VarExecAssignDeclNode{ 1180 NodeType: NodeVarExecAssignDecl, 1181 ExecAssign: assignNode, 1182 } 1183 } 1184 1185 func (n *VarExecAssignDeclNode) IsEqual(other Node) bool { 1186 if !n.equal(n, other) { 1187 return false 1188 } 1189 1190 o, ok := other.(*VarExecAssignDeclNode) 1191 if !ok { 1192 return false 1193 } 1194 1195 return n.ExecAssign.IsEqual(o.ExecAssign) 1196 } 1197 1198 // NewFnDeclNode creates a new function declaration 1199 func NewFnDeclNode(info token.FileInfo, name string) *FnDeclNode { 1200 return &FnDeclNode{ 1201 NodeType: NodeFnDecl, 1202 FileInfo: info, 1203 1204 name: name, 1205 } 1206 } 1207 1208 // SetName set the function name 1209 func (n *FnDeclNode) SetName(a string) { 1210 n.name = a 1211 } 1212 1213 // Name return the function name 1214 func (n *FnDeclNode) Name() string { 1215 return n.name 1216 } 1217 1218 // Args returns function arguments 1219 func (n *FnDeclNode) Args() []*FnArgNode { 1220 return n.args 1221 } 1222 1223 // AddArg add a new argument to end of argument list 1224 func (n *FnDeclNode) AddArg(arg *FnArgNode) { 1225 n.args = append(n.args, arg) 1226 } 1227 1228 // Tree return the function block 1229 func (n *FnDeclNode) Tree() *Tree { 1230 return n.tree 1231 } 1232 1233 // SetTree set the function tree 1234 func (n *FnDeclNode) SetTree(t *Tree) { 1235 n.tree = t 1236 } 1237 1238 func (n *FnDeclNode) IsEqual(other Node) bool { 1239 if !n.equal(n, other) { 1240 return false 1241 } 1242 1243 o, ok := other.(*FnDeclNode) 1244 1245 if !ok { 1246 return false 1247 } 1248 1249 if n.name != o.name || len(n.args) != len(o.args) { 1250 return false 1251 } 1252 1253 for i := 0; i < len(n.args); i++ { 1254 if !n.args[i].IsEqual(o.args[i]) { 1255 return false 1256 } 1257 } 1258 1259 return true 1260 } 1261 1262 // NewFnInvNode creates a new function invocation 1263 func NewFnInvNode(info token.FileInfo, name string) *FnInvNode { 1264 return &FnInvNode{ 1265 NodeType: NodeFnInv, 1266 FileInfo: info, 1267 1268 name: name, 1269 } 1270 } 1271 1272 // SetName set the function name 1273 func (n *FnInvNode) SetName(a string) { 1274 n.name = a 1275 } 1276 1277 // Name return the function name 1278 func (n *FnInvNode) Name() string { 1279 return n.name 1280 } 1281 1282 // AddArg add another argument to end of argument list 1283 func (n *FnInvNode) AddArg(arg Expr) { 1284 n.args = append(n.args, arg) 1285 } 1286 1287 // Args return the invocation arguments. 1288 func (n *FnInvNode) Args() []Expr { return n.args } 1289 1290 // IsEqual returns if it is equal to the other node. 1291 func (n *FnInvNode) IsEqual(other Node) bool { 1292 if !n.equal(n, other) { 1293 return false 1294 } 1295 1296 o, ok := other.(*FnInvNode) 1297 1298 if !ok { 1299 return false 1300 } 1301 1302 if len(n.args) != len(o.args) { 1303 return false 1304 } 1305 1306 for i := 0; i < len(n.args); i++ { 1307 if !n.args[i].IsEqual(o.args[i]) { 1308 return false 1309 } 1310 } 1311 1312 return true 1313 } 1314 1315 // NewBindFnNode creates a new bindfn statement 1316 func NewBindFnNode(info token.FileInfo, name, cmd string) *BindFnNode { 1317 return &BindFnNode{ 1318 NodeType: NodeBindFn, 1319 FileInfo: info, 1320 1321 name: name, 1322 cmdname: cmd, 1323 } 1324 } 1325 1326 // Name return the function name 1327 func (n *BindFnNode) Name() string { return n.name } 1328 1329 // CmdName return the command name 1330 func (n *BindFnNode) CmdName() string { return n.cmdname } 1331 1332 func (n *BindFnNode) IsEqual(other Node) bool { 1333 if !n.equal(n, other) { 1334 return false 1335 } 1336 1337 o, ok := other.(*BindFnNode) 1338 1339 if !ok { 1340 return false 1341 } 1342 1343 return n.name == o.name && n.cmdname == o.cmdname 1344 } 1345 1346 // NewReturnNode create a return statement 1347 func NewReturnNode(info token.FileInfo) *ReturnNode { 1348 return &ReturnNode{ 1349 FileInfo: info, 1350 NodeType: NodeReturn, 1351 } 1352 } 1353 1354 func (n *ReturnNode) IsEqual(other Node) bool { 1355 if !n.equal(n, other) { 1356 return false 1357 } 1358 1359 if n.Type() != other.Type() { 1360 return false 1361 } 1362 1363 o, ok := other.(*ReturnNode) 1364 1365 if !ok { 1366 return false 1367 } 1368 1369 if len(n.Returns) != len(o.Returns) { 1370 return false 1371 } 1372 1373 for i := 0; i < len(n.Returns); i++ { 1374 arg := n.Returns[i] 1375 oarg := o.Returns[i] 1376 1377 if arg != nil && !arg.IsEqual(oarg) { 1378 return false 1379 } 1380 } 1381 1382 return true 1383 } 1384 1385 // NewForNode create a new for statement 1386 func NewForNode(info token.FileInfo) *ForNode { 1387 return &ForNode{ 1388 NodeType: NodeFor, 1389 FileInfo: info, 1390 } 1391 } 1392 1393 // SetIdentifier set the for indentifier 1394 func (n *ForNode) SetIdentifier(a string) { 1395 n.identifier = a 1396 } 1397 1398 // Identifier return the identifier part 1399 func (n *ForNode) Identifier() string { return n.identifier } 1400 1401 // InVar return the "in" variable 1402 func (n *ForNode) InExpr() Expr { return n.inExpr } 1403 1404 // SetInVar set "in" expression 1405 func (n *ForNode) SetInExpr(a Expr) { n.inExpr = a } 1406 1407 // SetTree set the for block of statements 1408 func (n *ForNode) SetTree(a *Tree) { 1409 n.tree = a 1410 } 1411 1412 // Tree return the for block 1413 func (n *ForNode) Tree() *Tree { return n.tree } 1414 1415 func (n *ForNode) IsEqual(other Node) bool { 1416 if !n.equal(n, other) { 1417 return false 1418 } 1419 1420 if n.Type() != other.Type() { 1421 return false 1422 } 1423 1424 o, ok := other.(*ForNode) 1425 1426 if !ok { 1427 return false 1428 } 1429 1430 if n.identifier != o.identifier { 1431 return false 1432 } 1433 1434 if n.inExpr == o.inExpr { 1435 return true 1436 } 1437 1438 if n.inExpr != nil { 1439 return n.inExpr.IsEqual(o.inExpr) 1440 } 1441 1442 return false 1443 } 1444 1445 func cmpInfo(n, other Node) bool { 1446 if n.Line() != other.Line() || 1447 n.Column() != other.Column() { 1448 debug("file info mismatch on %v (%s): (%d, %d) != (%d, %d)", 1449 n, n.Type(), n.Line(), n.Column(), 1450 other.Line(), other.Column()) 1451 return false 1452 } 1453 1454 return true 1455 }