github.com/cilki/sh@v2.6.4+incompatible/syntax/filetests_test.go (about)

     1  // Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
     2  // See LICENSE for licensing information
     3  
     4  package syntax
     5  
     6  import (
     7  	"reflect"
     8  	"strings"
     9  	"testing"
    10  )
    11  
    12  func prepareTest(c *testCase) {
    13  	c.common = fullProg(c.common)
    14  	c.bash = fullProg(c.bash)
    15  	c.posix = fullProg(c.posix)
    16  	c.mksh = fullProg(c.mksh)
    17  	c.bsmk = fullProg(c.bsmk) // bash AND mksh
    18  	if f, ok := c.common.(*File); ok && f != nil {
    19  		c.All = append(c.All, f)
    20  		c.Bash = f
    21  		c.Posix = f
    22  		c.MirBSDKorn = f
    23  	}
    24  	if f, ok := c.bash.(*File); ok && f != nil {
    25  		c.All = append(c.All, f)
    26  		c.Bash = f
    27  	}
    28  	if f, ok := c.posix.(*File); ok && f != nil {
    29  		c.All = append(c.All, f)
    30  		c.Posix = f
    31  	}
    32  	if f, ok := c.mksh.(*File); ok && f != nil {
    33  		c.All = append(c.All, f)
    34  		c.MirBSDKorn = f
    35  	}
    36  	if f, ok := c.bsmk.(*File); ok && f != nil {
    37  		c.All = append(c.All, f)
    38  		c.Bash = f
    39  		c.MirBSDKorn = f
    40  	}
    41  }
    42  
    43  func init() {
    44  	for i := range fileTests {
    45  		prepareTest(&fileTests[i])
    46  	}
    47  	for i := range fileTestsNoPrint {
    48  		prepareTest(&fileTestsNoPrint[i])
    49  	}
    50  }
    51  
    52  func lit(s string) *Lit         { return &Lit{Value: s} }
    53  func word(ps ...WordPart) *Word { return &Word{Parts: ps} }
    54  func litWord(s string) *Word    { return word(lit(s)) }
    55  func litWords(strs ...string) []*Word {
    56  	l := make([]*Word, 0, len(strs))
    57  	for _, s := range strs {
    58  		l = append(l, litWord(s))
    59  	}
    60  	return l
    61  }
    62  
    63  func call(words ...*Word) *CallExpr    { return &CallExpr{Args: words} }
    64  func litCall(strs ...string) *CallExpr { return call(litWords(strs...)...) }
    65  
    66  func stmt(cmd Command) *Stmt         { return &Stmt{Cmd: cmd} }
    67  func stmtList(sts ...*Stmt) StmtList { return StmtList{Stmts: sts} }
    68  func stmts(cmds ...Command) StmtList {
    69  	l := make([]*Stmt, len(cmds))
    70  	for i, cmd := range cmds {
    71  		l[i] = stmt(cmd)
    72  	}
    73  	return stmtList(l...)
    74  }
    75  
    76  func litStmt(strs ...string) *Stmt { return stmt(litCall(strs...)) }
    77  func litStmts(strs ...string) StmtList {
    78  	l := make([]*Stmt, len(strs))
    79  	for i, s := range strs {
    80  		l[i] = litStmt(s)
    81  	}
    82  	return stmtList(l...)
    83  }
    84  
    85  func sglQuoted(s string) *SglQuoted        { return &SglQuoted{Value: s} }
    86  func sglDQuoted(s string) *SglQuoted       { return &SglQuoted{Dollar: true, Value: s} }
    87  func dblQuoted(ps ...WordPart) *DblQuoted  { return &DblQuoted{Parts: ps} }
    88  func dblDQuoted(ps ...WordPart) *DblQuoted { return &DblQuoted{Dollar: true, Parts: ps} }
    89  func block(sts ...*Stmt) *Block            { return &Block{StmtList: stmtList(sts...)} }
    90  func subshell(sts ...*Stmt) *Subshell      { return &Subshell{StmtList: stmtList(sts...)} }
    91  func arithmExp(e ArithmExpr) *ArithmExp    { return &ArithmExp{X: e} }
    92  func arithmExpBr(e ArithmExpr) *ArithmExp  { return &ArithmExp{Bracket: true, X: e} }
    93  func arithmCmd(e ArithmExpr) *ArithmCmd    { return &ArithmCmd{X: e} }
    94  func parenArit(e ArithmExpr) *ParenArithm  { return &ParenArithm{X: e} }
    95  func parenTest(e TestExpr) *ParenTest      { return &ParenTest{X: e} }
    96  
    97  func cmdSubst(sts ...*Stmt) *CmdSubst { return &CmdSubst{StmtList: stmtList(sts...)} }
    98  func litParamExp(s string) *ParamExp {
    99  	return &ParamExp{Short: true, Param: lit(s)}
   100  }
   101  func letClause(exps ...ArithmExpr) *LetClause {
   102  	return &LetClause{Exprs: exps}
   103  }
   104  
   105  func arrValues(words ...*Word) *ArrayExpr {
   106  	ae := &ArrayExpr{}
   107  	for _, w := range words {
   108  		ae.Elems = append(ae.Elems, &ArrayElem{Value: w})
   109  	}
   110  	return ae
   111  }
   112  
   113  type testCase struct {
   114  	Strs        []string
   115  	common      interface{}
   116  	bash, posix interface{}
   117  	bsmk, mksh  interface{}
   118  	All         []*File
   119  	Bash, Posix *File
   120  	MirBSDKorn  *File
   121  }
   122  
   123  var fileTests = []testCase{
   124  	{
   125  		Strs:   []string{"", " ", "\t", "\n \n", "\r \r\n"},
   126  		common: &File{},
   127  	},
   128  	{
   129  		Strs:   []string{"", "# foo", "# foo ( bar", "# foo'bar"},
   130  		common: &File{},
   131  	},
   132  	{
   133  		Strs:   []string{"foo", "foo ", " foo", "foo # bar"},
   134  		common: litWord("foo"),
   135  	},
   136  	{
   137  		Strs:   []string{`\`},
   138  		common: litWord(`\`),
   139  	},
   140  	{
   141  		Strs:   []string{`foo\`, "f\\\noo\\"},
   142  		common: litWord(`foo\`),
   143  	},
   144  	{
   145  		Strs:   []string{`foo\a`, "f\\\noo\\a"},
   146  		common: litWord(`foo\a`),
   147  	},
   148  	{
   149  		Strs: []string{
   150  			"foo\nbar",
   151  			"foo; bar;",
   152  			"foo;bar;",
   153  			"\nfoo\nbar\n",
   154  			"foo\r\nbar\r\n",
   155  		},
   156  		common: litStmts("foo", "bar"),
   157  	},
   158  	{
   159  		Strs:   []string{"foo a b", " foo  a  b ", "foo \\\n a b"},
   160  		common: litCall("foo", "a", "b"),
   161  	},
   162  	{
   163  		Strs:   []string{"foobar", "foo\\\nbar", "foo\\\nba\\\nr"},
   164  		common: litWord("foobar"),
   165  	},
   166  	{
   167  		Strs:   []string{"foo", "foo \\\n"},
   168  		common: litWord("foo"),
   169  	},
   170  	{
   171  		Strs:   []string{"foo'bar'"},
   172  		common: word(lit("foo"), sglQuoted("bar")),
   173  	},
   174  	{
   175  		Strs:   []string{"(foo)", "(foo;)", "(\nfoo\n)"},
   176  		common: subshell(litStmt("foo")),
   177  	},
   178  	{
   179  		Strs:   []string{"(\n\tfoo\n\tbar\n)", "(foo; bar)"},
   180  		common: subshell(litStmt("foo"), litStmt("bar")),
   181  	},
   182  	{
   183  		Strs:   []string{"{ foo; }", "{\nfoo\n}"},
   184  		common: block(litStmt("foo")),
   185  	},
   186  	{
   187  		Strs: []string{
   188  			"{ if a; then b; fi; }",
   189  			"{ if a; then b; fi }",
   190  		},
   191  		common: block(stmt(&IfClause{
   192  			Cond: litStmts("a"),
   193  			Then: litStmts("b"),
   194  		})),
   195  	},
   196  	{
   197  		Strs: []string{
   198  			"if a; then b; fi",
   199  			"if a\nthen\nb\nfi",
   200  			"if a;\nthen\nb\nfi",
   201  			"if a \nthen\nb\nfi",
   202  		},
   203  		common: &IfClause{
   204  			Cond: litStmts("a"),
   205  			Then: litStmts("b"),
   206  		},
   207  	},
   208  	{
   209  		Strs: []string{
   210  			"if a; then b; else c; fi",
   211  			"if a\nthen b\nelse\nc\nfi",
   212  		},
   213  		common: &IfClause{
   214  			Cond: litStmts("a"),
   215  			Then: litStmts("b"),
   216  			Else: litStmts("c"),
   217  		},
   218  	},
   219  	{
   220  		Strs: []string{
   221  			"if a; then a; elif b; then b; else c; fi",
   222  		},
   223  		common: &IfClause{
   224  			Cond: litStmts("a"),
   225  			Then: litStmts("a"),
   226  			Else: stmtList(stmt(&IfClause{
   227  				Elif: true,
   228  				Cond: litStmts("b"),
   229  				Then: litStmts("b"),
   230  				Else: litStmts("c"),
   231  			})),
   232  		},
   233  	},
   234  	{
   235  		Strs: []string{
   236  			"if a; then a; elif b; then b; elif c; then c; else d; fi",
   237  			"if a\nthen a\nelif b\nthen b\nelif c\nthen c\nelse\nd\nfi",
   238  		},
   239  		common: &IfClause{
   240  			Cond: litStmts("a"),
   241  			Then: litStmts("a"),
   242  			Else: stmtList(stmt(&IfClause{
   243  				Elif: true,
   244  				Cond: litStmts("b"),
   245  				Then: litStmts("b"),
   246  				Else: stmtList(stmt(&IfClause{
   247  					Elif: true,
   248  					Cond: litStmts("c"),
   249  					Then: litStmts("c"),
   250  					Else: litStmts("d"),
   251  				})),
   252  			})),
   253  		},
   254  	},
   255  	{
   256  		Strs: []string{
   257  			"if\n\ta1\n\ta2 foo\n\ta3 bar\nthen b; fi",
   258  			"if a1; a2 foo; a3 bar; then b; fi",
   259  		},
   260  		common: &IfClause{
   261  			Cond: stmtList(
   262  				litStmt("a1"),
   263  				litStmt("a2", "foo"),
   264  				litStmt("a3", "bar"),
   265  			),
   266  			Then: litStmts("b"),
   267  		},
   268  	},
   269  	{
   270  		Strs: []string{`((a == 2))`},
   271  		bsmk: stmt(arithmCmd(&BinaryArithm{
   272  			Op: Eql,
   273  			X:  litWord("a"),
   274  			Y:  litWord("2"),
   275  		})),
   276  		posix: subshell(stmt(subshell(litStmt("a", "==", "2")))),
   277  	},
   278  	{
   279  		Strs: []string{"if (($# > 2)); then b; fi"},
   280  		bsmk: &IfClause{
   281  			Cond: stmts(arithmCmd(&BinaryArithm{
   282  				Op: Gtr,
   283  				X:  word(litParamExp("#")),
   284  				Y:  litWord("2"),
   285  			})),
   286  			Then: litStmts("b"),
   287  		},
   288  	},
   289  	{
   290  		Strs: []string{
   291  			"(($(date -u) > DATE))",
   292  			"((`date -u` > DATE))",
   293  		},
   294  		bsmk: arithmCmd(&BinaryArithm{
   295  			Op: Gtr,
   296  			X:  word(cmdSubst(litStmt("date", "-u"))),
   297  			Y:  litWord("DATE"),
   298  		}),
   299  	},
   300  	{
   301  		Strs: []string{": $((0x$foo == 10))"},
   302  		common: call(
   303  			litWord(":"),
   304  			word(arithmExp(&BinaryArithm{
   305  				Op: Eql,
   306  				X:  word(lit("0x"), litParamExp("foo")),
   307  				Y:  litWord("10"),
   308  			})),
   309  		),
   310  	},
   311  	{
   312  		Strs: []string{"((# 1 + 2))", "(( # 1 + 2 ))"},
   313  		mksh: &ArithmCmd{
   314  			X: &BinaryArithm{
   315  				Op: Add,
   316  				X:  litWord("1"),
   317  				Y:  litWord("2"),
   318  			},
   319  			Unsigned: true,
   320  		},
   321  	},
   322  	{
   323  		Strs: []string{"$((# 1 + 2))", "$(( # 1 + 2 ))"},
   324  		mksh: &ArithmExp{
   325  			X: &BinaryArithm{
   326  				Op: Add,
   327  				X:  litWord("1"),
   328  				Y:  litWord("2"),
   329  			},
   330  			Unsigned: true,
   331  		},
   332  	},
   333  	{
   334  		Strs: []string{"((3#20))"},
   335  		bsmk: arithmCmd(litWord("3#20")),
   336  	},
   337  	{
   338  		Strs: []string{
   339  			"while a; do b; done",
   340  			"wh\\\nile a; do b; done",
   341  			"while a\ndo\nb\ndone",
   342  			"while a;\ndo\nb\ndone",
   343  		},
   344  		common: &WhileClause{
   345  			Cond: litStmts("a"),
   346  			Do:   litStmts("b"),
   347  		},
   348  	},
   349  	{
   350  		Strs: []string{"while { a; }; do b; done", "while { a; } do b; done"},
   351  		common: &WhileClause{
   352  			Cond: stmts(block(litStmt("a"))),
   353  			Do:   litStmts("b"),
   354  		},
   355  	},
   356  	{
   357  		Strs: []string{"while (a); do b; done", "while (a) do b; done"},
   358  		common: &WhileClause{
   359  			Cond: stmts(subshell(litStmt("a"))),
   360  			Do:   litStmts("b"),
   361  		},
   362  	},
   363  	{
   364  		Strs: []string{"while ((1 > 2)); do b; done"},
   365  		bsmk: &WhileClause{
   366  			Cond: stmts(arithmCmd(&BinaryArithm{
   367  				Op: Gtr,
   368  				X:  litWord("1"),
   369  				Y:  litWord("2"),
   370  			})),
   371  			Do: litStmts("b"),
   372  		},
   373  	},
   374  	{
   375  		Strs: []string{"until a; do b; done", "until a\ndo\nb\ndone"},
   376  		common: &WhileClause{
   377  			Until: true,
   378  			Cond:  litStmts("a"),
   379  			Do:    litStmts("b"),
   380  		},
   381  	},
   382  	{
   383  		Strs: []string{
   384  			"for i; do foo; done",
   385  			"for i do foo; done",
   386  			"for i\ndo foo\ndone",
   387  			"for i;\ndo foo\ndone",
   388  			"for i in; do foo; done",
   389  		},
   390  		common: &ForClause{
   391  			Loop: &WordIter{Name: lit("i")},
   392  			Do:   litStmts("foo"),
   393  		},
   394  	},
   395  	{
   396  		Strs: []string{
   397  			"for i in 1 2 3; do echo $i; done",
   398  			"for i in 1 2 3\ndo echo $i\ndone",
   399  			"for i in 1 2 3;\ndo echo $i\ndone",
   400  			"for i in 1 2 3 #foo\ndo echo $i\ndone",
   401  		},
   402  		common: &ForClause{
   403  			Loop: &WordIter{
   404  				Name:  lit("i"),
   405  				Items: litWords("1", "2", "3"),
   406  			},
   407  			Do: stmts(call(
   408  				litWord("echo"),
   409  				word(litParamExp("i")),
   410  			)),
   411  		},
   412  	},
   413  	{
   414  		Strs: []string{
   415  			"for i in \\\n\t1 2 3; do #foo\n\techo $i\ndone",
   416  			"for i #foo\n\tin 1 2 3; do\n\techo $i\ndone",
   417  		},
   418  		common: &ForClause{
   419  			Loop: &WordIter{
   420  				Name:  lit("i"),
   421  				Items: litWords("1", "2", "3"),
   422  			},
   423  			Do: stmts(call(
   424  				litWord("echo"),
   425  				word(litParamExp("i")),
   426  			)),
   427  		},
   428  	},
   429  	{
   430  		Strs: []string{
   431  			"for ((i = 0; i < 10; i++)); do echo $i; done",
   432  			"for ((i=0;i<10;i++)) do echo $i; done",
   433  			"for (( i = 0 ; i < 10 ; i++ ))\ndo echo $i\ndone",
   434  			"for (( i = 0 ; i < 10 ; i++ ));\ndo echo $i\ndone",
   435  		},
   436  		bash: &ForClause{
   437  			Loop: &CStyleLoop{
   438  				Init: &BinaryArithm{
   439  					Op: Assgn,
   440  					X:  litWord("i"),
   441  					Y:  litWord("0"),
   442  				},
   443  				Cond: &BinaryArithm{
   444  					Op: Lss,
   445  					X:  litWord("i"),
   446  					Y:  litWord("10"),
   447  				},
   448  				Post: &UnaryArithm{
   449  					Op:   Inc,
   450  					Post: true,
   451  					X:    litWord("i"),
   452  				},
   453  			},
   454  			Do: stmts(call(
   455  				litWord("echo"),
   456  				word(litParamExp("i")),
   457  			)),
   458  		},
   459  	},
   460  	{
   461  		Strs: []string{
   462  			"for (( ; ; )); do foo; done",
   463  			"for ((;;)); do foo; done",
   464  		},
   465  		bash: &ForClause{
   466  			Loop: &CStyleLoop{},
   467  			Do:   litStmts("foo"),
   468  		},
   469  	},
   470  	{
   471  		Strs: []string{
   472  			"for ((i = 0; ; )); do foo; done",
   473  			"for ((i = 0;;)); do foo; done",
   474  		},
   475  		bash: &ForClause{
   476  			Loop: &CStyleLoop{
   477  				Init: &BinaryArithm{
   478  					Op: Assgn,
   479  					X:  litWord("i"),
   480  					Y:  litWord("0"),
   481  				},
   482  			},
   483  			Do: litStmts("foo"),
   484  		},
   485  	},
   486  	{
   487  		Strs: []string{
   488  			"select i; do foo; done",
   489  			// TODO: bash won't allow this - bug?
   490  			//"select i in; do foo; done",
   491  		},
   492  		bsmk: &ForClause{
   493  			Select: true,
   494  			Loop:   &WordIter{Name: lit("i")},
   495  			Do:     litStmts("foo"),
   496  		},
   497  	},
   498  	{
   499  		Strs: []string{
   500  			"select i in 1 2 3; do echo $i; done",
   501  			"select i in 1 2 3\ndo echo $i\ndone",
   502  			"select i in 1 2 3 #foo\ndo echo $i\ndone",
   503  		},
   504  		bsmk: &ForClause{
   505  			Select: true,
   506  			Loop: &WordIter{
   507  				Name:  lit("i"),
   508  				Items: litWords("1", "2", "3"),
   509  			},
   510  			Do: stmts(call(
   511  				litWord("echo"),
   512  				word(litParamExp("i")),
   513  			)),
   514  		},
   515  	},
   516  	{
   517  		Strs:  []string{"select foo bar"},
   518  		posix: litStmt("select", "foo", "bar"),
   519  	},
   520  	{
   521  		Strs: []string{`' ' "foo bar"`},
   522  		common: call(
   523  			word(sglQuoted(" ")),
   524  			word(dblQuoted(lit("foo bar"))),
   525  		),
   526  	},
   527  	{
   528  		Strs:   []string{`"foo \" bar"`},
   529  		common: word(dblQuoted(lit(`foo \" bar`))),
   530  	},
   531  	{
   532  		Strs: []string{"\">foo\" \"\nbar\""},
   533  		common: call(
   534  			word(dblQuoted(lit(">foo"))),
   535  			word(dblQuoted(lit("\nbar"))),
   536  		),
   537  	},
   538  	{
   539  		Strs:   []string{`foo \" bar`},
   540  		common: litCall(`foo`, `\"`, `bar`),
   541  	},
   542  	{
   543  		Strs:   []string{`'"'`},
   544  		common: sglQuoted(`"`),
   545  	},
   546  	{
   547  		Strs:   []string{"'`'"},
   548  		common: sglQuoted("`"),
   549  	},
   550  	{
   551  		Strs:   []string{`"'"`},
   552  		common: dblQuoted(lit("'")),
   553  	},
   554  	{
   555  		Strs:   []string{`""`},
   556  		common: dblQuoted(),
   557  	},
   558  	{
   559  		Strs:   []string{"=a s{s s=s"},
   560  		common: litCall("=a", "s{s", "s=s"),
   561  	},
   562  	{
   563  		Strs: []string{"foo && bar", "foo&&bar", "foo &&\nbar"},
   564  		common: &BinaryCmd{
   565  			Op: AndStmt,
   566  			X:  litStmt("foo"),
   567  			Y:  litStmt("bar"),
   568  		},
   569  	},
   570  	{
   571  		Strs: []string{"foo &&\n\tbar"},
   572  		common: &BinaryCmd{
   573  			Op: AndStmt,
   574  			X:  litStmt("foo"),
   575  			Y:  litStmt("bar"),
   576  		},
   577  	},
   578  	{
   579  		Strs: []string{"foo || bar", "foo||bar", "foo ||\nbar"},
   580  		common: &BinaryCmd{
   581  			Op: OrStmt,
   582  			X:  litStmt("foo"),
   583  			Y:  litStmt("bar"),
   584  		},
   585  	},
   586  	{
   587  		Strs: []string{"if a; then b; fi || while a; do b; done"},
   588  		common: &BinaryCmd{
   589  			Op: OrStmt,
   590  			X: stmt(&IfClause{
   591  				Cond: litStmts("a"),
   592  				Then: litStmts("b"),
   593  			}),
   594  			Y: stmt(&WhileClause{
   595  				Cond: litStmts("a"),
   596  				Do:   litStmts("b"),
   597  			}),
   598  		},
   599  	},
   600  	{
   601  		Strs: []string{"foo && bar1 || bar2"},
   602  		common: &BinaryCmd{
   603  			Op: OrStmt,
   604  			X: stmt(&BinaryCmd{
   605  				Op: AndStmt,
   606  				X:  litStmt("foo"),
   607  				Y:  litStmt("bar1"),
   608  			}),
   609  			Y: litStmt("bar2"),
   610  		},
   611  	},
   612  	{
   613  		Strs: []string{"a || b || c || d"},
   614  		common: &BinaryCmd{
   615  			Op: OrStmt,
   616  			X: stmt(&BinaryCmd{
   617  				Op: OrStmt,
   618  				X: stmt(&BinaryCmd{
   619  					Op: OrStmt,
   620  					X:  litStmt("a"),
   621  					Y:  litStmt("b"),
   622  				}),
   623  				Y: litStmt("c"),
   624  			}),
   625  			Y: litStmt("d"),
   626  		},
   627  	},
   628  	{
   629  		Strs: []string{"foo | bar", "foo|bar", "foo |\n#etc\nbar"},
   630  		common: &BinaryCmd{
   631  			Op: Pipe,
   632  			X:  litStmt("foo"),
   633  			Y:  litStmt("bar"),
   634  		},
   635  	},
   636  	{
   637  		Strs: []string{"foo | bar | extra"},
   638  		common: &BinaryCmd{
   639  			Op: Pipe,
   640  			X:  litStmt("foo"),
   641  			Y: stmt(&BinaryCmd{
   642  				Op: Pipe,
   643  				X:  litStmt("bar"),
   644  				Y:  litStmt("extra"),
   645  			}),
   646  		},
   647  	},
   648  	{
   649  		Strs: []string{"foo | a=b bar"},
   650  		common: &BinaryCmd{
   651  			Op: Pipe,
   652  			X:  litStmt("foo"),
   653  			Y: stmt(&CallExpr{
   654  				Assigns: []*Assign{{
   655  					Name:  lit("a"),
   656  					Value: litWord("b"),
   657  				}},
   658  				Args: litWords("bar"),
   659  			}),
   660  		},
   661  	},
   662  	{
   663  		Strs: []string{"foo |&"},
   664  		mksh: &Stmt{Cmd: litCall("foo"), Coprocess: true},
   665  	},
   666  	{
   667  		Strs: []string{"foo |& bar", "foo|&bar"},
   668  		bash: &BinaryCmd{
   669  			Op: PipeAll,
   670  			X:  litStmt("foo"),
   671  			Y:  litStmt("bar"),
   672  		},
   673  		mksh: []*Stmt{
   674  			{Cmd: litCall("foo"), Coprocess: true},
   675  			litStmt("bar"),
   676  		},
   677  	},
   678  	{
   679  		Strs: []string{
   680  			"foo() {\n\ta\n\tb\n}",
   681  			"foo() { a; b; }",
   682  			"foo ( ) {\na\nb\n}",
   683  			"foo()\n{\na\nb\n}",
   684  		},
   685  		common: &FuncDecl{
   686  			Name: lit("foo"),
   687  			Body: stmt(block(litStmt("a"), litStmt("b"))),
   688  		},
   689  	},
   690  	{
   691  		Strs: []string{"foo() { a; }\nbar", "foo() {\na\n}; bar"},
   692  		common: []Command{
   693  			&FuncDecl{
   694  				Name: lit("foo"),
   695  				Body: stmt(block(litStmt("a"))),
   696  			},
   697  			litCall("bar"),
   698  		},
   699  	},
   700  	{
   701  		Strs: []string{"foO_123() { a; }"},
   702  		common: &FuncDecl{
   703  			Name: lit("foO_123"),
   704  			Body: stmt(block(litStmt("a"))),
   705  		},
   706  	},
   707  	{
   708  		Strs: []string{"-foo_.,+-bar() { a; }"},
   709  		bsmk: &FuncDecl{
   710  			Name: lit("-foo_.,+-bar"),
   711  			Body: stmt(block(litStmt("a"))),
   712  		},
   713  	},
   714  	{
   715  		Strs: []string{
   716  			"function foo() {\n\ta\n\tb\n}",
   717  			"function foo {\n\ta\n\tb\n}",
   718  			"function foo() { a; b; }",
   719  		},
   720  		bsmk: &FuncDecl{
   721  			RsrvWord: true,
   722  			Name:     lit("foo"),
   723  			Body:     stmt(block(litStmt("a"), litStmt("b"))),
   724  		},
   725  	},
   726  	{
   727  		Strs: []string{"function foo() (a)"},
   728  		bash: &FuncDecl{
   729  			RsrvWord: true,
   730  			Name:     lit("foo"),
   731  			Body:     stmt(subshell(litStmt("a"))),
   732  		},
   733  	},
   734  	{
   735  		Strs: []string{"a=b foo=$bar foo=start$bar"},
   736  		common: &CallExpr{
   737  			Assigns: []*Assign{
   738  				{Name: lit("a"), Value: litWord("b")},
   739  				{Name: lit("foo"), Value: word(litParamExp("bar"))},
   740  				{Name: lit("foo"), Value: word(
   741  					lit("start"),
   742  					litParamExp("bar"),
   743  				)},
   744  			},
   745  		},
   746  	},
   747  	{
   748  		Strs: []string{"a=\"\nbar\""},
   749  		common: &CallExpr{
   750  			Assigns: []*Assign{{
   751  				Name:  lit("a"),
   752  				Value: word(dblQuoted(lit("\nbar"))),
   753  			}},
   754  		},
   755  	},
   756  	{
   757  		Strs: []string{"A_3a= foo"},
   758  		common: &CallExpr{
   759  			Assigns: []*Assign{{Name: lit("A_3a")}},
   760  			Args:    litWords("foo"),
   761  		},
   762  	},
   763  	{
   764  		Strs: []string{"a=b=c"},
   765  		common: &CallExpr{
   766  			Assigns: []*Assign{{Name: lit("a"), Value: litWord("b=c")}},
   767  		},
   768  	},
   769  	{
   770  		Strs:   []string{"à=b foo"},
   771  		common: litStmt("à=b", "foo"),
   772  	},
   773  	{
   774  		Strs: []string{
   775  			"foo >a >>b <c",
   776  			"foo > a >> b < c",
   777  			">a >>b <c foo",
   778  		},
   779  		common: &Stmt{
   780  			Cmd: litCall("foo"),
   781  			Redirs: []*Redirect{
   782  				{Op: RdrOut, Word: litWord("a")},
   783  				{Op: AppOut, Word: litWord("b")},
   784  				{Op: RdrIn, Word: litWord("c")},
   785  			},
   786  		},
   787  	},
   788  	{
   789  		Strs: []string{
   790  			"foo bar >a",
   791  			"foo >a bar",
   792  		},
   793  		common: &Stmt{
   794  			Cmd: litCall("foo", "bar"),
   795  			Redirs: []*Redirect{
   796  				{Op: RdrOut, Word: litWord("a")},
   797  			},
   798  		},
   799  	},
   800  	{
   801  		Strs: []string{`>a >\b`},
   802  		common: &Stmt{
   803  			Redirs: []*Redirect{
   804  				{Op: RdrOut, Word: litWord("a")},
   805  				{Op: RdrOut, Word: litWord(`\b`)},
   806  			},
   807  		},
   808  	},
   809  	{
   810  		Strs: []string{">a\n>b", ">a; >b"},
   811  		common: []*Stmt{
   812  			{Redirs: []*Redirect{
   813  				{Op: RdrOut, Word: litWord("a")},
   814  			}},
   815  			{Redirs: []*Redirect{
   816  				{Op: RdrOut, Word: litWord("b")},
   817  			}},
   818  		},
   819  	},
   820  	{
   821  		Strs: []string{"foo1\nfoo2 >r2", "foo1; >r2 foo2"},
   822  		common: []*Stmt{
   823  			litStmt("foo1"),
   824  			{
   825  				Cmd: litCall("foo2"),
   826  				Redirs: []*Redirect{
   827  					{Op: RdrOut, Word: litWord("r2")},
   828  				},
   829  			},
   830  		},
   831  	},
   832  	{
   833  		Strs: []string{"foo >bar$(etc)", "foo >b\\\nar`etc`"},
   834  		common: &Stmt{
   835  			Cmd: litCall("foo"),
   836  			Redirs: []*Redirect{
   837  				{Op: RdrOut, Word: word(
   838  					lit("bar"),
   839  					cmdSubst(litStmt("etc")),
   840  				)},
   841  			},
   842  		},
   843  	},
   844  	{
   845  		Strs: []string{
   846  			"a=b c=d foo >x <y",
   847  			"a=b c=d >x <y foo",
   848  			">x a=b c=d <y foo",
   849  			">x <y a=b c=d foo",
   850  			"a=b >x c=d foo <y",
   851  		},
   852  		common: &Stmt{
   853  			Cmd: &CallExpr{
   854  				Assigns: []*Assign{
   855  					{Name: lit("a"), Value: litWord("b")},
   856  					{Name: lit("c"), Value: litWord("d")},
   857  				},
   858  				Args: litWords("foo"),
   859  			},
   860  			Redirs: []*Redirect{
   861  				{Op: RdrOut, Word: litWord("x")},
   862  				{Op: RdrIn, Word: litWord("y")},
   863  			},
   864  		},
   865  	},
   866  	{
   867  		Strs: []string{
   868  			"foo <<EOF\nbar\nEOF",
   869  			"foo <<EOF \nbar\nEOF",
   870  			"foo <<EOF\t\nbar\nEOF",
   871  		},
   872  		common: &Stmt{
   873  			Cmd: litCall("foo"),
   874  			Redirs: []*Redirect{{
   875  				Op:   Hdoc,
   876  				Word: litWord("EOF"),
   877  				Hdoc: litWord("bar\n"),
   878  			}},
   879  		},
   880  	},
   881  	{
   882  		Strs: []string{"foo <<EOF\n\nbar\nEOF"},
   883  		common: &Stmt{
   884  			Cmd: litCall("foo"),
   885  			Redirs: []*Redirect{{
   886  				Op:   Hdoc,
   887  				Word: litWord("EOF"),
   888  				Hdoc: litWord("\nbar\n"),
   889  			}},
   890  		},
   891  	},
   892  	{
   893  		Strs: []string{"foo <<EOF\nbar\n\nEOF"},
   894  		common: &Stmt{
   895  			Cmd: litCall("foo"),
   896  			Redirs: []*Redirect{{
   897  				Op:   Hdoc,
   898  				Word: litWord("EOF"),
   899  				Hdoc: litWord("bar\n\n"),
   900  			}},
   901  		},
   902  	},
   903  	{
   904  		Strs: []string{"foo <<EOF\n1\n2\n3\nEOF"},
   905  		common: &Stmt{
   906  			Cmd: litCall("foo"),
   907  			Redirs: []*Redirect{{
   908  				Op:   Hdoc,
   909  				Word: litWord("EOF"),
   910  				Hdoc: litWord("1\n2\n3\n"),
   911  			}},
   912  		},
   913  	},
   914  	{
   915  		Strs: []string{"a <<EOF\nfoo$bar\nEOF"},
   916  		common: &Stmt{
   917  			Cmd: litCall("a"),
   918  			Redirs: []*Redirect{{
   919  				Op:   Hdoc,
   920  				Word: litWord("EOF"),
   921  				Hdoc: word(
   922  					lit("foo"),
   923  					litParamExp("bar"),
   924  					lit("\n"),
   925  				),
   926  			}},
   927  		},
   928  	},
   929  	{
   930  		Strs: []string{"a <<EOF\n\"$bar\"\nEOF"},
   931  		common: &Stmt{
   932  			Cmd: litCall("a"),
   933  			Redirs: []*Redirect{{
   934  				Op:   Hdoc,
   935  				Word: litWord("EOF"),
   936  				Hdoc: word(
   937  					lit(`"`),
   938  					litParamExp("bar"),
   939  					lit("\"\n"),
   940  				),
   941  			}},
   942  		},
   943  	},
   944  	{
   945  		Strs: []string{"a <<EOF\n$''$bar\nEOF"},
   946  		bash: &Stmt{
   947  			Cmd: litCall("a"),
   948  			Redirs: []*Redirect{{
   949  				Op:   Hdoc,
   950  				Word: litWord("EOF"),
   951  				Hdoc: word(
   952  					lit("$"),
   953  					lit("''"),
   954  					litParamExp("bar"),
   955  					lit("\n"),
   956  				),
   957  			}},
   958  		},
   959  	},
   960  	{
   961  		Strs: []string{
   962  			"a <<EOF\n$(b)\nc\nEOF",
   963  			"a <<EOF\n`b`\nc\nEOF",
   964  		},
   965  		common: &Stmt{
   966  			Cmd: litCall("a"),
   967  			Redirs: []*Redirect{{
   968  				Op:   Hdoc,
   969  				Word: litWord("EOF"),
   970  				Hdoc: word(
   971  					cmdSubst(litStmt("b")),
   972  					lit("\nc\n"),
   973  				),
   974  			}},
   975  		},
   976  	},
   977  	{
   978  		Strs: []string{"a <<EOF\n\\${\nEOF"},
   979  		common: &Stmt{
   980  			Cmd: litCall("a"),
   981  			Redirs: []*Redirect{{
   982  				Op:   Hdoc,
   983  				Word: litWord("EOF"),
   984  				Hdoc: litWord("\\${\n"),
   985  			}},
   986  		},
   987  	},
   988  	{
   989  		Strs: []string{
   990  			"{\n\tfoo <<EOF\nbar\nEOF\n}",
   991  			"{ foo <<EOF\nbar\nEOF\n}",
   992  		},
   993  		common: block(&Stmt{
   994  			Cmd: litCall("foo"),
   995  			Redirs: []*Redirect{{
   996  				Op:   Hdoc,
   997  				Word: litWord("EOF"),
   998  				Hdoc: litWord("bar\n"),
   999  			}},
  1000  		}),
  1001  	},
  1002  	{
  1003  		Strs: []string{
  1004  			"$(\n\tfoo <<EOF\nbar\nEOF\n)",
  1005  			"$(foo <<EOF\nbar\nEOF\n)",
  1006  		},
  1007  		common: cmdSubst(&Stmt{
  1008  			Cmd: litCall("foo"),
  1009  			Redirs: []*Redirect{{
  1010  				Op:   Hdoc,
  1011  				Word: litWord("EOF"),
  1012  				Hdoc: litWord("bar\n"),
  1013  			}},
  1014  		}),
  1015  	},
  1016  	{
  1017  		Strs: []string{"$(<foo)", "`<foo`"},
  1018  		common: cmdSubst(&Stmt{
  1019  			Redirs: []*Redirect{{
  1020  				Op:   RdrIn,
  1021  				Word: litWord("foo"),
  1022  			}},
  1023  		}),
  1024  	},
  1025  	{
  1026  		Strs: []string{"foo <<EOF >f\nbar\nEOF"},
  1027  		common: &Stmt{
  1028  			Cmd: litCall("foo"),
  1029  			Redirs: []*Redirect{
  1030  				{
  1031  					Op:   Hdoc,
  1032  					Word: litWord("EOF"),
  1033  					Hdoc: litWord("bar\n"),
  1034  				},
  1035  				{Op: RdrOut, Word: litWord("f")},
  1036  			},
  1037  		},
  1038  	},
  1039  	{
  1040  		Strs: []string{"foo <<EOF && {\nbar\nEOF\n\tetc\n}"},
  1041  		common: &BinaryCmd{
  1042  			Op: AndStmt,
  1043  			X: &Stmt{
  1044  				Cmd: litCall("foo"),
  1045  				Redirs: []*Redirect{{
  1046  					Op:   Hdoc,
  1047  					Word: litWord("EOF"),
  1048  					Hdoc: litWord("bar\n"),
  1049  				}},
  1050  			},
  1051  			Y: stmt(block(litStmt("etc"))),
  1052  		},
  1053  	},
  1054  	{
  1055  		Strs: []string{
  1056  			"$(\n\tfoo\n) <<EOF\nbar\nEOF",
  1057  			"<<EOF $(\n\tfoo\n)\nbar\nEOF",
  1058  		},
  1059  		// note that dash won't accept the second one
  1060  		bsmk: &Stmt{
  1061  			Cmd: call(word(cmdSubst(litStmt("foo")))),
  1062  			Redirs: []*Redirect{{
  1063  				Op:   Hdoc,
  1064  				Word: litWord("EOF"),
  1065  				Hdoc: litWord("bar\n"),
  1066  			}},
  1067  		},
  1068  	},
  1069  	{
  1070  		Strs: []string{
  1071  			"$(\n\tfoo\n) <<EOF\nbar\nEOF",
  1072  			"`\n\tfoo\n` <<EOF\nbar\nEOF",
  1073  			"<<EOF `\n\tfoo\n`\nbar\nEOF",
  1074  		},
  1075  		common: &Stmt{
  1076  			Cmd: call(word(cmdSubst(litStmt("foo")))),
  1077  			Redirs: []*Redirect{{
  1078  				Op:   Hdoc,
  1079  				Word: litWord("EOF"),
  1080  				Hdoc: litWord("bar\n"),
  1081  			}},
  1082  		},
  1083  	},
  1084  	{
  1085  		Strs: []string{
  1086  			"$((foo)) <<EOF\nbar\nEOF",
  1087  			"<<EOF $((\n\tfoo\n))\nbar\nEOF",
  1088  		},
  1089  		common: &Stmt{
  1090  			Cmd: call(word(arithmExp(litWord("foo")))),
  1091  			Redirs: []*Redirect{{
  1092  				Op:   Hdoc,
  1093  				Word: litWord("EOF"),
  1094  				Hdoc: litWord("bar\n"),
  1095  			}},
  1096  		},
  1097  	},
  1098  	{
  1099  		Strs: []string{"if true; then\n\tfoo <<-EOF\n\t\tbar\n\tEOF\nfi"},
  1100  		common: &IfClause{
  1101  			Cond: litStmts("true"),
  1102  			Then: stmtList(&Stmt{
  1103  				Cmd: litCall("foo"),
  1104  				Redirs: []*Redirect{{
  1105  					Op:   DashHdoc,
  1106  					Word: litWord("EOF"),
  1107  					Hdoc: litWord("\t\tbar\n\t"),
  1108  				}},
  1109  			}),
  1110  		},
  1111  	},
  1112  	{
  1113  		Strs: []string{"if true; then\n\tfoo <<-EOF\n\tEOF\nfi"},
  1114  		common: &IfClause{
  1115  			Cond: litStmts("true"),
  1116  			Then: stmtList(&Stmt{
  1117  				Cmd: litCall("foo"),
  1118  				Redirs: []*Redirect{{
  1119  					Op:   DashHdoc,
  1120  					Word: litWord("EOF"),
  1121  					Hdoc: litWord("\t"),
  1122  				}},
  1123  			}),
  1124  		},
  1125  	},
  1126  	{
  1127  		Strs: []string{"foo <<EOF\nbar\nEOF\nfoo2"},
  1128  		common: []*Stmt{
  1129  			{
  1130  				Cmd: litCall("foo"),
  1131  				Redirs: []*Redirect{{
  1132  					Op:   Hdoc,
  1133  					Word: litWord("EOF"),
  1134  					Hdoc: litWord("bar\n"),
  1135  				}},
  1136  			},
  1137  			litStmt("foo2"),
  1138  		},
  1139  	},
  1140  	{
  1141  		Strs: []string{"foo <<FOOBAR\nbar\nFOOBAR"},
  1142  		common: &Stmt{
  1143  			Cmd: litCall("foo"),
  1144  			Redirs: []*Redirect{{
  1145  				Op:   Hdoc,
  1146  				Word: litWord("FOOBAR"),
  1147  				Hdoc: litWord("bar\n"),
  1148  			}},
  1149  		},
  1150  	},
  1151  	{
  1152  		Strs: []string{"foo <<\"EOF\"\nbar\nEOF"},
  1153  		common: &Stmt{
  1154  			Cmd: litCall("foo"),
  1155  			Redirs: []*Redirect{{
  1156  				Op:   Hdoc,
  1157  				Word: word(dblQuoted(lit("EOF"))),
  1158  				Hdoc: litWord("bar\n"),
  1159  			}},
  1160  		},
  1161  	},
  1162  	{
  1163  		Strs: []string{"foo <<'EOF'\n${\nEOF"},
  1164  		common: &Stmt{
  1165  			Cmd: litCall("foo"),
  1166  			Redirs: []*Redirect{{
  1167  				Op:   Hdoc,
  1168  				Word: word(sglQuoted("EOF")),
  1169  				Hdoc: litWord("${\n"),
  1170  			}},
  1171  		},
  1172  	},
  1173  	{
  1174  		Strs: []string{"foo <<'EOF'\nEOF"},
  1175  		common: &Stmt{
  1176  			Cmd: litCall("foo"),
  1177  			Redirs: []*Redirect{{
  1178  				Op:   Hdoc,
  1179  				Word: word(sglQuoted("EOF")),
  1180  			}},
  1181  		},
  1182  	},
  1183  	{
  1184  		Strs: []string{"foo <<\"EOF\"2\nbar\nEOF2"},
  1185  		common: &Stmt{
  1186  			Cmd: litCall("foo"),
  1187  			Redirs: []*Redirect{{
  1188  				Op:   Hdoc,
  1189  				Word: word(dblQuoted(lit("EOF")), lit("2")),
  1190  				Hdoc: litWord("bar\n"),
  1191  			}},
  1192  		},
  1193  	},
  1194  	{
  1195  		Strs: []string{"foo <<\\EOF\nbar\nEOF"},
  1196  		common: &Stmt{
  1197  			Cmd: litCall("foo"),
  1198  			Redirs: []*Redirect{{
  1199  				Op:   Hdoc,
  1200  				Word: litWord("\\EOF"),
  1201  				Hdoc: litWord("bar\n"),
  1202  			}},
  1203  		},
  1204  	},
  1205  	{
  1206  		Strs: []string{"foo <<-EOF\n\tbar\nEOF"},
  1207  		common: &Stmt{
  1208  			Cmd: litCall("foo"),
  1209  			Redirs: []*Redirect{{
  1210  				Op:   DashHdoc,
  1211  				Word: litWord("EOF"),
  1212  				Hdoc: litWord("\tbar\n"),
  1213  			}},
  1214  		},
  1215  	},
  1216  	{
  1217  		Strs: []string{"foo <<EOF\nEOF"},
  1218  		common: &Stmt{
  1219  			Cmd: litCall("foo"),
  1220  			Redirs: []*Redirect{{
  1221  				Op:   Hdoc,
  1222  				Word: litWord("EOF"),
  1223  			}},
  1224  		},
  1225  	},
  1226  	{
  1227  		Strs: []string{"foo <<-EOF\nEOF"},
  1228  		common: &Stmt{
  1229  			Cmd: litCall("foo"),
  1230  			Redirs: []*Redirect{{
  1231  				Op:   DashHdoc,
  1232  				Word: litWord("EOF"),
  1233  			}},
  1234  		},
  1235  	},
  1236  	{
  1237  		Strs: []string{"foo <<-EOF\n\tbar\nEOF"},
  1238  		common: &Stmt{
  1239  			Cmd: litCall("foo"),
  1240  			Redirs: []*Redirect{{
  1241  				Op:   DashHdoc,
  1242  				Word: litWord("EOF"),
  1243  				Hdoc: litWord("\tbar\n"),
  1244  			}},
  1245  		},
  1246  	},
  1247  	{
  1248  		Strs: []string{"foo <<-'EOF'\n\tbar\nEOF"},
  1249  		common: &Stmt{
  1250  			Cmd: litCall("foo"),
  1251  			Redirs: []*Redirect{{
  1252  				Op:   DashHdoc,
  1253  				Word: word(sglQuoted("EOF")),
  1254  				Hdoc: litWord("\tbar\n"),
  1255  			}},
  1256  		},
  1257  	},
  1258  	{
  1259  		Strs: []string{
  1260  			"f1 <<EOF1\nh1\nEOF1\nf2 <<EOF2\nh2\nEOF2",
  1261  			"f1 <<EOF1; f2 <<EOF2\nh1\nEOF1\nh2\nEOF2",
  1262  		},
  1263  		common: []*Stmt{
  1264  			{
  1265  				Cmd: litCall("f1"),
  1266  				Redirs: []*Redirect{{
  1267  					Op:   Hdoc,
  1268  					Word: litWord("EOF1"),
  1269  					Hdoc: litWord("h1\n"),
  1270  				}},
  1271  			},
  1272  			{
  1273  				Cmd: litCall("f2"),
  1274  				Redirs: []*Redirect{{
  1275  					Op:   Hdoc,
  1276  					Word: litWord("EOF2"),
  1277  					Hdoc: litWord("h2\n"),
  1278  				}},
  1279  			},
  1280  		},
  1281  	},
  1282  	{
  1283  		Strs: []string{
  1284  			"a <<EOF\nfoo\nEOF\nb\nb\nb\nb\nb\nb\nb\nb\nb",
  1285  			"a <<EOF;b;b;b;b;b;b;b;b;b\nfoo\nEOF",
  1286  		},
  1287  		common: []*Stmt{
  1288  			{
  1289  				Cmd: litCall("a"),
  1290  				Redirs: []*Redirect{{
  1291  					Op:   Hdoc,
  1292  					Word: litWord("EOF"),
  1293  					Hdoc: litWord("foo\n"),
  1294  				}},
  1295  			},
  1296  			litStmt("b"), litStmt("b"), litStmt("b"),
  1297  			litStmt("b"), litStmt("b"), litStmt("b"),
  1298  			litStmt("b"), litStmt("b"), litStmt("b"),
  1299  		},
  1300  	},
  1301  	{
  1302  		Strs: []string{
  1303  			"foo \"\narg\" <<EOF\nbar\nEOF",
  1304  			"foo <<EOF \"\narg\"\nbar\nEOF",
  1305  		},
  1306  		common: &Stmt{
  1307  			Cmd: call(
  1308  				litWord("foo"),
  1309  				word(dblQuoted(lit("\narg"))),
  1310  			),
  1311  			Redirs: []*Redirect{{
  1312  				Op:   Hdoc,
  1313  				Word: litWord("EOF"),
  1314  				Hdoc: litWord("bar\n"),
  1315  			}},
  1316  		},
  1317  	},
  1318  	{
  1319  		Strs: []string{"foo >&2 <&0 2>file 345>file <>f2"},
  1320  		common: &Stmt{
  1321  			Cmd: litCall("foo"),
  1322  			Redirs: []*Redirect{
  1323  				{Op: DplOut, Word: litWord("2")},
  1324  				{Op: DplIn, Word: litWord("0")},
  1325  				{Op: RdrOut, N: lit("2"), Word: litWord("file")},
  1326  				{Op: RdrOut, N: lit("345"), Word: litWord("file")},
  1327  				{Op: RdrInOut, Word: litWord("f2")},
  1328  			},
  1329  		},
  1330  	},
  1331  	{
  1332  		Strs: []string{
  1333  			"foo bar >file",
  1334  			"foo bar>file",
  1335  		},
  1336  		common: &Stmt{
  1337  			Cmd: litCall("foo", "bar"),
  1338  			Redirs: []*Redirect{
  1339  				{Op: RdrOut, Word: litWord("file")},
  1340  			},
  1341  		},
  1342  	},
  1343  	{
  1344  		Strs: []string{"foo &>a &>>b"},
  1345  		bsmk: &Stmt{
  1346  			Cmd: litCall("foo"),
  1347  			Redirs: []*Redirect{
  1348  				{Op: RdrAll, Word: litWord("a")},
  1349  				{Op: AppAll, Word: litWord("b")},
  1350  			},
  1351  		},
  1352  		posix: []*Stmt{
  1353  			{Cmd: litCall("foo"), Background: true},
  1354  			{Redirs: []*Redirect{
  1355  				{Op: RdrOut, Word: litWord("a")},
  1356  			}, Background: true},
  1357  			{Redirs: []*Redirect{
  1358  				{Op: AppOut, Word: litWord("b")},
  1359  			}},
  1360  		},
  1361  	},
  1362  	{
  1363  		Strs: []string{"foo 2>file bar", "2>file foo bar"},
  1364  		common: &Stmt{
  1365  			Cmd: litCall("foo", "bar"),
  1366  			Redirs: []*Redirect{
  1367  				{Op: RdrOut, N: lit("2"), Word: litWord("file")},
  1368  			},
  1369  		},
  1370  	},
  1371  	{
  1372  		Strs: []string{"a >f1\nb >f2", "a >f1; b >f2"},
  1373  		common: []*Stmt{
  1374  			{
  1375  				Cmd:    litCall("a"),
  1376  				Redirs: []*Redirect{{Op: RdrOut, Word: litWord("f1")}},
  1377  			},
  1378  			{
  1379  				Cmd:    litCall("b"),
  1380  				Redirs: []*Redirect{{Op: RdrOut, Word: litWord("f2")}},
  1381  			},
  1382  		},
  1383  	},
  1384  	{
  1385  		Strs: []string{"foo >|bar"},
  1386  		common: &Stmt{
  1387  			Cmd: litCall("foo"),
  1388  			Redirs: []*Redirect{
  1389  				{Op: ClbOut, Word: litWord("bar")},
  1390  			},
  1391  		},
  1392  	},
  1393  	{
  1394  		Strs: []string{
  1395  			"foo <<<input",
  1396  			"foo <<< input",
  1397  		},
  1398  		bsmk: &Stmt{
  1399  			Cmd: litCall("foo"),
  1400  			Redirs: []*Redirect{{
  1401  				Op:   WordHdoc,
  1402  				Word: litWord("input"),
  1403  			}},
  1404  		},
  1405  	},
  1406  	{
  1407  		Strs: []string{
  1408  			`foo <<<"spaced input"`,
  1409  			`foo <<< "spaced input"`,
  1410  		},
  1411  		bsmk: &Stmt{
  1412  			Cmd: litCall("foo"),
  1413  			Redirs: []*Redirect{{
  1414  				Op:   WordHdoc,
  1415  				Word: word(dblQuoted(lit("spaced input"))),
  1416  			}},
  1417  		},
  1418  	},
  1419  	{
  1420  		Strs: []string{"foo >(foo)"},
  1421  		bash: call(
  1422  			litWord("foo"),
  1423  			word(&ProcSubst{
  1424  				Op:       CmdOut,
  1425  				StmtList: litStmts("foo"),
  1426  			}),
  1427  		),
  1428  	},
  1429  	{
  1430  		Strs: []string{"foo < <(foo)"},
  1431  		bash: &Stmt{
  1432  			Cmd: litCall("foo"),
  1433  			Redirs: []*Redirect{{
  1434  				Op: RdrIn,
  1435  				Word: word(&ProcSubst{
  1436  					Op:       CmdIn,
  1437  					StmtList: litStmts("foo"),
  1438  				}),
  1439  			}},
  1440  		},
  1441  	},
  1442  	{
  1443  		Strs: []string{"a<(b) c>(d)"},
  1444  		bash: call(
  1445  			word(lit("a"), &ProcSubst{
  1446  				Op:       CmdIn,
  1447  				StmtList: litStmts("b"),
  1448  			}),
  1449  			word(lit("c"), &ProcSubst{
  1450  				Op:       CmdOut,
  1451  				StmtList: litStmts("d"),
  1452  			}),
  1453  		),
  1454  	},
  1455  	{
  1456  		Strs: []string{"foo {fd}<f"},
  1457  		bash: &Stmt{
  1458  			Cmd: litCall("foo"),
  1459  			Redirs: []*Redirect{
  1460  				{Op: RdrIn, N: lit("{fd}"), Word: litWord("f")},
  1461  			},
  1462  		},
  1463  	},
  1464  	{
  1465  		Strs: []string{"! foo"},
  1466  		common: &Stmt{
  1467  			Negated: true,
  1468  			Cmd:     litCall("foo"),
  1469  		},
  1470  	},
  1471  	{
  1472  		Strs: []string{"foo &\nbar", "foo & bar", "foo&bar"},
  1473  		common: []*Stmt{
  1474  			{Cmd: litCall("foo"), Background: true},
  1475  			litStmt("bar"),
  1476  		},
  1477  	},
  1478  	{
  1479  		Strs: []string{"! if foo; then bar; fi >/dev/null &"},
  1480  		common: &Stmt{
  1481  			Negated: true,
  1482  			Cmd: &IfClause{
  1483  				Cond: litStmts("foo"),
  1484  				Then: litStmts("bar"),
  1485  			},
  1486  			Redirs: []*Redirect{
  1487  				{Op: RdrOut, Word: litWord("/dev/null")},
  1488  			},
  1489  			Background: true,
  1490  		},
  1491  	},
  1492  	{
  1493  		Strs: []string{"! foo && bar"},
  1494  		common: &BinaryCmd{
  1495  			Op: AndStmt,
  1496  			X: &Stmt{
  1497  				Cmd:     litCall("foo"),
  1498  				Negated: true,
  1499  			},
  1500  			Y: litStmt("bar"),
  1501  		},
  1502  	},
  1503  	{
  1504  		Strs: []string{"! foo | bar"},
  1505  		common: &Stmt{
  1506  			Cmd: &BinaryCmd{
  1507  				Op: Pipe,
  1508  				X:  litStmt("foo"),
  1509  				Y:  litStmt("bar"),
  1510  			},
  1511  			Negated: true,
  1512  		},
  1513  	},
  1514  	{
  1515  		Strs: []string{
  1516  			"a && b &\nc",
  1517  			"a && b & c",
  1518  		},
  1519  		common: []*Stmt{
  1520  			{
  1521  				Cmd: &BinaryCmd{
  1522  					Op: AndStmt,
  1523  					X:  litStmt("a"),
  1524  					Y:  litStmt("b"),
  1525  				},
  1526  				Background: true,
  1527  			},
  1528  			litStmt("c"),
  1529  		},
  1530  	},
  1531  	{
  1532  		Strs: []string{"a | b &"},
  1533  		common: &Stmt{
  1534  			Cmd: &BinaryCmd{
  1535  				Op: Pipe,
  1536  				X:  litStmt("a"),
  1537  				Y:  litStmt("b"),
  1538  			},
  1539  			Background: true,
  1540  		},
  1541  	},
  1542  	{
  1543  		Strs:   []string{"foo#bar"},
  1544  		common: litWord("foo#bar"),
  1545  	},
  1546  	{
  1547  		Strs:   []string{"{ echo } }; }"},
  1548  		common: block(litStmt("echo", "}", "}")),
  1549  	},
  1550  	{
  1551  		Strs: []string{"$({ echo; })"},
  1552  		common: cmdSubst(stmt(
  1553  			block(litStmt("echo")),
  1554  		)),
  1555  	},
  1556  	{
  1557  		Strs: []string{
  1558  			"$( (echo foo bar))",
  1559  			"$( (echo foo bar) )",
  1560  			"`(echo foo bar)`",
  1561  		},
  1562  		common: cmdSubst(stmt(
  1563  			subshell(litStmt("echo", "foo", "bar")),
  1564  		)),
  1565  	},
  1566  	{
  1567  		Strs:   []string{"$()"},
  1568  		common: cmdSubst(),
  1569  	},
  1570  	{
  1571  		Strs: []string{"()"},
  1572  		mksh: subshell(), // not common, as dash/bash wrongly error
  1573  	},
  1574  	{
  1575  		Strs: []string{
  1576  			"$(\n\t(a)\n\tb\n)",
  1577  			"$( (a); b)",
  1578  			"`(a); b`",
  1579  		},
  1580  		common: cmdSubst(
  1581  			stmt(subshell(litStmt("a"))),
  1582  			litStmt("b"),
  1583  		),
  1584  	},
  1585  	{
  1586  		Strs: []string{
  1587  			`$(echo \')`,
  1588  			"`" + `echo \\'` + "`",
  1589  		},
  1590  		common: cmdSubst(litStmt("echo", `\'`)),
  1591  	},
  1592  	{
  1593  		Strs: []string{
  1594  			`$(echo \\)`,
  1595  			"`" + `echo \\\\` + "`",
  1596  		},
  1597  		common: cmdSubst(litStmt("echo", `\\`)),
  1598  	},
  1599  	{
  1600  		Strs: []string{
  1601  			`$(echo '\' 'a\b' "\\" "a\a")`,
  1602  			"`" + `echo '\' 'a\b' "\\\\" "a\a"` + "`",
  1603  		},
  1604  		common: cmdSubst(stmt(call(
  1605  			litWord("echo"),
  1606  			word(sglQuoted(`\`)),
  1607  			word(sglQuoted(`a\b`)),
  1608  			word(dblQuoted(lit(`\\`))),
  1609  			word(dblQuoted(lit(`a\a`))),
  1610  		))),
  1611  	},
  1612  	{
  1613  		Strs: []string{
  1614  			"$(echo $(x))",
  1615  			"`echo \\`x\\``",
  1616  		},
  1617  		common: cmdSubst(stmt(call(
  1618  			litWord("echo"),
  1619  			word(cmdSubst(litStmt("x"))),
  1620  		))),
  1621  	},
  1622  	{
  1623  		Strs: []string{
  1624  			"$($(foo bar))",
  1625  			"`\\`foo bar\\``",
  1626  		},
  1627  		common: cmdSubst(stmt(call(
  1628  			word(cmdSubst(litStmt("foo", "bar"))),
  1629  		))),
  1630  	},
  1631  	{
  1632  		Strs: []string{"$( (a) | b)"},
  1633  		common: cmdSubst(
  1634  			stmt(&BinaryCmd{
  1635  				Op: Pipe,
  1636  				X:  stmt(subshell(litStmt("a"))),
  1637  				Y:  litStmt("b"),
  1638  			}),
  1639  		),
  1640  	},
  1641  	{
  1642  		Strs: []string{`"$( (foo))"`},
  1643  		common: dblQuoted(cmdSubst(stmt(
  1644  			subshell(litStmt("foo")),
  1645  		))),
  1646  	},
  1647  	{
  1648  		Strs: []string{"$({ echo; })", "`{ echo; }`"},
  1649  		common: cmdSubst(stmt(
  1650  			block(litStmt("echo")),
  1651  		)),
  1652  	},
  1653  	{
  1654  		Strs:   []string{`{foo}`},
  1655  		common: litWord(`{foo}`),
  1656  	},
  1657  	{
  1658  		Strs:   []string{`{"foo"`},
  1659  		common: word(lit("{"), dblQuoted(lit("foo"))),
  1660  	},
  1661  	{
  1662  		Strs:   []string{`foo"bar"`, "fo\\\no\"bar\""},
  1663  		common: word(lit("foo"), dblQuoted(lit("bar"))),
  1664  	},
  1665  	{
  1666  		Strs:   []string{`!foo`},
  1667  		common: litWord(`!foo`),
  1668  	},
  1669  	{
  1670  		Strs:   []string{"$(foo bar)", "`foo bar`"},
  1671  		common: cmdSubst(litStmt("foo", "bar")),
  1672  	},
  1673  	{
  1674  		Strs: []string{"$(foo | bar)", "`foo | bar`"},
  1675  		common: cmdSubst(
  1676  			stmt(&BinaryCmd{
  1677  				Op: Pipe,
  1678  				X:  litStmt("foo"),
  1679  				Y:  litStmt("bar"),
  1680  			}),
  1681  		),
  1682  	},
  1683  	{
  1684  		Strs: []string{"$(foo | >f)", "`foo | >f`"},
  1685  		common: cmdSubst(
  1686  			stmt(&BinaryCmd{
  1687  				Op: Pipe,
  1688  				X:  litStmt("foo"),
  1689  				Y: &Stmt{Redirs: []*Redirect{{
  1690  					Op:   RdrOut,
  1691  					Word: litWord("f"),
  1692  				}}},
  1693  			}),
  1694  		),
  1695  	},
  1696  	{
  1697  		Strs: []string{"$(foo $(b1 b2))"},
  1698  		common: cmdSubst(stmt(call(
  1699  			litWord("foo"),
  1700  			word(cmdSubst(litStmt("b1", "b2"))),
  1701  		))),
  1702  	},
  1703  	{
  1704  		Strs: []string{`"$(foo "bar")"`},
  1705  		common: dblQuoted(cmdSubst(stmt(call(
  1706  			litWord("foo"),
  1707  			word(dblQuoted(lit("bar"))),
  1708  		)))),
  1709  	},
  1710  	{
  1711  		Strs:   []string{"$(foo)", "`fo\\\no`"},
  1712  		common: cmdSubst(litStmt("foo")),
  1713  	},
  1714  	{
  1715  		Strs: []string{"foo $(bar)", "foo `bar`"},
  1716  		common: call(
  1717  			litWord("foo"),
  1718  			word(cmdSubst(litStmt("bar"))),
  1719  		),
  1720  	},
  1721  	{
  1722  		Strs: []string{"$(foo 'bar')", "`foo 'bar'`"},
  1723  		common: cmdSubst(stmt(call(
  1724  			litWord("foo"),
  1725  			word(sglQuoted("bar")),
  1726  		))),
  1727  	},
  1728  	{
  1729  		Strs: []string{`$(foo "bar")`, "`foo \"bar\"`"},
  1730  		common: cmdSubst(stmt(call(
  1731  			litWord("foo"),
  1732  			word(dblQuoted(lit("bar"))),
  1733  		))),
  1734  	},
  1735  	{
  1736  		Strs: []string{`"$(foo "bar")"`, "\"`foo \"bar\"`\""},
  1737  		common: dblQuoted(cmdSubst(stmt(call(
  1738  			litWord("foo"),
  1739  			word(dblQuoted(lit("bar"))),
  1740  		)))),
  1741  	},
  1742  	{
  1743  		Strs: []string{"${ foo;}", "${\n\tfoo; }", "${\tfoo;}"},
  1744  		mksh: &CmdSubst{
  1745  			StmtList: litStmts("foo"),
  1746  			TempFile: true,
  1747  		},
  1748  	},
  1749  	{
  1750  		Strs: []string{"${\n\tfoo\n\tbar\n}", "${ foo; bar;}"},
  1751  		mksh: &CmdSubst{
  1752  			StmtList: litStmts("foo", "bar"),
  1753  			TempFile: true,
  1754  		},
  1755  	},
  1756  	{
  1757  		Strs: []string{"${|foo;}", "${| foo; }"},
  1758  		mksh: &CmdSubst{
  1759  			StmtList: litStmts("foo"),
  1760  			ReplyVar: true,
  1761  		},
  1762  	},
  1763  	{
  1764  		Strs: []string{"${|\n\tfoo\n\tbar\n}", "${|foo; bar;}"},
  1765  		mksh: &CmdSubst{
  1766  			StmtList: litStmts("foo", "bar"),
  1767  			ReplyVar: true,
  1768  		},
  1769  	},
  1770  	{
  1771  		Strs:   []string{`"$foo"`},
  1772  		common: dblQuoted(litParamExp("foo")),
  1773  	},
  1774  	{
  1775  		Strs:   []string{`"#foo"`},
  1776  		common: dblQuoted(lit("#foo")),
  1777  	},
  1778  	{
  1779  		Strs: []string{`$@a $*a $#a $$a $?a $!a $-a $0a $30a $_a`},
  1780  		common: call(
  1781  			word(litParamExp("@"), lit("a")),
  1782  			word(litParamExp("*"), lit("a")),
  1783  			word(litParamExp("#"), lit("a")),
  1784  			word(litParamExp("$"), lit("a")),
  1785  			word(litParamExp("?"), lit("a")),
  1786  			word(litParamExp("!"), lit("a")),
  1787  			word(litParamExp("-"), lit("a")),
  1788  			word(litParamExp("0"), lit("a")),
  1789  			word(litParamExp("3"), lit("0a")),
  1790  			word(litParamExp("_a")),
  1791  		),
  1792  	},
  1793  	{
  1794  		Strs:   []string{`$`, `$ #`},
  1795  		common: litWord("$"),
  1796  	},
  1797  	{
  1798  		Strs: []string{`${@} ${*} ${#} ${$} ${?} ${!} ${0} ${29} ${-}`},
  1799  		common: call(
  1800  			word(&ParamExp{Param: lit("@")}),
  1801  			word(&ParamExp{Param: lit("*")}),
  1802  			word(&ParamExp{Param: lit("#")}),
  1803  			word(&ParamExp{Param: lit("$")}),
  1804  			word(&ParamExp{Param: lit("?")}),
  1805  			word(&ParamExp{Param: lit("!")}),
  1806  			word(&ParamExp{Param: lit("0")}),
  1807  			word(&ParamExp{Param: lit("29")}),
  1808  			word(&ParamExp{Param: lit("-")}),
  1809  		),
  1810  	},
  1811  	{
  1812  		Strs: []string{`${#$} ${#@} ${#*} ${##}`},
  1813  		common: call(
  1814  			word(&ParamExp{Length: true, Param: lit("$")}),
  1815  			word(&ParamExp{Length: true, Param: lit("@")}),
  1816  			word(&ParamExp{Length: true, Param: lit("*")}),
  1817  			word(&ParamExp{Length: true, Param: lit("#")}),
  1818  		),
  1819  	},
  1820  	{
  1821  		Strs:   []string{`${foo}`},
  1822  		common: &ParamExp{Param: lit("foo")},
  1823  	},
  1824  	{
  1825  		Strs: []string{`${foo}"bar"`},
  1826  		common: word(
  1827  			&ParamExp{Param: lit("foo")},
  1828  			dblQuoted(lit("bar")),
  1829  		),
  1830  	},
  1831  	{
  1832  		Strs: []string{`$a/b $a-b $a:b $a}b $a]b $a.b $a,b $a*b $a_b $a2b`},
  1833  		common: call(
  1834  			word(litParamExp("a"), lit("/b")),
  1835  			word(litParamExp("a"), lit("-b")),
  1836  			word(litParamExp("a"), lit(":b")),
  1837  			word(litParamExp("a"), lit("}b")),
  1838  			word(litParamExp("a"), lit("]b")),
  1839  			word(litParamExp("a"), lit(".b")),
  1840  			word(litParamExp("a"), lit(",b")),
  1841  			word(litParamExp("a"), lit("*b")),
  1842  			word(litParamExp("a_b")),
  1843  			word(litParamExp("a2b")),
  1844  		),
  1845  	},
  1846  	{
  1847  		Strs: []string{`$aàb $àb $,b`},
  1848  		common: call(
  1849  			word(litParamExp("a"), lit("àb")),
  1850  			word(lit("$"), lit("àb")),
  1851  			word(lit("$"), lit(",b")),
  1852  		),
  1853  	},
  1854  	{
  1855  		Strs:   []string{"$à", "$\\\nà"},
  1856  		common: word(lit("$"), lit("à")),
  1857  	},
  1858  	{
  1859  		Strs: []string{"$foobar", "$foo\\\nbar"},
  1860  		common: call(
  1861  			word(litParamExp("foobar")),
  1862  		),
  1863  	},
  1864  	{
  1865  		Strs: []string{"$foo\\bar"},
  1866  		common: call(
  1867  			word(litParamExp("foo"), lit("\\bar")),
  1868  		),
  1869  	},
  1870  	{
  1871  		Strs: []string{`echo -e "$foo\nbar"`},
  1872  		common: call(
  1873  			litWord("echo"), litWord("-e"),
  1874  			word(dblQuoted(
  1875  				litParamExp("foo"), lit(`\nbar`),
  1876  			)),
  1877  		),
  1878  	},
  1879  	{
  1880  		Strs: []string{`${foo-bar}`},
  1881  		common: &ParamExp{
  1882  			Param: lit("foo"),
  1883  			Exp: &Expansion{
  1884  				Op:   SubstMinus,
  1885  				Word: litWord("bar"),
  1886  			},
  1887  		},
  1888  	},
  1889  	{
  1890  		Strs: []string{`${foo+}"bar"`},
  1891  		common: word(
  1892  			&ParamExp{
  1893  				Param: lit("foo"),
  1894  				Exp:   &Expansion{Op: SubstPlus},
  1895  			},
  1896  			dblQuoted(lit("bar")),
  1897  		),
  1898  	},
  1899  	{
  1900  		Strs: []string{`${foo:=<"bar"}`},
  1901  		common: &ParamExp{
  1902  			Param: lit("foo"),
  1903  			Exp: &Expansion{
  1904  				Op:   SubstColAssgn,
  1905  				Word: word(lit("<"), dblQuoted(lit("bar"))),
  1906  			},
  1907  		},
  1908  	},
  1909  	{
  1910  		Strs: []string{
  1911  			"${foo:=b${c}$(d)}",
  1912  			"${foo:=b${c}`d`}",
  1913  		},
  1914  		common: &ParamExp{
  1915  			Param: lit("foo"),
  1916  			Exp: &Expansion{
  1917  				Op: SubstColAssgn,
  1918  				Word: word(
  1919  					lit("b"),
  1920  					&ParamExp{Param: lit("c")},
  1921  					cmdSubst(litStmt("d")),
  1922  				),
  1923  			},
  1924  		},
  1925  	},
  1926  	{
  1927  		Strs: []string{`${foo?"${bar}"}`},
  1928  		common: &ParamExp{
  1929  			Param: lit("foo"),
  1930  			Exp: &Expansion{
  1931  				Op: SubstQuest,
  1932  				Word: word(dblQuoted(
  1933  					&ParamExp{Param: lit("bar")},
  1934  				)),
  1935  			},
  1936  		},
  1937  	},
  1938  	{
  1939  		Strs: []string{`${foo:?bar1 bar2}`},
  1940  		common: &ParamExp{
  1941  			Param: lit("foo"),
  1942  			Exp: &Expansion{
  1943  				Op:   SubstColQuest,
  1944  				Word: litWord("bar1 bar2"),
  1945  			},
  1946  		},
  1947  	},
  1948  	{
  1949  		Strs: []string{`${a:+b}${a:-b}${a=b}`},
  1950  		common: word(
  1951  			&ParamExp{
  1952  				Param: lit("a"),
  1953  				Exp: &Expansion{
  1954  					Op:   SubstColPlus,
  1955  					Word: litWord("b"),
  1956  				},
  1957  			},
  1958  			&ParamExp{
  1959  				Param: lit("a"),
  1960  				Exp: &Expansion{
  1961  					Op:   SubstColMinus,
  1962  					Word: litWord("b"),
  1963  				},
  1964  			},
  1965  			&ParamExp{
  1966  				Param: lit("a"),
  1967  				Exp: &Expansion{
  1968  					Op:   SubstAssgn,
  1969  					Word: litWord("b"),
  1970  				},
  1971  			},
  1972  		),
  1973  	},
  1974  	{
  1975  		Strs: []string{`${3:-'$x'}`},
  1976  		common: &ParamExp{
  1977  			Param: lit("3"),
  1978  			Exp: &Expansion{
  1979  				Op:   SubstColMinus,
  1980  				Word: word(sglQuoted("$x")),
  1981  			},
  1982  		},
  1983  	},
  1984  	{
  1985  		Strs: []string{`${@:-$x}`},
  1986  		common: &ParamExp{
  1987  			Param: lit("@"),
  1988  			Exp: &Expansion{
  1989  				Op:   SubstColMinus,
  1990  				Word: word(litParamExp("x")),
  1991  			},
  1992  		},
  1993  	},
  1994  	{
  1995  		Strs: []string{`${foo%bar}${foo%%bar*}`},
  1996  		common: word(
  1997  			&ParamExp{
  1998  				Param: lit("foo"),
  1999  				Exp: &Expansion{
  2000  					Op:   RemSmallSuffix,
  2001  					Word: litWord("bar"),
  2002  				},
  2003  			},
  2004  			&ParamExp{
  2005  				Param: lit("foo"),
  2006  				Exp: &Expansion{
  2007  					Op:   RemLargeSuffix,
  2008  					Word: litWord("bar*"),
  2009  				},
  2010  			},
  2011  		),
  2012  	},
  2013  	{
  2014  		Strs: []string{`${3#bar}${-##bar*}`},
  2015  		common: word(
  2016  			&ParamExp{
  2017  				Param: lit("3"),
  2018  				Exp: &Expansion{
  2019  					Op:   RemSmallPrefix,
  2020  					Word: litWord("bar"),
  2021  				},
  2022  			},
  2023  			&ParamExp{
  2024  				Param: lit("-"),
  2025  				Exp: &Expansion{
  2026  					Op:   RemLargePrefix,
  2027  					Word: litWord("bar*"),
  2028  				},
  2029  			},
  2030  		),
  2031  	},
  2032  	{
  2033  		Strs: []string{`${foo%?}`},
  2034  		common: &ParamExp{
  2035  			Param: lit("foo"),
  2036  			Exp: &Expansion{
  2037  				Op:   RemSmallSuffix,
  2038  				Word: litWord("?"),
  2039  			},
  2040  		},
  2041  	},
  2042  	{
  2043  		Strs: []string{
  2044  			`${foo[1]}`,
  2045  			`${foo[ 1 ]}`,
  2046  		},
  2047  		bsmk: &ParamExp{
  2048  			Param: lit("foo"),
  2049  			Index: litWord("1"),
  2050  		},
  2051  	},
  2052  	{
  2053  		Strs: []string{`${foo[-1]}`},
  2054  		bsmk: &ParamExp{
  2055  			Param: lit("foo"),
  2056  			Index: &UnaryArithm{
  2057  				Op: Minus,
  2058  				X:  litWord("1"),
  2059  			},
  2060  		},
  2061  	},
  2062  	{
  2063  		Strs: []string{`${foo[@]}`},
  2064  		bsmk: &ParamExp{
  2065  			Param: lit("foo"),
  2066  			Index: litWord("@"),
  2067  		},
  2068  	},
  2069  	{
  2070  		Strs: []string{`${foo[*]-etc}`},
  2071  		bsmk: &ParamExp{
  2072  			Param: lit("foo"),
  2073  			Index: litWord("*"),
  2074  			Exp: &Expansion{
  2075  				Op:   SubstMinus,
  2076  				Word: litWord("etc"),
  2077  			},
  2078  		},
  2079  	},
  2080  	{
  2081  		Strs: []string{`${foo[bar]}`},
  2082  		bsmk: &ParamExp{
  2083  			Param: lit("foo"),
  2084  			Index: litWord("bar"),
  2085  		},
  2086  	},
  2087  	{
  2088  		Strs: []string{`${foo[$bar]}`},
  2089  		bsmk: &ParamExp{
  2090  			Param: lit("foo"),
  2091  			Index: word(litParamExp("bar")),
  2092  		},
  2093  	},
  2094  	{
  2095  		Strs: []string{`${foo[${bar}]}`},
  2096  		bsmk: &ParamExp{
  2097  			Param: lit("foo"),
  2098  			Index: word(&ParamExp{Param: lit("bar")}),
  2099  		},
  2100  	},
  2101  	{
  2102  		Strs: []string{`${foo:1}`, `${foo: 1 }`},
  2103  		bsmk: &ParamExp{
  2104  			Param: lit("foo"),
  2105  			Slice: &Slice{Offset: litWord("1")},
  2106  		},
  2107  	},
  2108  	{
  2109  		Strs: []string{`${foo:1:2}`, `${foo: 1 : 2 }`},
  2110  		bsmk: &ParamExp{
  2111  			Param: lit("foo"),
  2112  			Slice: &Slice{
  2113  				Offset: litWord("1"),
  2114  				Length: litWord("2"),
  2115  			},
  2116  		},
  2117  	},
  2118  	{
  2119  		Strs: []string{`${foo:a:b}`},
  2120  		bsmk: &ParamExp{
  2121  			Param: lit("foo"),
  2122  			Slice: &Slice{
  2123  				Offset: litWord("a"),
  2124  				Length: litWord("b"),
  2125  			},
  2126  		},
  2127  	},
  2128  	{
  2129  		Strs: []string{`${foo:1:-2}`},
  2130  		bsmk: &ParamExp{
  2131  			Param: lit("foo"),
  2132  			Slice: &Slice{
  2133  				Offset: litWord("1"),
  2134  				Length: &UnaryArithm{Op: Minus, X: litWord("2")},
  2135  			},
  2136  		},
  2137  	},
  2138  	{
  2139  		Strs: []string{`${foo::+3}`},
  2140  		bsmk: &ParamExp{
  2141  			Param: lit("foo"),
  2142  			Slice: &Slice{
  2143  				Length: &UnaryArithm{Op: Plus, X: litWord("3")},
  2144  			},
  2145  		},
  2146  	},
  2147  	{
  2148  		Strs: []string{`${foo: -1}`},
  2149  		bsmk: &ParamExp{
  2150  			Param: lit("foo"),
  2151  			Slice: &Slice{
  2152  				Offset: &UnaryArithm{Op: Minus, X: litWord("1")},
  2153  			},
  2154  		},
  2155  	},
  2156  	{
  2157  		Strs: []string{`${foo: +2+3}`},
  2158  		bsmk: &ParamExp{
  2159  			Param: lit("foo"),
  2160  			Slice: &Slice{
  2161  				Offset: &BinaryArithm{
  2162  					Op: Add,
  2163  					X:  &UnaryArithm{Op: Plus, X: litWord("2")},
  2164  					Y:  litWord("3"),
  2165  				},
  2166  			},
  2167  		},
  2168  	},
  2169  	{
  2170  		Strs: []string{`${foo:a?1:2:3}`},
  2171  		bsmk: &ParamExp{
  2172  			Param: lit("foo"),
  2173  			Slice: &Slice{
  2174  				Offset: &BinaryArithm{
  2175  					Op: Quest,
  2176  					X:  litWord("a"),
  2177  					Y: &BinaryArithm{
  2178  						Op: Colon,
  2179  						X:  litWord("1"),
  2180  						Y:  litWord("2"),
  2181  					},
  2182  				},
  2183  				Length: litWord("3"),
  2184  			},
  2185  		},
  2186  	},
  2187  	{
  2188  		Strs: []string{`${foo/a/b}`},
  2189  		bsmk: &ParamExp{
  2190  			Param: lit("foo"),
  2191  			Repl:  &Replace{Orig: litWord("a"), With: litWord("b")},
  2192  		},
  2193  	},
  2194  	{
  2195  		Strs: []string{"${foo/ /\t}"},
  2196  		bsmk: &ParamExp{
  2197  			Param: lit("foo"),
  2198  			Repl:  &Replace{Orig: litWord(" "), With: litWord("\t")},
  2199  		},
  2200  	},
  2201  	{
  2202  		Strs: []string{`${foo/[/]-}`},
  2203  		bsmk: &ParamExp{
  2204  			Param: lit("foo"),
  2205  			Repl:  &Replace{Orig: litWord("["), With: litWord("]-")},
  2206  		},
  2207  	},
  2208  	{
  2209  		Strs: []string{`${foo/bar/b/a/r}`},
  2210  		bsmk: &ParamExp{
  2211  			Param: lit("foo"),
  2212  			Repl: &Replace{
  2213  				Orig: litWord("bar"),
  2214  				With: litWord("b/a/r"),
  2215  			},
  2216  		},
  2217  	},
  2218  	{
  2219  		Strs: []string{`${foo/$a/$'\''}`},
  2220  		bsmk: &ParamExp{
  2221  			Param: lit("foo"),
  2222  			Repl: &Replace{
  2223  				Orig: word(litParamExp("a")),
  2224  				With: word(sglDQuoted(`\'`)),
  2225  			},
  2226  		},
  2227  	},
  2228  	{
  2229  		Strs: []string{`${foo//b1/b2}`},
  2230  		bsmk: &ParamExp{
  2231  			Param: lit("foo"),
  2232  			Repl: &Replace{
  2233  				All:  true,
  2234  				Orig: litWord("b1"),
  2235  				With: litWord("b2"),
  2236  			},
  2237  		},
  2238  	},
  2239  	{
  2240  		Strs: []string{`${foo///}`, `${foo//}`},
  2241  		bsmk: &ParamExp{
  2242  			Param: lit("foo"),
  2243  			Repl:  &Replace{All: true},
  2244  		},
  2245  	},
  2246  	{
  2247  		Strs: []string{`${foo/-//}`},
  2248  		bsmk: &ParamExp{
  2249  			Param: lit("foo"),
  2250  			Repl:  &Replace{Orig: litWord("-"), With: litWord("/")},
  2251  		},
  2252  	},
  2253  	{
  2254  		Strs: []string{`${foo//#/}`, `${foo//#}`},
  2255  		bsmk: &ParamExp{
  2256  			Param: lit("foo"),
  2257  			Repl:  &Replace{All: true, Orig: litWord("#")},
  2258  		},
  2259  	},
  2260  	{
  2261  		Strs: []string{`${foo//[42]/}`},
  2262  		bsmk: &ParamExp{
  2263  			Param: lit("foo"),
  2264  			Repl:  &Replace{All: true, Orig: litWord("[42]")},
  2265  		},
  2266  	},
  2267  	{
  2268  		Strs: []string{`${a^b} ${a^^b} ${a,b} ${a,,b}`},
  2269  		bash: call(
  2270  			word(&ParamExp{Param: lit("a"),
  2271  				Exp: &Expansion{
  2272  					Op:   UpperFirst,
  2273  					Word: litWord("b"),
  2274  				},
  2275  			}),
  2276  			word(&ParamExp{Param: lit("a"),
  2277  				Exp: &Expansion{
  2278  					Op:   UpperAll,
  2279  					Word: litWord("b"),
  2280  				},
  2281  			}),
  2282  			word(&ParamExp{Param: lit("a"),
  2283  				Exp: &Expansion{
  2284  					Op:   LowerFirst,
  2285  					Word: litWord("b"),
  2286  				},
  2287  			}),
  2288  			word(&ParamExp{Param: lit("a"),
  2289  				Exp: &Expansion{
  2290  					Op:   LowerAll,
  2291  					Word: litWord("b"),
  2292  				},
  2293  			}),
  2294  		),
  2295  	},
  2296  	{
  2297  		Strs: []string{`${a@E} ${b@a} ${@@Q}`},
  2298  		bsmk: call(
  2299  			word(&ParamExp{Param: lit("a"),
  2300  				Exp: &Expansion{
  2301  					Op:   OtherParamOps,
  2302  					Word: litWord("E"),
  2303  				},
  2304  			}),
  2305  			word(&ParamExp{Param: lit("b"),
  2306  				Exp: &Expansion{
  2307  					Op:   OtherParamOps,
  2308  					Word: litWord("a"),
  2309  				},
  2310  			}),
  2311  			word(&ParamExp{Param: lit("@"),
  2312  				Exp: &Expansion{
  2313  					Op:   OtherParamOps,
  2314  					Word: litWord("Q"),
  2315  				},
  2316  			}),
  2317  		),
  2318  	},
  2319  	{
  2320  		Strs: []string{`${#foo}`},
  2321  		common: &ParamExp{
  2322  			Length: true,
  2323  			Param:  lit("foo"),
  2324  		},
  2325  	},
  2326  	{
  2327  		Strs: []string{`${%foo}`},
  2328  		mksh: &ParamExp{
  2329  			Width: true,
  2330  			Param: lit("foo"),
  2331  		},
  2332  	},
  2333  	{
  2334  		Strs: []string{`${!foo} ${!bar[@]}`},
  2335  		bsmk: call(
  2336  			word(&ParamExp{
  2337  				Excl:  true,
  2338  				Param: lit("foo"),
  2339  			}),
  2340  			word(&ParamExp{
  2341  				Excl:  true,
  2342  				Param: lit("bar"),
  2343  				Index: litWord("@"),
  2344  			}),
  2345  		),
  2346  	},
  2347  	{
  2348  		Strs: []string{`${!foo*} ${!bar@}`},
  2349  		bsmk: call(
  2350  			word(&ParamExp{
  2351  				Excl:  true,
  2352  				Param: lit("foo"),
  2353  				Names: NamesPrefix,
  2354  			}),
  2355  			word(&ParamExp{
  2356  				Excl:  true,
  2357  				Param: lit("bar"),
  2358  				Names: NamesPrefixWords,
  2359  			}),
  2360  		),
  2361  	},
  2362  	{
  2363  		Strs: []string{`${#?}`},
  2364  		common: call(
  2365  			word(&ParamExp{Length: true, Param: lit("?")}),
  2366  		),
  2367  	},
  2368  	{
  2369  		Strs:   []string{`"${foo}"`},
  2370  		common: dblQuoted(&ParamExp{Param: lit("foo")}),
  2371  	},
  2372  	{
  2373  		Strs:   []string{`"(foo)"`},
  2374  		common: dblQuoted(lit("(foo)")),
  2375  	},
  2376  	{
  2377  		Strs: []string{`"${foo}>"`},
  2378  		common: dblQuoted(
  2379  			&ParamExp{Param: lit("foo")},
  2380  			lit(">"),
  2381  		),
  2382  	},
  2383  	{
  2384  		Strs:   []string{`"$(foo)"`, "\"`foo`\""},
  2385  		common: dblQuoted(cmdSubst(litStmt("foo"))),
  2386  	},
  2387  	{
  2388  		Strs: []string{
  2389  			`"$(foo bar)"`,
  2390  			`"$(foo  bar)"`,
  2391  			"\"`foo bar`\"",
  2392  			"\"`foo  bar`\"",
  2393  		},
  2394  		common: dblQuoted(cmdSubst(litStmt("foo", "bar"))),
  2395  	},
  2396  	{
  2397  		Strs:   []string{`'${foo}'`},
  2398  		common: sglQuoted("${foo}"),
  2399  	},
  2400  	{
  2401  		Strs:   []string{"$((1))"},
  2402  		common: arithmExp(litWord("1")),
  2403  	},
  2404  	{
  2405  		Strs: []string{"$((1 + 3))", "$((1+3))"},
  2406  		common: arithmExp(&BinaryArithm{
  2407  			Op: Add,
  2408  			X:  litWord("1"),
  2409  			Y:  litWord("3"),
  2410  		}),
  2411  	},
  2412  	{
  2413  		Strs: []string{`"$((foo))"`},
  2414  		common: dblQuoted(arithmExp(
  2415  			litWord("foo"),
  2416  		)),
  2417  	},
  2418  	{
  2419  		Strs: []string{`$((a)) b`},
  2420  		common: call(
  2421  			word(arithmExp(litWord("a"))),
  2422  			litWord("b"),
  2423  		),
  2424  	},
  2425  	{
  2426  		Strs: []string{`$((arr[0]++))`},
  2427  		bsmk: arithmExp(&UnaryArithm{
  2428  			Op: Inc, Post: true,
  2429  			X: word(&ParamExp{
  2430  				Short: true,
  2431  				Param: lit("arr"),
  2432  				Index: litWord("0"),
  2433  			}),
  2434  		}),
  2435  	},
  2436  	{
  2437  		Strs: []string{`$((++arr[0]))`},
  2438  		bsmk: arithmExp(&UnaryArithm{
  2439  			Op: Inc,
  2440  			X: word(&ParamExp{
  2441  				Short: true,
  2442  				Param: lit("arr"),
  2443  				Index: litWord("0"),
  2444  			}),
  2445  		}),
  2446  	},
  2447  	{
  2448  		Strs: []string{`$((${a:-1}))`},
  2449  		bsmk: arithmExp(word(&ParamExp{
  2450  			Param: lit("a"),
  2451  			Exp: &Expansion{
  2452  				Op:   SubstColMinus,
  2453  				Word: litWord("1"),
  2454  			},
  2455  		})),
  2456  	},
  2457  	{
  2458  		Strs: []string{"$((5 * 2 - 1))", "$((5*2-1))"},
  2459  		common: arithmExp(&BinaryArithm{
  2460  			Op: Sub,
  2461  			X: &BinaryArithm{
  2462  				Op: Mul,
  2463  				X:  litWord("5"),
  2464  				Y:  litWord("2"),
  2465  			},
  2466  			Y: litWord("1"),
  2467  		}),
  2468  	},
  2469  	{
  2470  		Strs: []string{"$((i | 13))"},
  2471  		common: arithmExp(&BinaryArithm{
  2472  			Op: Or,
  2473  			X:  litWord("i"),
  2474  			Y:  litWord("13"),
  2475  		}),
  2476  	},
  2477  	{
  2478  		Strs: []string{
  2479  			"$(((a) + ((b))))",
  2480  			"$((\n(a) + \n(\n(b)\n)\n))",
  2481  		},
  2482  		common: arithmExp(&BinaryArithm{
  2483  			Op: Add,
  2484  			X:  parenArit(litWord("a")),
  2485  			Y:  parenArit(parenArit(litWord("b"))),
  2486  		}),
  2487  	},
  2488  	{
  2489  		Strs: []string{
  2490  			"$((3 % 7))",
  2491  			"$((3\n% 7))",
  2492  			"$((3\\\n % 7))",
  2493  		},
  2494  		common: arithmExp(&BinaryArithm{
  2495  			Op: Rem,
  2496  			X:  litWord("3"),
  2497  			Y:  litWord("7"),
  2498  		}),
  2499  	},
  2500  	{
  2501  		Strs: []string{`"$((1 / 3))"`},
  2502  		common: dblQuoted(arithmExp(&BinaryArithm{
  2503  			Op: Quo,
  2504  			X:  litWord("1"),
  2505  			Y:  litWord("3"),
  2506  		})),
  2507  	},
  2508  	{
  2509  		Strs: []string{"$((2 ** 10))"},
  2510  		common: arithmExp(&BinaryArithm{
  2511  			Op: Pow,
  2512  			X:  litWord("2"),
  2513  			Y:  litWord("10"),
  2514  		}),
  2515  	},
  2516  	{
  2517  		Strs: []string{`$(((1) ^ 3))`},
  2518  		common: arithmExp(&BinaryArithm{
  2519  			Op: Xor,
  2520  			X:  parenArit(litWord("1")),
  2521  			Y:  litWord("3"),
  2522  		}),
  2523  	},
  2524  	{
  2525  		Strs: []string{`$((1 >> (3 << 2)))`},
  2526  		common: arithmExp(&BinaryArithm{
  2527  			Op: Shr,
  2528  			X:  litWord("1"),
  2529  			Y: parenArit(&BinaryArithm{
  2530  				Op: Shl,
  2531  				X:  litWord("3"),
  2532  				Y:  litWord("2"),
  2533  			}),
  2534  		}),
  2535  	},
  2536  	{
  2537  		Strs: []string{`$((-(1)))`},
  2538  		common: arithmExp(&UnaryArithm{
  2539  			Op: Minus,
  2540  			X:  parenArit(litWord("1")),
  2541  		}),
  2542  	},
  2543  	{
  2544  		Strs: []string{`$((i++))`},
  2545  		common: arithmExp(&UnaryArithm{
  2546  			Op:   Inc,
  2547  			Post: true,
  2548  			X:    litWord("i"),
  2549  		}),
  2550  	},
  2551  	{
  2552  		Strs:   []string{`$((--i))`},
  2553  		common: arithmExp(&UnaryArithm{Op: Dec, X: litWord("i")}),
  2554  	},
  2555  	{
  2556  		Strs:   []string{`$((!i))`},
  2557  		common: arithmExp(&UnaryArithm{Op: Not, X: litWord("i")}),
  2558  	},
  2559  	{
  2560  		Strs: []string{`$((-!+i))`},
  2561  		common: arithmExp(&UnaryArithm{
  2562  			Op: Minus,
  2563  			X: &UnaryArithm{
  2564  				Op: Not,
  2565  				X:  &UnaryArithm{Op: Plus, X: litWord("i")},
  2566  			},
  2567  		}),
  2568  	},
  2569  	{
  2570  		Strs: []string{`$((!!i))`},
  2571  		common: arithmExp(&UnaryArithm{
  2572  			Op: Not,
  2573  			X:  &UnaryArithm{Op: Not, X: litWord("i")},
  2574  		}),
  2575  	},
  2576  	{
  2577  		Strs: []string{`$((1 < 3))`},
  2578  		common: arithmExp(&BinaryArithm{
  2579  			Op: Lss,
  2580  			X:  litWord("1"),
  2581  			Y:  litWord("3"),
  2582  		}),
  2583  	},
  2584  	{
  2585  		Strs: []string{`$((i = 2))`, `$((i=2))`},
  2586  		common: arithmExp(&BinaryArithm{
  2587  			Op: Assgn,
  2588  			X:  litWord("i"),
  2589  			Y:  litWord("2"),
  2590  		}),
  2591  	},
  2592  	{
  2593  		Strs: []string{`((a[i] = 4))`, `((a[i]=4))`},
  2594  		bsmk: arithmCmd(&BinaryArithm{
  2595  			Op: Assgn,
  2596  			X: word(&ParamExp{
  2597  				Short: true,
  2598  				Param: lit("a"),
  2599  				Index: litWord("i"),
  2600  			}),
  2601  			Y: litWord("4"),
  2602  		}),
  2603  	},
  2604  	{
  2605  		Strs: []string{"$((a += 2, b -= 3))"},
  2606  		common: arithmExp(&BinaryArithm{
  2607  			Op: Comma,
  2608  			X: &BinaryArithm{
  2609  				Op: AddAssgn,
  2610  				X:  litWord("a"),
  2611  				Y:  litWord("2"),
  2612  			},
  2613  			Y: &BinaryArithm{
  2614  				Op: SubAssgn,
  2615  				X:  litWord("b"),
  2616  				Y:  litWord("3"),
  2617  			},
  2618  		}),
  2619  	},
  2620  	{
  2621  		Strs: []string{"$((a >>= 2, b <<= 3))"},
  2622  		common: arithmExp(&BinaryArithm{
  2623  			Op: Comma,
  2624  			X: &BinaryArithm{
  2625  				Op: ShrAssgn,
  2626  				X:  litWord("a"),
  2627  				Y:  litWord("2"),
  2628  			},
  2629  			Y: &BinaryArithm{
  2630  				Op: ShlAssgn,
  2631  				X:  litWord("b"),
  2632  				Y:  litWord("3"),
  2633  			},
  2634  		}),
  2635  	},
  2636  	{
  2637  		Strs: []string{"$((a == b && c > d))"},
  2638  		common: arithmExp(&BinaryArithm{
  2639  			Op: AndArit,
  2640  			X: &BinaryArithm{
  2641  				Op: Eql,
  2642  				X:  litWord("a"),
  2643  				Y:  litWord("b"),
  2644  			},
  2645  			Y: &BinaryArithm{
  2646  				Op: Gtr,
  2647  				X:  litWord("c"),
  2648  				Y:  litWord("d"),
  2649  			},
  2650  		}),
  2651  	},
  2652  	{
  2653  		Strs: []string{"$((a != b))"},
  2654  		common: arithmExp(&BinaryArithm{
  2655  			Op: Neq,
  2656  			X:  litWord("a"),
  2657  			Y:  litWord("b"),
  2658  		}),
  2659  	},
  2660  	{
  2661  		Strs: []string{"$((a &= b))"},
  2662  		common: arithmExp(&BinaryArithm{
  2663  			Op: AndAssgn,
  2664  			X:  litWord("a"),
  2665  			Y:  litWord("b"),
  2666  		}),
  2667  	},
  2668  	{
  2669  		Strs: []string{"$((a |= b))"},
  2670  		common: arithmExp(&BinaryArithm{
  2671  			Op: OrAssgn,
  2672  			X:  litWord("a"),
  2673  			Y:  litWord("b"),
  2674  		}),
  2675  	},
  2676  	{
  2677  		Strs: []string{"$((a %= b))"},
  2678  		common: arithmExp(&BinaryArithm{
  2679  			Op: RemAssgn,
  2680  			X:  litWord("a"),
  2681  			Y:  litWord("b"),
  2682  		}),
  2683  	},
  2684  	{
  2685  		Strs: []string{"$((a /= b))", "$((a/=b))"},
  2686  		common: arithmExp(&BinaryArithm{
  2687  			Op: QuoAssgn,
  2688  			X:  litWord("a"),
  2689  			Y:  litWord("b"),
  2690  		}),
  2691  	},
  2692  	{
  2693  		Strs: []string{"$((a ^= b))"},
  2694  		common: arithmExp(&BinaryArithm{
  2695  			Op: XorAssgn,
  2696  			X:  litWord("a"),
  2697  			Y:  litWord("b"),
  2698  		}),
  2699  	},
  2700  	{
  2701  		Strs: []string{"$((i *= 3))"},
  2702  		common: arithmExp(&BinaryArithm{
  2703  			Op: MulAssgn,
  2704  			X:  litWord("i"),
  2705  			Y:  litWord("3"),
  2706  		}),
  2707  	},
  2708  	{
  2709  		Strs: []string{"$((2 >= 10))"},
  2710  		common: arithmExp(&BinaryArithm{
  2711  			Op: Geq,
  2712  			X:  litWord("2"),
  2713  			Y:  litWord("10"),
  2714  		}),
  2715  	},
  2716  	{
  2717  		Strs: []string{"$((foo ? b1 : b2))"},
  2718  		common: arithmExp(&BinaryArithm{
  2719  			Op: Quest,
  2720  			X:  litWord("foo"),
  2721  			Y: &BinaryArithm{
  2722  				Op: Colon,
  2723  				X:  litWord("b1"),
  2724  				Y:  litWord("b2"),
  2725  			},
  2726  		}),
  2727  	},
  2728  	{
  2729  		Strs: []string{`$((a <= (1 || 2)))`},
  2730  		common: arithmExp(&BinaryArithm{
  2731  			Op: Leq,
  2732  			X:  litWord("a"),
  2733  			Y: parenArit(&BinaryArithm{
  2734  				Op: OrArit,
  2735  				X:  litWord("1"),
  2736  				Y:  litWord("2"),
  2737  			}),
  2738  		}),
  2739  	},
  2740  	{
  2741  		Strs:   []string{"foo$", "foo$\n"},
  2742  		common: word(lit("foo"), lit("$")),
  2743  	},
  2744  	{
  2745  		Strs:   []string{"foo$", "foo$\\\n"},
  2746  		common: word(lit("foo"), lit("$")),
  2747  	},
  2748  	{
  2749  		Strs:  []string{`$''`},
  2750  		bsmk:  sglDQuoted(""),
  2751  		posix: word(lit("$"), sglQuoted("")),
  2752  	},
  2753  	{
  2754  		Strs:  []string{`$""`},
  2755  		bsmk:  dblDQuoted(),
  2756  		posix: word(lit("$"), dblQuoted()),
  2757  	},
  2758  	{
  2759  		Strs:  []string{`$'foo'`},
  2760  		bsmk:  sglDQuoted("foo"),
  2761  		posix: word(lit("$"), sglQuoted("foo")),
  2762  	},
  2763  	{
  2764  		Strs: []string{`$'f+oo${'`},
  2765  		bsmk: sglDQuoted("f+oo${"),
  2766  	},
  2767  	{
  2768  		Strs: []string{"$'foo bar`'"},
  2769  		bsmk: sglDQuoted("foo bar`"),
  2770  	},
  2771  	{
  2772  		Strs: []string{"$'a ${b} c'"},
  2773  		bsmk: sglDQuoted("a ${b} c"),
  2774  	},
  2775  	{
  2776  		Strs: []string{`$"a ${b} c"`},
  2777  		bsmk: dblDQuoted(
  2778  			lit("a "),
  2779  			&ParamExp{Param: lit("b")},
  2780  			lit(" c"),
  2781  		),
  2782  	},
  2783  	{
  2784  		Strs:   []string{`"a $b c"`},
  2785  		common: dblQuoted(lit("a "), litParamExp("b"), lit(" c")),
  2786  	},
  2787  	{
  2788  		Strs: []string{`$"a $b c"`},
  2789  		bsmk: dblDQuoted(
  2790  			lit("a "),
  2791  			litParamExp("b"),
  2792  			lit(" c"),
  2793  		),
  2794  	},
  2795  	{
  2796  		Strs: []string{"$'f\\'oo\n'"},
  2797  		bsmk: sglDQuoted("f\\'oo\n"),
  2798  	},
  2799  	{
  2800  		Strs:  []string{`$"foo"`},
  2801  		bsmk:  dblDQuoted(lit("foo")),
  2802  		posix: word(lit("$"), dblQuoted(lit("foo"))),
  2803  	},
  2804  	{
  2805  		Strs: []string{`$"foo$"`},
  2806  		bsmk: dblDQuoted(lit("foo"), lit("$")),
  2807  	},
  2808  	{
  2809  		Strs: []string{`$"foo bar"`},
  2810  		bsmk: dblDQuoted(lit("foo bar")),
  2811  	},
  2812  	{
  2813  		Strs: []string{`$'f\'oo'`},
  2814  		bsmk: sglDQuoted(`f\'oo`),
  2815  	},
  2816  	{
  2817  		Strs: []string{`$"f\"oo"`},
  2818  		bsmk: dblDQuoted(lit(`f\"oo`)),
  2819  	},
  2820  	{
  2821  		Strs:   []string{`"foo$"`},
  2822  		common: dblQuoted(lit("foo"), lit("$")),
  2823  	},
  2824  	{
  2825  		Strs:   []string{`"foo$$"`},
  2826  		common: dblQuoted(lit("foo"), litParamExp("$")),
  2827  	},
  2828  	{
  2829  		Strs:   []string{`"a $\"b\" c"`},
  2830  		common: dblQuoted(lit(`a `), lit(`$`), lit(`\"b\" c`)),
  2831  	},
  2832  	{
  2833  		Strs: []string{"$(foo$)", "`foo$`"},
  2834  		common: cmdSubst(
  2835  			stmt(call(word(lit("foo"), lit("$")))),
  2836  		),
  2837  	},
  2838  	{
  2839  		Strs:   []string{"foo$bar"},
  2840  		common: word(lit("foo"), litParamExp("bar")),
  2841  	},
  2842  	{
  2843  		Strs:   []string{"foo$(bar)"},
  2844  		common: word(lit("foo"), cmdSubst(litStmt("bar"))),
  2845  	},
  2846  	{
  2847  		Strs:   []string{"foo${bar}"},
  2848  		common: word(lit("foo"), &ParamExp{Param: lit("bar")}),
  2849  	},
  2850  	{
  2851  		Strs:   []string{"'foo${bar'"},
  2852  		common: sglQuoted("foo${bar"),
  2853  	},
  2854  	{
  2855  		Strs: []string{"(foo)\nbar", "(foo); bar"},
  2856  		common: []Command{
  2857  			subshell(litStmt("foo")),
  2858  			litCall("bar"),
  2859  		},
  2860  	},
  2861  	{
  2862  		Strs: []string{"foo\n(bar)", "foo; (bar)"},
  2863  		common: []Command{
  2864  			litCall("foo"),
  2865  			subshell(litStmt("bar")),
  2866  		},
  2867  	},
  2868  	{
  2869  		Strs: []string{"foo\n(bar)", "foo; (bar)"},
  2870  		common: []Command{
  2871  			litCall("foo"),
  2872  			subshell(litStmt("bar")),
  2873  		},
  2874  	},
  2875  	{
  2876  		Strs: []string{
  2877  			"case $i in 1) foo ;; 2 | 3*) bar ;; esac",
  2878  			"case $i in 1) foo;; 2 | 3*) bar; esac",
  2879  			"case $i in (1) foo;; 2 | 3*) bar;; esac",
  2880  			"case $i\nin\n#etc\n1)\nfoo\n;;\n2 | 3*)\nbar\n;;\nesac",
  2881  		},
  2882  		common: &CaseClause{
  2883  			Word: word(litParamExp("i")),
  2884  			Items: []*CaseItem{
  2885  				{
  2886  					Op:       Break,
  2887  					Patterns: litWords("1"),
  2888  					StmtList: litStmts("foo"),
  2889  				},
  2890  				{
  2891  					Op:       Break,
  2892  					Patterns: litWords("2", "3*"),
  2893  					StmtList: litStmts("bar"),
  2894  				},
  2895  			},
  2896  		},
  2897  	},
  2898  	{
  2899  		Strs: []string{"case i in 1) a ;& 2) ;; esac"},
  2900  		bsmk: &CaseClause{
  2901  			Word: litWord("i"),
  2902  			Items: []*CaseItem{
  2903  				{
  2904  					Op:       Fallthrough,
  2905  					Patterns: litWords("1"),
  2906  					StmtList: litStmts("a"),
  2907  				},
  2908  				{Op: Break, Patterns: litWords("2")},
  2909  			},
  2910  		},
  2911  	},
  2912  	{
  2913  		Strs: []string{
  2914  			"case i in 1) a ;; esac",
  2915  			"case i { 1) a ;; }",
  2916  			"case i {\n1) a ;;\n}",
  2917  		},
  2918  		mksh: &CaseClause{
  2919  			Word: litWord("i"),
  2920  			Items: []*CaseItem{{
  2921  				Op:       Break,
  2922  				Patterns: litWords("1"),
  2923  				StmtList: litStmts("a"),
  2924  			}},
  2925  		},
  2926  	},
  2927  	{
  2928  		Strs: []string{"case i in 1) a ;;& 2) b ;; esac"},
  2929  		bash: &CaseClause{
  2930  			Word: litWord("i"),
  2931  			Items: []*CaseItem{
  2932  				{
  2933  					Op:       Resume,
  2934  					Patterns: litWords("1"),
  2935  					StmtList: litStmts("a"),
  2936  				},
  2937  				{
  2938  					Op:       Break,
  2939  					Patterns: litWords("2"),
  2940  					StmtList: litStmts("b"),
  2941  				},
  2942  			},
  2943  		},
  2944  	},
  2945  	{
  2946  		Strs: []string{"case i in 1) a ;| 2) b ;; esac"},
  2947  		mksh: &CaseClause{
  2948  			Word: litWord("i"),
  2949  			Items: []*CaseItem{
  2950  				{
  2951  					Op:       ResumeKorn,
  2952  					Patterns: litWords("1"),
  2953  					StmtList: litStmts("a"),
  2954  				},
  2955  				{
  2956  					Op:       Break,
  2957  					Patterns: litWords("2"),
  2958  					StmtList: litStmts("b"),
  2959  				},
  2960  			},
  2961  		},
  2962  	},
  2963  	{
  2964  		Strs: []string{"case $i in 1) cat <<EOF ;;\nfoo\nEOF\nesac"},
  2965  		common: &CaseClause{
  2966  			Word: word(litParamExp("i")),
  2967  			Items: []*CaseItem{{
  2968  				Op:       Break,
  2969  				Patterns: litWords("1"),
  2970  				StmtList: stmtList(&Stmt{
  2971  					Cmd: litCall("cat"),
  2972  					Redirs: []*Redirect{{
  2973  						Op:   Hdoc,
  2974  						Word: litWord("EOF"),
  2975  						Hdoc: litWord("foo\n"),
  2976  					}},
  2977  				}),
  2978  			}},
  2979  		},
  2980  	},
  2981  	{
  2982  		Strs: []string{"foo | while read a; do b; done"},
  2983  		common: &BinaryCmd{
  2984  			Op: Pipe,
  2985  			X:  litStmt("foo"),
  2986  			Y: stmt(&WhileClause{
  2987  				Cond: stmtList(
  2988  					litStmt("read", "a"),
  2989  				),
  2990  				Do: litStmts("b"),
  2991  			}),
  2992  		},
  2993  	},
  2994  	{
  2995  		Strs: []string{"while read l; do foo || bar; done"},
  2996  		common: &WhileClause{
  2997  			Cond: stmtList(litStmt("read", "l")),
  2998  			Do: stmts(&BinaryCmd{
  2999  				Op: OrStmt,
  3000  				X:  litStmt("foo"),
  3001  				Y:  litStmt("bar"),
  3002  			}),
  3003  		},
  3004  	},
  3005  	{
  3006  		Strs:   []string{"echo if while"},
  3007  		common: litCall("echo", "if", "while"),
  3008  	},
  3009  	{
  3010  		Strs:   []string{"${foo}if"},
  3011  		common: word(&ParamExp{Param: lit("foo")}, lit("if")),
  3012  	},
  3013  	{
  3014  		Strs:   []string{"$if'|'"},
  3015  		common: word(litParamExp("if"), sglQuoted("|")),
  3016  	},
  3017  	{
  3018  		Strs: []string{"if a; then b=; fi", "if a; then b=\nfi"},
  3019  		common: &IfClause{
  3020  			Cond: litStmts("a"),
  3021  			Then: stmtList(stmt(&CallExpr{
  3022  				Assigns: []*Assign{
  3023  					{Name: lit("b")},
  3024  				},
  3025  			})),
  3026  		},
  3027  	},
  3028  	{
  3029  		Strs: []string{"if a; then >f; fi", "if a; then >f\nfi"},
  3030  		common: &IfClause{
  3031  			Cond: litStmts("a"),
  3032  			Then: stmtList(&Stmt{
  3033  				Redirs: []*Redirect{
  3034  					{Op: RdrOut, Word: litWord("f")},
  3035  				},
  3036  			}),
  3037  		},
  3038  	},
  3039  	{
  3040  		Strs: []string{"if a; then (a); fi", "if a; then (a) fi"},
  3041  		common: &IfClause{
  3042  			Cond: litStmts("a"),
  3043  			Then: stmts(subshell(litStmt("a"))),
  3044  		},
  3045  	},
  3046  	{
  3047  		Strs: []string{"a=b\nc=d", "a=b; c=d"},
  3048  		common: []Command{
  3049  			&CallExpr{Assigns: []*Assign{
  3050  				{Name: lit("a"), Value: litWord("b")},
  3051  			}},
  3052  			&CallExpr{Assigns: []*Assign{
  3053  				{Name: lit("c"), Value: litWord("d")},
  3054  			}},
  3055  		},
  3056  	},
  3057  	{
  3058  		Strs: []string{"foo && write | read"},
  3059  		common: &BinaryCmd{
  3060  			Op: AndStmt,
  3061  			X:  litStmt("foo"),
  3062  			Y: stmt(&BinaryCmd{
  3063  				Op: Pipe,
  3064  				X:  litStmt("write"),
  3065  				Y:  litStmt("read"),
  3066  			}),
  3067  		},
  3068  	},
  3069  	{
  3070  		Strs: []string{"write | read && bar"},
  3071  		common: &BinaryCmd{
  3072  			Op: AndStmt,
  3073  			X: stmt(&BinaryCmd{
  3074  				Op: Pipe,
  3075  				X:  litStmt("write"),
  3076  				Y:  litStmt("read"),
  3077  			}),
  3078  			Y: litStmt("bar"),
  3079  		},
  3080  	},
  3081  	{
  3082  		Strs: []string{"foo >f | bar"},
  3083  		common: &BinaryCmd{
  3084  			Op: Pipe,
  3085  			X: &Stmt{
  3086  				Cmd: litCall("foo"),
  3087  				Redirs: []*Redirect{
  3088  					{Op: RdrOut, Word: litWord("f")},
  3089  				},
  3090  			},
  3091  			Y: litStmt("bar"),
  3092  		},
  3093  	},
  3094  	{
  3095  		Strs: []string{"(foo) >f | bar"},
  3096  		common: &BinaryCmd{
  3097  			Op: Pipe,
  3098  			X: &Stmt{
  3099  				Cmd: subshell(litStmt("foo")),
  3100  				Redirs: []*Redirect{
  3101  					{Op: RdrOut, Word: litWord("f")},
  3102  				},
  3103  			},
  3104  			Y: litStmt("bar"),
  3105  		},
  3106  	},
  3107  	{
  3108  		Strs: []string{"foo | >f"},
  3109  		common: &BinaryCmd{
  3110  			Op: Pipe,
  3111  			X:  litStmt("foo"),
  3112  			Y: &Stmt{Redirs: []*Redirect{
  3113  				{Op: RdrOut, Word: litWord("f")},
  3114  			}},
  3115  		},
  3116  	},
  3117  	{
  3118  		Strs:  []string{"[[ a ]]"},
  3119  		bsmk:  &TestClause{X: litWord("a")},
  3120  		posix: litStmt("[[", "a", "]]"),
  3121  	},
  3122  	{
  3123  		Strs: []string{"[[ a ]]\nb"},
  3124  		bsmk: stmts(
  3125  			&TestClause{X: litWord("a")},
  3126  			litCall("b"),
  3127  		),
  3128  	},
  3129  	{
  3130  		Strs: []string{"[[ a > b ]]"},
  3131  		bsmk: &TestClause{X: &BinaryTest{
  3132  			Op: TsAfter,
  3133  			X:  litWord("a"),
  3134  			Y:  litWord("b"),
  3135  		}},
  3136  	},
  3137  	{
  3138  		Strs: []string{"[[ 1 -nt 2 ]]"},
  3139  		bsmk: &TestClause{X: &BinaryTest{
  3140  			Op: TsNewer,
  3141  			X:  litWord("1"),
  3142  			Y:  litWord("2"),
  3143  		}},
  3144  	},
  3145  	{
  3146  		Strs: []string{"[[ 1 -eq 2 ]]"},
  3147  		bsmk: &TestClause{X: &BinaryTest{
  3148  			Op: TsEql,
  3149  			X:  litWord("1"),
  3150  			Y:  litWord("2"),
  3151  		}},
  3152  	},
  3153  	{
  3154  		Strs: []string{
  3155  			"[[ -R a ]]",
  3156  			"[[\n-R a\n]]",
  3157  		},
  3158  		bash: &TestClause{X: &UnaryTest{
  3159  			Op: TsRefVar,
  3160  			X:  litWord("a"),
  3161  		}},
  3162  	},
  3163  	{
  3164  		Strs: []string{"[[ a =~ b ]]", "[[ a =~ b ]];"},
  3165  		bash: &TestClause{X: &BinaryTest{
  3166  			Op: TsReMatch,
  3167  			X:  litWord("a"),
  3168  			Y:  litWord("b"),
  3169  		}},
  3170  	},
  3171  	{
  3172  		Strs: []string{`[[ a =~ " foo "$bar ]]`},
  3173  		bash: &TestClause{X: &BinaryTest{
  3174  			Op: TsReMatch,
  3175  			X:  litWord("a"),
  3176  			Y: word(
  3177  				dblQuoted(lit(" foo ")),
  3178  				litParamExp("bar"),
  3179  			),
  3180  		}},
  3181  	},
  3182  	{
  3183  		Strs: []string{`[[ a =~ foo"bar" ]]`},
  3184  		bash: &TestClause{X: &BinaryTest{
  3185  			Op: TsReMatch,
  3186  			X:  litWord("a"),
  3187  			Y: word(
  3188  				lit("foo"),
  3189  				dblQuoted(lit("bar")),
  3190  			),
  3191  		}},
  3192  	},
  3193  	{
  3194  		Strs: []string{`[[ a =~ [ab](c |d) ]]`},
  3195  		bash: &TestClause{X: &BinaryTest{
  3196  			Op: TsReMatch,
  3197  			X:  litWord("a"),
  3198  			Y:  litWord("[ab](c |d)"),
  3199  		}},
  3200  	},
  3201  	{
  3202  		Strs: []string{`[[ a =~ ( ]]) ]]`},
  3203  		bash: &TestClause{X: &BinaryTest{
  3204  			Op: TsReMatch,
  3205  			X:  litWord("a"),
  3206  			Y:  litWord("( ]])"),
  3207  		}},
  3208  	},
  3209  	{
  3210  		Strs: []string{`[[ a =~ ($foo) ]]`},
  3211  		bash: &TestClause{X: &BinaryTest{
  3212  			Op: TsReMatch,
  3213  			X:  litWord("a"),
  3214  			Y:  word(lit("("), litParamExp("foo"), lit(")")),
  3215  		}},
  3216  	},
  3217  	{
  3218  		Strs: []string{`[[ a =~ b\ c|d ]]`},
  3219  		bash: &TestClause{X: &BinaryTest{
  3220  			Op: TsReMatch,
  3221  			X:  litWord("a"),
  3222  			Y:  litWord(`b\ c|d`),
  3223  		}},
  3224  	},
  3225  	{
  3226  		Strs: []string{`[[ a == -n ]]`},
  3227  		bsmk: &TestClause{X: &BinaryTest{
  3228  			Op: TsMatch,
  3229  			X:  litWord("a"),
  3230  			Y:  litWord("-n"),
  3231  		}},
  3232  	},
  3233  	{
  3234  		Strs: []string{`[[ a =~ -n ]]`},
  3235  		bash: &TestClause{X: &BinaryTest{
  3236  			Op: TsReMatch,
  3237  			X:  litWord("a"),
  3238  			Y:  litWord("-n"),
  3239  		}},
  3240  	},
  3241  	{
  3242  		Strs: []string{"[[ a =~ b$ || c =~ d$ ]]"},
  3243  		bash: &TestClause{X: &BinaryTest{
  3244  			Op: OrTest,
  3245  			X: &BinaryTest{
  3246  				Op: TsReMatch,
  3247  				X:  litWord("a"),
  3248  				Y:  word(lit("b"), lit("$")),
  3249  			},
  3250  			Y: &BinaryTest{
  3251  				Op: TsReMatch,
  3252  				X:  litWord("c"),
  3253  				Y:  word(lit("d"), lit("$")),
  3254  			},
  3255  		}},
  3256  	},
  3257  	{
  3258  		Strs: []string{"[[ -n $a ]]"},
  3259  		bsmk: &TestClause{
  3260  			X: &UnaryTest{Op: TsNempStr, X: word(litParamExp("a"))},
  3261  		},
  3262  	},
  3263  	{
  3264  		Strs: []string{"[[ ! $a < 'b' ]]"},
  3265  		bsmk: &TestClause{X: &UnaryTest{
  3266  			Op: TsNot,
  3267  			X: &BinaryTest{
  3268  				Op: TsBefore,
  3269  				X:  word(litParamExp("a")),
  3270  				Y:  word(sglQuoted("b")),
  3271  			},
  3272  		}},
  3273  	},
  3274  	{
  3275  		Strs: []string{
  3276  			"[[ ! -e $a ]]",
  3277  			"[[ ! -a $a ]]",
  3278  			"[[\n!\n-a $a\n]]",
  3279  		},
  3280  		bsmk: &TestClause{X: &UnaryTest{
  3281  			Op: TsNot,
  3282  			X:  &UnaryTest{Op: TsExists, X: word(litParamExp("a"))},
  3283  		}},
  3284  	},
  3285  	{
  3286  		Strs: []string{
  3287  			"[[ a && b ]]",
  3288  			"[[\na &&\nb ]]",
  3289  			"[[\n\na &&\n\nb ]]",
  3290  		},
  3291  		bsmk: &TestClause{X: &BinaryTest{
  3292  			Op: AndTest,
  3293  			X:  litWord("a"),
  3294  			Y:  litWord("b"),
  3295  		}},
  3296  	},
  3297  	{
  3298  		Strs: []string{"[[ (a && b) ]]"},
  3299  		bsmk: &TestClause{X: parenTest(&BinaryTest{
  3300  			Op: AndTest,
  3301  			X:  litWord("a"),
  3302  			Y:  litWord("b"),
  3303  		})},
  3304  	},
  3305  	{
  3306  		Strs: []string{
  3307  			"[[ a && (b) ]]",
  3308  			"[[ a &&\n(\nb) ]]",
  3309  		},
  3310  		bsmk: &TestClause{X: &BinaryTest{
  3311  			Op: AndTest,
  3312  			X:  litWord("a"),
  3313  			Y:  parenTest(litWord("b")),
  3314  		}},
  3315  	},
  3316  	{
  3317  		Strs: []string{"[[ (a && b) || -f c ]]"},
  3318  		bsmk: &TestClause{X: &BinaryTest{
  3319  			Op: OrTest,
  3320  			X: parenTest(&BinaryTest{
  3321  				Op: AndTest,
  3322  				X:  litWord("a"),
  3323  				Y:  litWord("b"),
  3324  			}),
  3325  			Y: &UnaryTest{Op: TsRegFile, X: litWord("c")},
  3326  		}},
  3327  	},
  3328  	{
  3329  		Strs: []string{
  3330  			"[[ -S a && -L b ]]",
  3331  			"[[ -S a && -h b ]]",
  3332  		},
  3333  		bsmk: &TestClause{X: &BinaryTest{
  3334  			Op: AndTest,
  3335  			X:  &UnaryTest{Op: TsSocket, X: litWord("a")},
  3336  			Y:  &UnaryTest{Op: TsSmbLink, X: litWord("b")},
  3337  		}},
  3338  	},
  3339  	{
  3340  		Strs: []string{"[[ -k a && -N b ]]"},
  3341  		bash: &TestClause{X: &BinaryTest{
  3342  			Op: AndTest,
  3343  			X:  &UnaryTest{Op: TsSticky, X: litWord("a")},
  3344  			Y:  &UnaryTest{Op: TsModif, X: litWord("b")},
  3345  		}},
  3346  	},
  3347  	{
  3348  		Strs: []string{"[[ -G a && -O b ]]"},
  3349  		bsmk: &TestClause{X: &BinaryTest{
  3350  			Op: AndTest,
  3351  			X:  &UnaryTest{Op: TsGrpOwn, X: litWord("a")},
  3352  			Y:  &UnaryTest{Op: TsUsrOwn, X: litWord("b")},
  3353  		}},
  3354  	},
  3355  	{
  3356  		Strs: []string{"[[ -d a && -c b ]]"},
  3357  		bsmk: &TestClause{X: &BinaryTest{
  3358  			Op: AndTest,
  3359  			X:  &UnaryTest{Op: TsDirect, X: litWord("a")},
  3360  			Y:  &UnaryTest{Op: TsCharSp, X: litWord("b")},
  3361  		}},
  3362  	},
  3363  	{
  3364  		Strs: []string{"[[ -b a && -p b ]]"},
  3365  		bsmk: &TestClause{X: &BinaryTest{
  3366  			Op: AndTest,
  3367  			X:  &UnaryTest{Op: TsBlckSp, X: litWord("a")},
  3368  			Y:  &UnaryTest{Op: TsNmPipe, X: litWord("b")},
  3369  		}},
  3370  	},
  3371  	{
  3372  		Strs: []string{"[[ -g a && -u b ]]"},
  3373  		bsmk: &TestClause{X: &BinaryTest{
  3374  			Op: AndTest,
  3375  			X:  &UnaryTest{Op: TsGIDSet, X: litWord("a")},
  3376  			Y:  &UnaryTest{Op: TsUIDSet, X: litWord("b")},
  3377  		}},
  3378  	},
  3379  	{
  3380  		Strs: []string{"[[ -r a && -w b ]]"},
  3381  		bsmk: &TestClause{X: &BinaryTest{
  3382  			Op: AndTest,
  3383  			X:  &UnaryTest{Op: TsRead, X: litWord("a")},
  3384  			Y:  &UnaryTest{Op: TsWrite, X: litWord("b")},
  3385  		}},
  3386  	},
  3387  	{
  3388  		Strs: []string{"[[ -x a && -s b ]]"},
  3389  		bsmk: &TestClause{X: &BinaryTest{
  3390  			Op: AndTest,
  3391  			X:  &UnaryTest{Op: TsExec, X: litWord("a")},
  3392  			Y:  &UnaryTest{Op: TsNoEmpty, X: litWord("b")},
  3393  		}},
  3394  	},
  3395  	{
  3396  		Strs: []string{"[[ -t a && -z b ]]"},
  3397  		bsmk: &TestClause{X: &BinaryTest{
  3398  			Op: AndTest,
  3399  			X:  &UnaryTest{Op: TsFdTerm, X: litWord("a")},
  3400  			Y:  &UnaryTest{Op: TsEmpStr, X: litWord("b")},
  3401  		}},
  3402  	},
  3403  	{
  3404  		Strs: []string{"[[ -o a && -v b ]]"},
  3405  		bsmk: &TestClause{X: &BinaryTest{
  3406  			Op: AndTest,
  3407  			X:  &UnaryTest{Op: TsOptSet, X: litWord("a")},
  3408  			Y:  &UnaryTest{Op: TsVarSet, X: litWord("b")},
  3409  		}},
  3410  	},
  3411  	{
  3412  		Strs: []string{"[[ a -ot b && c -ef d ]]"},
  3413  		bsmk: &TestClause{X: &BinaryTest{
  3414  			Op: AndTest,
  3415  			X: &BinaryTest{
  3416  				Op: TsOlder,
  3417  				X:  litWord("a"),
  3418  				Y:  litWord("b"),
  3419  			},
  3420  			Y: &BinaryTest{
  3421  				Op: TsDevIno,
  3422  				X:  litWord("c"),
  3423  				Y:  litWord("d"),
  3424  			},
  3425  		}},
  3426  	},
  3427  	{
  3428  		Strs: []string{
  3429  			"[[ a == b && c != d ]]",
  3430  			"[[ a = b && c != d ]]",
  3431  		},
  3432  		bsmk: &TestClause{X: &BinaryTest{
  3433  			Op: AndTest,
  3434  			X: &BinaryTest{
  3435  				Op: TsMatch,
  3436  				X:  litWord("a"),
  3437  				Y:  litWord("b"),
  3438  			},
  3439  			Y: &BinaryTest{
  3440  				Op: TsNoMatch,
  3441  				X:  litWord("c"),
  3442  				Y:  litWord("d"),
  3443  			},
  3444  		}},
  3445  	},
  3446  	{
  3447  		Strs: []string{"[[ a -ne b && c -le d ]]"},
  3448  		bsmk: &TestClause{X: &BinaryTest{
  3449  			Op: AndTest,
  3450  			X: &BinaryTest{
  3451  				Op: TsNeq,
  3452  				X:  litWord("a"),
  3453  				Y:  litWord("b"),
  3454  			},
  3455  			Y: &BinaryTest{
  3456  				Op: TsLeq,
  3457  				X:  litWord("c"),
  3458  				Y:  litWord("d"),
  3459  			},
  3460  		}},
  3461  	},
  3462  	{
  3463  		Strs: []string{"[[ c -ge d ]]"},
  3464  		bsmk: &TestClause{X: &BinaryTest{
  3465  			Op: TsGeq,
  3466  			X:  litWord("c"),
  3467  			Y:  litWord("d"),
  3468  		}},
  3469  	},
  3470  	{
  3471  		Strs: []string{"[[ a -lt b && c -gt d ]]"},
  3472  		bsmk: &TestClause{X: &BinaryTest{
  3473  			Op: AndTest,
  3474  			X: &BinaryTest{
  3475  				Op: TsLss,
  3476  				X:  litWord("a"),
  3477  				Y:  litWord("b"),
  3478  			},
  3479  			Y: &BinaryTest{
  3480  				Op: TsGtr,
  3481  				X:  litWord("c"),
  3482  				Y:  litWord("d"),
  3483  			},
  3484  		}},
  3485  	},
  3486  	{
  3487  		Strs:   []string{"declare -f func"},
  3488  		common: litStmt("declare", "-f", "func"),
  3489  		bash: &DeclClause{
  3490  			Variant: lit("declare"),
  3491  			Opts:    litWords("-f"),
  3492  			Assigns: []*Assign{{
  3493  				Naked: true,
  3494  				Name:  lit("func"),
  3495  			}},
  3496  		},
  3497  	},
  3498  	{
  3499  		Strs: []string{"(local bar)"},
  3500  		bsmk: subshell(stmt(&DeclClause{
  3501  			Variant: lit("local"),
  3502  			Assigns: []*Assign{{
  3503  				Naked: true,
  3504  				Name:  lit("bar"),
  3505  			}},
  3506  		})),
  3507  		posix: subshell(litStmt("local", "bar")),
  3508  	},
  3509  	{
  3510  		Strs:  []string{"typeset"},
  3511  		bsmk:  &DeclClause{Variant: lit("typeset")},
  3512  		posix: litStmt("typeset"),
  3513  	},
  3514  	{
  3515  		Strs: []string{"export bar"},
  3516  		bsmk: &DeclClause{
  3517  			Variant: lit("export"),
  3518  			Assigns: []*Assign{{
  3519  				Naked: true,
  3520  				Name:  lit("bar"),
  3521  			}},
  3522  		},
  3523  		posix: litStmt("export", "bar"),
  3524  	},
  3525  	{
  3526  		Strs:  []string{"readonly -n"},
  3527  		bsmk:  &DeclClause{Variant: lit("readonly"), Opts: litWords("-n")},
  3528  		posix: litStmt("readonly", "-n"),
  3529  	},
  3530  	{
  3531  		Strs: []string{"nameref bar="},
  3532  		bsmk: &DeclClause{
  3533  			Variant: lit("nameref"),
  3534  			Assigns: []*Assign{{
  3535  				Name: lit("bar"),
  3536  			}},
  3537  		},
  3538  		posix: litStmt("nameref", "bar="),
  3539  	},
  3540  	{
  3541  		Strs: []string{"declare -a +n -b$o foo=bar"},
  3542  		bash: &DeclClause{
  3543  			Variant: lit("declare"),
  3544  			Opts: []*Word{
  3545  				litWord("-a"),
  3546  				litWord("+n"),
  3547  				word(lit("-b"), litParamExp("o")),
  3548  			},
  3549  			Assigns: []*Assign{
  3550  				{Name: lit("foo"), Value: litWord("bar")},
  3551  			},
  3552  		},
  3553  	},
  3554  	{
  3555  		Strs: []string{
  3556  			"declare -a foo=(b1 $(b2))",
  3557  			"declare -a foo=(b1 `b2`)",
  3558  		},
  3559  		bash: &DeclClause{
  3560  			Variant: lit("declare"),
  3561  			Opts:    litWords("-a"),
  3562  			Assigns: []*Assign{{
  3563  				Name: lit("foo"),
  3564  				Array: arrValues(
  3565  					litWord("b1"),
  3566  					word(cmdSubst(litStmt("b2"))),
  3567  				),
  3568  			}},
  3569  		},
  3570  	},
  3571  	{
  3572  		Strs: []string{"local -a foo=(b1)"},
  3573  		bash: &DeclClause{
  3574  			Variant: lit("local"),
  3575  			Opts:    litWords("-a"),
  3576  			Assigns: []*Assign{{
  3577  				Name:  lit("foo"),
  3578  				Array: arrValues(litWord("b1")),
  3579  			}},
  3580  		},
  3581  	},
  3582  	{
  3583  		Strs: []string{"declare -A foo=([a]=b)"},
  3584  		bash: &DeclClause{
  3585  			Variant: lit("declare"),
  3586  			Opts:    litWords("-A"),
  3587  			Assigns: []*Assign{{
  3588  				Name: lit("foo"),
  3589  				Array: &ArrayExpr{Elems: []*ArrayElem{{
  3590  					Index: litWord("a"),
  3591  					Value: litWord("b"),
  3592  				}}},
  3593  			}},
  3594  		},
  3595  	},
  3596  	{
  3597  		Strs: []string{"declare foo[a]="},
  3598  		bash: &DeclClause{
  3599  			Variant: lit("declare"),
  3600  			Assigns: []*Assign{{
  3601  				Name:  lit("foo"),
  3602  				Index: litWord("a"),
  3603  			}},
  3604  		},
  3605  	},
  3606  	{
  3607  		Strs: []string{"declare foo[*]"},
  3608  		bash: &DeclClause{
  3609  			Variant: lit("declare"),
  3610  			Assigns: []*Assign{{
  3611  				Name:  lit("foo"),
  3612  				Index: litWord("*"),
  3613  				Naked: true,
  3614  			}},
  3615  		},
  3616  	},
  3617  	{
  3618  		Strs: []string{`declare foo["x y"]`},
  3619  		bash: &DeclClause{
  3620  			Variant: lit("declare"),
  3621  			Assigns: []*Assign{{
  3622  				Name:  lit("foo"),
  3623  				Index: word(dblQuoted(lit("x y"))),
  3624  				Naked: true,
  3625  			}},
  3626  		},
  3627  	},
  3628  	{
  3629  		Strs: []string{`declare foo['x y']`},
  3630  		bash: &DeclClause{
  3631  			Variant: lit("declare"),
  3632  			Assigns: []*Assign{{
  3633  				Name:  lit("foo"),
  3634  				Index: word(sglQuoted("x y")),
  3635  				Naked: true,
  3636  			}},
  3637  		},
  3638  	},
  3639  	{
  3640  		Strs: []string{"foo=([)"},
  3641  		mksh: &CallExpr{Assigns: []*Assign{{
  3642  			Name:  lit("foo"),
  3643  			Array: arrValues(litWord("[")),
  3644  		}}},
  3645  	},
  3646  	{
  3647  		Strs: []string{
  3648  			"a && b=(c)\nd",
  3649  			"a && b=(c); d",
  3650  		},
  3651  		bsmk: stmts(
  3652  			&BinaryCmd{
  3653  				Op: AndStmt,
  3654  				X:  litStmt("a"),
  3655  				Y: stmt(&CallExpr{Assigns: []*Assign{{
  3656  					Name:  lit("b"),
  3657  					Array: arrValues(litWord("c")),
  3658  				}}}),
  3659  			},
  3660  			litCall("d"),
  3661  		),
  3662  	},
  3663  	{
  3664  		Strs: []string{"declare -f $func >/dev/null"},
  3665  		bash: &Stmt{
  3666  			Cmd: &DeclClause{
  3667  				Variant: lit("declare"),
  3668  				Opts:    litWords("-f"),
  3669  				Assigns: []*Assign{{
  3670  					Naked: true,
  3671  					Value: word(litParamExp("func")),
  3672  				}},
  3673  			},
  3674  			Redirs: []*Redirect{
  3675  				{Op: RdrOut, Word: litWord("/dev/null")},
  3676  			},
  3677  		},
  3678  	},
  3679  	{
  3680  		Strs: []string{"declare a\n{ x; }"},
  3681  		bash: stmts(
  3682  			&DeclClause{
  3683  				Variant: lit("declare"),
  3684  				Assigns: []*Assign{{
  3685  					Naked: true,
  3686  					Name:  lit("a"),
  3687  				}},
  3688  			},
  3689  			block(litStmt("x")),
  3690  		),
  3691  	},
  3692  	{
  3693  		Strs:   []string{"eval a=b foo"},
  3694  		common: litStmt("eval", "a=b", "foo"),
  3695  	},
  3696  	{
  3697  		Strs:  []string{"time", "time\n"},
  3698  		posix: litStmt("time"),
  3699  		bsmk:  &TimeClause{},
  3700  	},
  3701  	{
  3702  		Strs:  []string{"time -p"},
  3703  		posix: litStmt("time", "-p"),
  3704  		bsmk:  &TimeClause{PosixFormat: true},
  3705  	},
  3706  	{
  3707  		Strs:  []string{"time -a"},
  3708  		posix: litStmt("time", "-a"),
  3709  		bsmk:  &TimeClause{Stmt: litStmt("-a")},
  3710  	},
  3711  	{
  3712  		Strs:  []string{"time --"},
  3713  		posix: litStmt("time", "--"),
  3714  		bsmk:  &TimeClause{Stmt: litStmt("--")},
  3715  	},
  3716  	{
  3717  		Strs: []string{"time foo"},
  3718  		bsmk: &TimeClause{Stmt: litStmt("foo")},
  3719  	},
  3720  	{
  3721  		Strs: []string{"time { foo; }"},
  3722  		bsmk: &TimeClause{Stmt: stmt(block(litStmt("foo")))},
  3723  	},
  3724  	{
  3725  		Strs: []string{"time\nfoo"},
  3726  		bsmk: []*Stmt{
  3727  			stmt(&TimeClause{}),
  3728  			litStmt("foo"),
  3729  		},
  3730  	},
  3731  	{
  3732  		Strs:   []string{"coproc foo bar"},
  3733  		common: litStmt("coproc", "foo", "bar"),
  3734  		bash:   &CoprocClause{Stmt: litStmt("foo", "bar")},
  3735  	},
  3736  	{
  3737  		Strs: []string{"coproc name { foo; }"},
  3738  		bash: &CoprocClause{
  3739  			Name: lit("name"),
  3740  			Stmt: stmt(block(litStmt("foo"))),
  3741  		},
  3742  	},
  3743  	{
  3744  		Strs: []string{"coproc foo", "coproc foo;"},
  3745  		bash: &CoprocClause{Stmt: litStmt("foo")},
  3746  	},
  3747  	{
  3748  		Strs: []string{"coproc { foo; }"},
  3749  		bash: &CoprocClause{
  3750  			Stmt: stmt(block(litStmt("foo"))),
  3751  		},
  3752  	},
  3753  	{
  3754  		Strs: []string{"coproc (foo)"},
  3755  		bash: &CoprocClause{
  3756  			Stmt: stmt(subshell(litStmt("foo"))),
  3757  		},
  3758  	},
  3759  	{
  3760  		Strs: []string{"coproc name foo | bar"},
  3761  		bash: &CoprocClause{
  3762  			Name: lit("name"),
  3763  			Stmt: stmt(&BinaryCmd{
  3764  				Op: Pipe,
  3765  				X:  litStmt("foo"),
  3766  				Y:  litStmt("bar"),
  3767  			}),
  3768  		},
  3769  	},
  3770  	{
  3771  		Strs: []string{"coproc $()", "coproc ``"},
  3772  		bash: &CoprocClause{Stmt: stmt(call(
  3773  			word(cmdSubst()),
  3774  		))},
  3775  	},
  3776  	{
  3777  		Strs: []string{`let i++`},
  3778  		bsmk: letClause(
  3779  			&UnaryArithm{Op: Inc, Post: true, X: litWord("i")},
  3780  		),
  3781  		posix: litStmt("let", "i++"),
  3782  	},
  3783  	{
  3784  		Strs: []string{`let a++ b++ c +d`},
  3785  		bsmk: letClause(
  3786  			&UnaryArithm{Op: Inc, Post: true, X: litWord("a")},
  3787  			&UnaryArithm{Op: Inc, Post: true, X: litWord("b")},
  3788  			litWord("c"),
  3789  			&UnaryArithm{Op: Plus, X: litWord("d")},
  3790  		),
  3791  	},
  3792  	{
  3793  		Strs: []string{`let ++i >/dev/null`},
  3794  		bsmk: &Stmt{
  3795  			Cmd:    letClause(&UnaryArithm{Op: Inc, X: litWord("i")}),
  3796  			Redirs: []*Redirect{{Op: RdrOut, Word: litWord("/dev/null")}},
  3797  		},
  3798  	},
  3799  	{
  3800  		Strs: []string{
  3801  			`let a=(1 + 2) b=3+4`,
  3802  			`let a=(1+2) b=3+4`,
  3803  		},
  3804  		bash: letClause(
  3805  			&BinaryArithm{
  3806  				Op: Assgn,
  3807  				X:  litWord("a"),
  3808  				Y: parenArit(&BinaryArithm{
  3809  					Op: Add,
  3810  					X:  litWord("1"),
  3811  					Y:  litWord("2"),
  3812  				}),
  3813  			},
  3814  			&BinaryArithm{
  3815  				Op: Assgn,
  3816  				X:  litWord("b"),
  3817  				Y: &BinaryArithm{
  3818  					Op: Add,
  3819  					X:  litWord("3"),
  3820  					Y:  litWord("4"),
  3821  				},
  3822  			},
  3823  		),
  3824  	},
  3825  	{
  3826  		Strs: []string{
  3827  			`let a=$(echo 3)`,
  3828  			"let a=`echo 3`",
  3829  		},
  3830  		bash: letClause(
  3831  			&BinaryArithm{
  3832  				Op: Assgn,
  3833  				X:  litWord("a"),
  3834  				Y:  word(cmdSubst(litStmt("echo", "3"))),
  3835  			},
  3836  		),
  3837  	},
  3838  	{
  3839  		Strs:   []string{"(foo-bar)"},
  3840  		common: subshell(litStmt("foo-bar")),
  3841  	},
  3842  	{
  3843  		Strs: []string{
  3844  			"let i++\nbar",
  3845  			"let i++ \nbar",
  3846  			"let i++; bar",
  3847  		},
  3848  		bsmk: []*Stmt{
  3849  			stmt(letClause(&UnaryArithm{
  3850  				Op:   Inc,
  3851  				Post: true,
  3852  				X:    litWord("i"),
  3853  			})),
  3854  			litStmt("bar"),
  3855  		},
  3856  	},
  3857  	{
  3858  		Strs: []string{
  3859  			"let i++\nfoo=(bar)",
  3860  			"let i++; foo=(bar)",
  3861  			"let i++; foo=(bar)\n",
  3862  		},
  3863  		bsmk: []*Stmt{
  3864  			stmt(letClause(&UnaryArithm{
  3865  				Op:   Inc,
  3866  				Post: true,
  3867  				X:    litWord("i"),
  3868  			})),
  3869  			stmt(&CallExpr{Assigns: []*Assign{{
  3870  				Name:  lit("foo"),
  3871  				Array: arrValues(litWord("bar")),
  3872  			}}}),
  3873  		},
  3874  	},
  3875  	{
  3876  		Strs: []string{
  3877  			"case a in b) let i++ ;; esac",
  3878  			"case a in b) let i++;; esac",
  3879  		},
  3880  		bsmk: &CaseClause{
  3881  			Word: word(lit("a")),
  3882  			Items: []*CaseItem{{
  3883  				Op:       Break,
  3884  				Patterns: litWords("b"),
  3885  				StmtList: stmtList(stmt(letClause(&UnaryArithm{
  3886  					Op:   Inc,
  3887  					Post: true,
  3888  					X:    litWord("i"),
  3889  				}))),
  3890  			}},
  3891  		},
  3892  	},
  3893  	{
  3894  		Strs: []string{"case a in b) [[ x =~ y ]] ;; esac"},
  3895  		bash: &CaseClause{
  3896  			Word: word(lit("a")),
  3897  			Items: []*CaseItem{{
  3898  				Op:       Break,
  3899  				Patterns: litWords("b"),
  3900  				StmtList: stmtList(stmt(
  3901  					&TestClause{X: &BinaryTest{
  3902  						Op: TsReMatch,
  3903  						X:  litWord("x"),
  3904  						Y:  litWord("y"),
  3905  					}},
  3906  				)),
  3907  			}},
  3908  		},
  3909  	},
  3910  	{
  3911  		Strs: []string{"a+=1"},
  3912  		bsmk: &CallExpr{
  3913  			Assigns: []*Assign{{
  3914  				Append: true,
  3915  				Name:   lit("a"),
  3916  				Value:  litWord("1"),
  3917  			}},
  3918  		},
  3919  		posix: litStmt("a+=1"),
  3920  	},
  3921  	{
  3922  		Strs: []string{"b+=(2 3)"},
  3923  		bsmk: &CallExpr{Assigns: []*Assign{{
  3924  			Append: true,
  3925  			Name:   lit("b"),
  3926  			Array:  arrValues(litWords("2", "3")...),
  3927  		}}},
  3928  	},
  3929  	{
  3930  		Strs:  []string{"a[2]=b c[-3]= d[x]+=e"},
  3931  		posix: litStmt("a[2]=b", "c[-3]=", "d[x]+=e"),
  3932  		bsmk: &CallExpr{Assigns: []*Assign{
  3933  			{
  3934  				Name:  lit("a"),
  3935  				Index: litWord("2"),
  3936  				Value: litWord("b"),
  3937  			},
  3938  			{
  3939  				Name: lit("c"),
  3940  				Index: &UnaryArithm{
  3941  					Op: Minus,
  3942  					X:  litWord("3"),
  3943  				},
  3944  			},
  3945  			{
  3946  				Name:   lit("d"),
  3947  				Index:  litWord("x"),
  3948  				Append: true,
  3949  				Value:  litWord("e"),
  3950  			},
  3951  		}},
  3952  	},
  3953  	{
  3954  		Strs: []string{
  3955  			"b[i]+=2",
  3956  			"b[ i ]+=2",
  3957  		},
  3958  		bsmk: &CallExpr{Assigns: []*Assign{{
  3959  			Append: true,
  3960  			Name:   lit("b"),
  3961  			Index:  litWord("i"),
  3962  			Value:  litWord("2"),
  3963  		}}},
  3964  	},
  3965  	{
  3966  		Strs: []string{`$((a + "b + $c"))`},
  3967  		common: arithmExp(&BinaryArithm{
  3968  			Op: Add,
  3969  			X:  litWord("a"),
  3970  			Y: word(dblQuoted(
  3971  				lit("b + "),
  3972  				litParamExp("c"),
  3973  			)),
  3974  		}),
  3975  	},
  3976  	{
  3977  		Strs: []string{`let 'i++'`},
  3978  		bsmk: letClause(word(sglQuoted("i++"))),
  3979  	},
  3980  	{
  3981  		Strs: []string{`echo ${a["x y"]}`},
  3982  		bash: call(litWord("echo"), word(&ParamExp{
  3983  			Param: lit("a"),
  3984  			Index: word(dblQuoted(lit("x y"))),
  3985  		})),
  3986  	},
  3987  	{
  3988  		Strs: []string{
  3989  			`a[$"x y"]=b`,
  3990  			`a[ $"x y" ]=b`,
  3991  		},
  3992  		bash: &CallExpr{Assigns: []*Assign{{
  3993  			Name: lit("a"),
  3994  			Index: word(&DblQuoted{Dollar: true, Parts: []WordPart{
  3995  				lit("x y"),
  3996  			}}),
  3997  			Value: litWord("b"),
  3998  		}}},
  3999  	},
  4000  	{
  4001  		Strs: []string{`((a["x y"] = b))`, `((a["x y"]=b))`},
  4002  		bsmk: arithmCmd(&BinaryArithm{
  4003  			Op: Assgn,
  4004  			X: word(&ParamExp{
  4005  				Short: true,
  4006  				Param: lit("a"),
  4007  				Index: word(dblQuoted(lit("x y"))),
  4008  			}),
  4009  			Y: litWord("b"),
  4010  		}),
  4011  	},
  4012  	{
  4013  		Strs: []string{
  4014  			`a=(["x y"]=b)`,
  4015  			`a=( [ "x y" ]=b)`,
  4016  		},
  4017  		bash: &CallExpr{Assigns: []*Assign{{
  4018  			Name: lit("a"),
  4019  			Array: &ArrayExpr{Elems: []*ArrayElem{{
  4020  				Index: word(dblQuoted(lit("x y"))),
  4021  				Value: litWord("b"),
  4022  			}}},
  4023  		}}},
  4024  	},
  4025  	{
  4026  		Strs: []string{
  4027  			"a=([x]= [y]=)",
  4028  			"a=(\n[x]=\n[y]=\n)",
  4029  		},
  4030  		bash: &CallExpr{Assigns: []*Assign{{
  4031  			Name: lit("a"),
  4032  			Array: &ArrayExpr{Elems: []*ArrayElem{
  4033  				{Index: litWord("x")},
  4034  				{Index: litWord("y")},
  4035  			}},
  4036  		}}},
  4037  	},
  4038  	{
  4039  		Strs:   []string{"a]b"},
  4040  		common: litStmt("a]b"),
  4041  	},
  4042  	{
  4043  		Strs:  []string{"echo a[b c[de]f"},
  4044  		posix: litStmt("echo", "a[b", "c[de]f"),
  4045  		bsmk: call(litWord("echo"),
  4046  			word(lit("a"), lit("[b")),
  4047  			word(lit("c"), lit("[de]f")),
  4048  		),
  4049  	},
  4050  	{
  4051  		Strs: []string{"<<EOF | b\nfoo\nEOF"},
  4052  		common: &BinaryCmd{
  4053  			Op: Pipe,
  4054  			X: &Stmt{Redirs: []*Redirect{{
  4055  				Op:   Hdoc,
  4056  				Word: litWord("EOF"),
  4057  				Hdoc: litWord("foo\n"),
  4058  			}}},
  4059  			Y: litStmt("b"),
  4060  		},
  4061  	},
  4062  	{
  4063  		Strs: []string{"<<EOF1 <<EOF2 | c && d\nEOF1\nEOF2"},
  4064  		common: &BinaryCmd{
  4065  			Op: AndStmt,
  4066  			X: stmt(&BinaryCmd{
  4067  				Op: Pipe,
  4068  				X: &Stmt{Redirs: []*Redirect{
  4069  					{Op: Hdoc, Word: litWord("EOF1")},
  4070  					{Op: Hdoc, Word: litWord("EOF2")},
  4071  				}},
  4072  				Y: litStmt("c"),
  4073  			}),
  4074  			Y: litStmt("d"),
  4075  		},
  4076  	},
  4077  	{
  4078  		Strs: []string{
  4079  			"<<EOF && { bar; }\nhdoc\nEOF",
  4080  			"<<EOF &&\nhdoc\nEOF\n{ bar; }",
  4081  		},
  4082  		common: &BinaryCmd{
  4083  			Op: AndStmt,
  4084  			X: &Stmt{Redirs: []*Redirect{{
  4085  				Op:   Hdoc,
  4086  				Word: litWord("EOF"),
  4087  				Hdoc: litWord("hdoc\n"),
  4088  			}}},
  4089  			Y: stmt(block(litStmt("bar"))),
  4090  		},
  4091  	},
  4092  	{
  4093  		Strs: []string{"foo() {\n\t<<EOF && { bar; }\nhdoc\nEOF\n}"},
  4094  		common: &FuncDecl{
  4095  			Name: lit("foo"),
  4096  			Body: stmt(block(stmt(&BinaryCmd{
  4097  				Op: AndStmt,
  4098  				X: &Stmt{Redirs: []*Redirect{{
  4099  					Op:   Hdoc,
  4100  					Word: litWord("EOF"),
  4101  					Hdoc: litWord("hdoc\n"),
  4102  				}}},
  4103  				Y: stmt(block(litStmt("bar"))),
  4104  			}))),
  4105  		},
  4106  	},
  4107  	{
  4108  		Strs: []string{`"a$("")"`, "\"a`\"\"`\""},
  4109  		common: dblQuoted(
  4110  			lit("a"),
  4111  			cmdSubst(stmt(call(
  4112  				word(dblQuoted()),
  4113  			))),
  4114  		),
  4115  	},
  4116  	{
  4117  		Strs: []string{"echo ?(b)*(c)+(d)@(e)!(f)"},
  4118  		bsmk: call(litWord("echo"), word(
  4119  			&ExtGlob{Op: GlobQuest, Pattern: lit("b")},
  4120  			&ExtGlob{Op: GlobStar, Pattern: lit("c")},
  4121  			&ExtGlob{Op: GlobPlus, Pattern: lit("d")},
  4122  			&ExtGlob{Op: GlobAt, Pattern: lit("e")},
  4123  			&ExtGlob{Op: GlobExcl, Pattern: lit("f")},
  4124  		)),
  4125  	},
  4126  	{
  4127  		Strs: []string{"echo foo@(b*(c|d))bar"},
  4128  		bsmk: call(litWord("echo"), word(
  4129  			lit("foo"),
  4130  			&ExtGlob{Op: GlobAt, Pattern: lit("b*(c|d)")},
  4131  			lit("bar"),
  4132  		)),
  4133  	},
  4134  	{
  4135  		Strs: []string{"echo $a@(b)$c?(d)$e*(f)$g+(h)$i!(j)$k"},
  4136  		bsmk: call(litWord("echo"), word(
  4137  			litParamExp("a"),
  4138  			&ExtGlob{Op: GlobAt, Pattern: lit("b")},
  4139  			litParamExp("c"),
  4140  			&ExtGlob{Op: GlobQuest, Pattern: lit("d")},
  4141  			litParamExp("e"),
  4142  			&ExtGlob{Op: GlobStar, Pattern: lit("f")},
  4143  			litParamExp("g"),
  4144  			&ExtGlob{Op: GlobPlus, Pattern: lit("h")},
  4145  			litParamExp("i"),
  4146  			&ExtGlob{Op: GlobExcl, Pattern: lit("j")},
  4147  			litParamExp("k"),
  4148  		)),
  4149  	},
  4150  }
  4151  
  4152  // these don't have a canonical format with the same syntax tree
  4153  var fileTestsNoPrint = []testCase{
  4154  	{
  4155  		Strs:  []string{`$[foo]`},
  4156  		posix: word(lit("$"), lit("[foo]")),
  4157  	},
  4158  	{
  4159  		Strs:  []string{`"$[foo]"`},
  4160  		posix: dblQuoted(lit("$"), lit("[foo]")),
  4161  	},
  4162  	{
  4163  		Strs: []string{`"$[1 + 3]"`},
  4164  		bash: dblQuoted(arithmExpBr(&BinaryArithm{
  4165  			Op: Add,
  4166  			X:  litWord("1"),
  4167  			Y:  litWord("3"),
  4168  		})),
  4169  	},
  4170  }
  4171  
  4172  func fullProg(v interface{}) *File {
  4173  	f := &File{}
  4174  	switch x := v.(type) {
  4175  	case *File:
  4176  		return x
  4177  	case StmtList:
  4178  		f.Stmts = x.Stmts
  4179  		return f
  4180  	case []*Stmt:
  4181  		f.Stmts = x
  4182  		return f
  4183  	case *Stmt:
  4184  		f.Stmts = append(f.Stmts, x)
  4185  		return f
  4186  	case []Command:
  4187  		for _, cmd := range x {
  4188  			f.Stmts = append(f.Stmts, stmt(cmd))
  4189  		}
  4190  		return f
  4191  	case *Word:
  4192  		return fullProg(call(x))
  4193  	case WordPart:
  4194  		return fullProg(word(x))
  4195  	case Command:
  4196  		return fullProg(stmt(x))
  4197  	case nil:
  4198  	default:
  4199  		panic(reflect.TypeOf(v))
  4200  	}
  4201  	return nil
  4202  }
  4203  
  4204  func clearPosRecurse(tb testing.TB, src string, v interface{}) {
  4205  	zeroPos := Pos{}
  4206  	checkSrc := func(pos Pos, strs ...string) {
  4207  		if src == "" {
  4208  			return
  4209  		}
  4210  		offs := pos.Offset()
  4211  		if offs > uint(len(src)) {
  4212  			tb.Fatalf("Pos %d in %T is out of bounds in %q",
  4213  				pos, v, src)
  4214  			return
  4215  		}
  4216  		if strs == nil {
  4217  			return
  4218  		}
  4219  		if strings.Contains(src, "<<-") {
  4220  			// since the tab indentation in <<- heredoc bodies
  4221  			// aren't part of the final literals
  4222  			return
  4223  		}
  4224  		var gotErr string
  4225  		for i, want := range strs {
  4226  			got := src[offs:]
  4227  			if i == 0 {
  4228  				gotErr = got
  4229  			}
  4230  			got = strings.Replace(got, "\\\n", "", -1)
  4231  			if len(got) > len(want) {
  4232  				got = got[:len(want)]
  4233  			}
  4234  			if got == want {
  4235  				return
  4236  			}
  4237  		}
  4238  		tb.Fatalf("Expected one of %q at %d in %q, found %q",
  4239  			strs, pos, src, gotErr)
  4240  	}
  4241  	setPos := func(p *Pos, strs ...string) {
  4242  		checkSrc(*p, strs...)
  4243  		if *p == zeroPos {
  4244  			tb.Fatalf("Pos in %T is already %v", v, zeroPos)
  4245  		}
  4246  		*p = zeroPos
  4247  	}
  4248  	checkPos := func(n Node) {
  4249  		if n == nil {
  4250  			return
  4251  		}
  4252  		if n.Pos() != zeroPos {
  4253  			tb.Fatalf("Found unexpected Pos() in %T: want %d, got %d",
  4254  				n, zeroPos, n.Pos())
  4255  		}
  4256  		if n.Pos().After(n.End()) {
  4257  			tb.Fatalf("Found End() before Pos() in %T", n)
  4258  		}
  4259  	}
  4260  	recurse := func(v interface{}) {
  4261  		clearPosRecurse(tb, src, v)
  4262  		if n, ok := v.(Node); ok {
  4263  			checkPos(n)
  4264  		}
  4265  	}
  4266  	switch x := v.(type) {
  4267  	case *File:
  4268  		recurse(x.StmtList)
  4269  		checkPos(x)
  4270  	case []Comment:
  4271  		for i := range x {
  4272  			recurse(&x[i])
  4273  		}
  4274  	case *Comment:
  4275  		setPos(&x.Hash, "#"+x.Text)
  4276  	case StmtList:
  4277  		for _, s := range x.Stmts {
  4278  			recurse(s)
  4279  		}
  4280  		recurse(x.Last)
  4281  	case *Stmt:
  4282  		endOff := int(x.End().Offset())
  4283  		if endOff < len(src) {
  4284  			end := src[endOff]
  4285  			switch {
  4286  			case end == ' ', end == '\n', end == '\t', end == '\r':
  4287  				// ended by whitespace
  4288  			case regOps(rune(end)):
  4289  				// ended by end character
  4290  			case endOff > 0 && src[endOff-1] == ';':
  4291  				// ended by semicolon
  4292  			case endOff > 0 && src[endOff-1] == '&':
  4293  				// ended by & or |&
  4294  			default:
  4295  				tb.Fatalf("Unexpected Stmt.End() %d %q in %q",
  4296  					endOff, end, src)
  4297  			}
  4298  		}
  4299  		recurse(x.Comments)
  4300  		if src[x.Position.Offset()] == '#' {
  4301  			tb.Fatalf("Stmt.Pos() should not be a comment")
  4302  		}
  4303  		setPos(&x.Position)
  4304  		if x.Semicolon.IsValid() {
  4305  			setPos(&x.Semicolon, ";", "&", "|&")
  4306  		}
  4307  		if x.Cmd != nil {
  4308  			recurse(x.Cmd)
  4309  		}
  4310  		for _, r := range x.Redirs {
  4311  			setPos(&r.OpPos, r.Op.String())
  4312  			if r.N != nil {
  4313  				recurse(r.N)
  4314  			}
  4315  			recurse(r.Word)
  4316  			if r.Hdoc != nil {
  4317  				recurse(r.Hdoc)
  4318  			}
  4319  		}
  4320  	case []*Assign:
  4321  		for _, a := range x {
  4322  			if a.Name != nil {
  4323  				recurse(a.Name)
  4324  			}
  4325  			if a.Index != nil {
  4326  				recurse(a.Index)
  4327  			}
  4328  			if a.Value != nil {
  4329  				recurse(a.Value)
  4330  			}
  4331  			if a.Array != nil {
  4332  				recurse(a.Array)
  4333  			}
  4334  			checkPos(a)
  4335  		}
  4336  	case *CallExpr:
  4337  		recurse(x.Assigns)
  4338  		recurse(x.Args)
  4339  	case []*Word:
  4340  		for _, w := range x {
  4341  			recurse(w)
  4342  		}
  4343  	case *Word:
  4344  		recurse(x.Parts)
  4345  	case []WordPart:
  4346  		for _, wp := range x {
  4347  			recurse(wp)
  4348  		}
  4349  	case *Lit:
  4350  		pos, end := int(x.Pos().Offset()), int(x.End().Offset())
  4351  		want := pos + len(x.Value)
  4352  		val := x.Value
  4353  		posLine := x.Pos().Line()
  4354  		endLine := x.End().Line()
  4355  		switch {
  4356  		case src == "":
  4357  		case strings.Contains(src, "\\\n"):
  4358  		case !strings.Contains(x.Value, "\n") && posLine != endLine:
  4359  			tb.Fatalf("Lit without newlines has Pos/End lines %d and %d",
  4360  				posLine, endLine)
  4361  		case strings.Contains(src, "`") && strings.Contains(src, "\\"):
  4362  			// removed quotes inside backquote cmd substs
  4363  			val = ""
  4364  		case end < len(src) && src[end] == '\n':
  4365  			// heredoc literals that end with the
  4366  			// stop word and a newline
  4367  		case end == len(src):
  4368  			// same as above, but with word and EOF
  4369  		case end != want:
  4370  			tb.Fatalf("Unexpected Lit %q End() %d (wanted %d) in %q",
  4371  				val, end, want, src)
  4372  		}
  4373  		setPos(&x.ValuePos, val)
  4374  		setPos(&x.ValueEnd)
  4375  	case *Subshell:
  4376  		setPos(&x.Lparen, "(")
  4377  		setPos(&x.Rparen, ")")
  4378  		recurse(x.StmtList)
  4379  	case *Block:
  4380  		setPos(&x.Lbrace, "{")
  4381  		setPos(&x.Rbrace, "}")
  4382  		recurse(x.StmtList)
  4383  	case *IfClause:
  4384  		setPos(&x.IfPos, "if", "elif")
  4385  		setPos(&x.ThenPos, "then")
  4386  		if x.FiPos.IsValid() {
  4387  			setPos(&x.FiPos, "fi", "elif")
  4388  		}
  4389  		recurse(x.Cond)
  4390  		recurse(x.Then)
  4391  		if !x.Else.empty() {
  4392  			setPos(&x.ElsePos, "else", "elif")
  4393  			recurse(x.Else)
  4394  		}
  4395  	case *WhileClause:
  4396  		rsrv := "while"
  4397  		if x.Until {
  4398  			rsrv = "until"
  4399  		}
  4400  		setPos(&x.WhilePos, rsrv)
  4401  		setPos(&x.DoPos, "do")
  4402  		setPos(&x.DonePos, "done")
  4403  		recurse(x.Cond)
  4404  		recurse(x.Do)
  4405  	case *ForClause:
  4406  		if x.Select {
  4407  			setPos(&x.ForPos, "select")
  4408  		} else {
  4409  			setPos(&x.ForPos, "for")
  4410  		}
  4411  		setPos(&x.DoPos, "do")
  4412  		setPos(&x.DonePos, "done")
  4413  		recurse(x.Loop)
  4414  		recurse(x.Do)
  4415  	case *WordIter:
  4416  		recurse(x.Name)
  4417  		if x.InPos.IsValid() {
  4418  			setPos(&x.InPos, "in")
  4419  		}
  4420  		recurse(x.Items)
  4421  	case *CStyleLoop:
  4422  		setPos(&x.Lparen, "((")
  4423  		setPos(&x.Rparen, "))")
  4424  		if x.Init != nil {
  4425  			recurse(x.Init)
  4426  		}
  4427  		if x.Cond != nil {
  4428  			recurse(x.Cond)
  4429  		}
  4430  		if x.Post != nil {
  4431  			recurse(x.Post)
  4432  		}
  4433  	case *SglQuoted:
  4434  		checkSrc(posAddCol(x.End(), -1), "'")
  4435  		valuePos := posAddCol(x.Left, 1)
  4436  		if x.Dollar {
  4437  			valuePos = posAddCol(valuePos, 1)
  4438  		}
  4439  		checkSrc(valuePos, x.Value)
  4440  		if x.Dollar {
  4441  			setPos(&x.Left, "$'")
  4442  		} else {
  4443  			setPos(&x.Left, "'")
  4444  		}
  4445  		setPos(&x.Right, "'")
  4446  	case *DblQuoted:
  4447  		checkSrc(posAddCol(x.End(), -1), `"`)
  4448  		if x.Dollar {
  4449  			setPos(&x.Position, `$"`)
  4450  		} else {
  4451  			setPos(&x.Position, `"`)
  4452  		}
  4453  		recurse(x.Parts)
  4454  	case *UnaryArithm:
  4455  		setPos(&x.OpPos, x.Op.String())
  4456  		recurse(x.X)
  4457  	case *UnaryTest:
  4458  		strs := []string{x.Op.String()}
  4459  		switch x.Op {
  4460  		case TsExists:
  4461  			strs = append(strs, "-a")
  4462  		case TsSmbLink:
  4463  			strs = append(strs, "-h")
  4464  		}
  4465  		setPos(&x.OpPos, strs...)
  4466  		recurse(x.X)
  4467  	case *BinaryCmd:
  4468  		setPos(&x.OpPos, x.Op.String())
  4469  		recurse(x.X)
  4470  		recurse(x.Y)
  4471  	case *BinaryArithm:
  4472  		setPos(&x.OpPos, x.Op.String())
  4473  		recurse(x.X)
  4474  		recurse(x.Y)
  4475  	case *BinaryTest:
  4476  		strs := []string{x.Op.String()}
  4477  		switch x.Op {
  4478  		case TsMatch:
  4479  			strs = append(strs, "=")
  4480  		}
  4481  		setPos(&x.OpPos, strs...)
  4482  		recurse(x.X)
  4483  		recurse(x.Y)
  4484  	case *ParenArithm:
  4485  		setPos(&x.Lparen, "(")
  4486  		setPos(&x.Rparen, ")")
  4487  		recurse(x.X)
  4488  	case *ParenTest:
  4489  		setPos(&x.Lparen, "(")
  4490  		setPos(&x.Rparen, ")")
  4491  		recurse(x.X)
  4492  	case *FuncDecl:
  4493  		if x.RsrvWord {
  4494  			setPos(&x.Position, "function")
  4495  		} else {
  4496  			setPos(&x.Position)
  4497  		}
  4498  		recurse(x.Name)
  4499  		recurse(x.Body)
  4500  	case *ParamExp:
  4501  		doll := "$"
  4502  		if x.nakedIndex() {
  4503  			doll = ""
  4504  		}
  4505  		setPos(&x.Dollar, doll)
  4506  		if !x.Short {
  4507  			setPos(&x.Rbrace, "}")
  4508  		} else if x.nakedIndex() {
  4509  			checkSrc(posAddCol(x.End(), -1), "]")
  4510  		}
  4511  		recurse(x.Param)
  4512  		if x.Index != nil {
  4513  			recurse(x.Index)
  4514  		}
  4515  		if x.Slice != nil {
  4516  			if x.Slice.Offset != nil {
  4517  				recurse(x.Slice.Offset)
  4518  			}
  4519  			if x.Slice.Length != nil {
  4520  				recurse(x.Slice.Length)
  4521  			}
  4522  		}
  4523  		if x.Repl != nil {
  4524  			if x.Repl.Orig != nil {
  4525  				recurse(x.Repl.Orig)
  4526  			}
  4527  			if x.Repl.With != nil {
  4528  				recurse(x.Repl.With)
  4529  			}
  4530  		}
  4531  		if x.Exp != nil && x.Exp.Word != nil {
  4532  			recurse(x.Exp.Word)
  4533  		}
  4534  	case *ArithmExp:
  4535  		if x.Bracket {
  4536  			// deprecated $(( form
  4537  			setPos(&x.Left, "$[")
  4538  			setPos(&x.Right, "]")
  4539  		} else {
  4540  			setPos(&x.Left, "$((")
  4541  			setPos(&x.Right, "))")
  4542  		}
  4543  		recurse(x.X)
  4544  	case *ArithmCmd:
  4545  		setPos(&x.Left, "((")
  4546  		setPos(&x.Right, "))")
  4547  		recurse(x.X)
  4548  	case *CmdSubst:
  4549  		switch {
  4550  		case x.TempFile:
  4551  			setPos(&x.Left, "${ ", "${\t", "${\n")
  4552  			setPos(&x.Right, "}")
  4553  		case x.ReplyVar:
  4554  			setPos(&x.Left, "${|")
  4555  			setPos(&x.Right, "}")
  4556  		default:
  4557  			setPos(&x.Left, "$(", "`", "\\`")
  4558  			setPos(&x.Right, ")", "`", "\\`")
  4559  		}
  4560  		recurse(x.StmtList)
  4561  	case *CaseClause:
  4562  		setPos(&x.Case, "case")
  4563  		setPos(&x.Esac, "esac", "}")
  4564  		recurse(x.Word)
  4565  		for _, ci := range x.Items {
  4566  			recurse(ci)
  4567  		}
  4568  	case *CaseItem:
  4569  		if x.OpPos.IsValid() {
  4570  			setPos(&x.OpPos, x.Op.String(), "esac")
  4571  		}
  4572  		recurse(x.Patterns)
  4573  		recurse(x.StmtList)
  4574  	case *TestClause:
  4575  		setPos(&x.Left, "[[")
  4576  		setPos(&x.Right, "]]")
  4577  		recurse(x.X)
  4578  	case *DeclClause:
  4579  		recurse(x.Variant)
  4580  		recurse(x.Opts)
  4581  		recurse(x.Assigns)
  4582  	case *TimeClause:
  4583  		setPos(&x.Time, "time")
  4584  		if x.Stmt != nil {
  4585  			recurse(x.Stmt)
  4586  		}
  4587  	case *CoprocClause:
  4588  		setPos(&x.Coproc, "coproc")
  4589  		if x.Name != nil {
  4590  			recurse(x.Name)
  4591  		}
  4592  		recurse(x.Stmt)
  4593  	case *LetClause:
  4594  		setPos(&x.Let, "let")
  4595  		for _, expr := range x.Exprs {
  4596  			recurse(expr)
  4597  		}
  4598  	case *ArrayExpr:
  4599  		setPos(&x.Lparen, "(")
  4600  		setPos(&x.Rparen, ")")
  4601  		for _, elem := range x.Elems {
  4602  			recurse(elem)
  4603  		}
  4604  	case *ArrayElem:
  4605  		if x.Index != nil {
  4606  			recurse(x.Index)
  4607  		}
  4608  		if x.Value != nil {
  4609  			recurse(x.Value)
  4610  		}
  4611  	case *ExtGlob:
  4612  		setPos(&x.OpPos, x.Op.String())
  4613  		checkSrc(posAddCol(x.End(), -1), ")")
  4614  		recurse(x.Pattern)
  4615  	case *ProcSubst:
  4616  		setPos(&x.OpPos, x.Op.String())
  4617  		setPos(&x.Rparen, ")")
  4618  		recurse(x.StmtList)
  4619  	default:
  4620  		panic(reflect.TypeOf(v))
  4621  	}
  4622  }