blob: 2556ffb47a694d7810aa2107f71296d86d9b4297 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2013 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.
""" Parser for PPAPI IDL """
#
# IDL Parser
#
# The parser is uses the PLY yacc library to build a set of parsing rules based
# on WebIDL.
#
# WebIDL, and WebIDL grammar can be found at:
# http://heycam.github.io/webidl/
# PLY can be found at:
# http://www.dabeaz.com/ply/
#
# The parser generates a tree by recursively matching sets of items against
# defined patterns. When a match is made, that set of items is reduced
# to a new item. The new item can provide a match for parent patterns.
# In this way an AST is built (reduced) depth first.
#
#
# Disable check for line length and Member as Function due to how grammar rules
# are defined with PLY
#
# pylint: disable=R0201
# pylint: disable=C0301
import sys
from idl_ppapi_lexer import IDLPPAPILexer
from idl_parser import IDLParser, ListFromConcat, ParseFile
from idl_node import IDLNode
class IDLPPAPIParser(IDLParser):
#
# We force all input files to start with two comments. The first comment is a
# Copyright notice followed by a file comment and finally by file level
# productions.
#
# [0] Insert a TOP definition for Copyright and Comments
def p_Top(self, p):
"""Top : COMMENT COMMENT Definitions"""
Copyright = self.BuildComment('Copyright', p, 1)
Filedoc = self.BuildComment('Comment', p, 2)
p[0] = ListFromConcat(Copyright, Filedoc, p[3])
#
#The parser is based on the WebIDL standard. See:
# http://heycam.github.io/webidl/#idl-grammar
#
# [1]
def p_Definitions(self, p):
"""Definitions : ExtendedAttributeList Definition Definitions
| """
if len(p) > 1:
p[2].AddChildren(p[1])
p[0] = ListFromConcat(p[2], p[3])
# [2] Add INLINE definition
def p_Definition(self, p):
"""Definition : CallbackOrInterface
| Struct
| Partial
| Dictionary
| Exception
| Enum
| Typedef
| ImplementsStatement
| Label
| Inline"""
p[0] = p[1]
def p_Inline(self, p):
"""Inline : INLINE"""
words = p[1].split()
name = self.BuildAttribute('NAME', words[1])
lines = p[1].split('\n')
value = self.BuildAttribute('VALUE', '\n'.join(lines[1:-1]) + '\n')
children = ListFromConcat(name, value)
p[0] = self.BuildProduction('Inline', p, 1, children)
#
# Label
#
# A label is a special kind of enumeration which allows us to go from a
# set of version numbrs to releases
#
def p_Label(self, p):
"""Label : LABEL identifier '{' LabelList '}' ';'"""
p[0] = self.BuildNamed('Label', p, 2, p[4])
def p_LabelList(self, p):
"""LabelList : identifier '=' float LabelCont"""
val = self.BuildAttribute('VALUE', p[3])
label = self.BuildNamed('LabelItem', p, 1, val)
p[0] = ListFromConcat(label, p[4])
def p_LabelCont(self, p):
"""LabelCont : ',' LabelList
|"""
if len(p) > 1:
p[0] = p[2]
def p_LabelContError(self, p):
"""LabelCont : error LabelCont"""
p[0] = p[2]
# [5.1] Add "struct" style interface
def p_Struct(self, p):
"""Struct : STRUCT identifier Inheritance '{' StructMembers '}' ';'"""
p[0] = self.BuildNamed('Struct', p, 2, ListFromConcat(p[3], p[5]))
def p_StructMembers(self, p):
"""StructMembers : StructMember StructMembers
|"""
if len(p) > 1:
p[0] = ListFromConcat(p[1], p[2])
def p_StructMember(self, p):
"""StructMember : ExtendedAttributeList Type identifier ';'"""
p[0] = self.BuildNamed('Member', p, 3, ListFromConcat(p[1], p[2]))
def p_Typedef(self, p):
"""Typedef : TYPEDEF ExtendedAttributeListNoComments Type identifier ';'"""
p[0] = self.BuildNamed('Typedef', p, 4, ListFromConcat(p[2], p[3]))
def p_TypedefFunc(self, p):
"""Typedef : TYPEDEF ExtendedAttributeListNoComments ReturnType identifier '(' ArgumentList ')' ';'"""
args = self.BuildProduction('Arguments', p, 5, p[6])
p[0] = self.BuildNamed('Callback', p, 4, ListFromConcat(p[2], p[3], args))
def p_ConstValue(self, p):
"""ConstValue : integer
| integer LSHIFT integer
| integer RSHIFT integer"""
val = str(p[1])
if len(p) > 2:
val = "%s %s %s" % (p[1], p[2], p[3])
p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'integer'),
self.BuildAttribute('VALUE', val))
def p_ConstValueStr(self, p):
"""ConstValue : string"""
p[0] = ListFromConcat(self.BuildAttribute('TYPE', 'string'),
self.BuildAttribute('VALUE', p[1]))
# Boolean & Float Literals area already BuildAttributes
def p_ConstValueLiteral(self, p):
"""ConstValue : FloatLiteral
| BooleanLiteral """
p[0] = p[1]
def p_EnumValueList(self, p):
"""EnumValueList : EnumValue EnumValues"""
p[0] = ListFromConcat(p[1], p[2])
def p_EnumValues(self, p):
"""EnumValues : ',' EnumValue EnumValues
|"""
if len(p) > 1:
p[0] = ListFromConcat(p[2], p[3])
def p_EnumValue(self, p):
"""EnumValue : ExtendedAttributeList identifier
| ExtendedAttributeList identifier '=' ConstValue"""
p[0] = self.BuildNamed('EnumItem', p, 2, p[1])
if len(p) > 3:
p[0].AddChildren(p[4])
# Omit PromiseType, as it is a JS type.
def p_NonAnyType(self, p):
"""NonAnyType : PrimitiveType TypeSuffix
| identifier TypeSuffix
| SEQUENCE '<' Type '>' Null"""
IDLParser.p_NonAnyType(self, p)
def p_PrimitiveType(self, p):
"""PrimitiveType : IntegerType
| UnsignedIntegerType
| FloatType
| HandleType
| PointerType"""
if type(p[1]) == str:
p[0] = self.BuildNamed('PrimitiveType', p, 1)
else:
p[0] = p[1]
def p_PointerType(self, p):
"""PointerType : STR_T
| MEM_T
| CSTR_T
| INTERFACE_T
| NULL"""
p[0] = p[1]
def p_HandleType(self, p):
"""HandleType : HANDLE_T
| PP_FILEHANDLE"""
p[0] = p[1]
def p_FloatType(self, p):
"""FloatType : FLOAT_T
| DOUBLE_T"""
p[0] = p[1]
def p_UnsignedIntegerType(self, p):
"""UnsignedIntegerType : UINT8_T
| UINT16_T
| UINT32_T
| UINT64_T"""
p[0] = p[1]
def p_IntegerType(self, p):
"""IntegerType : CHAR
| INT8_T
| INT16_T
| INT32_T
| INT64_T"""
p[0] = p[1]
# These targets are no longer used
def p_OptionalLong(self, p):
""" """
pass
def p_UnrestrictedFloatType(self, p):
""" """
pass
def p_null(self, p):
""" """
pass
def p_PromiseType(self, p):
""" """
pass
def p_EnumValueListComma(self, p):
""" """
pass
def p_EnumValueListString(self, p):
""" """
pass
def p_StringType(self, p):
""" """
pass
def p_RecordType(self, p):
""" """
pass
def p_RecordTypeError(self, p):
""" """
pass
# We only support:
# [ identifier ]
# [ identifier ( ArgumentList )]
# [ identifier ( ValueList )]
# [ identifier = identifier ]
# [ identifier = ( IdentifierList )]
# [ identifier = ConstValue ]
# [ identifier = identifier ( ArgumentList )]
# [51] map directly to 74-77
# [52-54, 56] are unsupported
def p_ExtendedAttribute(self, p):
"""ExtendedAttribute : ExtendedAttributeNoArgs
| ExtendedAttributeArgList
| ExtendedAttributeValList
| ExtendedAttributeIdent
| ExtendedAttributeIdentList
| ExtendedAttributeIdentConst
| ExtendedAttributeNamedArgList"""
p[0] = p[1]
def p_ExtendedAttributeValList(self, p):
"""ExtendedAttributeValList : identifier '(' ValueList ')'"""
arguments = self.BuildProduction('Values', p, 2, p[3])
p[0] = self.BuildNamed('ExtAttribute', p, 1, arguments)
def p_ValueList(self, p):
"""ValueList : ConstValue ValueListCont"""
p[0] = ListFromConcat(p[1], p[2])
def p_ValueListCont(self, p):
"""ValueListCont : ValueList
|"""
if len(p) > 1:
p[0] = p[1]
def p_ExtendedAttributeIdentConst(self, p):
"""ExtendedAttributeIdentConst : identifier '=' ConstValue"""
p[0] = self.BuildNamed('ExtAttribute', p, 1, p[3])
def __init__(self, lexer, verbose=False, debug=False, mute_error=False):
IDLParser.__init__(self, lexer, verbose, debug, mute_error)
def main(argv):
nodes = []
parser = IDLPPAPIParser(IDLPPAPILexer())
errors = 0
for filename in argv:
filenode = ParseFile(parser, filename)
if filenode:
errors += filenode.GetProperty('ERRORS')
nodes.append(filenode)
ast = IDLNode('AST', '__AST__', 0, 0, nodes)
print '\n'.join(ast.Tree(accept_props=['PROD', 'TYPE', 'VALUE']))
if errors:
print '\nFound %d errors.\n' % errors
return errors
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))