diff --git a/cmd/webhook/admission/admission_test.go b/cmd/webhook/admission/admission_test.go index 424fb0b9a7..84b9879a56 100644 --- a/cmd/webhook/admission/admission_test.go +++ b/cmd/webhook/admission/admission_test.go @@ -107,7 +107,7 @@ func TestRouteGroupAdmitter(t *testing.T) { { name: "invalid eskip filters", inputFile: "rg-with-invalid-eskip-filters.json", - message: "parse failed after token status, position 11: syntax error", + message: "parse failed after token status, position 6: syntax error", }, { name: "valid eskip predicates", @@ -121,7 +121,7 @@ func TestRouteGroupAdmitter(t *testing.T) { { name: "invalid eskip filters and predicates", inputFile: "rg-with-invalid-eskip-filters-and-predicates.json", - message: "parse failed after token status, position 11: syntax error\\nparse failed after token Method, position 6: syntax error", + message: "parse failed after token status, position 6: syntax error\\nparse failed after token Method, position 6: syntax error", }, { name: "invalid routgroup multiple filters per json/yaml array item", @@ -185,12 +185,12 @@ func TestIngressAdmitter(t *testing.T) { { name: "invalid eskip filters", inputFile: "invalid-filters.json", - message: `invalid \"zalando.org/skipper-filter\" annotation: parse failed after token this, position 9: syntax error`, + message: `invalid \"zalando.org/skipper-filter\" annotation: parse failed after token this, position 4: syntax error`, }, { name: "invalid eskip predicates", inputFile: "invalid-predicates.json", - message: `invalid \"zalando.org/skipper-predicate\" annotation: parse failed after token ), position 15: syntax error`, + message: `invalid \"zalando.org/skipper-predicate\" annotation: parse failed after token ), position 15: unexpected token`, }, { name: "invalid eskip routes", @@ -200,7 +200,7 @@ func TestIngressAdmitter(t *testing.T) { { name: "invalid eskip filters and predicates", inputFile: "invalid-filters-and-predicates.json", - message: `invalid \"zalando.org/skipper-filter\" annotation: parse failed after token this, position 9: syntax error\ninvalid \"zalando.org/skipper-predicate\" annotation: parse failed after token ), position 15: syntax error`, + message: `invalid \"zalando.org/skipper-filter\" annotation: parse failed after token this, position 4: syntax error\ninvalid \"zalando.org/skipper-predicate\" annotation: parse failed after token ), position 15: unexpected token`, }, } { t.Run(tc.name, func(t *testing.T) { diff --git a/dataclients/kubernetes/testdata/routegroups/convert/failing-filter.log b/dataclients/kubernetes/testdata/routegroups/convert/failing-filter.log index b90ceb446a..ba158256c3 100644 --- a/dataclients/kubernetes/testdata/routegroups/convert/failing-filter.log +++ b/dataclients/kubernetes/testdata/routegroups/convert/failing-filter.log @@ -1 +1 @@ -\[routegroup\] parse failed after token foo, position 8: syntax error +\[routegroup\] parse failed after token foo, position 3: syntax error diff --git a/dataclients/kubernetes/testdata/routegroups/convert/no-catchall-for-failed-route-group.log b/dataclients/kubernetes/testdata/routegroups/convert/no-catchall-for-failed-route-group.log index b90ceb446a..ba158256c3 100644 --- a/dataclients/kubernetes/testdata/routegroups/convert/no-catchall-for-failed-route-group.log +++ b/dataclients/kubernetes/testdata/routegroups/convert/no-catchall-for-failed-route-group.log @@ -1 +1 @@ -\[routegroup\] parse failed after token foo, position 8: syntax error +\[routegroup\] parse failed after token foo, position 3: syntax error diff --git a/eskip/eskip.go b/eskip/eskip.go index 90cad17401..e68ab5d22c 100644 --- a/eskip/eskip.go +++ b/eskip/eskip.go @@ -588,8 +588,22 @@ type eskipLexParser struct { var parserPool sync.Pool -// executes the parser. -func parse(code string) ([]*parsedRoute, error) { +func parseDocument(code string) ([]*parsedRoute, error) { + routes, _, _, err := parse(start_document, code) + return routes, err +} + +func parsePredicates(code string) ([]*Predicate, error) { + _, predicates, _, err := parse(start_predicates, code) + return predicates, err +} + +func parseFilters(code string) ([]*Filter, error) { + _, _, filters, err := parse(start_filters, code) + return filters, err +} + +func parse(start int, code string) ([]*parsedRoute, []*Predicate, []*Filter, error) { lp, ok := parserPool.Get().(*eskipLexParser) if !ok { lp = &eskipLexParser{} @@ -598,16 +612,19 @@ func parse(code string) ([]*parsedRoute, error) { } defer parserPool.Put(lp) - lp.lexer.init(code) + lexer := &lp.lexer + lexer.init(start, code) - lp.parser.Parse(&lp.lexer) + lp.parser.Parse(lexer) - return lp.lexer.routes, lp.lexer.err + // Do not return lexer to avoid reading lexer fields after returning eskipLexParser to the pool. + // Let the caller decide which of return values to use based on the start token. + return lexer.routes, lexer.predicates, lexer.filters, lexer.err } // Parses a route expression or a routing document to a set of route definitions. func Parse(code string) ([]*Route, error) { - parsedRoutes, err := parse(code) + parsedRoutes, err := parseDocument(code) if err != nil { return nil, err } @@ -662,12 +679,7 @@ func ParseFilters(f string) ([]*Filter, error) { return nil, nil } - rs, err := parse("* -> " + f + " -> ") - if err != nil { - return nil, err - } - - return rs[0].filters, nil + return parseFilters(f) } // ParsePredicates parses a set of predicates (combined by '&&') into @@ -678,7 +690,7 @@ func ParsePredicates(p string) ([]*Predicate, error) { return nil, nil } - rs, err := parse(p + " -> ") + rs, err := parsePredicates(p) if err != nil { return nil, err } @@ -687,9 +699,8 @@ func ParsePredicates(p string) ([]*Predicate, error) { return nil, nil } - r := rs[0] - ps := make([]*Predicate, 0, len(r.predicates)) - for _, p := range r.predicates { + ps := make([]*Predicate, 0, len(rs)) + for _, p := range rs { if p.Name != "*" { ps = append(ps, p) } diff --git a/eskip/eskip_test.go b/eskip/eskip_test.go index 4a5f72c93e..bbc832dec0 100644 --- a/eskip/eskip_test.go +++ b/eskip/eskip_test.go @@ -239,19 +239,39 @@ func TestParseFilters(t *testing.T) { "error", "trallala", nil, - // TODO: fix position - "parse failed after token ->, position 16: syntax error", + "parse failed after token trallala, position 8: syntax error", }, { "error 2", "foo-bar", nil, - // TODO: fix position - "parse failed after token foo, position 8: syntax error", + "parse failed after token foo, position 3: syntax error", }, { "success", `filter1(3.14) -> filter2("key", 42)`, []*Filter{{Name: "filter1", Args: []interface{}{3.14}}, {Name: "filter2", Args: []interface{}{"key", float64(42)}}}, "", + }, { + "a comment produces empty filters without error", + "// a comment", + nil, + "", + }, { + "a trailing comment is ignored", + `filter1(3.14) -> filter2("key", 42) // a trailing comment`, + []*Filter{{Name: "filter1", Args: []interface{}{3.14}}, {Name: "filter2", Args: []interface{}{"key", float64(42)}}}, + "", + }, { + "a comment before is ignored", + `// a comment on a separate line + filter1(3.14) -> filter2("key", 42)`, + []*Filter{{Name: "filter1", Args: []interface{}{3.14}}, {Name: "filter2", Args: []interface{}{"key", float64(42)}}}, + "", + }, { + "a comment after is ignored", + `filter1(3.14) -> filter2("key", 42) + // a comment on a separate line`, + []*Filter{{Name: "filter1", Args: []interface{}{3.14}}, {Name: "filter2", Args: []interface{}{"key", float64(42)}}}, + "", }} { t.Run(ti.msg, func(t *testing.T) { fs, err := ParseFilters(ti.expression) @@ -306,6 +326,29 @@ func TestParsePredicates(t *testing.T) { title: "comment fuzz 2", // "\x2f\x2f..." == "//..." input: "\x2f\x2f\x00\x00\x00\xe6\xfe\x00\x00\x2f\x00\x00\x00\x00\x00\x00\x00\xe6\xfe\x00\x00\x2f\x00\x00\x00\x00", expected: nil, + }, { + title: "a trailing comment is ignored", + input: `Foo("bar") && Baz("qux") // a trailing comment`, + expected: []*Predicate{ + {Name: "Foo", Args: []interface{}{"bar"}}, + {Name: "Baz", Args: []interface{}{"qux"}}, + }, + }, { + title: "a comment before is ignored", + input: `// a comment on a separate line + Foo("bar") && Baz("qux")`, + expected: []*Predicate{ + {Name: "Foo", Args: []interface{}{"bar"}}, + {Name: "Baz", Args: []interface{}{"qux"}}, + }, + }, { + title: "a comment after is ignored", + input: `Foo("bar") && Baz("qux") + // a comment on a separate line`, + expected: []*Predicate{ + {Name: "Foo", Args: []interface{}{"bar"}}, + {Name: "Baz", Args: []interface{}{"qux"}}, + }, }} { t.Run(test.title, func(t *testing.T) { ps, err := ParsePredicates(test.input) diff --git a/eskip/lexer.go b/eskip/lexer.go index c49942f190..875aafd806 100644 --- a/eskip/lexer.go +++ b/eskip/lexer.go @@ -14,12 +14,15 @@ type token struct { type charPredicate func(byte) bool type eskipLex struct { + start int code string lastToken string lastRouteID string err error initialLength int routes []*parsedRoute + predicates []*Predicate + filters []*Filter } type fixedScanner token @@ -62,7 +65,8 @@ func (fs *fixedScanner) scan(code string) (t token, rest string, err error) { return token(*fs), code[len(fs.val):], nil } -func (l *eskipLex) init(code string) { +func (l *eskipLex) init(start int, code string) { + l.start = start l.code = code l.initialLength = len(code) } @@ -352,6 +356,13 @@ func (l *eskipLex) next() (token, error) { } func (l *eskipLex) Lex(lval *eskipSymType) int { + // first emit the start token + if l.start != 0 { + start := l.start + l.start = 0 + return start + } + t, err := l.next() if err == eof { return -1 diff --git a/eskip/parser.go b/eskip/parser.go index 569ff7f323..52de78b4a5 100644 --- a/eskip/parser.go +++ b/eskip/parser.go @@ -31,8 +31,6 @@ type eskipSymType struct { dynamic bool lbBackend bool numval float64 - stringval string - regexpval string stringvals []string lbAlgorithm string lbEndpoints []string @@ -55,6 +53,9 @@ const stringliteral = 57359 const symbol = 57360 const openarrow = 57361 const closearrow = 57362 +const start_document = 57363 +const start_predicates = 57364 +const start_filters = 57365 var eskipToknames = [...]string{ "$end", @@ -77,6 +78,9 @@ var eskipToknames = [...]string{ "symbol", "openarrow", "closearrow", + "start_document", + "start_predicates", + "start_filters", } var eskipStatenames = [...]string{} @@ -93,62 +97,65 @@ var eskipExca = [...]int8{ const eskipPrivate = 57344 -const eskipLast = 64 +const eskipLast = 68 var eskipAct = [...]int8{ - 34, 40, 32, 31, 26, 19, 22, 23, 24, 27, - 29, 28, 21, 48, 36, 9, 37, 27, 41, 9, - 18, 27, 27, 7, 10, 49, 43, 15, 14, 42, - 16, 16, 8, 55, 4, 45, 30, 50, 44, 21, - 45, 15, 3, 47, 46, 17, 38, 51, 52, 13, - 53, 42, 54, 12, 25, 11, 39, 35, 33, 20, - 5, 6, 2, 1, + 48, 39, 17, 29, 38, 2, 3, 4, 32, 33, + 34, 31, 18, 36, 11, 55, 16, 43, 8, 42, + 50, 49, 13, 18, 41, 50, 13, 28, 44, 59, + 23, 45, 19, 24, 26, 15, 37, 30, 27, 12, + 24, 7, 53, 51, 52, 52, 56, 57, 23, 44, + 54, 21, 22, 20, 58, 46, 25, 21, 60, 9, + 35, 47, 40, 14, 10, 6, 5, 1, } var eskipPact = [...]int16{ - 14, -1000, 11, -1000, -1000, 49, 10, -1000, 19, -1000, - 2, -8, 10, -1000, 20, -1000, 4, -1000, 33, -1000, - 40, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 0, 15, - -1000, 31, -1000, -1000, -1000, -1000, -1000, -1000, -8, -7, - 16, 28, -1000, 4, -1000, 4, -1000, -1000, -1000, 5, - 5, 26, -1000, -1000, 16, -1000, + -16, -1000, 21, 17, 5, -1000, 19, -1000, -1000, 47, + 17, -1000, 22, -1000, 53, 29, 50, -1000, 23, 9, + -6, 17, -1000, -1000, 7, 5, 7, -1000, 40, -1000, + 49, -1000, -1000, -1000, -1000, -1000, 3, -1000, 36, -1000, + -1000, -1000, -1000, -1000, -1000, 35, -6, -5, 37, 38, + -1000, -1000, 7, -1000, -1000, -1000, 12, 8, -1000, -1000, + 37, } var eskipPgo = [...]int8{ - 0, 63, 62, 42, 34, 61, 60, 5, 59, 23, - 3, 4, 2, 58, 0, 57, 1, 56, 54, + 0, 67, 66, 59, 16, 65, 41, 18, 64, 3, + 14, 4, 2, 1, 62, 0, 61, 60, } var eskipR1 = [...]int8{ - 0, 1, 1, 2, 2, 2, 2, 4, 5, 3, - 3, 6, 6, 9, 9, 8, 8, 11, 10, 10, - 10, 12, 12, 12, 16, 16, 17, 17, 18, 7, - 7, 7, 7, 7, 13, 14, 15, + 0, 1, 1, 1, 1, 1, 2, 2, 5, 5, + 5, 5, 7, 8, 6, 6, 3, 3, 10, 10, + 4, 4, 12, 11, 11, 11, 13, 13, 13, 15, + 15, 16, 16, 17, 9, 9, 9, 9, 9, 14, } var eskipR2 = [...]int8{ - 0, 1, 1, 0, 1, 3, 2, 2, 2, 3, - 5, 1, 3, 1, 4, 1, 3, 4, 0, 1, - 3, 1, 1, 1, 1, 3, 1, 3, 3, 1, - 1, 1, 1, 1, 1, 1, 1, + 0, 2, 1, 2, 1, 2, 1, 1, 0, 1, + 3, 2, 2, 2, 3, 5, 1, 3, 1, 4, + 1, 3, 4, 0, 1, 3, 1, 1, 1, 1, + 3, 1, 3, 3, 1, 1, 1, 1, 1, 1, } var eskipChk = [...]int16{ - -1000, -1, -2, -3, -4, -6, -5, -9, 18, 5, - 13, 6, 4, -3, 18, 8, 11, -4, 18, -7, - -8, -14, 14, 15, 16, -18, -11, 17, 19, 18, - -9, -10, -12, -13, -14, -15, 10, 12, 6, -17, - -16, 18, -14, 11, 7, 9, -7, -11, 20, 9, - 9, -10, -12, -14, -16, 7, + -1000, -1, 21, 22, 23, -2, -5, -6, -7, -3, + -8, -10, 18, 5, -3, 18, -4, -12, 18, 13, + 6, 4, -6, 8, 11, 6, 11, -7, 18, -9, + -4, 17, 14, 15, 16, -17, 19, -10, -11, -13, + -14, 17, 12, 10, -12, -11, 6, -16, -15, 18, + 17, 7, 9, 7, -9, 20, 9, 9, -13, 17, + -15, } var eskipDef = [...]int8{ - 3, -2, 1, 2, 4, 0, 0, 11, 0, 13, - 6, 0, 0, 7, 0, 8, 18, 5, 0, 9, - 0, 29, 30, 31, 32, 33, 15, 35, 0, 0, - 12, 0, 19, 21, 22, 23, 34, 36, 0, 0, - 26, 0, 24, 18, 14, 0, 10, 16, 28, 0, - 0, 0, 20, 25, 27, 17, + 0, -2, 8, 2, 4, 1, 6, 7, 9, 0, + 0, 16, 0, 18, 3, 0, 5, 20, 0, 11, + 0, 0, 12, 13, 23, 0, 23, 10, 0, 14, + 0, 34, 35, 36, 37, 38, 0, 17, 0, 24, + 26, 27, 28, 39, 21, 0, 0, 0, 31, 0, + 29, 19, 0, 22, 15, 33, 0, 0, 25, 30, + 32, } var eskipTok1 = [...]int8{ @@ -157,7 +164,8 @@ var eskipTok1 = [...]int8{ var eskipTok2 = [...]int8{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, 14, 15, 16, 17, 18, 19, 20, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, } var eskipTok3 = [...]int8{ @@ -500,47 +508,72 @@ eskipdefault: switch eskipnt { case 1: + eskipDollar = eskipS[eskippt-2 : eskippt+1] + { + eskiplex.(*eskipLex).routes = eskipDollar[2].routes + } + case 2: + eskipDollar = eskipS[eskippt-1 : eskippt+1] + { + // allow empty or comments only + eskiplex.(*eskipLex).predicates = nil + } + case 3: + eskipDollar = eskipS[eskippt-2 : eskippt+1] + { + eskiplex.(*eskipLex).predicates = eskipDollar[2].predicates + } + case 4: + eskipDollar = eskipS[eskippt-1 : eskippt+1] + { + // allow empty or comments only + eskiplex.(*eskipLex).filters = nil + } + case 5: + eskipDollar = eskipS[eskippt-2 : eskippt+1] + { + eskiplex.(*eskipLex).filters = eskipDollar[2].filters + } + case 6: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.routes = eskipDollar[1].routes - eskiplex.(*eskipLex).routes = eskipVAL.routes } - case 2: + case 7: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.routes = []*parsedRoute{eskipDollar[1].route} - eskiplex.(*eskipLex).routes = eskipVAL.routes } - case 4: + case 9: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.routes = []*parsedRoute{eskipDollar[1].route} } - case 5: + case 10: eskipDollar = eskipS[eskippt-3 : eskippt+1] { eskipVAL.routes = eskipDollar[1].routes eskipVAL.routes = append(eskipVAL.routes, eskipDollar[3].route) } - case 6: + case 11: eskipDollar = eskipS[eskippt-2 : eskippt+1] { eskipVAL.routes = eskipDollar[1].routes } - case 7: + case 12: eskipDollar = eskipS[eskippt-2 : eskippt+1] { eskipVAL.route = eskipDollar[2].route eskipVAL.route.id = eskipDollar[1].token } - case 8: + case 13: eskipDollar = eskipS[eskippt-2 : eskippt+1] { // match symbol and colon to get route id early even if route parsing fails later eskipVAL.token = eskipDollar[1].token eskiplex.(*eskipLex).lastRouteID = eskipDollar[1].token } - case 9: + case 14: eskipDollar = eskipS[eskippt-3 : eskippt+1] { eskipVAL.route = &parsedRoute{ @@ -556,7 +589,7 @@ eskipdefault: eskipDollar[1].predicates = nil eskipDollar[3].lbEndpoints = nil } - case 10: + case 15: eskipDollar = eskipS[eskippt-5 : eskippt+1] { eskipVAL.route = &parsedRoute{ @@ -574,40 +607,40 @@ eskipdefault: eskipDollar[3].filters = nil eskipDollar[5].lbEndpoints = nil } - case 11: + case 16: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.predicates = []*Predicate{eskipDollar[1].predicate} } - case 12: + case 17: eskipDollar = eskipS[eskippt-3 : eskippt+1] { eskipVAL.predicates = eskipDollar[1].predicates eskipVAL.predicates = append(eskipVAL.predicates, eskipDollar[3].predicate) } - case 13: + case 18: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.predicate = &Predicate{"*", nil} } - case 14: + case 19: eskipDollar = eskipS[eskippt-4 : eskippt+1] { eskipVAL.predicate = &Predicate{eskipDollar[1].token, eskipDollar[3].args} eskipDollar[3].args = nil } - case 15: + case 20: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.filters = []*Filter{eskipDollar[1].filter} } - case 16: + case 21: eskipDollar = eskipS[eskippt-3 : eskippt+1] { eskipVAL.filters = eskipDollar[1].filters eskipVAL.filters = append(eskipVAL.filters, eskipDollar[3].filter) } - case 17: + case 22: eskipDollar = eskipS[eskippt-4 : eskippt+1] { eskipVAL.filter = &Filter{ @@ -615,70 +648,70 @@ eskipdefault: Args: eskipDollar[3].args} eskipDollar[3].args = nil } - case 19: + case 24: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.args = []interface{}{eskipDollar[1].arg} } - case 20: + case 25: eskipDollar = eskipS[eskippt-3 : eskippt+1] { eskipVAL.args = eskipDollar[1].args eskipVAL.args = append(eskipVAL.args, eskipDollar[3].arg) } - case 21: + case 26: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.arg = eskipDollar[1].numval } - case 22: + case 27: eskipDollar = eskipS[eskippt-1 : eskippt+1] { - eskipVAL.arg = eskipDollar[1].stringval + eskipVAL.arg = eskipDollar[1].token } - case 23: + case 28: eskipDollar = eskipS[eskippt-1 : eskippt+1] { - eskipVAL.arg = eskipDollar[1].regexpval + eskipVAL.arg = eskipDollar[1].token } - case 24: + case 29: eskipDollar = eskipS[eskippt-1 : eskippt+1] { - eskipVAL.stringvals = []string{eskipDollar[1].stringval} + eskipVAL.stringvals = []string{eskipDollar[1].token} } - case 25: + case 30: eskipDollar = eskipS[eskippt-3 : eskippt+1] { eskipVAL.stringvals = eskipDollar[1].stringvals - eskipVAL.stringvals = append(eskipVAL.stringvals, eskipDollar[3].stringval) + eskipVAL.stringvals = append(eskipVAL.stringvals, eskipDollar[3].token) } - case 26: + case 31: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.lbEndpoints = eskipDollar[1].stringvals } - case 27: + case 32: eskipDollar = eskipS[eskippt-3 : eskippt+1] { eskipVAL.lbAlgorithm = eskipDollar[1].token eskipVAL.lbEndpoints = eskipDollar[3].stringvals } - case 28: + case 33: eskipDollar = eskipS[eskippt-3 : eskippt+1] { eskipVAL.lbAlgorithm = eskipDollar[2].lbAlgorithm eskipVAL.lbEndpoints = eskipDollar[2].lbEndpoints } - case 29: + case 34: eskipDollar = eskipS[eskippt-1 : eskippt+1] { - eskipVAL.backend = eskipDollar[1].stringval + eskipVAL.backend = eskipDollar[1].token eskipVAL.shunt = false eskipVAL.loopback = false eskipVAL.dynamic = false eskipVAL.lbBackend = false } - case 30: + case 35: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.shunt = true @@ -686,7 +719,7 @@ eskipdefault: eskipVAL.dynamic = false eskipVAL.lbBackend = false } - case 31: + case 36: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.shunt = false @@ -694,7 +727,7 @@ eskipdefault: eskipVAL.dynamic = false eskipVAL.lbBackend = false } - case 32: + case 37: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.shunt = false @@ -702,7 +735,7 @@ eskipdefault: eskipVAL.dynamic = true eskipVAL.lbBackend = false } - case 33: + case 38: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.shunt = false @@ -712,21 +745,11 @@ eskipdefault: eskipVAL.lbAlgorithm = eskipDollar[1].lbAlgorithm eskipVAL.lbEndpoints = eskipDollar[1].lbEndpoints } - case 34: + case 39: eskipDollar = eskipS[eskippt-1 : eskippt+1] { eskipVAL.numval = convertNumber(eskipDollar[1].token) } - case 35: - eskipDollar = eskipS[eskippt-1 : eskippt+1] - { - eskipVAL.stringval = eskipDollar[1].token - } - case 36: - eskipDollar = eskipS[eskippt-1 : eskippt+1] - { - eskipVAL.regexpval = eskipDollar[1].token - } } goto eskipstack /* stack new state and value */ } diff --git a/eskip/parser.y b/eskip/parser.y index 86a7048d23..5378360834 100644 --- a/eskip/parser.y +++ b/eskip/parser.y @@ -44,8 +44,6 @@ func convertNumber(s string) float64 { dynamic bool lbBackend bool numval float64 - stringval string - regexpval string stringvals []string lbAlgorithm string lbEndpoints []string @@ -69,17 +67,42 @@ func convertNumber(s string) float64 { %token openarrow %token closearrow +%token start_document; +%token start_predicates; +%token start_filters; + %% +start: + start_document document { + eskiplex.(*eskipLex).routes = $2.routes + } + | + start_predicates { + // allow empty or comments only + eskiplex.(*eskipLex).predicates = nil + } + | + start_predicates predicates { + eskiplex.(*eskipLex).predicates = $2.predicates + } + | + start_filters { + // allow empty or comments only + eskiplex.(*eskipLex).filters = nil + } + | + start_filters filters { + eskiplex.(*eskipLex).filters = $2.filters + } + document: routes { $$.routes = $1.routes - eskiplex.(*eskipLex).routes = $$.routes } | route { $$.routes = []*parsedRoute{$1.route} - eskiplex.(*eskipLex).routes = $$.routes } routes: @@ -197,22 +220,22 @@ arg: $$.arg = $1.numval } | - stringval { - $$.arg = $1.stringval + stringliteral { + $$.arg = $1.token } | - regexpval { - $$.arg = $1.regexpval + regexpliteral { + $$.arg = $1.token } stringvals: - stringval { - $$.stringvals = []string{$1.stringval} + stringliteral { + $$.stringvals = []string{$1.token} } | - stringvals comma stringval { + stringvals comma stringliteral { $$.stringvals = $1.stringvals - $$.stringvals = append($$.stringvals, $3.stringval) + $$.stringvals = append($$.stringvals, $3.token) } lbbackendbody: @@ -232,8 +255,8 @@ lbbackend: } backend: - stringval { - $$.backend = $1.stringval + stringliteral { + $$.backend = $1.token $$.shunt = false $$.loopback = false $$.dynamic = false @@ -275,14 +298,4 @@ numval: $$.numval = convertNumber($1.token) } -stringval: - stringliteral { - $$.stringval = $1.token - } - -regexpval: - regexpliteral { - $$.regexpval = $1.token - } - %% diff --git a/eskip/parser_test.go b/eskip/parser_test.go index a48085037e..ffe226d0ef 100644 --- a/eskip/parser_test.go +++ b/eskip/parser_test.go @@ -82,14 +82,14 @@ func checkSingleRouteExample(r *parsedRoute, t *testing.T) { } func TestReturnsLexerErrors(t *testing.T) { - _, err := parse("invalid code") + _, err := parseDocument("invalid code") if err == nil { t.Error("failed to fail") } } func TestParseSingleRoute(t *testing.T) { - r, err := parse(singleRouteExample) + r, err := parseDocument(singleRouteExample) if err != nil { t.Error("failed to parse", err) @@ -103,7 +103,7 @@ func TestParseSingleRoute(t *testing.T) { } func TestParsingSpecialChars(t *testing.T) { - r, err := parse(`Path("newlines") -> inlineContent("Line \ \1\nLine 2\r\nLine 3\a\b\f\n\r\t\v") -> `) + r, err := parseDocument(`Path("newlines") -> inlineContent("Line \ \1\nLine 2\r\nLine 3\a\b\f\n\r\t\v") -> `) if err != nil { t.Error("failed to parse", err) @@ -122,7 +122,7 @@ func TestParsingSpecialChars(t *testing.T) { } func TestParseSingleRouteDef(t *testing.T) { - r, err := parse(singleRouteDefExample) + r, err := parseDocument(singleRouteDefExample) if err != nil { t.Error("failed to parse", err) @@ -144,14 +144,14 @@ func TestParseInvalidDocument(t *testing.T) { route0: Method("GET") -> "https://backend-0.example.com" route1: Method("POST") -> "https://backend-1.example.com"` - _, err := parse(missingSemicolon) + _, err := parseDocument(missingSemicolon) if err == nil { t.Error("failed to fail") } } func TestParseDocument(t *testing.T) { - r, err := parse(routingDocumentExample) + r, err := parseDocument(routingDocumentExample) if err != nil { t.Error("failed to parse document", err) @@ -188,21 +188,21 @@ func TestParseDocument(t *testing.T) { } func TestNumberNotClosedWithDecimalSign(t *testing.T) { - _, err := parse(`* -> number(3.) -> `) + _, err := parseDocument(`* -> number(3.) -> `) if err == nil { t.Error("failed to fail") } } func TestNumberStartingWithDecimal(t *testing.T) { - _, err := parse(`* -> number(.3) -> `) + _, err := parseDocument(`* -> number(.3) -> `) if err != nil { t.Error("failed to parse number", err) } } func TestNumber(t *testing.T) { - _, err := parse(`* -> number(3.14) -> `) + _, err := parseDocument(`* -> number(3.14) -> `) if err != nil { t.Error("failed to parse number", err) } @@ -221,7 +221,7 @@ func TestRegExp(t *testing.T) { } func testRegExpOnce(t *testing.T, regexpStr string, expectedRegExp string) { - routes, err := parse(regexpStr) + routes, err := parseDocument(regexpStr) if err != nil { t.Error("failed to parse PathRegexp:"+regexpStr, err) return