webidl 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/.autotest +9 -0
  2. data/.document +5 -0
  3. data/.gitignore +5 -0
  4. data/LICENSE +20 -0
  5. data/README.rdoc +31 -0
  6. data/Rakefile +65 -0
  7. data/VERSION +1 -0
  8. data/bin/webidl2ruby +13 -0
  9. data/examples/html5.rb +6 -0
  10. data/lib/webidl.rb +47 -0
  11. data/lib/webidl/ast/argument.rb +25 -0
  12. data/lib/webidl/ast/attribute.rb +29 -0
  13. data/lib/webidl/ast/const.rb +17 -0
  14. data/lib/webidl/ast/exception.rb +15 -0
  15. data/lib/webidl/ast/extended_attribute.rb +14 -0
  16. data/lib/webidl/ast/field.rb +14 -0
  17. data/lib/webidl/ast/implements_statement.rb +14 -0
  18. data/lib/webidl/ast/interface.rb +24 -0
  19. data/lib/webidl/ast/module.rb +17 -0
  20. data/lib/webidl/ast/node.rb +29 -0
  21. data/lib/webidl/ast/operation.rb +43 -0
  22. data/lib/webidl/ast/scoped_name.rb +28 -0
  23. data/lib/webidl/ast/type.rb +20 -0
  24. data/lib/webidl/ast/typedef.rb +15 -0
  25. data/lib/webidl/extensions/string.rb +22 -0
  26. data/lib/webidl/extensions/syntax_node.rb +5 -0
  27. data/lib/webidl/generator.rb +38 -0
  28. data/lib/webidl/generator/ruby_sexp_visitor.rb +118 -0
  29. data/lib/webidl/parse_tree/absolute_scoped_name.rb +11 -0
  30. data/lib/webidl/parse_tree/argument.rb +20 -0
  31. data/lib/webidl/parse_tree/argument_list.rb +14 -0
  32. data/lib/webidl/parse_tree/attribute.rb +17 -0
  33. data/lib/webidl/parse_tree/const.rb +9 -0
  34. data/lib/webidl/parse_tree/definitions.rb +25 -0
  35. data/lib/webidl/parse_tree/exception.rb +15 -0
  36. data/lib/webidl/parse_tree/exception_field.rb +11 -0
  37. data/lib/webidl/parse_tree/extended_attributes.rb +41 -0
  38. data/lib/webidl/parse_tree/implements_statement.rb +20 -0
  39. data/lib/webidl/parse_tree/interface.rb +21 -0
  40. data/lib/webidl/parse_tree/interface_inheritance.rb +11 -0
  41. data/lib/webidl/parse_tree/interface_members.rb +21 -0
  42. data/lib/webidl/parse_tree/module.rb +15 -0
  43. data/lib/webidl/parse_tree/nullable_type.rb +11 -0
  44. data/lib/webidl/parse_tree/operation.rb +30 -0
  45. data/lib/webidl/parse_tree/relative_scoped_name.rb +15 -0
  46. data/lib/webidl/parse_tree/scoped_name_list.rb +16 -0
  47. data/lib/webidl/parse_tree/specials.rb +17 -0
  48. data/lib/webidl/parse_tree/stringifier_attribute_or_operation.rb +16 -0
  49. data/lib/webidl/parse_tree/typedef.rb +11 -0
  50. data/lib/webidl/parser/debug_helper.rb +17 -0
  51. data/lib/webidl/parser/idl.treetop +369 -0
  52. data/spec/ast_spec.rb +218 -0
  53. data/spec/fixtures/empty_interface.idl +3 -0
  54. data/spec/fixtures/empty_module.idl +3 -0
  55. data/spec/fixtures/framework.idl +48 -0
  56. data/spec/fixtures/html5.idl +1440 -0
  57. data/spec/fixtures/interface_with_attribute.idl +7 -0
  58. data/spec/fixtures/interface_with_inheritance.idl +9 -0
  59. data/spec/fixtures/interface_with_members.idl +5 -0
  60. data/spec/fixtures/interface_with_stringifiers.idl +5 -0
  61. data/spec/fixtures/module_with_exception.idl +13 -0
  62. data/spec/fixtures/module_with_implements_statement.idl +6 -0
  63. data/spec/fixtures/module_with_typedef.idl +4 -0
  64. data/spec/fixtures/module_with_xattr_ident.idl +4 -0
  65. data/spec/fixtures/module_with_xattr_named_args.idl +5 -0
  66. data/spec/fixtures/module_with_xattr_no_arg.idl +5 -0
  67. data/spec/fixtures/module_with_xattr_scoped.idl +4 -0
  68. data/spec/fixtures/module_with_xattr_two_args.idl +4 -0
  69. data/spec/fixtures/nested.idl +4 -0
  70. data/spec/fixtures/websocket.idl +19 -0
  71. data/spec/generator_spec.rb +92 -0
  72. data/spec/parser_spec.rb +64 -0
  73. data/spec/spec_helper.rb +45 -0
  74. metadata +161 -0
