Home | History | Annotate | Download | only in parse
      1 #!/usr/bin/env python
      2 # Copyright 2013 The Chromium Authors. All rights reserved.
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Generates a syntax tree from a Mojo IDL file."""
      7 
      8 
      9 import sys
     10 import os.path
     11 
     12 
     13 # Try to load the ply module, if not, then assume it is in the third_party
     14 # directory.
     15 try:
     16   # Disable lint check which fails to find the ply module.
     17   # pylint: disable=F0401
     18   from ply import lex
     19   from ply import yacc
     20 except ImportError:
     21   module_path, module_name = os.path.split(__file__)
     22   third_party = os.path.join(
     23       module_path, os.pardir, os.pardir, os.pardir, os.pardir, 'third_party')
     24   sys.path.append(third_party)
     25   # pylint: disable=F0401
     26   from ply import lex
     27   from ply import yacc
     28 
     29 
     30 def ListFromConcat(*items):
     31   """Generate list by concatenating inputs"""
     32   itemsout = []
     33   for item in items:
     34     if item is None:
     35       continue
     36     if type(item) is not type([]):
     37       itemsout.append(item)
     38     else:
     39       itemsout.extend(item)
     40 
     41   return itemsout
     42 
     43 
     44 class Lexer(object):
     45 
     46   # This field is required by lex to specify the complete list of valid tokens.
     47   tokens = (
     48     'NAME',
     49     'NUMBER',
     50 
     51     'ARRAY',
     52     'ORDINAL',
     53 
     54     'MSGPIPE',
     55     'MSGPIPEARRAY',
     56 
     57     'MODULE',
     58     'STRUCT',
     59     'INTERFACE',
     60     'ENUM',
     61     'VOID',
     62 
     63     'LCURLY',
     64     'RCURLY',
     65     'LPAREN',
     66     'RPAREN',
     67     'LBRACKET',
     68     'RBRACKET',
     69     'COMMA',
     70     'SEMICOLON',
     71     'EQUALS',
     72   )
     73 
     74   t_LCURLY     = r'{'
     75   t_RCURLY     = r'}'
     76   t_LPAREN     = r'\('
     77   t_RPAREN     = r'\)'
     78   t_LBRACKET   = r'\['
     79   t_RBRACKET   = r'\]'
     80   t_COMMA      = r','
     81   t_SEMICOLON  = r';'
     82   t_EQUALS     = r'='
     83   t_NAME       = r'[a-zA-Z_][a-zA-Z0-9_]*'
     84   t_ARRAY      = r'[a-zA-Z_][a-zA-Z0-9_]*\[\]'
     85   t_NUMBER     = r'\d+'
     86   t_ORDINAL    = r'@[0-9]*'
     87 
     88   def t_MSGPIPE(self, t):
     89     r'handle<message_pipe>'
     90     return t
     91 
     92   def t_MSGPIPEARRAY(self, t):
     93     r'handle<message_pipe>\[\]'
     94     return t
     95 
     96   def t_MODULE(self, t):
     97     r'module'
     98     return t
     99 
    100   def t_STRUCT(self, t):
    101     r'struct'
    102     return t
    103 
    104   def t_INTERFACE(self, t):
    105     r'interface'
    106     return t
    107 
    108   def t_ENUM(self, t):
    109     r'enum'
    110     return t
    111 
    112   def t_VOID(self, t):
    113     r'void'
    114     return t
    115 
    116   # Ignore C and C++ style comments
    117   def t_COMMENT(self, t):
    118     r'(/\*(.|\n)*?\*/)|(//.*(\n[ \t]*//.*)*)'
    119     pass
    120 
    121   # Ignored characters
    122   t_ignore = " \t"
    123 
    124   def t_newline(self, t):
    125     r'\n+'
    126     t.lexer.lineno += t.value.count("\n")
    127 
    128   def t_error(self, t):
    129     print("Illegal character '%s'" % t.value[0])
    130     t.lexer.skip(1)
    131 
    132 
    133 class Parser(object):
    134 
    135   def __init__(self, lexer):
    136     self.tokens = lexer.tokens
    137 
    138   def p_module(self, p):
    139     """module : MODULE NAME LCURLY definitions RCURLY"""
    140     p[0] = ('MODULE', p[2], p[4])
    141 
    142   def p_definitions(self, p):
    143     """definitions : definition definitions
    144                    |"""
    145     if len(p) > 1:
    146       p[0] = ListFromConcat(p[1], p[2])
    147 
    148   def p_definition(self, p):
    149     """definition : struct
    150                   | interface
    151                   | enum"""
    152     p[0] = p[1]
    153 
    154   def p_attribute_section(self, p):
    155     """attribute_section : LBRACKET attributes RBRACKET
    156                          | """
    157     if len(p) > 3:
    158       p[0] = p[2]
    159 
    160   def p_attributes(self, p):
    161     """attributes : attribute
    162                   | attribute COMMA attributes
    163                   | """
    164     if len(p) == 2:
    165       p[0] = ListFromConcat(p[1])
    166     elif len(p) > 3:
    167       p[0] = ListFromConcat(p[1], p[3])
    168 
    169   def p_attribute(self, p):
    170     """attribute : NAME EQUALS NUMBER
    171                  | NAME EQUALS NAME"""
    172     p[0] = ('ATTRIBUTE', p[1], p[3])
    173 
    174   def p_struct(self, p):
    175     """struct : attribute_section STRUCT NAME LCURLY fields RCURLY SEMICOLON"""
    176     p[0] = ('STRUCT', p[3], p[1], p[5])
    177 
    178   def p_fields(self, p):
    179     """fields : field fields
    180               |"""
    181     if len(p) > 1:
    182       p[0] = ListFromConcat(p[1], p[2])
    183 
    184   def p_field(self, p):
    185     """field : typename NAME ordinal SEMICOLON"""
    186     p[0] = ('FIELD', p[1], p[2], p[3])
    187 
    188   def p_interface(self, p):
    189     """interface : attribute_section INTERFACE NAME LCURLY methods RCURLY SEMICOLON"""
    190     p[0] = ('INTERFACE', p[3], p[1], p[5])
    191 
    192   def p_methods(self, p):
    193     """methods : method methods
    194                | """
    195     if len(p) > 1:
    196       p[0] = ListFromConcat(p[1], p[2])
    197 
    198   def p_method(self, p):
    199     """method : VOID NAME LPAREN parameters RPAREN ordinal SEMICOLON"""
    200     p[0] = ('METHOD', p[2], p[4], p[6])
    201 
    202   def p_parameters(self, p):
    203     """parameters : parameter
    204                   | parameter COMMA parameters
    205                   | """
    206     if len(p) == 1:
    207       p[0] = []
    208     elif len(p) == 2:
    209       p[0] = ListFromConcat(p[1])
    210     elif len(p) > 3:
    211       p[0] = ListFromConcat(p[1], p[3])
    212 
    213   def p_parameter(self, p):
    214     """parameter : typename NAME ordinal"""
    215     p[0] = ('PARAM', p[1], p[2], p[3])
    216 
    217   def p_typename(self, p):
    218     """typename : NAME
    219                 | ARRAY
    220                 | MSGPIPE
    221                 | MSGPIPEARRAY"""
    222     p[0] = p[1]
    223 
    224   def p_ordinal(self, p):
    225     """ordinal : ORDINAL
    226                | """
    227     if len(p) > 1:
    228       p[0] = p[1]
    229 
    230   def p_enum(self, p):
    231     """enum : ENUM NAME LCURLY enum_fields RCURLY SEMICOLON"""
    232     p[0] = ('ENUM', p[2], p[4])
    233 
    234   def p_enum_fields(self, p):
    235     """enum_fields : enum_field
    236                    | enum_field COMMA enum_fields
    237                    |"""
    238     if len(p) == 2:
    239       p[0] = ListFromConcat(p[1])
    240     elif len(p) > 3:
    241       p[0] = ListFromConcat(p[1], p[3])
    242 
    243   def p_enum_field(self, p):
    244     """enum_field : NAME
    245                   | NAME EQUALS NUMBER"""
    246     if len(p) == 2:
    247       p[0] = ('ENUM_FIELD', p[1], None)
    248     else:
    249       p[0] = ('ENUM_FIELD', p[1], p[3])
    250 
    251   def p_error(self, e):
    252     print('error: %s'%e)
    253 
    254 
    255 def Parse(filename):
    256   lexer = Lexer()
    257   parser = Parser(lexer)
    258 
    259   lex.lex(object=lexer)
    260   yacc.yacc(module=parser, debug=0, write_tables=0)
    261 
    262   tree = yacc.parse(open(filename).read())
    263   return tree
    264 
    265 
    266 def Main():
    267   if len(sys.argv) < 2:
    268     print("usage: %s filename" % (sys.argv[0]))
    269     sys.exit(1)
    270   tree = Parse(filename=sys.argv[1])
    271   print(tree)
    272 
    273 
    274 if __name__ == '__main__':
    275   Main()
    276