github.com/NeowayLabs/nash@v0.2.2-0.20200127205349-a227041ffd50/parser/parse_test.go (about)

     1  package parser
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/madlambda/nash/ast"
     8  	"github.com/madlambda/nash/token"
     9  )
    10  
    11  func parserTest(name, content string, expected *ast.Tree, t *testing.T, enableReverse bool) *ast.Tree {
    12  	parser := NewParser(name, content)
    13  	tr, err := parser.Parse()
    14  
    15  	if err != nil {
    16  		t.Error(err)
    17  		t.Logf("Failed syntax: '%s'", content)
    18  		return nil
    19  	}
    20  
    21  	if tr == nil {
    22  		t.Errorf("Failed to parse")
    23  		return nil
    24  	}
    25  
    26  	if !expected.IsEqual(tr) {
    27  		t.Errorf("Expected: %s\n\nResult: %s\n", expected, tr)
    28  		t.Logf("Failed syntax: '%s'", content)
    29  		return tr
    30  	}
    31  
    32  	if !enableReverse {
    33  		return tr
    34  	}
    35  
    36  	// Test if the reverse of tree is the content again... *hard*
    37  	trcontent := strings.TrimSpace(tr.String())
    38  	content = strings.TrimSpace(content)
    39  
    40  	if content != trcontent {
    41  		t.Errorf(`Failed to reverse the tree.
    42  Expected:
    43  '%s'
    44  
    45  But got:
    46  '%s'
    47  `, content, trcontent)
    48  	}
    49  
    50  	return tr
    51  }
    52  
    53  func parserTestFail(t *testing.T, execStr string) {
    54  	parser := NewParser("", execStr)
    55  
    56  	tr, err := parser.Parse()
    57  
    58  	if err == nil {
    59  		t.Errorf("Parsing '%s' must fail", execStr)
    60  		return
    61  	}
    62  
    63  	if tr != nil {
    64  		t.Error("tr must be nil")
    65  		return
    66  	}
    67  }
    68  
    69  func TestParseSimple(t *testing.T) {
    70  	expected := ast.NewTree("parser simple")
    71  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
    72  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 0), "echo", false)
    73  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 6), "hello world", true))
    74  	ln.Push(cmd)
    75  
    76  	expected.Root = ln
    77  
    78  	parserTest("parser simple", `echo "hello world"`, expected, t, true)
    79  
    80  	cmd1 := ast.NewCommandNode(token.NewFileInfo(1, 0), "cat", false)
    81  	arg1 := ast.NewStringExpr(token.NewFileInfo(1, 4), "/etc/resolv.conf", false)
    82  	arg2 := ast.NewStringExpr(token.NewFileInfo(1, 21), "/etc/hosts", false)
    83  	cmd1.AddArg(arg1)
    84  	cmd1.AddArg(arg2)
    85  
    86  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
    87  	ln.Push(cmd1)
    88  	expected.Root = ln
    89  
    90  	parserTest("parser simple", `cat /etc/resolv.conf /etc/hosts`, expected, t, true)
    91  }
    92  
    93  func TestParseReverseGetSame(t *testing.T) {
    94  	parser := NewParser("reverse simple", "echo \"hello world\"")
    95  
    96  	tr, err := parser.Parse()
    97  
    98  	if err != nil {
    99  		t.Error(err)
   100  		return
   101  	}
   102  
   103  	if tr.String() != "echo \"hello world\"" {
   104  		t.Errorf("Failed to reverse tree: %s", tr.String())
   105  		return
   106  	}
   107  }
   108  
   109  func TestParsePipe(t *testing.T) {
   110  	expected := ast.NewTree("parser pipe")
   111  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   112  	first := ast.NewCommandNode(token.NewFileInfo(1, 0), "echo", false)
   113  	first.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 6), "hello world", true))
   114  
   115  	second := ast.NewCommandNode(token.NewFileInfo(1, 21), "awk", false)
   116  	second.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 26), "{print $1}", true))
   117  
   118  	pipe := ast.NewPipeNode(token.NewFileInfo(1, 19), false)
   119  	pipe.AddCmd(first)
   120  	pipe.AddCmd(second)
   121  
   122  	ln.Push(pipe)
   123  
   124  	expected.Root = ln
   125  
   126  	parserTest("parser pipe", `echo "hello world" | awk "{print $1}"`, expected, t, true)
   127  }
   128  
   129  func TestBasicSetEnvAssignment(t *testing.T) {
   130  	expected := ast.NewTree("simple set assignment")
   131  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   132  	set, err := ast.NewSetenvNode(token.NewFileInfo(1, 0), "test", nil)
   133  
   134  	if err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	ln.Push(set)
   139  	expected.Root = ln
   140  
   141  	parserTest("simple set assignment", `setenv test`, expected, t, true)
   142  
   143  	// setenv with assignment
   144  	expected = ast.NewTree("setenv with simple assignment")
   145  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   146  	assign := ast.NewSingleAssignNode(token.NewFileInfo(1, 7),
   147  		ast.NewNameNode(token.NewFileInfo(1, 7), "test", nil),
   148  		ast.NewStringExpr(token.NewFileInfo(1, 15), "hello", true))
   149  	set, err = ast.NewSetenvNode(token.NewFileInfo(1, 0), "test", assign)
   150  
   151  	if err != nil {
   152  		t.Fatal(err)
   153  	}
   154  
   155  	ln.Push(set)
   156  	expected.Root = ln
   157  
   158  	parserTest("setenv with simple assignment", `setenv test = "hello"`, expected, t, true)
   159  
   160  	expected = ast.NewTree("setenv with simple cmd assignment")
   161  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   162  
   163  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 15), "ls", false)
   164  
   165  	cmdAssign, err := ast.NewExecAssignNode(
   166  		token.NewFileInfo(1, 7),
   167  		[]*ast.NameNode{ast.NewNameNode(token.NewFileInfo(1, 7), "test", nil)},
   168  		cmd,
   169  	)
   170  
   171  	if err != nil {
   172  		t.Fatal(err)
   173  	}
   174  
   175  	set, err = ast.NewSetenvNode(token.NewFileInfo(1, 0), "test", cmdAssign)
   176  
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  
   181  	ln.Push(set)
   182  	expected.Root = ln
   183  
   184  	parserTest("simple assignment", `setenv test <= ls`, expected, t, true)
   185  }
   186  
   187  func TestBasicAssignment(t *testing.T) {
   188  	expected := ast.NewTree("simple assignment")
   189  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   190  	assign := ast.NewSingleAssignNode(token.NewFileInfo(1, 0),
   191  		ast.NewNameNode(token.NewFileInfo(1, 0), "test", nil),
   192  		ast.NewStringExpr(token.NewFileInfo(1, 8), "hello", true))
   193  	ln.Push(assign)
   194  	expected.Root = ln
   195  
   196  	parserTest("simple assignment", `test = "hello"`, expected, t, true)
   197  
   198  	// test concatenation of strings and variables
   199  
   200  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   201  
   202  	concats := make([]ast.Expr, 2, 2)
   203  	concats[0] = ast.NewStringExpr(token.NewFileInfo(1, 8), "hello", true)
   204  	concats[1] = ast.NewVarExpr(token.NewFileInfo(1, 15), "$var")
   205  
   206  	arg1 := ast.NewConcatExpr(token.NewFileInfo(1, 8), concats)
   207  
   208  	assign = ast.NewSingleAssignNode(token.NewFileInfo(1, 0),
   209  		ast.NewNameNode(token.NewFileInfo(1, 0), "test", nil),
   210  		arg1,
   211  	)
   212  
   213  	ln.Push(assign)
   214  	expected.Root = ln
   215  
   216  	parserTest("test", `test = "hello"+$var`, expected, t, true)
   217  
   218  	for _, test := range []string{
   219  		"test=hello",
   220  		"test = hello",
   221  		"test = 1",
   222  		"test = false",
   223  		"test = -1",
   224  		`test = "1", "2"`,
   225  	} {
   226  		parserTestFail(t, test)
   227  	}
   228  }
   229  
   230  func TestVarAssignment(t *testing.T) {
   231  	expected := ast.NewTree("var assignment")
   232  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   233  	varAssign := ast.NewVarAssignDecl(token.NewFileInfo(1, 0),
   234  		ast.NewSingleAssignNode(token.NewFileInfo(1, 4),
   235  			ast.NewNameNode(token.NewFileInfo(1, 4), "test", nil),
   236  			ast.NewStringExpr(token.NewFileInfo(1, 12), "hello", true)),
   237  	)
   238  	ln.Push(varAssign)
   239  	expected.Root = ln
   240  
   241  	parserTest("var assignment", `var test = "hello"`, expected, t, true)
   242  
   243  	for _, test := range []string{
   244  		"var test=hello",
   245  		"var",
   246  		"var test",
   247  		"var test = false",
   248  		"var test = -1",
   249  		`var test = "1", "2"`,
   250  	} {
   251  		parserTestFail(t, test)
   252  	}
   253  }
   254  
   255  func TestParseMultipleAssign(t *testing.T) {
   256  	one := ast.NewNameNode(token.NewFileInfo(1, 0), "one", nil)
   257  	two := ast.NewNameNode(token.NewFileInfo(1, 5), "two", nil)
   258  	value1 := ast.NewStringExpr(token.NewFileInfo(1, 12), "1", true)
   259  	value2 := ast.NewStringExpr(token.NewFileInfo(1, 17), "2", true)
   260  	assign := ast.NewAssignNode(token.NewFileInfo(1, 0),
   261  		[]*ast.NameNode{one, two},
   262  		[]ast.Expr{value1, value2},
   263  	)
   264  
   265  	expected := ast.NewTree("tuple assignment")
   266  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   267  	ln.Push(assign)
   268  	expected.Root = ln
   269  
   270  	parserTest("tuple assignment", `one, two = "1", "2"`, expected, t, true)
   271  
   272  	lvalue1 := ast.NewListExpr(token.NewFileInfo(1, 11), []ast.Expr{})
   273  	value2 = ast.NewStringExpr(token.NewFileInfo(1, 16), "2", true)
   274  	assign = ast.NewAssignNode(token.NewFileInfo(1, 0),
   275  		[]*ast.NameNode{one, two},
   276  		[]ast.Expr{lvalue1, value2},
   277  	)
   278  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   279  	ln.Push(assign)
   280  	expected.Root = ln
   281  	parserTest("tuple assignment", `one, two = (), "2"`, expected, t, true)
   282  
   283  }
   284  
   285  func TestParseMultipleExecAssignment(t *testing.T) {
   286  	expected := ast.NewTree("multiple cmd assignment")
   287  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   288  
   289  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 16), "ls", false)
   290  
   291  	assign, err := ast.NewExecAssignNode(token.NewFileInfo(1, 0),
   292  		[]*ast.NameNode{
   293  			ast.NewNameNode(token.NewFileInfo(1, 0), "test", nil),
   294  			ast.NewNameNode(token.NewFileInfo(1, 6), "status", nil),
   295  		},
   296  		cmd,
   297  	)
   298  
   299  	if err != nil {
   300  		t.Error(err)
   301  		return
   302  	}
   303  
   304  	ln.Push(assign)
   305  	expected.Root = ln
   306  
   307  	parserTest("multiple cmd assignment", `test, status <= ls`, expected, t, true)
   308  
   309  	expected = ast.NewTree("multiple cmd assignment")
   310  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   311  
   312  	cmd = ast.NewCommandNode(token.NewFileInfo(1, 19), "ls", false)
   313  
   314  	assign, err = ast.NewExecAssignNode(token.NewFileInfo(1, 0),
   315  		[]*ast.NameNode{
   316  			ast.NewNameNode(token.NewFileInfo(1, 0), "test", ast.NewIntExpr(token.NewFileInfo(1, 5), 0)),
   317  			ast.NewNameNode(token.NewFileInfo(1, 9), "status", nil),
   318  		},
   319  		cmd,
   320  	)
   321  
   322  	if err != nil {
   323  		t.Error(err)
   324  		return
   325  	}
   326  
   327  	ln.Push(assign)
   328  	expected.Root = ln
   329  
   330  	parserTest("multiple cmd assignment", `test[0], status <= ls`, expected, t, true)
   331  }
   332  
   333  func TestParseInvalidIndexing(t *testing.T) {
   334  	// test indexed assignment
   335  	parser := NewParser("invalid", `test[a] = "a"`)
   336  
   337  	_, err := parser.Parse()
   338  
   339  	if err == nil {
   340  		t.Error("Parse must fail")
   341  		return
   342  	} else if err.Error() != "invalid:1:5: Expected number or variable in index. Found ARG" {
   343  		t.Error("Invalid err msg")
   344  		return
   345  	}
   346  
   347  	parser = NewParser("invalid", `test[] = "a"`)
   348  
   349  	_, err = parser.Parse()
   350  
   351  	if err == nil {
   352  		t.Error("Parse must fail")
   353  		return
   354  	} else if err.Error() != "invalid:1:5: Expected number or variable in index. Found ]" {
   355  		t.Error("Invalid err msg")
   356  		return
   357  	}
   358  
   359  	parser = NewParser("invalid", `test[10.0] = "a"`)
   360  
   361  	_, err = parser.Parse()
   362  
   363  	if err == nil {
   364  		t.Error("Parse must fail")
   365  		return
   366  	} else if err.Error() != "invalid:1:5: Expected number or variable in index. Found ARG" {
   367  		t.Error("Invalid err msg")
   368  		return
   369  	}
   370  }
   371  
   372  func TestParseListAssignment(t *testing.T) {
   373  	expected := ast.NewTree("list assignment")
   374  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   375  
   376  	values := make([]ast.Expr, 0, 4)
   377  
   378  	values = append(values,
   379  		ast.NewStringExpr(token.NewFileInfo(2, 1), "plan9", false),
   380  		ast.NewStringExpr(token.NewFileInfo(3, 1), "from", false),
   381  		ast.NewStringExpr(token.NewFileInfo(4, 1), "bell", false),
   382  		ast.NewStringExpr(token.NewFileInfo(5, 1), "labs", false),
   383  	)
   384  
   385  	elem := ast.NewListExpr(token.NewFileInfo(1, 7), values)
   386  
   387  	assign := ast.NewSingleAssignNode(token.NewFileInfo(1, 0),
   388  		ast.NewNameNode(token.NewFileInfo(1, 0), "test", nil),
   389  		elem,
   390  	)
   391  
   392  	ln.Push(assign)
   393  	expected.Root = ln
   394  
   395  	parserTest("list assignment", `test = (
   396  	plan9
   397  	from
   398  	bell
   399  	labs
   400  )`, expected, t, false)
   401  }
   402  
   403  func TestParseListOfListsAssignment(t *testing.T) {
   404  	expected := ast.NewTree("list assignment")
   405  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   406  
   407  	plan9 := make([]ast.Expr, 0, 4)
   408  	plan9 = append(plan9,
   409  		ast.NewStringExpr(token.NewFileInfo(2, 2), "plan9", false),
   410  		ast.NewStringExpr(token.NewFileInfo(2, 8), "from", false),
   411  		ast.NewStringExpr(token.NewFileInfo(2, 13), "bell", false),
   412  		ast.NewStringExpr(token.NewFileInfo(2, 18), "labs", false),
   413  	)
   414  
   415  	elem1 := ast.NewListExpr(token.NewFileInfo(2, 1), plan9)
   416  
   417  	linux := make([]ast.Expr, 0, 2)
   418  	linux = append(linux, ast.NewStringExpr(token.NewFileInfo(3, 2), "linux", false))
   419  	linux = append(linux, ast.NewStringExpr(token.NewFileInfo(3, 8), "kernel", false))
   420  
   421  	elem2 := ast.NewListExpr(token.NewFileInfo(3, 1), linux)
   422  
   423  	values := make([]ast.Expr, 2)
   424  	values[0] = elem1
   425  	values[1] = elem2
   426  
   427  	elem := ast.NewListExpr(token.NewFileInfo(1, 7), values)
   428  
   429  	assign := ast.NewSingleAssignNode(token.NewFileInfo(1, 0),
   430  		ast.NewNameNode(token.NewFileInfo(1, 0), "test", nil),
   431  		elem,
   432  	)
   433  
   434  	ln.Push(assign)
   435  	expected.Root = ln
   436  
   437  	parserTest("list assignment", `test = (
   438  	(plan9 from bell labs)
   439  	(linux kernel)
   440  	)`, expected, t, false)
   441  }
   442  
   443  func TestParseCmdAssignment(t *testing.T) {
   444  	expected := ast.NewTree("simple cmd assignment")
   445  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   446  
   447  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 8), "ls", false)
   448  
   449  	assign, err := ast.NewExecAssignNode(token.NewFileInfo(1, 0),
   450  		[]*ast.NameNode{ast.NewNameNode(token.NewFileInfo(1, 0), "test", nil)},
   451  		cmd,
   452  	)
   453  
   454  	if err != nil {
   455  		t.Error(err)
   456  		return
   457  	}
   458  
   459  	ln.Push(assign)
   460  	expected.Root = ln
   461  
   462  	parserTest("simple assignment", `test <= ls`, expected, t, true)
   463  }
   464  
   465  func TestParseInvalidEmpty(t *testing.T) {
   466  	parser := NewParser("invalid", ";")
   467  
   468  	_, err := parser.Parse()
   469  
   470  	if err == nil {
   471  		t.Error("Parse must fail")
   472  		return
   473  	}
   474  }
   475  
   476  func TestParsePathCommand(t *testing.T) {
   477  	expected := ast.NewTree("parser simple")
   478  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   479  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 0), "/bin/echo", false)
   480  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 11), "hello world", true))
   481  	ln.Push(cmd)
   482  
   483  	expected.Root = ln
   484  
   485  	parserTest("parser simple", `/bin/echo "hello world"`, expected, t, true)
   486  }
   487  
   488  func TestParseWithShebang(t *testing.T) {
   489  	expected := ast.NewTree("parser shebang")
   490  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   491  	cmt := ast.NewCommentNode(token.NewFileInfo(1, 0), "#!/bin/nash")
   492  	cmd := ast.NewCommandNode(token.NewFileInfo(3, 0), "echo", false)
   493  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(3, 5), "bleh", false))
   494  	ln.Push(cmt)
   495  	ln.Push(cmd)
   496  
   497  	expected.Root = ln
   498  
   499  	parserTest("parser shebang", `#!/bin/nash
   500  
   501  echo bleh
   502  `, expected, t, true)
   503  }
   504  
   505  func TestParseEmptyFile(t *testing.T) {
   506  	expected := ast.NewTree("empty file")
   507  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   508  	expected.Root = ln
   509  
   510  	parserTest("empty file", "", expected, t, true)
   511  }
   512  
   513  func TestParseSingleCommand(t *testing.T) {
   514  	expected := ast.NewTree("single command")
   515  	expected.Root = ast.NewBlockNode(token.NewFileInfo(1, 0))
   516  	expected.Root.Push(ast.NewCommandNode(token.NewFileInfo(1, 0), "bleh", false))
   517  
   518  	parserTest("single command", `bleh`, expected, t, true)
   519  }
   520  
   521  func TestParseRedirectSimple(t *testing.T) {
   522  	expected := ast.NewTree("redirect")
   523  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   524  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 0), "cmd", false)
   525  	redir := ast.NewRedirectNode(token.NewFileInfo(1, 4))
   526  	redir.SetMap(2, ast.RedirMapSupress)
   527  	cmd.AddRedirect(redir)
   528  	ln.Push(cmd)
   529  
   530  	expected.Root = ln
   531  
   532  	parserTest("simple redirect", `cmd >[2=]`, expected, t, true)
   533  
   534  	expected = ast.NewTree("redirect2")
   535  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   536  	cmd = ast.NewCommandNode(token.NewFileInfo(1, 0), "cmd", false)
   537  	redir = ast.NewRedirectNode(token.NewFileInfo(1, 4))
   538  	redir.SetMap(2, 1)
   539  	cmd.AddRedirect(redir)
   540  	ln.Push(cmd)
   541  
   542  	expected.Root = ln
   543  
   544  	parserTest("simple redirect", `cmd >[2=1]`, expected, t, true)
   545  }
   546  
   547  func TestParseRedirectWithLocation(t *testing.T) {
   548  	expected := ast.NewTree("redirect with location")
   549  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   550  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 0), "cmd", false)
   551  	redir := ast.NewRedirectNode(token.NewFileInfo(1, 4))
   552  	redir.SetMap(2, ast.RedirMapNoValue)
   553  	redir.SetLocation(ast.NewStringExpr(token.NewFileInfo(1, 9), "/var/log/service.log", false))
   554  	cmd.AddRedirect(redir)
   555  	ln.Push(cmd)
   556  
   557  	expected.Root = ln
   558  
   559  	parserTest("simple redirect", `cmd >[2] /var/log/service.log`, expected, t, true)
   560  }
   561  
   562  func TestParseRedirectMultiples(t *testing.T) {
   563  	expected := ast.NewTree("redirect multiples")
   564  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   565  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 0), "cmd", false)
   566  	redir1 := ast.NewRedirectNode(token.NewFileInfo(1, 4))
   567  	redir2 := ast.NewRedirectNode(token.NewFileInfo(1, 11))
   568  
   569  	redir1.SetMap(1, 2)
   570  	redir2.SetMap(2, ast.RedirMapSupress)
   571  
   572  	cmd.AddRedirect(redir1)
   573  	cmd.AddRedirect(redir2)
   574  	ln.Push(cmd)
   575  
   576  	expected.Root = ln
   577  
   578  	parserTest("multiple redirects", `cmd >[1=2] >[2=]`, expected, t, true)
   579  }
   580  
   581  func TestParseCommandWithStringsEqualsNot(t *testing.T) {
   582  	expected := ast.NewTree("strings works as expected")
   583  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   584  	cmd1 := ast.NewCommandNode(token.NewFileInfo(1, 0), "echo", false)
   585  	cmd2 := ast.NewCommandNode(token.NewFileInfo(2, 0), "echo", false)
   586  	cmd1.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 5), "hello", false))
   587  	cmd2.AddArg(ast.NewStringExpr(token.NewFileInfo(2, 6), "hello", true))
   588  
   589  	ln.Push(cmd1)
   590  	ln.Push(cmd2)
   591  	expected.Root = ln
   592  
   593  	parserTest("strings works as expected", `echo hello
   594  echo "hello"
   595  `, expected, t, true)
   596  }
   597  
   598  func TestParseCommandSeparatedBySemicolon(t *testing.T) {
   599  	expected := ast.NewTree("semicolon")
   600  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   601  	cmd1 := ast.NewCommandNode(token.NewFileInfo(1, 0), "echo", false)
   602  	cmd2 := ast.NewCommandNode(token.NewFileInfo(1, 11), "echo", false)
   603  	cmd1.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 5), "hello", false))
   604  	cmd2.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 16), "world", false))
   605  
   606  	ln.Push(cmd1)
   607  	ln.Push(cmd2)
   608  	expected.Root = ln
   609  
   610  	parserTest("strings works as expected", `echo hello;echo world`, expected, t, false)
   611  }
   612  
   613  func TestParseStringNotFinished(t *testing.T) {
   614  	parser := NewParser("string not finished", `echo "hello world`)
   615  	tr, err := parser.Parse()
   616  
   617  	if err == nil {
   618  		t.Error("Error: should fail")
   619  		return
   620  	}
   621  
   622  	if tr != nil {
   623  		t.Errorf("Failed to parse")
   624  		return
   625  	}
   626  }
   627  
   628  func TestParseCd(t *testing.T) {
   629  	expected := ast.NewTree("test cd")
   630  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   631  	cd := ast.NewCommandNode(token.NewFileInfo(1, 0), "cd", false)
   632  	arg := ast.NewStringExpr(token.NewFileInfo(1, 3), "/tmp", false)
   633  	cd.AddArg(arg)
   634  	ln.Push(cd)
   635  	expected.Root = ln
   636  
   637  	parserTest("test cd", "cd /tmp", expected, t, true)
   638  
   639  	// test cd into home
   640  	expected = ast.NewTree("test cd into home")
   641  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   642  	cd = ast.NewCommandNode(token.NewFileInfo(1, 0), "cd", false)
   643  	ln.Push(cd)
   644  	expected.Root = ln
   645  
   646  	parserTest("test cd into home", "cd", expected, t, true)
   647  
   648  	// test cd ..
   649  	expected = ast.NewTree("test cd ..")
   650  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   651  	cd = ast.NewCommandNode(token.NewFileInfo(1, 0), "cd", false)
   652  	cd.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 3), "..", false))
   653  	ln.Push(cd)
   654  	expected.Root = ln
   655  
   656  	parserTest("test cd ..", "cd ..", expected, t, true)
   657  
   658  	expected = ast.NewTree("cd into HOME by setenv")
   659  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   660  
   661  	assign := ast.NewSingleAssignNode(token.NewFileInfo(1, 0),
   662  		ast.NewNameNode(token.NewFileInfo(1, 0), "HOME", nil),
   663  		ast.NewStringExpr(token.NewFileInfo(1, 8), "/", true),
   664  	)
   665  
   666  	set, err := ast.NewSetenvNode(token.NewFileInfo(3, 0), "HOME", nil)
   667  
   668  	if err != nil {
   669  		t.Fatal(err)
   670  	}
   671  
   672  	cd = ast.NewCommandNode(token.NewFileInfo(5, 0), "cd", false)
   673  	pwd := ast.NewCommandNode(token.NewFileInfo(6, 0), "pwd", false)
   674  
   675  	ln.Push(assign)
   676  	ln.Push(set)
   677  	ln.Push(cd)
   678  	ln.Push(pwd)
   679  
   680  	expected.Root = ln
   681  
   682  	parserTest("test cd into HOME by setenv", `HOME = "/"
   683  
   684  setenv HOME
   685  
   686  cd
   687  pwd`, expected, t, true)
   688  
   689  	// Test cd into custom variable
   690  	expected = ast.NewTree("cd into variable value")
   691  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   692  
   693  	arg = ast.NewStringExpr(token.NewFileInfo(1, 10), "/home/i4k/gopath", true)
   694  
   695  	assign = ast.NewSingleAssignNode(token.NewFileInfo(1, 0),
   696  		ast.NewNameNode(token.NewFileInfo(1, 0), "GOPATH", nil),
   697  		arg,
   698  	)
   699  
   700  	cd = ast.NewCommandNode(token.NewFileInfo(3, 0), "cd", false)
   701  	arg2 := ast.NewVarExpr(token.NewFileInfo(3, 3), "$GOPATH")
   702  	cd.AddArg(arg2)
   703  
   704  	ln.Push(assign)
   705  	ln.Push(cd)
   706  
   707  	expected.Root = ln
   708  
   709  	parserTest("test cd into variable value", `GOPATH = "/home/i4k/gopath"
   710  
   711  cd $GOPATH`, expected, t, true)
   712  
   713  	// Test cd into custom variable
   714  	expected = ast.NewTree("cd into variable value with concat")
   715  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   716  
   717  	arg = ast.NewStringExpr(token.NewFileInfo(1, 10), "/home/i4k/gopath", true)
   718  
   719  	assign = ast.NewSingleAssignNode(token.NewFileInfo(1, 0),
   720  		ast.NewNameNode(token.NewFileInfo(1, 0), "GOPATH", nil),
   721  		arg,
   722  	)
   723  
   724  	concat := make([]ast.Expr, 0, 2)
   725  	concat = append(concat, ast.NewVarExpr(token.NewFileInfo(3, 3), "$GOPATH"))
   726  	concat = append(concat, ast.NewStringExpr(token.NewFileInfo(3, 12), "/src/github.com", true))
   727  
   728  	cd = ast.NewCommandNode(token.NewFileInfo(3, 0), "cd", false)
   729  	carg := ast.NewConcatExpr(token.NewFileInfo(3, 3), concat)
   730  	cd.AddArg(carg)
   731  
   732  	ln.Push(assign)
   733  	ln.Push(cd)
   734  
   735  	expected.Root = ln
   736  
   737  	parserTest("test cd into variable value", `GOPATH = "/home/i4k/gopath"
   738  
   739  cd $GOPATH+"/src/github.com"`, expected, t, true)
   740  
   741  }
   742  
   743  func TestParseConcatOfIndexedVar(t *testing.T) {
   744  	expected := ast.NewTree("concat indexed var")
   745  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   746  	arg1 := ast.NewStringExpr(token.NewFileInfo(1, 4), "ec2", false)
   747  	arg2 := ast.NewStringExpr(token.NewFileInfo(1, 8), "create-tags", false)
   748  	arg3 := ast.NewStringExpr(token.NewFileInfo(1, 20), "--resources", false)
   749  	arg4 := ast.NewVarExpr(token.NewFileInfo(1, 32), "$resource")
   750  	arg5 := ast.NewStringExpr(token.NewFileInfo(1, 42), "--tags", false)
   751  
   752  	c1 := ast.NewStringExpr(token.NewFileInfo(1, 50), "Key=", true)
   753  	c2 := ast.NewIndexExpr(token.NewFileInfo(1, 56),
   754  		ast.NewVarExpr(token.NewFileInfo(1, 56), "$tag"),
   755  		ast.NewIntExpr(token.NewFileInfo(1, 61), 0))
   756  	c3 := ast.NewStringExpr(token.NewFileInfo(1, 65), ",Value=", true)
   757  	c4 := ast.NewIndexExpr(token.NewFileInfo(1, 74),
   758  		ast.NewVarExpr(token.NewFileInfo(1, 74), "$tag"),
   759  		ast.NewIntExpr(token.NewFileInfo(1, 79), 1))
   760  	cvalues := make([]ast.Expr, 4)
   761  	cvalues[0] = c1
   762  	cvalues[1] = c2
   763  	cvalues[2] = c3
   764  	cvalues[3] = c4
   765  
   766  	arg6 := ast.NewConcatExpr(token.NewFileInfo(1, 50), cvalues)
   767  
   768  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 0), "aws", false)
   769  	cmd.AddArg(arg1)
   770  	cmd.AddArg(arg2)
   771  	cmd.AddArg(arg3)
   772  	cmd.AddArg(arg4)
   773  	cmd.AddArg(arg5)
   774  	cmd.AddArg(arg6)
   775  
   776  	ln.Push(cmd)
   777  	expected.Root = ln
   778  
   779  	parserTest("concat indexed var",
   780  		`aws ec2 create-tags --resources $resource --tags "Key="+$tag[0]+",Value="+$tag[1]`,
   781  		expected, t, true)
   782  }
   783  
   784  func TestParseRfork(t *testing.T) {
   785  	expected := ast.NewTree("test rfork")
   786  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   787  	cmd1 := ast.NewRforkNode(token.NewFileInfo(1, 0))
   788  	f1 := ast.NewStringExpr(token.NewFileInfo(1, 6), "u", false)
   789  	cmd1.SetFlags(f1)
   790  	ln.Push(cmd1)
   791  	expected.Root = ln
   792  
   793  	parserTest("test rfork", "rfork u", expected, t, true)
   794  }
   795  
   796  func TestParseRforkWithBlock(t *testing.T) {
   797  	expected := ast.NewTree("rfork with block")
   798  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   799  	rfork := ast.NewRforkNode(token.NewFileInfo(1, 0))
   800  	arg := ast.NewStringExpr(token.NewFileInfo(1, 6), "u", false)
   801  	rfork.SetFlags(arg)
   802  
   803  	insideFork := ast.NewCommandNode(token.NewFileInfo(2, 1), "mount", false)
   804  	insideFork.AddArg(ast.NewStringExpr(token.NewFileInfo(2, 7), "-t", false))
   805  	insideFork.AddArg(ast.NewStringExpr(token.NewFileInfo(2, 10), "proc", false))
   806  	insideFork.AddArg(ast.NewStringExpr(token.NewFileInfo(2, 15), "proc", false))
   807  	insideFork.AddArg(ast.NewStringExpr(token.NewFileInfo(2, 20), "/proc", false))
   808  
   809  	bln := ast.NewBlockNode(token.NewFileInfo(1, 8))
   810  	bln.Push(insideFork)
   811  	subtree := ast.NewTree("rfork")
   812  	subtree.Root = bln
   813  
   814  	rfork.SetTree(subtree)
   815  
   816  	ln.Push(rfork)
   817  	expected.Root = ln
   818  
   819  	parserTest("rfork with block", `rfork u {
   820  	mount -t proc proc /proc
   821  }
   822  `, expected, t, true)
   823  
   824  }
   825  
   826  func TestUnpairedRforkBlocks(t *testing.T) {
   827  	parser := NewParser("unpaired", "rfork u {")
   828  
   829  	_, err := parser.Parse()
   830  
   831  	if err == nil {
   832  		t.Errorf("Should fail because of unpaired open/close blocks")
   833  		return
   834  	}
   835  }
   836  
   837  func TestParseImport(t *testing.T) {
   838  	expected := ast.NewTree("test import")
   839  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   840  	importStmt := ast.NewImportNode(token.NewFileInfo(1, 0),
   841  		ast.NewStringExpr(token.NewFileInfo(1, 7), "env.sh", false))
   842  	ln.Push(importStmt)
   843  	expected.Root = ln
   844  
   845  	parserTest("test import", "import env.sh", expected, t, true)
   846  
   847  	expected = ast.NewTree("test import with quotes")
   848  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   849  	importStmt = ast.NewImportNode(token.NewFileInfo(1, 0),
   850  		ast.NewStringExpr(token.NewFileInfo(1, 8), "env.sh", true))
   851  	ln.Push(importStmt)
   852  	expected.Root = ln
   853  
   854  	parserTest("test import", `import "env.sh"`, expected, t, true)
   855  }
   856  
   857  func TestParseIf(t *testing.T) {
   858  	expected := ast.NewTree("test if")
   859  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   860  	ifDecl := ast.NewIfNode(token.NewFileInfo(1, 0))
   861  	ifDecl.SetLvalue(ast.NewStringExpr(token.NewFileInfo(1, 4), "test", true))
   862  	ifDecl.SetRvalue(ast.NewStringExpr(token.NewFileInfo(1, 14), "other", true))
   863  	ifDecl.SetOp("==")
   864  
   865  	subBlock := ast.NewBlockNode(token.NewFileInfo(1, 21))
   866  	cmd := ast.NewCommandNode(token.NewFileInfo(2, 1), "pwd", false)
   867  	subBlock.Push(cmd)
   868  
   869  	ifTree := ast.NewTree("if block")
   870  	ifTree.Root = subBlock
   871  
   872  	ifDecl.SetIfTree(ifTree)
   873  
   874  	ln.Push(ifDecl)
   875  	expected.Root = ln
   876  
   877  	parserTest("test if", `if "test" == "other" {
   878  	pwd
   879  }`, expected, t, true)
   880  
   881  	expected = ast.NewTree("test if")
   882  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   883  	ifDecl = ast.NewIfNode(token.NewFileInfo(1, 0))
   884  	ifDecl.SetLvalue(ast.NewStringExpr(token.NewFileInfo(1, 4), "", true))
   885  	ifDecl.SetRvalue(ast.NewStringExpr(token.NewFileInfo(1, 10), "other", true))
   886  	ifDecl.SetOp("!=")
   887  
   888  	subBlock = ast.NewBlockNode(token.NewFileInfo(1, 17))
   889  	cmd = ast.NewCommandNode(token.NewFileInfo(2, 1), "pwd", false)
   890  	subBlock.Push(cmd)
   891  
   892  	ifTree = ast.NewTree("if block")
   893  	ifTree.Root = subBlock
   894  
   895  	ifDecl.SetIfTree(ifTree)
   896  
   897  	ln.Push(ifDecl)
   898  	expected.Root = ln
   899  
   900  	parserTest("test if", `if "" != "other" {
   901  	pwd
   902  }`, expected, t, true)
   903  }
   904  
   905  func TestParseFuncall(t *testing.T) {
   906  	expected := ast.NewTree("fn inv")
   907  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
   908  	aFn := ast.NewFnInvNode(token.NewFileInfo(1, 0), "a")
   909  	ln.Push(aFn)
   910  	expected.Root = ln
   911  
   912  	parserTest("test basic fn inv", `a()`, expected, t, true)
   913  
   914  	expected = ast.NewTree("fn inv")
   915  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   916  	aFn = ast.NewFnInvNode(token.NewFileInfo(1, 0), "a")
   917  	bFn := ast.NewFnInvNode(token.NewFileInfo(1, 2), "b")
   918  	aFn.AddArg(bFn)
   919  	ln.Push(aFn)
   920  	expected.Root = ln
   921  
   922  	parserTest("test fn composition", `a(b())`, expected, t, true)
   923  
   924  	expected = ast.NewTree("fn inv")
   925  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   926  	aFn = ast.NewFnInvNode(token.NewFileInfo(1, 0), "a")
   927  	bFn = ast.NewFnInvNode(token.NewFileInfo(1, 2), "b")
   928  	b2Fn := ast.NewFnInvNode(token.NewFileInfo(1, 7), "b")
   929  	aFn.AddArg(bFn)
   930  	aFn.AddArg(b2Fn)
   931  	ln.Push(aFn)
   932  	expected.Root = ln
   933  
   934  	parserTest("test fn composition", `a(b(), b())`, expected, t, true)
   935  
   936  	expected = ast.NewTree("fn inv")
   937  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   938  	aFn = ast.NewFnInvNode(token.NewFileInfo(1, 0), "a")
   939  	bFn = ast.NewFnInvNode(token.NewFileInfo(1, 2), "b")
   940  	b2Fn = ast.NewFnInvNode(token.NewFileInfo(1, 4), "b")
   941  	bFn.AddArg(b2Fn)
   942  	aFn.AddArg(bFn)
   943  	ln.Push(aFn)
   944  	expected.Root = ln
   945  
   946  	parserTest("test fn composition", `a(b(b()))`, expected, t, true)
   947  
   948  	expected = ast.NewTree("fn inv list")
   949  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
   950  	aFn = ast.NewFnInvNode(token.NewFileInfo(1, 0), "a")
   951  	lExpr := ast.NewListExpr(token.NewFileInfo(1, 2), []ast.Expr{
   952  		ast.NewStringExpr(token.NewFileInfo(1, 4), "1", true),
   953  		ast.NewStringExpr(token.NewFileInfo(1, 8), "2", true),
   954  		ast.NewStringExpr(token.NewFileInfo(1, 12), "3", true),
   955  	})
   956  	aFn.AddArg(lExpr)
   957  	ln.Push(aFn)
   958  	expected.Root = ln
   959  	parserTest("test fn list arg", `a(("1" "2" "3"))`, expected, t, true)
   960  
   961  	// test valid funcall syntaxes (do not verify AST)
   962  	for _, tc := range []string{
   963  		`func()`,
   964  		`func(())`, // empty list
   965  		`func($a)`,
   966  		`_($a, $b)`,
   967  		`func($a())`,
   968  		`func($a(), $b())`,
   969  		`func($a($b($c())))`,
   970  		`func($a("a"))`,
   971  		`__((((()))))`, // perfect fit for a nash obfuscating code contest
   972  		`_(
   973  			()
   974  		)`,
   975  		`_(
   976  		(), (), (), (), (),
   977  		)`,
   978  		`_((() () () () ()))`,
   979  		`deploy((bomb shell))`, // unquoted list elements are still supported :-(
   980  		`func("a", ())`,
   981  		`_($a+$b)`,
   982  		`_($a+"")`,
   983  		`_(""+$a)`,
   984  		`func((()()))`,
   985  	} {
   986  		parser := NewParser("test", tc)
   987  		_, err := parser.Parse()
   988  		if err != nil {
   989  			t.Fatal(err)
   990  		}
   991  	}
   992  }
   993  
   994  func TestParseFuncallInvalid(t *testing.T) {
   995  	for _, tc := range []string{
   996  		`test(()`,
   997  		`_())`,
   998  		`func(a)`,
   999  		`func("a", a)`,
  1000  		`func(_(((((()))))`,
  1001  		`func(()+())`,
  1002  		`func("1"+("2" "3"))`,
  1003  		`func(()())`,
  1004  	} {
  1005  		parser := NewParser("test", tc)
  1006  		_, err := parser.Parse()
  1007  		if err == nil {
  1008  			t.Fatalf("Syntax '%s' must fail...", tc)
  1009  		}
  1010  	}
  1011  }
  1012  
  1013  func TestParseIfFnInv(t *testing.T) {
  1014  	expected := ast.NewTree("test if")
  1015  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1016  	ifDecl := ast.NewIfNode(token.NewFileInfo(1, 0))
  1017  	ifDecl.SetLvalue(ast.NewFnInvNode(token.NewFileInfo(1, 3), "test"))
  1018  	ifDecl.SetRvalue(ast.NewStringExpr(token.NewFileInfo(1, 14), "other", true))
  1019  	ifDecl.SetOp("==")
  1020  
  1021  	subBlock := ast.NewBlockNode(token.NewFileInfo(1, 21))
  1022  	cmd := ast.NewCommandNode(token.NewFileInfo(2, 1), "pwd", false)
  1023  	subBlock.Push(cmd)
  1024  
  1025  	ifTree := ast.NewTree("if block")
  1026  	ifTree.Root = subBlock
  1027  
  1028  	ifDecl.SetIfTree(ifTree)
  1029  
  1030  	ln.Push(ifDecl)
  1031  	expected.Root = ln
  1032  
  1033  	parserTest("test if", `if test() == "other" {
  1034  	pwd
  1035  }`, expected, t, true)
  1036  
  1037  	expected = ast.NewTree("test if")
  1038  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1039  	ifDecl = ast.NewIfNode(token.NewFileInfo(1, 0))
  1040  
  1041  	fnInv := ast.NewFnInvNode(token.NewFileInfo(1, 3), "test")
  1042  	fnInv.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 9), "bleh", true))
  1043  	ifDecl.SetLvalue(fnInv)
  1044  	ifDecl.SetRvalue(ast.NewStringExpr(token.NewFileInfo(1, 20), "other", true))
  1045  	ifDecl.SetOp("!=")
  1046  
  1047  	subBlock = ast.NewBlockNode(token.NewFileInfo(1, 27))
  1048  	cmd = ast.NewCommandNode(token.NewFileInfo(2, 1), "pwd", false)
  1049  	subBlock.Push(cmd)
  1050  
  1051  	ifTree = ast.NewTree("if block")
  1052  	ifTree.Root = subBlock
  1053  
  1054  	ifDecl.SetIfTree(ifTree)
  1055  
  1056  	ln.Push(ifDecl)
  1057  	expected.Root = ln
  1058  
  1059  	parserTest("test if", `if test("bleh") != "other" {
  1060  	pwd
  1061  }`, expected, t, true)
  1062  }
  1063  
  1064  func TestParseIfLvariable(t *testing.T) {
  1065  	expected := ast.NewTree("test if with variable")
  1066  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1067  	ifDecl := ast.NewIfNode(token.NewFileInfo(1, 0))
  1068  	ifDecl.SetLvalue(ast.NewVarExpr(token.NewFileInfo(1, 3), "$test"))
  1069  	ifDecl.SetRvalue(ast.NewStringExpr(token.NewFileInfo(1, 13), "other", true))
  1070  	ifDecl.SetOp("==")
  1071  
  1072  	subBlock := ast.NewBlockNode(token.NewFileInfo(1, 20))
  1073  	cmd := ast.NewCommandNode(token.NewFileInfo(2, 1), "pwd", false)
  1074  	subBlock.Push(cmd)
  1075  
  1076  	ifTree := ast.NewTree("if block")
  1077  	ifTree.Root = subBlock
  1078  
  1079  	ifDecl.SetIfTree(ifTree)
  1080  
  1081  	ln.Push(ifDecl)
  1082  	expected.Root = ln
  1083  
  1084  	parserTest("test if", `if $test == "other" {
  1085  	pwd
  1086  }`, expected, t, true)
  1087  }
  1088  
  1089  func TestParseIfRvariable(t *testing.T) {
  1090  	expected := ast.NewTree("test if with variable")
  1091  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1092  	ifDecl := ast.NewIfNode(token.NewFileInfo(1, 0))
  1093  	ifDecl.SetLvalue(ast.NewVarExpr(token.NewFileInfo(1, 3), "$test"))
  1094  	ifDecl.SetRvalue(ast.NewVarExpr(token.NewFileInfo(1, 12), "$other"))
  1095  	ifDecl.SetOp("==")
  1096  
  1097  	subBlock := ast.NewBlockNode(token.NewFileInfo(1, 19))
  1098  	cmd := ast.NewCommandNode(token.NewFileInfo(2, 1), "pwd", false)
  1099  	subBlock.Push(cmd)
  1100  
  1101  	ifTree := ast.NewTree("if block")
  1102  	ifTree.Root = subBlock
  1103  
  1104  	ifDecl.SetIfTree(ifTree)
  1105  
  1106  	ln.Push(ifDecl)
  1107  	expected.Root = ln
  1108  
  1109  	parserTest("test if", `if $test == $other {
  1110  	pwd
  1111  }`, expected, t, true)
  1112  }
  1113  
  1114  func TestParseIfElse(t *testing.T) {
  1115  	expected := ast.NewTree("test if else with variable")
  1116  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1117  	ifDecl := ast.NewIfNode(token.NewFileInfo(1, 0))
  1118  	ifDecl.SetLvalue(ast.NewVarExpr(token.NewFileInfo(1, 3), "$test"))
  1119  	ifDecl.SetRvalue(ast.NewStringExpr(token.NewFileInfo(1, 13), "other", true))
  1120  	ifDecl.SetOp("==")
  1121  
  1122  	subBlock := ast.NewBlockNode(token.NewFileInfo(1, 20))
  1123  	cmd := ast.NewCommandNode(token.NewFileInfo(2, 1), "pwd", false)
  1124  	subBlock.Push(cmd)
  1125  
  1126  	ifTree := ast.NewTree("if block")
  1127  	ifTree.Root = subBlock
  1128  
  1129  	ifDecl.SetIfTree(ifTree)
  1130  
  1131  	elseBlock := ast.NewBlockNode(token.NewFileInfo(3, 7))
  1132  	exitCmd := ast.NewCommandNode(token.NewFileInfo(4, 1), "exit", false)
  1133  	elseBlock.Push(exitCmd)
  1134  
  1135  	elseTree := ast.NewTree("else block")
  1136  	elseTree.Root = elseBlock
  1137  
  1138  	ifDecl.SetElseTree(elseTree)
  1139  
  1140  	ln.Push(ifDecl)
  1141  	expected.Root = ln
  1142  
  1143  	parserTest("test if", `if $test == "other" {
  1144  	pwd
  1145  } else {
  1146  	exit
  1147  }`, expected, t, true)
  1148  }
  1149  
  1150  func TestParseIfElseIf(t *testing.T) {
  1151  	expected := ast.NewTree("test if else with variable")
  1152  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1153  	ifDecl := ast.NewIfNode(token.NewFileInfo(1, 0))
  1154  	ifDecl.SetLvalue(ast.NewVarExpr(token.NewFileInfo(1, 3), "$test"))
  1155  	ifDecl.SetRvalue(ast.NewStringExpr(token.NewFileInfo(1, 13), "other", true))
  1156  	ifDecl.SetOp("==")
  1157  
  1158  	subBlock := ast.NewBlockNode(token.NewFileInfo(1, 20))
  1159  	cmd := ast.NewCommandNode(token.NewFileInfo(2, 1), "pwd", false)
  1160  	subBlock.Push(cmd)
  1161  
  1162  	ifTree := ast.NewTree("if block")
  1163  	ifTree.Root = subBlock
  1164  
  1165  	ifDecl.SetIfTree(ifTree)
  1166  
  1167  	elseIfDecl := ast.NewIfNode(token.NewFileInfo(3, 7))
  1168  
  1169  	elseIfDecl.SetLvalue(ast.NewVarExpr(token.NewFileInfo(3, 10), "$test"))
  1170  	elseIfDecl.SetRvalue(ast.NewStringExpr(token.NewFileInfo(3, 20), "others", true))
  1171  	elseIfDecl.SetOp("==")
  1172  
  1173  	elseIfBlock := ast.NewBlockNode(token.NewFileInfo(3, 28))
  1174  	elseifCmd := ast.NewCommandNode(token.NewFileInfo(4, 1), "ls", false)
  1175  	elseIfBlock.Push(elseifCmd)
  1176  
  1177  	elseIfTree := ast.NewTree("if block")
  1178  	elseIfTree.Root = elseIfBlock
  1179  
  1180  	elseIfDecl.SetIfTree(elseIfTree)
  1181  
  1182  	elseBlock := ast.NewBlockNode(token.NewFileInfo(5, 7))
  1183  	exitCmd := ast.NewCommandNode(token.NewFileInfo(6, 1), "exit", false)
  1184  	elseBlock.Push(exitCmd)
  1185  
  1186  	elseTree := ast.NewTree("else block")
  1187  	elseTree.Root = elseBlock
  1188  
  1189  	elseIfDecl.SetElseTree(elseTree)
  1190  
  1191  	elseBlock2 := ast.NewBlockNode(token.NewFileInfo(3, 7))
  1192  	elseBlock2.Push(elseIfDecl)
  1193  
  1194  	elseTree2 := ast.NewTree("first else tree")
  1195  	elseTree2.Root = elseBlock2
  1196  	ifDecl.SetElseTree(elseTree2)
  1197  
  1198  	ln.Push(ifDecl)
  1199  	expected.Root = ln
  1200  
  1201  	parserTest("test if", `if $test == "other" {
  1202  	pwd
  1203  } else if $test == "others" {
  1204  	ls
  1205  } else {
  1206  	exit
  1207  }`, expected, t, true)
  1208  }
  1209  
  1210  func TestParseFnBasic(t *testing.T) {
  1211  	// root
  1212  	expected := ast.NewTree("fn")
  1213  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1214  
  1215  	// fn
  1216  	fn := ast.NewFnDeclNode(token.NewFileInfo(1, 3), "build")
  1217  	tree := ast.NewTree("fn body")
  1218  	lnBody := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1219  	tree.Root = lnBody
  1220  	fn.SetTree(tree)
  1221  
  1222  	// root
  1223  	ln.Push(fn)
  1224  	expected.Root = ln
  1225  
  1226  	parserTest("fn", `fn build() {
  1227  
  1228  }`, expected, t, true)
  1229  
  1230  	// root
  1231  	expected = ast.NewTree("fn")
  1232  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1233  
  1234  	// fn
  1235  	fn = ast.NewFnDeclNode(token.NewFileInfo(1, 3), "build")
  1236  	tree = ast.NewTree("fn body")
  1237  	lnBody = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1238  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 0), "ls", false)
  1239  	lnBody.Push(cmd)
  1240  	tree.Root = lnBody
  1241  	fn.SetTree(tree)
  1242  
  1243  	// root
  1244  	ln.Push(fn)
  1245  	expected.Root = ln
  1246  
  1247  	parserTest("fn", `fn build() {
  1248  	ls
  1249  }`, expected, t, true)
  1250  
  1251  	// root
  1252  	expected = ast.NewTree("fn")
  1253  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1254  
  1255  	// fn
  1256  	fn = ast.NewFnDeclNode(token.NewFileInfo(1, 3), "build")
  1257  	fn.AddArg(ast.NewFnArgNode(token.NewFileInfo(1, 9), "image", false))
  1258  	tree = ast.NewTree("fn body")
  1259  	lnBody = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1260  	cmd = ast.NewCommandNode(token.NewFileInfo(1, 0), "ls", false)
  1261  	lnBody.Push(cmd)
  1262  	tree.Root = lnBody
  1263  	fn.SetTree(tree)
  1264  
  1265  	// root
  1266  	ln.Push(fn)
  1267  	expected.Root = ln
  1268  
  1269  	parserTest("fn", `fn build(image) {
  1270  	ls
  1271  }`, expected, t, true)
  1272  
  1273  	// root
  1274  	expected = ast.NewTree("fn")
  1275  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1276  
  1277  	// fn
  1278  	fn = ast.NewFnDeclNode(token.NewFileInfo(1, 3), "build")
  1279  	fn.AddArg(ast.NewFnArgNode(token.NewFileInfo(1, 9), "image", false))
  1280  	fn.AddArg(ast.NewFnArgNode(token.NewFileInfo(1, 16), "debug", false))
  1281  	tree = ast.NewTree("fn body")
  1282  	lnBody = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1283  	cmd = ast.NewCommandNode(token.NewFileInfo(1, 0), "ls", false)
  1284  	lnBody.Push(cmd)
  1285  	tree.Root = lnBody
  1286  	fn.SetTree(tree)
  1287  
  1288  	// root
  1289  	ln.Push(fn)
  1290  	expected.Root = ln
  1291  
  1292  	parserTest("fn", `fn build(image, debug) {
  1293  	ls
  1294  }`, expected, t, true)
  1295  }
  1296  
  1297  func TestParseInlineFnDecl(t *testing.T) {
  1298  	expected := ast.NewTree("fn")
  1299  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1300  
  1301  	fn := ast.NewFnDeclNode(token.NewFileInfo(1, 3), "cd")
  1302  	tree := ast.NewTree("fn body")
  1303  	lnBody := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1304  	echo := ast.NewCommandNode(token.NewFileInfo(1, 11), "echo", false)
  1305  	echo.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 16), "hello", true))
  1306  	lnBody.Push(echo)
  1307  
  1308  	tree.Root = lnBody
  1309  	fn.SetTree(tree)
  1310  
  1311  	// root
  1312  	ln.Push(fn)
  1313  	expected.Root = ln
  1314  
  1315  	parserTest("inline fn", `fn cd() { echo "hello" }`,
  1316  		expected, t, false)
  1317  
  1318  	test := ast.NewCommandNode(token.NewFileInfo(1, 26), "test", false)
  1319  	test.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 32), "-d", false))
  1320  	test.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 35), "/etc", false))
  1321  
  1322  	pipe := ast.NewPipeNode(token.NewFileInfo(1, 11), false)
  1323  	pipe.AddCmd(echo)
  1324  	pipe.AddCmd(test)
  1325  	lnBody = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1326  	lnBody.Push(pipe)
  1327  	tree.Root = lnBody
  1328  	fn.SetTree(tree)
  1329  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1330  	ln.Push(fn)
  1331  	expected.Root = ln
  1332  
  1333  	parserTest("inline fn", `fn cd() { echo "hello" | test -d /etc }`,
  1334  		expected, t, false)
  1335  }
  1336  
  1337  func TestParseBindFn(t *testing.T) {
  1338  	expected := ast.NewTree("bindfn")
  1339  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1340  
  1341  	bindFn := ast.NewBindFnNode(token.NewFileInfo(1, 0), "cd", "cd2")
  1342  	ln.Push(bindFn)
  1343  	expected.Root = ln
  1344  
  1345  	parserTest("bindfn", `bindfn cd cd2`, expected, t, true)
  1346  }
  1347  
  1348  func TestParseRedirectionVariable(t *testing.T) {
  1349  	expected := ast.NewTree("redirection var")
  1350  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1351  
  1352  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 0), "cmd", false)
  1353  	redir := ast.NewRedirectNode(token.NewFileInfo(1, 4))
  1354  	redirArg := ast.NewVarExpr(token.NewFileInfo(1, 6), "$outFname")
  1355  	redir.SetLocation(redirArg)
  1356  	cmd.AddRedirect(redir)
  1357  	ln.Push(cmd)
  1358  	expected.Root = ln
  1359  
  1360  	parserTest("redir var", `cmd > $outFname`, expected, t, true)
  1361  }
  1362  
  1363  func TestParseReturn(t *testing.T) {
  1364  	expected := ast.NewTree("return")
  1365  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1366  
  1367  	ret := ast.NewReturnNode(token.NewFileInfo(1, 0))
  1368  	ln.Push(ret)
  1369  	expected.Root = ln
  1370  
  1371  	parserTest("return", `return`, expected, t, true)
  1372  
  1373  	expected = ast.NewTree("return list")
  1374  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1375  
  1376  	ret = ast.NewReturnNode(token.NewFileInfo(1, 0))
  1377  
  1378  	listvalues := make([]ast.Expr, 2)
  1379  
  1380  	listvalues[0] = ast.NewStringExpr(token.NewFileInfo(1, 9), "val1", true)
  1381  	listvalues[1] = ast.NewStringExpr(token.NewFileInfo(1, 16), "val2", true)
  1382  
  1383  	retReturn := ast.NewListExpr(token.NewFileInfo(1, 7), listvalues)
  1384  
  1385  	ret.Returns = []ast.Expr{retReturn}
  1386  
  1387  	ln.Push(ret)
  1388  	expected.Root = ln
  1389  
  1390  	parserTest("return", `return ("val1" "val2")`, expected, t, true)
  1391  
  1392  	expected = ast.NewTree("return variable")
  1393  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1394  
  1395  	ret = ast.NewReturnNode(token.NewFileInfo(1, 0))
  1396  
  1397  	ret.Returns = []ast.Expr{ast.NewVarExpr(token.NewFileInfo(1, 7), "$var")}
  1398  
  1399  	ln.Push(ret)
  1400  	expected.Root = ln
  1401  
  1402  	parserTest("return", `return $var`, expected, t, true)
  1403  
  1404  	expected = ast.NewTree("return string")
  1405  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1406  
  1407  	ret = ast.NewReturnNode(token.NewFileInfo(1, 0))
  1408  
  1409  	ret.Returns = []ast.Expr{ast.NewStringExpr(token.NewFileInfo(1, 8), "value", true)}
  1410  
  1411  	ln.Push(ret)
  1412  	expected.Root = ln
  1413  
  1414  	parserTest("return", `return "value"`, expected, t, true)
  1415  
  1416  	expected = ast.NewTree("return funcall")
  1417  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1418  
  1419  	ret = ast.NewReturnNode(token.NewFileInfo(1, 0))
  1420  
  1421  	aFn := ast.NewFnInvNode(token.NewFileInfo(1, 7), "a")
  1422  
  1423  	ret.Returns = []ast.Expr{aFn}
  1424  
  1425  	ln.Push(ret)
  1426  	expected.Root = ln
  1427  
  1428  	parserTest("return", `return a()`, expected, t, true)
  1429  
  1430  	expected = ast.NewTree("return multiple values")
  1431  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1432  	ret = ast.NewReturnNode(token.NewFileInfo(1, 0))
  1433  
  1434  	a1 := ast.NewStringExpr(token.NewFileInfo(1, 8), "1", true)
  1435  	a2 := ast.NewStringExpr(token.NewFileInfo(1, 13), "2", true)
  1436  	a3 := ast.NewStringExpr(token.NewFileInfo(1, 18), "3", true)
  1437  
  1438  	ret.Returns = []ast.Expr{a1, a2, a3}
  1439  
  1440  	ln.Push(ret)
  1441  	expected.Root = ln
  1442  
  1443  	parserTest("return", `return "1", "2", "3"`, expected, t, true)
  1444  }
  1445  
  1446  func TestParseIfInvalid(t *testing.T) {
  1447  	parser := NewParser("if invalid", `if a == b { pwd }`)
  1448  	_, err := parser.Parse()
  1449  
  1450  	if err == nil {
  1451  		t.Error("Must fail. Only quoted strings and variables on if clauses.")
  1452  		return
  1453  	}
  1454  }
  1455  
  1456  func TestParseFor(t *testing.T) {
  1457  	expected := ast.NewTree("for")
  1458  
  1459  	forStmt := ast.NewForNode(token.NewFileInfo(1, 0))
  1460  	forTree := ast.NewTree("for block")
  1461  	forBlock := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1462  	forTree.Root = forBlock
  1463  	forStmt.SetTree(forTree)
  1464  
  1465  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1466  	ln.Push(forStmt)
  1467  	expected.Root = ln
  1468  
  1469  	parserTest("for", `for {
  1470  
  1471  }`, expected, t, true)
  1472  
  1473  	forStmt.SetIdentifier("f")
  1474  	forStmt.SetInExpr(ast.NewVarExpr(token.NewFileInfo(1, 9), "$files"))
  1475  
  1476  	parserTest("for", `for f in $files {
  1477  
  1478  }`, expected, t, true)
  1479  
  1480  	forStmt.SetIdentifier("f")
  1481  	fnInv := ast.NewFnInvNode(token.NewFileInfo(1, 9), "getfiles")
  1482  	fnArg := ast.NewStringExpr(token.NewFileInfo(1, 19), "/", true)
  1483  	fnInv.AddArg(fnArg)
  1484  	forStmt.SetInExpr(fnInv)
  1485  
  1486  	parserTest("for", `for f in getfiles("/") {
  1487  
  1488  }`, expected, t, true)
  1489  
  1490  	forStmt.SetIdentifier("f")
  1491  	value1 := ast.NewStringExpr(token.NewFileInfo(1, 10), "1", false)
  1492  	value2 := ast.NewStringExpr(token.NewFileInfo(1, 12), "2", false)
  1493  	value3 := ast.NewStringExpr(token.NewFileInfo(1, 14), "3", false)
  1494  	value4 := ast.NewStringExpr(token.NewFileInfo(1, 16), "4", false)
  1495  	value5 := ast.NewStringExpr(token.NewFileInfo(1, 18), "5", false)
  1496  
  1497  	list := ast.NewListExpr(token.NewFileInfo(1, 9), []ast.Expr{
  1498  		value1, value2, value3, value4, value5,
  1499  	})
  1500  
  1501  	forStmt.SetInExpr(list)
  1502  
  1503  	parserTest("for", `for f in (1 2 3 4 5) {
  1504  
  1505  }`, expected, t, true)
  1506  }
  1507  
  1508  func TestParseVariableIndexing(t *testing.T) {
  1509  	expected := ast.NewTree("variable indexing")
  1510  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1511  
  1512  	indexedVar := ast.NewIndexExpr(
  1513  		token.NewFileInfo(1, 7),
  1514  		ast.NewVarExpr(token.NewFileInfo(1, 7), "$values"),
  1515  		ast.NewIntExpr(token.NewFileInfo(1, 15), 0),
  1516  	)
  1517  
  1518  	assignment := ast.NewSingleAssignNode(token.NewFileInfo(1, 0),
  1519  		ast.NewNameNode(token.NewFileInfo(1, 0), "test", nil),
  1520  		indexedVar,
  1521  	)
  1522  
  1523  	ln.Push(assignment)
  1524  	expected.Root = ln
  1525  
  1526  	parserTest("variable indexing", `test = $values[0]`, expected, t, true)
  1527  
  1528  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1529  
  1530  	ifDecl := ast.NewIfNode(token.NewFileInfo(1, 0))
  1531  	lvalue := ast.NewVarExpr(token.NewFileInfo(1, 3), "$values")
  1532  
  1533  	indexedVar = ast.NewIndexExpr(token.NewFileInfo(1, 3), lvalue,
  1534  		ast.NewIntExpr(token.NewFileInfo(1, 11), 0))
  1535  
  1536  	ifDecl.SetLvalue(indexedVar)
  1537  	ifDecl.SetOp("==")
  1538  	ifDecl.SetRvalue(ast.NewStringExpr(token.NewFileInfo(1, 18), "1", true))
  1539  
  1540  	ifBlock := ast.NewTree("if")
  1541  	lnBody := ast.NewBlockNode(token.NewFileInfo(1, 21))
  1542  	ifBlock.Root = lnBody
  1543  	ifDecl.SetIfTree(ifBlock)
  1544  
  1545  	ln.Push(ifDecl)
  1546  	expected.Root = ln
  1547  
  1548  	parserTest("variable indexing", `if $values[0] == "1" {
  1549  
  1550  }`, expected, t, true)
  1551  }
  1552  
  1553  func TestParseMultilineCmdExec(t *testing.T) {
  1554  	expected := ast.NewTree("parser simple")
  1555  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1556  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 1), "echo", true)
  1557  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 7), "hello world", true))
  1558  	ln.Push(cmd)
  1559  
  1560  	expected.Root = ln
  1561  
  1562  	parserTest("parser simple", `(echo "hello world")`, expected, t, true)
  1563  
  1564  	expected = ast.NewTree("parser aws cmd")
  1565  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1566  	cmd = ast.NewCommandNode(token.NewFileInfo(2, 1), "aws", true)
  1567  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(2, 5), "ec2", false))
  1568  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(2, 9), "run-instances", false))
  1569  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(3, 3), "--image-id", false))
  1570  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(3, 14), "ami-xxxxxxxx", false))
  1571  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(4, 3), "--count", false))
  1572  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(4, 11), "1", false))
  1573  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(5, 3), "--instance-type", false))
  1574  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(5, 19), "t1.micro", false))
  1575  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(6, 3), "--key-name", false))
  1576  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(6, 14), "MyKeyPair", false))
  1577  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(7, 3), "--security-groups", false))
  1578  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(7, 21), "my-sg", false))
  1579  
  1580  	ln.Push(cmd)
  1581  
  1582  	expected.Root = ln
  1583  
  1584  	parserTest("parser simple", `(
  1585  	aws ec2 run-instances
  1586  			--image-id ami-xxxxxxxx
  1587  			--count 1
  1588  			--instance-type t1.micro
  1589  			--key-name MyKeyPair
  1590  			--security-groups my-sg
  1591  )`, expected, t, true)
  1592  }
  1593  
  1594  func TestParseMultilineCmdAssign(t *testing.T) {
  1595  	expected := ast.NewTree("parser simple assign")
  1596  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1597  	cmd := ast.NewCommandNode(token.NewFileInfo(1, 10), "echo", true)
  1598  	cmd.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 16), "hello world", true))
  1599  	assign, err := ast.NewExecAssignNode(token.NewFileInfo(1, 0),
  1600  		[]*ast.NameNode{ast.NewNameNode(token.NewFileInfo(1, 0), "hello", nil)},
  1601  		cmd,
  1602  	)
  1603  
  1604  	if err != nil {
  1605  		t.Error(err)
  1606  		return
  1607  	}
  1608  
  1609  	ln.Push(assign)
  1610  
  1611  	expected.Root = ln
  1612  
  1613  	parserTest("parser simple", `hello <= (echo "hello world")`, expected, t, true)
  1614  }
  1615  
  1616  func TestMultiPipe(t *testing.T) {
  1617  	expected := ast.NewTree("parser pipe")
  1618  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1619  	first := ast.NewCommandNode(token.NewFileInfo(1, 1), "echo", false)
  1620  	first.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 7), "hello world", true))
  1621  
  1622  	second := ast.NewCommandNode(token.NewFileInfo(1, 22), "awk", false)
  1623  	second.AddArg(ast.NewStringExpr(token.NewFileInfo(1, 27), "{print $1}", true))
  1624  
  1625  	pipe := ast.NewPipeNode(token.NewFileInfo(1, 20), true)
  1626  	pipe.AddCmd(first)
  1627  	pipe.AddCmd(second)
  1628  
  1629  	ln.Push(pipe)
  1630  
  1631  	expected.Root = ln
  1632  
  1633  	parserTest("parser pipe", `(echo "hello world" | awk "{print $1}")`, expected, t, true)
  1634  
  1635  	// get longer stringify
  1636  	expected = ast.NewTree("parser pipe")
  1637  	ln = ast.NewBlockNode(token.NewFileInfo(1, 0))
  1638  	first = ast.NewCommandNode(token.NewFileInfo(2, 1), "echo", false)
  1639  	first.AddArg(ast.NewStringExpr(token.NewFileInfo(2, 7), "hello world", true))
  1640  
  1641  	second = ast.NewCommandNode(token.NewFileInfo(3, 1), "awk", false)
  1642  	second.AddArg(ast.NewStringExpr(token.NewFileInfo(3, 6), "{print AAAAAAAAAAAAAAAAAAAAAA}", true))
  1643  
  1644  	pipe = ast.NewPipeNode(token.NewFileInfo(2, 20), true)
  1645  	pipe.AddCmd(first)
  1646  	pipe.AddCmd(second)
  1647  
  1648  	ln.Push(pipe)
  1649  
  1650  	expected.Root = ln
  1651  
  1652  	parserTest("parser pipe", `(
  1653  	echo "hello world" |
  1654  	awk "{print AAAAAAAAAAAAAAAAAAAAAA}"
  1655  )`, expected, t, true)
  1656  }
  1657  
  1658  func TestFnVariadic(t *testing.T) {
  1659  	// root
  1660  	expected := ast.NewTree("variadic")
  1661  	ln := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1662  
  1663  	// fn
  1664  	fn := ast.NewFnDeclNode(token.NewFileInfo(1, 3), "println")
  1665  	fn.AddArg(ast.NewFnArgNode(token.NewFileInfo(1, 11), "fmt", false))
  1666  	fn.AddArg(ast.NewFnArgNode(token.NewFileInfo(1, 16), "arg", true))
  1667  	tree := ast.NewTree("fn body")
  1668  	lnBody := ast.NewBlockNode(token.NewFileInfo(1, 0))
  1669  	print := ast.NewFnInvNode(token.NewFileInfo(2, 2), "print")
  1670  	print.AddArg(ast.NewConcatExpr(token.NewFileInfo(1, 7), []ast.Expr{
  1671  		ast.NewVarExpr(token.NewFileInfo(2, 7), "$fmt"),
  1672  		ast.NewStringExpr(token.NewFileInfo(2, 12), "\n", true),
  1673  	}))
  1674  	print.AddArg(ast.NewVarVariadicExpr(token.NewFileInfo(2, 12), "$arg", true))
  1675  	lnBody.Push(print)
  1676  	tree.Root = lnBody
  1677  	fn.SetTree(tree)
  1678  
  1679  	// root
  1680  	ln.Push(fn)
  1681  	expected.Root = ln
  1682  
  1683  	parserTest("fn", `fn println(fmt, arg...) {
  1684  	print($fmt+"\n", $arg...)
  1685  }`, expected, t, true)
  1686  }
  1687  
  1688  func TestParseValidDotdotdot(t *testing.T) {
  1689  	for _, tc := range []string{
  1690  		// things that should not break
  1691  		"ls ...",
  1692  		"go get ./...",
  1693  		`echo "..."`,
  1694  		`strangecmd... -h`,
  1695  		`bad_designed...fail -f`,
  1696  	} {
  1697  		parser := NewParser("", tc)
  1698  		_, err := parser.Parse()
  1699  		if err != nil {
  1700  			t.Fatalf("Code: '%s' failed: %s", tc, err.Error())
  1701  		}
  1702  	}
  1703  }
  1704  
  1705  func TestParseInvalidDotdotdot(t *testing.T) {
  1706  	for _, tc := range []string{
  1707  		"...",
  1708  		`if ... == "" {}`,
  1709  		`if $var... == "" {}`,
  1710  		`a = $var...`,
  1711  		`a, b, c = ("a" "b" "c")...`, // please, no
  1712  		// `fn println(arg..., fmt) {}`, // Not sure if must fail at parsing...
  1713  	} {
  1714  		parser := NewParser("", tc)
  1715  		_, err := parser.Parse()
  1716  		if err == nil {
  1717  			t.Fatalf("Syntax '%s' must fail", tc)
  1718  		}
  1719  	}
  1720  }
  1721  
  1722  func TestFunctionPipes(t *testing.T) {
  1723  	parser := NewParser("invalid pipe with functions",
  1724  		`echo "some thing" | replace(" ", "|")`)
  1725  
  1726  	_, err := parser.Parse()
  1727  
  1728  	if err == nil {
  1729  		t.Error("Must fail. Function must be bind'ed to command name to use in pipe.")
  1730  		return
  1731  	}
  1732  }