xdry 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,129 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class DictionaryCoding < Generator
|
6
|
+
id "dict-coding"
|
7
|
+
|
8
|
+
def process_class oclass
|
9
|
+
return unless oclass.attributes.any? { |a| a.persistent? }
|
10
|
+
|
11
|
+
dictionary_var = "dictionary"
|
12
|
+
|
13
|
+
defines_emitter = Emitter.new
|
14
|
+
init_out = Emitter.new
|
15
|
+
repr_out = Emitter.new
|
16
|
+
|
17
|
+
dictionary_var = "dictionary"
|
18
|
+
|
19
|
+
oclass.attributes.select { |a| a.persistent? }.each do |oattr|
|
20
|
+
name, type = oattr.name, oattr.type
|
21
|
+
field_name = oattr.field_name
|
22
|
+
raw_name = "#{name}Raw"
|
23
|
+
capitalized_name = name.capitalized_identifier
|
24
|
+
key_const = "#{capitalized_name}Key"
|
25
|
+
|
26
|
+
type_boxer = Boxing.converter_for type
|
27
|
+
if type_boxer.nil?
|
28
|
+
puts "Persistence not (yet) supported for type #{type}"
|
29
|
+
next
|
30
|
+
end
|
31
|
+
|
32
|
+
defines_emitter << %Q`\#define #{key_const} @"#{capitalized_name}"`
|
33
|
+
|
34
|
+
init_out << %Q`id #{raw_name} = [#{dictionary_var} objectForKey:#{key_const}];`
|
35
|
+
init_out.if "#{raw_name} != nil" do
|
36
|
+
unboxed = type_boxer.unbox_retained(init_out, raw_name, name)
|
37
|
+
init_out << "#{field_name} = #{unboxed};"
|
38
|
+
end
|
39
|
+
|
40
|
+
boxed = type_boxer.box(repr_out, field_name, name)
|
41
|
+
repr_out << %Q`[dictionary setObject:#{boxed} forKey:#{key_const}];`
|
42
|
+
end
|
43
|
+
|
44
|
+
init_code = Emitter.capture do |o|
|
45
|
+
o.method "(id)initWithDictionary:(NSDictionary *)#{dictionary_var}" do
|
46
|
+
o.if "self = [super init]" do
|
47
|
+
end
|
48
|
+
o << "return self;"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
repr_code = Emitter.capture do |o|
|
53
|
+
o.method "(NSDictionary *)dictionaryRepresentation" do
|
54
|
+
o << "NSMutableDictionary *#{dictionary_var} = [NSMutableDictionary dictionary];"
|
55
|
+
o << "return #{dictionary_var};"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
file_scope = oclass.main_implementation.parent_scope
|
60
|
+
define_ip = MultiIP.new(AfterDefineIP.new(file_scope), BeforeImplementationStartIP.new(oclass))
|
61
|
+
define_lines = Emitter.capture do |o|
|
62
|
+
each_persistent_attr(oclass) do |oattr, capitalized_name, key_const, type_boxer|
|
63
|
+
unless file_scope.children.any? { |n| NDefine === n && n.word == key_const }
|
64
|
+
o << %Q`\#define #{key_const} @"#{capitalized_name}"`
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
define_ip.wrap_with_empty_lines_if_last!
|
69
|
+
define_ip.insert @patcher, define_lines
|
70
|
+
|
71
|
+
MethodPatcher.new(patcher, oclass, 'dictionaryRepresentation', ImplementationStartIP.new(oclass), repr_code) do |omethod|
|
72
|
+
impl = omethod.impl
|
73
|
+
ip = BeforeReturnIP.new(impl)
|
74
|
+
|
75
|
+
lines = Emitter.capture do |o|
|
76
|
+
each_persistent_attr(oclass) do |oattr, capitalized_name, key_const, type_boxer|
|
77
|
+
unless impl.children.any? { |n| NLine === n && n.line =~ /\bsetObject:.*(?:\b#{oattr.name}|\b#{oattr.field_name}\b)/ }
|
78
|
+
boxed = type_boxer.box(o, oattr.field_name, oattr.name)
|
79
|
+
o << %Q`[dictionary setObject:#{boxed} forKey:#{key_const}];`
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
ip.insert @patcher, lines unless lines.empty?
|
85
|
+
end
|
86
|
+
|
87
|
+
MethodPatcher.new(patcher, oclass, 'initWithDictionary:', ImplementationStartIP.new(oclass), init_code) do |omethod|
|
88
|
+
impl = omethod.impl
|
89
|
+
ip = InsideConstructorIfSuperIP.new(impl)
|
90
|
+
var_name = impl.start_node.selector_def.var_name_after_keyword('initWithDictionary:')
|
91
|
+
|
92
|
+
lines = Emitter.capture do |o|
|
93
|
+
each_persistent_attr(oclass) do |oattr, capitalized_name, key_const, type_boxer|
|
94
|
+
unless impl.children.any? { |n| NLine === n && n.line =~ /^(?:self\s*.\s*#{oattr.name}|#{oattr.field_name})\s*=/ }
|
95
|
+
raw_name = "#{oattr.name}Raw"
|
96
|
+
o << %Q`id #{raw_name} = [#{var_name} objectForKey:#{key_const}];`
|
97
|
+
o.if "#{raw_name} != nil" do
|
98
|
+
unboxed = type_boxer.unbox_retained(o, raw_name, oattr.name)
|
99
|
+
o << "#{oattr.field_name} = #{unboxed};"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
ip.insert @patcher, lines unless lines.empty?
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def each_persistent_attr oclass
|
110
|
+
oclass.attributes.select { |a| a.persistent? }.each do |oattr|
|
111
|
+
name, type = oattr.name, oattr.type
|
112
|
+
field_name = oattr.field_name
|
113
|
+
capitalized_name = name.capitalized_identifier
|
114
|
+
key_const = "#{capitalized_name}Key"
|
115
|
+
|
116
|
+
type_boxer = Boxing.converter_for type
|
117
|
+
if type_boxer.nil?
|
118
|
+
puts "Persistence not (yet) supported for type #{type}"
|
119
|
+
next
|
120
|
+
end
|
121
|
+
|
122
|
+
yield oattr, capitalized_name, key_const, type_boxer
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class FieldFromProperty < Generator
|
6
|
+
id "field-from-prop"
|
7
|
+
|
8
|
+
def process_attribute oclass, oattr
|
9
|
+
if !oattr.has_field_def? && oattr.type_known? && !oclass.has_method_impl?(oattr.getter_selector)
|
10
|
+
lines = [oattr.new_field_def.to_source]
|
11
|
+
if oclass.main_interface # && oclass.main_interface.fields_scope
|
12
|
+
node = oclass.main_interface.fields_scope.end_node
|
13
|
+
patcher.insert_before node.pos, lines, node.indent + INDENT_STEP
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class PropertyFromField < Generator
|
6
|
+
id "prop-from-field"
|
7
|
+
|
8
|
+
def process_attribute oclass, oattr
|
9
|
+
if !oattr.has_property_def? && oattr.wants_property? && oattr.type_known?
|
10
|
+
pd = oattr.new_property_def
|
11
|
+
|
12
|
+
ip = BeforeInterfaceEndIP.new(oclass)
|
13
|
+
lines = Emitter.capture do |o|
|
14
|
+
o << pd.to_source
|
15
|
+
end
|
16
|
+
ip.insert patcher, [""] + lines + [""]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class StoringConstructor < Generator
|
6
|
+
id "store-ctor"
|
7
|
+
|
8
|
+
def process_class oclass
|
9
|
+
oclass.methods.each do |omethod|
|
10
|
+
case omethod.selector
|
11
|
+
when /^initWith/
|
12
|
+
process_method oclass, omethod
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def process_method oclass, omethod
|
18
|
+
components = omethod.header_or_impl.selector_def.components
|
19
|
+
mapping = {}
|
20
|
+
components.each do |comp|
|
21
|
+
kw = comp.keyword_without_colon
|
22
|
+
oattr = oclass.find_attribute(kw)
|
23
|
+
if oattr.nil? and comp == components.first
|
24
|
+
stripped_kw = kw.gsub(/^initWith/, '')
|
25
|
+
[stripped_kw.downcase, stripped_kw[0..0].downcase + stripped_kw[1..-1]].each do |alt_kw|
|
26
|
+
oattr = oclass.find_attribute(alt_kw)
|
27
|
+
break unless oattr.nil?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
mapping[kw] = oattr unless oattr.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
unless mapping.empty?
|
34
|
+
new_selector_def = CompoundSelectorDef.new(components.collect do |comp|
|
35
|
+
if oattr = mapping[kw = comp.keyword_without_colon]
|
36
|
+
arg_name = comp.arg_name
|
37
|
+
arg_name = oattr.name if arg_name.empty?
|
38
|
+
SelectorComponent.new(comp.keyword, arg_name, comp.type || oattr.type)
|
39
|
+
else
|
40
|
+
comp
|
41
|
+
end
|
42
|
+
end)
|
43
|
+
method_header = NMethodHeader.new(new_selector_def, omethod.ret_type)
|
44
|
+
|
45
|
+
init_out = Emitter.new
|
46
|
+
|
47
|
+
new_selector_def.components.each do |comp|
|
48
|
+
if oattr = mapping[kw = comp.keyword_without_colon]
|
49
|
+
field_name = oattr.field_name
|
50
|
+
arg_name = comp.arg_name
|
51
|
+
type = comp.type
|
52
|
+
retain_policy = Boxing.retain_policy_of(type)
|
53
|
+
|
54
|
+
retained = retain_policy.retain(arg_name)
|
55
|
+
init_out << "#{field_name} = #{retained};"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# out << "#{method_header};"
|
60
|
+
# out.block "#{method_header}" do
|
61
|
+
# out.if "self = [super init]" do
|
62
|
+
# out << init_out
|
63
|
+
# end
|
64
|
+
# out << "return self;"
|
65
|
+
# end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
module Generators
|
4
|
+
|
5
|
+
class Synthesize < Generator
|
6
|
+
id "synth"
|
7
|
+
|
8
|
+
def process_attribute oclass, oattr
|
9
|
+
if oattr.has_property_def? && !oattr.has_synthesize? && !oclass.has_method_impl?(oattr.getter_selector)
|
10
|
+
synthesize = oattr.new_synthesize
|
11
|
+
impl = oclass.main_implementation
|
12
|
+
new_lines = [synthesize.to_s]
|
13
|
+
if impl.synthesizes.empty?
|
14
|
+
pos = impl.start_node.pos
|
15
|
+
new_lines = [""] + new_lines + [""]
|
16
|
+
else
|
17
|
+
pos = impl.synthesizes.sort { |a, b| a.pos.line_no <=> b.pos.line_no }.last.pos
|
18
|
+
end
|
19
|
+
patcher.insert_after pos, new_lines
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
module XDry::Generators
|
3
|
+
|
4
|
+
ALL = []
|
5
|
+
|
6
|
+
Emitter = XDry::Emitter
|
7
|
+
Boxing = XDry::Boxing
|
8
|
+
|
9
|
+
class Generator
|
10
|
+
|
11
|
+
attr_reader :patcher
|
12
|
+
|
13
|
+
def initialize config, patcher
|
14
|
+
@config = config
|
15
|
+
@patcher = patcher
|
16
|
+
end
|
17
|
+
|
18
|
+
def verbose?
|
19
|
+
@config.verbose
|
20
|
+
end
|
21
|
+
|
22
|
+
def process_class oclass
|
23
|
+
oclass.attributes.each do |oattr|
|
24
|
+
process_attribute oclass, oattr
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def process_attribute oclass, oattr
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.id(value=nil)
|
32
|
+
@id = value if value
|
33
|
+
@id
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.inherited subclass
|
37
|
+
ALL << subclass
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'generator'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module XDry
|
5
|
+
|
6
|
+
class ParsingDriver
|
7
|
+
attr_accessor :verbose
|
8
|
+
attr_reader :markers
|
9
|
+
|
10
|
+
def initialize oglobal
|
11
|
+
@oglobal = oglobal
|
12
|
+
@verbose = false
|
13
|
+
@markers = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse_file file_name
|
17
|
+
gen = Generator.new { |g|
|
18
|
+
file_ref = FileRef.new(file_name)
|
19
|
+
File.open(file_name) do |file|
|
20
|
+
new_lines_generator g, file_ref, file
|
21
|
+
end
|
22
|
+
}
|
23
|
+
parse_data_in_file_scope gen
|
24
|
+
end
|
25
|
+
|
26
|
+
def parse_string file_name, source
|
27
|
+
gen = Generator.new { |g|
|
28
|
+
file_ref = TestFileRef.new(file_name, source)
|
29
|
+
new_lines_generator g, file_ref, StringIO.new(source)
|
30
|
+
}
|
31
|
+
parse_data_in_file_scope gen
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse_fragment file_ref, lines, start_lineno, start_scope
|
35
|
+
gen = Generator.new { |g|
|
36
|
+
new_lines_from_array_generator g, file_ref, start_lineno, lines
|
37
|
+
}
|
38
|
+
parse_data_in_scopes gen, start_scope.all_scopes
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def parse_data_in_file_scope gen
|
44
|
+
parse_data_in_scopes gen, [@oglobal.new_file_scope]
|
45
|
+
end
|
46
|
+
|
47
|
+
def parse_data_in_scopes gen, scopes
|
48
|
+
scope_stack = ScopeStack.new(scopes)
|
49
|
+
scope_stack.verbose = @verbose
|
50
|
+
while gen.next?
|
51
|
+
orig_line, line, pos, eol_comments, indent = gen.next
|
52
|
+
puts " #{pos} #{orig_line}" if @verbose
|
53
|
+
pos.scope_before = scope_stack.current_scope
|
54
|
+
scope_stack.parse_line line, eol_comments, indent do |scope, child|
|
55
|
+
# child is a Node or a Scope
|
56
|
+
if child.is_a? Node
|
57
|
+
child.pos = pos
|
58
|
+
child.indent = indent
|
59
|
+
end
|
60
|
+
if child.is_a? NMarker
|
61
|
+
@markers << child
|
62
|
+
end
|
63
|
+
|
64
|
+
puts "#{scope} << #{child}" if @verbose
|
65
|
+
scope << child
|
66
|
+
end
|
67
|
+
pos.scope_after = scope_stack.current_scope
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def new_lines_generator g, file_ref, io
|
72
|
+
line_no = 0
|
73
|
+
io.each_line do |line|
|
74
|
+
line_no += 1
|
75
|
+
orig_line, line, eol_comments, indent = split_line(line)
|
76
|
+
g.yield [orig_line, line, Pos.new(file_ref, line_no), eol_comments, indent]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def new_lines_from_array_generator g, file_ref, start_lineno, lines
|
81
|
+
line_no = start_lineno - 1
|
82
|
+
lines.each do |line|
|
83
|
+
line_no += 1
|
84
|
+
orig_line, line, eol_comments, indent = split_line(line.dup)
|
85
|
+
g.yield [orig_line, line, Pos.new(file_ref, line_no), eol_comments, indent]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def split_line line
|
90
|
+
orig_line = line.dup
|
91
|
+
line.strip!
|
92
|
+
|
93
|
+
# strip end-of-line comments, but keep comment lines
|
94
|
+
eol_comments = ''
|
95
|
+
line_without_comments = line.sub(%r`//.*$`) { eol_comments = $&; '' }
|
96
|
+
unless line_without_comments.empty?
|
97
|
+
line = line_without_comments
|
98
|
+
end
|
99
|
+
|
100
|
+
indent = if orig_line =~ /^(\s+)/ then $1 else '' end
|
101
|
+
return [orig_line, line, eol_comments, indent]
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
|
2
|
+
module XDry
|
3
|
+
|
4
|
+
class OGlobal
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@names_to_classes = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def classes
|
11
|
+
@names_to_classes.values
|
12
|
+
end
|
13
|
+
|
14
|
+
def new_file_scope
|
15
|
+
SFile.new.bind(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def << child
|
19
|
+
case child
|
20
|
+
when SInterface
|
21
|
+
lookup_class(child.class_name).add_interface child
|
22
|
+
when SImplementation
|
23
|
+
lookup_class(child.class_name).add_implementation child
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def lookup_class name
|
30
|
+
@names_to_classes[name] ||= OClass.new(self, name)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class OClass
|
35
|
+
attr_reader :oglobal
|
36
|
+
attr_reader :name, :field_defs, :attributes, :methods
|
37
|
+
attr_reader :interfaces
|
38
|
+
attr_reader :implementations
|
39
|
+
|
40
|
+
def initialize oglobal, name
|
41
|
+
@oglobal, @name = oglobal, name
|
42
|
+
|
43
|
+
@names_to_attributes = {}
|
44
|
+
@attributes = []
|
45
|
+
|
46
|
+
@selectors_to_methods = {}
|
47
|
+
@methods = []
|
48
|
+
|
49
|
+
@field_defs = []
|
50
|
+
@property_defs = []
|
51
|
+
@interfaces = []
|
52
|
+
@implementations = []
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_interface child
|
56
|
+
@interfaces << child.bind(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_implementation child
|
60
|
+
@implementations << child.bind(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
def main_interface
|
64
|
+
# FIXME
|
65
|
+
@interfaces.first
|
66
|
+
end
|
67
|
+
|
68
|
+
def main_implementation
|
69
|
+
# FIXME
|
70
|
+
@implementations.first
|
71
|
+
end
|
72
|
+
|
73
|
+
def << node
|
74
|
+
case node
|
75
|
+
when NFieldDef
|
76
|
+
@field_defs << node
|
77
|
+
attr_name = node.name.gsub /^_/, ''
|
78
|
+
lookup_attribute(attr_name).add_field_def! node
|
79
|
+
when NPropertyDef
|
80
|
+
@property_defs << node
|
81
|
+
attr_name = node.name
|
82
|
+
lookup_attribute(attr_name).add_property_def! node
|
83
|
+
when NMethodHeader
|
84
|
+
selector = node.selector
|
85
|
+
lookup_method(selector).add_method_header! node
|
86
|
+
when NSynthesize
|
87
|
+
node.items.each do |item|
|
88
|
+
lookup_attribute(item.property_name).add_synthesize! node
|
89
|
+
end
|
90
|
+
when SInterfaceFields
|
91
|
+
node.bind(self)
|
92
|
+
when SMethodImpl
|
93
|
+
lookup_method(node.selector).add_method_impl! node
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_s
|
98
|
+
"class #{name}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def find_attribute name
|
102
|
+
@names_to_attributes[name]
|
103
|
+
end
|
104
|
+
|
105
|
+
def find_method selector
|
106
|
+
@selectors_to_methods[selector]
|
107
|
+
end
|
108
|
+
|
109
|
+
def has_method_impl? selector
|
110
|
+
m = find_method(selector)
|
111
|
+
return m && m.has_impl?
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def lookup_attribute name
|
117
|
+
@names_to_attributes[name] ||= create_attribute(name)
|
118
|
+
end
|
119
|
+
|
120
|
+
def lookup_method selector
|
121
|
+
@selectors_to_methods[selector] ||= create_method(selector)
|
122
|
+
end
|
123
|
+
|
124
|
+
def create_attribute name
|
125
|
+
a = OAttribute.new(self, name)
|
126
|
+
@attributes << a
|
127
|
+
return a
|
128
|
+
end
|
129
|
+
|
130
|
+
def create_method selector
|
131
|
+
a = OMethod.new(selector)
|
132
|
+
@methods << a
|
133
|
+
return a
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
class OAttribute
|
139
|
+
attr_reader :name
|
140
|
+
attr_reader :field_def
|
141
|
+
|
142
|
+
def initialize oclass, name
|
143
|
+
@oclass, @name = oclass, name
|
144
|
+
|
145
|
+
@field_def = nil
|
146
|
+
@property_def = nil
|
147
|
+
@synthesizes = []
|
148
|
+
end
|
149
|
+
|
150
|
+
def field_name
|
151
|
+
if @field_def
|
152
|
+
@field_def.name
|
153
|
+
else
|
154
|
+
FIELD_PREFIX + name
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def add_field_def! field_def
|
159
|
+
@field_def = field_def
|
160
|
+
end
|
161
|
+
|
162
|
+
def add_property_def! property_def
|
163
|
+
@property_def = property_def
|
164
|
+
end
|
165
|
+
|
166
|
+
def add_synthesize! synthesize
|
167
|
+
@synthesizes << synthesize
|
168
|
+
end
|
169
|
+
|
170
|
+
def has_field_def?
|
171
|
+
not @field_def.nil?
|
172
|
+
end
|
173
|
+
|
174
|
+
def has_property_def?
|
175
|
+
not @property_def.nil?
|
176
|
+
end
|
177
|
+
|
178
|
+
def has_synthesize?
|
179
|
+
not @synthesizes.empty?
|
180
|
+
end
|
181
|
+
|
182
|
+
def new_property_def
|
183
|
+
NPropertyDef.new(name, type)
|
184
|
+
end
|
185
|
+
|
186
|
+
def new_field_def
|
187
|
+
NFieldDef.new(field_name, type)
|
188
|
+
end
|
189
|
+
|
190
|
+
def new_synthesize
|
191
|
+
NSynthesize.new([SynthesizeItem.new(name, (field_name == name ? nil : field_name) )])
|
192
|
+
end
|
193
|
+
|
194
|
+
def persistent?
|
195
|
+
@field_def && @field_def.persistent?
|
196
|
+
end
|
197
|
+
|
198
|
+
def type_known?
|
199
|
+
not type.nil?
|
200
|
+
end
|
201
|
+
|
202
|
+
def wants_property?
|
203
|
+
has_field_def? && field_def.wants_property?
|
204
|
+
end
|
205
|
+
|
206
|
+
def wants_constructor?
|
207
|
+
has_field_def? && field_def.wants_constructor?
|
208
|
+
end
|
209
|
+
|
210
|
+
def getter_selector
|
211
|
+
# FIXME: need to account for a possible selector override declared in @property
|
212
|
+
@name
|
213
|
+
end
|
214
|
+
|
215
|
+
def type
|
216
|
+
if @property_def
|
217
|
+
@property_def.type
|
218
|
+
elsif @field_def
|
219
|
+
@field_def.type
|
220
|
+
else
|
221
|
+
nil
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def to_s
|
226
|
+
traits = []
|
227
|
+
traits << field_name if has_field_def?
|
228
|
+
"#{type} #{name}" + (traits.empty? ? "" : " (" + traits.join(", ") + ")")
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class OMethod
|
233
|
+
attr_reader :selector, :header, :impl
|
234
|
+
|
235
|
+
def initialize selector
|
236
|
+
@selector = selector
|
237
|
+
end
|
238
|
+
|
239
|
+
def add_method_header! method_header
|
240
|
+
@header = method_header
|
241
|
+
end
|
242
|
+
|
243
|
+
def add_method_impl! method_impl
|
244
|
+
@impl = method_impl.bind(self)
|
245
|
+
end
|
246
|
+
|
247
|
+
def has_header?
|
248
|
+
not @header.nil?
|
249
|
+
end
|
250
|
+
|
251
|
+
def has_impl?
|
252
|
+
not @impl.nil?
|
253
|
+
end
|
254
|
+
|
255
|
+
def ret_type
|
256
|
+
header_or_impl.ret_type
|
257
|
+
end
|
258
|
+
|
259
|
+
def << child
|
260
|
+
end
|
261
|
+
|
262
|
+
def to_s
|
263
|
+
header_or_impl.to_s
|
264
|
+
end
|
265
|
+
|
266
|
+
def header_or_impl
|
267
|
+
@header || @impl.start_node
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
|
272
|
+
end
|