James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 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 | """ Parser for PPAPI IDL """ |
| 7 | |
| 8 | # |
| 9 | # IDL Parser |
| 10 | # |
| 11 | # The parser is uses the PLY yacc library to build a set of parsing rules based |
| 12 | # on WebIDL. |
| 13 | # |
| 14 | # WebIDL, and WebIDL grammar can be found at: |
| 15 | # http://heycam.github.io/webidl/ |
| 16 | # PLY can be found at: |
| 17 | # http://www.dabeaz.com/ply/ |
| 18 | # |
| 19 | # The parser generates a tree by recursively matching sets of items against |
| 20 | # defined patterns. When a match is made, that set of items is reduced |
| 21 | # to a new item. The new item can provide a match for parent patterns. |
| 22 | # In this way an AST is built (reduced) depth first. |
| 23 | # |
| 24 | |
| 25 | # |
| 26 | # Disable check for line length and Member as Function due to how grammar rules |
| 27 | # are defined with PLY |
| 28 | # |
| 29 | # pylint: disable=R0201 |
| 30 | # pylint: disable=C0301 |
| 31 | |
| 32 | import sys |
| 33 | |
| 34 | from idl_ppapi_lexer import IDLPPAPILexer |
| 35 | from idl_parser import IDLParser, ListFromConcat, ParseFile |
| 36 | from idl_node import IDLNode |
| 37 | |
| 38 | class IDLPPAPIParser(IDLParser): |
| 39 | # |
| 40 | # We force all input files to start with two comments. The first comment is a |
| 41 | # Copyright notice followed by a file comment and finally by file level |
| 42 | # productions. |
| 43 | # |
| 44 | # [0] Insert a TOP definition for Copyright and Comments |
| 45 | def p_Top(self, p): |
| 46 | """Top : COMMENT COMMENT Definitions""" |
| 47 | Copyright = self.BuildComment('Copyright', p, 1) |
| 48 | Filedoc = self.BuildComment('Comment', p, 2) |
| 49 | p[0] = ListFromConcat(Copyright, Filedoc, p[3]) |
| 50 | |
| 51 | # |
| 52 | #The parser is based on the WebIDL standard. See: |
| 53 | # http://heycam.github.io/webidl/#idl-grammar |
| 54 | # |
| 55 | # [1] |
| 56 | def p_Definitions(self, p): |
| 57 | """Definitions : ExtendedAttributeList Definition Definitions |
| 58 | | """ |
| 59 | if len(p) > 1: |
| 60 | p[2].AddChildren(p[1]) |
| 61 | p[0] = ListFromConcat(p[2], p[3]) |
| 62 | |
| 63 | # [2] Add INLINE definition |
| 64 | def p_Definition(self, p): |
| 65 | """Definition : CallbackOrInterface |
| 66 | | Struct |
| 67 | | Partial |
| 68 | | Dictionary |
| 69 | | Exception |
| 70 | | Enum |
| 71 | | Typedef |
| 72 | | ImplementsStatement |
| 73 | | Label |
| 74 | | Inline""" |
| 75 | p[0] = p[1] |
| 76 | |
| 77 | def p_Inline(self, p): |
| 78 | """Inline : INLINE""" |
| 79 | words = p[1].split() |
| 80 | name = self.BuildAttribute('NAME', words[1]) |
| 81 | lines = p[1].split('\n') |
| 82 | value = self.BuildAttribute('VALUE', '\n'.join(lines[1:-1]) + '\n') |
| 83 | children = ListFromConcat(name, value) |
| 84 | p[0] = self.BuildProduction('Inline', p, 1, children) |
| 85 | |
| 86 | # |
| 87 | # Label |
| 88 | # |
| 89 | # A label is a special kind of enumeration which allows us to go from a |
| 90 | # set of version numbrs to releases |
| 91 | # |
| 92 | def p_Label(self, p): |
| 93 | """Label : LABEL identifier '{' LabelList '}' ';'""" |
| 94 | p[0] = self.BuildNamed('Label', p, 2, p[4]) |
| 95 | |
| 96 | def p_LabelList(self, p): |
| 97 | """LabelList : identifier '=' float LabelCont""" |
| 98 | val = self.BuildAttribute('VALUE', p[3]) |
| 99 | label = self.BuildNamed('LabelItem', p, 1, val) |
| 100 | p[0] = ListFromConcat(label, p[4]) |
| 101 | |
| 102 | def p_LabelCont(self, p): |
| 103 | """LabelCont : ',' LabelList |
| 104 | |""" |
| 105 | if len(p) > 1: |
| 106 | p[0] = p[2] |
| 107 | |
| 108 | def p_LabelContError(self, p): |
| 109 | """LabelCont : error LabelCont""" |
| 110 | p[0] = p[2] |
| 111 | |
| 112 | # [5.1] Add "struct" style interface |
| 113 | def p_Struct(self, p): |
| 114 | """Struct : STRUCT identifier Inheritance '{' StructMembers '}' ';'""" |
| 115 | p[0] = self.BuildNamed('Struct', p, 2, ListFromConcat(p[3], p[5])) |
| 116 | |
| 117 | def p_StructMembers(self, p): |
| 118 | """StructMembers : StructMember StructMembers |
| 119 | |""" |
| 120 | if len(p) > 1: |
| 121 | p[0] = ListFromConcat(p[1], p[2]) |
| 122 | |
| 123 | def p_StructMember(self, p): |
| 124 | """StructMember : ExtendedAttributeList Type identifier ';'""" |
| 125 | p[0] = self.BuildNamed('Member', p, 3, ListFromConcat(p[1], p[2])) |
| 126 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 127 | def p_Typedef(self, p): |
| 128 | """Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'""" |
| 129 | p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3])) |
| 130 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 131 | def p_TypedefFunc(self, p): |
| 132 | """Typedef : TYPEDEF ExtendedAttributeListNoComments ReturnType identifier '(' ArgumentList ')' ';'""" |
| 133 | args = self.BuildProduction('Arguments', p, 5, p[6]) |
| 134 | p[0] = self.BuildNamed('Callback', p, 4, ListFromConcat(p[2], p[3], args)) |
| 135 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 136 | def p_ConstValue(self, p): |
| 137 | """ConstValue : integer |
| 138 | | integer LSHIFT integer |
| 139 | | integer RSHIFT integer""" |
| 140 | val = str(p[1]) |
| 141 | if len(p) > 2: |
| 142 | val = "%s %s %s" % (p[1], p[2], p[3]) |
| 143 | p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'), |
| 144 | self.BuildAttribute('VALUE', val)) |
| 145 | |
| 146 | def p_ConstValueStr(self, p): |
| 147 | """ConstValue : string""" |
| 148 | p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'string'), |
| 149 | self.BuildAttribute('VALUE', p[1])) |
| 150 | |
| 151 | # Boolean & Float Literals area already BuildAttributes |
| 152 | def p_ConstValueLiteral(self, p): |
| 153 | """ConstValue : FloatLiteral |
| 154 | | BooleanLiteral """ |
| 155 | p[0] = p[1] |
| 156 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 157 | def p_EnumValueList(self, p): |
| 158 | """EnumValueList : EnumValue EnumValues""" |
| 159 | p[0] = ListFromConcat(p[1], p[2]) |
| 160 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 161 | def p_EnumValues(self, p): |
| 162 | """EnumValues : ',' EnumValue EnumValues |
| 163 | |""" |
| 164 | if len(p) > 1: |
| 165 | p[0] = ListFromConcat(p[2], p[3]) |
| 166 | |
| 167 | def p_EnumValue(self, p): |
| 168 | """EnumValue : ExtendedAttributeList identifier |
| 169 | | ExtendedAttributeList identifier '=' ConstValue""" |
| 170 | p[0] = self.BuildNamed('EnumItem', p, 2, p[1]) |
| 171 | if len(p) > 3: |
| 172 | p[0].AddChildren(p[4]) |
| 173 | |
James Robinson | 24218d7 | 2014-10-20 16:18:41 -0700 | [diff] [blame] | 174 | # Omit PromiseType, as it is a JS type. |
| 175 | def p_NonAnyType(self, p): |
| 176 | """NonAnyType : PrimitiveType TypeSuffix |
| 177 | | identifier TypeSuffix |
| 178 | | SEQUENCE '<' Type '>' Null""" |
| 179 | IDLParser.p_NonAnyType(self, p) |
| 180 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 181 | def p_PrimitiveType(self, p): |
| 182 | """PrimitiveType : IntegerType |
| 183 | | UnsignedIntegerType |
| 184 | | FloatType |
| 185 | | HandleType |
| 186 | | PointerType""" |
| 187 | if type(p[1]) == str: |
| 188 | p[0] = self.BuildNamed('PrimitiveType', p, 1) |
| 189 | else: |
| 190 | p[0] = p[1] |
| 191 | |
| 192 | def p_PointerType(self, p): |
| 193 | """PointerType : STR_T |
| 194 | | MEM_T |
| 195 | | CSTR_T |
| 196 | | INTERFACE_T |
| 197 | | NULL""" |
| 198 | p[0] = p[1] |
| 199 | |
| 200 | def p_HandleType(self, p): |
| 201 | """HandleType : HANDLE_T |
| 202 | | PP_FILEHANDLE""" |
| 203 | p[0] = p[1] |
| 204 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 205 | def p_FloatType(self, p): |
| 206 | """FloatType : FLOAT_T |
| 207 | | DOUBLE_T""" |
| 208 | p[0] = p[1] |
| 209 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 210 | def p_UnsignedIntegerType(self, p): |
| 211 | """UnsignedIntegerType : UINT8_T |
| 212 | | UINT16_T |
| 213 | | UINT32_T |
| 214 | | UINT64_T""" |
| 215 | p[0] = p[1] |
| 216 | |
| 217 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 218 | def p_IntegerType(self, p): |
| 219 | """IntegerType : CHAR |
| 220 | | INT8_T |
| 221 | | INT16_T |
| 222 | | INT32_T |
| 223 | | INT64_T""" |
| 224 | p[0] = p[1] |
| 225 | |
| 226 | # These targets are no longer used |
| 227 | def p_OptionalLong(self, p): |
| 228 | """ """ |
| 229 | pass |
| 230 | |
| 231 | def p_UnrestrictedFloatType(self, p): |
| 232 | """ """ |
| 233 | pass |
| 234 | |
| 235 | def p_null(self, p): |
| 236 | """ """ |
| 237 | pass |
| 238 | |
James Robinson | 24218d7 | 2014-10-20 16:18:41 -0700 | [diff] [blame] | 239 | def p_PromiseType(self, p): |
| 240 | """ """ |
| 241 | pass |
| 242 | |
Etienne Membrives | 175837a | 2014-12-19 15:45:38 +0100 | [diff] [blame] | 243 | def p_EnumValueListComma(self, p): |
| 244 | """ """ |
| 245 | pass |
| 246 | |
| 247 | def p_EnumValueListString(self, p): |
| 248 | """ """ |
| 249 | pass |
| 250 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 251 | # We only support: |
| 252 | # [ identifier ] |
| 253 | # [ identifier ( ArgumentList )] |
| 254 | # [ identifier ( ValueList )] |
| 255 | # [ identifier = identifier ] |
| 256 | # [ identifier = ( IdentifierList )] |
| 257 | # [ identifier = ConstValue ] |
| 258 | # [ identifier = identifier ( ArgumentList )] |
| 259 | # [51] map directly to 74-77 |
| 260 | # [52-54, 56] are unsupported |
| 261 | def p_ExtendedAttribute(self, p): |
| 262 | """ExtendedAttribute : ExtendedAttributeNoArgs |
| 263 | | ExtendedAttributeArgList |
| 264 | | ExtendedAttributeValList |
| 265 | | ExtendedAttributeIdent |
| 266 | | ExtendedAttributeIdentList |
| 267 | | ExtendedAttributeIdentConst |
| 268 | | ExtendedAttributeNamedArgList""" |
| 269 | p[0] = p[1] |
| 270 | |
| 271 | def p_ExtendedAttributeValList(self, p): |
| 272 | """ExtendedAttributeValList : identifier '(' ValueList ')'""" |
| 273 | arguments = self.BuildProduction('Values', p, 2, p[3]) |
| 274 | p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments) |
| 275 | |
| 276 | def p_ValueList(self, p): |
| 277 | """ValueList : ConstValue ValueListCont""" |
| 278 | p[0] = ListFromConcat(p[1], p[2]) |
| 279 | |
| 280 | def p_ValueListCont(self, p): |
| 281 | """ValueListCont : ValueList |
| 282 | |""" |
| 283 | if len(p) > 1: |
| 284 | p[0] = p[1] |
| 285 | |
James Robinson | 646469d | 2014-10-03 15:33:28 -0700 | [diff] [blame] | 286 | def p_ExtendedAttributeIdentConst(self, p): |
| 287 | """ExtendedAttributeIdentConst : identifier '=' ConstValue""" |
| 288 | p[0] = self.BuildNamed('ExtAttribute', p, 1, p[3]) |
| 289 | |
| 290 | |
| 291 | def __init__(self, lexer, verbose=False, debug=False, mute_error=False): |
| 292 | IDLParser.__init__(self, lexer, verbose, debug, mute_error) |
| 293 | |
| 294 | |
| 295 | def main(argv): |
| 296 | nodes = [] |
| 297 | parser = IDLPPAPIParser(IDLPPAPILexer()) |
| 298 | errors = 0 |
| 299 | |
| 300 | for filename in argv: |
| 301 | filenode = ParseFile(parser, filename) |
| 302 | if filenode: |
| 303 | errors += filenode.GetProperty('ERRORS') |
| 304 | nodes.append(filenode) |
| 305 | |
| 306 | ast = IDLNode('AST', '__AST__', 0, 0, nodes) |
| 307 | |
| 308 | print '\n'.join(ast.Tree(accept_props=['PROD', 'TYPE', 'VALUE'])) |
| 309 | if errors: |
| 310 | print '\nFound %d errors.\n' % errors |
| 311 | |
| 312 | |
| 313 | return errors |
| 314 | |
| 315 | |
| 316 | if __name__ == '__main__': |
| 317 | sys.exit(main(sys.argv[1:])) |