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  }