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