OLD | NEW |
(Empty) | |
| 1 // vim: syntax=go |
| 2 { |
| 3 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 4 // Use of this source code is governed by a BSD-style license that can be |
| 5 // found in the LICENSE file. |
| 6 |
| 7 package parser |
| 8 |
| 9 type retriesHolder uint64 |
| 10 type identityHolder uint64 |
| 11 |
| 12 var whitespaceRE = regexp.MustCompile("#[^\n]*\n|\\s+") |
| 13 |
| 14 // CleanPhrase removes all whitespace and comments from a phrase in preparation |
| 15 // for parsing it. The parsers defined in this package only operate on cleaned |
| 16 // phrases, and will fail to parse if you provide a phrase with whitespace. |
| 17 func CleanPhrase(p string) string { |
| 18 return whitespaceRE.ReplaceAllLiteralString(p, "") |
| 19 } |
| 20 |
| 21 // ParsePhrase is a convenience function for the parser functions in this |
| 22 // package to return a typed Phrase object as the result of a string. |
| 23 func ParsePhrase(p string) (Phrase, error) { |
| 24 ret, err := Parse("<string>", []byte(p)) |
| 25 if err != nil { |
| 26 return nil, err |
| 27 } |
| 28 return ret.(Phrase), nil |
| 29 } |
| 30 } |
| 31 |
| 32 Overall = p:Phrase !. { |
| 33 return p, nil |
| 34 } / p:Phrase ext:Extra { |
| 35 return nil, fmt.Errorf("found extra: %q", ext.(string)) |
| 36 } / !. { |
| 37 return nil, fmt.Errorf("empty phrase") |
| 38 } / ext:Extra { |
| 39 return nil, fmt.Errorf("expected phrase: %q", ext.(string)) |
| 40 } |
| 41 |
| 42 Extra = .* { |
| 43 return string(c.text), nil |
| 44 } |
| 45 |
| 46 Phrase = ret:ReturnStage { |
| 47 return Phrase{ret.(Stage)}, nil |
| 48 } / first:Stage middleI:(',' Stage)* retI:(',' ReturnStage)? { |
| 49 middle, _ := middleI.([]interface{}) |
| 50 retStage, _ := retI.([]interface{}) |
| 51 |
| 52 amt := 1 |
| 53 if middle != nil { |
| 54 amt += len(middle) |
| 55 } |
| 56 if retStage != nil { |
| 57 amt++ |
| 58 } |
| 59 |
| 60 ret := make(Phrase, 0, amt) |
| 61 ret = append(ret, first.(Stage)) |
| 62 for _, itm := range middle { |
| 63 ret = append(ret, itm.([]interface{})[1].(Stage)) |
| 64 } |
| 65 if retStage != nil { |
| 66 ret = append(ret, retStage[1].(Stage)) |
| 67 } |
| 68 |
| 69 return ret, nil |
| 70 } / ',' .* { |
| 71 return nil, fmt.Errorf("expected stage in %q", string(c.text)) |
| 72 } |
| 73 |
| 74 Stage = s:(FailureStage / StallStage / DepsStage) { |
| 75 return s, nil |
| 76 } |
| 77 |
| 78 FailureStage = '%' num:Num { |
| 79 return FailureStage(num.(uint64)), nil |
| 80 } |
| 81 |
| 82 StallStage = '@' amt:Duration { |
| 83 return StallStage(amt.(time.Duration)), nil |
| 84 } |
| 85 |
| 86 ReturnStage = '=' val:Num exp:('<' Duration)? { |
| 87 ret := &ReturnStage{Value: val.(uint64)} |
| 88 if exp != nil { |
| 89 ret.Expiration = exp.([]interface{})[1].(time.Duration) |
| 90 } |
| 91 return ret, nil |
| 92 } |
| 93 |
| 94 DepsStage = first:Dependency restI:('&' Dependency)* { |
| 95 rest, _ := restI.([]interface{}) |
| 96 |
| 97 if rest == nil { |
| 98 return DepsStage{first.(*Dependency)}, nil |
| 99 } |
| 100 ret := make(DepsStage, 0, 1+len(rest)) |
| 101 ret = append(ret, first.(*Dependency)) |
| 102 for _, itm := range rest{ |
| 103 ret = append(ret, itm.([]interface{})[1].(*Dependency)) |
| 104 } |
| 105 return ret, nil |
| 106 } / '&' .* { |
| 107 return nil, fmt.Errorf("expected dependency in %q", string(c.text)) |
| 108 } |
| 109 |
| 110 Dependency = shards:Shards? attempts:Attempts? id:ID options:Option? subI:( '('
Phrase ')' )? { |
| 111 ret := &Dependency{Name: id.(string)} |
| 112 |
| 113 if shards != nil { |
| 114 ret.ShardCount = shards.(uint64) |
| 115 } |
| 116 |
| 117 if attempts != nil { |
| 118 ret.AttemptNums = attempts.(RangeSlice) |
| 119 } |
| 120 |
| 121 if options != nil { |
| 122 switch x := options.(type) { |
| 123 case retriesHolder: |
| 124 if attempts != nil { |
| 125 return nil, fmt.Errorf( |
| 126 "in %q: retries are incompatible with specified Attempts", |
| 127 string(c.text)) |
| 128 } |
| 129 ret.Retries = uint64(x) |
| 130 |
| 131 case identityHolder: |
| 132 ret.Uniq = uint64(x) |
| 133 } |
| 134 } |
| 135 |
| 136 sub, _ := subI.([]interface{}) |
| 137 if sub != nil { |
| 138 ret.Substages = sub[1].(Phrase) |
| 139 } |
| 140 |
| 141 return ret, nil |
| 142 } |
| 143 |
| 144 Option = '+' num:Num { |
| 145 return retriesHolder(num.(uint64)), nil |
| 146 } / '^' num:Num { |
| 147 return identityHolder(num.(uint64)), nil |
| 148 } |
| 149 |
| 150 Shards = '{' num:Num '}' { |
| 151 return num, nil |
| 152 } |
| 153 |
| 154 Attempts = '[' rs:RangeSlice ']' { |
| 155 return rs, nil |
| 156 } |
| 157 |
| 158 RangeSlice = firstI:Range restI:(',' Range)* { |
| 159 rest, _ := restI.([]interface{}) |
| 160 |
| 161 ret := make(RangeSlice, 0, 1+len(rest)) |
| 162 ret = append(ret, firstI.(Range)) |
| 163 for _, itm := range rest { |
| 164 ret = append(ret, itm.([]interface{})[1].(Range)) |
| 165 } |
| 166 |
| 167 return ret, nil |
| 168 } |
| 169 |
| 170 Range = loI:Num '-' hiI:Num { |
| 171 lo := loI.(uint64) |
| 172 hi := hiI.(uint64) |
| 173 |
| 174 if lo >= hi { |
| 175 return nil, fmt.Errorf("invalid range %q: lo >= hi", string(c.text)) |
| 176 } |
| 177 |
| 178 return Range{lo, hi}, nil |
| 179 } / num:Num { |
| 180 return Range{num.(uint64), 0}, nil |
| 181 } |
| 182 |
| 183 Duration = num:Num { |
| 184 return time.Second * time.Duration(num.(uint64)), nil |
| 185 } |
| 186 |
| 187 Num = '0' { |
| 188 return nil, fmt.Errorf("zero value not acceptable") |
| 189 } / [1-9][0-9]* { |
| 190 return strconv.ParseUint(string(c.text), 10, 64) |
| 191 } / .* { |
| 192 return nil, fmt.Errorf("expected number, got %q", string(c.text)) |
| 193 } |
| 194 |
| 195 ID = [a-zA-Z'_]+ { |
| 196 return string(c.text), nil |
| 197 } |
OLD | NEW |