Skip to content

Commit

Permalink
syntax: support array elements without values
Browse files Browse the repository at this point in the history
For example:

	declare -A x=([index]=)

Fixes mvdan#361.
  • Loading branch information
mvdan committed Mar 9, 2019
1 parent 3684192 commit 55e3a63
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 8 deletions.
17 changes: 16 additions & 1 deletion syntax/filetests_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4008,6 +4008,19 @@ var fileTests = []testCase{
}}},
}}},
},
{
Strs: []string{
"a=([x]= [y]=)",
"a=(\n[x]=\n[y]=\n)",
},
bash: &CallExpr{Assigns: []*Assign{{
Name: lit("a"),
Array: &ArrayExpr{Elems: []*ArrayElem{
{Index: litWord("x")},
{Index: litWord("y")},
}},
}}},
},
{
Strs: []string{"a]b"},
common: litStmt("a]b"),
Expand Down Expand Up @@ -4579,7 +4592,9 @@ func clearPosRecurse(tb testing.TB, src string, v interface{}) {
if x.Index != nil {
recurse(x.Index)
}
recurse(x.Value)
if x.Value != nil {
recurse(x.Value)
}
case *ExtGlob:
setPos(&x.OpPos, x.Op.String())
checkSrc(posAddCol(x.End(), -1), ")")
Expand Down
13 changes: 11 additions & 2 deletions syntax/nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -756,8 +756,12 @@ func (a *ArrayExpr) Pos() Pos { return a.Lparen }
func (a *ArrayExpr) End() Pos { return posAddCol(a.Rparen, 1) }

// ArrayElem represents a Bash array element.
//
// Index can be nil; for example, declare -a x=(value).
// Value can be nil; for example, declare -A x=([index]=).
// Finally, neither can be nil; for example, declare -A x=([index]=value)
type ArrayElem struct {
Index ArithmExpr // [i]=, ["k"]=
Index ArithmExpr
Value *Word
Comments []Comment
}
Expand All @@ -768,7 +772,12 @@ func (a *ArrayElem) Pos() Pos {
}
return a.Value.Pos()
}
func (a *ArrayElem) End() Pos { return a.Value.End() }
func (a *ArrayElem) End() Pos {
if a.Value != nil {
return a.Value.End()
}
return posAddCol(a.Index.Pos(), 1)
}

// ExtGlob represents a Bash extended globbing expression. Note that these are
// parsed independently of whether shopt has been called or not.
Expand Down
11 changes: 8 additions & 3 deletions syntax/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1609,11 +1609,16 @@ func (p *Parser) getAssign(needEqual bool) *Assign {
p.follow(left, `"[x]"`, assgn)
}
if ae.Value = p.getWord(); ae.Value == nil {
if p.tok == leftParen {
switch p.tok {
case leftParen:
p.curErr("arrays cannot be nested")
return nil
case _Newl, rightParen, leftBrack:
// TODO: support [index]=[
default:
p.curErr("array element values must be words")
break
}
p.curErr("array element values must be words")
break
}
if len(p.accComs) > 0 {
c := p.accComs[0]
Expand Down
4 changes: 3 additions & 1 deletion syntax/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -788,7 +788,9 @@ func (p *Printer) elemJoin(elems []*ArrayElem, last []Comment) {
if p.wroteIndex(el.Index) {
p.WriteByte('=')
}
p.word(el.Value)
if el.Value != nil {
p.word(el.Value)
}
p.comments(left...)
}
if len(last) > 0 {
Expand Down
4 changes: 3 additions & 1 deletion syntax/walk.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,9 @@ func Walk(node Node, f func(Node) bool) {
if x.Index != nil {
Walk(x.Index, f)
}
Walk(x.Value, f)
if x.Value != nil {
Walk(x.Value, f)
}
case *ExtGlob:
Walk(x.Pattern, f)
case *ProcSubst:
Expand Down

0 comments on commit 55e3a63

Please sign in to comment.