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,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
|