Fix issues with variable expansion.

This commit is contained in:
Daniel Jones 2013-03-02 10:57:40 -08:00
parent 207e052372
commit 115d8425be
3 changed files with 125 additions and 53 deletions

103
expand.go
View file

@ -10,18 +10,24 @@ import (
// Expand a word. This includes substituting variables and handling quotes. // Expand a word. This includes substituting variables and handling quotes.
func expand(input string, vars map[string][]string, expandBackticks bool) string { func expand(input string, vars map[string][]string, expandBackticks bool) []string {
expanded := make([]byte, 0) parts := make([]string, 0)
expanded := ""
var i, j int var i, j int
for i = 0; i < len(input); { for i = 0; i < len(input); {
j = i + strings.IndexAny(input[i:], "\"'`$\\") j = i + strings.IndexAny(input[i:], "\"'`$\\")
if j < 0 { if j < 0 {
expanded = append(expanded, input[i:]...) expanded += input[i:]
break break
} }
expanded = append(expanded, input[i:j]...) println("-------------------")
println(len(input))
println(i)
println(j)
expanded += input[i:j]
c, w := utf8.DecodeRuneInString(input[j:]) c, w := utf8.DecodeRuneInString(input[j:])
i = j + w i = j + w
@ -30,12 +36,15 @@ func expand(input string, vars map[string][]string, expandBackticks bool) string
switch c { switch c {
case '\\': case '\\':
out, off = expandEscape(input[i:]) out, off = expandEscape(input[i:])
expanded += out
case '"': case '"':
out, off = expandDoubleQuoted(input[i:], vars, expandBackticks) out, off = expandDoubleQuoted(input[i:], vars, expandBackticks)
expanded += out
case '\'': case '\'':
out, off = expandSingleQuoted(input[i:]) out, off = expandSingleQuoted(input[i:])
expanded += out
case '`': case '`':
if expandBackticks { if expandBackticks {
@ -44,16 +53,26 @@ func expand(input string, vars map[string][]string, expandBackticks bool) string
out = input out = input
off = len(input) off = len(input)
} }
expanded += out
case '$': case '$':
out, off = expandSigil(input[i:], vars) var outparts []string
outparts, off = expandSigil(input[i:], vars)
if len(outparts) > 0 {
outparts[0] = expanded + outparts[0]
expanded = outparts[len(outparts)-1]
parts = append(parts, outparts[:len(outparts)-1]...)
}
} }
expanded = append(expanded, out...)
i += off i += off
} }
return string(expanded) if len(expanded) > 0 {
parts = append(parts, expanded)
}
return parts
} }
// Expand following a '\\' // Expand following a '\\'
@ -79,7 +98,7 @@ func expandDoubleQuoted(input string, vars map[string][]string, expandBackticks
j += w j += w
if c == '"' { if c == '"' {
return expand(input[:j], vars, expandBackticks), (j + w) return strings.Join(expand(input[:j], vars, expandBackticks), " "), (j + w)
} }
if c == '\\' { if c == '\\' {
@ -106,21 +125,21 @@ func expandSingleQuoted(input string) (string, int) {
} }
// Expand something starting with at '$'. // Expand something starting with at '$'.
func expandSigil(input string, vars map[string][]string) (string, int) { func expandSigil(input string, vars map[string][]string) ([]string, int) {
c, w := utf8.DecodeRuneInString(input) c, w := utf8.DecodeRuneInString(input)
var offset int var offset int
var varname string var varname string
if c == '{' { if c == '{' {
j := strings.IndexRune(input[w:], '}') j := strings.IndexRune(input[w:], '}')
if j < 0 { if j < 0 {
return input, len(input) return []string{"$" + input}, len(input)
} }
varname = input[w:j] varname = input[w:j]
offset = j + 1 offset = j + 1
} else { } else {
// try to match a variable name // try to match a variable name
i := w i := 0
j := i j := i
for j < len(input) { for j < len(input) {
c, w = utf8.DecodeRuneInString(input) c, w = utf8.DecodeRuneInString(input)
@ -132,38 +151,48 @@ func expandSigil(input string, vars map[string][]string) (string, int) {
if j > i { if j > i {
varname = input[i:j] varname = input[i:j]
offset = j
} else { } else {
return input, len(input) return []string{"$" + input}, len(input)
} }
} }
if isValidVarName(varname) { if isValidVarName(varname) {
varvals, ok := vars[varname] varvals, ok := vars[varname]
if ok { if ok {
return strings.Join(varvals, " "), offset return varvals, offset
} }
} }
return input, len(input) return []string{"$" + input}, len(input)
} }
// Find and expand all sigils. // Find and expand all sigils.
func expandSigils(input string, vars map[string][]string) string { func expandSigils(input string, vars map[string][]string) []string {
expanded := make([]byte, 0) parts := make([]string, 0)
expanded := ""
for i := 0; i < len(input); { for i := 0; i < len(input); {
j := strings.IndexRune(input[i:], '$') j := strings.IndexRune(input[i:], '$')
if j < 0 { if j < 0 {
expanded = append(expanded, input[i:]...) expanded += input[i:]
break break
} }
ex, k := expandSigil(input[j+1:], vars) ex, k := expandSigil(input[j+1:], vars)
expanded = append(expanded, ex...) if len(ex) > 0 {
ex[0] = expanded + ex[0]
expanded = ex[len(ex)-1]
parts = append(parts, ex[:len(ex)-1]...)
}
i = k i = k
} }
return string(expanded) if len(expanded) > 0 {
parts = append(parts, expanded)
}
return parts
} }
@ -212,3 +241,39 @@ func expandBackQuoted(input string, vars map[string][]string) (string, int) {
} }
// Split a string on whitespace taking into account escaping and quoting.
//func splitQuoted(input string) []string {
//parts := make([]string, 0)
//var i, j int
//i = 0
//for {
//// skip all unescaped whitespace
//for i < len(input) {
//c, w := utf8.DecodeRuneInString(input[i:])
//if strings.IndexRune(" \t", c) < 0 {
//break
//}
//i += w
//}
//if i >= len(input) {
//break
//}
//// Ugh. Will this take into account quoting in variables?
//switch c {
//case '"':
//case '\'':
//default:
//}
//}
//return parts
//}

View file

@ -269,9 +269,10 @@ func parseRecipe(p *parser, t token) parserStateFun {
// rule has attributes // rule has attributes
if j < len(p.tokenbuf) { if j < len(p.tokenbuf) {
attribs := make([]string, j-i-1) attribs := make([]string, 0)
for k := i + 1; k < j; k++ { for k := i + 1; k < j; k++ {
attribs[k-i-1] = expand(p.tokenbuf[k].val, p.rules.vars, true) exparts := expand(p.tokenbuf[k].val, p.rules.vars, true)
attribs = append(attribs, exparts...)
} }
err := r.parseAttribs(attribs) err := r.parseAttribs(attribs)
if err != nil { if err != nil {
@ -287,10 +288,12 @@ func parseRecipe(p *parser, t token) parserStateFun {
} }
// targets // targets
r.targets = make([]pattern, i) r.targets = make([]pattern, 0)
for k := 0; k < i; k++ { for k := 0; k < i; k++ {
targetstr := expand(p.tokenbuf[k].val, p.rules.vars, true) exparts := expand(p.tokenbuf[k].val, p.rules.vars, true)
r.targets[k].spat = targetstr for i := range exparts {
targetstr := exparts[i]
r.targets = append(r.targets, pattern{spat: targetstr})
if r.attributes.regex { if r.attributes.regex {
rpat, err := regexp.Compile(targetstr) rpat, err := regexp.Compile(targetstr)
@ -298,7 +301,7 @@ func parseRecipe(p *parser, t token) parserStateFun {
msg := fmt.Sprintf("invalid regular expression: %q", err) msg := fmt.Sprintf("invalid regular expression: %q", err)
p.basicErrorAtToken(msg, p.tokenbuf[k]) p.basicErrorAtToken(msg, p.tokenbuf[k])
} }
r.targets[k].rpat = rpat r.targets[len(r.targets)-1].rpat = rpat
} else { } else {
idx := strings.IndexRune(targetstr, '%') idx := strings.IndexRune(targetstr, '%')
if idx >= 0 { if idx >= 0 {
@ -316,17 +319,19 @@ func parseRecipe(p *parser, t token) parserStateFun {
msg := fmt.Sprintf("error compiling suffix rule. This is a bug.", err) msg := fmt.Sprintf("error compiling suffix rule. This is a bug.", err)
p.basicErrorAtToken(msg, p.tokenbuf[k]) p.basicErrorAtToken(msg, p.tokenbuf[k])
} }
r.targets[k].rpat = rpat r.targets[len(r.targets)-1].rpat = rpat
r.targets[k].issuffix = true r.targets[len(r.targets)-1].issuffix = true
r.ismeta = true r.ismeta = true
} }
} }
} }
}
// prereqs // prereqs
r.prereqs = make([]string, len(p.tokenbuf)-j-1) r.prereqs = make([]string, 0)
for k := j + 1; k < len(p.tokenbuf); k++ { for k := j + 1; k < len(p.tokenbuf); k++ {
r.prereqs[k-j-1] = expand(p.tokenbuf[k].val, p.rules.vars, true) exparts := expand(p.tokenbuf[k].val, p.rules.vars, true)
r.prereqs = append(r.prereqs, exparts...)
} }
if t.typ == tokenRecipe { if t.typ == tokenRecipe {

View file

@ -173,12 +173,14 @@ func (rs *ruleSet) executeAssignment(ts []token) *assignmentError {
ts[0]} ts[0]}
} }
// expanded variables // expanded variables
vals := make([]string, len(ts)-1) vals := make([]string, 0)
for i := 0; i < len(vals); i++ { for i := 1; i < len(ts); i++ {
vals[i] = expand(ts[i+1].val, rs.vars, true) vals = append(vals, expand(ts[i].val, rs.vars, true)...)
} }
rs.vars[assignee] = vals rs.vars[assignee] = vals
return nil return nil
} }