Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Helper functions useful when writing scripts that integrate with GN. | 5 """Helper functions useful when writing scripts that integrate with GN. |
| 6 | 6 |
| 7 The main functions are ToGNString and FromGNString which convert between | 7 The main functions are ToGNString and FromGNString which convert between |
| 8 serialized GN veriables and Python variables. | 8 serialized GN veriables and Python variables. |
| 9 | 9 |
| 10 To use in a random python file in the build: | 10 To use in a random python file in the build: |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 93 the Python string literal directly. | 93 the Python string literal directly. |
| 94 | 94 |
| 95 The main use cases for this is for other types, in particular lists. When | 95 The main use cases for this is for other types, in particular lists. When |
| 96 using string interpolation on a list (as in the top example) the embedded | 96 using string interpolation on a list (as in the top example) the embedded |
| 97 strings will be quoted and escaped according to GN rules so the list can be | 97 strings will be quoted and escaped according to GN rules so the list can be |
| 98 re-parsed to get the same result.""" | 98 re-parsed to get the same result.""" |
| 99 parser = GNValueParser(input) | 99 parser = GNValueParser(input) |
| 100 return parser.Parse() | 100 return parser.Parse() |
| 101 | 101 |
| 102 | 102 |
| 103 def FromGNArgs(input): | |
| 104 """Converts a string with a bunch of gn arg assignments into a Python dict. | |
| 105 | |
| 106 Given a whitespace-separated list of | |
| 107 | |
| 108 <ident> = (integer | string | boolean) | |
| 109 | |
| 110 gn assignments, this returns a Python dict, i.e.: | |
| 111 | |
| 112 FromGNArgs("foo=true\nbar=1\n") -> { 'foo': True, 'bar': 1 }. | |
| 113 | |
| 114 Only simple types are supported; variables, complex types, calls | |
| 115 and other, more complicated things are not. | |
| 116 | |
| 117 This routine is meant to handle only the simple sorts of values that | |
| 118 arise in parsing --args. | |
| 119 """ | |
| 120 parser = GNValueParser(input) | |
| 121 return parser.ParseArgs() | |
| 122 | |
| 123 | |
| 103 def UnescapeGNString(value): | 124 def UnescapeGNString(value): |
| 104 """Given a string with GN escaping, returns the unescaped string. | 125 """Given a string with GN escaping, returns the unescaped string. |
| 105 | 126 |
| 106 Be careful not to feed with input from a Python parsing function like | 127 Be careful not to feed with input from a Python parsing function like |
| 107 'ast' because it will do Python unescaping, which will be incorrect when | 128 'ast' because it will do Python unescaping, which will be incorrect when |
| 108 fed into the GN unescaper.""" | 129 fed into the GN unescaper.""" |
| 109 result = '' | 130 result = '' |
| 110 i = 0 | 131 i = 0 |
| 111 while i < len(value): | 132 while i < len(value): |
| 112 if value[i] == '\\': | 133 if value[i] == '\\': |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 164 - GN lists ('[1, "asdf", 3]') will be converted to Python lists. | 185 - GN lists ('[1, "asdf", 3]') will be converted to Python lists. |
| 165 | 186 |
| 166 - GN scopes ('{ ... }') are not supported.""" | 187 - GN scopes ('{ ... }') are not supported.""" |
| 167 result = self._ParseAllowTrailing() | 188 result = self._ParseAllowTrailing() |
| 168 self.ConsumeWhitespace() | 189 self.ConsumeWhitespace() |
| 169 if not self.IsDone(): | 190 if not self.IsDone(): |
| 170 raise GNException("Trailing input after parsing:\n " + | 191 raise GNException("Trailing input after parsing:\n " + |
| 171 self.input[self.cur:]) | 192 self.input[self.cur:]) |
| 172 return result | 193 return result |
| 173 | 194 |
| 174 def _ParseAllowTrailing(self): | 195 def ParseArgs(self): |
| 196 """Converts a whitespace-separated list of ident=literals to a dict. | |
| 197 | |
| 198 See additional usage notes on FromGNArgs, above. | |
| 199 """ | |
| 200 d = {} | |
| 201 | |
| 202 self.ConsumeWhitespace() | |
| 203 while not self.IsDone(): | |
| 204 ident = self._ParseIdent() | |
| 205 self.ConsumeWhitespace() | |
| 206 if self.input[self.cur] != '=': | |
| 207 raise GNException("Unexpected token: " + self.input[self.cur:]) | |
| 208 self.cur += 1 | |
| 209 self.ConsumeWhitespace() | |
| 210 val = self._ParseAllowTrailing(lists_are_allowed=False) | |
|
brettw
2016/04/05 19:25:27
It seems like you go through some effort here to d
Dirk Pranke
2016/04/05 19:28:42
I don't remember exactly now, but I have a vague m
| |
| 211 self.ConsumeWhitespace() | |
| 212 d[ident] = val | |
| 213 | |
| 214 return d | |
| 215 | |
| 216 def _ParseAllowTrailing(self, lists_are_allowed=True): | |
| 175 """Internal version of Parse that doesn't check for trailing stuff.""" | 217 """Internal version of Parse that doesn't check for trailing stuff.""" |
| 176 self.ConsumeWhitespace() | 218 self.ConsumeWhitespace() |
| 177 if self.IsDone(): | 219 if self.IsDone(): |
| 178 raise GNException("Expected input to parse.") | 220 raise GNException("Expected input to parse.") |
| 179 | 221 |
| 180 next_char = self.input[self.cur] | 222 next_char = self.input[self.cur] |
| 181 if next_char == '[': | 223 if lists_are_allowed and next_char == '[': |
| 182 return self.ParseList() | 224 return self.ParseList() |
| 183 elif _IsDigitOrMinus(next_char): | 225 elif _IsDigitOrMinus(next_char): |
| 184 return self.ParseNumber() | 226 return self.ParseNumber() |
| 185 elif next_char == '"': | 227 elif next_char == '"': |
| 186 return self.ParseString() | 228 return self.ParseString() |
| 187 elif self._ConstantFollows('true'): | 229 elif self._ConstantFollows('true'): |
| 188 return True | 230 return True |
| 189 elif self._ConstantFollows('false'): | 231 elif self._ConstantFollows('false'): |
| 190 return False | 232 return False |
| 191 else: | 233 else: |
| 192 raise GNException("Unexpected token: " + self.input[self.cur:]) | 234 raise GNException("Unexpected token: " + self.input[self.cur:]) |
| 193 | 235 |
| 236 def _ParseIdent(self): | |
| 237 id = '' | |
| 238 | |
| 239 next_char = self.input[self.cur] | |
| 240 if not next_char.isalpha() and not next_char=='_': | |
| 241 raise GNException("Expected an identifier: " + self.input[self.cur:]) | |
| 242 | |
| 243 id += next_char | |
| 244 self.cur += 1 | |
| 245 | |
| 246 next_char = self.input[self.cur] | |
| 247 while next_char.isalpha() or next_char.isdigit() or next_char=='_': | |
| 248 id += next_char | |
| 249 self.cur += 1 | |
| 250 next_char = self.input[self.cur] | |
| 251 | |
| 252 return id | |
| 253 | |
| 194 def ParseNumber(self): | 254 def ParseNumber(self): |
| 195 self.ConsumeWhitespace() | 255 self.ConsumeWhitespace() |
| 196 if self.IsDone(): | 256 if self.IsDone(): |
| 197 raise GNException('Expected number but got nothing.') | 257 raise GNException('Expected number but got nothing.') |
| 198 | 258 |
| 199 begin = self.cur | 259 begin = self.cur |
| 200 | 260 |
| 201 # The first character can include a negative sign. | 261 # The first character can include a negative sign. |
| 202 if not self.IsDone() and _IsDigitOrMinus(self.input[self.cur]): | 262 if not self.IsDone() and _IsDigitOrMinus(self.input[self.cur]): |
| 203 self.cur += 1 | 263 self.cur += 1 |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 279 location in the input. If it does, the text is consumed and the function | 339 location in the input. If it does, the text is consumed and the function |
| 280 returns true. Otherwise, returns false and the current position is | 340 returns true. Otherwise, returns false and the current position is |
| 281 unchanged.""" | 341 unchanged.""" |
| 282 end = self.cur + len(constant) | 342 end = self.cur + len(constant) |
| 283 if end > len(self.input): | 343 if end > len(self.input): |
| 284 return False # Not enough room. | 344 return False # Not enough room. |
| 285 if self.input[self.cur:end] == constant: | 345 if self.input[self.cur:end] == constant: |
| 286 self.cur = end | 346 self.cur = end |
| 287 return True | 347 return True |
| 288 return False | 348 return False |
| OLD | NEW |