blob: ba676878838d3b69a049550adae2daf314522b99 [file] [log] [blame]
#!/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)