Support for namelists. Fixes #3.

This commit is contained in:
Daniel Jones 2014-08-04 12:30:57 -07:00
parent e343ba3689
commit 8cb7110099
2 changed files with 46 additions and 2 deletions

View file

@ -3,6 +3,7 @@
package main package main
import ( import (
"regexp"
"strings" "strings"
"unicode/utf8" "unicode/utf8"
) )
@ -136,18 +137,45 @@ 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
var namelist_pattern = regexp.MustCompile(`^\s*([^:]+)\s*:\s*([^%]*)%([^=]*)\s*=\s*([^%]*)%([^%]*)\s*`)
// escaping of "$" with "$$" // escaping of "$" with "$$"
if c == '$' { if c == '$' {
return []string{"$"}, 2 return []string{"$"}, 2
// match bracketed expansions: ${foo}, or ${foo:a%b=c%d}
} else if c == '{' { } else if c == '{' {
j := strings.IndexRune(input[w:], '}') j := strings.IndexRune(input[w:], '}')
if j < 0 { if j < 0 {
return []string{"$" + input}, len(input) return []string{"$" + input}, len(input)
} }
varname = input[w : w+j] varname = input[w : w+j]
offset = w + j + 1 offset = w + j + 1
// is this a namelist?
mat := namelist_pattern.FindStringSubmatch(varname)
if mat != nil && isValidVarName(mat[1]) {
// ${varname:a%b=c%d}
varname = mat[1]
a, b, c, d := mat[2], mat[3], mat[4], mat[5]
values, ok := vars[varname]
if !ok {
return []string{}, offset
}
pat := regexp.MustCompile(strings.Join([]string{`^\Q`, a, `\E(.*)\Q`, b, `\E$`}, ""))
expanded_values := make([]string, len(values))
for i, value := range values {
value_match := pat.FindStringSubmatch(value)
if value_match != nil {
expanded_values[i] = strings.Join([]string{c, value_match[1], d}, "")
} else {
expanded_values[i] = value
}
}
return expanded_values, offset
}
// bare variables: $foo
} else { } else {
// try to match a variable name // try to match a variable name
i := 0 i := 0

18
lex.go
View file

@ -11,7 +11,7 @@ type tokenType int
const eof rune = '\000' const eof rune = '\000'
// Rune's that cannot be part of a bare (unquoted) string. // Rune's that cannot be part of a bare (unquoted) string.
const nonBareRunes = " \t\n\r\\=:#'\"" const nonBareRunes = " \t\n\r\\=:#'\"$"
// Return true if the string contains whitespace only. // Return true if the string contains whitespace only.
func onlyWhitespace(s string) bool { func onlyWhitespace(s string) bool {
@ -383,6 +383,14 @@ func lexBareWord(l *lexer) lexerStateFun {
l.next() l.next()
return lexBareWord return lexBareWord
} }
} else if c == '$' {
c1 := l.peekN(1)
if c1 == '{' {
return lexBracketExpansion
} else {
l.next()
return lexBareWord
}
} }
if l.start < l.pos { if l.start < l.pos {
@ -391,3 +399,11 @@ func lexBareWord(l *lexer) lexerStateFun {
return lexTopLevel return lexTopLevel
} }
func lexBracketExpansion(l *lexer) lexerStateFun {
l.next() // '$'
l.next() // '{'
l.acceptUntil("}")
l.next() // '}'
return lexBareWord
}