xref: /aosp_15_r20/external/libchrome/build/gn_helpers.py (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker# Copyright 2014 The Chromium Authors. All rights reserved.
2*635a8641SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
3*635a8641SAndroid Build Coastguard Worker# found in the LICENSE file.
4*635a8641SAndroid Build Coastguard Worker
5*635a8641SAndroid Build Coastguard Worker"""Helper functions useful when writing scripts that integrate with GN.
6*635a8641SAndroid Build Coastguard Worker
7*635a8641SAndroid Build Coastguard WorkerThe main functions are ToGNString and FromGNString which convert between
8*635a8641SAndroid Build Coastguard Workerserialized GN veriables and Python variables.
9*635a8641SAndroid Build Coastguard Worker
10*635a8641SAndroid Build Coastguard WorkerTo use in a random python file in the build:
11*635a8641SAndroid Build Coastguard Worker
12*635a8641SAndroid Build Coastguard Worker  import os
13*635a8641SAndroid Build Coastguard Worker  import sys
14*635a8641SAndroid Build Coastguard Worker
15*635a8641SAndroid Build Coastguard Worker  sys.path.append(os.path.join(os.path.dirname(__file__),
16*635a8641SAndroid Build Coastguard Worker                               os.pardir, os.pardir, "build"))
17*635a8641SAndroid Build Coastguard Worker  import gn_helpers
18*635a8641SAndroid Build Coastguard Worker
19*635a8641SAndroid Build Coastguard WorkerWhere the sequence of parameters to join is the relative path from your source
20*635a8641SAndroid Build Coastguard Workerfile to the build directory."""
21*635a8641SAndroid Build Coastguard Worker
22*635a8641SAndroid Build Coastguard Workerclass GNException(Exception):
23*635a8641SAndroid Build Coastguard Worker  pass
24*635a8641SAndroid Build Coastguard Worker
25*635a8641SAndroid Build Coastguard Worker
26*635a8641SAndroid Build Coastguard Workerdef ToGNString(value, allow_dicts = True):
27*635a8641SAndroid Build Coastguard Worker  """Returns a stringified GN equivalent of the Python value.
28*635a8641SAndroid Build Coastguard Worker
29*635a8641SAndroid Build Coastguard Worker  allow_dicts indicates if this function will allow converting dictionaries
30*635a8641SAndroid Build Coastguard Worker  to GN scopes. This is only possible at the top level, you can't nest a
31*635a8641SAndroid Build Coastguard Worker  GN scope in a list, so this should be set to False for recursive calls."""
32*635a8641SAndroid Build Coastguard Worker  if isinstance(value, basestring):
33*635a8641SAndroid Build Coastguard Worker    if value.find('\n') >= 0:
34*635a8641SAndroid Build Coastguard Worker      raise GNException("Trying to print a string with a newline in it.")
35*635a8641SAndroid Build Coastguard Worker    return '"' + \
36*635a8641SAndroid Build Coastguard Worker        value.replace('\\', '\\\\').replace('"', '\\"').replace('$', '\\$') + \
37*635a8641SAndroid Build Coastguard Worker        '"'
38*635a8641SAndroid Build Coastguard Worker
39*635a8641SAndroid Build Coastguard Worker  if isinstance(value, unicode):
40*635a8641SAndroid Build Coastguard Worker    return ToGNString(value.encode('utf-8'))
41*635a8641SAndroid Build Coastguard Worker
42*635a8641SAndroid Build Coastguard Worker  if isinstance(value, bool):
43*635a8641SAndroid Build Coastguard Worker    if value:
44*635a8641SAndroid Build Coastguard Worker      return "true"
45*635a8641SAndroid Build Coastguard Worker    return "false"
46*635a8641SAndroid Build Coastguard Worker
47*635a8641SAndroid Build Coastguard Worker  if isinstance(value, list):
48*635a8641SAndroid Build Coastguard Worker    return '[ %s ]' % ', '.join(ToGNString(v) for v in value)
49*635a8641SAndroid Build Coastguard Worker
50*635a8641SAndroid Build Coastguard Worker  if isinstance(value, dict):
51*635a8641SAndroid Build Coastguard Worker    if not allow_dicts:
52*635a8641SAndroid Build Coastguard Worker      raise GNException("Attempting to recursively print a dictionary.")
53*635a8641SAndroid Build Coastguard Worker    result = ""
54*635a8641SAndroid Build Coastguard Worker    for key in sorted(value):
55*635a8641SAndroid Build Coastguard Worker      if not isinstance(key, basestring):
56*635a8641SAndroid Build Coastguard Worker        raise GNException("Dictionary key is not a string.")
57*635a8641SAndroid Build Coastguard Worker      result += "%s = %s\n" % (key, ToGNString(value[key], False))
58*635a8641SAndroid Build Coastguard Worker    return result
59*635a8641SAndroid Build Coastguard Worker
60*635a8641SAndroid Build Coastguard Worker  if isinstance(value, int):
61*635a8641SAndroid Build Coastguard Worker    return str(value)
62*635a8641SAndroid Build Coastguard Worker
63*635a8641SAndroid Build Coastguard Worker  raise GNException("Unsupported type when printing to GN.")
64*635a8641SAndroid Build Coastguard Worker
65*635a8641SAndroid Build Coastguard Worker
66*635a8641SAndroid Build Coastguard Workerdef FromGNString(input_string):
67*635a8641SAndroid Build Coastguard Worker  """Converts the input string from a GN serialized value to Python values.
68*635a8641SAndroid Build Coastguard Worker
69*635a8641SAndroid Build Coastguard Worker  For details on supported types see GNValueParser.Parse() below.
70*635a8641SAndroid Build Coastguard Worker
71*635a8641SAndroid Build Coastguard Worker  If your GN script did:
72*635a8641SAndroid Build Coastguard Worker    something = [ "file1", "file2" ]
73*635a8641SAndroid Build Coastguard Worker    args = [ "--values=$something" ]
74*635a8641SAndroid Build Coastguard Worker  The command line would look something like:
75*635a8641SAndroid Build Coastguard Worker    --values="[ \"file1\", \"file2\" ]"
76*635a8641SAndroid Build Coastguard Worker  Which when interpreted as a command line gives the value:
77*635a8641SAndroid Build Coastguard Worker    [ "file1", "file2" ]
78*635a8641SAndroid Build Coastguard Worker
79*635a8641SAndroid Build Coastguard Worker  You can parse this into a Python list using GN rules with:
80*635a8641SAndroid Build Coastguard Worker    input_values = FromGNValues(options.values)
81*635a8641SAndroid Build Coastguard Worker  Although the Python 'ast' module will parse many forms of such input, it
82*635a8641SAndroid Build Coastguard Worker  will not handle GN escaping properly, nor GN booleans. You should use this
83*635a8641SAndroid Build Coastguard Worker  function instead.
84*635a8641SAndroid Build Coastguard Worker
85*635a8641SAndroid Build Coastguard Worker
86*635a8641SAndroid Build Coastguard Worker  A NOTE ON STRING HANDLING:
87*635a8641SAndroid Build Coastguard Worker
88*635a8641SAndroid Build Coastguard Worker  If you just pass a string on the command line to your Python script, or use
89*635a8641SAndroid Build Coastguard Worker  string interpolation on a string variable, the strings will not be quoted:
90*635a8641SAndroid Build Coastguard Worker    str = "asdf"
91*635a8641SAndroid Build Coastguard Worker    args = [ str, "--value=$str" ]
92*635a8641SAndroid Build Coastguard Worker  Will yield the command line:
93*635a8641SAndroid Build Coastguard Worker    asdf --value=asdf
94*635a8641SAndroid Build Coastguard Worker  The unquoted asdf string will not be valid input to this function, which
95*635a8641SAndroid Build Coastguard Worker  accepts only quoted strings like GN scripts. In such cases, you can just use
96*635a8641SAndroid Build Coastguard Worker  the Python string literal directly.
97*635a8641SAndroid Build Coastguard Worker
98*635a8641SAndroid Build Coastguard Worker  The main use cases for this is for other types, in particular lists. When
99*635a8641SAndroid Build Coastguard Worker  using string interpolation on a list (as in the top example) the embedded
100*635a8641SAndroid Build Coastguard Worker  strings will be quoted and escaped according to GN rules so the list can be
101*635a8641SAndroid Build Coastguard Worker  re-parsed to get the same result."""
102*635a8641SAndroid Build Coastguard Worker  parser = GNValueParser(input_string)
103*635a8641SAndroid Build Coastguard Worker  return parser.Parse()
104*635a8641SAndroid Build Coastguard Worker
105*635a8641SAndroid Build Coastguard Worker
106*635a8641SAndroid Build Coastguard Workerdef FromGNArgs(input_string):
107*635a8641SAndroid Build Coastguard Worker  """Converts a string with a bunch of gn arg assignments into a Python dict.
108*635a8641SAndroid Build Coastguard Worker
109*635a8641SAndroid Build Coastguard Worker  Given a whitespace-separated list of
110*635a8641SAndroid Build Coastguard Worker
111*635a8641SAndroid Build Coastguard Worker    <ident> = (integer | string | boolean | <list of the former>)
112*635a8641SAndroid Build Coastguard Worker
113*635a8641SAndroid Build Coastguard Worker  gn assignments, this returns a Python dict, i.e.:
114*635a8641SAndroid Build Coastguard Worker
115*635a8641SAndroid Build Coastguard Worker    FromGNArgs("foo=true\nbar=1\n") -> { 'foo': True, 'bar': 1 }.
116*635a8641SAndroid Build Coastguard Worker
117*635a8641SAndroid Build Coastguard Worker  Only simple types and lists supported; variables, structs, calls
118*635a8641SAndroid Build Coastguard Worker  and other, more complicated things are not.
119*635a8641SAndroid Build Coastguard Worker
120*635a8641SAndroid Build Coastguard Worker  This routine is meant to handle only the simple sorts of values that
121*635a8641SAndroid Build Coastguard Worker  arise in parsing --args.
122*635a8641SAndroid Build Coastguard Worker  """
123*635a8641SAndroid Build Coastguard Worker  parser = GNValueParser(input_string)
124*635a8641SAndroid Build Coastguard Worker  return parser.ParseArgs()
125*635a8641SAndroid Build Coastguard Worker
126*635a8641SAndroid Build Coastguard Worker
127*635a8641SAndroid Build Coastguard Workerdef UnescapeGNString(value):
128*635a8641SAndroid Build Coastguard Worker  """Given a string with GN escaping, returns the unescaped string.
129*635a8641SAndroid Build Coastguard Worker
130*635a8641SAndroid Build Coastguard Worker  Be careful not to feed with input from a Python parsing function like
131*635a8641SAndroid Build Coastguard Worker  'ast' because it will do Python unescaping, which will be incorrect when
132*635a8641SAndroid Build Coastguard Worker  fed into the GN unescaper."""
133*635a8641SAndroid Build Coastguard Worker  result = ''
134*635a8641SAndroid Build Coastguard Worker  i = 0
135*635a8641SAndroid Build Coastguard Worker  while i < len(value):
136*635a8641SAndroid Build Coastguard Worker    if value[i] == '\\':
137*635a8641SAndroid Build Coastguard Worker      if i < len(value) - 1:
138*635a8641SAndroid Build Coastguard Worker        next_char = value[i + 1]
139*635a8641SAndroid Build Coastguard Worker        if next_char in ('$', '"', '\\'):
140*635a8641SAndroid Build Coastguard Worker          # These are the escaped characters GN supports.
141*635a8641SAndroid Build Coastguard Worker          result += next_char
142*635a8641SAndroid Build Coastguard Worker          i += 1
143*635a8641SAndroid Build Coastguard Worker        else:
144*635a8641SAndroid Build Coastguard Worker          # Any other backslash is a literal.
145*635a8641SAndroid Build Coastguard Worker          result += '\\'
146*635a8641SAndroid Build Coastguard Worker    else:
147*635a8641SAndroid Build Coastguard Worker      result += value[i]
148*635a8641SAndroid Build Coastguard Worker    i += 1
149*635a8641SAndroid Build Coastguard Worker  return result
150*635a8641SAndroid Build Coastguard Worker
151*635a8641SAndroid Build Coastguard Worker
152*635a8641SAndroid Build Coastguard Workerdef _IsDigitOrMinus(char):
153*635a8641SAndroid Build Coastguard Worker  return char in "-0123456789"
154*635a8641SAndroid Build Coastguard Worker
155*635a8641SAndroid Build Coastguard Worker
156*635a8641SAndroid Build Coastguard Workerclass GNValueParser(object):
157*635a8641SAndroid Build Coastguard Worker  """Duplicates GN parsing of values and converts to Python types.
158*635a8641SAndroid Build Coastguard Worker
159*635a8641SAndroid Build Coastguard Worker  Normally you would use the wrapper function FromGNValue() below.
160*635a8641SAndroid Build Coastguard Worker
161*635a8641SAndroid Build Coastguard Worker  If you expect input as a specific type, you can also call one of the Parse*
162*635a8641SAndroid Build Coastguard Worker  functions directly. All functions throw GNException on invalid input. """
163*635a8641SAndroid Build Coastguard Worker  def __init__(self, string):
164*635a8641SAndroid Build Coastguard Worker    self.input = string
165*635a8641SAndroid Build Coastguard Worker    self.cur = 0
166*635a8641SAndroid Build Coastguard Worker
167*635a8641SAndroid Build Coastguard Worker  def IsDone(self):
168*635a8641SAndroid Build Coastguard Worker    return self.cur == len(self.input)
169*635a8641SAndroid Build Coastguard Worker
170*635a8641SAndroid Build Coastguard Worker  def ConsumeWhitespace(self):
171*635a8641SAndroid Build Coastguard Worker    while not self.IsDone() and self.input[self.cur] in ' \t\n':
172*635a8641SAndroid Build Coastguard Worker      self.cur += 1
173*635a8641SAndroid Build Coastguard Worker
174*635a8641SAndroid Build Coastguard Worker  def Parse(self):
175*635a8641SAndroid Build Coastguard Worker    """Converts a string representing a printed GN value to the Python type.
176*635a8641SAndroid Build Coastguard Worker
177*635a8641SAndroid Build Coastguard Worker    See additional usage notes on FromGNString above.
178*635a8641SAndroid Build Coastguard Worker
179*635a8641SAndroid Build Coastguard Worker    - GN booleans ('true', 'false') will be converted to Python booleans.
180*635a8641SAndroid Build Coastguard Worker
181*635a8641SAndroid Build Coastguard Worker    - GN numbers ('123') will be converted to Python numbers.
182*635a8641SAndroid Build Coastguard Worker
183*635a8641SAndroid Build Coastguard Worker    - GN strings (double-quoted as in '"asdf"') will be converted to Python
184*635a8641SAndroid Build Coastguard Worker      strings with GN escaping rules. GN string interpolation (embedded
185*635a8641SAndroid Build Coastguard Worker      variables preceded by $) are not supported and will be returned as
186*635a8641SAndroid Build Coastguard Worker      literals.
187*635a8641SAndroid Build Coastguard Worker
188*635a8641SAndroid Build Coastguard Worker    - GN lists ('[1, "asdf", 3]') will be converted to Python lists.
189*635a8641SAndroid Build Coastguard Worker
190*635a8641SAndroid Build Coastguard Worker    - GN scopes ('{ ... }') are not supported."""
191*635a8641SAndroid Build Coastguard Worker    result = self._ParseAllowTrailing()
192*635a8641SAndroid Build Coastguard Worker    self.ConsumeWhitespace()
193*635a8641SAndroid Build Coastguard Worker    if not self.IsDone():
194*635a8641SAndroid Build Coastguard Worker      raise GNException("Trailing input after parsing:\n  " +
195*635a8641SAndroid Build Coastguard Worker                        self.input[self.cur:])
196*635a8641SAndroid Build Coastguard Worker    return result
197*635a8641SAndroid Build Coastguard Worker
198*635a8641SAndroid Build Coastguard Worker  def ParseArgs(self):
199*635a8641SAndroid Build Coastguard Worker    """Converts a whitespace-separated list of ident=literals to a dict.
200*635a8641SAndroid Build Coastguard Worker
201*635a8641SAndroid Build Coastguard Worker    See additional usage notes on FromGNArgs, above.
202*635a8641SAndroid Build Coastguard Worker    """
203*635a8641SAndroid Build Coastguard Worker    d = {}
204*635a8641SAndroid Build Coastguard Worker
205*635a8641SAndroid Build Coastguard Worker    self.ConsumeWhitespace()
206*635a8641SAndroid Build Coastguard Worker    while not self.IsDone():
207*635a8641SAndroid Build Coastguard Worker      ident = self._ParseIdent()
208*635a8641SAndroid Build Coastguard Worker      self.ConsumeWhitespace()
209*635a8641SAndroid Build Coastguard Worker      if self.input[self.cur] != '=':
210*635a8641SAndroid Build Coastguard Worker        raise GNException("Unexpected token: " + self.input[self.cur:])
211*635a8641SAndroid Build Coastguard Worker      self.cur += 1
212*635a8641SAndroid Build Coastguard Worker      self.ConsumeWhitespace()
213*635a8641SAndroid Build Coastguard Worker      val = self._ParseAllowTrailing()
214*635a8641SAndroid Build Coastguard Worker      self.ConsumeWhitespace()
215*635a8641SAndroid Build Coastguard Worker      d[ident] = val
216*635a8641SAndroid Build Coastguard Worker
217*635a8641SAndroid Build Coastguard Worker    return d
218*635a8641SAndroid Build Coastguard Worker
219*635a8641SAndroid Build Coastguard Worker  def _ParseAllowTrailing(self):
220*635a8641SAndroid Build Coastguard Worker    """Internal version of Parse that doesn't check for trailing stuff."""
221*635a8641SAndroid Build Coastguard Worker    self.ConsumeWhitespace()
222*635a8641SAndroid Build Coastguard Worker    if self.IsDone():
223*635a8641SAndroid Build Coastguard Worker      raise GNException("Expected input to parse.")
224*635a8641SAndroid Build Coastguard Worker
225*635a8641SAndroid Build Coastguard Worker    next_char = self.input[self.cur]
226*635a8641SAndroid Build Coastguard Worker    if next_char == '[':
227*635a8641SAndroid Build Coastguard Worker      return self.ParseList()
228*635a8641SAndroid Build Coastguard Worker    elif _IsDigitOrMinus(next_char):
229*635a8641SAndroid Build Coastguard Worker      return self.ParseNumber()
230*635a8641SAndroid Build Coastguard Worker    elif next_char == '"':
231*635a8641SAndroid Build Coastguard Worker      return self.ParseString()
232*635a8641SAndroid Build Coastguard Worker    elif self._ConstantFollows('true'):
233*635a8641SAndroid Build Coastguard Worker      return True
234*635a8641SAndroid Build Coastguard Worker    elif self._ConstantFollows('false'):
235*635a8641SAndroid Build Coastguard Worker      return False
236*635a8641SAndroid Build Coastguard Worker    else:
237*635a8641SAndroid Build Coastguard Worker      raise GNException("Unexpected token: " + self.input[self.cur:])
238*635a8641SAndroid Build Coastguard Worker
239*635a8641SAndroid Build Coastguard Worker  def _ParseIdent(self):
240*635a8641SAndroid Build Coastguard Worker    ident = ''
241*635a8641SAndroid Build Coastguard Worker
242*635a8641SAndroid Build Coastguard Worker    next_char = self.input[self.cur]
243*635a8641SAndroid Build Coastguard Worker    if not next_char.isalpha() and not next_char=='_':
244*635a8641SAndroid Build Coastguard Worker      raise GNException("Expected an identifier: " + self.input[self.cur:])
245*635a8641SAndroid Build Coastguard Worker
246*635a8641SAndroid Build Coastguard Worker    ident += next_char
247*635a8641SAndroid Build Coastguard Worker    self.cur += 1
248*635a8641SAndroid Build Coastguard Worker
249*635a8641SAndroid Build Coastguard Worker    next_char = self.input[self.cur]
250*635a8641SAndroid Build Coastguard Worker    while next_char.isalpha() or next_char.isdigit() or next_char=='_':
251*635a8641SAndroid Build Coastguard Worker      ident += next_char
252*635a8641SAndroid Build Coastguard Worker      self.cur += 1
253*635a8641SAndroid Build Coastguard Worker      next_char = self.input[self.cur]
254*635a8641SAndroid Build Coastguard Worker
255*635a8641SAndroid Build Coastguard Worker    return ident
256*635a8641SAndroid Build Coastguard Worker
257*635a8641SAndroid Build Coastguard Worker  def ParseNumber(self):
258*635a8641SAndroid Build Coastguard Worker    self.ConsumeWhitespace()
259*635a8641SAndroid Build Coastguard Worker    if self.IsDone():
260*635a8641SAndroid Build Coastguard Worker      raise GNException('Expected number but got nothing.')
261*635a8641SAndroid Build Coastguard Worker
262*635a8641SAndroid Build Coastguard Worker    begin = self.cur
263*635a8641SAndroid Build Coastguard Worker
264*635a8641SAndroid Build Coastguard Worker    # The first character can include a negative sign.
265*635a8641SAndroid Build Coastguard Worker    if not self.IsDone() and _IsDigitOrMinus(self.input[self.cur]):
266*635a8641SAndroid Build Coastguard Worker      self.cur += 1
267*635a8641SAndroid Build Coastguard Worker    while not self.IsDone() and self.input[self.cur].isdigit():
268*635a8641SAndroid Build Coastguard Worker      self.cur += 1
269*635a8641SAndroid Build Coastguard Worker
270*635a8641SAndroid Build Coastguard Worker    number_string = self.input[begin:self.cur]
271*635a8641SAndroid Build Coastguard Worker    if not len(number_string) or number_string == '-':
272*635a8641SAndroid Build Coastguard Worker      raise GNException("Not a valid number.")
273*635a8641SAndroid Build Coastguard Worker    return int(number_string)
274*635a8641SAndroid Build Coastguard Worker
275*635a8641SAndroid Build Coastguard Worker  def ParseString(self):
276*635a8641SAndroid Build Coastguard Worker    self.ConsumeWhitespace()
277*635a8641SAndroid Build Coastguard Worker    if self.IsDone():
278*635a8641SAndroid Build Coastguard Worker      raise GNException('Expected string but got nothing.')
279*635a8641SAndroid Build Coastguard Worker
280*635a8641SAndroid Build Coastguard Worker    if self.input[self.cur] != '"':
281*635a8641SAndroid Build Coastguard Worker      raise GNException('Expected string beginning in a " but got:\n  ' +
282*635a8641SAndroid Build Coastguard Worker                        self.input[self.cur:])
283*635a8641SAndroid Build Coastguard Worker    self.cur += 1  # Skip over quote.
284*635a8641SAndroid Build Coastguard Worker
285*635a8641SAndroid Build Coastguard Worker    begin = self.cur
286*635a8641SAndroid Build Coastguard Worker    while not self.IsDone() and self.input[self.cur] != '"':
287*635a8641SAndroid Build Coastguard Worker      if self.input[self.cur] == '\\':
288*635a8641SAndroid Build Coastguard Worker        self.cur += 1  # Skip over the backslash.
289*635a8641SAndroid Build Coastguard Worker        if self.IsDone():
290*635a8641SAndroid Build Coastguard Worker          raise GNException("String ends in a backslash in:\n  " +
291*635a8641SAndroid Build Coastguard Worker                            self.input)
292*635a8641SAndroid Build Coastguard Worker      self.cur += 1
293*635a8641SAndroid Build Coastguard Worker
294*635a8641SAndroid Build Coastguard Worker    if self.IsDone():
295*635a8641SAndroid Build Coastguard Worker      raise GNException('Unterminated string:\n  ' + self.input[begin:])
296*635a8641SAndroid Build Coastguard Worker
297*635a8641SAndroid Build Coastguard Worker    end = self.cur
298*635a8641SAndroid Build Coastguard Worker    self.cur += 1  # Consume trailing ".
299*635a8641SAndroid Build Coastguard Worker
300*635a8641SAndroid Build Coastguard Worker    return UnescapeGNString(self.input[begin:end])
301*635a8641SAndroid Build Coastguard Worker
302*635a8641SAndroid Build Coastguard Worker  def ParseList(self):
303*635a8641SAndroid Build Coastguard Worker    self.ConsumeWhitespace()
304*635a8641SAndroid Build Coastguard Worker    if self.IsDone():
305*635a8641SAndroid Build Coastguard Worker      raise GNException('Expected list but got nothing.')
306*635a8641SAndroid Build Coastguard Worker
307*635a8641SAndroid Build Coastguard Worker    # Skip over opening '['.
308*635a8641SAndroid Build Coastguard Worker    if self.input[self.cur] != '[':
309*635a8641SAndroid Build Coastguard Worker      raise GNException("Expected [ for list but got:\n  " +
310*635a8641SAndroid Build Coastguard Worker                        self.input[self.cur:])
311*635a8641SAndroid Build Coastguard Worker    self.cur += 1
312*635a8641SAndroid Build Coastguard Worker    self.ConsumeWhitespace()
313*635a8641SAndroid Build Coastguard Worker    if self.IsDone():
314*635a8641SAndroid Build Coastguard Worker      raise GNException("Unterminated list:\n  " + self.input)
315*635a8641SAndroid Build Coastguard Worker
316*635a8641SAndroid Build Coastguard Worker    list_result = []
317*635a8641SAndroid Build Coastguard Worker    previous_had_trailing_comma = True
318*635a8641SAndroid Build Coastguard Worker    while not self.IsDone():
319*635a8641SAndroid Build Coastguard Worker      if self.input[self.cur] == ']':
320*635a8641SAndroid Build Coastguard Worker        self.cur += 1  # Skip over ']'.
321*635a8641SAndroid Build Coastguard Worker        return list_result
322*635a8641SAndroid Build Coastguard Worker
323*635a8641SAndroid Build Coastguard Worker      if not previous_had_trailing_comma:
324*635a8641SAndroid Build Coastguard Worker        raise GNException("List items not separated by comma.")
325*635a8641SAndroid Build Coastguard Worker
326*635a8641SAndroid Build Coastguard Worker      list_result += [ self._ParseAllowTrailing() ]
327*635a8641SAndroid Build Coastguard Worker      self.ConsumeWhitespace()
328*635a8641SAndroid Build Coastguard Worker      if self.IsDone():
329*635a8641SAndroid Build Coastguard Worker        break
330*635a8641SAndroid Build Coastguard Worker
331*635a8641SAndroid Build Coastguard Worker      # Consume comma if there is one.
332*635a8641SAndroid Build Coastguard Worker      previous_had_trailing_comma = self.input[self.cur] == ','
333*635a8641SAndroid Build Coastguard Worker      if previous_had_trailing_comma:
334*635a8641SAndroid Build Coastguard Worker        # Consume comma.
335*635a8641SAndroid Build Coastguard Worker        self.cur += 1
336*635a8641SAndroid Build Coastguard Worker        self.ConsumeWhitespace()
337*635a8641SAndroid Build Coastguard Worker
338*635a8641SAndroid Build Coastguard Worker    raise GNException("Unterminated list:\n  " + self.input)
339*635a8641SAndroid Build Coastguard Worker
340*635a8641SAndroid Build Coastguard Worker  def _ConstantFollows(self, constant):
341*635a8641SAndroid Build Coastguard Worker    """Returns true if the given constant follows immediately at the current
342*635a8641SAndroid Build Coastguard Worker    location in the input. If it does, the text is consumed and the function
343*635a8641SAndroid Build Coastguard Worker    returns true. Otherwise, returns false and the current position is
344*635a8641SAndroid Build Coastguard Worker    unchanged."""
345*635a8641SAndroid Build Coastguard Worker    end = self.cur + len(constant)
346*635a8641SAndroid Build Coastguard Worker    if end > len(self.input):
347*635a8641SAndroid Build Coastguard Worker      return False  # Not enough room.
348*635a8641SAndroid Build Coastguard Worker    if self.input[self.cur:end] == constant:
349*635a8641SAndroid Build Coastguard Worker      self.cur = end
350*635a8641SAndroid Build Coastguard Worker      return True
351*635a8641SAndroid Build Coastguard Worker    return False
352