github.com/cilki/sh@v2.6.4+incompatible/syntax/expand.go (about) 1 // Copyright (c) 2018, Daniel Martà <mvdan@mvdan.cc> 2 // See LICENSE for licensing information 3 4 package syntax 5 6 import "strconv" 7 8 // TODO(v3): Consider making these special syntax nodes. 9 // Among other things, we can make use of Word.Lit. 10 11 type brace struct { 12 seq bool // {x..y[..incr]} instead of {x,y[,...]} 13 chars bool // sequence is of chars, not numbers 14 elems []*braceWord 15 } 16 17 // braceWord is like Word, but with braceWordPart. 18 type braceWord struct { 19 parts []braceWordPart 20 } 21 22 // braceWordPart contains any WordPart or a brace. 23 type braceWordPart interface{} 24 25 var ( 26 litLeftBrace = &Lit{Value: "{"} 27 litComma = &Lit{Value: ","} 28 litDots = &Lit{Value: ".."} 29 litRightBrace = &Lit{Value: "}"} 30 ) 31 32 func splitBraces(word *Word) (*braceWord, bool) { 33 any := false 34 top := &braceWord{} 35 acc := top 36 var cur *brace 37 open := []*brace{} 38 39 pop := func() *brace { 40 old := cur 41 open = open[:len(open)-1] 42 if len(open) == 0 { 43 cur = nil 44 acc = top 45 } else { 46 cur = open[len(open)-1] 47 acc = cur.elems[len(cur.elems)-1] 48 } 49 return old 50 } 51 addLit := func(lit *Lit) { 52 acc.parts = append(acc.parts, lit) 53 } 54 addParts := func(parts ...braceWordPart) { 55 acc.parts = append(acc.parts, parts...) 56 } 57 58 for _, wp := range word.Parts { 59 lit, ok := wp.(*Lit) 60 if !ok { 61 addParts(wp) 62 continue 63 } 64 last := 0 65 for j := 0; j < len(lit.Value); j++ { 66 addlitidx := func() { 67 if last == j { 68 return // empty lit 69 } 70 l2 := *lit 71 l2.Value = l2.Value[last:j] 72 addLit(&l2) 73 } 74 switch lit.Value[j] { 75 case '{': 76 addlitidx() 77 acc = &braceWord{} 78 cur = &brace{elems: []*braceWord{acc}} 79 open = append(open, cur) 80 case ',': 81 if cur == nil { 82 continue 83 } 84 addlitidx() 85 acc = &braceWord{} 86 cur.elems = append(cur.elems, acc) 87 case '.': 88 if cur == nil { 89 continue 90 } 91 if j+1 >= len(lit.Value) || lit.Value[j+1] != '.' { 92 continue 93 } 94 addlitidx() 95 cur.seq = true 96 acc = &braceWord{} 97 cur.elems = append(cur.elems, acc) 98 j++ 99 case '}': 100 if cur == nil { 101 continue 102 } 103 any = true 104 addlitidx() 105 br := pop() 106 if len(br.elems) == 1 { 107 // return {x} to a non-brace 108 addLit(litLeftBrace) 109 addParts(br.elems[0].parts...) 110 addLit(litRightBrace) 111 break 112 } 113 if !br.seq { 114 addParts(br) 115 break 116 } 117 var chars [2]bool 118 broken := false 119 for i, elem := range br.elems[:2] { 120 val := braceWordLit(elem) 121 if _, err := strconv.Atoi(val); err == nil { 122 } else if len(val) == 1 && 123 'a' <= val[0] && val[0] <= 'z' { 124 chars[i] = true 125 } else { 126 broken = true 127 } 128 } 129 if len(br.elems) == 3 { 130 // increment must be a number 131 val := braceWordLit(br.elems[2]) 132 if _, err := strconv.Atoi(val); err != nil { 133 broken = true 134 } 135 } 136 // are start and end both chars or 137 // non-chars? 138 if chars[0] != chars[1] { 139 broken = true 140 } 141 if !broken { 142 br.chars = chars[0] 143 addParts(br) 144 break 145 } 146 // return broken {x..y[..incr]} to a non-brace 147 addLit(litLeftBrace) 148 for i, elem := range br.elems { 149 if i > 0 { 150 addLit(litDots) 151 } 152 addParts(elem.parts...) 153 } 154 addLit(litRightBrace) 155 default: 156 continue 157 } 158 last = j + 1 159 } 160 if last == 0 { 161 addLit(lit) 162 } else { 163 left := *lit 164 left.Value = left.Value[last:] 165 addLit(&left) 166 } 167 } 168 // open braces that were never closed fall back to non-braces 169 for acc != top { 170 br := pop() 171 addLit(litLeftBrace) 172 for i, elem := range br.elems { 173 if i > 0 { 174 if br.seq { 175 addLit(litDots) 176 } else { 177 addLit(litComma) 178 } 179 } 180 addParts(elem.parts...) 181 } 182 } 183 return top, any 184 } 185 186 func braceWordLit(v interface{}) string { 187 word, _ := v.(*braceWord) 188 if word == nil || len(word.parts) != 1 { 189 return "" 190 } 191 lit, ok := word.parts[0].(*Lit) 192 if !ok { 193 return "" 194 } 195 return lit.Value 196 } 197 198 func expandRec(bw *braceWord) []*Word { 199 var all []*Word 200 var left []WordPart 201 for i, wp := range bw.parts { 202 br, ok := wp.(*brace) 203 if !ok { 204 left = append(left, wp.(WordPart)) 205 continue 206 } 207 if br.seq { 208 var from, to int 209 if br.chars { 210 from = int(braceWordLit(br.elems[0])[0]) 211 to = int(braceWordLit(br.elems[1])[0]) 212 } else { 213 from, _ = strconv.Atoi(braceWordLit(br.elems[0])) 214 to, _ = strconv.Atoi(braceWordLit(br.elems[1])) 215 } 216 upward := from <= to 217 incr := 1 218 if !upward { 219 incr = -1 220 } 221 if len(br.elems) > 2 { 222 val := braceWordLit(br.elems[2]) 223 n, _ := strconv.Atoi(val) 224 if n != 0 && n > 0 == upward { 225 incr = n 226 } 227 } 228 n := from 229 for { 230 if upward && n > to { 231 break 232 } 233 if !upward && n < to { 234 break 235 } 236 next := *bw 237 next.parts = next.parts[i+1:] 238 lit := &Lit{} 239 if br.chars { 240 lit.Value = string(n) 241 } else { 242 lit.Value = strconv.Itoa(n) 243 } 244 next.parts = append([]braceWordPart{lit}, next.parts...) 245 exp := expandRec(&next) 246 for _, w := range exp { 247 w.Parts = append(left, w.Parts...) 248 } 249 all = append(all, exp...) 250 n += incr 251 } 252 return all 253 } 254 for _, elem := range br.elems { 255 next := *bw 256 next.parts = next.parts[i+1:] 257 next.parts = append(elem.parts, next.parts...) 258 exp := expandRec(&next) 259 for _, w := range exp { 260 w.Parts = append(left, w.Parts...) 261 } 262 all = append(all, exp...) 263 } 264 return all 265 } 266 return []*Word{{Parts: left}} 267 } 268 269 // TODO(v3): remove 270 271 // ExpandBraces performs Bash brace expansion on a word. For example, 272 // passing it a single-literal word "foo{bar,baz}" will return two 273 // single-literal words, "foobar" and "foobaz". 274 // 275 // Deprecated: use mvdan.cc/sh/expand.Braces instead. 276 func ExpandBraces(word *Word) []*Word { 277 topBrace, any := splitBraces(word) 278 if !any { 279 return []*Word{word} 280 } 281 return expandRec(topBrace) 282 }