func (n *CommandNode) AddArg(a Expr) {
	n.args = append(n.args, a)
}
AddArg adds a new argument to the command
package ast
import (
	"errors"
	"fmt"
	"github.com/madlambda/nash/token"
)
const (
	RedirMapNoValue = -1
	RedirMapSupress = -2
	RforkFlags = "umnips"
)
type (
	Node interface {
		Type() NodeType
		IsEqual(Node) bool
		Line() int
		Column() int
		String() string
	}
	assignable interface {
		names() []*NameNode
		setEqSpace(int)
		getEqSpace() int
		string() (string, bool)
	}
	egalitarian struct{}
	Expr Node
	NodeType int
	BlockNode struct {
		NodeType
		token.FileInfo
		egalitarian
		Nodes []Node
	}
	ImportNode struct {
		NodeType
		token.FileInfo
		egalitarian
		Path *StringExpr  
	}
	SetenvNode struct {
		NodeType
		token.FileInfo
		egalitarian
		Name   string
		assign Node
	}
	NameNode struct {
		NodeType
		token.FileInfo
		egalitarian
		Ident string
		Index Expr
	}
	AssignNode struct {
		NodeType
		token.FileInfo
		egalitarian
		Names   []*NameNode
		Values  []Expr
		eqSpace int
	}
	ExecAssignNode struct {
		NodeType
		token.FileInfo
		egalitarian
		Names   []*NameNode
		cmd     Node
		eqSpace int
	}
	CommandNode struct {
		NodeType
		token.FileInfo
		egalitarian
		name   string
		args   []Expr
		redirs []*RedirectNode
		multi bool
	}
	PipeNode struct {
		NodeType
		token.FileInfo
		egalitarian
		cmds  []*CommandNode
		multi bool
	}
	StringExpr struct {
		NodeType
		token.FileInfo
		egalitarian
		str    string
		quoted bool
	}
	IntExpr struct {
		NodeType
		token.FileInfo
		egalitarian
		val int
	}
	ListExpr struct {
		NodeType
		token.FileInfo
		egalitarian
		List       []Expr
		IsVariadic bool
	}
	ConcatExpr struct {
		NodeType
		token.FileInfo
		egalitarian
		concat []Expr
	}
	VarExpr struct {
		NodeType
		token.FileInfo
		egalitarian
		Name       string
		IsVariadic bool
	}
	IndexExpr struct {
		NodeType
		token.FileInfo
		egalitarian
		Var        *VarExpr
		Index      Expr
		IsVariadic bool
	}
	RedirectNode struct {
		NodeType
		token.FileInfo
		egalitarian
		rmap     RedirMap
		location Expr
	}
	RforkNode struct {
		NodeType
		token.FileInfo
		egalitarian
		arg  *StringExpr
		tree *Tree
	}
	CommentNode struct {
		NodeType
		token.FileInfo
		egalitarian
		val string
	}
	RedirMap struct {
		lfd int
		rfd int
	}
	IfNode struct {
		NodeType
		token.FileInfo
		egalitarian
		lvalue Expr
		rvalue Expr
		op     string
		elseIf bool
		ifTree   *Tree
		elseTree *Tree
	}
	VarAssignDeclNode struct {
		NodeType
		token.FileInfo
		egalitarian
		Assign *AssignNode
	}
	VarExecAssignDeclNode struct {
		NodeType
		token.FileInfo
		egalitarian
		ExecAssign *ExecAssignNode
	}
	FnArgNode struct {
		NodeType
		token.FileInfo
		egalitarian
		Name       string
		IsVariadic bool
	}
	FnDeclNode struct {
		NodeType
		token.FileInfo
		egalitarian
		name string
		args []*FnArgNode
		tree *Tree
	}
	FnInvNode struct {
		NodeType
		token.FileInfo
		egalitarian
		name string
		args []Expr
	}
	ReturnNode struct {
		NodeType
		token.FileInfo
		egalitarian
		Returns []Expr
	}
	BindFnNode struct {
		NodeType
		token.FileInfo
		egalitarian
		name    string
		cmdname string
	}
	ForNode struct {
		NodeType
		token.FileInfo
		egalitarian
		identifier string
		inExpr     Expr
		tree       *Tree
	}
)
const (
	NodeSetenv NodeType = iota + 1
	NodeBlock
	NodeName
	NodeAssign
	NodeExecAssign
	NodeImport
	execBegin
	NodeCommand
	NodePipe
	NodeRedirect
	NodeFnInv
	execEnd
	expressionBegin
	NodeStringExpr
	NodeIntExpr
	NodeVarExpr
	NodeListExpr
	NodeIndexExpr
	NodeConcatExpr
	expressionEnd
	NodeString
	NodeRfork
	NodeRforkFlags
	NodeIf
	NodeComment
	NodeFnArg
	NodeVarAssignDecl
	NodeVarExecAssignDecl
	NodeFnDecl
	NodeReturn
	NodeBindFn
	NodeFor
)
var (
	DebugCmp bool
)
func debug(format string, args ...interface{}) {
	if DebugCmp {
		fmt.Printf("[debug] "+format+"\n", args...)
	}
}
func (t NodeType) Type() NodeType {
	return t
}
func (t NodeType) IsExpr() bool {
	return t > expressionBegin && t < expressionEnd
}
func (t NodeType) IsExecutable() bool {
	return t > execBegin && t < execEnd
}
func (e egalitarian) equal(node, other Node) bool {
	if node == other {
		return true
	}
	if node == nil {
		return false
	}
	if !cmpInfo(node, other) {
		return false
	}
	return true
}
func NewBlockNode(info token.FileInfo) *BlockNode {
	return &BlockNode{
		NodeType: NodeBlock,
		FileInfo: info,
	}
}
func (l *BlockNode) Push(n Node) {
	l.Nodes = append(l.Nodes, n)
}
func (l *BlockNode) IsEqual(other Node) bool {
	if !l.equal(l, other) {
		return false
	}
	o, ok := other.(*BlockNode)
	if !ok {
		debug("Failed to cast other node to BlockNode")
		return false
	}
	if len(l.Nodes) != len(o.Nodes) {
		debug("Nodes differs in length")
		return false
	}
	for i := 0; i < len(l.Nodes); i++ {
		if !l.Nodes[i].IsEqual(o.Nodes[i]) {
			debug("List entry %d differ... '%s' != '%s'", i, l.Nodes[i], o.Nodes[i])
			return false
		}
	}
	return true
}
func NewImportNode(info token.FileInfo, path *StringExpr) *ImportNode {
	return &ImportNode{
		NodeType: NodeImport,
		FileInfo: info,
		Path: path,
	}
}
func (n *ImportNode) IsEqual(other Node) bool {
	if !n.equal(n, other) {
		return false
	}
	o, ok := other.(*ImportNode)
	if !ok {
		debug("Failed to cast to ImportNode")
		return false
	}
	if n.Path != o.Path {
		if n.Path != nil {
			return n.Path.IsEqual(o.Path)
		}
	}
	return false
}
func NewSetenvNode(info token.FileInfo, name string, assign Node) (*SetenvNode, error) {
	if assign != nil && assign.Type() != NodeAssign &&
		assign.Type() != NodeExecAssign {
		return nil, errors.New("Invalid assignment in setenv")
	}
	return &SetenvNode{
		NodeType: NodeSetenv,
		FileInfo: info,
		Name:   name,
		assign: assign,
	}, nil
}
func (n *SetenvNode) Assignment() Node { return n.assign }
func (n *SetenvNode) IsEqual(other Node) bool {
	if !n.equal(n, other) {
		return false
	}
	o, ok := other.(*SetenvNode)
	if !ok {
		debug("Failed to convert to SetenvNode")
		return false
	}
	if n.assign != o.assign {
		if !n.assign.IsEqual(o.assign) {
			return false
		}
	}
	return n.Name == o.Name
}
func NewNameNode(info token.FileInfo, ident string, index Expr) *NameNode {
	return &NameNode{
		NodeType: NodeName,
		FileInfo: info,
		Ident:    ident,
		Index:    index,
	}
}
func (n *NameNode) IsEqual(other Node) bool {
	if !n.equal(n, other) {
		return false
	}
	o, ok := other.(*NameNode)
	if !ok {
		debug("Failed to convert to NameNode")
		return false
	}
	if n.Ident != o.Ident {
		return false
	}
	if n.Index == o.Index {
		return true
	}
	if n.Index != nil {
		return n.Index.IsEqual(o.Index)
	}
	return false
}
func NewAssignNode(info token.FileInfo, names []*NameNode, values []Expr) *AssignNode {
	return &AssignNode{
		NodeType: NodeAssign,
		FileInfo: info,
		eqSpace:  -1,
		Names:  names,
		Values: values,
	}
}
func NewSingleAssignNode(info token.FileInfo, name *NameNode, value Expr) *AssignNode {
	return NewAssignNode(info, []*NameNode{name}, []Expr{value})
}
func (n *AssignNode) names() []*NameNode   { return n.Names }
func (n *AssignNode) getEqSpace() int      { return n.eqSpace }
func (n *AssignNode) setEqSpace(value int) { n.eqSpace = value }
func (n *AssignNode) IsEqual(other Node) bool {
	if !n.equal(n, other) {
		return false
	}
	o, ok := other.(*AssignNode)
	if !ok {
		debug("Failed to convert to AssignNode")
		return false
	}
	if len(n.Names) == len(o.Names) {
		for i := 0; i < len(n.Names); i++ {
			if !n.Names[i].IsEqual(o.Names[i]) {
				debug("Assignment identifier doesn't match: '%s' != '%s'",
					n.Names[i], o.Names[i])
				return false
			}
		}
	} else {
		return false
	}
	if len(n.Values) == len(o.Values) {
		for i := 0; i < len(n.Values); i++ {
			if !n.Values[i].IsEqual(o.Values[i]) {
				return false
			}
		}
	} else {
		return false
	}
	return true
}
func NewExecAssignNode(info token.FileInfo, names []*NameNode, n Node) (*ExecAssignNode, error) {
	if !n.Type().IsExecutable() {
		return nil, errors.New("NewExecAssignNode expects a CommandNode, PipeNode or FninvNode")
	}
	return &ExecAssignNode{
		NodeType: NodeExecAssign,
		FileInfo: info,
		Names:   names,
		cmd:     n,
		eqSpace: -1,
	}, nil
}
func (n *ExecAssignNode) names() []*NameNode   { return n.Names }
func (n *ExecAssignNode) getEqSpace() int      { return n.eqSpace }
func (n *ExecAssignNode) setEqSpace(value int) { n.eqSpace = value }
func (n *ExecAssignNode) Command() Node {
	return n.cmd
}
func (n *ExecAssignNode) SetCommand(c Node) {
	n.cmd = c
}
func (n *ExecAssignNode) IsEqual(other Node) bool {
	if !n.equal(n, other) {
		return false
	}
	o, ok := other.(*ExecAssignNode)
	if !ok {
		debug("Failed to convert to ExecAssignNode")
		return false
	}
	if len(n.Names) != len(o.Names) {
		return false
	}
	for i := 0; i < len(n.Names); i++ {
		if n.Names[i] != nil {
			if !n.Names[i].IsEqual(o.Names[i]) {
				debug("Exec assignment name differs")
				return false
			}
		}
	}
	if n.cmd == o.cmd {
		return true
	} else if n.cmd != nil {
		return n.cmd.IsEqual(o.cmd)
	}
	return false
}
func NewCommandNode(info token.FileInfo, name string, multiline bool) *CommandNode {
	return &CommandNode{
		NodeType: NodeCommand,
		FileInfo: info,
		name:  name,
		multi: multiline,
	}
}
func (n *CommandNode) IsMulti() bool   { return n.multi }
func (n *CommandNode) SetMulti(b bool) { n.multi = b }