195640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org#!/usr/bin/env python
295640e3a20adea634b4df4ccf8c93f411184c438joi@chromium.org# Copyright (c) 2012 The Chromium Authors. All rights reserved.
301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# Use of this source code is governed by a BSD-style license that can be
401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org# found in the LICENSE file.
501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''Class for reading GRD files into memory, without processing them.
701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org'''
801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.orgimport os.path
1001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport types
1101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport xml.sax
1201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgimport xml.sax.handler
1301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit import exception
15b9161407f737461b5db16a29782f8a31d19e602dbenrg@chromium.orgfrom grit import util
1601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.node import base
1701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.node import mapping
1801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgfrom grit.node import misc
1901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass StopParsingException(Exception):
2201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''An exception used to stop parsing.'''
2301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  pass
2401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgclass GrdContentHandler(xml.sax.handler.ContentHandler):
27976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org  def __init__(self, stop_after, debug, dir, defines, tags_to_ignore,
28976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org               target_platform):
2901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Invariant of data:
3001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # 'root' is the root of the parse tree being created, or None if we haven't
3101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # parsed out any elements.
3201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # 'stack' is the a stack of elements that we push new nodes onto and
3301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # pop from when they finish parsing, or [] if we are not currently parsing.
3401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # 'stack[-1]' is the top of the stack.
3501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.root = None
3601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.stack = []
3701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.stop_after = stop_after
3801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.debug = debug
3983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    self.dir = dir
4001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.defines = defines
4101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.tags_to_ignore = tags_to_ignore or set()
4201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.ignore_depth = 0
43976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org    self.target_platform = target_platform
4401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
4501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def startElement(self, name, attrs):
4683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    if self.ignore_depth or name in self.tags_to_ignore:
4783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      if self.debug and self.ignore_depth == 0:
4801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        print "Ignoring element %s and its children" % name
4983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      self.ignore_depth += 1
5001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      return
5101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
5201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.debug:
5383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      attr_list = ' '.join('%s="%s"' % kv for kv in attrs.items())
5401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      print ("Starting parsing of element %s with attributes %r" %
5583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org          (name, attr_list or '(none)'))
5601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
5783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    typeattr = attrs.get('type')
5801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    node = mapping.ElementToClass(name, typeattr)()
5901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
6083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    if self.stack:
6101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.stack[-1].AddChild(node)
6201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      node.StartParsing(name, self.stack[-1])
6301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    else:
6483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      assert self.root is None
6583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      self.root = node
66976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org      if isinstance(self.root, misc.GritNode):
67976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org        if self.target_platform:
68976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org          self.root.SetTargetPlatform(self.target_platform)
6901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      node.StartParsing(name, None)
7083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      if self.defines:
7183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org        node.SetDefines(self.defines)
7201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    self.stack.append(node)
7301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    for attr, attrval in attrs.items():
7583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      node.HandleAttribute(attr, attrval)
7601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
7701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def endElement(self, name):
7883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    if self.ignore_depth:
7901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      self.ignore_depth -= 1
8083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      return
8183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
8283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    if name == 'part':
8383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      partnode = self.stack[-1]
8483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      partnode.started_inclusion = True
8583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      # Add the contents of the sub-grd file as children of the <part> node.
8683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      partname = partnode.GetInputPath()
8783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      if os.path.dirname(partname):
8883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org        # TODO(benrg): Remove this limitation. (The problem is that GRIT
8983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org        # assumes that files referenced from the GRD file are relative to
9083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org        # a path stored in the root <grit> node.)
9183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org        raise exception.GotPathExpectedFilenameOnly()
9283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      partname = os.path.join(self.dir, partname)
9383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      # Exceptions propagate to the handler in grd_reader.Parse().
9483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      xml.sax.parse(partname, GrdPartContentHandler(self))
9583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
9683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    if self.debug:
9783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      print "End parsing of element %s" % name
9883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    self.stack.pop().EndParsing()
9983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
10083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    if name == self.stop_after:
10183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      raise StopParsingException()
10201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
10301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def characters(self, content):
10401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if self.ignore_depth == 0:
10501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      if self.stack[-1]:
10601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org        self.stack[-1].AppendContent(content)
10701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
10801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  def ignorableWhitespace(self, whitespace):
10901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # TODO(joi)  This is not supported by expat.  Should use a different XML parser?
11001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    pass
11101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
11201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
11383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.orgclass GrdPartContentHandler(xml.sax.handler.ContentHandler):
11483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  def __init__(self, parent):
11583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    self.parent = parent
11683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    self.depth = 0
11783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
11883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  def startElement(self, name, attrs):
11983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    if self.depth:
12083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      self.parent.startElement(name, attrs)
12183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    else:
12283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      if name != 'grit-part':
12383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org        raise exception.MissingElement("root tag must be <grit-part>")
12483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      if attrs:
12583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org        raise exception.UnexpectedAttribute(
12683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org            "<grit-part> tag must not have attributes")
12783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    self.depth += 1
12883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
12983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  def endElement(self, name):
13083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    self.depth -= 1
13183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    if self.depth:
13283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org      self.parent.endElement(name)
13383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
13483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  def characters(self, content):
13583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    self.parent.characters(content)
13683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
13783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  def ignorableWhitespace(self, whitespace):
13883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    self.parent.ignorableWhitespace(whitespace)
13983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
14083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
141ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.orgdef Parse(filename_or_stream, dir=None, stop_after=None, first_ids_file=None,
142abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org          debug=False, defines=None, tags_to_ignore=None, target_platform=None):
14301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''Parses a GRD file into a tree of nodes (from grit.node).
14401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
14583717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  If filename_or_stream is a stream, 'dir' should point to the directory
14683717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  notionally containing the stream (this feature is only used in unit tests).
14701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
14801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  If 'stop_after' is provided, the parsing will stop once the first node
14901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  with this name has been fully parsed (including all its contents).
15001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
15101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  If 'debug' is true, lots of information about the parsing events will be
15201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  printed out during parsing of the file.
15301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
1543a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org  If 'first_ids_file' is non-empty, it is used to override the setting for the
1553a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org  first_ids_file attribute of the <grit> root node. Note that the first_ids_file
1563a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org  parameter should be relative to the cwd, even though the first_ids_file
1573a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org  attribute of the <grit> node is relative to the grd file.
15801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
159abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org  If 'target_platform' is set, this is used to determine the target
160abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org  platform of builds, instead of using |sys.platform|.
161abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org
16201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  Args:
16383717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    filename_or_stream: './bla.xml'
16483717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    dir: None (if filename_or_stream is a filename) or '.'
16501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    stop_after: 'inputs'
1660dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    first_ids_file: 'GRIT_DIR/../gritsettings/resource_ids'
16701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    debug: False
16801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    defines: dictionary of defines, like {'chromeos': '1'}
169abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org    target_platform: None or the value that would be returned by sys.platform
170abf28c9e0d607e293a9d790300090e557be6b41ejoi@chromium.org        on your target platform.
17101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
17201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  Return:
17301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    Subclass of grit.node.base.Node
17401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
17501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  Throws:
17601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    grit.exception.Parsing
17701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  '''
17883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
17983717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  if dir is None and isinstance(filename_or_stream, types.StringType):
18083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    dir = util.dirname(filename_or_stream)
18183717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
18283717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  handler = GrdContentHandler(stop_after=stop_after, debug=debug, dir=dir,
183976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org                              defines=defines, tags_to_ignore=tags_to_ignore,
184976a48583e8fb65888e9f660125231cee30eb04bjoi@chromium.org                              target_platform=target_platform)
18501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  try:
18601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    xml.sax.parse(filename_or_stream, handler)
18701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  except StopParsingException:
18801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    assert stop_after
18901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    pass
19001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  except:
19101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    if not debug:
19201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org      print "parse exception: run GRIT with the -x flag to debug .grd problems"
19301b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    raise
19401b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
195ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org  if handler.root.name != 'grit':
196ccda47032903a6550dac2921f88c51b4da55aa36benrg@chromium.org    raise exception.MissingElement("root tag must be <grit>")
19783717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org
19883717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org  if hasattr(handler.root, 'SetOwnDir'):
19901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    # Fix up the base_dir so it is relative to the input file.
20083717e82a9b5e0c629ff4f1078d50503ffd2ae75benrg@chromium.org    assert dir is not None
20101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org    handler.root.SetOwnDir(dir)
20201b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
2030dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org  if isinstance(handler.root, misc.GritNode):
2040dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    if first_ids_file:
2053a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org      # Make the path to the first_ids_file relative to the grd file,
2063a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org      # unless it begins with GRIT_DIR.
2073a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org      GRIT_DIR_PREFIX = 'GRIT_DIR'
2083a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org      if not (first_ids_file.startswith(GRIT_DIR_PREFIX)
2093a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org          and first_ids_file[len(GRIT_DIR_PREFIX)] in ['/', '\\']):
2103a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org        rel_dir = os.path.relpath(os.getcwd(), dir)
2113a274382e822cce4cba6284438fd394fd0f30d14newt@chromium.org        first_ids_file = util.normpath(os.path.join(rel_dir, first_ids_file))
2120dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org      handler.root.attrs['first_ids_file'] = first_ids_file
2130dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    # Assign first ids to the nodes that don't have them.
2140dc1eef9854b5a4102aee7b7acae978eec2e4df9joi@chromium.org    handler.root.AssignFirstIds(filename_or_stream, defines)
21501b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21601b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  return handler.root
21701b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21801b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org
21901b3bc768461bd303bff39f8cd1663682254e407joi@chromium.orgif __name__ == '__main__':
22001b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  util.ChangeStdoutEncoding()
22101b3bc768461bd303bff39f8cd1663682254e407joi@chromium.org  print unicode(Parse(sys.argv[1]))
222