| #!/usr/bin/python |
| # Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| # for details. All rights reserved. Use of this source code is governed by a |
| # BSD-style license that can be found in the LICENSE file. |
| |
| import re |
| import subprocess |
| import tempfile |
| import os |
| |
| from pegparser import * |
| |
| # IDL grammar variants. |
| WEBIDL_SYNTAX = 0 |
| WEBKIT_SYNTAX = 1 |
| FREMONTCUT_SYNTAX = 2 |
| |
| |
| class IDLParser(object): |
| """IDLParser is a PEG based IDL files parser.""" |
| |
| def __init__(self, syntax=WEBIDL_SYNTAX): |
| """Constructor. |
| |
| Initializes the IDLParser by defining the grammar and initializing |
| a PEGParserinstance. |
| |
| Args: |
| syntax -- supports either WEBIDL_SYNTAX (0) or WEBKIT_SYNTAX (1) |
| """ |
| self._syntax = syntax |
| self._pegparser = PegParser(self._idl_grammar(), |
| self._whitespace_grammar(), |
| strings_are_tokens=True) |
| |
| def _idl_grammar(self): |
| """Returns the PEG grammar for IDL parsing.""" |
| |
| # utilities: |
| def syntax_switch(w3c_syntax, webkit_syntax, fremontcut_syntax=None): |
| """Returns w3c_syntax or web_syntax, depending on the current |
| configuration. |
| """ |
| if self._syntax == WEBIDL_SYNTAX: |
| return w3c_syntax |
| elif self._syntax == WEBKIT_SYNTAX: |
| return webkit_syntax |
| elif self._syntax == FREMONTCUT_SYNTAX: |
| if fremontcut_syntax is not None: |
| return fremontcut_syntax |
| return w3c_syntax |
| else: |
| raise RuntimeError('unsupported IDL syntax %s' % syntax) |
| |
| # The following grammar is based on the Web IDL's LL(1) grammar |
| # (specified in: http://dev.w3.org/2006/webapi/WebIDL/#idl-grammar). |
| # It is adjusted to PEG grammar, as well as to also support |
| # WebKit IDL and FremontCut grammar. |
| |
| ###################### BEGIN GRAMMAR ##################### |
| |
| def Id(): |
| return re.compile(r'[\w\_]+') |
| |
| def _Definitions(): |
| return MAYBE(MANY(_Definition)) |
| |
| def _Definition(): |
| return syntax_switch( |
| # Web IDL: |
| OR(Module, Interface, ExceptionDef, TypeDef, ImplStmt, |
| ValueTypeDef, Const), |
| # WebKit: |
| OR(Module, Interface)) |
| |
| def Module(): |
| return syntax_switch( |
| # Web IDL: |
| [MAYBE(ExtAttrs), 'module', Id, '{', _Definitions, '}', |
| MAYBE(';')], |
| # WebKit: |
| ['module', MAYBE(ExtAttrs), Id, '{', _Definitions, '}', |
| MAYBE(';')], |
| # FremontCut: |
| [MAYBE(_Annotations), MAYBE(ExtAttrs), 'module', Id, |
| '{', _Definitions, '}', MAYBE(';')]) |
| |
| def Interface(): |
| return syntax_switch( |
| # Web IDL: |
| [MAYBE(ExtAttrs), 'interface', Id, MAYBE(_ParentInterfaces), |
| MAYBE(['{', MAYBE(MANY(_Member)), '}']), ';'], |
| # WebKit: |
| [OR('interface', 'exception'), MAYBE(ExtAttrs), Id, MAYBE(_ParentInterfaces), |
| MAYBE(['{', MAYBE(MANY(_Member)), '}']), MAYBE(';')], |
| # FremontCut: |
| [MAYBE(_Annotations), MAYBE(ExtAttrs), 'interface', |
| Id, MAYBE(_ParentInterfaces), MAYBE(['{', MAYBE(MANY(_Member)), |
| '}']), ';']) |
| |
| def _Member(): |
| return syntax_switch( |
| # Web IDL: |
| OR(Const, Attribute, Operation, ExtAttrs), |
| # WebKit: |
| OR(Const, Attribute, Operation), |
| # FremontCut: |
| OR(Const, Attribute, Operation)) |
| |
| # Interface inheritance: |
| def _ParentInterfaces(): |
| return [':', MANY(ParentInterface, separator=',')] |
| |
| def ParentInterface(): |
| return syntax_switch( |
| # Web IDL: |
| [InterfaceType], |
| # WebKit: |
| [InterfaceType], |
| # FremontCut: |
| [MAYBE(_Annotations), InterfaceType]) |
| |
| # TypeDef (Web IDL): |
| def TypeDef(): |
| return ['typedef', Type, Id, ';'] |
| |
| # TypeDef (Old-school W3C IDLs) |
| def ValueTypeDef(): |
| return ['valuetype', Id, Type, ';'] |
| |
| # Implements Statement (Web IDL): |
| def ImplStmt(): |
| return [ImplStmtImplementor, 'implements', ImplStmtImplemented, |
| ';'] |
| |
| def ImplStmtImplementor(): |
| return ScopedName |
| |
| def ImplStmtImplemented(): |
| return ScopedName |
| |
| # Constants: |
| def Const(): |
| return syntax_switch( |
| # Web IDL: |
| [MAYBE(ExtAttrs), 'const', Type, Id, '=', ConstExpr, ';'], |
| # WebKit: |
| [MAYBE(ExtAttrs), 'const', Type, Id, '=', ConstExpr, ';'], |
| # FremontCut: |
| [MAYBE(_Annotations), MAYBE(ExtAttrs), 'const', Type, Id, '=', |
| ConstExpr, ';']) |
| |
| def ConstExpr(): |
| return OR(_BooleanLiteral, |
| _IntegerLiteral, |
| _FloatLiteral) |
| |
| def _BooleanLiteral(): |
| return re.compile(r'true|false') |
| |
| def _IntegerLiteral(): |
| return OR(re.compile(r'(0x)?[0-9ABCDEF]+'), |
| re.compile(r'[0-9]+')) |
| |
| def _FloatLiteral(): |
| return re.compile(r'[0-9]+\.[0-9]*') |
| |
| # Attributes: |
| def Attribute(): |
| return syntax_switch( |
| # Web IDL: |
| [MAYBE(ExtAttrs), MAYBE(Stringifier), MAYBE(ReadOnly), |
| 'attribute', Type, Id, MAYBE(_AttrRaises), ';'], |
| # WebKit: |
| [MAYBE(Stringifier), MAYBE(ReadOnly), 'attribute', |
| MAYBE(ExtAttrs), Type, Id, MAYBE(_AttrRaises), ';'], |
| # FremontCut: |
| [MAYBE(_Annotations), MAYBE(ExtAttrs), |
| MAYBE(_AttrGetterSetter), MAYBE(Stringifier), MAYBE(ReadOnly), |
| 'attribute', Type, Id, MAYBE(_AttrRaises), ';']) |
| |
| def _AttrRaises(): |
| return syntax_switch( |
| # Web IDL: |
| MANY(OR(GetRaises, SetRaises)), |
| # WebKit: |
| MANY(OR(GetRaises, SetRaises, Raises), separator=',')) |
| |
| # Special fremontcut feature: |
| def _AttrGetterSetter(): |
| return OR(AttrGetter, AttrSetter) |
| |
| def AttrGetter(): |
| return 'getter' |
| |
| def AttrSetter(): |
| return 'setter' |
| |
| def ReadOnly(): |
| return 'readonly' |
| |
| def GetRaises(): |
| return syntax_switch( |
| # Web IDL: |
| ['getraises', '(', _ScopedNames, ')'], |
| # WebKit: |
| ['getter', 'raises', '(', _ScopedNames, ')']) |
| |
| def SetRaises(): |
| return syntax_switch( |
| # Web IDL: |
| ['setraises', '(', _ScopedNames, ')'], |
| # WebKit: |
| ['setter', 'raises', '(', _ScopedNames, ')']) |
| |
| # Operation: |
| def Operation(): |
| return syntax_switch( |
| # Web IDL: |
| [MAYBE(ExtAttrs), MAYBE(Static), MAYBE(Stringifier), MAYBE(_Specials), |
| ReturnType, MAYBE(Id), '(', _Arguments, ')', MAYBE(Raises), |
| ';'], |
| # WebKit: |
| [MAYBE(ExtAttrs), MAYBE(Static), |
| ReturnType, MAYBE(Id), '(', _Arguments, ')', |
| MAYBE(Raises), ';'], |
| # FremontCut: |
| [MAYBE(_Annotations), MAYBE(ExtAttrs), MAYBE(Static), MAYBE(Stringifier), |
| MAYBE(_Specials), ReturnType, MAYBE(Id), '(', _Arguments, ')', |
| MAYBE(Raises), ';']) |
| |
| def Static(): |
| return 'static' |
| |
| def _Specials(): |
| return MANY(Special) |
| |
| def Special(): |
| return re.compile(r'getter|setter|creator|deleter|caller') |
| |
| def Stringifier(): |
| return 'stringifier' |
| |
| def Raises(): |
| return ['raises', '(', _ScopedNames, ')'] |
| |
| # Operation arguments: |
| def _Arguments(): |
| return MAYBE(MANY(Argument, ',')) |
| |
| def Argument(): |
| return syntax_switch( |
| # Web IDL: |
| [MAYBE(ExtAttrs), MAYBE(Optional), MAYBE('in'), |
| MAYBE(Optional), Type, MAYBE(AnEllipsis), Id], |
| # WebKit: |
| [MAYBE(Optional), MAYBE('in'), MAYBE(Optional), |
| MAYBE(ExtAttrs), Type, Id]) |
| |
| def Optional(): |
| return 'optional' |
| |
| def AnEllipsis(): |
| return '...' |
| |
| # Exceptions (Web IDL). |
| def ExceptionDef(): |
| return ['exception', Id, '{', MAYBE(MANY(_ExceptionMember)), '}', |
| ';'] |
| |
| def _ExceptionMember(): |
| return OR(Const, ExceptionField, ExtAttrs) |
| |
| def ExceptionField(): |
| return [Type, Id, ';'] |
| |
| # Types: |
| def Type(): |
| return _Type |
| |
| def ReturnType(): |
| return OR(VoidType, _Type) |
| |
| def InterfaceType(): |
| return ScopedName |
| |
| def _Type(): |
| return OR(AnyArrayType, AnyType, ObjectType, _NullableType) |
| |
| def _NullableType(): |
| return [OR(_IntegerType, BooleanType, OctetType, FloatType, |
| DoubleType, SequenceType, DOMStringArrayType, ScopedName), |
| MAYBE(Nullable)] |
| |
| def Nullable(): |
| return '?' |
| |
| def SequenceType(): |
| return ['sequence', '<', Type, '>'] |
| |
| def AnyType(): |
| return 'any' |
| |
| def AnyArrayType(): |
| # TODO(sra): Do more general handling of array types. |
| return 'any[]' |
| |
| def ObjectType(): |
| return re.compile(r'(object|Object)\b') # both spellings. |
| |
| def VoidType(): |
| return 'void' |
| |
| def _IntegerType(): |
| return [MAYBE(Unsigned), OR(ByteType, IntType, LongLongType, |
| LongType, OctetType, ShortType)] |
| |
| def Unsigned(): |
| return 'unsigned' |
| |
| def ShortType(): |
| return 'short' |
| |
| def LongLongType(): |
| return ['long', 'long'] |
| |
| def LongType(): |
| return 'long' |
| |
| def IntType(): |
| return 'int' |
| |
| def ByteType(): |
| return 'byte' |
| |
| def OctetType(): |
| return 'octet' |
| |
| def BooleanType(): |
| return 'boolean' |
| |
| def FloatType(): |
| return 'float' |
| |
| def DoubleType(): |
| return 'double' |
| |
| def _ScopedNames(): |
| return MANY(ScopedName, separator=',') |
| |
| def ScopedName(): |
| return re.compile(r'[\w\_\:\.\<\>]+') |
| |
| def DOMStringArrayType(): |
| return 'DOMString[]' |
| |
| # Extended Attributes: |
| def ExtAttrs(): |
| return ['[', MAYBE(MANY(ExtAttr, ',')), ']'] |
| |
| def ExtAttr(): |
| return [Id, MAYBE(OR(['=', ExtAttrValue], ExtAttrArgList))] |
| |
| def ExtAttrValue(): |
| return OR(ExtAttrFunctionValue, re.compile(r'[\w&0-9:\-\|]+')) |
| |
| def ExtAttrFunctionValue(): |
| return [Id, ExtAttrArgList] |
| |
| def ExtAttrArgList(): |
| return ['(', MAYBE(MANY(Argument, ',')), ')'] |
| |
| # Annotations - used in the FremontCut IDL grammar: |
| def _Annotations(): |
| return MANY(Annotation) |
| |
| def Annotation(): |
| return ['@', Id, MAYBE(_AnnotationBody)] |
| |
| def _AnnotationBody(): |
| return ['(', MAYBE(MANY(AnnotationArg, ',')), ')'] |
| |
| def AnnotationArg(): |
| return [Id, MAYBE(['=', AnnotationArgValue])] |
| |
| def AnnotationArgValue(): |
| return re.compile(r'[\w&0-9:/\-\.]+') |
| |
| ###################### END GRAMMAR ##################### |
| |
| # Return the grammar's root rule: |
| return MANY(_Definition) |
| |
| def _whitespace_grammar(self): |
| return OR(re.compile(r'\s+'), |
| re.compile(r'//.*'), |
| re.compile(r'#.*'), |
| re.compile(r'/\*.*?\*/', re.S)) |
| |
| def _pre_process(self, content, defines, includePaths): |
| """Pre-processes the content using gcc. |
| |
| WebKit IDLs require pre-processing by gcc. This is done by invoking |
| gcc in a sub-process and capturing the results. |
| |
| Returns: |
| The result of running gcc on the content. |
| |
| Args: |
| content -- text to process. |
| defines -- an array of pre-processor defines. |
| includePaths -- an array of path strings. |
| """ |
| # FIXME: Handle gcc not found, or any other processing errors |
| gcc = os.environ.get('CC', None) |
| if not gcc: |
| gcc = 'gcc' |
| cmd = [gcc, '-E', '-P', '-C', '-x', 'c++']; |
| for define in defines: |
| cmd.append('-D%s' % define) |
| |
| cmd.append('-') |
| pipe = subprocess.Popen(cmd, stdin=subprocess.PIPE, |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| (content, stderr) = pipe.communicate(content) |
| return content |
| |
| def parse(self, content, defines=[], includePaths=[]): |
| """Parse the give content string. |
| |
| The WebKit IDL syntax also allows gcc pre-processing instructions. |
| Lists of defined variables and include paths can be provided. |
| |
| Returns: |
| An abstract syntax tree (AST). |
| |
| Args: |
| content -- text to parse. |
| defines -- an array of pre-processor defines. |
| includePaths -- an array of path strings used by the |
| gcc pre-processor. |
| """ |
| if self._syntax == WEBKIT_SYNTAX: |
| content = self._pre_process(content, defines, includePaths) |
| |
| return self._pegparser.parse(content) |