# -*- coding: utf-8 -*- module WebIDL module Parser # http://dev.w3.org/2006/webapi/WebIDL/#idl-grammar grammar IDL rule Definitions ws metadef:(eal:ExtendedAttributeList ws d:Definition ws defs:Definitions)? ws end rule Definition CallbackOrInterface / Interface / Partial / Dictionary / Exception / Enum / TypeDef / ImplementsStatement end rule CallbackOrInterface "callback" ws obj:CallbackRestOrInterface { def build(parent) obj.build(parent) end } end rule CallbackRestOrInterface CallbackRest / Interface end rule Interface "interface" ws name:identifier ws inherits:Inheritance ws "{" ws members:InterfaceMembers ws "}" ws ";" end rule Partial "partial" ws defn:PartialDefinition { def build(parent) defn.build(parent) end } end rule PartialDefinition PartialInterface / PartialDictionary end # like an interface, but no inheritance rule PartialInterface "interface" ws name:identifier ws "{" ws members:InterfaceMembers ws "}" ws ";" end rule Inheritance (":" ws names:ScopedNameList )? end # extract to a generic Members syntax node? rule InterfaceMembers (eal:ExtendedAttributeList ws member:InterfaceMember ws members:InterfaceMembers ws )? end rule InterfaceMember Const / AttributeOrOperation end # Dictionary → "dictionary" identifier Inheritance "{" DictionaryMembers "}" ";" rule Dictionary "dictionary" ws name:identifier ws inherits:Inheritance ws "{" ws members:DictionaryMembers ws "}" ws ";" end # Like a dictionary, but no inheritance rule PartialDictionary "dictionary" ws name:identifier ws "{" ws members:DictionaryMembers ws "}" ws ";" end # [11] DictionaryMembers → ExtendedAttributeList DictionaryMember DictionaryMembers # | ε rule DictionaryMembers (eal:ExtendedAttributeList ws member:DictionaryMember ws members:DictionaryMembers )? end rule Required "required" end rule DictionaryMember required:Required? ws type:Type ws name:identifier ws default:Default ws ";" end rule Default ("=" ws val:DefaultValue { def build() val.build end })? end rule DefaultValue string / ConstValue end rule Exception "exception" ws name:identifier ws inherits:Inheritance ws "{" ws members:ExceptionMembers ws "}" ws ";" end # extract to a generic Members syntax node? rule ExceptionMembers (eal:ExtendedAttributeList ws member:ExceptionMember ws members:ExceptionMembers ws )? end rule CallbackRest name:identifier ws "=" ws return_type:ReturnType ws "(" ws args:ArgumentList ")" ws ";" end rule TypeDef "typedef" ws type:Type ws name:identifier ws ";" end rule ImplementsStatement implementor:ScopedName ws "implements" ws implementee:ScopedName ws ";" end rule Const "const" ws type:Type ws name:identifier ws "=" ws const_expr:ConstValue ws ";" end rule ConstValue "null" { def build() nil end } / BooleanLiteral / FloatLiteral / integer end rule BooleanLiteral ("true" / "false") { def build() text_value == "true" end } end rule FloatLiteral float / "-" "Infinity" { def build() -1.0/0 end } / "Infinity" { def build() 1.0/0 end } / "NaN" { def build() Float::NAN end } end rule Enum "enum" ws id:identifier ws "{" ws values:EnumValueList ws "}" ws ";" end rule EnumValueList name:string ws vals:EnumValues ws ","? # trailing commas actually not allowed, but in use { def build() res = [name.build] res += vals.build if vals.any? res end } end rule EnumValues ("," ws name:string ws vals:EnumValues { def build() res = [name.build] res += vals.build if vals.any? res end })? end rule AttributeOrOperation StringifierAttributeOrOperation / Attribute / Operation end rule StringifierAttributeOrOperation "stringifier" ws a_or_op:(Attribute / OperationRest / ";") end rule Attribute readonly:ReadOnly ws "attribute" ws type:Type ws name:identifier ws getraises:GetRaises ws setraises:SetRaises ws ";" end rule ReadOnly "readonly"? end rule GetRaises ("getraises" ws list:ExceptionList)? end rule SetRaises ("setraises" ws list:ExceptionList)? end rule Operation static:"static"? specials:(OmittableSpecials / Specials) ws op:OperationRest end rule OmittableSpecials omit:"omittable" ws Specials end rule Specials (first:Special ws rest:Specials )? end rule Special "getter" / "setter" / "creator" / "deleter" / "legacycaller" end rule OperationRest type:ReturnType ws optional_id:OptionalIdentifier ws "(" ws args:ArgumentList ws ")" ws raises:Raises ws ";" end rule OptionalIdentifier identifier? end rule Raises ("raises" ExceptionList)? end rule ExceptionList "(" ScopedNameList ")" end rule ArgumentList (arg:Argument ws args:Arguments )? end rule Arguments ("," ws arg:Argument ws args:Arguments )? end rule Argument eal:ExtendedAttributeList ws "in"? ws arg:OptionalOrRequiredArgument end rule OptionalOrRequiredArgument optional:"optional" ws type:Type ws name:ArgumentName ws default:Default / type:Type ws variadic:Ellipsis ws name:ArgumentName end rule ArgumentName ArgumentNameKeyword { def build() text_value end } / identifier end rule ArgumentNameKeyword "attribute" / "callback" / "const" / "creator" / "deleter" / "dictionary" / "enum" / "exception" / "getter" / "implements" / "inherit" / "interface" / "legacycaller" / "partial" / "setter" / "static" / "stringifier" / "typedef" / "unrestricted" end rule Ellipsis "..."? end rule ExceptionMember Const / ExceptionField end rule ExceptionField type:Type ws id:identifier ws ";" end rule ExtendedAttributeList ("[" ws attribute:ExtendedAttribute ws attributes:ExtendedAttributes ws "]" )? end rule ExtendedAttributes ("," ws attribute:ExtendedAttribute ws attributes:ExtendedAttributes )? end # # avoid infinite recursion trouble with the actual spec by using the "more restricted" ones # rule ExtendedAttribute ExtendedAttributeNamedArgList / ExtendedAttributeIdent / ExtendedAttributeScopedName / ExtendedAttributeArgList / ExtendedAttributeIdentList / ExtendedAttributeNoArg end rule ExtendedAttributeNoArg identifier { def build(parent) Ast::ExtendedAttribute.new(text_value) end } end rule ExtendedAttributeArgList name:identifier "(" ws args:ArgumentList ws ")" end rule ExtendedAttributeScopedName key:identifier "=" scoped_name:ScopedName end rule ExtendedAttributeIdent key:identifier "=" value:identifier !"::" end rule ExtendedAttributeNamedArgList key:identifier "=" value:(name:identifier "(" ws args:ArgumentList ws ")" ) end rule ExtendedAttributeIdentList key:identifier "=" "(" list:IdentifierList ")" end rule IdentifierList identifier Identifiers end rule Identifiers ("," IdentifierList)? end # rule ExtendedAttribute # "(" ws ExtendedAttributeInner ws ")" ws ExtendedAttributeRest # / "[" ws ExtendedAttributeInner ws "]" ws ExtendedAttributeRest # / "{" ws ExtendedAttributeInner ws "}" ws ExtendedAttributeRest # / Other ws ExtendedAttributeRest # end # # rule ExtendedAttributeRest # ExtendedAttribute? # end # # rule ExtendedAttributeInner # ( "(" ws ExtendedAttributeInner ws ")" ws ExtendedAttributeInner # / "[" ws ExtendedAttributeInner ws "]" ws ExtendedAttributeInner # / "{" ws ExtendedAttributeInner ws "}" ws ExtendedAttributeInner # / OtherOrComma ExtendedAttributeInner)? # end # rule Other integer / float / identifier / string / other / "..." / ":" / "::" / ";" / "<" / "=" / ">" / "?" / "false" / "object" / "true" / "any" / "attribute" / "boolean" / "caller" / "const" / "creator" / "deleter" / "double" / "exception" / "float" / "getraises" / "getter" / "implements" / "in" / "interface" / "long" / "module" / "octet" / "omittable" / "optional" / "raises" / "sequence" / "setraises" / "setter" / "short" / "DOMString" / "stringifier" / "typedef" / "unsigned" / "void" end rule OtherOrComma Other / "," end rule Type SingleType / type:UnionType suffix:TypeSuffix end rule SingleType NonAnyType / type:"any" suffix:TypeSuffixStartingWithArray end rule UnionType "(" ws UnionMemberType ws "or" ws UnionMemberType ws UnionMemberTypes ws ")" end rule UnionMemberType NonAnyType / type:UnionType suffix:TypeSuffix / type:"any" suffix:TypeSuffixStartingWithArray end rule UnionMemberTypes ("or" ws UnionMemberType ws UnionMemberTypes)? end rule NonAnyType type:PrimitiveType suffix:TypeSuffix # added: cannot be followed by a NonSpace character, since e.g. DOMStringMap, DOMStringList or other identifiers would break parsing / type:PromiseType Null { def build(parent); type.build(parent); end } / type:"DOMString" suffix:TypeSuffix !NonSpace / "sequence" ws "<" ws type:Type ws ">" null:Null / type:"object" suffix:TypeSuffix / type:"Date" suffix:TypeSuffix / type:identifier suffix:TypeSuffix end rule PromiseType "Promise" ws "<" ws return_type:ReturnType ws ">" { def build(parent) Ast::PromiseType.new(return_type.text_value) end } end rule ConstType PrimitiveType Null / identifier Null end # added: cannot be followed by a NonSpace character, since e.g. DOMStringMap, DOMStringList or other identifiers would break parsing rule PrimitiveType (UnsignedIntegerType / UnrestrictedFloatType / "boolean" !NonSpace / "byte" !NonSpace / "octet" !NonSpace) { def build(parent) Ast::Type.new(parent, text_value) end } end rule Null "?"? end rule TypeSuffix ( array:("[" ws "]") suffix:TypeSuffix / null:"?" suffix:TypeSuffixStartingWithArray )? end rule TypeSuffixStartingWithArray (array:("[" ws "]") ws suffix:TypeSuffix )? end rule UnsignedIntegerType "unsigned" ws IntegerType / IntegerType end rule UnrestrictedFloatType "unrestricted" ws FloatType / FloatType end rule FloatType "float" / "double" end rule IntegerType ("short" / "long" ws OptionalLong) end rule OptionalLong "long"? end rule Array ("[" ws "]")? end rule NonSpace (!' ' .) end rule ReturnType Type / "void" end rule ScopedNameList name:ScopedName ws names:ScopedNames end rule ScopedNames ("," ScopedName ws ScopedNames)? end rule ScopedName AbsoluteScopedName / RelativeScopedName end rule AbsoluteScopedName "::" name:identifier parts:ScopedNameParts end rule RelativeScopedName name:identifier parts:ScopedNameParts end rule ScopedNameParts ("::" ws name:identifier parts:ScopedNameParts)? end rule integer (hexint / octint / decint) { def build() Integer(text_value) end} # TODO: check edge cases end rule hexint "-"? "0" [Xx] [0-9A-Fa-f]+ end rule octint "-"? "0" [0-7]+ end rule decint "-"? [0-9]* end rule float ("-"? ([0-9]+ "." [0-9]* / [0-9]* "." [0-9]+) ([Ee] [+-]? [0-9]+)? / [0-9]+ [Ee] [+-]? [0-9]+) { def build() Float(text_value) end} # TODO: check edge cases end rule identifier [A-Z_a-z] [0-9A-Z_a-z]* { def build(parent = nil) text_value[/^_?(.+)/, 1] end } end rule string # spec only allows double quotes, but single quotes are used in the HTML spec double_quoted_string / single_quoted_string end rule double_quoted_string "\"" data:[^"]* "\"" { def build() data.text_value end } end rule single_quoted_string "'" data:[^']* "'" { def build() data.text_value end } end rule ws ([\t\n\r ]* ((line_comment / block_comment) [\t\n\r ]*)+ / [\t\n\r ]+)? end rule other [^\t\n\r 0-9A-Z_a-z] end rule line_comment "//" (![\n\r] . )* end rule block_comment '/*' (!'*/' . )* '*/' end end # IDL end # Parser end # WebIDL