@@ -0,0 +1,28 @@
1
+ module WebIDL
2
+ module Ast
3
+ class ScopedName < Node
4
+
5
+ attr_reader :name
6
+
7
+ def initialize(parent, name, opts = {})
8
+ super(parent)
9
+
10
+ @name = name
11
+ @relative = !!opts[:relative]
12
+ end
13
+
14
+ def qualified_name
15
+ if relative? && @parent
16
+ "#{@parent.qualified_name}::#{@name}"
17
+ else
18
+ "::#{@name}"
19
+ end
20
+ end
21
+
22
+ def relative?
23
+ @relative
24
+ end
25
+
26
+ end # ScopedName
27
+ end # Ast
28
+ end # WebIDL
@@ -0,0 +1,20 @@
1
+ module WebIDL
2
+ module Ast
3
+ class Type < Node
4
+
5
+ attr_reader :name
6
+
7
+ def initialize(parent, name, opts = {})
8
+ super(parent)
9
+
10
+ @name = name.strip.to_sym
11
+ @nullable = !!opts[:nullable]
12
+ end
13
+
14
+ def nullable?
15
+ @nullable
16
+ end
17
+
18
+ end # Type
19
+ end # Ast
20
+ end # WebIDL
@@ -0,0 +1,15 @@
1
+ module WebIDL
2
+ module Ast
3
+ class TypeDef < Node
4
+
5
+ attr_reader :type, :name
6
+
7
+ def initialize(parent, type, name)
8
+ @parent = parent
9
+ @type = type
10
+ @name = name
11
+ end
12
+
13
+ end # TypeDef
14
+ end # Ast
15
+ end # WebIDL
@@ -0,0 +1,22 @@
1
+ class String
2
+ #
3
+ # Convert from camel case to snake case
4
+ #
5
+ # 'FooBar'.snake_case # => "foo_bar"
6
+ #
7
+
8
+ def snake_case
9
+ gsub(/\B[A-Z][^A-Z]/, '_\&').downcase.gsub(' ', '_')
10
+ end
11
+
12
+ #
13
+ # Convert from snake case to camel case
14
+ #
15
+ # 'foo_bar'.camel_case # => "FooBar"
16
+ #
17
+
18
+ def camel_case
19
+ split('_').map { |e| e.capitalize }.join
20
+ end
21
+
22
+ end
@@ -0,0 +1,5 @@
1
+ class Treetop::Runtime::SyntaxNode
2
+ def any?
3
+ !empty?
4
+ end
5
+ end
@@ -0,0 +1,38 @@
1
+ require "webidl/generator/ruby_sexp_visitor"
2
+ module WebIDL
3
+ class Generator
4
+
5
+ class ParseError < StandardError; end
6
+
7
+ def initialize(visitor = nil)
8
+ @visitor = visitor
9
+ end
10
+
11
+ def generate(str)
12
+ parse_tree = parser.parse(str)
13
+
14
+ if parse_tree.nil?
15
+ raise ParseError, parser.failure_reason
16
+ end
17
+
18
+ ast_defs = parse_tree.build
19
+ strings = ast_defs.map { |definition| ruby2ruby.process definition.accept(visitor) }
20
+ strings.join("\n\n")
21
+ end
22
+
23
+ private
24
+
25
+ def ruby2ruby
26
+ @ruby2ruby ||= Ruby2Ruby.new
27
+ end
28
+
29
+ def parser
30
+ @parser ||= WebIDL::Parser::IDLParser.new
31
+ end
32
+
33
+ def visitor
34
+ @visitor ||= RubySexpVisitor.new
35
+ end
36
+
37
+ end # Generator
38
+ end # WebIDL
@@ -0,0 +1,118 @@
1
+ module WebIDL
2
+ class RubySexpVisitor
3
+
4
+ def visit_module(mod)
5
+ [:block,
6
+ [:module, classify(mod.name),
7
+ [:scope,
8
+ [:block] + mod.definitions.map { |d| d.accept(self) }
9
+ ]
10
+ ]
11
+ ]
12
+ end
13
+
14
+ def visit_interface(intf)
15
+ [:module, classify(intf.name),
16
+ ([:scope, [:block] + intf.inherits.map { |inherit| [:call, nil, :include, [:arglist, inherit.accept(self)]] }] unless intf.inherits.empty?),
17
+ [:scope,
18
+ [:block] + intf.members.map { |m| m.accept(self) }
19
+ ]
20
+ ].compact
21
+ end
22
+
23
+ def visit_exception(ex)
24
+ [:class, classify(ex.name), [:const, :StandardError],
25
+ [:scope,
26
+ [:block] + ex.members.map { |m| m.accept(self)}
27
+ ]
28
+ ]
29
+ end
30
+
31
+ def visit_const(const)
32
+ [:cdecl, const.name, [:lit, const.value]] # FIXME: won't always be literals - need Literal AST node?
33
+ end
34
+
35
+ def visit_field(field)
36
+ [:call, nil, :attr_accessor, [:arglist, [:lit, field.name.to_sym]]]
37
+ end
38
+
39
+ def visit_attribute(attribute)
40
+ func = attribute.readonly? ? :attr_reader : :attr_accessor
41
+ [:call, nil, func, [:arglist, [:lit, attribute.name.snake_case.to_sym]]]
42
+ end
43
+
44
+ def visit_operation(operation)
45
+ if operation.name.empty?
46
+ if operation.setter?
47
+ meth = :[]=
48
+ elsif operation.getter?
49
+ meth = :[]
50
+ elsif operation.creator?
51
+ meth = :initialize
52
+ elsif operation.stringifier?
53
+ meth = :to_s
54
+ elsif operation.deleter?
55
+ meth = :delete!
56
+ else
57
+ raise "no name for operation #{operation.pretty_inspect}"
58
+ end
59
+ else
60
+ meth = operation.name.snake_case
61
+ meth << "=" if operation.setter?
62
+ end
63
+
64
+ [:defn, meth.to_sym,
65
+ [:args] + operation.args.map { |a| a.accept(self) },
66
+ [:scope,
67
+ [:block,
68
+ [:call, nil, :raise,
69
+ [:arglist,
70
+ [:const, :NotImplementedError]
71
+ ]
72
+ ]
73
+ ]
74
+ ]
75
+ ]
76
+ end
77
+
78
+ def visit_argument(argument)
79
+ name = argument.name.snake_case
80
+ arg = argument.variadic? ? "*#{name}" : name
81
+
82
+ arg.to_sym
83
+ end
84
+
85
+ def visit_type_def(typedef)
86
+ # don't care
87
+ end
88
+
89
+ def visit_implements_statement(impls)
90
+ [:module, classify(impls.implementor),
91
+ [:scope,
92
+ [:call, nil, :include,
93
+ [:arglist,
94
+ [:const, classify(impls.implementee)]
95
+ ]
96
+ ]
97
+ ]
98
+ ]
99
+ end
100
+
101
+ def visit_scoped_name(sn)
102
+ [:const, classify(sn.qualified_name)]
103
+ end
104
+
105
+ private
106
+
107
+ def classify(string)
108
+ s = string.to_s
109
+
110
+ if s.include?("::")
111
+ s.split("::").map { |e| classify(e) unless e.empty? }.compact.join("::").to_sym
112
+ else
113
+ "#{s.slice(0,1).upcase}#{s[1..-1]}".to_sym
114
+ end
115
+ end
116
+
117
+ end # RubySexpVisitor
118
+ end # WebIDL
@@ -0,0 +1,11 @@
1
+ module WebIDL
2
+ module ParseTree
3
+ class AbsoluteScopedName < Treetop::Runtime::SyntaxNode
4
+
5
+ def build(parent)
6
+ raise NotImplementedError
7
+ end
8
+
9
+ end # RelativeScopedName
10
+ end # ParseTree
11
+ end # WebIDL
@@ -0,0 +1,20 @@
1
+ module WebIDL
2
+ module ParseTree
3
+ class Argument < Treetop::Runtime::SyntaxNode
4
+
5
+ def build(parent)
6
+ arg = Ast::Argument.new(
7
+ id.build,
8
+ type.build(parent),
9
+ :optional => optional.any?,
10
+ :variadic => variadic.any?
11
+ )
12
+
13
+ arg.extended_attributes = eal.build unless eal.empty?
14
+
15
+ arg
16
+ end
17
+
18
+ end # Argument
19
+ end # ParseTree
20
+ end # WebIDL
@@ -0,0 +1,14 @@
1
+ module WebIDL
2
+ module ParseTree
3
+ class ArgumentList < Treetop::Runtime::SyntaxNode
4
+
5
+ def build(parent)
6
+ list = [arg.build(parent)]
7
+ list += args.build(parent) unless args.empty?
8
+
9
+ list
10
+ end
11
+
12
+ end # ArgumentList
13
+ end # ParseTree
14
+ end # WebIDL
@@ -0,0 +1,17 @@
1
+ module WebIDL
2
+ module ParseTree
3
+ class Attribute < Treetop::Runtime::SyntaxNode
4
+
5
+ def build(parent)
6
+ options = {
7
+ :readonly => readonly.any?,
8
+ :getraises => (getraises.build unless getraises.empty?),
9
+ :setraises => (setraises.build unless setraises.empty?)
10
+ }
11
+
12
+ Ast::Attribute.new parent, type.build(parent), name.build, options
13
+ end
14
+
15
+ end # Attribute
16
+ end # ParseTree
17
+ end # WebIDL
@@ -0,0 +1,9 @@
1
+ module WebIDL
2
+ module ParseTree
3
+ class Const < Treetop::Runtime::SyntaxNode
4
+ def build(parent)
5
+ Ast::Const.new(parent, type.build(parent), name.text_value, const_expr.build)
6
+ end
7
+ end # Const
8
+ end # ParseTree
9
+ end # WebIDL
@@ -0,0 +1,25 @@
1
+ module WebIDL
2
+ module ParseTree
3
+ class Definitions < Treetop::Runtime::SyntaxNode
4
+
5
+ def build(parent = nil)
6
+ return [] if metadef.empty?
7
+
8
+ unless metadef.d.empty?
9
+ definition = metadef.d.build(parent)
10
+ definition.extended_attributes = metadef.eal.build(parent) unless metadef.eal.empty?
11
+ end
12
+
13
+ result = [definition]
14
+ result += metadef.defs.build(parent) unless metadef.defs.empty?
15
+
16
+ if parent
17
+ parent.definitions = result
18
+ end
19
+
20
+ result.compact
21
+ end
22
+
23
+ end # Definitions
24
+ end # ParseTree
25
+ end # WebIDL
@@ -0,0 +1,15 @@
1
+ module WebIDL
2
+ module ParseTree
3
+ class Exception < Treetop::Runtime::SyntaxNode
4
+
5
+ def build(parent)
6
+ ex = Ast::Exception.new(parent, name.text_value)
7
+
8
+ members.build(ex) unless members.empty?
9
+
10
+ ex
11
+ end
12
+
13
+ end # Exception
14
+ end # ParseTree
15
+ end # WebIDL
@@ -0,0 +1,11 @@
1
+ module WebIDL
2
+ module ParseTree
3
+ class ExceptionField < Treetop::Runtime::SyntaxNode
4
+
5
+ def build(parent)
6
+ Ast::Field.new(parent, type.build(parent), id.text_value)
7
+ end
8
+
9
+ end # ExceptionField
10
+ end # ParseTree
11
+ end # WebIDL
@@ -0,0 +1,41 @@
1
+ module WebIDL
2
+ module ParseTree
3
+
4
+ class ExtendedAttributeList < Treetop::Runtime::SyntaxNode
5
+ def build(parent)
6
+ list = [attribute.build(parent)]
7
+ list += attributes.build(parent) unless attributes.empty?
8
+
9
+ list
10
+ end
11
+ end
12
+
13
+ class ExtendedAttributeArgList < Treetop::Runtime::SyntaxNode
14
+ def build(parent)
15
+ unless args.empty?
16
+ arguments = args.build(parent)
17
+ end
18
+ Ast::ExtendedAttribute.new(name.text_value, arguments)
19
+ end
20
+ end
21
+
22
+ class ExtendedAttributeIdent < Treetop::Runtime::SyntaxNode
23
+ def build(parent)
24
+ [key, value].map { |e| e.text_value }
25
+ end
26
+ end
27
+
28
+ class ExtendedAttributeNamedArgList < Treetop::Runtime::SyntaxNode
29
+ def build(parent)
30
+ [key.text_value, value.build(parent)]
31
+ end
32
+ end
33
+
34
+ class ExtendedAttributeScopedName < Treetop::Runtime::SyntaxNode
35
+ def build(parent)
36
+ [key.text_value, scoped_name.build(parent)]
37
+ end
38
+ end
39
+
40
+ end # ParseTree
41
+ end # WebIDL
@@ -0,0 +1,20 @@
1
+ module WebIDL
2
+ module ParseTree
3
+ class ImplementsStatement < Treetop::Runtime::SyntaxNode
4
+
5
+ def build(parent)
6
+ # not sure how this should be handled
7
+ # currently keep a global list of interfaces, find the implementor and put the implementee in its 'implements' list
8
+ # perhaps this is too early to do the resolving?
9
+ implor_name = implementor.build(parent).qualified_name
10
+ implee_name = implementee.build(parent).qualified_name
11
+
12
+ # implor = Ast::Interface.list[implor_name]
13
+ # implee = Ast::Interface.list[implee_name]
14
+
15
+ Ast::ImplementsStatement.new(parent, implor_name, implee_name)
16
+ end
17
+
18
+ end # ImplementsStatement
19
+ end # ParseTree
20
+ end # WebIDL