summaryrefslogtreecommitdiffstats
path: root/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py')
-rw-r--r--chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py323
1 files changed, 323 insertions, 0 deletions
diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
new file mode 100644
index 00000000000..da441e3806c
--- /dev/null
+++ b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/parser.py
@@ -0,0 +1,323 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Generates a syntax tree from a Mojo IDL file."""
+
+import imp
+import os.path
+import sys
+
+# Disable lint check for finding modules:
+# pylint: disable=F0401
+
+def _GetDirAbove(dirname):
+ """Returns the directory "above" this file containing |dirname| (which must
+ also be "above" this file)."""
+ path = os.path.abspath(__file__)
+ while True:
+ path, tail = os.path.split(path)
+ assert tail
+ if tail == dirname:
+ return path
+
+try:
+ imp.find_module("ply")
+except ImportError:
+ sys.path.append(os.path.join(_GetDirAbove("mojo"), "third_party"))
+from ply import lex
+from ply import yacc
+
+from ..error import Error
+import ast
+from lexer import Lexer
+
+
+_MAX_ORDINAL_VALUE = 0xffffffff
+
+
+def _ListFromConcat(*items):
+ """Generate list by concatenating inputs (note: only concatenates lists, not
+ tuples or other iterables)."""
+ itemsout = []
+ for item in items:
+ if item is None:
+ continue
+ if type(item) is not type([]):
+ itemsout.append(item)
+ else:
+ itemsout.extend(item)
+ return itemsout
+
+
+# Disable lint check for exceptions deriving from Exception:
+# pylint: disable=W0710
+class ParseError(Error):
+ """Class for errors from the parser."""
+
+ def __init__(self, filename, message, lineno=None, snippet=None):
+ Error.__init__(self, filename, message, lineno=lineno,
+ addenda=([snippet] if snippet else None))
+
+
+# We have methods which look like they could be functions:
+# pylint: disable=R0201
+class Parser(object):
+
+ def __init__(self, lexer, source, filename):
+ self.tokens = lexer.tokens
+ self.source = source
+ self.filename = filename
+
+ def p_root(self, p):
+ """root : import root
+ | module
+ | definitions"""
+ if len(p) > 2:
+ p[0] = _ListFromConcat(p[1], p[2])
+ else:
+ # Generator expects a module. If one wasn't specified insert one with an
+ # empty name.
+ if p[1][0] != 'MODULE':
+ p[0] = [('MODULE', '', None, p[1])]
+ else:
+ p[0] = [p[1]]
+
+ def p_import(self, p):
+ """import : IMPORT STRING_LITERAL"""
+ # 'eval' the literal to strip the quotes.
+ p[0] = ('IMPORT', eval(p[2]))
+
+ def p_module(self, p):
+ """module : attribute_section MODULE identifier LBRACE definitions RBRACE"""
+ p[0] = ('MODULE', p[3], p[1], p[5])
+
+ def p_definitions(self, p):
+ """definitions : definition definitions
+ | """
+ if len(p) > 1:
+ p[0] = _ListFromConcat(p[1], p[2])
+
+ def p_definition(self, p):
+ """definition : struct
+ | interface
+ | enum
+ | const"""
+ p[0] = p[1]
+
+ def p_attribute_section(self, p):
+ """attribute_section : LBRACKET attributes RBRACKET
+ | """
+ if len(p) > 3:
+ p[0] = p[2]
+
+ def p_attributes(self, p):
+ """attributes : attribute
+ | attribute COMMA attributes
+ | """
+ if len(p) == 2:
+ p[0] = _ListFromConcat(p[1])
+ elif len(p) > 3:
+ p[0] = _ListFromConcat(p[1], p[3])
+
+ def p_attribute(self, p):
+ """attribute : NAME EQUALS evaled_literal
+ | NAME EQUALS NAME"""
+ p[0] = ('ATTRIBUTE', p[1], p[3])
+
+ def p_evaled_literal(self, p):
+ """evaled_literal : literal"""
+ # 'eval' the literal to strip the quotes.
+ p[0] = eval(p[1])
+
+ def p_struct(self, p):
+ """struct : attribute_section STRUCT NAME LBRACE struct_body RBRACE SEMI"""
+ p[0] = ('STRUCT', p[3], p[1], p[5])
+
+ def p_struct_body(self, p):
+ """struct_body : field struct_body
+ | enum struct_body
+ | const struct_body
+ | """
+ if len(p) > 1:
+ p[0] = _ListFromConcat(p[1], p[2])
+
+ def p_field(self, p):
+ """field : typename NAME ordinal default SEMI"""
+ p[0] = ('FIELD', p[1], p[2], p[3], p[4])
+
+ def p_default(self, p):
+ """default : EQUALS constant
+ | """
+ if len(p) > 2:
+ p[0] = p[2]
+
+ def p_interface(self, p):
+ """interface : attribute_section INTERFACE NAME LBRACE interface_body \
+ RBRACE SEMI"""
+ p[0] = ('INTERFACE', p[3], p[1], p[5])
+
+ def p_interface_body(self, p):
+ """interface_body : method interface_body
+ | enum interface_body
+ | const interface_body
+ | """
+ if len(p) > 1:
+ p[0] = _ListFromConcat(p[1], p[2])
+
+ def p_response(self, p):
+ """response : RESPONSE LPAREN parameters RPAREN
+ | """
+ if len(p) > 3:
+ p[0] = p[3]
+
+ def p_method(self, p):
+ """method : NAME ordinal LPAREN parameters RPAREN response SEMI"""
+ p[0] = ('METHOD', p[1], p[4], p[2], p[6])
+
+ def p_parameters(self, p):
+ """parameters : parameter
+ | parameter COMMA parameters
+ | """
+ if len(p) == 1:
+ p[0] = []
+ elif len(p) == 2:
+ p[0] = _ListFromConcat(p[1])
+ elif len(p) > 3:
+ p[0] = _ListFromConcat(p[1], p[3])
+
+ def p_parameter(self, p):
+ """parameter : typename NAME ordinal"""
+ p[0] = ('PARAM', p[1], p[2], p[3])
+
+ def p_typename(self, p):
+ """typename : basictypename
+ | array
+ | interfacerequest"""
+ p[0] = p[1]
+
+ def p_basictypename(self, p):
+ """basictypename : identifier
+ | handletype"""
+ p[0] = p[1]
+
+ def p_handletype(self, p):
+ """handletype : HANDLE
+ | HANDLE LANGLE NAME RANGLE"""
+ if len(p) == 2:
+ p[0] = p[1]
+ else:
+ if p[3] not in ('data_pipe_consumer',
+ 'data_pipe_producer',
+ 'message_pipe',
+ 'shared_buffer'):
+ # Note: We don't enable tracking of line numbers for everything, so we
+ # can't use |p.lineno(3)|.
+ raise ParseError(self.filename, "Invalid handle type %r:" % p[3],
+ lineno=p.lineno(1),
+ snippet=self._GetSnippet(p.lineno(1)))
+ p[0] = "handle<" + p[3] + ">"
+
+ def p_array(self, p):
+ """array : typename LBRACKET RBRACKET"""
+ p[0] = p[1] + "[]"
+
+ def p_interfacerequest(self, p):
+ """interfacerequest : identifier AMP"""
+ p[0] = p[1] + "&"
+
+ def p_ordinal(self, p):
+ """ordinal : ORDINAL
+ | """
+ if len(p) > 1:
+ value = int(p[1][1:])
+ if value > _MAX_ORDINAL_VALUE:
+ raise ParseError(self.filename, "Ordinal value %d too large:" % value,
+ lineno=p.lineno(1),
+ snippet=self._GetSnippet(p.lineno(1)))
+ p[0] = ast.Ordinal(value, filename=self.filename, lineno=p.lineno(1))
+ else:
+ p[0] = ast.Ordinal(None)
+
+ def p_enum(self, p):
+ """enum : ENUM NAME LBRACE enum_fields RBRACE SEMI"""
+ p[0] = ('ENUM', p[2], p[4])
+
+ def p_enum_fields(self, p):
+ """enum_fields : enum_field
+ | enum_field COMMA enum_fields
+ | """
+ if len(p) == 2:
+ p[0] = _ListFromConcat(p[1])
+ elif len(p) > 3:
+ p[0] = _ListFromConcat(p[1], p[3])
+
+ def p_enum_field(self, p):
+ """enum_field : NAME
+ | NAME EQUALS constant"""
+ if len(p) == 2:
+ p[0] = ('ENUM_FIELD', p[1], None)
+ else:
+ p[0] = ('ENUM_FIELD', p[1], p[3])
+
+ def p_const(self, p):
+ """const : CONST typename NAME EQUALS constant SEMI"""
+ p[0] = ('CONST', p[2], p[3], p[5])
+
+ def p_constant(self, p):
+ """constant : literal
+ | identifier_wrapped"""
+ p[0] = p[1]
+
+ def p_identifier_wrapped(self, p):
+ """identifier_wrapped : identifier"""
+ p[0] = ('IDENTIFIER', p[1])
+
+ def p_identifier(self, p):
+ """identifier : NAME
+ | NAME DOT identifier"""
+ p[0] = ''.join(p[1:])
+
+ def p_literal(self, p):
+ """literal : number
+ | CHAR_CONST
+ | TRUE
+ | FALSE
+ | DEFAULT
+ | STRING_LITERAL"""
+ p[0] = p[1]
+
+ def p_number(self, p):
+ """number : digits
+ | PLUS digits
+ | MINUS digits"""
+ p[0] = ''.join(p[1:])
+
+ def p_digits(self, p):
+ """digits : INT_CONST_DEC
+ | INT_CONST_HEX
+ | FLOAT_CONST"""
+ p[0] = p[1]
+
+ def p_error(self, e):
+ if e is None:
+ # Unexpected EOF.
+ # TODO(vtl): Can we figure out what's missing?
+ raise ParseError(self.filename, "Unexpected end of file")
+
+ raise ParseError(self.filename, "Unexpected %r:" % e.value, lineno=e.lineno,
+ snippet=self._GetSnippet(e.lineno))
+
+ def _GetSnippet(self, lineno):
+ return self.source.split('\n')[lineno - 1]
+
+
+def Parse(source, filename):
+ lexer = Lexer(filename)
+ parser = Parser(lexer, source, filename)
+
+ lex.lex(object=lexer)
+ yacc.yacc(module=parser, debug=0, write_tables=0)
+
+ tree = yacc.parse(source)
+ return tree