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 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 the Python string literal directly. | 96 the Python string literal directly. |
97 | 97 |
98 The main use cases for this is for other types, in particular lists. When | 98 The main use cases for this is for other types, in particular lists. When |
99 using string interpolation on a list (as in the top example) the embedded | 99 using string interpolation on a list (as in the top example) the embedded |
100 strings will be quoted and escaped according to GN rules so the list can be | 100 strings will be quoted and escaped according to GN rules so the list can be |
101 re-parsed to get the same result.""" | 101 re-parsed to get the same result.""" |
102 parser = GNValueParser(input) | 102 parser = GNValueParser(input) |
103 return parser.Parse() | 103 return parser.Parse() |
104 | 104 |
105 | 105 |
| 106 def FromGNArgs(input): |
| 107 """Converts a string with a bunch of gn arg assignments into a Python dict. |
| 108 |
| 109 Given a whitespace-separated list of |
| 110 |
| 111 <ident> = (integer | string | boolean | <list of the former>) |
| 112 |
| 113 gn assignments, this returns a Python dict, i.e.: |
| 114 |
| 115 FromGNArgs("foo=true\nbar=1\n") -> { 'foo': True, 'bar': 1 }. |
| 116 |
| 117 Only simple types and lists supported; variables, structs, calls |
| 118 and other, more complicated things are not. |
| 119 |
| 120 This routine is meant to handle only the simple sorts of values that |
| 121 arise in parsing --args. |
| 122 """ |
| 123 parser = GNValueParser(input) |
| 124 return parser.ParseArgs() |
| 125 |
| 126 |
106 def UnescapeGNString(value): | 127 def UnescapeGNString(value): |
107 """Given a string with GN escaping, returns the unescaped string. | 128 """Given a string with GN escaping, returns the unescaped string. |
108 | 129 |
109 Be careful not to feed with input from a Python parsing function like | 130 Be careful not to feed with input from a Python parsing function like |
110 'ast' because it will do Python unescaping, which will be incorrect when | 131 'ast' because it will do Python unescaping, which will be incorrect when |
111 fed into the GN unescaper.""" | 132 fed into the GN unescaper.""" |
112 result = '' | 133 result = '' |
113 i = 0 | 134 i = 0 |
114 while i < len(value): | 135 while i < len(value): |
115 if value[i] == '\\': | 136 if value[i] == '\\': |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
167 - GN lists ('[1, "asdf", 3]') will be converted to Python lists. | 188 - GN lists ('[1, "asdf", 3]') will be converted to Python lists. |
168 | 189 |
169 - GN scopes ('{ ... }') are not supported.""" | 190 - GN scopes ('{ ... }') are not supported.""" |
170 result = self._ParseAllowTrailing() | 191 result = self._ParseAllowTrailing() |
171 self.ConsumeWhitespace() | 192 self.ConsumeWhitespace() |
172 if not self.IsDone(): | 193 if not self.IsDone(): |
173 raise GNException("Trailing input after parsing:\n " + | 194 raise GNException("Trailing input after parsing:\n " + |
174 self.input[self.cur:]) | 195 self.input[self.cur:]) |
175 return result | 196 return result |
176 | 197 |
| 198 def ParseArgs(self): |
| 199 """Converts a whitespace-separated list of ident=literals to a dict. |
| 200 |
| 201 See additional usage notes on FromGNArgs, above. |
| 202 """ |
| 203 d = {} |
| 204 |
| 205 self.ConsumeWhitespace() |
| 206 while not self.IsDone(): |
| 207 ident = self._ParseIdent() |
| 208 self.ConsumeWhitespace() |
| 209 if self.input[self.cur] != '=': |
| 210 raise GNException("Unexpected token: " + self.input[self.cur:]) |
| 211 self.cur += 1 |
| 212 self.ConsumeWhitespace() |
| 213 val = self._ParseAllowTrailing() |
| 214 self.ConsumeWhitespace() |
| 215 d[ident] = val |
| 216 |
| 217 return d |
| 218 |
177 def _ParseAllowTrailing(self): | 219 def _ParseAllowTrailing(self): |
178 """Internal version of Parse that doesn't check for trailing stuff.""" | 220 """Internal version of Parse that doesn't check for trailing stuff.""" |
179 self.ConsumeWhitespace() | 221 self.ConsumeWhitespace() |
180 if self.IsDone(): | 222 if self.IsDone(): |
181 raise GNException("Expected input to parse.") | 223 raise GNException("Expected input to parse.") |
182 | 224 |
183 next_char = self.input[self.cur] | 225 next_char = self.input[self.cur] |
184 if next_char == '[': | 226 if next_char == '[': |
185 return self.ParseList() | 227 return self.ParseList() |
186 elif _IsDigitOrMinus(next_char): | 228 elif _IsDigitOrMinus(next_char): |
187 return self.ParseNumber() | 229 return self.ParseNumber() |
188 elif next_char == '"': | 230 elif next_char == '"': |
189 return self.ParseString() | 231 return self.ParseString() |
190 elif self._ConstantFollows('true'): | 232 elif self._ConstantFollows('true'): |
191 return True | 233 return True |
192 elif self._ConstantFollows('false'): | 234 elif self._ConstantFollows('false'): |
193 return False | 235 return False |
194 else: | 236 else: |
195 raise GNException("Unexpected token: " + self.input[self.cur:]) | 237 raise GNException("Unexpected token: " + self.input[self.cur:]) |
196 | 238 |
| 239 def _ParseIdent(self): |
| 240 id = '' |
| 241 |
| 242 next_char = self.input[self.cur] |
| 243 if not next_char.isalpha() and not next_char=='_': |
| 244 raise GNException("Expected an identifier: " + self.input[self.cur:]) |
| 245 |
| 246 id += next_char |
| 247 self.cur += 1 |
| 248 |
| 249 next_char = self.input[self.cur] |
| 250 while next_char.isalpha() or next_char.isdigit() or next_char=='_': |
| 251 id += next_char |
| 252 self.cur += 1 |
| 253 next_char = self.input[self.cur] |
| 254 |
| 255 return id |
| 256 |
197 def ParseNumber(self): | 257 def ParseNumber(self): |
198 self.ConsumeWhitespace() | 258 self.ConsumeWhitespace() |
199 if self.IsDone(): | 259 if self.IsDone(): |
200 raise GNException('Expected number but got nothing.') | 260 raise GNException('Expected number but got nothing.') |
201 | 261 |
202 begin = self.cur | 262 begin = self.cur |
203 | 263 |
204 # The first character can include a negative sign. | 264 # The first character can include a negative sign. |
205 if not self.IsDone() and _IsDigitOrMinus(self.input[self.cur]): | 265 if not self.IsDone() and _IsDigitOrMinus(self.input[self.cur]): |
206 self.cur += 1 | 266 self.cur += 1 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
282 location in the input. If it does, the text is consumed and the function | 342 location in the input. If it does, the text is consumed and the function |
283 returns true. Otherwise, returns false and the current position is | 343 returns true. Otherwise, returns false and the current position is |
284 unchanged.""" | 344 unchanged.""" |
285 end = self.cur + len(constant) | 345 end = self.cur + len(constant) |
286 if end > len(self.input): | 346 if end > len(self.input): |
287 return False # Not enough room. | 347 return False # Not enough room. |
288 if self.input[self.cur:end] == constant: | 348 if self.input[self.cur:end] == constant: |
289 self.cur = end | 349 self.cur = end |
290 return True | 350 return True |
291 return False | 351 return False |
OLD | NEW |