config: parser fixes and application
This commit is contained in:
parent
bffdcbaede
commit
4f57437144
|
@ -35,7 +35,7 @@ import (
|
|||
top:
|
||||
expr
|
||||
{
|
||||
fmt.Printf("%#v", $1)
|
||||
exprResult = $1
|
||||
}
|
||||
|
||||
expr:
|
||||
|
@ -49,7 +49,13 @@ expr:
|
|||
}
|
||||
| IDENTIFIER LEFTPAREN args RIGHTPAREN
|
||||
{
|
||||
$$ = &FunctionInterpolation{Func: $1, Args: $3}
|
||||
f, ok := Funcs[$1]
|
||||
if !ok {
|
||||
exprErrors = append(exprErrors, fmt.Errorf(
|
||||
"Unknown function: %s", $1))
|
||||
}
|
||||
|
||||
$$ = &FunctionInterpolation{Func: f, Args: $3}
|
||||
}
|
||||
|
||||
args:
|
||||
|
|
|
@ -58,6 +58,7 @@ func (x *exprLex) lexId(yylval *exprSymType) int {
|
|||
// If this isn't a character we want in an ID, return out.
|
||||
// One day we should make this a regexp.
|
||||
if c != '_' &&
|
||||
c != '-' &&
|
||||
c != '.' &&
|
||||
c != '*' &&
|
||||
!unicode.IsLetter(c) &&
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/terraform/helper/multierror"
|
||||
)
|
||||
|
||||
// exprErrors are the errors built up from parsing. These should not
|
||||
// be accessed directly.
|
||||
var exprErrors []error
|
||||
var exprLock sync.Mutex
|
||||
var exprResult Interpolation
|
||||
|
||||
// ExprParse parses the given expression and returns an executable
|
||||
// Interpolation.
|
||||
func ExprParse(v string) (Interpolation, error) {
|
||||
exprLock.Lock()
|
||||
defer exprLock.Unlock()
|
||||
exprErrors = nil
|
||||
exprResult = nil
|
||||
|
||||
// Parse
|
||||
exprParse(&exprLex{input: v})
|
||||
|
||||
// Build up the errors
|
||||
var err error
|
||||
if len(exprErrors) > 0 {
|
||||
err = &multierror.Error{Errors: exprErrors}
|
||||
exprResult = nil
|
||||
}
|
||||
|
||||
return exprResult, err
|
||||
}
|
|
@ -17,7 +17,6 @@ var funcRegexp *regexp.Regexp = regexp.MustCompile(
|
|||
// Interpolations might be simple variable references, or it might be
|
||||
// function calls, or even nested function calls.
|
||||
type Interpolation interface {
|
||||
FullString() string
|
||||
Interpolate(map[string]string) (string, error)
|
||||
Variables() map[string]InterpolatedVariable
|
||||
}
|
||||
|
@ -37,26 +36,20 @@ type InterpolatedVariable interface {
|
|||
// FunctionInterpolation is an Interpolation that executes a function
|
||||
// with some variable number of arguments to generate a value.
|
||||
type FunctionInterpolation struct {
|
||||
Func string
|
||||
Func InterpolationFunc
|
||||
Args []Interpolation
|
||||
|
||||
key string
|
||||
}
|
||||
|
||||
// LiteralInterpolation implements Interpolation for literals. Ex:
|
||||
// ${"foo"} will equal "foo".
|
||||
type LiteralInterpolation struct {
|
||||
Literal string
|
||||
|
||||
key string
|
||||
}
|
||||
|
||||
// VariableInterpolation implements Interpolation for simple variable
|
||||
// interpolation. Ex: "${var.foo}" or "${aws_instance.foo.bar}"
|
||||
type VariableInterpolation struct {
|
||||
Variable InterpolatedVariable
|
||||
|
||||
key string
|
||||
}
|
||||
|
||||
// A ResourceVariable is a variable that is referencing the field
|
||||
|
@ -82,61 +75,6 @@ type UserVariable struct {
|
|||
key string
|
||||
}
|
||||
|
||||
// NewInterpolation takes some string and returns the valid
|
||||
// Interpolation associated with it, or error if a valid
|
||||
// interpolation could not be found or the interpolation itself
|
||||
// is invalid.
|
||||
func NewInterpolation(v string) (Interpolation, error) {
|
||||
match := funcRegexp.FindStringSubmatch(v)
|
||||
if match != nil {
|
||||
fn, ok := Funcs[match[1]]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(
|
||||
"%s: Unknown function '%s'",
|
||||
v, match[1])
|
||||
}
|
||||
|
||||
args := make([]InterpolatedVariable, 0, len(match)-2)
|
||||
for i := 2; i < len(match); i++ {
|
||||
// This can be empty if we have a single argument
|
||||
// due to the format of the regexp.
|
||||
if match[i] == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
v, err := NewInterpolatedVariable(match[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args = append(args, v)
|
||||
}
|
||||
|
||||
return &FunctionInterpolation{
|
||||
Func: fn,
|
||||
Args: args,
|
||||
|
||||
key: v,
|
||||
}, nil
|
||||
}
|
||||
|
||||
if idx := strings.Index(v, "."); idx >= 0 {
|
||||
v, err := NewInterpolatedVariable(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &VariableInterpolation{
|
||||
Variable: v,
|
||||
key: v.FullKey(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf(
|
||||
"Interpolation '%s' is not a valid interpolation. " +
|
||||
"Please check your syntax and try again.")
|
||||
}
|
||||
|
||||
func NewInterpolatedVariable(v string) (InterpolatedVariable, error) {
|
||||
if !strings.HasPrefix(v, "var.") {
|
||||
return NewResourceVariable(v)
|
||||
|
@ -145,21 +83,13 @@ func NewInterpolatedVariable(v string) (InterpolatedVariable, error) {
|
|||
return NewUserVariable(v)
|
||||
}
|
||||
|
||||
func (i *FunctionInterpolation) FullString() string {
|
||||
return i.key
|
||||
}
|
||||
|
||||
func (i *FunctionInterpolation) Interpolate(
|
||||
vs map[string]string) (string, error) {
|
||||
args := make([]string, len(i.Args))
|
||||
for idx, a := range i.Args {
|
||||
k := a.FullKey()
|
||||
v, ok := vs[k]
|
||||
if !ok {
|
||||
return "", fmt.Errorf(
|
||||
"%s: variable argument value unknown: %s",
|
||||
i.FullString(),
|
||||
k)
|
||||
v, err := a.Interpolate(vs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
args[idx] = v
|
||||
|
@ -171,21 +101,14 @@ func (i *FunctionInterpolation) Interpolate(
|
|||
func (i *FunctionInterpolation) Variables() map[string]InterpolatedVariable {
|
||||
result := make(map[string]InterpolatedVariable)
|
||||
for _, a := range i.Args {
|
||||
k := a.FullKey()
|
||||
if _, ok := result[k]; ok {
|
||||
continue
|
||||
for k, v := range a.Variables() {
|
||||
result[k] = v
|
||||
}
|
||||
|
||||
result[k] = a
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (i *LiteralInterpolation) FullString() string {
|
||||
return i.key
|
||||
}
|
||||
|
||||
func (i *LiteralInterpolation) Interpolate(
|
||||
map[string]string) (string, error) {
|
||||
return i.Literal, nil
|
||||
|
@ -195,24 +118,20 @@ func (i *LiteralInterpolation) Variables() map[string]InterpolatedVariable {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (i *VariableInterpolation) FullString() string {
|
||||
return i.key
|
||||
}
|
||||
|
||||
func (i *VariableInterpolation) Interpolate(
|
||||
vs map[string]string) (string, error) {
|
||||
v, ok := vs[i.key]
|
||||
v, ok := vs[i.Variable.FullKey()]
|
||||
if !ok {
|
||||
return "", fmt.Errorf(
|
||||
"%s: value for variable not found",
|
||||
i.key)
|
||||
i.Variable.FullKey())
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (i *VariableInterpolation) Variables() map[string]InterpolatedVariable {
|
||||
return map[string]InterpolatedVariable{i.key: i.Variable}
|
||||
return map[string]InterpolatedVariable{i.Variable.FullKey(): i.Variable}
|
||||
}
|
||||
|
||||
func NewResourceVariable(key string) (*ResourceVariable, error) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
/*
|
||||
func TestNewInterpolation(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
|
@ -67,6 +68,7 @@ func TestNewInterpolation(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func TestNewInterpolatedVariable(t *testing.T) {
|
||||
cases := []struct {
|
||||
|
@ -171,11 +173,10 @@ func TestFunctionInterpolation(t *testing.T) {
|
|||
|
||||
i := &FunctionInterpolation{
|
||||
Func: fn,
|
||||
Args: []InterpolatedVariable{v1, v2},
|
||||
key: "foo",
|
||||
}
|
||||
if i.FullString() != "foo" {
|
||||
t.Fatalf("err: %#v", i)
|
||||
Args: []Interpolation{
|
||||
&VariableInterpolation{Variable: v1},
|
||||
&VariableInterpolation{Variable: v2},
|
||||
},
|
||||
}
|
||||
|
||||
expected := map[string]InterpolatedVariable{
|
||||
|
@ -206,10 +207,6 @@ func TestLiteralInterpolation_impl(t *testing.T) {
|
|||
func TestLiteralInterpolation(t *testing.T) {
|
||||
i := &LiteralInterpolation{
|
||||
Literal: "bar",
|
||||
key: "foo",
|
||||
}
|
||||
if i.FullString() != "foo" {
|
||||
t.Fatalf("err: %#v", i)
|
||||
}
|
||||
|
||||
if i.Variables() != nil {
|
||||
|
@ -292,10 +289,7 @@ func TestVariableInterpolation(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
i := &VariableInterpolation{Variable: uv, key: "var.foo"}
|
||||
if i.FullString() != "var.foo" {
|
||||
t.Fatalf("err: %#v", i)
|
||||
}
|
||||
i := &VariableInterpolation{Variable: uv}
|
||||
|
||||
expected := map[string]InterpolatedVariable{"var.foo": uv}
|
||||
if !reflect.DeepEqual(i.Variables(), expected) {
|
||||
|
@ -320,7 +314,7 @@ func TestVariableInterpolation_missing(t *testing.T) {
|
|||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
i := &VariableInterpolation{Variable: uv, key: "var.foo"}
|
||||
i := &VariableInterpolation{Variable: uv}
|
||||
_, err = i.Interpolate(map[string]string{
|
||||
"var.bar": "bar",
|
||||
})
|
||||
|
|
|
@ -95,7 +95,7 @@ func (w *interpolationWalker) Primitive(v reflect.Value) error {
|
|||
// Interpolation found, instantiate it
|
||||
key := match[2]
|
||||
|
||||
i, err := NewInterpolation(key)
|
||||
i, err := ExprParse(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ func TestInterpolationWalker_detect(t *testing.T) {
|
|||
Name: "foo",
|
||||
key: "var.foo",
|
||||
},
|
||||
key: "var.foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -41,13 +40,14 @@ func TestInterpolationWalker_detect(t *testing.T) {
|
|||
Result: []Interpolation{
|
||||
&FunctionInterpolation{
|
||||
Func: nil,
|
||||
Args: []InterpolatedVariable{
|
||||
&UserVariable{
|
||||
Name: "foo",
|
||||
key: "var.foo",
|
||||
Args: []Interpolation{
|
||||
&VariableInterpolation{
|
||||
Variable: &UserVariable{
|
||||
Name: "foo",
|
||||
key: "var.foo",
|
||||
},
|
||||
},
|
||||
},
|
||||
key: "lookup(var.foo)",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue