Index: appengine/cmd/dm/distributor/impl/jobsim/parser/parser.go |
diff --git a/appengine/cmd/dm/distributor/impl/jobsim/parser/parser.go b/appengine/cmd/dm/distributor/impl/jobsim/parser/parser.go |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0be6fba160d5872901b82ab83ec670e3ea366f38 |
--- /dev/null |
+++ b/appengine/cmd/dm/distributor/impl/jobsim/parser/parser.go |
@@ -0,0 +1,2025 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package parser |
+ |
+import ( |
+ "bytes" |
+ "errors" |
+ "fmt" |
+ "io" |
+ "io/ioutil" |
+ "os" |
+ "regexp" |
+ "strconv" |
+ "strings" |
+ "time" |
+ "unicode" |
+ "unicode/utf8" |
+) |
+ |
+type retriesHolder uint64 |
+type identityHolder uint64 |
+ |
+var whitespaceRE = regexp.MustCompile("#[^\n]*\n|\\s+") |
+ |
+// CleanPhrase removes all whitespace and comments from a phrase in preparation |
+// for parsing it. The parsers defined in this package only operate on cleaned |
+// phrases, and will fail to parse if you provide a phrase with whitespace. |
+func CleanPhrase(p string) string { |
+ return whitespaceRE.ReplaceAllLiteralString(p, "") |
+} |
+ |
+// ParsePhrase is a convenience function for the parser functions in this |
+// package to return a typed Phrase object as the result of a string. |
+func ParsePhrase(p string) (Phrase, error) { |
+ ret, err := Parse("<string>", []byte(p)) |
+ if err != nil { |
+ return nil, err |
+ } |
+ return ret.(Phrase), nil |
+} |
+ |
+var g = &grammar{ |
+ rules: []*rule{ |
+ { |
+ name: "Overall", |
+ pos: position{line: 32, col: 1, offset: 946}, |
+ expr: &choiceExpr{ |
+ pos: position{line: 32, col: 11, offset: 956}, |
+ alternatives: []interface{}{ |
+ &actionExpr{ |
+ pos: position{line: 32, col: 11, offset: 956}, |
+ run: (*parser).callonOverall2, |
+ expr: &seqExpr{ |
+ pos: position{line: 32, col: 11, offset: 956}, |
+ exprs: []interface{}{ |
+ &labeledExpr{ |
+ pos: position{line: 32, col: 11, offset: 956}, |
+ label: "p", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 32, col: 13, offset: 958}, |
+ name: "Phrase", |
+ }, |
+ }, |
+ ¬Expr{ |
+ pos: position{line: 32, col: 20, offset: 965}, |
+ expr: &anyMatcher{ |
+ line: 32, col: 21, offset: 966, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 34, col: 5, offset: 990}, |
+ run: (*parser).callonOverall8, |
+ expr: &seqExpr{ |
+ pos: position{line: 34, col: 5, offset: 990}, |
+ exprs: []interface{}{ |
+ &labeledExpr{ |
+ pos: position{line: 34, col: 5, offset: 990}, |
+ label: "p", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 34, col: 7, offset: 992}, |
+ name: "Phrase", |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 34, col: 14, offset: 999}, |
+ label: "ext", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 34, col: 18, offset: 1003}, |
+ name: "Extra", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 36, col: 5, offset: 1073}, |
+ run: (*parser).callonOverall14, |
+ expr: ¬Expr{ |
+ pos: position{line: 36, col: 5, offset: 1073}, |
+ expr: &anyMatcher{ |
+ line: 36, col: 6, offset: 1074, |
+ }, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 38, col: 5, offset: 1123}, |
+ run: (*parser).callonOverall17, |
+ expr: &labeledExpr{ |
+ pos: position{line: 38, col: 5, offset: 1123}, |
+ label: "ext", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 38, col: 9, offset: 1127}, |
+ name: "Extra", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Extra", |
+ pos: position{line: 42, col: 1, offset: 1200}, |
+ expr: &actionExpr{ |
+ pos: position{line: 42, col: 9, offset: 1208}, |
+ run: (*parser).callonExtra1, |
+ expr: &zeroOrMoreExpr{ |
+ pos: position{line: 42, col: 9, offset: 1208}, |
+ expr: &anyMatcher{ |
+ line: 42, col: 9, offset: 1208, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Phrase", |
+ pos: position{line: 46, col: 1, offset: 1245}, |
+ expr: &choiceExpr{ |
+ pos: position{line: 46, col: 10, offset: 1254}, |
+ alternatives: []interface{}{ |
+ &actionExpr{ |
+ pos: position{line: 46, col: 10, offset: 1254}, |
+ run: (*parser).callonPhrase2, |
+ expr: &labeledExpr{ |
+ pos: position{line: 46, col: 10, offset: 1254}, |
+ label: "ret", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 46, col: 14, offset: 1258}, |
+ name: "ReturnStage", |
+ }, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 48, col: 5, offset: 1310}, |
+ run: (*parser).callonPhrase5, |
+ expr: &seqExpr{ |
+ pos: position{line: 48, col: 5, offset: 1310}, |
+ exprs: []interface{}{ |
+ &labeledExpr{ |
+ pos: position{line: 48, col: 5, offset: 1310}, |
+ label: "first", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 48, col: 11, offset: 1316}, |
+ name: "Stage", |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 48, col: 17, offset: 1322}, |
+ label: "middleI", |
+ expr: &zeroOrMoreExpr{ |
+ pos: position{line: 48, col: 25, offset: 1330}, |
+ expr: &seqExpr{ |
+ pos: position{line: 48, col: 26, offset: 1331}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 48, col: 26, offset: 1331}, |
+ val: ",", |
+ ignoreCase: false, |
+ }, |
+ &ruleRefExpr{ |
+ pos: position{line: 48, col: 30, offset: 1335}, |
+ name: "Stage", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 48, col: 38, offset: 1343}, |
+ label: "retI", |
+ expr: &zeroOrOneExpr{ |
+ pos: position{line: 48, col: 43, offset: 1348}, |
+ expr: &seqExpr{ |
+ pos: position{line: 48, col: 44, offset: 1349}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 48, col: 44, offset: 1349}, |
+ val: ",", |
+ ignoreCase: false, |
+ }, |
+ &ruleRefExpr{ |
+ pos: position{line: 48, col: 48, offset: 1353}, |
+ name: "ReturnStage", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 70, col: 5, offset: 1791}, |
+ run: (*parser).callonPhrase19, |
+ expr: &seqExpr{ |
+ pos: position{line: 70, col: 5, offset: 1791}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 70, col: 5, offset: 1791}, |
+ val: ",", |
+ ignoreCase: false, |
+ }, |
+ &zeroOrMoreExpr{ |
+ pos: position{line: 70, col: 9, offset: 1795}, |
+ expr: &anyMatcher{ |
+ line: 70, col: 9, offset: 1795, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Stage", |
+ pos: position{line: 74, col: 1, offset: 1868}, |
+ expr: &actionExpr{ |
+ pos: position{line: 74, col: 9, offset: 1876}, |
+ run: (*parser).callonStage1, |
+ expr: &labeledExpr{ |
+ pos: position{line: 74, col: 9, offset: 1876}, |
+ label: "s", |
+ expr: &choiceExpr{ |
+ pos: position{line: 74, col: 12, offset: 1879}, |
+ alternatives: []interface{}{ |
+ &ruleRefExpr{ |
+ pos: position{line: 74, col: 12, offset: 1879}, |
+ name: "FailureStage", |
+ }, |
+ &ruleRefExpr{ |
+ pos: position{line: 74, col: 27, offset: 1894}, |
+ name: "StallStage", |
+ }, |
+ &ruleRefExpr{ |
+ pos: position{line: 74, col: 40, offset: 1907}, |
+ name: "DepsStage", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "FailureStage", |
+ pos: position{line: 78, col: 1, offset: 1939}, |
+ expr: &actionExpr{ |
+ pos: position{line: 78, col: 16, offset: 1954}, |
+ run: (*parser).callonFailureStage1, |
+ expr: &seqExpr{ |
+ pos: position{line: 78, col: 16, offset: 1954}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 78, col: 16, offset: 1954}, |
+ val: "%", |
+ ignoreCase: false, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 78, col: 20, offset: 1958}, |
+ label: "num", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 78, col: 24, offset: 1962}, |
+ name: "Num", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "StallStage", |
+ pos: position{line: 82, col: 1, offset: 2012}, |
+ expr: &actionExpr{ |
+ pos: position{line: 82, col: 14, offset: 2025}, |
+ run: (*parser).callonStallStage1, |
+ expr: &seqExpr{ |
+ pos: position{line: 82, col: 14, offset: 2025}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 82, col: 14, offset: 2025}, |
+ val: "@", |
+ ignoreCase: false, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 82, col: 18, offset: 2029}, |
+ label: "amt", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 82, col: 22, offset: 2033}, |
+ name: "Duration", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "ReturnStage", |
+ pos: position{line: 86, col: 1, offset: 2093}, |
+ expr: &actionExpr{ |
+ pos: position{line: 86, col: 15, offset: 2107}, |
+ run: (*parser).callonReturnStage1, |
+ expr: &seqExpr{ |
+ pos: position{line: 86, col: 15, offset: 2107}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 86, col: 15, offset: 2107}, |
+ val: "=", |
+ ignoreCase: false, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 86, col: 19, offset: 2111}, |
+ label: "val", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 86, col: 23, offset: 2115}, |
+ name: "Num", |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 86, col: 27, offset: 2119}, |
+ label: "exp", |
+ expr: &zeroOrOneExpr{ |
+ pos: position{line: 86, col: 31, offset: 2123}, |
+ expr: &seqExpr{ |
+ pos: position{line: 86, col: 32, offset: 2124}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 86, col: 32, offset: 2124}, |
+ val: "<", |
+ ignoreCase: false, |
+ }, |
+ &ruleRefExpr{ |
+ pos: position{line: 86, col: 36, offset: 2128}, |
+ name: "Duration", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "DepsStage", |
+ pos: position{line: 94, col: 1, offset: 2287}, |
+ expr: &choiceExpr{ |
+ pos: position{line: 94, col: 13, offset: 2299}, |
+ alternatives: []interface{}{ |
+ &actionExpr{ |
+ pos: position{line: 94, col: 13, offset: 2299}, |
+ run: (*parser).callonDepsStage2, |
+ expr: &seqExpr{ |
+ pos: position{line: 94, col: 13, offset: 2299}, |
+ exprs: []interface{}{ |
+ &labeledExpr{ |
+ pos: position{line: 94, col: 13, offset: 2299}, |
+ label: "first", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 94, col: 19, offset: 2305}, |
+ name: "Dependency", |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 94, col: 30, offset: 2316}, |
+ label: "restI", |
+ expr: &zeroOrMoreExpr{ |
+ pos: position{line: 94, col: 36, offset: 2322}, |
+ expr: &seqExpr{ |
+ pos: position{line: 94, col: 37, offset: 2323}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 94, col: 37, offset: 2323}, |
+ val: "&", |
+ ignoreCase: false, |
+ }, |
+ &ruleRefExpr{ |
+ pos: position{line: 94, col: 41, offset: 2327}, |
+ name: "Dependency", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 106, col: 5, offset: 2644}, |
+ run: (*parser).callonDepsStage11, |
+ expr: &seqExpr{ |
+ pos: position{line: 106, col: 5, offset: 2644}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 106, col: 5, offset: 2644}, |
+ val: "&", |
+ ignoreCase: false, |
+ }, |
+ &zeroOrMoreExpr{ |
+ pos: position{line: 106, col: 9, offset: 2648}, |
+ expr: &anyMatcher{ |
+ line: 106, col: 9, offset: 2648, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Dependency", |
+ pos: position{line: 110, col: 1, offset: 2726}, |
+ expr: &actionExpr{ |
+ pos: position{line: 110, col: 14, offset: 2739}, |
+ run: (*parser).callonDependency1, |
+ expr: &seqExpr{ |
+ pos: position{line: 110, col: 14, offset: 2739}, |
+ exprs: []interface{}{ |
+ &labeledExpr{ |
+ pos: position{line: 110, col: 14, offset: 2739}, |
+ label: "shards", |
+ expr: &zeroOrOneExpr{ |
+ pos: position{line: 110, col: 21, offset: 2746}, |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 110, col: 21, offset: 2746}, |
+ name: "Shards", |
+ }, |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 110, col: 29, offset: 2754}, |
+ label: "attempts", |
+ expr: &zeroOrOneExpr{ |
+ pos: position{line: 110, col: 38, offset: 2763}, |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 110, col: 38, offset: 2763}, |
+ name: "Attempts", |
+ }, |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 110, col: 48, offset: 2773}, |
+ label: "id", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 110, col: 51, offset: 2776}, |
+ name: "ID", |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 110, col: 54, offset: 2779}, |
+ label: "options", |
+ expr: &zeroOrOneExpr{ |
+ pos: position{line: 110, col: 62, offset: 2787}, |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 110, col: 62, offset: 2787}, |
+ name: "Option", |
+ }, |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 110, col: 70, offset: 2795}, |
+ label: "subI", |
+ expr: &zeroOrOneExpr{ |
+ pos: position{line: 110, col: 75, offset: 2800}, |
+ expr: &seqExpr{ |
+ pos: position{line: 110, col: 77, offset: 2802}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 110, col: 77, offset: 2802}, |
+ val: "(", |
+ ignoreCase: false, |
+ }, |
+ &ruleRefExpr{ |
+ pos: position{line: 110, col: 81, offset: 2806}, |
+ name: "Phrase", |
+ }, |
+ &litMatcher{ |
+ pos: position{line: 110, col: 88, offset: 2813}, |
+ val: ")", |
+ ignoreCase: false, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Option", |
+ pos: position{line: 144, col: 1, offset: 3446}, |
+ expr: &choiceExpr{ |
+ pos: position{line: 144, col: 10, offset: 3455}, |
+ alternatives: []interface{}{ |
+ &actionExpr{ |
+ pos: position{line: 144, col: 10, offset: 3455}, |
+ run: (*parser).callonOption2, |
+ expr: &seqExpr{ |
+ pos: position{line: 144, col: 10, offset: 3455}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 144, col: 10, offset: 3455}, |
+ val: "+", |
+ ignoreCase: false, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 144, col: 14, offset: 3459}, |
+ label: "num", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 144, col: 18, offset: 3463}, |
+ name: "Num", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 146, col: 5, offset: 3516}, |
+ run: (*parser).callonOption7, |
+ expr: &seqExpr{ |
+ pos: position{line: 146, col: 5, offset: 3516}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 146, col: 5, offset: 3516}, |
+ val: "^", |
+ ignoreCase: false, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 146, col: 9, offset: 3520}, |
+ label: "num", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 146, col: 13, offset: 3524}, |
+ name: "Num", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Shards", |
+ pos: position{line: 150, col: 1, offset: 3577}, |
+ expr: &actionExpr{ |
+ pos: position{line: 150, col: 10, offset: 3586}, |
+ run: (*parser).callonShards1, |
+ expr: &seqExpr{ |
+ pos: position{line: 150, col: 10, offset: 3586}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 150, col: 10, offset: 3586}, |
+ val: "{", |
+ ignoreCase: false, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 150, col: 14, offset: 3590}, |
+ label: "num", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 150, col: 18, offset: 3594}, |
+ name: "Num", |
+ }, |
+ }, |
+ &litMatcher{ |
+ pos: position{line: 150, col: 22, offset: 3598}, |
+ val: "}", |
+ ignoreCase: false, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Attempts", |
+ pos: position{line: 154, col: 1, offset: 3625}, |
+ expr: &actionExpr{ |
+ pos: position{line: 154, col: 12, offset: 3636}, |
+ run: (*parser).callonAttempts1, |
+ expr: &seqExpr{ |
+ pos: position{line: 154, col: 12, offset: 3636}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 154, col: 12, offset: 3636}, |
+ val: "[", |
+ ignoreCase: false, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 154, col: 16, offset: 3640}, |
+ label: "rs", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 154, col: 19, offset: 3643}, |
+ name: "RangeSlice", |
+ }, |
+ }, |
+ &litMatcher{ |
+ pos: position{line: 154, col: 30, offset: 3654}, |
+ val: "]", |
+ ignoreCase: false, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "RangeSlice", |
+ pos: position{line: 158, col: 1, offset: 3680}, |
+ expr: &actionExpr{ |
+ pos: position{line: 158, col: 14, offset: 3693}, |
+ run: (*parser).callonRangeSlice1, |
+ expr: &seqExpr{ |
+ pos: position{line: 158, col: 14, offset: 3693}, |
+ exprs: []interface{}{ |
+ &labeledExpr{ |
+ pos: position{line: 158, col: 14, offset: 3693}, |
+ label: "firstI", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 158, col: 21, offset: 3700}, |
+ name: "Range", |
+ }, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 158, col: 27, offset: 3706}, |
+ label: "restI", |
+ expr: &zeroOrMoreExpr{ |
+ pos: position{line: 158, col: 33, offset: 3712}, |
+ expr: &seqExpr{ |
+ pos: position{line: 158, col: 34, offset: 3713}, |
+ exprs: []interface{}{ |
+ &litMatcher{ |
+ pos: position{line: 158, col: 34, offset: 3713}, |
+ val: ",", |
+ ignoreCase: false, |
+ }, |
+ &ruleRefExpr{ |
+ pos: position{line: 158, col: 38, offset: 3717}, |
+ name: "Range", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Range", |
+ pos: position{line: 170, col: 1, offset: 3950}, |
+ expr: &choiceExpr{ |
+ pos: position{line: 170, col: 9, offset: 3958}, |
+ alternatives: []interface{}{ |
+ &actionExpr{ |
+ pos: position{line: 170, col: 9, offset: 3958}, |
+ run: (*parser).callonRange2, |
+ expr: &seqExpr{ |
+ pos: position{line: 170, col: 9, offset: 3958}, |
+ exprs: []interface{}{ |
+ &labeledExpr{ |
+ pos: position{line: 170, col: 9, offset: 3958}, |
+ label: "loI", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 170, col: 13, offset: 3962}, |
+ name: "Num", |
+ }, |
+ }, |
+ &litMatcher{ |
+ pos: position{line: 170, col: 17, offset: 3966}, |
+ val: "-", |
+ ignoreCase: false, |
+ }, |
+ &labeledExpr{ |
+ pos: position{line: 170, col: 21, offset: 3970}, |
+ label: "hiI", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 170, col: 25, offset: 3974}, |
+ name: "Num", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 179, col: 5, offset: 4149}, |
+ run: (*parser).callonRange9, |
+ expr: &labeledExpr{ |
+ pos: position{line: 179, col: 5, offset: 4149}, |
+ label: "num", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 179, col: 9, offset: 4153}, |
+ name: "Num", |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Duration", |
+ pos: position{line: 183, col: 1, offset: 4199}, |
+ expr: &actionExpr{ |
+ pos: position{line: 183, col: 12, offset: 4210}, |
+ run: (*parser).callonDuration1, |
+ expr: &labeledExpr{ |
+ pos: position{line: 183, col: 12, offset: 4210}, |
+ label: "num", |
+ expr: &ruleRefExpr{ |
+ pos: position{line: 183, col: 16, offset: 4214}, |
+ name: "Num", |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "Num", |
+ pos: position{line: 187, col: 1, offset: 4279}, |
+ expr: &choiceExpr{ |
+ pos: position{line: 187, col: 7, offset: 4285}, |
+ alternatives: []interface{}{ |
+ &actionExpr{ |
+ pos: position{line: 187, col: 7, offset: 4285}, |
+ run: (*parser).callonNum2, |
+ expr: &litMatcher{ |
+ pos: position{line: 187, col: 7, offset: 4285}, |
+ val: "0", |
+ ignoreCase: false, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 189, col: 5, offset: 4349}, |
+ run: (*parser).callonNum4, |
+ expr: &seqExpr{ |
+ pos: position{line: 189, col: 5, offset: 4349}, |
+ exprs: []interface{}{ |
+ &charClassMatcher{ |
+ pos: position{line: 189, col: 5, offset: 4349}, |
+ val: "[1-9]", |
+ ranges: []rune{'1', '9'}, |
+ ignoreCase: false, |
+ inverted: false, |
+ }, |
+ &zeroOrMoreExpr{ |
+ pos: position{line: 189, col: 10, offset: 4354}, |
+ expr: &charClassMatcher{ |
+ pos: position{line: 189, col: 10, offset: 4354}, |
+ val: "[0-9]", |
+ ranges: []rune{'0', '9'}, |
+ ignoreCase: false, |
+ inverted: false, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ &actionExpr{ |
+ pos: position{line: 191, col: 5, offset: 4418}, |
+ run: (*parser).callonNum9, |
+ expr: &zeroOrMoreExpr{ |
+ pos: position{line: 191, col: 5, offset: 4418}, |
+ expr: &anyMatcher{ |
+ line: 191, col: 5, offset: 4418, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+ { |
+ name: "ID", |
+ pos: position{line: 195, col: 1, offset: 4494}, |
+ expr: &actionExpr{ |
+ pos: position{line: 195, col: 6, offset: 4499}, |
+ run: (*parser).callonID1, |
+ expr: &oneOrMoreExpr{ |
+ pos: position{line: 195, col: 6, offset: 4499}, |
+ expr: &charClassMatcher{ |
+ pos: position{line: 195, col: 6, offset: 4499}, |
+ val: "[a-zA-Z'_]", |
+ chars: []rune{'\'', '_'}, |
+ ranges: []rune{'a', 'z', 'A', 'Z'}, |
+ ignoreCase: false, |
+ inverted: false, |
+ }, |
+ }, |
+ }, |
+ }, |
+ }, |
+} |
+ |
+func (c *current) onOverall2(p interface{}) (interface{}, error) { |
+ return p, nil |
+} |
+ |
+func (p *parser) callonOverall2() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onOverall2(stack["p"]) |
+} |
+ |
+func (c *current) onOverall8(p, ext interface{}) (interface{}, error) { |
+ return nil, fmt.Errorf("found extra: %q", ext.(string)) |
+} |
+ |
+func (p *parser) callonOverall8() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onOverall8(stack["p"], stack["ext"]) |
+} |
+ |
+func (c *current) onOverall14() (interface{}, error) { |
+ return nil, fmt.Errorf("empty phrase") |
+} |
+ |
+func (p *parser) callonOverall14() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onOverall14() |
+} |
+ |
+func (c *current) onOverall17(ext interface{}) (interface{}, error) { |
+ return nil, fmt.Errorf("expected phrase: %q", ext.(string)) |
+} |
+ |
+func (p *parser) callonOverall17() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onOverall17(stack["ext"]) |
+} |
+ |
+func (c *current) onExtra1() (interface{}, error) { |
+ return string(c.text), nil |
+} |
+ |
+func (p *parser) callonExtra1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onExtra1() |
+} |
+ |
+func (c *current) onPhrase2(ret interface{}) (interface{}, error) { |
+ return Phrase{ret.(Stage)}, nil |
+} |
+ |
+func (p *parser) callonPhrase2() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onPhrase2(stack["ret"]) |
+} |
+ |
+func (c *current) onPhrase5(first, middleI, retI interface{}) (interface{}, error) { |
+ middle, _ := middleI.([]interface{}) |
+ retStage, _ := retI.([]interface{}) |
+ |
+ amt := 1 |
+ if middle != nil { |
+ amt += len(middle) |
+ } |
+ if retStage != nil { |
+ amt++ |
+ } |
+ |
+ ret := make(Phrase, 0, amt) |
+ ret = append(ret, first.(Stage)) |
+ for _, itm := range middle { |
+ ret = append(ret, itm.([]interface{})[1].(Stage)) |
+ } |
+ if retStage != nil { |
+ ret = append(ret, retStage[1].(Stage)) |
+ } |
+ |
+ return ret, nil |
+} |
+ |
+func (p *parser) callonPhrase5() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onPhrase5(stack["first"], stack["middleI"], stack["retI"]) |
+} |
+ |
+func (c *current) onPhrase19() (interface{}, error) { |
+ return nil, fmt.Errorf("expected stage in %q", string(c.text)) |
+} |
+ |
+func (p *parser) callonPhrase19() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onPhrase19() |
+} |
+ |
+func (c *current) onStage1(s interface{}) (interface{}, error) { |
+ return s, nil |
+} |
+ |
+func (p *parser) callonStage1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onStage1(stack["s"]) |
+} |
+ |
+func (c *current) onFailureStage1(num interface{}) (interface{}, error) { |
+ return FailureStage(num.(uint64)), nil |
+} |
+ |
+func (p *parser) callonFailureStage1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onFailureStage1(stack["num"]) |
+} |
+ |
+func (c *current) onStallStage1(amt interface{}) (interface{}, error) { |
+ return StallStage(amt.(time.Duration)), nil |
+} |
+ |
+func (p *parser) callonStallStage1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onStallStage1(stack["amt"]) |
+} |
+ |
+func (c *current) onReturnStage1(val, exp interface{}) (interface{}, error) { |
+ ret := &ReturnStage{Value: val.(uint64)} |
+ if exp != nil { |
+ ret.Expiration = exp.([]interface{})[1].(time.Duration) |
+ } |
+ return ret, nil |
+} |
+ |
+func (p *parser) callonReturnStage1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onReturnStage1(stack["val"], stack["exp"]) |
+} |
+ |
+func (c *current) onDepsStage2(first, restI interface{}) (interface{}, error) { |
+ rest, _ := restI.([]interface{}) |
+ |
+ if rest == nil { |
+ return DepsStage{first.(*Dependency)}, nil |
+ } |
+ ret := make(DepsStage, 0, 1+len(rest)) |
+ ret = append(ret, first.(*Dependency)) |
+ for _, itm := range rest { |
+ ret = append(ret, itm.([]interface{})[1].(*Dependency)) |
+ } |
+ return ret, nil |
+} |
+ |
+func (p *parser) callonDepsStage2() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onDepsStage2(stack["first"], stack["restI"]) |
+} |
+ |
+func (c *current) onDepsStage11() (interface{}, error) { |
+ return nil, fmt.Errorf("expected dependency in %q", string(c.text)) |
+} |
+ |
+func (p *parser) callonDepsStage11() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onDepsStage11() |
+} |
+ |
+func (c *current) onDependency1(shards, attempts, id, options, subI interface{}) (interface{}, error) { |
+ ret := &Dependency{Name: id.(string)} |
+ |
+ if shards != nil { |
+ ret.ShardCount = shards.(uint64) |
+ } |
+ |
+ if attempts != nil { |
+ ret.AttemptNums = attempts.(RangeSlice) |
+ } |
+ |
+ if options != nil { |
+ switch x := options.(type) { |
+ case retriesHolder: |
+ if attempts != nil { |
+ return nil, fmt.Errorf( |
+ "in %q: retries are incompatible with specified Attempts", |
+ string(c.text)) |
+ } |
+ ret.Retries = uint64(x) |
+ |
+ case identityHolder: |
+ ret.Uniq = uint64(x) |
+ } |
+ } |
+ |
+ sub, _ := subI.([]interface{}) |
+ if sub != nil { |
+ ret.Substages = sub[1].(Phrase) |
+ } |
+ |
+ return ret, nil |
+} |
+ |
+func (p *parser) callonDependency1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onDependency1(stack["shards"], stack["attempts"], stack["id"], stack["options"], stack["subI"]) |
+} |
+ |
+func (c *current) onOption2(num interface{}) (interface{}, error) { |
+ return retriesHolder(num.(uint64)), nil |
+} |
+ |
+func (p *parser) callonOption2() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onOption2(stack["num"]) |
+} |
+ |
+func (c *current) onOption7(num interface{}) (interface{}, error) { |
+ return identityHolder(num.(uint64)), nil |
+} |
+ |
+func (p *parser) callonOption7() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onOption7(stack["num"]) |
+} |
+ |
+func (c *current) onShards1(num interface{}) (interface{}, error) { |
+ return num, nil |
+} |
+ |
+func (p *parser) callonShards1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onShards1(stack["num"]) |
+} |
+ |
+func (c *current) onAttempts1(rs interface{}) (interface{}, error) { |
+ return rs, nil |
+} |
+ |
+func (p *parser) callonAttempts1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onAttempts1(stack["rs"]) |
+} |
+ |
+func (c *current) onRangeSlice1(firstI, restI interface{}) (interface{}, error) { |
+ rest, _ := restI.([]interface{}) |
+ |
+ ret := make(RangeSlice, 0, 1+len(rest)) |
+ ret = append(ret, firstI.(Range)) |
+ for _, itm := range rest { |
+ ret = append(ret, itm.([]interface{})[1].(Range)) |
+ } |
+ |
+ return ret, nil |
+} |
+ |
+func (p *parser) callonRangeSlice1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onRangeSlice1(stack["firstI"], stack["restI"]) |
+} |
+ |
+func (c *current) onRange2(loI, hiI interface{}) (interface{}, error) { |
+ lo := loI.(uint64) |
+ hi := hiI.(uint64) |
+ |
+ if lo >= hi { |
+ return nil, fmt.Errorf("invalid range %q: lo >= hi", string(c.text)) |
+ } |
+ |
+ return Range{lo, hi}, nil |
+} |
+ |
+func (p *parser) callonRange2() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onRange2(stack["loI"], stack["hiI"]) |
+} |
+ |
+func (c *current) onRange9(num interface{}) (interface{}, error) { |
+ return Range{num.(uint64), 0}, nil |
+} |
+ |
+func (p *parser) callonRange9() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onRange9(stack["num"]) |
+} |
+ |
+func (c *current) onDuration1(num interface{}) (interface{}, error) { |
+ return time.Second * time.Duration(num.(uint64)), nil |
+} |
+ |
+func (p *parser) callonDuration1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onDuration1(stack["num"]) |
+} |
+ |
+func (c *current) onNum2() (interface{}, error) { |
+ return nil, fmt.Errorf("zero value not acceptable") |
+} |
+ |
+func (p *parser) callonNum2() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onNum2() |
+} |
+ |
+func (c *current) onNum4() (interface{}, error) { |
+ return strconv.ParseUint(string(c.text), 10, 64) |
+} |
+ |
+func (p *parser) callonNum4() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onNum4() |
+} |
+ |
+func (c *current) onNum9() (interface{}, error) { |
+ return nil, fmt.Errorf("expected number, got %q", string(c.text)) |
+} |
+ |
+func (p *parser) callonNum9() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onNum9() |
+} |
+ |
+func (c *current) onID1() (interface{}, error) { |
+ return string(c.text), nil |
+} |
+ |
+func (p *parser) callonID1() (interface{}, error) { |
+ stack := p.vstack[len(p.vstack)-1] |
+ _ = stack |
+ return p.cur.onID1() |
+} |
+ |
+var ( |
+ // errNoRule is returned when the grammar to parse has no rule. |
+ errNoRule = errors.New("grammar has no rule") |
+ |
+ // errInvalidEncoding is returned when the source is not properly |
+ // utf8-encoded. |
+ errInvalidEncoding = errors.New("invalid encoding") |
+ |
+ // errNoMatch is returned if no match could be found. |
+ errNoMatch = errors.New("no match found") |
+) |
+ |
+// Option is a function that can set an option on the parser. It returns |
+// the previous setting as an Option. |
+type Option func(*parser) Option |
+ |
+// Debug creates an Option to set the debug flag to b. When set to true, |
+// debugging information is printed to stdout while parsing. |
+// |
+// The default is false. |
+func Debug(b bool) Option { |
+ return func(p *parser) Option { |
+ old := p.debug |
+ p.debug = b |
+ return Debug(old) |
+ } |
+} |
+ |
+// Memoize creates an Option to set the memoize flag to b. When set to true, |
+// the parser will cache all results so each expression is evaluated only |
+// once. This guarantees linear parsing time even for pathological cases, |
+// at the expense of more memory and slower times for typical cases. |
+// |
+// The default is false. |
+func Memoize(b bool) Option { |
+ return func(p *parser) Option { |
+ old := p.memoize |
+ p.memoize = b |
+ return Memoize(old) |
+ } |
+} |
+ |
+// Recover creates an Option to set the recover flag to b. When set to |
+// true, this causes the parser to recover from panics and convert it |
+// to an error. Setting it to false can be useful while debugging to |
+// access the full stack trace. |
+// |
+// The default is true. |
+func Recover(b bool) Option { |
+ return func(p *parser) Option { |
+ old := p.recover |
+ p.recover = b |
+ return Recover(old) |
+ } |
+} |
+ |
+// ParseFile parses the file identified by filename. |
+func ParseFile(filename string, opts ...Option) (interface{}, error) { |
+ f, err := os.Open(filename) |
+ if err != nil { |
+ return nil, err |
+ } |
+ defer f.Close() |
+ return ParseReader(filename, f, opts...) |
+} |
+ |
+// ParseReader parses the data from r using filename as information in the |
+// error messages. |
+func ParseReader(filename string, r io.Reader, opts ...Option) (interface{}, error) { |
+ b, err := ioutil.ReadAll(r) |
+ if err != nil { |
+ return nil, err |
+ } |
+ |
+ return Parse(filename, b, opts...) |
+} |
+ |
+// Parse parses the data from b using filename as information in the |
+// error messages. |
+func Parse(filename string, b []byte, opts ...Option) (interface{}, error) { |
+ return newParser(filename, b, opts...).parse(g) |
+} |
+ |
+// position records a position in the text. |
+type position struct { |
+ line, col, offset int |
+} |
+ |
+func (p position) String() string { |
+ return fmt.Sprintf("%d:%d [%d]", p.line, p.col, p.offset) |
+} |
+ |
+// savepoint stores all state required to go back to this point in the |
+// parser. |
+type savepoint struct { |
+ position |
+ rn rune |
+ w int |
+} |
+ |
+type current struct { |
+ pos position // start position of the match |
+ text []byte // raw text of the match |
+} |
+ |
+// the AST types... |
+ |
+type grammar struct { |
+ pos position |
+ rules []*rule |
+} |
+ |
+type rule struct { |
+ pos position |
+ name string |
+ displayName string |
+ expr interface{} |
+} |
+ |
+type choiceExpr struct { |
+ pos position |
+ alternatives []interface{} |
+} |
+ |
+type actionExpr struct { |
+ pos position |
+ expr interface{} |
+ run func(*parser) (interface{}, error) |
+} |
+ |
+type seqExpr struct { |
+ pos position |
+ exprs []interface{} |
+} |
+ |
+type labeledExpr struct { |
+ pos position |
+ label string |
+ expr interface{} |
+} |
+ |
+type expr struct { |
+ pos position |
+ expr interface{} |
+} |
+ |
+type andExpr expr |
+type notExpr expr |
+type zeroOrOneExpr expr |
+type zeroOrMoreExpr expr |
+type oneOrMoreExpr expr |
+ |
+type ruleRefExpr struct { |
+ pos position |
+ name string |
+} |
+ |
+type andCodeExpr struct { |
+ pos position |
+ run func(*parser) (bool, error) |
+} |
+ |
+type notCodeExpr struct { |
+ pos position |
+ run func(*parser) (bool, error) |
+} |
+ |
+type litMatcher struct { |
+ pos position |
+ val string |
+ ignoreCase bool |
+} |
+ |
+type charClassMatcher struct { |
+ pos position |
+ val string |
+ chars []rune |
+ ranges []rune |
+ classes []*unicode.RangeTable |
+ ignoreCase bool |
+ inverted bool |
+} |
+ |
+type anyMatcher position |
+ |
+// errList cumulates the errors found by the parser. |
+type errList []error |
+ |
+func (e *errList) add(err error) { |
+ *e = append(*e, err) |
+} |
+ |
+func (e errList) err() error { |
+ if len(e) == 0 { |
+ return nil |
+ } |
+ e.dedupe() |
+ return e |
+} |
+ |
+func (e *errList) dedupe() { |
+ var cleaned []error |
+ set := make(map[string]bool) |
+ for _, err := range *e { |
+ if msg := err.Error(); !set[msg] { |
+ set[msg] = true |
+ cleaned = append(cleaned, err) |
+ } |
+ } |
+ *e = cleaned |
+} |
+ |
+func (e errList) Error() string { |
+ switch len(e) { |
+ case 0: |
+ return "" |
+ case 1: |
+ return e[0].Error() |
+ default: |
+ var buf bytes.Buffer |
+ |
+ for i, err := range e { |
+ if i > 0 { |
+ buf.WriteRune('\n') |
+ } |
+ buf.WriteString(err.Error()) |
+ } |
+ return buf.String() |
+ } |
+} |
+ |
+// parserError wraps an error with a prefix indicating the rule in which |
+// the error occurred. The original error is stored in the Inner field. |
+type parserError struct { |
+ Inner error |
+ pos position |
+ prefix string |
+} |
+ |
+// Error returns the error message. |
+func (p *parserError) Error() string { |
+ return p.prefix + ": " + p.Inner.Error() |
+} |
+ |
+// newParser creates a parser with the specified input source and options. |
+func newParser(filename string, b []byte, opts ...Option) *parser { |
+ p := &parser{ |
+ filename: filename, |
+ errs: new(errList), |
+ data: b, |
+ pt: savepoint{position: position{line: 1}}, |
+ recover: true, |
+ } |
+ p.setOptions(opts) |
+ return p |
+} |
+ |
+// setOptions applies the options to the parser. |
+func (p *parser) setOptions(opts []Option) { |
+ for _, opt := range opts { |
+ opt(p) |
+ } |
+} |
+ |
+type resultTuple struct { |
+ v interface{} |
+ b bool |
+ end savepoint |
+} |
+ |
+type parser struct { |
+ filename string |
+ pt savepoint |
+ cur current |
+ |
+ data []byte |
+ errs *errList |
+ |
+ recover bool |
+ debug bool |
+ depth int |
+ |
+ memoize bool |
+ // memoization table for the packrat algorithm: |
+ // map[offset in source] map[expression or rule] {value, match} |
+ memo map[int]map[interface{}]resultTuple |
+ |
+ // rules table, maps the rule identifier to the rule node |
+ rules map[string]*rule |
+ // variables stack, map of label to value |
+ vstack []map[string]interface{} |
+ // rule stack, allows identification of the current rule in errors |
+ rstack []*rule |
+ |
+ // stats |
+ exprCnt int |
+} |
+ |
+// push a variable set on the vstack. |
+func (p *parser) pushV() { |
+ if cap(p.vstack) == len(p.vstack) { |
+ // create new empty slot in the stack |
+ p.vstack = append(p.vstack, nil) |
+ } else { |
+ // slice to 1 more |
+ p.vstack = p.vstack[:len(p.vstack)+1] |
+ } |
+ |
+ // get the last args set |
+ m := p.vstack[len(p.vstack)-1] |
+ if m != nil && len(m) == 0 { |
+ // empty map, all good |
+ return |
+ } |
+ |
+ m = make(map[string]interface{}) |
+ p.vstack[len(p.vstack)-1] = m |
+} |
+ |
+// pop a variable set from the vstack. |
+func (p *parser) popV() { |
+ // if the map is not empty, clear it |
+ m := p.vstack[len(p.vstack)-1] |
+ if len(m) > 0 { |
+ // GC that map |
+ p.vstack[len(p.vstack)-1] = nil |
+ } |
+ p.vstack = p.vstack[:len(p.vstack)-1] |
+} |
+ |
+func (p *parser) print(prefix, s string) string { |
+ if !p.debug { |
+ return s |
+ } |
+ |
+ fmt.Printf("%s %d:%d:%d: %s [%#U]\n", |
+ prefix, p.pt.line, p.pt.col, p.pt.offset, s, p.pt.rn) |
+ return s |
+} |
+ |
+func (p *parser) in(s string) string { |
+ p.depth++ |
+ return p.print(strings.Repeat(" ", p.depth)+">", s) |
+} |
+ |
+func (p *parser) out(s string) string { |
+ p.depth-- |
+ return p.print(strings.Repeat(" ", p.depth)+"<", s) |
+} |
+ |
+func (p *parser) addErr(err error) { |
+ p.addErrAt(err, p.pt.position) |
+} |
+ |
+func (p *parser) addErrAt(err error, pos position) { |
+ var buf bytes.Buffer |
+ if p.filename != "" { |
+ buf.WriteString(p.filename) |
+ } |
+ if buf.Len() > 0 { |
+ buf.WriteString(":") |
+ } |
+ buf.WriteString(fmt.Sprintf("%d:%d (%d)", pos.line, pos.col, pos.offset)) |
+ if len(p.rstack) > 0 { |
+ if buf.Len() > 0 { |
+ buf.WriteString(": ") |
+ } |
+ rule := p.rstack[len(p.rstack)-1] |
+ if rule.displayName != "" { |
+ buf.WriteString("rule " + rule.displayName) |
+ } else { |
+ buf.WriteString("rule " + rule.name) |
+ } |
+ } |
+ pe := &parserError{Inner: err, prefix: buf.String()} |
+ p.errs.add(pe) |
+} |
+ |
+// read advances the parser to the next rune. |
+func (p *parser) read() { |
+ p.pt.offset += p.pt.w |
+ rn, n := utf8.DecodeRune(p.data[p.pt.offset:]) |
+ p.pt.rn = rn |
+ p.pt.w = n |
+ p.pt.col++ |
+ if rn == '\n' { |
+ p.pt.line++ |
+ p.pt.col = 0 |
+ } |
+ |
+ if rn == utf8.RuneError { |
+ if n > 0 { |
+ p.addErr(errInvalidEncoding) |
+ } |
+ } |
+} |
+ |
+// restore parser position to the savepoint pt. |
+func (p *parser) restore(pt savepoint) { |
+ if p.debug { |
+ defer p.out(p.in("restore")) |
+ } |
+ if pt.offset == p.pt.offset { |
+ return |
+ } |
+ p.pt = pt |
+} |
+ |
+// get the slice of bytes from the savepoint start to the current position. |
+func (p *parser) sliceFrom(start savepoint) []byte { |
+ return p.data[start.position.offset:p.pt.position.offset] |
+} |
+ |
+func (p *parser) getMemoized(node interface{}) (resultTuple, bool) { |
+ if len(p.memo) == 0 { |
+ return resultTuple{}, false |
+ } |
+ m := p.memo[p.pt.offset] |
+ if len(m) == 0 { |
+ return resultTuple{}, false |
+ } |
+ res, ok := m[node] |
+ return res, ok |
+} |
+ |
+func (p *parser) setMemoized(pt savepoint, node interface{}, tuple resultTuple) { |
+ if p.memo == nil { |
+ p.memo = make(map[int]map[interface{}]resultTuple) |
+ } |
+ m := p.memo[pt.offset] |
+ if m == nil { |
+ m = make(map[interface{}]resultTuple) |
+ p.memo[pt.offset] = m |
+ } |
+ m[node] = tuple |
+} |
+ |
+func (p *parser) buildRulesTable(g *grammar) { |
+ p.rules = make(map[string]*rule, len(g.rules)) |
+ for _, r := range g.rules { |
+ p.rules[r.name] = r |
+ } |
+} |
+ |
+func (p *parser) parse(g *grammar) (val interface{}, err error) { |
+ if len(g.rules) == 0 { |
+ p.addErr(errNoRule) |
+ return nil, p.errs.err() |
+ } |
+ |
+ // TODO : not super critical but this could be generated |
+ p.buildRulesTable(g) |
+ |
+ if p.recover { |
+ // panic can be used in action code to stop parsing immediately |
+ // and return the panic as an error. |
+ defer func() { |
+ if e := recover(); e != nil { |
+ if p.debug { |
+ defer p.out(p.in("panic handler")) |
+ } |
+ val = nil |
+ switch e := e.(type) { |
+ case error: |
+ p.addErr(e) |
+ default: |
+ p.addErr(fmt.Errorf("%v", e)) |
+ } |
+ err = p.errs.err() |
+ } |
+ }() |
+ } |
+ |
+ // start rule is rule [0] |
+ p.read() // advance to first rune |
+ val, ok := p.parseRule(g.rules[0]) |
+ if !ok { |
+ if len(*p.errs) == 0 { |
+ // make sure this doesn't go out silently |
+ p.addErr(errNoMatch) |
+ } |
+ return nil, p.errs.err() |
+ } |
+ return val, p.errs.err() |
+} |
+ |
+func (p *parser) parseRule(rule *rule) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseRule " + rule.name)) |
+ } |
+ |
+ if p.memoize { |
+ res, ok := p.getMemoized(rule) |
+ if ok { |
+ p.restore(res.end) |
+ return res.v, res.b |
+ } |
+ } |
+ |
+ start := p.pt |
+ p.rstack = append(p.rstack, rule) |
+ p.pushV() |
+ val, ok := p.parseExpr(rule.expr) |
+ p.popV() |
+ p.rstack = p.rstack[:len(p.rstack)-1] |
+ if ok && p.debug { |
+ p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) |
+ } |
+ |
+ if p.memoize { |
+ p.setMemoized(start, rule, resultTuple{val, ok, p.pt}) |
+ } |
+ return val, ok |
+} |
+ |
+func (p *parser) parseExpr(expr interface{}) (interface{}, bool) { |
+ var pt savepoint |
+ var ok bool |
+ |
+ if p.memoize { |
+ res, ok := p.getMemoized(expr) |
+ if ok { |
+ p.restore(res.end) |
+ return res.v, res.b |
+ } |
+ pt = p.pt |
+ } |
+ |
+ p.exprCnt++ |
+ var val interface{} |
+ switch expr := expr.(type) { |
+ case *actionExpr: |
+ val, ok = p.parseActionExpr(expr) |
+ case *andCodeExpr: |
+ val, ok = p.parseAndCodeExpr(expr) |
+ case *andExpr: |
+ val, ok = p.parseAndExpr(expr) |
+ case *anyMatcher: |
+ val, ok = p.parseAnyMatcher(expr) |
+ case *charClassMatcher: |
+ val, ok = p.parseCharClassMatcher(expr) |
+ case *choiceExpr: |
+ val, ok = p.parseChoiceExpr(expr) |
+ case *labeledExpr: |
+ val, ok = p.parseLabeledExpr(expr) |
+ case *litMatcher: |
+ val, ok = p.parseLitMatcher(expr) |
+ case *notCodeExpr: |
+ val, ok = p.parseNotCodeExpr(expr) |
+ case *notExpr: |
+ val, ok = p.parseNotExpr(expr) |
+ case *oneOrMoreExpr: |
+ val, ok = p.parseOneOrMoreExpr(expr) |
+ case *ruleRefExpr: |
+ val, ok = p.parseRuleRefExpr(expr) |
+ case *seqExpr: |
+ val, ok = p.parseSeqExpr(expr) |
+ case *zeroOrMoreExpr: |
+ val, ok = p.parseZeroOrMoreExpr(expr) |
+ case *zeroOrOneExpr: |
+ val, ok = p.parseZeroOrOneExpr(expr) |
+ default: |
+ panic(fmt.Sprintf("unknown expression type %T", expr)) |
+ } |
+ if p.memoize { |
+ p.setMemoized(pt, expr, resultTuple{val, ok, p.pt}) |
+ } |
+ return val, ok |
+} |
+ |
+func (p *parser) parseActionExpr(act *actionExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseActionExpr")) |
+ } |
+ |
+ start := p.pt |
+ val, ok := p.parseExpr(act.expr) |
+ if ok { |
+ p.cur.pos = start.position |
+ p.cur.text = p.sliceFrom(start) |
+ actVal, err := act.run(p) |
+ if err != nil { |
+ p.addErrAt(err, start.position) |
+ } |
+ val = actVal |
+ } |
+ if ok && p.debug { |
+ p.print(strings.Repeat(" ", p.depth)+"MATCH", string(p.sliceFrom(start))) |
+ } |
+ return val, ok |
+} |
+ |
+func (p *parser) parseAndCodeExpr(and *andCodeExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseAndCodeExpr")) |
+ } |
+ |
+ ok, err := and.run(p) |
+ if err != nil { |
+ p.addErr(err) |
+ } |
+ return nil, ok |
+} |
+ |
+func (p *parser) parseAndExpr(and *andExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseAndExpr")) |
+ } |
+ |
+ pt := p.pt |
+ p.pushV() |
+ _, ok := p.parseExpr(and.expr) |
+ p.popV() |
+ p.restore(pt) |
+ return nil, ok |
+} |
+ |
+func (p *parser) parseAnyMatcher(any *anyMatcher) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseAnyMatcher")) |
+ } |
+ |
+ if p.pt.rn != utf8.RuneError { |
+ start := p.pt |
+ p.read() |
+ return p.sliceFrom(start), true |
+ } |
+ return nil, false |
+} |
+ |
+func (p *parser) parseCharClassMatcher(chr *charClassMatcher) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseCharClassMatcher")) |
+ } |
+ |
+ cur := p.pt.rn |
+ // can't match EOF |
+ if cur == utf8.RuneError { |
+ return nil, false |
+ } |
+ start := p.pt |
+ if chr.ignoreCase { |
+ cur = unicode.ToLower(cur) |
+ } |
+ |
+ // try to match in the list of available chars |
+ for _, rn := range chr.chars { |
+ if rn == cur { |
+ if chr.inverted { |
+ return nil, false |
+ } |
+ p.read() |
+ return p.sliceFrom(start), true |
+ } |
+ } |
+ |
+ // try to match in the list of ranges |
+ for i := 0; i < len(chr.ranges); i += 2 { |
+ if cur >= chr.ranges[i] && cur <= chr.ranges[i+1] { |
+ if chr.inverted { |
+ return nil, false |
+ } |
+ p.read() |
+ return p.sliceFrom(start), true |
+ } |
+ } |
+ |
+ // try to match in the list of Unicode classes |
+ for _, cl := range chr.classes { |
+ if unicode.Is(cl, cur) { |
+ if chr.inverted { |
+ return nil, false |
+ } |
+ p.read() |
+ return p.sliceFrom(start), true |
+ } |
+ } |
+ |
+ if chr.inverted { |
+ p.read() |
+ return p.sliceFrom(start), true |
+ } |
+ return nil, false |
+} |
+ |
+func (p *parser) parseChoiceExpr(ch *choiceExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseChoiceExpr")) |
+ } |
+ |
+ for _, alt := range ch.alternatives { |
+ p.pushV() |
+ val, ok := p.parseExpr(alt) |
+ p.popV() |
+ if ok { |
+ return val, ok |
+ } |
+ } |
+ return nil, false |
+} |
+ |
+func (p *parser) parseLabeledExpr(lab *labeledExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseLabeledExpr")) |
+ } |
+ |
+ p.pushV() |
+ val, ok := p.parseExpr(lab.expr) |
+ p.popV() |
+ if ok && lab.label != "" { |
+ m := p.vstack[len(p.vstack)-1] |
+ m[lab.label] = val |
+ } |
+ return val, ok |
+} |
+ |
+func (p *parser) parseLitMatcher(lit *litMatcher) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseLitMatcher")) |
+ } |
+ |
+ start := p.pt |
+ for _, want := range lit.val { |
+ cur := p.pt.rn |
+ if lit.ignoreCase { |
+ cur = unicode.ToLower(cur) |
+ } |
+ if cur != want { |
+ p.restore(start) |
+ return nil, false |
+ } |
+ p.read() |
+ } |
+ return p.sliceFrom(start), true |
+} |
+ |
+func (p *parser) parseNotCodeExpr(not *notCodeExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseNotCodeExpr")) |
+ } |
+ |
+ ok, err := not.run(p) |
+ if err != nil { |
+ p.addErr(err) |
+ } |
+ return nil, !ok |
+} |
+ |
+func (p *parser) parseNotExpr(not *notExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseNotExpr")) |
+ } |
+ |
+ pt := p.pt |
+ p.pushV() |
+ _, ok := p.parseExpr(not.expr) |
+ p.popV() |
+ p.restore(pt) |
+ return nil, !ok |
+} |
+ |
+func (p *parser) parseOneOrMoreExpr(expr *oneOrMoreExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseOneOrMoreExpr")) |
+ } |
+ |
+ var vals []interface{} |
+ |
+ for { |
+ p.pushV() |
+ val, ok := p.parseExpr(expr.expr) |
+ p.popV() |
+ if !ok { |
+ if len(vals) == 0 { |
+ // did not match once, no match |
+ return nil, false |
+ } |
+ return vals, true |
+ } |
+ vals = append(vals, val) |
+ } |
+} |
+ |
+func (p *parser) parseRuleRefExpr(ref *ruleRefExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseRuleRefExpr " + ref.name)) |
+ } |
+ |
+ if ref.name == "" { |
+ panic(fmt.Sprintf("%s: invalid rule: missing name", ref.pos)) |
+ } |
+ |
+ rule := p.rules[ref.name] |
+ if rule == nil { |
+ p.addErr(fmt.Errorf("undefined rule: %s", ref.name)) |
+ return nil, false |
+ } |
+ return p.parseRule(rule) |
+} |
+ |
+func (p *parser) parseSeqExpr(seq *seqExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseSeqExpr")) |
+ } |
+ |
+ var vals []interface{} |
+ |
+ pt := p.pt |
+ for _, expr := range seq.exprs { |
+ val, ok := p.parseExpr(expr) |
+ if !ok { |
+ p.restore(pt) |
+ return nil, false |
+ } |
+ vals = append(vals, val) |
+ } |
+ return vals, true |
+} |
+ |
+func (p *parser) parseZeroOrMoreExpr(expr *zeroOrMoreExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseZeroOrMoreExpr")) |
+ } |
+ |
+ var vals []interface{} |
+ |
+ for { |
+ p.pushV() |
+ val, ok := p.parseExpr(expr.expr) |
+ p.popV() |
+ if !ok { |
+ return vals, true |
+ } |
+ vals = append(vals, val) |
+ } |
+} |
+ |
+func (p *parser) parseZeroOrOneExpr(expr *zeroOrOneExpr) (interface{}, bool) { |
+ if p.debug { |
+ defer p.out(p.in("parseZeroOrOneExpr")) |
+ } |
+ |
+ p.pushV() |
+ val, _ := p.parseExpr(expr.expr) |
+ p.popV() |
+ // whether it matched or not, consider it a match |
+ return val, true |
+} |
+ |
+func rangeTable(class string) *unicode.RangeTable { |
+ if rt, ok := unicode.Categories[class]; ok { |
+ return rt |
+ } |
+ if rt, ok := unicode.Properties[class]; ok { |
+ return rt |
+ } |
+ if rt, ok := unicode.Scripts[class]; ok { |
+ return rt |
+ } |
+ |
+ // cannot happen |
+ panic(fmt.Sprintf("invalid Unicode class: %s", class)) |
+} |