From abb16de8f121e448295f0795143012fcab32700b Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 8 Jun 2016 09:53:26 +0200 Subject: [PATCH 1/6] support simple expressions in name field of lists --- flow/cascade_test.go | 28 ++++++++++++++++++++++++++++ flow/flow.go | 28 +++++++++++++++++++++++----- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/flow/cascade_test.go b/flow/cascade_test.go index 8fdb6c6..ce0b607 100644 --- a/flow/cascade_test.go +++ b/flow/cascade_test.go @@ -625,4 +625,32 @@ peter: 26 Expect(source).To(CascadeAs(resolved, stub1, stub2)) }) }) + + Describe("lists with key expressions", func() { + It("merges appropriate list entry", func() { + source := parseYAML(` +--- +name: foobar +list: + - name: (( .name )) + value: alice +`) + stub := parseYAML(` +--- +list: + - name: foo + value: peter + - name: foobar + value: bob +`) + resolved := parseYAML(` +--- +name: foobar +list: + - name: foobar + value: bob +`) + Expect(source).To(CascadeAs(resolved, stub)) + }) + }) }) diff --git a/flow/flow.go b/flow/flow.go index 0cb7e64..219bb07 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -299,9 +299,11 @@ func flowList(root yaml.Node, env dynaml.Binding) yaml.Node { env = env.RedirectOverwrite(redirectPath) } for idx, val := range merged.([]yaml.Node) { - step := stepName(idx, val, keyName) + step, resolved := stepName(idx, val, keyName, env) debug.Debug(" step %s\n", step) - val = flow(val, env.WithPath(step), false) + if resolved { + val = flow(val, env.WithPath(step), false) + } if !val.Undefined() { newList = append(newList, val) } @@ -345,16 +347,32 @@ func flowString(root yaml.Node, env dynaml.Binding) yaml.Node { return yaml.SubstituteNode(expr, root) } -func stepName(index int, value yaml.Node, keyName string) string { +func stepName(index int, value yaml.Node, keyName string, env dynaml.Binding) (string, bool) { if keyName == "" { keyName = "name" } name, ok := yaml.FindString(value, keyName) if ok { - return keyName + ":" + name + return keyName + ":" + name, true } - return fmt.Sprintf("[%d]", index) + step := fmt.Sprintf("[%d]", index) + v, ok := yaml.FindR(true, value, keyName) + if ok && v.Value() != nil { + _, ok := v.Value().(dynaml.Expression) + if ok { + v = flow(v, env.WithPath(step), false) + _, ok := v.Value().(dynaml.Expression) + if ok { + return step, false + } + name, ok = v.Value().(string) + if ok { + return keyName + ":" + name, true + } + } + } + return step, true } func processMerges(orig yaml.Node, root []yaml.Node, env dynaml.Binding) (interface{}, bool, bool, []string, string, bool) { From 96de3a7094e526d31622e1b17a5f7026d2f26959 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Wed, 8 Jun 2016 10:50:05 +0200 Subject: [PATCH 2/6] fix explicit merge handling for named list entries --- flow/cascade_test.go | 28 ----------------------- flow/flow.go | 13 ++++++----- flow/flow_test.go | 53 ++++++++++++++++++++++++++++++++++++++++++++ yaml/find.go | 3 +++ 4 files changed, 64 insertions(+), 33 deletions(-) diff --git a/flow/cascade_test.go b/flow/cascade_test.go index ce0b607..8fdb6c6 100644 --- a/flow/cascade_test.go +++ b/flow/cascade_test.go @@ -625,32 +625,4 @@ peter: 26 Expect(source).To(CascadeAs(resolved, stub1, stub2)) }) }) - - Describe("lists with key expressions", func() { - It("merges appropriate list entry", func() { - source := parseYAML(` ---- -name: foobar -list: - - name: (( .name )) - value: alice -`) - stub := parseYAML(` ---- -list: - - name: foo - value: peter - - name: foobar - value: bob -`) - resolved := parseYAML(` ---- -name: foobar -list: - - name: foobar - value: bob -`) - Expect(source).To(CascadeAs(resolved, stub)) - }) - }) }) diff --git a/flow/flow.go b/flow/flow.go index 219bb07..349333f 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -293,7 +293,7 @@ func flowList(root yaml.Node, env dynaml.Binding) yaml.Node { merged, process, replaced, redirectPath, keyName, temporary := processMerges(root, rootList, env) if process { - + debug.Debug("process list (key: %s) %v\n", keyName, env.Path()) newList := []yaml.Node{} if len(redirectPath) > 0 { env = env.RedirectOverwrite(redirectPath) @@ -359,6 +359,7 @@ func stepName(index int, value yaml.Node, keyName string, env dynaml.Binding) (s step := fmt.Sprintf("[%d]", index) v, ok := yaml.FindR(true, value, keyName) if ok && v.Value() != nil { + debug.Debug("found raw %s", keyName) _, ok := v.Value().(dynaml.Expression) if ok { v = flow(v, env.WithPath(step), false) @@ -366,11 +367,13 @@ func stepName(index int, value yaml.Node, keyName string, env dynaml.Binding) (s if ok { return step, false } - name, ok = v.Value().(string) - if ok { - return keyName + ":" + name, true - } } + name, ok = v.Value().(string) + if ok { + return keyName + ":" + name, true + } + } else { + debug.Debug("raw %s not found", keyName) } return step, true } diff --git a/flow/flow_test.go b/flow/flow_test.go index 3d2b44e..2ddf2a4 100644 --- a/flow/flow_test.go +++ b/flow/flow_test.go @@ -1703,6 +1703,59 @@ properties: Expect(source).To(FlowAs(resolved, stub)) }) + + It("merges appropriate list entry for lists with explicitly merged maps", func() { + source := parseYAML(` +--- +list: + - name: alice + married: bob + <<: (( merge )) +`) + stub := parseYAML(` +--- +list: + - name: mary + married: no + - name: alice + married: peter + age: 25 +`) + resolved := parseYAML(` +--- +list: + - name: alice + married: peter + age: 25 +`) + Expect(source).To(FlowAs(resolved, stub)) + }) + + It("merges appropriate list entry for lists with key expressions", func() { + source := parseYAML(` +--- +name: foobar +list: + - name: (( .name )) + value: alice +`) + stub := parseYAML(` +--- +list: + - name: foo + value: peter + - name: foobar + value: bob +`) + resolved := parseYAML(` +--- +name: foobar +list: + - name: foobar + value: bob +`) + Expect(source).To(FlowAs(resolved, stub)) + }) }) Describe("for list expressions", func() { diff --git a/yaml/find.go b/yaml/find.go index c6cd07e..2fff42c 100644 --- a/yaml/find.go +++ b/yaml/find.go @@ -4,6 +4,8 @@ import ( "regexp" "strconv" "strings" + + "github.com/cloudfoundry-incubator/spiff/debug" ) var listIndex = regexp.MustCompile(`^\[(\d+)\]$`) @@ -38,6 +40,7 @@ func FindString(root Node, path ...string) (string, bool) { func FindStringR(raw bool, root Node, path ...string) (string, bool) { node, ok := FindR(raw, root, path...) if !ok { + debug.Debug("%v not found", path) return "", false } From 25dcf7037ac78049795e14a44e49971e193ad894 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Thu, 9 Jun 2016 22:22:42 +0200 Subject: [PATCH 3/6] string functions and valid --- README.md | 100 ++++++++++++ dynaml/call.go | 11 ++ dynaml/contains.go | 41 +++-- dynaml/defined.go | 21 +++ dynaml/index.go | 56 +++++++ dynaml/replace.go | 39 +++++ flow/flow_test.go | 370 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 624 insertions(+), 14 deletions(-) create mode 100644 dynaml/index.go create mode 100644 dynaml/replace.go diff --git a/README.md b/README.md index bf60aa4..1288967 100644 --- a/README.md +++ b/README.md @@ -54,9 +54,13 @@ Contents: - [(( trim(string) ))](#-trimstring-) - [(( uniq(list) ))](#-uniqlist-) - [(( contains(list, "foobar") ))](#-containslist-foobar-) + - [(( index(list, "foobar") ))](#-indexlist-foobar-) + - [(( lastindex(list, "foobar") ))](#-lastindexlist-foobar-) + - [(( replace(string, "foo", "bar") ))](#-replacestring-foo-bar-) - [(( match("(f.*)(b.*)", "xxxfoobar") ))](#-matchfb-xxxfoobar-) - [(( length(list) ))](#-lengthlist-) - [(( defined(foobar) ))](#-definedfoobar-) + - [(( valid(foobar) ))](#-validfoobar-) - [(( require(foobar) ))](#-requirefoobar-) - [(( exec( "command", arg1, arg2) ))](#-exec-command-arg1-arg2-) - [(( eval( foo "." bar ) ))](#-eval-foo--bar--) @@ -930,6 +934,68 @@ list: contains: true ``` +The function `contains` also works on strings to look for sub strings. + +e.g.: + +```yaml +contains: (( contains("foobar", "bar") )) +``` + +yields `true`. + +### `(( index(list, "foobar") ))` + +Checks whether a list contains a dedicated value and returns the index of the first match. +Values might also be lists or maps. If no entry could be found `-1` is returned. + +e.g.: + +```yaml +list: + - foo + - bar + - foobar +index: (( index(list, "foobar") )) +``` + +yields: + +```yaml +list: + - foo + - bar + - foobar +index: 2 +``` + +The function `index` also works on strings to look for sub strings. + +e.g.: + +```yaml +index: (( index("foobar", "bar") )) +``` + +yields `3`. + +### `(( lastindex(list, "foobar") ))` + +The function `lastindex` works like [`index`](#-indexlist-foobar-) but the index of the last occurence is returned. + +### `(( replace(string, "foo", "bar") ))` + +Replace all occurences of a sub string in a string by a replacement string. With an optional +fourth integer argument the number of substitutions can be limited (-1 mean unlimited). + +e.g.: + +```yaml +string: (( replace("foobar", "o", "u") )) +``` + +yields `fuubar`. + ### `(( match("(f.*)(b.*)", "xxxfoobar") ))` Returns the match of a regular expression for a given string value. The match is a list of the matched values for the sub expressions contained in the regular expression. Index 0 refers to the match of the complete regular expression. If the string value does not match an empty list is returned. @@ -995,6 +1061,40 @@ null_def: false This function can be used in combination of the [conditional operator](#-a--1--foo-bar-) to evaluate expressions depending on the resolvability of another expression. +### `(( valid(foobar) ))` + +The function `valid` checks whether an expression can successfully be evaluated and evaluates to a defined value not equals to `nil`. It yields the boolean value `true`, if the expression can be evaluated, and `false` otherwise. + +e.g.: + +```yaml +zero: 0 +empty: +map: {} +list: [] +div_ok: (( valid(1 / zero ) )) +zero_def: (( valid( zero ) )) +null_def: (( valid( ~ ) )) +empty_def: (( valid( empty ) )) +map_def: (( valid( map ) )) +list_def: (( valid( list ) )) +``` + +evaluates to + +```yaml +zero: 0 +empty: null +map: {} +list: [] +div_ok: false +zero_def: true +null_def: false +empty_def: false +map_def: true +list_def: true +``` + ### `(( require(foobar) ))` The function `require` yields an error if the given argument is undefined or `nil`, otherwise it yields the given value. diff --git a/dynaml/call.go b/dynaml/call.go index b8f7a92..4bbb6f8 100644 --- a/dynaml/call.go +++ b/dynaml/call.go @@ -42,6 +42,8 @@ func (e CallExpr) Evaluate(binding Binding, locally bool) (interface{}, Evaluati return e.defined(binding) case "require": return e.require(binding) + case "valid": + return e.valid(binding) } values, info, ok := ResolveExpressionListOrPushEvaluation(&e.Arguments, &resolved, nil, binding, false) @@ -84,6 +86,15 @@ func (e CallExpr) Evaluate(binding Binding, locally bool) (interface{}, Evaluati case "contains": result, sub, ok = func_contains(values, binding) + case "index": + result, sub, ok = func_index(values, binding) + + case "lastindex": + result, sub, ok = func_lastindex(values, binding) + + case "replace": + result, sub, ok = func_replace(values, binding) + case "match": result, sub, ok = func_match(values, binding) diff --git a/dynaml/contains.go b/dynaml/contains.go index 3846e12..c5bd5de 100644 --- a/dynaml/contains.go +++ b/dynaml/contains.go @@ -2,31 +2,44 @@ package dynaml import ( "github.com/cloudfoundry-incubator/spiff/yaml" + "strconv" + "strings" ) func func_contains(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { info := DefaultInfo() if len(arguments) != 2 { - return info.Error("contains takes exactly two arguments") + return info.Error("function contains takes exactly two arguments") } - list, ok := arguments[0].([]yaml.Node) - if !ok { - return info.Error("list expected for argument one of function contains required") - } - - if arguments[1] == nil { - return false, info, true - } + switch val := arguments[0].(type) { + case []yaml.Node: + if arguments[1] == nil { + return false, info, true + } - elem := arguments[1] + elem := arguments[1] - for _, v := range list { - r, _, _ := compareEquals(v.Value(), elem) - if r { - return true, info, true + for _, v := range val { + r, _, _ := compareEquals(v.Value(), elem) + if r { + return true, info, true + } + } + case string: + switch elem := arguments[1].(type) { + case string: + return strings.Contains(val, elem), info, true + case int64: + return strings.Contains(val, strconv.FormatInt(elem, 10)), info, true + case bool: + return strings.Contains(val, strconv.FormatBool(elem)), info, true + default: + return info.Error("invalid type for check string") } + default: + return info.Error("list or string expected for argument one of function contains") } return false, info, true } diff --git a/dynaml/defined.go b/dynaml/defined.go index 5d70693..f6070fd 100644 --- a/dynaml/defined.go +++ b/dynaml/defined.go @@ -1,5 +1,26 @@ package dynaml +func (e CallExpr) valid(binding Binding) (interface{}, EvaluationInfo, bool) { + pushed := make([]Expression, len(e.Arguments)) + ok := true + resolved := true + valid := true + var val interface{} + + copy(pushed, e.Arguments) + for i, _ := range pushed { + val, _, ok = ResolveExpressionOrPushEvaluation(&pushed[i], &resolved, nil, binding, true) + if resolved && !ok { + return false, DefaultInfo(), true + } + valid = valid && (val != nil) + } + if !resolved { + return e, DefaultInfo(), true + } + return valid, DefaultInfo(), ok +} + func (e CallExpr) defined(binding Binding) (interface{}, EvaluationInfo, bool) { pushed := make([]Expression, len(e.Arguments)) ok := true diff --git a/dynaml/index.go b/dynaml/index.go new file mode 100644 index 0000000..597a942 --- /dev/null +++ b/dynaml/index.go @@ -0,0 +1,56 @@ +package dynaml + +import ( + "github.com/cloudfoundry-incubator/spiff/yaml" + "strconv" + "strings" +) + +func func_index(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { + return _index(true, strings.Index, arguments, binding) +} +func func_lastindex(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { + return _index(false, strings.LastIndex, arguments, binding) +} + +func _index(first bool, f func(string, string) int, arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { + info := DefaultInfo() + found := int64(-1) + + if len(arguments) != 2 { + return info.Error("function index takes exactly two arguments") + } + + switch val := arguments[0].(type) { + case []yaml.Node: + if arguments[1] == nil { + return -1, info, true + } + + elem := arguments[1] + + for i, v := range val { + r, _, _ := compareEquals(v.Value(), elem) + if r { + found = int64(i) + if first { + break + } + } + } + case string: + switch elem := arguments[1].(type) { + case string: + return int64(f(val, elem)), info, true + case int64: + return int64(f(val, strconv.FormatInt(elem, 10))), info, true + case bool: + return int64(f(val, strconv.FormatBool(elem))), info, true + default: + return info.Error("invalid type for check string") + } + default: + return info.Error("list or string expected for argument one of function index") + } + return int64(found), info, true +} diff --git a/dynaml/replace.go b/dynaml/replace.go new file mode 100644 index 0000000..67b4f73 --- /dev/null +++ b/dynaml/replace.go @@ -0,0 +1,39 @@ +package dynaml + +import ( + "strings" +) + +func func_replace(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { + info := DefaultInfo() + + if len(arguments) < 3 { + return info.Error("replace requires at least 3 arguments") + } + if len(arguments) > 4 { + return info.Error("replace does not take more than 4 arguments") + } + + str, ok := arguments[0].(string) + if !ok { + return info.Error("first argument for replace must be a string") + } + src, ok := arguments[1].(string) + if !ok { + return info.Error("second argument for replace must be a string") + } + dst, ok := arguments[2].(string) + if !ok { + return info.Error("third argument for replace must be a string") + } + n := int64(-1) + if len(arguments) > 3 { + n, ok = arguments[3].(int64) + if !ok { + return info.Error("fourth argument for replace must be an integer") + } + } + + e := strings.Replace(str, src, dst, int(n)) + return e, info, true +} diff --git a/flow/flow_test.go b/flow/flow_test.go index 2ddf2a4..719a210 100644 --- a/flow/flow_test.go +++ b/flow/flow_test.go @@ -2848,6 +2848,288 @@ list: - c contains: false +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles string contains", func() { + source := parseYAML(` +--- +contains: (( contains("1234567890123", "0") )) +`) + resolved := parseYAML(` +--- +contains: true +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles string contains with int", func() { + source := parseYAML(` +--- +contains: (( contains("1234567890123", 0) )) +`) + resolved := parseYAML(` +--- +contains: true +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles string contains and fails", func() { + source := parseYAML(` +--- +contains: (( contains("1234567890123", "a") )) +`) + resolved := parseYAML(` +--- +contains: false +`) + Expect(source).To(FlowAs(resolved)) + }) + }) + + Describe("when calling index", func() { + It("finds ints", func() { + source := parseYAML(` +--- +list: +- a +- b +- 0 +- c +- 0 +index: (( index(list, "0") )) +`) + resolved := parseYAML(` +--- +list: +- a +- b +- 0 +- c +- 0 + +index: 2 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("finds string", func() { + source := parseYAML(` +--- +list: +- a +- b +- "0" +- c +- "0" +index: (( index(list, "0") )) +`) + resolved := parseYAML(` +--- +list: +- a +- b +- "0" +- c +- "0" +index: 2 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("works for no match", func() { + source := parseYAML(` +--- +list: +- a +- b +- 0 +- c +index: (( index(list, "d") )) +`) + resolved := parseYAML(` +--- +list: +- a +- b +- 0 +- c + +index: -1 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles string index", func() { + source := parseYAML(` +--- +index: (( index("12345678901230", "0") )) +`) + resolved := parseYAML(` +--- +index: 9 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles string index with int", func() { + source := parseYAML(` +--- +index: (( index("12345678901230", 0) )) +`) + resolved := parseYAML(` +--- +index: 9 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles string index and fails", func() { + source := parseYAML(` +--- +index: (( index("1234567890123", "a") )) +`) + resolved := parseYAML(` +--- +index: -1 +`) + Expect(source).To(FlowAs(resolved)) + }) + }) + + Describe("when calling lastindex", func() { + It("finds ints", func() { + source := parseYAML(` +--- +list: +- a +- b +- 0 +- c +- 0 +index: (( lastindex(list, "0") )) +`) + resolved := parseYAML(` +--- +list: +- a +- b +- 0 +- c +- 0 + +index: 4 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("finds string", func() { + source := parseYAML(` +--- +list: +- a +- b +- "0" +- c +- "0" +index: (( lastindex(list, "0") )) +`) + resolved := parseYAML(` +--- +list: +- a +- b +- "0" +- c +- "0" +index: 4 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("works for no match", func() { + source := parseYAML(` +--- +list: +- a +- b +- 0 +- c +index: (( lastindex(list, "d") )) +`) + resolved := parseYAML(` +--- +list: +- a +- b +- 0 +- c + +index: -1 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles string index", func() { + source := parseYAML(` +--- +index: (( lastindex("12345678901230", "0") )) +`) + resolved := parseYAML(` +--- +index: 13 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles string index with int", func() { + source := parseYAML(` +--- +index: (( lastindex("12345678901230", 0) )) +`) + resolved := parseYAML(` +--- +index: 13 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles string index and fails", func() { + source := parseYAML(` +--- +index: (( lastindex("1234567890123", "a") )) +`) + resolved := parseYAML(` +--- +index: -1 +`) + Expect(source).To(FlowAs(resolved)) + }) + }) + + Describe("when replacing", func() { + It("replaces unlimited", func() { + source := parseYAML(` +--- +result: (( replace("foobar","o", "u") )) +`) + resolved := parseYAML(` +--- +result: fuubar +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("replaces limited", func() { + source := parseYAML(` +--- +result: (( replace("foobar","o", "u", 1) )) +`) + resolved := parseYAML(` +--- +result: fuobar `) Expect(source).To(FlowAs(resolved)) }) @@ -4371,6 +4653,94 @@ foo: }) }) + Describe("valid values", func() { + It("fails for undefined values", func() { + source := parseYAML(` +--- +foo: (( valid(bar) )) +`) + + resolved := parseYAML(` +--- +foo: false +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("fails for nil values", func() { + source := parseYAML(` +--- +foo: (( valid(bar) )) +bar: ~ +`) + + resolved := parseYAML(` +--- +foo: false +bar: ~ +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("fails for empty values", func() { + source := parseYAML(` +--- +foo: (( valid(bar) )) +bar: +`) + + resolved := parseYAML(` +--- +foo: false +bar: +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("succeeds for empty maps", func() { + source := parseYAML(` +--- +foo: (( valid(bar) )) +bar: {} +`) + + resolved := parseYAML(` +--- +foo: true +bar: {} +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("succeeds for empty arrays", func() { + source := parseYAML(` +--- +foo: (( valid(bar) )) +bar: [] +`) + + resolved := parseYAML(` +--- +foo: true +bar: [] +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("fails for nil value", func() { + source := parseYAML(` +--- +foo: (( valid(~) )) +`) + + resolved := parseYAML(` +--- +foo: false +`) + Expect(source).To(FlowAs(resolved)) + }) + }) + Describe("require values", func() { It("checks for undefined values", func() { source := parseYAML(` From ac7def024c54044c1990df94e27a1b65395b5929 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Mon, 13 Jun 2016 18:32:30 +0200 Subject: [PATCH 4/6] function makemap --- README.md | 62 +++++++++++++++++++++++++++++++++++++++++++++ dynaml/call.go | 3 +++ dynaml/makemap.go | 64 +++++++++++++++++++++++++++++++++++++++++++++++ flow/flow_test.go | 48 +++++++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+) create mode 100644 dynaml/makemap.go diff --git a/README.md b/README.md index 1288967..ec0a840 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ Contents: - [(( read("file.yml") ))](#-readfileyml-) - [(( static_ips(0, 1, 3) ))](#-static_ips0-1-3-) - [(( list_to_map(list, "key") ))](#-list_to_maplist-key-) + - [(( makemap(fieldlist) ))](#-makemapfieldlist-) + - [(( makemap(key, value) ))](#-makemapkey-value-) - [(( lambda |x|->x ":" port ))](#-lambda-x-x--port-) - [(( &temporary ))](#-temporary-) - [Mappings](#mappings) @@ -1375,6 +1377,66 @@ map: In combination with templates and lambda expressions this can be used to generate maps with arbitrarily named key values, although dynaml expressions are not allowed for key values. +### `(( makemap(fieldlist) ))` + +In this flavor `makemap` creates a map with entries described by the given field list. +The list is expected to contain maps with the entries `key` and `value`, describing +dedicated map entries. + +e.g.: + +```yaml +list: + - key: alice + value: 24 + - key: bob + value: 25 + - key: 5 + value: 25 + +map: (( makemap(list) )) +``` + +yields + + +```yaml +list: + - key: alice + value: 24 + - key: bob + value: 25 + - key: 5 + value: 25 + +map: + "5": 25 + alice: 24 + bob: 25 +``` + +If the key value is a boolean or an integer it will be mapped to a string. + +### `(( makemap(key, value) ))` + +In this flavor `makemap` creates a map with entries described by the given argument +pairs. The arguments may be a sequence of key/values pairs (given by separate arguments). + +e.g.: + +```yaml +map: (( makemap("peter", 23, "paul", 22) )) +``` + +yields + + +```yaml +map: + paul: 22 + peter: 23 +``` + ## `(( lambda |x|->x ":" port ))` Lambda expressions can be used to define additional anonymous functions. They can be assigned to yaml nodes as values and referenced with path expressions to call the function with approriate arguments in other dynaml expressions. For the final document they are mapped to string values. diff --git a/dynaml/call.go b/dynaml/call.go index 4bbb6f8..fe9c132 100644 --- a/dynaml/call.go +++ b/dynaml/call.go @@ -125,6 +125,9 @@ func (e CallExpr) Evaluate(binding Binding, locally bool) (interface{}, Evaluati case "num_ip": result, sub, ok = func_numIP(values, binding) + case "makemap": + result, sub, ok = func_makemap(values, binding) + case "list_to_map": result, sub, ok = func_list_to_map(e.Arguments[0], values, binding) diff --git a/dynaml/makemap.go b/dynaml/makemap.go new file mode 100644 index 0000000..7ef676e --- /dev/null +++ b/dynaml/makemap.go @@ -0,0 +1,64 @@ +package dynaml + +import ( + "github.com/cloudfoundry-incubator/spiff/yaml" + "strconv" +) + +func func_makemap(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { + info := DefaultInfo() + + result := make(map[string]yaml.Node) + var key string + + if len(arguments) == 1 { + list, ok := arguments[0].([]yaml.Node) + if !ok { + return info.Error("single argument flavor of mapentry requires a list") + } + for i, elem := range list { + m, ok := elem.Value().(map[string]yaml.Node) + if !ok { + return info.Error("entry %d is no map entry", i) + } + k, ok := m["key"] + if !ok { + return info.Error("entry %d has no entry 'key'", i) + } + switch elem := k.Value().(type) { + case string: + key = elem + case int64: + key = strconv.FormatInt(elem, 10) + case bool: + key = strconv.FormatBool(elem) + default: + return info.Error("invalid type for 'key' value of entry %d", i) + } + v, ok := m["value"] + if !ok { + return info.Error("entry %d has no entry 'value'", i) + } + result[key] = v + } + } else if len(arguments)%2 == 0 { + for i := 0; i < len(arguments); i += 2 { + switch elem := arguments[i].(type) { + case string: + key = elem + case int64: + key = strconv.FormatInt(elem, 10) + case bool: + key = strconv.FormatBool(elem) + default: + return info.Error("invalid type for key value of arument pair %d", i/2) + } + + result[key] = node(arguments[i+1], binding) + } + } else { + return info.Error("mapentry takes one or two arguments") + } + + return result, info, true +} diff --git a/flow/flow_test.go b/flow/flow_test.go index 719a210..0785687 100644 --- a/flow/flow_test.go +++ b/flow/flow_test.go @@ -3478,6 +3478,54 @@ map: }) }) + Describe("when making a map", func() { + It("handles entries given by a list", func() { + source := parseYAML(` +--- +list: + - key: alice + value: 24 + - key: bob + value: 25 + - key: 5 + value: 25 + +map: (( makemap(list) )) + +`) + resolved := parseYAML(` +--- +list: + - key: alice + value: 24 + - key: bob + value: 25 + - key: 5 + value: 25 + +map: + "5": 25 + alice: 24 + bob: 25 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles a entries given by arguments", func() { + source := parseYAML(` +--- +map: (( makemap("peter", 23, "paul", 22) )) +`) + resolved := parseYAML(` +--- +map: + paul: 22 + peter: 23 +`) + Expect(source).To(FlowAs(resolved)) + }) + }) + Describe("when doing a mapping", func() { Context("for a list", func() { It("maps simple expression", func() { From c96a56914472aac966fb2bf76f980ee7487e4722 Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Tue, 14 Jun 2016 18:00:15 +0200 Subject: [PATCH 5/6] map literals --- README.md | 38 + dynaml/dynaml.peg | 10 +- dynaml/dynaml.peg.go | 1204 +++++++++++++------------ dynaml/empty.go | 15 - dynaml/map.go | 54 ++ dynaml/{empty_test.go => map_test.go} | 2 +- dynaml/parser.go | 22 +- flow/flow_test.go | 44 + 8 files changed, 810 insertions(+), 579 deletions(-) delete mode 100644 dynaml/empty.go create mode 100644 dynaml/map.go rename dynaml/{empty_test.go => map_test.go} (78%) diff --git a/README.md b/README.md index ec0a840..0ca29ff 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Contents: - [(( foo.bar.[1].baz ))](#-foobar1baz-) - [(( "foo" ))](#-foo--1) - [(( [ 1, 2, 3 ] ))](#--1-2-3--) + - [(( { "alice" = 25 } ))](#--alice--25--) - [(( foo bar ))](#-foo-bar-) - [(( "foo" bar ))](#-foo-bar--1) - [(( [1,2] bar ))](#-12-bar-) @@ -263,6 +264,40 @@ list: - -1 ``` +## `(( { "alice" = 25 } ))` + +The map literal can be used to describe maps as part of a dynaml expression. Both, +the key and the value, might again be expressions, whereby the key expression must +evaluate to a string. This way it is possible to create maps with non-static keys. +The assignment operator `=` has been chosen instead of the regular colon `:` +character used in yaml, because this would result in conflicts with the yaml +syntax. + +A map literal might consist of any number of field assignments separated by a +comma `,`. + +e.g.: + +```yaml +name: peter +age: 23 +map: (( { "alice" = {}, name = age } )) +``` + +yields + +```yaml +name: peter +age: 23 +map: + alice: {} + peter: 23 +``` + +Another way to compose lists based on expressions are the functions +[`makemap`](#-makemapkey-value-) and [`list_to_map`](#-list_to_maplist-key-). + + ## `(( foo bar ))` Concatenation expression used to concatenate a sequence of dynaml expressions. @@ -1437,6 +1472,9 @@ map: peter: 23 ``` +In contrast to the previous `makemap` flavor, this one could also be handled by +[map literals](#--alice--25--). + ## `(( lambda |x|->x ":" port ))` Lambda expressions can be used to define additional anonymous functions. They can be assigned to yaml nodes as values and referenced with path expressions to call the function with approriate arguments in other dynaml expressions. For the final document they are mapped to string values. diff --git a/dynaml/dynaml.peg b/dynaml/dynaml.peg index cb675ce..bc89382 100644 --- a/dynaml/dynaml.peg +++ b/dynaml/dynaml.peg @@ -37,10 +37,10 @@ Multiplication <- '*' req_ws Level0 Division <- '/' req_ws Level0 Modulo <- '%' req_ws Level0 -Level0 <- String / Integer / Boolean / EmptyHash / Undefined / Nil / Not / +Level0 <- String / Integer / Boolean / Undefined / Nil / Not / Substitution / Merge / Auto / Lambda / Chained -Chained <- (( Mapping / Sum / List / Range / ( Grouped / Reference ) ChainedCall* ) ) ( ChainedQualifiedExpression ChainedCall+)* ChainedQualifiedExpression? +Chained <- (( Mapping / Sum / List / Map / Range / ( Grouped / Reference ) ChainedCall* ) ) ( ChainedQualifiedExpression ChainedCall+)* ChainedQualifiedExpression? ChainedQualifiedExpression <- '.' FollowUpRef ChainedCall <- '(' Arguments ')' Arguments <- Expression (NextExpression)* @@ -56,11 +56,15 @@ String <- '"' ('\\"' / !'"' .)* '"' Boolean <- 'true' / 'false' Nil <- 'nil' / '~' Undefined <- '~~' -EmptyHash <- '{' ws '}' List <- '[' Contents? ']' Contents <- Expression (NextExpression)* +Map <- CreateMap ws Assignments? '}' +CreateMap <- '{' +Assignments <- Assignment (',' Assignment)* +Assignment <- Expression '=' Expression + Merge <- RefMerge / SimpleMerge RefMerge <- 'merge' !( req_ws Required ) ( req_ws (Replace / On ))? req_ws Reference SimpleMerge <- 'merge' ( req_ws (Replace/Required/On) )? diff --git a/dynaml/dynaml.peg.go b/dynaml/dynaml.peg.go index 668e339..4c1263f 100644 --- a/dynaml/dynaml.peg.go +++ b/dynaml/dynaml.peg.go @@ -54,9 +54,12 @@ const ( ruleBoolean ruleNil ruleUndefined - ruleEmptyHash ruleList ruleContents + ruleMap + ruleCreateMap + ruleAssignments + ruleAssignment ruleMerge ruleRefMerge ruleSimpleMerge @@ -125,9 +128,12 @@ var rul3s = [...]string{ "Boolean", "Nil", "Undefined", - "EmptyHash", "List", "Contents", + "Map", + "CreateMap", + "Assignments", + "Assignment", "Merge", "RefMerge", "SimpleMerge", @@ -466,7 +472,7 @@ func (t *tokens32) Expand(index int) tokenTree { type DynamlGrammar struct { Buffer string buffer []rune - rules [64]func() bool + rules [67]func() bool Parse func(rule ...int) error Reset func() tokenTree @@ -1490,7 +1496,7 @@ func (p *DynamlGrammar) Init() { position, tokenIndex, depth = position88, tokenIndex88, depth88 return false }, - /* 25 Level0 <- <(String / Integer / Boolean / EmptyHash / Undefined / Nil / Not / Substitution / Merge / Auto / Lambda / Chained)> */ + /* 25 Level0 <- <(String / Integer / Boolean / Undefined / Nil / Not / Substitution / Merge / Auto / Lambda / Chained)> */ func() bool { position90, tokenIndex90, depth90 := position, tokenIndex, depth { @@ -1516,53 +1522,47 @@ func (p *DynamlGrammar) Init() { goto l92 l95: position, tokenIndex, depth = position92, tokenIndex92, depth92 - if !_rules[ruleEmptyHash]() { + if !_rules[ruleUndefined]() { goto l96 } goto l92 l96: position, tokenIndex, depth = position92, tokenIndex92, depth92 - if !_rules[ruleUndefined]() { + if !_rules[ruleNil]() { goto l97 } goto l92 l97: position, tokenIndex, depth = position92, tokenIndex92, depth92 - if !_rules[ruleNil]() { + if !_rules[ruleNot]() { goto l98 } goto l92 l98: position, tokenIndex, depth = position92, tokenIndex92, depth92 - if !_rules[ruleNot]() { + if !_rules[ruleSubstitution]() { goto l99 } goto l92 l99: position, tokenIndex, depth = position92, tokenIndex92, depth92 - if !_rules[ruleSubstitution]() { + if !_rules[ruleMerge]() { goto l100 } goto l92 l100: position, tokenIndex, depth = position92, tokenIndex92, depth92 - if !_rules[ruleMerge]() { + if !_rules[ruleAuto]() { goto l101 } goto l92 l101: position, tokenIndex, depth = position92, tokenIndex92, depth92 - if !_rules[ruleAuto]() { + if !_rules[ruleLambda]() { goto l102 } goto l92 l102: - position, tokenIndex, depth = position92, tokenIndex92, depth92 - if !_rules[ruleLambda]() { - goto l103 - } - goto l92 - l103: position, tokenIndex, depth = position92, tokenIndex92, depth92 if !_rules[ruleChained]() { goto l90 @@ -1577,38 +1577,44 @@ func (p *DynamlGrammar) Init() { position, tokenIndex, depth = position90, tokenIndex90, depth90 return false }, - /* 26 Chained <- <((Mapping / Sum / List / Range / ((Grouped / Reference) ChainedCall*)) (ChainedQualifiedExpression ChainedCall+)* ChainedQualifiedExpression?)> */ + /* 26 Chained <- <((Mapping / Sum / List / Map / Range / ((Grouped / Reference) ChainedCall*)) (ChainedQualifiedExpression ChainedCall+)* ChainedQualifiedExpression?)> */ func() bool { - position104, tokenIndex104, depth104 := position, tokenIndex, depth + position103, tokenIndex103, depth103 := position, tokenIndex, depth { - position105 := position + position104 := position depth++ { - position106, tokenIndex106, depth106 := position, tokenIndex, depth + position105, tokenIndex105, depth105 := position, tokenIndex, depth if !_rules[ruleMapping]() { + goto l106 + } + goto l105 + l106: + position, tokenIndex, depth = position105, tokenIndex105, depth105 + if !_rules[ruleSum]() { goto l107 } - goto l106 + goto l105 l107: - position, tokenIndex, depth = position106, tokenIndex106, depth106 - if !_rules[ruleSum]() { + position, tokenIndex, depth = position105, tokenIndex105, depth105 + if !_rules[ruleList]() { goto l108 } - goto l106 + goto l105 l108: - position, tokenIndex, depth = position106, tokenIndex106, depth106 - if !_rules[ruleList]() { + position, tokenIndex, depth = position105, tokenIndex105, depth105 + if !_rules[ruleMap]() { goto l109 } - goto l106 + goto l105 l109: - position, tokenIndex, depth = position106, tokenIndex106, depth106 + position, tokenIndex, depth = position105, tokenIndex105, depth105 if !_rules[ruleRange]() { goto l110 } - goto l106 + goto l105 l110: - position, tokenIndex, depth = position106, tokenIndex106, depth106 + position, tokenIndex, depth = position105, tokenIndex105, depth105 { position111, tokenIndex111, depth111 := position, tokenIndex, depth if !_rules[ruleGrouped]() { @@ -1618,7 +1624,7 @@ func (p *DynamlGrammar) Init() { l112: position, tokenIndex, depth = position111, tokenIndex111, depth111 if !_rules[ruleReference]() { - goto l104 + goto l103 } } l111: @@ -1633,7 +1639,7 @@ func (p *DynamlGrammar) Init() { position, tokenIndex, depth = position114, tokenIndex114, depth114 } } - l106: + l105: l115: { position116, tokenIndex116, depth116 := position, tokenIndex, depth @@ -1668,11 +1674,11 @@ func (p *DynamlGrammar) Init() { } l120: depth-- - add(ruleChained, position105) + add(ruleChained, position104) } return true - l104: - position, tokenIndex, depth = position104, tokenIndex104, depth104 + l103: + position, tokenIndex, depth = position103, tokenIndex103, depth103 return false }, /* 27 ChainedQualifiedExpression <- <('.' FollowUpRef)> */ @@ -2099,1183 +2105,1265 @@ func (p *DynamlGrammar) Init() { position, tokenIndex, depth = position162, tokenIndex162, depth162 return false }, - /* 40 EmptyHash <- <('{' ws '}')> */ + /* 40 List <- <('[' Contents? ']')> */ func() bool { position164, tokenIndex164, depth164 := position, tokenIndex, depth { position165 := position depth++ - if buffer[position] != rune('{') { + if buffer[position] != rune('[') { goto l164 } position++ - if !_rules[rulews]() { - goto l164 + { + position166, tokenIndex166, depth166 := position, tokenIndex, depth + if !_rules[ruleContents]() { + goto l166 + } + goto l167 + l166: + position, tokenIndex, depth = position166, tokenIndex166, depth166 } - if buffer[position] != rune('}') { + l167: + if buffer[position] != rune(']') { goto l164 } position++ depth-- - add(ruleEmptyHash, position165) + add(ruleList, position165) } return true l164: position, tokenIndex, depth = position164, tokenIndex164, depth164 return false }, - /* 41 List <- <('[' Contents? ']')> */ + /* 41 Contents <- <(Expression NextExpression*)> */ func() bool { - position166, tokenIndex166, depth166 := position, tokenIndex, depth + position168, tokenIndex168, depth168 := position, tokenIndex, depth { - position167 := position + position169 := position depth++ - if buffer[position] != rune('[') { - goto l166 + if !_rules[ruleExpression]() { + goto l168 } - position++ + l170: { - position168, tokenIndex168, depth168 := position, tokenIndex, depth - if !_rules[ruleContents]() { - goto l168 + position171, tokenIndex171, depth171 := position, tokenIndex, depth + if !_rules[ruleNextExpression]() { + goto l171 } - goto l169 - l168: - position, tokenIndex, depth = position168, tokenIndex168, depth168 + goto l170 + l171: + position, tokenIndex, depth = position171, tokenIndex171, depth171 } - l169: - if buffer[position] != rune(']') { - goto l166 + depth-- + add(ruleContents, position169) + } + return true + l168: + position, tokenIndex, depth = position168, tokenIndex168, depth168 + return false + }, + /* 42 Map <- <(CreateMap ws Assignments? '}')> */ + func() bool { + position172, tokenIndex172, depth172 := position, tokenIndex, depth + { + position173 := position + depth++ + if !_rules[ruleCreateMap]() { + goto l172 + } + if !_rules[rulews]() { + goto l172 + } + { + position174, tokenIndex174, depth174 := position, tokenIndex, depth + if !_rules[ruleAssignments]() { + goto l174 + } + goto l175 + l174: + position, tokenIndex, depth = position174, tokenIndex174, depth174 + } + l175: + if buffer[position] != rune('}') { + goto l172 } position++ depth-- - add(ruleList, position167) + add(ruleMap, position173) } return true - l166: - position, tokenIndex, depth = position166, tokenIndex166, depth166 + l172: + position, tokenIndex, depth = position172, tokenIndex172, depth172 return false }, - /* 42 Contents <- <(Expression NextExpression*)> */ + /* 43 CreateMap <- <'{'> */ func() bool { - position170, tokenIndex170, depth170 := position, tokenIndex, depth + position176, tokenIndex176, depth176 := position, tokenIndex, depth { - position171 := position + position177 := position depth++ - if !_rules[ruleExpression]() { - goto l170 + if buffer[position] != rune('{') { + goto l176 } - l172: + position++ + depth-- + add(ruleCreateMap, position177) + } + return true + l176: + position, tokenIndex, depth = position176, tokenIndex176, depth176 + return false + }, + /* 44 Assignments <- <(Assignment (',' Assignment)*)> */ + func() bool { + position178, tokenIndex178, depth178 := position, tokenIndex, depth + { + position179 := position + depth++ + if !_rules[ruleAssignment]() { + goto l178 + } + l180: { - position173, tokenIndex173, depth173 := position, tokenIndex, depth - if !_rules[ruleNextExpression]() { - goto l173 + position181, tokenIndex181, depth181 := position, tokenIndex, depth + if buffer[position] != rune(',') { + goto l181 } - goto l172 - l173: - position, tokenIndex, depth = position173, tokenIndex173, depth173 + position++ + if !_rules[ruleAssignment]() { + goto l181 + } + goto l180 + l181: + position, tokenIndex, depth = position181, tokenIndex181, depth181 } depth-- - add(ruleContents, position171) + add(ruleAssignments, position179) } return true - l170: - position, tokenIndex, depth = position170, tokenIndex170, depth170 + l178: + position, tokenIndex, depth = position178, tokenIndex178, depth178 return false }, - /* 43 Merge <- <(RefMerge / SimpleMerge)> */ + /* 45 Assignment <- <(Expression '=' Expression)> */ func() bool { - position174, tokenIndex174, depth174 := position, tokenIndex, depth + position182, tokenIndex182, depth182 := position, tokenIndex, depth { - position175 := position + position183 := position + depth++ + if !_rules[ruleExpression]() { + goto l182 + } + if buffer[position] != rune('=') { + goto l182 + } + position++ + if !_rules[ruleExpression]() { + goto l182 + } + depth-- + add(ruleAssignment, position183) + } + return true + l182: + position, tokenIndex, depth = position182, tokenIndex182, depth182 + return false + }, + /* 46 Merge <- <(RefMerge / SimpleMerge)> */ + func() bool { + position184, tokenIndex184, depth184 := position, tokenIndex, depth + { + position185 := position depth++ { - position176, tokenIndex176, depth176 := position, tokenIndex, depth + position186, tokenIndex186, depth186 := position, tokenIndex, depth if !_rules[ruleRefMerge]() { - goto l177 + goto l187 } - goto l176 - l177: - position, tokenIndex, depth = position176, tokenIndex176, depth176 + goto l186 + l187: + position, tokenIndex, depth = position186, tokenIndex186, depth186 if !_rules[ruleSimpleMerge]() { - goto l174 + goto l184 } } - l176: + l186: depth-- - add(ruleMerge, position175) + add(ruleMerge, position185) } return true - l174: - position, tokenIndex, depth = position174, tokenIndex174, depth174 + l184: + position, tokenIndex, depth = position184, tokenIndex184, depth184 return false }, - /* 44 RefMerge <- <('m' 'e' 'r' 'g' 'e' !(req_ws Required) (req_ws (Replace / On))? req_ws Reference)> */ + /* 47 RefMerge <- <('m' 'e' 'r' 'g' 'e' !(req_ws Required) (req_ws (Replace / On))? req_ws Reference)> */ func() bool { - position178, tokenIndex178, depth178 := position, tokenIndex, depth + position188, tokenIndex188, depth188 := position, tokenIndex, depth { - position179 := position + position189 := position depth++ if buffer[position] != rune('m') { - goto l178 + goto l188 } position++ if buffer[position] != rune('e') { - goto l178 + goto l188 } position++ if buffer[position] != rune('r') { - goto l178 + goto l188 } position++ if buffer[position] != rune('g') { - goto l178 + goto l188 } position++ if buffer[position] != rune('e') { - goto l178 + goto l188 } position++ { - position180, tokenIndex180, depth180 := position, tokenIndex, depth + position190, tokenIndex190, depth190 := position, tokenIndex, depth if !_rules[rulereq_ws]() { - goto l180 + goto l190 } if !_rules[ruleRequired]() { - goto l180 + goto l190 } - goto l178 - l180: - position, tokenIndex, depth = position180, tokenIndex180, depth180 + goto l188 + l190: + position, tokenIndex, depth = position190, tokenIndex190, depth190 } { - position181, tokenIndex181, depth181 := position, tokenIndex, depth + position191, tokenIndex191, depth191 := position, tokenIndex, depth if !_rules[rulereq_ws]() { - goto l181 + goto l191 } { - position183, tokenIndex183, depth183 := position, tokenIndex, depth + position193, tokenIndex193, depth193 := position, tokenIndex, depth if !_rules[ruleReplace]() { - goto l184 + goto l194 } - goto l183 - l184: - position, tokenIndex, depth = position183, tokenIndex183, depth183 + goto l193 + l194: + position, tokenIndex, depth = position193, tokenIndex193, depth193 if !_rules[ruleOn]() { - goto l181 + goto l191 } } - l183: - goto l182 - l181: - position, tokenIndex, depth = position181, tokenIndex181, depth181 + l193: + goto l192 + l191: + position, tokenIndex, depth = position191, tokenIndex191, depth191 } - l182: + l192: if !_rules[rulereq_ws]() { - goto l178 + goto l188 } if !_rules[ruleReference]() { - goto l178 + goto l188 } depth-- - add(ruleRefMerge, position179) + add(ruleRefMerge, position189) } return true - l178: - position, tokenIndex, depth = position178, tokenIndex178, depth178 + l188: + position, tokenIndex, depth = position188, tokenIndex188, depth188 return false }, - /* 45 SimpleMerge <- <('m' 'e' 'r' 'g' 'e' (req_ws (Replace / Required / On))?)> */ + /* 48 SimpleMerge <- <('m' 'e' 'r' 'g' 'e' (req_ws (Replace / Required / On))?)> */ func() bool { - position185, tokenIndex185, depth185 := position, tokenIndex, depth + position195, tokenIndex195, depth195 := position, tokenIndex, depth { - position186 := position + position196 := position depth++ if buffer[position] != rune('m') { - goto l185 + goto l195 } position++ if buffer[position] != rune('e') { - goto l185 + goto l195 } position++ if buffer[position] != rune('r') { - goto l185 + goto l195 } position++ if buffer[position] != rune('g') { - goto l185 + goto l195 } position++ if buffer[position] != rune('e') { - goto l185 + goto l195 } position++ { - position187, tokenIndex187, depth187 := position, tokenIndex, depth + position197, tokenIndex197, depth197 := position, tokenIndex, depth if !_rules[rulereq_ws]() { - goto l187 + goto l197 } { - position189, tokenIndex189, depth189 := position, tokenIndex, depth + position199, tokenIndex199, depth199 := position, tokenIndex, depth if !_rules[ruleReplace]() { - goto l190 + goto l200 } - goto l189 - l190: - position, tokenIndex, depth = position189, tokenIndex189, depth189 + goto l199 + l200: + position, tokenIndex, depth = position199, tokenIndex199, depth199 if !_rules[ruleRequired]() { - goto l191 + goto l201 } - goto l189 - l191: - position, tokenIndex, depth = position189, tokenIndex189, depth189 + goto l199 + l201: + position, tokenIndex, depth = position199, tokenIndex199, depth199 if !_rules[ruleOn]() { - goto l187 + goto l197 } } - l189: - goto l188 - l187: - position, tokenIndex, depth = position187, tokenIndex187, depth187 + l199: + goto l198 + l197: + position, tokenIndex, depth = position197, tokenIndex197, depth197 } - l188: + l198: depth-- - add(ruleSimpleMerge, position186) + add(ruleSimpleMerge, position196) } return true - l185: - position, tokenIndex, depth = position185, tokenIndex185, depth185 + l195: + position, tokenIndex, depth = position195, tokenIndex195, depth195 return false }, - /* 46 Replace <- <('r' 'e' 'p' 'l' 'a' 'c' 'e')> */ + /* 49 Replace <- <('r' 'e' 'p' 'l' 'a' 'c' 'e')> */ func() bool { - position192, tokenIndex192, depth192 := position, tokenIndex, depth + position202, tokenIndex202, depth202 := position, tokenIndex, depth { - position193 := position + position203 := position depth++ if buffer[position] != rune('r') { - goto l192 + goto l202 } position++ if buffer[position] != rune('e') { - goto l192 + goto l202 } position++ if buffer[position] != rune('p') { - goto l192 + goto l202 } position++ if buffer[position] != rune('l') { - goto l192 + goto l202 } position++ if buffer[position] != rune('a') { - goto l192 + goto l202 } position++ if buffer[position] != rune('c') { - goto l192 + goto l202 } position++ if buffer[position] != rune('e') { - goto l192 + goto l202 } position++ depth-- - add(ruleReplace, position193) + add(ruleReplace, position203) } return true - l192: - position, tokenIndex, depth = position192, tokenIndex192, depth192 + l202: + position, tokenIndex, depth = position202, tokenIndex202, depth202 return false }, - /* 47 Required <- <('r' 'e' 'q' 'u' 'i' 'r' 'e' 'd')> */ + /* 50 Required <- <('r' 'e' 'q' 'u' 'i' 'r' 'e' 'd')> */ func() bool { - position194, tokenIndex194, depth194 := position, tokenIndex, depth + position204, tokenIndex204, depth204 := position, tokenIndex, depth { - position195 := position + position205 := position depth++ if buffer[position] != rune('r') { - goto l194 + goto l204 } position++ if buffer[position] != rune('e') { - goto l194 + goto l204 } position++ if buffer[position] != rune('q') { - goto l194 + goto l204 } position++ if buffer[position] != rune('u') { - goto l194 + goto l204 } position++ if buffer[position] != rune('i') { - goto l194 + goto l204 } position++ if buffer[position] != rune('r') { - goto l194 + goto l204 } position++ if buffer[position] != rune('e') { - goto l194 + goto l204 } position++ if buffer[position] != rune('d') { - goto l194 + goto l204 } position++ depth-- - add(ruleRequired, position195) + add(ruleRequired, position205) } return true - l194: - position, tokenIndex, depth = position194, tokenIndex194, depth194 + l204: + position, tokenIndex, depth = position204, tokenIndex204, depth204 return false }, - /* 48 On <- <('o' 'n' req_ws Name)> */ + /* 51 On <- <('o' 'n' req_ws Name)> */ func() bool { - position196, tokenIndex196, depth196 := position, tokenIndex, depth + position206, tokenIndex206, depth206 := position, tokenIndex, depth { - position197 := position + position207 := position depth++ if buffer[position] != rune('o') { - goto l196 + goto l206 } position++ if buffer[position] != rune('n') { - goto l196 + goto l206 } position++ if !_rules[rulereq_ws]() { - goto l196 + goto l206 } if !_rules[ruleName]() { - goto l196 + goto l206 } depth-- - add(ruleOn, position197) + add(ruleOn, position207) } return true - l196: - position, tokenIndex, depth = position196, tokenIndex196, depth196 + l206: + position, tokenIndex, depth = position206, tokenIndex206, depth206 return false }, - /* 49 Auto <- <('a' 'u' 't' 'o')> */ + /* 52 Auto <- <('a' 'u' 't' 'o')> */ func() bool { - position198, tokenIndex198, depth198 := position, tokenIndex, depth + position208, tokenIndex208, depth208 := position, tokenIndex, depth { - position199 := position + position209 := position depth++ if buffer[position] != rune('a') { - goto l198 + goto l208 } position++ if buffer[position] != rune('u') { - goto l198 + goto l208 } position++ if buffer[position] != rune('t') { - goto l198 + goto l208 } position++ if buffer[position] != rune('o') { - goto l198 + goto l208 } position++ depth-- - add(ruleAuto, position199) + add(ruleAuto, position209) } return true - l198: - position, tokenIndex, depth = position198, tokenIndex198, depth198 + l208: + position, tokenIndex, depth = position208, tokenIndex208, depth208 return false }, - /* 50 Mapping <- <('m' 'a' 'p' '[' Level7 (LambdaExpr / ('|' Expression)) ']')> */ + /* 53 Mapping <- <('m' 'a' 'p' '[' Level7 (LambdaExpr / ('|' Expression)) ']')> */ func() bool { - position200, tokenIndex200, depth200 := position, tokenIndex, depth + position210, tokenIndex210, depth210 := position, tokenIndex, depth { - position201 := position + position211 := position depth++ if buffer[position] != rune('m') { - goto l200 + goto l210 } position++ if buffer[position] != rune('a') { - goto l200 + goto l210 } position++ if buffer[position] != rune('p') { - goto l200 + goto l210 } position++ if buffer[position] != rune('[') { - goto l200 + goto l210 } position++ if !_rules[ruleLevel7]() { - goto l200 + goto l210 } { - position202, tokenIndex202, depth202 := position, tokenIndex, depth + position212, tokenIndex212, depth212 := position, tokenIndex, depth if !_rules[ruleLambdaExpr]() { - goto l203 + goto l213 } - goto l202 - l203: - position, tokenIndex, depth = position202, tokenIndex202, depth202 + goto l212 + l213: + position, tokenIndex, depth = position212, tokenIndex212, depth212 if buffer[position] != rune('|') { - goto l200 + goto l210 } position++ if !_rules[ruleExpression]() { - goto l200 + goto l210 } } - l202: + l212: if buffer[position] != rune(']') { - goto l200 + goto l210 } position++ depth-- - add(ruleMapping, position201) + add(ruleMapping, position211) } return true - l200: - position, tokenIndex, depth = position200, tokenIndex200, depth200 + l210: + position, tokenIndex, depth = position210, tokenIndex210, depth210 return false }, - /* 51 Sum <- <('s' 'u' 'm' '[' Level7 '|' Level7 (LambdaExpr / ('|' Expression)) ']')> */ + /* 54 Sum <- <('s' 'u' 'm' '[' Level7 '|' Level7 (LambdaExpr / ('|' Expression)) ']')> */ func() bool { - position204, tokenIndex204, depth204 := position, tokenIndex, depth + position214, tokenIndex214, depth214 := position, tokenIndex, depth { - position205 := position + position215 := position depth++ if buffer[position] != rune('s') { - goto l204 + goto l214 } position++ if buffer[position] != rune('u') { - goto l204 + goto l214 } position++ if buffer[position] != rune('m') { - goto l204 + goto l214 } position++ if buffer[position] != rune('[') { - goto l204 + goto l214 } position++ if !_rules[ruleLevel7]() { - goto l204 + goto l214 } if buffer[position] != rune('|') { - goto l204 + goto l214 } position++ if !_rules[ruleLevel7]() { - goto l204 + goto l214 } { - position206, tokenIndex206, depth206 := position, tokenIndex, depth + position216, tokenIndex216, depth216 := position, tokenIndex, depth if !_rules[ruleLambdaExpr]() { - goto l207 + goto l217 } - goto l206 - l207: - position, tokenIndex, depth = position206, tokenIndex206, depth206 + goto l216 + l217: + position, tokenIndex, depth = position216, tokenIndex216, depth216 if buffer[position] != rune('|') { - goto l204 + goto l214 } position++ if !_rules[ruleExpression]() { - goto l204 + goto l214 } } - l206: + l216: if buffer[position] != rune(']') { - goto l204 + goto l214 } position++ depth-- - add(ruleSum, position205) + add(ruleSum, position215) } return true - l204: - position, tokenIndex, depth = position204, tokenIndex204, depth204 + l214: + position, tokenIndex, depth = position214, tokenIndex214, depth214 return false }, - /* 52 Lambda <- <('l' 'a' 'm' 'b' 'd' 'a' (LambdaRef / LambdaExpr))> */ + /* 55 Lambda <- <('l' 'a' 'm' 'b' 'd' 'a' (LambdaRef / LambdaExpr))> */ func() bool { - position208, tokenIndex208, depth208 := position, tokenIndex, depth + position218, tokenIndex218, depth218 := position, tokenIndex, depth { - position209 := position + position219 := position depth++ if buffer[position] != rune('l') { - goto l208 + goto l218 } position++ if buffer[position] != rune('a') { - goto l208 + goto l218 } position++ if buffer[position] != rune('m') { - goto l208 + goto l218 } position++ if buffer[position] != rune('b') { - goto l208 + goto l218 } position++ if buffer[position] != rune('d') { - goto l208 + goto l218 } position++ if buffer[position] != rune('a') { - goto l208 + goto l218 } position++ { - position210, tokenIndex210, depth210 := position, tokenIndex, depth + position220, tokenIndex220, depth220 := position, tokenIndex, depth if !_rules[ruleLambdaRef]() { - goto l211 + goto l221 } - goto l210 - l211: - position, tokenIndex, depth = position210, tokenIndex210, depth210 + goto l220 + l221: + position, tokenIndex, depth = position220, tokenIndex220, depth220 if !_rules[ruleLambdaExpr]() { - goto l208 + goto l218 } } - l210: + l220: depth-- - add(ruleLambda, position209) + add(ruleLambda, position219) } return true - l208: - position, tokenIndex, depth = position208, tokenIndex208, depth208 + l218: + position, tokenIndex, depth = position218, tokenIndex218, depth218 return false }, - /* 53 LambdaRef <- <(req_ws Expression)> */ + /* 56 LambdaRef <- <(req_ws Expression)> */ func() bool { - position212, tokenIndex212, depth212 := position, tokenIndex, depth + position222, tokenIndex222, depth222 := position, tokenIndex, depth { - position213 := position + position223 := position depth++ if !_rules[rulereq_ws]() { - goto l212 + goto l222 } if !_rules[ruleExpression]() { - goto l212 + goto l222 } depth-- - add(ruleLambdaRef, position213) + add(ruleLambdaRef, position223) } return true - l212: - position, tokenIndex, depth = position212, tokenIndex212, depth212 + l222: + position, tokenIndex, depth = position222, tokenIndex222, depth222 return false }, - /* 54 LambdaExpr <- <(ws '|' ws Name NextName* ws '|' ws ('-' '>') Expression)> */ + /* 57 LambdaExpr <- <(ws '|' ws Name NextName* ws '|' ws ('-' '>') Expression)> */ func() bool { - position214, tokenIndex214, depth214 := position, tokenIndex, depth + position224, tokenIndex224, depth224 := position, tokenIndex, depth { - position215 := position + position225 := position depth++ if !_rules[rulews]() { - goto l214 + goto l224 } if buffer[position] != rune('|') { - goto l214 + goto l224 } position++ if !_rules[rulews]() { - goto l214 + goto l224 } if !_rules[ruleName]() { - goto l214 + goto l224 } - l216: + l226: { - position217, tokenIndex217, depth217 := position, tokenIndex, depth + position227, tokenIndex227, depth227 := position, tokenIndex, depth if !_rules[ruleNextName]() { - goto l217 + goto l227 } - goto l216 - l217: - position, tokenIndex, depth = position217, tokenIndex217, depth217 + goto l226 + l227: + position, tokenIndex, depth = position227, tokenIndex227, depth227 } if !_rules[rulews]() { - goto l214 + goto l224 } if buffer[position] != rune('|') { - goto l214 + goto l224 } position++ if !_rules[rulews]() { - goto l214 + goto l224 } if buffer[position] != rune('-') { - goto l214 + goto l224 } position++ if buffer[position] != rune('>') { - goto l214 + goto l224 } position++ if !_rules[ruleExpression]() { - goto l214 + goto l224 } depth-- - add(ruleLambdaExpr, position215) + add(ruleLambdaExpr, position225) } return true - l214: - position, tokenIndex, depth = position214, tokenIndex214, depth214 + l224: + position, tokenIndex, depth = position224, tokenIndex224, depth224 return false }, - /* 55 NextName <- <(ws ',' ws Name)> */ + /* 58 NextName <- <(ws ',' ws Name)> */ func() bool { - position218, tokenIndex218, depth218 := position, tokenIndex, depth + position228, tokenIndex228, depth228 := position, tokenIndex, depth { - position219 := position + position229 := position depth++ if !_rules[rulews]() { - goto l218 + goto l228 } if buffer[position] != rune(',') { - goto l218 + goto l228 } position++ if !_rules[rulews]() { - goto l218 + goto l228 } if !_rules[ruleName]() { - goto l218 + goto l228 } depth-- - add(ruleNextName, position219) + add(ruleNextName, position229) } return true - l218: - position, tokenIndex, depth = position218, tokenIndex218, depth218 + l228: + position, tokenIndex, depth = position228, tokenIndex228, depth228 return false }, - /* 56 Name <- <([a-z] / [A-Z] / [0-9] / '_')+> */ + /* 59 Name <- <([a-z] / [A-Z] / [0-9] / '_')+> */ func() bool { - position220, tokenIndex220, depth220 := position, tokenIndex, depth + position230, tokenIndex230, depth230 := position, tokenIndex, depth { - position221 := position + position231 := position depth++ { - position224, tokenIndex224, depth224 := position, tokenIndex, depth + position234, tokenIndex234, depth234 := position, tokenIndex, depth if c := buffer[position]; c < rune('a') || c > rune('z') { - goto l225 + goto l235 } position++ - goto l224 - l225: - position, tokenIndex, depth = position224, tokenIndex224, depth224 + goto l234 + l235: + position, tokenIndex, depth = position234, tokenIndex234, depth234 if c := buffer[position]; c < rune('A') || c > rune('Z') { - goto l226 + goto l236 } position++ - goto l224 - l226: - position, tokenIndex, depth = position224, tokenIndex224, depth224 + goto l234 + l236: + position, tokenIndex, depth = position234, tokenIndex234, depth234 if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l227 + goto l237 } position++ - goto l224 - l227: - position, tokenIndex, depth = position224, tokenIndex224, depth224 + goto l234 + l237: + position, tokenIndex, depth = position234, tokenIndex234, depth234 if buffer[position] != rune('_') { - goto l220 + goto l230 } position++ } - l224: - l222: + l234: + l232: { - position223, tokenIndex223, depth223 := position, tokenIndex, depth + position233, tokenIndex233, depth233 := position, tokenIndex, depth { - position228, tokenIndex228, depth228 := position, tokenIndex, depth + position238, tokenIndex238, depth238 := position, tokenIndex, depth if c := buffer[position]; c < rune('a') || c > rune('z') { - goto l229 + goto l239 } position++ - goto l228 - l229: - position, tokenIndex, depth = position228, tokenIndex228, depth228 + goto l238 + l239: + position, tokenIndex, depth = position238, tokenIndex238, depth238 if c := buffer[position]; c < rune('A') || c > rune('Z') { - goto l230 + goto l240 } position++ - goto l228 - l230: - position, tokenIndex, depth = position228, tokenIndex228, depth228 + goto l238 + l240: + position, tokenIndex, depth = position238, tokenIndex238, depth238 if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l231 + goto l241 } position++ - goto l228 - l231: - position, tokenIndex, depth = position228, tokenIndex228, depth228 + goto l238 + l241: + position, tokenIndex, depth = position238, tokenIndex238, depth238 if buffer[position] != rune('_') { - goto l223 + goto l233 } position++ } - l228: - goto l222 - l223: - position, tokenIndex, depth = position223, tokenIndex223, depth223 + l238: + goto l232 + l233: + position, tokenIndex, depth = position233, tokenIndex233, depth233 } depth-- - add(ruleName, position221) + add(ruleName, position231) } return true - l220: - position, tokenIndex, depth = position220, tokenIndex220, depth220 + l230: + position, tokenIndex, depth = position230, tokenIndex230, depth230 return false }, - /* 57 Reference <- <('.'? Key ('.' (Key / Index))*)> */ + /* 60 Reference <- <('.'? Key ('.' (Key / Index))*)> */ func() bool { - position232, tokenIndex232, depth232 := position, tokenIndex, depth + position242, tokenIndex242, depth242 := position, tokenIndex, depth { - position233 := position + position243 := position depth++ { - position234, tokenIndex234, depth234 := position, tokenIndex, depth + position244, tokenIndex244, depth244 := position, tokenIndex, depth if buffer[position] != rune('.') { - goto l234 + goto l244 } position++ - goto l235 - l234: - position, tokenIndex, depth = position234, tokenIndex234, depth234 + goto l245 + l244: + position, tokenIndex, depth = position244, tokenIndex244, depth244 } - l235: + l245: if !_rules[ruleKey]() { - goto l232 + goto l242 } - l236: + l246: { - position237, tokenIndex237, depth237 := position, tokenIndex, depth + position247, tokenIndex247, depth247 := position, tokenIndex, depth if buffer[position] != rune('.') { - goto l237 + goto l247 } position++ { - position238, tokenIndex238, depth238 := position, tokenIndex, depth + position248, tokenIndex248, depth248 := position, tokenIndex, depth if !_rules[ruleKey]() { - goto l239 + goto l249 } - goto l238 - l239: - position, tokenIndex, depth = position238, tokenIndex238, depth238 + goto l248 + l249: + position, tokenIndex, depth = position248, tokenIndex248, depth248 if !_rules[ruleIndex]() { - goto l237 + goto l247 } } - l238: - goto l236 - l237: - position, tokenIndex, depth = position237, tokenIndex237, depth237 + l248: + goto l246 + l247: + position, tokenIndex, depth = position247, tokenIndex247, depth247 } depth-- - add(ruleReference, position233) + add(ruleReference, position243) } return true - l232: - position, tokenIndex, depth = position232, tokenIndex232, depth232 + l242: + position, tokenIndex, depth = position242, tokenIndex242, depth242 return false }, - /* 58 FollowUpRef <- <((Key / Index) ('.' (Key / Index))*)> */ + /* 61 FollowUpRef <- <((Key / Index) ('.' (Key / Index))*)> */ func() bool { - position240, tokenIndex240, depth240 := position, tokenIndex, depth + position250, tokenIndex250, depth250 := position, tokenIndex, depth { - position241 := position + position251 := position depth++ { - position242, tokenIndex242, depth242 := position, tokenIndex, depth + position252, tokenIndex252, depth252 := position, tokenIndex, depth if !_rules[ruleKey]() { - goto l243 + goto l253 } - goto l242 - l243: - position, tokenIndex, depth = position242, tokenIndex242, depth242 + goto l252 + l253: + position, tokenIndex, depth = position252, tokenIndex252, depth252 if !_rules[ruleIndex]() { - goto l240 + goto l250 } } - l242: - l244: + l252: + l254: { - position245, tokenIndex245, depth245 := position, tokenIndex, depth + position255, tokenIndex255, depth255 := position, tokenIndex, depth if buffer[position] != rune('.') { - goto l245 + goto l255 } position++ { - position246, tokenIndex246, depth246 := position, tokenIndex, depth + position256, tokenIndex256, depth256 := position, tokenIndex, depth if !_rules[ruleKey]() { - goto l247 + goto l257 } - goto l246 - l247: - position, tokenIndex, depth = position246, tokenIndex246, depth246 + goto l256 + l257: + position, tokenIndex, depth = position256, tokenIndex256, depth256 if !_rules[ruleIndex]() { - goto l245 + goto l255 } } - l246: - goto l244 - l245: - position, tokenIndex, depth = position245, tokenIndex245, depth245 + l256: + goto l254 + l255: + position, tokenIndex, depth = position255, tokenIndex255, depth255 } depth-- - add(ruleFollowUpRef, position241) + add(ruleFollowUpRef, position251) } return true - l240: - position, tokenIndex, depth = position240, tokenIndex240, depth240 + l250: + position, tokenIndex, depth = position250, tokenIndex250, depth250 return false }, - /* 59 Key <- <(([a-z] / [A-Z] / [0-9] / '_') ([a-z] / [A-Z] / [0-9] / '_' / '-')* (':' ([a-z] / [A-Z] / [0-9] / '_') ([a-z] / [A-Z] / [0-9] / '_' / '-')*)?)> */ + /* 62 Key <- <(([a-z] / [A-Z] / [0-9] / '_') ([a-z] / [A-Z] / [0-9] / '_' / '-')* (':' ([a-z] / [A-Z] / [0-9] / '_') ([a-z] / [A-Z] / [0-9] / '_' / '-')*)?)> */ func() bool { - position248, tokenIndex248, depth248 := position, tokenIndex, depth + position258, tokenIndex258, depth258 := position, tokenIndex, depth { - position249 := position + position259 := position depth++ { - position250, tokenIndex250, depth250 := position, tokenIndex, depth + position260, tokenIndex260, depth260 := position, tokenIndex, depth if c := buffer[position]; c < rune('a') || c > rune('z') { - goto l251 + goto l261 } position++ - goto l250 - l251: - position, tokenIndex, depth = position250, tokenIndex250, depth250 + goto l260 + l261: + position, tokenIndex, depth = position260, tokenIndex260, depth260 if c := buffer[position]; c < rune('A') || c > rune('Z') { - goto l252 + goto l262 } position++ - goto l250 - l252: - position, tokenIndex, depth = position250, tokenIndex250, depth250 + goto l260 + l262: + position, tokenIndex, depth = position260, tokenIndex260, depth260 if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l253 + goto l263 } position++ - goto l250 - l253: - position, tokenIndex, depth = position250, tokenIndex250, depth250 + goto l260 + l263: + position, tokenIndex, depth = position260, tokenIndex260, depth260 if buffer[position] != rune('_') { - goto l248 + goto l258 } position++ } - l250: - l254: + l260: + l264: { - position255, tokenIndex255, depth255 := position, tokenIndex, depth + position265, tokenIndex265, depth265 := position, tokenIndex, depth { - position256, tokenIndex256, depth256 := position, tokenIndex, depth + position266, tokenIndex266, depth266 := position, tokenIndex, depth if c := buffer[position]; c < rune('a') || c > rune('z') { - goto l257 + goto l267 } position++ - goto l256 - l257: - position, tokenIndex, depth = position256, tokenIndex256, depth256 + goto l266 + l267: + position, tokenIndex, depth = position266, tokenIndex266, depth266 if c := buffer[position]; c < rune('A') || c > rune('Z') { - goto l258 + goto l268 } position++ - goto l256 - l258: - position, tokenIndex, depth = position256, tokenIndex256, depth256 + goto l266 + l268: + position, tokenIndex, depth = position266, tokenIndex266, depth266 if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l259 + goto l269 } position++ - goto l256 - l259: - position, tokenIndex, depth = position256, tokenIndex256, depth256 + goto l266 + l269: + position, tokenIndex, depth = position266, tokenIndex266, depth266 if buffer[position] != rune('_') { - goto l260 + goto l270 } position++ - goto l256 - l260: - position, tokenIndex, depth = position256, tokenIndex256, depth256 + goto l266 + l270: + position, tokenIndex, depth = position266, tokenIndex266, depth266 if buffer[position] != rune('-') { - goto l255 + goto l265 } position++ } - l256: - goto l254 - l255: - position, tokenIndex, depth = position255, tokenIndex255, depth255 + l266: + goto l264 + l265: + position, tokenIndex, depth = position265, tokenIndex265, depth265 } { - position261, tokenIndex261, depth261 := position, tokenIndex, depth + position271, tokenIndex271, depth271 := position, tokenIndex, depth if buffer[position] != rune(':') { - goto l261 + goto l271 } position++ { - position263, tokenIndex263, depth263 := position, tokenIndex, depth + position273, tokenIndex273, depth273 := position, tokenIndex, depth if c := buffer[position]; c < rune('a') || c > rune('z') { - goto l264 + goto l274 } position++ - goto l263 - l264: - position, tokenIndex, depth = position263, tokenIndex263, depth263 + goto l273 + l274: + position, tokenIndex, depth = position273, tokenIndex273, depth273 if c := buffer[position]; c < rune('A') || c > rune('Z') { - goto l265 + goto l275 } position++ - goto l263 - l265: - position, tokenIndex, depth = position263, tokenIndex263, depth263 + goto l273 + l275: + position, tokenIndex, depth = position273, tokenIndex273, depth273 if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l266 + goto l276 } position++ - goto l263 - l266: - position, tokenIndex, depth = position263, tokenIndex263, depth263 + goto l273 + l276: + position, tokenIndex, depth = position273, tokenIndex273, depth273 if buffer[position] != rune('_') { - goto l261 + goto l271 } position++ } - l263: - l267: + l273: + l277: { - position268, tokenIndex268, depth268 := position, tokenIndex, depth + position278, tokenIndex278, depth278 := position, tokenIndex, depth { - position269, tokenIndex269, depth269 := position, tokenIndex, depth + position279, tokenIndex279, depth279 := position, tokenIndex, depth if c := buffer[position]; c < rune('a') || c > rune('z') { - goto l270 + goto l280 } position++ - goto l269 - l270: - position, tokenIndex, depth = position269, tokenIndex269, depth269 + goto l279 + l280: + position, tokenIndex, depth = position279, tokenIndex279, depth279 if c := buffer[position]; c < rune('A') || c > rune('Z') { - goto l271 + goto l281 } position++ - goto l269 - l271: - position, tokenIndex, depth = position269, tokenIndex269, depth269 + goto l279 + l281: + position, tokenIndex, depth = position279, tokenIndex279, depth279 if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l272 + goto l282 } position++ - goto l269 - l272: - position, tokenIndex, depth = position269, tokenIndex269, depth269 + goto l279 + l282: + position, tokenIndex, depth = position279, tokenIndex279, depth279 if buffer[position] != rune('_') { - goto l273 + goto l283 } position++ - goto l269 - l273: - position, tokenIndex, depth = position269, tokenIndex269, depth269 + goto l279 + l283: + position, tokenIndex, depth = position279, tokenIndex279, depth279 if buffer[position] != rune('-') { - goto l268 + goto l278 } position++ } - l269: - goto l267 - l268: - position, tokenIndex, depth = position268, tokenIndex268, depth268 + l279: + goto l277 + l278: + position, tokenIndex, depth = position278, tokenIndex278, depth278 } - goto l262 - l261: - position, tokenIndex, depth = position261, tokenIndex261, depth261 + goto l272 + l271: + position, tokenIndex, depth = position271, tokenIndex271, depth271 } - l262: + l272: depth-- - add(ruleKey, position249) + add(ruleKey, position259) } return true - l248: - position, tokenIndex, depth = position248, tokenIndex248, depth248 + l258: + position, tokenIndex, depth = position258, tokenIndex258, depth258 return false }, - /* 60 Index <- <('[' [0-9]+ ']')> */ + /* 63 Index <- <('[' [0-9]+ ']')> */ func() bool { - position274, tokenIndex274, depth274 := position, tokenIndex, depth + position284, tokenIndex284, depth284 := position, tokenIndex, depth { - position275 := position + position285 := position depth++ if buffer[position] != rune('[') { - goto l274 + goto l284 } position++ if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l274 + goto l284 } position++ - l276: + l286: { - position277, tokenIndex277, depth277 := position, tokenIndex, depth + position287, tokenIndex287, depth287 := position, tokenIndex, depth if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l277 + goto l287 } position++ - goto l276 - l277: - position, tokenIndex, depth = position277, tokenIndex277, depth277 + goto l286 + l287: + position, tokenIndex, depth = position287, tokenIndex287, depth287 } if buffer[position] != rune(']') { - goto l274 + goto l284 } position++ depth-- - add(ruleIndex, position275) + add(ruleIndex, position285) } return true - l274: - position, tokenIndex, depth = position274, tokenIndex274, depth274 + l284: + position, tokenIndex, depth = position284, tokenIndex284, depth284 return false }, - /* 61 ws <- <(' ' / '\t' / '\n' / '\r')*> */ + /* 64 ws <- <(' ' / '\t' / '\n' / '\r')*> */ func() bool { { - position279 := position + position289 := position depth++ - l280: + l290: { - position281, tokenIndex281, depth281 := position, tokenIndex, depth + position291, tokenIndex291, depth291 := position, tokenIndex, depth { - position282, tokenIndex282, depth282 := position, tokenIndex, depth + position292, tokenIndex292, depth292 := position, tokenIndex, depth if buffer[position] != rune(' ') { - goto l283 + goto l293 } position++ - goto l282 - l283: - position, tokenIndex, depth = position282, tokenIndex282, depth282 + goto l292 + l293: + position, tokenIndex, depth = position292, tokenIndex292, depth292 if buffer[position] != rune('\t') { - goto l284 + goto l294 } position++ - goto l282 - l284: - position, tokenIndex, depth = position282, tokenIndex282, depth282 + goto l292 + l294: + position, tokenIndex, depth = position292, tokenIndex292, depth292 if buffer[position] != rune('\n') { - goto l285 + goto l295 } position++ - goto l282 - l285: - position, tokenIndex, depth = position282, tokenIndex282, depth282 + goto l292 + l295: + position, tokenIndex, depth = position292, tokenIndex292, depth292 if buffer[position] != rune('\r') { - goto l281 + goto l291 } position++ } - l282: - goto l280 - l281: - position, tokenIndex, depth = position281, tokenIndex281, depth281 + l292: + goto l290 + l291: + position, tokenIndex, depth = position291, tokenIndex291, depth291 } depth-- - add(rulews, position279) + add(rulews, position289) } return true }, - /* 62 req_ws <- <(' ' / '\t' / '\n' / '\r')+> */ + /* 65 req_ws <- <(' ' / '\t' / '\n' / '\r')+> */ func() bool { - position286, tokenIndex286, depth286 := position, tokenIndex, depth + position296, tokenIndex296, depth296 := position, tokenIndex, depth { - position287 := position + position297 := position depth++ { - position290, tokenIndex290, depth290 := position, tokenIndex, depth + position300, tokenIndex300, depth300 := position, tokenIndex, depth if buffer[position] != rune(' ') { - goto l291 + goto l301 } position++ - goto l290 - l291: - position, tokenIndex, depth = position290, tokenIndex290, depth290 + goto l300 + l301: + position, tokenIndex, depth = position300, tokenIndex300, depth300 if buffer[position] != rune('\t') { - goto l292 + goto l302 } position++ - goto l290 - l292: - position, tokenIndex, depth = position290, tokenIndex290, depth290 + goto l300 + l302: + position, tokenIndex, depth = position300, tokenIndex300, depth300 if buffer[position] != rune('\n') { - goto l293 + goto l303 } position++ - goto l290 - l293: - position, tokenIndex, depth = position290, tokenIndex290, depth290 + goto l300 + l303: + position, tokenIndex, depth = position300, tokenIndex300, depth300 if buffer[position] != rune('\r') { - goto l286 + goto l296 } position++ } - l290: - l288: + l300: + l298: { - position289, tokenIndex289, depth289 := position, tokenIndex, depth + position299, tokenIndex299, depth299 := position, tokenIndex, depth { - position294, tokenIndex294, depth294 := position, tokenIndex, depth + position304, tokenIndex304, depth304 := position, tokenIndex, depth if buffer[position] != rune(' ') { - goto l295 + goto l305 } position++ - goto l294 - l295: - position, tokenIndex, depth = position294, tokenIndex294, depth294 + goto l304 + l305: + position, tokenIndex, depth = position304, tokenIndex304, depth304 if buffer[position] != rune('\t') { - goto l296 + goto l306 } position++ - goto l294 - l296: - position, tokenIndex, depth = position294, tokenIndex294, depth294 + goto l304 + l306: + position, tokenIndex, depth = position304, tokenIndex304, depth304 if buffer[position] != rune('\n') { - goto l297 + goto l307 } position++ - goto l294 - l297: - position, tokenIndex, depth = position294, tokenIndex294, depth294 + goto l304 + l307: + position, tokenIndex, depth = position304, tokenIndex304, depth304 if buffer[position] != rune('\r') { - goto l289 + goto l299 } position++ } - l294: - goto l288 - l289: - position, tokenIndex, depth = position289, tokenIndex289, depth289 + l304: + goto l298 + l299: + position, tokenIndex, depth = position299, tokenIndex299, depth299 } depth-- - add(rulereq_ws, position287) + add(rulereq_ws, position297) } return true - l286: - position, tokenIndex, depth = position286, tokenIndex286, depth286 + l296: + position, tokenIndex, depth = position296, tokenIndex296, depth296 return false }, } diff --git a/dynaml/empty.go b/dynaml/empty.go deleted file mode 100644 index d4088d2..0000000 --- a/dynaml/empty.go +++ /dev/null @@ -1,15 +0,0 @@ -package dynaml - -import ( - "github.com/cloudfoundry-incubator/spiff/yaml" -) - -type EmptyHashExpr struct{} - -func (e EmptyHashExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { - return make(map[string]yaml.Node), DefaultInfo(), true -} - -func (e EmptyHashExpr) String() string { - return "{}" -} diff --git a/dynaml/map.go b/dynaml/map.go new file mode 100644 index 0000000..eddc9ff --- /dev/null +++ b/dynaml/map.go @@ -0,0 +1,54 @@ +package dynaml + +import ( + "fmt" + "github.com/cloudfoundry-incubator/spiff/yaml" +) + +type CreateMapExpr struct { + Assignments []Assignment +} + +func (e CreateMapExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { + + newMap := make(map[string]yaml.Node) + info := DefaultInfo() + resolved := true + + for _, a := range e.Assignments { + key, info, ok := ResolveExpressionOrPushEvaluation(&a.Key, &resolved, &info, binding, locally) + if !ok || !resolved { + return e, info, ok + } + + kstr, ok := key.(string) + if !ok { + return info.Error("assignment target must evaluate to string") + } + val, info, ok := ResolveExpressionOrPushEvaluation(&a.Value, &resolved, &info, binding, locally) + if !ok || !resolved { + return e, info, ok + } + newMap[kstr] = node(val, binding) + } + return newMap, DefaultInfo(), true +} + +func (e CreateMapExpr) String() string { + result := "{" + sep := " " + for _, a := range e.Assignments { + result += sep + a.String() + sep = ", " + } + return result + " }" +} + +type Assignment struct { + Key Expression + Value Expression +} + +func (e Assignment) String() string { + return fmt.Sprintf("%s = %s", e.Key, e.Value) +} diff --git a/dynaml/empty_test.go b/dynaml/map_test.go similarity index 78% rename from dynaml/empty_test.go rename to dynaml/map_test.go index 5fde37b..79da665 100644 --- a/dynaml/empty_test.go +++ b/dynaml/map_test.go @@ -9,6 +9,6 @@ import ( var _ = Describe("empty", func() { It("evaluates to empty hash", func() { - Expect(EmptyHashExpr{}).To(EvaluateAs(make(map[string]yaml.Node), FakeBinding{})) + Expect(CreateMapExpr{}).To(EvaluateAs(make(map[string]yaml.Node), FakeBinding{})) }) }) diff --git a/dynaml/parser.go b/dynaml/parser.go index ff9f3d7..3606d50 100644 --- a/dynaml/parser.go +++ b/dynaml/parser.go @@ -122,8 +122,15 @@ func buildExpression(grammar *DynamlGrammar, path []string, stubPath []string) E tokens.Push(NilExpr{}) case ruleUndefined: tokens.Push(UndefinedExpr{}) - case ruleEmptyHash: - tokens.Push(EmptyHashExpr{}) + case ruleCreateMap: + tokens.Push(CreateMapExpr{}) + case ruleAssignment: + rhs := tokens.Pop() + lhs := tokens.Pop() + m := tokens.Pop().(CreateMapExpr) + m.Assignments = append(m.Assignments, Assignment{lhs, rhs}) + tokens.Push(m) + case ruleBoolean: tokens.Push(BooleanExpr{contents == "true"}) case ruleString: @@ -260,6 +267,8 @@ func buildExpression(grammar *DynamlGrammar, path []string, stubPath []string) E case ruleGrouped: case ruleLevel0, ruleLevel1, ruleLevel2, ruleLevel3, ruleLevel4, ruleLevel5, ruleLevel6, ruleLevel7: case ruleExpression: + case ruleMap: + case ruleAssignments: case rulews: case rulereq_ws: default: @@ -305,6 +314,15 @@ func (s *tokenStack) Pop() Expression { return front.Value.(Expression) } +func (s *tokenStack) Peek() Expression { + front := s.Front() + if front == nil { + return nil + } + + return front.Value.(Expression) +} + func (s *tokenStack) Push(expr Expression) { s.PushFront(expr) } diff --git a/flow/flow_test.go b/flow/flow_test.go index 0785687..b435c5a 100644 --- a/flow/flow_test.go +++ b/flow/flow_test.go @@ -3521,6 +3521,50 @@ map: (( makemap("peter", 23, "paul", 22) )) map: paul: 22 peter: 23 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles map literals", func() { + source := parseYAML(` +--- +peter: + name: peter + age: 23 +paul: + name: paul + age: 22 +map: (( { peter.name=peter.age, paul.name=paul.age } )) +`) + resolved := parseYAML(` +--- +peter: + name: peter + age: 23 +paul: + name: paul + age: 22 +map: + paul: 22 + peter: 23 +`) + Expect(source).To(FlowAs(resolved)) + }) + + It("handles nested map literals", func() { + source := parseYAML(` +--- +name: peter +age: 23 +map: (( { "alice" = {}, name = age } )) +`) + resolved := parseYAML(` +--- +name: peter +age: 23 +map: + alice: {} + peter: 23 `) Expect(source).To(FlowAs(resolved)) }) From 7a5723439bbb8a9f079383a1f3a13c5e9004a4dd Mon Sep 17 00:00:00 2001 From: Uwe Krueger Date: Tue, 14 Jun 2016 22:44:01 +0200 Subject: [PATCH 6/6] examples for map literals --- README.md | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ca29ff..3b4eff3 100644 --- a/README.md +++ b/README.md @@ -609,6 +609,9 @@ bar: b: 3 ``` +This expression just adds new entries to the actual list. It does not merge +existing entries with the content described by the merge expression. + #### Merging lists ```yaml @@ -2474,8 +2477,91 @@ networks: peter: 26 # kept, because mapping source not available in mapping.yml ``` - This can be used to add an intermediate stub, that offers a dedicated configuration interface and - contains logic to map this interface to a manifest structure already defining default values. + This can be used to add an intermediate stub, that offers a dedicated + configuration interface and contains logic to map this interface to a manifest + structure already defining default values. + +- _Templates versus map literals_ + + As described earlier templates can be used inside functions and mappings to + easily describe complex data structures based on expressions refering to + parameters. Before the introduction of map literals this was the only way + to achieve such behaviour. The advantage is the possibility to describe + the complex structure as regular part of a yaml document, which allows using + the regular yaml formatting facilitating readability. + + e.g.: + + ```yaml + scaling: + runner_z1: 10 + router_z1: 4 + + jobs: (( sum[scaling|[]|s,k,v|->s [ *templates.job ] ] )) + + templates: + job: + <<: (( &template )) + name: (( k )) + instances: (( v )) + ``` + + evaluates to + + ```yaml + scaling: + runner_z1: 10 + router_z1: 4 + + jobs: + - instances: 4 + name: router_z1 + - instances: 10 + name: runner_z1 + ... + ``` + + With map literals this construct can significantly be simplified + + ```yaml + scaling: + runner_z1: 10 + router_z1: 4 + + jobs: (( sum[scaling|[]|s,k,v|->s [ {"name"=k, "value"=v} ] ] )) + ``` + + Nevertheless the first, template based version might still be useful, if + the data structures are more complex, deeper or with complex value expressions. + For such a scenario the description of the data structure as template should be + preferred. It provides a much better readability, because every field, list + entry and value expression can be put into dedicated lines. + + But there is still a qualitative difference. While map literals are part of a + single expression always evaluated as a whole before map fields are available + for referencing, templates are evaluated as regular yaml documents that might + contain multiple fields with separate expressions referencing each other. + + e.g.: + + ```yaml + range: (( (|cidr,first,size|->(*templates.addr).range)("10.0.0.0/16",10,255) )) + + templates: + addr: + <<: (( &template )) + base: (( min_ip(cidr) )) + start: (( base + first )) + end: (( start + size - 1 )) + range: (( start " - " end )) + ``` + + evaluates `range` to + + ```yaml + range: 10.0.0.10 - 10.0.1.8 + ... + ``` # Error Reporting