Fix issues with variable expansion.
This commit is contained in:
parent
207e052372
commit
115d8425be
3 changed files with 125 additions and 53 deletions
103
expand.go
103
expand.go
|
|
@ -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
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
67
parse.go
67
parse.go
|
|
@ -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,46 +288,50 @@ 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)
|
||||||
if err != nil {
|
|
||||||
msg := fmt.Sprintf("invalid regular expression: %q", err)
|
|
||||||
p.basicErrorAtToken(msg, p.tokenbuf[k])
|
|
||||||
}
|
|
||||||
r.targets[k].rpat = rpat
|
|
||||||
} else {
|
|
||||||
idx := strings.IndexRune(targetstr, '%')
|
|
||||||
if idx >= 0 {
|
|
||||||
var left, right string
|
|
||||||
if idx > 0 {
|
|
||||||
left = regexp.QuoteMeta(targetstr[:idx])
|
|
||||||
}
|
|
||||||
if idx < len(targetstr) - 1 {
|
|
||||||
right = regexp.QuoteMeta(targetstr[idx+1:])
|
|
||||||
}
|
|
||||||
|
|
||||||
patstr := fmt.Sprintf("^%s(.*)%s$", left, right)
|
|
||||||
rpat, err := regexp.Compile(patstr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("error compiling suffix rule. This is a bug.", 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
|
||||||
r.targets[k].issuffix = true
|
} else {
|
||||||
r.ismeta = true
|
idx := strings.IndexRune(targetstr, '%')
|
||||||
|
if idx >= 0 {
|
||||||
|
var left, right string
|
||||||
|
if idx > 0 {
|
||||||
|
left = regexp.QuoteMeta(targetstr[:idx])
|
||||||
|
}
|
||||||
|
if idx < len(targetstr) - 1 {
|
||||||
|
right = regexp.QuoteMeta(targetstr[idx+1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
patstr := fmt.Sprintf("^%s(.*)%s$", left, right)
|
||||||
|
rpat, err := regexp.Compile(patstr)
|
||||||
|
if err != nil {
|
||||||
|
msg := fmt.Sprintf("error compiling suffix rule. This is a bug.", err)
|
||||||
|
p.basicErrorAtToken(msg, p.tokenbuf[k])
|
||||||
|
}
|
||||||
|
r.targets[len(r.targets)-1].rpat = rpat
|
||||||
|
r.targets[len(r.targets)-1].issuffix = 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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue