xdry 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +11 -0
- data/Rakefile +76 -0
- data/VERSION +1 -0
- data/bin/xdry +4 -0
- data/lib/xdry.rb +18 -0
- data/lib/xdry/boxing.rb +212 -0
- data/lib/xdry/generators/ctor_from_field.rb +91 -0
- data/lib/xdry/generators/dealloc.rb +53 -0
- data/lib/xdry/generators/dictionary_coding.rb +129 -0
- data/lib/xdry/generators/field_from_property.rb +20 -0
- data/lib/xdry/generators/property-from-field.rb +22 -0
- data/lib/xdry/generators/storing_constructor.rb +72 -0
- data/lib/xdry/generators/synthesize.rb +25 -0
- data/lib/xdry/generators_support.rb +42 -0
- data/lib/xdry/parsing/driver.rb +106 -0
- data/lib/xdry/parsing/model.rb +272 -0
- data/lib/xdry/parsing/nodes.rb +260 -0
- data/lib/xdry/parsing/parsers.rb +166 -0
- data/lib/xdry/parsing/parts/selectors.rb +95 -0
- data/lib/xdry/parsing/parts/var_types.rb +66 -0
- data/lib/xdry/parsing/pos.rb +75 -0
- data/lib/xdry/parsing/scope_stack.rb +68 -0
- data/lib/xdry/parsing/scopes.rb +61 -0
- data/lib/xdry/parsing/scopes_support.rb +143 -0
- data/lib/xdry/patching/emitter.rb +60 -0
- data/lib/xdry/patching/insertion_points.rb +209 -0
- data/lib/xdry/patching/item_patchers.rb +74 -0
- data/lib/xdry/patching/patcher.rb +139 -0
- data/lib/xdry/run.rb +227 -0
- data/lib/xdry/support/enumerable_additions.rb +35 -0
- data/lib/xdry/support/string_additions.rb +27 -0
- data/lib/xdry/support/symbol_additions.rb +14 -0
- data/site/_config.yml +3 -0
- data/site/_example +9 -0
- data/site/_layouts/default.html +30 -0
- data/site/_plugins/example.rb +16 -0
- data/site/_plugins/highlight_unindent.rb +17 -0
- data/site/index.md +417 -0
- data/site/master.css +94 -0
- data/spec/boxing_spec.rb +80 -0
- data/spec/ctor_from_field_spec.rb +251 -0
- data/spec/dealloc_spec.rb +103 -0
- data/spec/dictionary_coding_spec.rb +132 -0
- data/spec/field_from_prop_spec.rb +72 -0
- data/spec/prop_from_field_spec.rb +39 -0
- data/spec/readme_samples_spec.rb +76 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/synthesize_spec.rb +94 -0
- data/xdry.gemspec +103 -0
- metadata +141 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
|
4
|
+
class VarType
|
5
|
+
def self.parse type_decl
|
6
|
+
type_decl = type_decl.strip
|
7
|
+
case type_decl
|
8
|
+
when /^id$/
|
9
|
+
IdVarType.new
|
10
|
+
when /^(?:unsigned\s+|signed\s+|long\s+)?\w+$/
|
11
|
+
SimpleVarType.new(type_decl.gsub(/\s+/, ' '))
|
12
|
+
when /^(\w+)\s*\*$/
|
13
|
+
class_name = $1
|
14
|
+
PointerVarType.new(class_name)
|
15
|
+
else
|
16
|
+
raise StandardError, "Cannot parse Obj-C type: '#{type_decl}'"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_source_with_space
|
21
|
+
needs_space? ? "#{to_s} " : "#{to_s}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def needs_space?; true; end
|
25
|
+
end
|
26
|
+
|
27
|
+
class IdVarType < VarType
|
28
|
+
def to_s
|
29
|
+
"id"
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_property_retainment_policy; 'assign'; end
|
33
|
+
end
|
34
|
+
|
35
|
+
class SimpleVarType < VarType
|
36
|
+
attr_reader :name
|
37
|
+
|
38
|
+
def initialize name
|
39
|
+
@name = name
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
"#{@name}"
|
44
|
+
end
|
45
|
+
|
46
|
+
def default_property_retainment_policy; ''; end
|
47
|
+
end
|
48
|
+
|
49
|
+
class PointerVarType < VarType
|
50
|
+
attr_reader :name
|
51
|
+
attr_accessor :type_hint
|
52
|
+
|
53
|
+
def initialize name
|
54
|
+
@name = name
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
"#{@name} *"
|
59
|
+
end
|
60
|
+
|
61
|
+
def needs_space?; false; end
|
62
|
+
|
63
|
+
def default_property_retainment_policy; 'retain'; end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
|
4
|
+
class BaseFileRef
|
5
|
+
|
6
|
+
attr_reader :positions
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@positions = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_pos! pos
|
13
|
+
@positions << pos
|
14
|
+
end
|
15
|
+
|
16
|
+
def fixup_positions! after_line_no, offset
|
17
|
+
@positions.each { |pos| pos.fixup! after_line_no, offset }
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
class FileRef < BaseFileRef
|
23
|
+
|
24
|
+
attr_reader :path
|
25
|
+
|
26
|
+
def initialize path
|
27
|
+
super()
|
28
|
+
@path = path
|
29
|
+
end
|
30
|
+
|
31
|
+
def read
|
32
|
+
open(@path) { |f| f.read }
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class TestFileRef < BaseFileRef
|
38
|
+
attr_reader :path
|
39
|
+
|
40
|
+
def initialize path, source
|
41
|
+
super()
|
42
|
+
@path, @source = path, source
|
43
|
+
end
|
44
|
+
|
45
|
+
def read
|
46
|
+
@source
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class Pos
|
51
|
+
attr_reader :file_ref, :line_no
|
52
|
+
attr_accessor :scope_before, :scope_after
|
53
|
+
|
54
|
+
def initialize file_ref, line_no
|
55
|
+
@file_ref = file_ref
|
56
|
+
@line_no = line_no
|
57
|
+
@file_ref.add_pos! self
|
58
|
+
end
|
59
|
+
|
60
|
+
def file_path
|
61
|
+
@file_ref.path
|
62
|
+
end
|
63
|
+
|
64
|
+
def fixup! after_line_no, offset
|
65
|
+
if @line_no > after_line_no
|
66
|
+
@line_no += offset
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
"#{File.basename(@file_ref.path)}:#{@line_no}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
|
4
|
+
class ScopeStack
|
5
|
+
|
6
|
+
attr_accessor :verbose
|
7
|
+
|
8
|
+
def initialize root_scopes
|
9
|
+
@stack = []
|
10
|
+
root_scopes.each { |scope| push(scope) }
|
11
|
+
end
|
12
|
+
|
13
|
+
def parse_line line, eol_comments, indent
|
14
|
+
parse_line_using_parser! line, eol_comments, indent do |node|
|
15
|
+
unless node.nil? # to simply code we allow the parser to yield nil when it cannot parse something
|
16
|
+
# update the scope based on this new node
|
17
|
+
while @current_scope.ends_after? node
|
18
|
+
yield @current_scope, node
|
19
|
+
pop
|
20
|
+
end
|
21
|
+
if subscope = @current_scope.subscope_for(node)
|
22
|
+
# a subscope is added as a child of its parent scope
|
23
|
+
yield @current_scope, subscope
|
24
|
+
subscope.assert_bound!
|
25
|
+
push subscope
|
26
|
+
end
|
27
|
+
# add the new node to the scope we have finally decided on
|
28
|
+
yield @current_scope, node
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def current_scope
|
34
|
+
@current_scope
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parse_line_using_parser! line, eol_comments, indent
|
40
|
+
parsed = false
|
41
|
+
@current_scope.parser.parse_line! line, eol_comments, indent do |node|
|
42
|
+
parsed = true
|
43
|
+
yield node
|
44
|
+
end
|
45
|
+
unless parsed
|
46
|
+
yield NLine.new(line)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def push subscope
|
51
|
+
raise StandardError, "Attempted to push a nil subscope" if subscope.nil?
|
52
|
+
@stack.push subscope
|
53
|
+
update_current_scope
|
54
|
+
end
|
55
|
+
|
56
|
+
def pop
|
57
|
+
@stack.pop
|
58
|
+
update_current_scope
|
59
|
+
end
|
60
|
+
|
61
|
+
def update_current_scope
|
62
|
+
old_scope, @current_scope = @current_scope, @stack[-1]
|
63
|
+
puts "#{old_scope} --> #{@current_scope}" if @verbose
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
|
4
|
+
class SMethodImpl < ChildScope
|
5
|
+
|
6
|
+
parse_using PMethodImpl
|
7
|
+
|
8
|
+
on NMethodEnd, :pop, :store_into => :end_node
|
9
|
+
|
10
|
+
def selector
|
11
|
+
@start_node.selector
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
class SInterfaceFields < ChildScope
|
17
|
+
|
18
|
+
parse_using PInterfaceFields
|
19
|
+
|
20
|
+
on NInterfaceFieldsEnd, :pop, :store_into => :end_node
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class SInterface < ChildScope
|
25
|
+
|
26
|
+
parse_using PInterfaceHeader
|
27
|
+
|
28
|
+
on NEnd, :pop, :store_into => :end_node
|
29
|
+
on NOpeningBrace, :start => SInterfaceFields
|
30
|
+
on SInterfaceFields, :store_into => :fields_scope
|
31
|
+
|
32
|
+
def class_name
|
33
|
+
@start_node.class_name
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class SImplementation < ChildScope
|
39
|
+
|
40
|
+
parse_using PInterfaceHeader
|
41
|
+
|
42
|
+
on NEnd, :pop
|
43
|
+
on NSynthesize, :add_to => :synthesizes
|
44
|
+
on NMethodStart, :start => SMethodImpl
|
45
|
+
|
46
|
+
def class_name
|
47
|
+
@start_node.class_name
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class SFile < Scope
|
53
|
+
|
54
|
+
parse_using PGlobal
|
55
|
+
|
56
|
+
on NInterfaceStart, :start => SInterface
|
57
|
+
on NImplementationStart, :start => SImplementation
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
|
4
|
+
class Scope
|
5
|
+
|
6
|
+
attr_reader :model
|
7
|
+
attr_reader :children
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@children = []
|
11
|
+
self.class.child_collections.each do |var_name|
|
12
|
+
instance_variable_set("@#{var_name}", [])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def bind model
|
17
|
+
raise StandardError, "#{self} already bound to #{@model} when trying to bind to #{model}" unless @model.nil?
|
18
|
+
@model = model
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def assert_bound!
|
23
|
+
raise StandardError, "#{self.class.name} hasn't been bound to a model" if @model.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def parser
|
27
|
+
@parser ||= create_parser
|
28
|
+
end
|
29
|
+
|
30
|
+
def subscope_for node
|
31
|
+
if subscope_class = self.class.child_subscope_table[node.class]
|
32
|
+
subscope_class.new(self, node)
|
33
|
+
else
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def ends_after? node
|
39
|
+
self.class.stop_children.include? node.class
|
40
|
+
end
|
41
|
+
|
42
|
+
def << child
|
43
|
+
raise StandardError, "#{self.class.name} hasn't been bound to a model, but is trying to accept a child #{child}" if @model.nil?
|
44
|
+
@children << child
|
45
|
+
@model << child
|
46
|
+
child_added child
|
47
|
+
end
|
48
|
+
|
49
|
+
def child_added child
|
50
|
+
(self.class.child_action_table[child.class] || []).each { |action| action.call(self, child) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_s
|
54
|
+
"#{self.class.name}:#{@model}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def parent_scopes
|
58
|
+
[]
|
59
|
+
end
|
60
|
+
|
61
|
+
def all_scopes
|
62
|
+
parent_scopes + [self]
|
63
|
+
end
|
64
|
+
|
65
|
+
protected
|
66
|
+
|
67
|
+
def create_parser
|
68
|
+
return self.class.parser_class.new(self) if self.class.parser_class
|
69
|
+
raise StandardError, "#{self.class.name} does not override create_parser"
|
70
|
+
end
|
71
|
+
|
72
|
+
class << self
|
73
|
+
|
74
|
+
attr_reader :parser_class
|
75
|
+
|
76
|
+
def parse_using parser_class
|
77
|
+
@parser_class = parser_class
|
78
|
+
end
|
79
|
+
|
80
|
+
# on NSome, :pop, :store_into => :my_var
|
81
|
+
# on NOther, :start => SOther
|
82
|
+
# on NAwesome, :add_to => :awesomes
|
83
|
+
def on child_class, *args
|
84
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
85
|
+
if args.include? :pop
|
86
|
+
stop_children << child_class
|
87
|
+
end
|
88
|
+
if options[:start]
|
89
|
+
child_subscope_table[child_class] = options[:start]
|
90
|
+
end
|
91
|
+
if var_name = options[:store_into]
|
92
|
+
(child_action_table[child_class] ||= []) << lambda do |instance, child|
|
93
|
+
instance.send(:instance_variable_set, "@#{var_name}", child)
|
94
|
+
end
|
95
|
+
attr_reader :"#{var_name}"
|
96
|
+
end
|
97
|
+
if coll_name = options[:add_to]
|
98
|
+
(child_action_table[child_class] ||= []) << lambda do |instance, child|
|
99
|
+
instance.send(:instance_variable_get, "@#{coll_name}") << child
|
100
|
+
end
|
101
|
+
child_collections << coll_name
|
102
|
+
attr_reader :"#{coll_name}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def stop_children
|
107
|
+
@stop_children ||= []
|
108
|
+
end
|
109
|
+
|
110
|
+
def child_subscope_table
|
111
|
+
@child_subscope_table ||= {}
|
112
|
+
end
|
113
|
+
|
114
|
+
def child_action_table
|
115
|
+
@child_action_table ||= {}
|
116
|
+
end
|
117
|
+
|
118
|
+
def child_collections
|
119
|
+
@child_collections ||= []
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
class ChildScope < Scope
|
127
|
+
|
128
|
+
attr_reader :start_node
|
129
|
+
attr_reader :parent_scope
|
130
|
+
|
131
|
+
def initialize parent_scope, start_node
|
132
|
+
super()
|
133
|
+
@parent_scope = parent_scope
|
134
|
+
@start_node = start_node
|
135
|
+
end
|
136
|
+
|
137
|
+
def parent_scopes
|
138
|
+
parent_scope.all_scopes
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module XDry
|
2
|
+
|
3
|
+
class Emitter
|
4
|
+
attr_reader :lines
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@lines = []
|
8
|
+
@indent = "\t"
|
9
|
+
@current_indent = ""
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.capture
|
13
|
+
emitter = Emitter.new
|
14
|
+
yield emitter
|
15
|
+
return emitter.lines
|
16
|
+
end
|
17
|
+
|
18
|
+
def << line
|
19
|
+
case line
|
20
|
+
when Emitter
|
21
|
+
@lines.push *line.lines.collect { |l| @current_indent + l }
|
22
|
+
when Array
|
23
|
+
@lines.push *line.collect { |l| @current_indent + l }
|
24
|
+
else
|
25
|
+
@lines << @current_indent + line
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def indent
|
30
|
+
prev_indent = @current_indent
|
31
|
+
@current_indent = @current_indent + @indent
|
32
|
+
yield
|
33
|
+
@current_indent = prev_indent
|
34
|
+
end
|
35
|
+
|
36
|
+
def empty?
|
37
|
+
@lines.empty?
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
@lines.collect { |line| line + "\n" }.join("")
|
42
|
+
end
|
43
|
+
|
44
|
+
def block prefix = ''
|
45
|
+
self << "#{prefix} {"
|
46
|
+
self.indent { yield }
|
47
|
+
self << "}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def method decl, &block
|
51
|
+
self << ""
|
52
|
+
self.block "- #{decl}", &block
|
53
|
+
end
|
54
|
+
|
55
|
+
def if condition, &block
|
56
|
+
self.block "if (#{condition})", &block
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|