wrapture 0.2.2 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a6c087443d51b13a641bf0810c398c073415422de2a3a5cc715bc75ab265230
4
- data.tar.gz: a2d2175fe6d0ffbdc5e1a878fda7c9ed3db8dd8d0ae8d2c88b143167bf76b0a8
3
+ metadata.gz: fb5e341af7e97543a1ee20d0a4d962f461372c5ef948de704c1508e346ca71c7
4
+ data.tar.gz: af4cf7f89111e459a866107ebc46823b81d6a4de0bbdd944a3b5294dc0ad9220
5
5
  SHA512:
6
- metadata.gz: e722abc8af3af03e5e98189c034a3d60df5f1814776d127cee8be5bebdc9d87a1226f4b305e1b083da38c03a9ad239e98e1572b75f1b5bb9dd4d5bc5e93a95ea
7
- data.tar.gz: e26c2a12e04af2b22172de83b31246846f6a73780a46987604748da9232b9191f5204c94ead1b3016db55b8c6b29e2d1e193629abfee0a1a18bd63d592e97f9f
6
+ metadata.gz: 9a3e8e31db51f492ab179bc047de225cd7db2ceb7156c1734ce45929cbce325f38abd40d54637c87839d0b0cc2bbf41ec8cf491d128e0a61a01aaafa724c6ef3
7
+ data.tar.gz: eb13f2975a79f0caf669b3a72408e9af1c61155598ce516c43d406e2b95f3016550724e2efe35766da6ebec0695d8052c2fa9b2149cedad87d0c89eab7672e44
@@ -5,9 +5,14 @@
5
5
  require 'yaml'
6
6
  require 'wrapture'
7
7
 
8
- spec = YAML.load_file ARGV[0]
8
+ scope = Wrapture::Scope.new
9
9
 
10
- spec['classes'].each do |spec_hash|
11
- class_spec = Wrapture::ClassSpec.new spec_hash
12
- class_spec.generate_wrappers
10
+ ARGV.each do |spec_file|
11
+ spec = YAML.load_file(spec_file)
12
+
13
+ spec['classes'].each do |class_spec|
14
+ Wrapture::ClassSpec.new(class_spec, scope: scope)
15
+ end
13
16
  end
17
+
18
+ scope.generate_wrappers
@@ -1,10 +1,33 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
1
3
  # frozen_string_literal: true
2
4
 
3
- ##
5
+ # Copyright 2019 Joel E. Anderson
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
4
19
  # Classes and functions for generating language wrappers
5
20
  module Wrapture
21
+ require 'wrapture/action_spec'
6
22
  require 'wrapture/constant_spec'
23
+ require 'wrapture/constants'
7
24
  require 'wrapture/class_spec'
25
+ require 'wrapture/errors'
8
26
  require 'wrapture/function_spec'
27
+ require 'wrapture/normalize'
28
+ require 'wrapture/rule_spec'
29
+ require 'wrapture/scope'
30
+ require 'wrapture/struct_spec'
9
31
  require 'wrapture/version'
32
+ require 'wrapture/wrapped_function_spec'
10
33
  end
@@ -0,0 +1,81 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ # frozen_string_literal: true
4
+
5
+ # Copyright 2020 Joel E. Anderson
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require 'wrapture/constants'
20
+ require 'wrapture/errors'
21
+
22
+ module Wrapture
23
+ # An action to take within a generated program.
24
+ class ActionSpec
25
+ # Normalizes a hash specification of a rule. Normalization checks for
26
+ # invalid keys and unrecognized conditions.
27
+ def self.normalize_spec_hash(spec)
28
+ normalized = spec.dup
29
+
30
+ required_keys = %w[name constructor]
31
+
32
+ missing_keys = required_keys - spec.keys
33
+ unless missing_keys.empty?
34
+ missing_msg = "required keys are missing: #{missing_keys.join(', ')}"
35
+ raise(MissingSpecKey, missing_msg)
36
+ end
37
+
38
+ extra_keys = spec.keys - required_keys
39
+ unless extra_keys.empty?
40
+ extra_msg = "these keys are unrecognized: #{extra_keys.join(', ')}"
41
+ raise(InvalidSpecKey, extra_msg)
42
+ end
43
+
44
+ includes = Wrapture.normalize_includes(spec['constructor']['includes'])
45
+ normalized['constructor']['includes'] = includes
46
+
47
+ normalized
48
+ end
49
+
50
+ # Creates an action spec based on the provided spec hash.
51
+ #
52
+ # The hash must have the following keys:
53
+ # name:: the type of action to take (currently only throw-exception is
54
+ # supported)
55
+ # constructor:: the function to use to create the exception, described as a
56
+ # wrapped function call
57
+ def initialize(spec)
58
+ @spec = ActionSpec.normalize_spec_hash(spec)
59
+ end
60
+
61
+ # A list of includes needed for the action.
62
+ def includes
63
+ @spec['constructor']['includes'].dup
64
+ end
65
+
66
+ # A string containing the invocation of this action.
67
+ def take
68
+ call_spec = @spec['constructor']
69
+
70
+ params = call_spec['params'].map do |param_spec|
71
+ if param_spec['value'] == RETURN_VALUE_KEYWORD
72
+ 'return_val'
73
+ else
74
+ param_spec['value']
75
+ end
76
+ end
77
+
78
+ "throw #{call_spec['name']}( #{params.join(', ')} )"
79
+ end
80
+ end
81
+ end
@@ -1,203 +1,214 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
1
3
  # frozen_string_literal: true
2
4
 
5
+ # Copyright 2019-2020 Joel E. Anderson
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
3
19
  require 'wrapture/constant_spec'
20
+ require 'wrapture/constants'
4
21
  require 'wrapture/function_spec'
22
+ require 'wrapture/normalize'
5
23
 
6
24
  module Wrapture
7
- ##
8
25
  # A description of a class, including its constants, functions, and other
9
26
  # details.
10
27
  class ClassSpec
11
- def self.typed_variable(type, name)
12
- if type.end_with? '*'
13
- "#{type}#{name}"
14
- else
15
- "#{type} #{name}"
16
- end
17
- end
28
+ # Normalizes a hash specification of a class. Normalization will check for
29
+ # things like invalid keys, duplicate entries in include lists, and will set
30
+ # missing keys to their default values (for example, an empty list if no
31
+ # includes are given).
32
+ #
33
+ # If this spec cannot be normalized, for example because it is invalid or
34
+ # it uses an unsupported version type, then an exception is raised.
35
+ def self.normalize_spec_hash(spec)
36
+ raise NoNamespace unless spec.key?('namespace')
18
37
 
19
- def initialize(spec)
20
- @spec = ClassSpec.normalize_spec_hash(spec)
38
+ normalized = spec.dup
39
+ normalized.default = []
21
40
 
22
- @functions = []
23
- @spec['functions'].each do |function_spec|
24
- @functions << FunctionSpec.new(function_spec, self)
25
- end
41
+ normalized['version'] = Wrapture.spec_version(spec)
42
+ normalized['includes'] = Wrapture.normalize_includes(spec['includes'])
43
+ normalized['type'] = ClassSpec.effective_type(normalized)
26
44
 
27
- @constants = []
28
- @spec['constants'].each do |constant_spec|
29
- @constants << ConstantSpec.new(constant_spec)
45
+ if spec.key?('parent')
46
+ includes = Wrapture.normalize_includes(spec['parent']['includes'])
47
+ normalized['parent']['includes'] = includes
30
48
  end
31
- end
32
49
 
33
- def generate_wrappers
34
- files = []
35
- files << generate_declaration_file
36
- files << generate_definition_file
50
+ normalized
37
51
  end
38
52
 
39
- def resolve_param(param)
40
- case param
41
- when 'equivalent-struct'
42
- equivalent_struct
43
- when 'equivalent-struct-pointer'
44
- equivalent_struct_pointer
45
- else
46
- param
53
+ # Gives the effective type of the given class spec hash.
54
+ def self.effective_type(spec)
55
+ inferred_pointer_wrapper = spec['constructors'].any? do |func|
56
+ func['wrapped-function']['return']['type'] == EQUIVALENT_POINTER_KEYWORD
47
57
  end
48
- end
49
58
 
50
- def function_call(spec)
51
- resolved_params = []
59
+ if spec.key?('type')
60
+ valid_types = %w[pointer struct]
61
+ unless valid_types.include?(spec['type'])
62
+ type_message = "#{spec['type']} is not a valid class type"
63
+ raise InvalidSpecKey.new(type_message, valid_keys: valid_types)
64
+ end
52
65
 
53
- spec['params'].each do |param|
54
- resolved_params << resolve_param(param['name'])
66
+ spec['type']
67
+ elsif inferred_pointer_wrapper
68
+ 'pointer'
69
+ else
70
+ 'struct'
55
71
  end
56
-
57
- "#{spec['name']}( #{resolved_params.join ', '} )"
58
72
  end
59
73
 
60
- def self.normalize_spec_hash(spec)
61
- normalized_spec = spec.dup
62
- normalized_spec.default = []
63
-
64
- normalized_spec['equivalent-struct']['members'] ||= []
65
- normalized_spec['equivalent-struct']['includes'] ||= []
66
-
67
- normalized_spec
74
+ # Returns a string of the variable with it's type, properly formatted.
75
+ def self.typed_variable(type, name)
76
+ "#{type}#{' ' unless type.end_with?('*')}#{name}"
68
77
  end
69
78
 
70
- private
71
-
72
- def header_guard
73
- "__#{@spec['name'].upcase}_HPP"
74
- end
79
+ # The underlying struct of this class.
80
+ attr_reader :struct
81
+
82
+ # Creates a class spec based on the provided hash spec.
83
+ #
84
+ # The scope can be provided if available.
85
+ #
86
+ # The hash must have the following keys:
87
+ # name:: the name of the class
88
+ # namespace:: the namespace to put the class into
89
+ # equivalent-struct:: a hash describing the struct this class wraps
90
+ #
91
+ # The following keys are optional:
92
+ # constructors:: a list of function specs that can create this class
93
+ # destructor:: a function spec for the destructor of the class
94
+ # functions:: a list of function specs
95
+ # constants:: a list of constant specs
96
+ def initialize(spec, scope: Scope.new)
97
+ @spec = ClassSpec.normalize_spec_hash(spec)
75
98
 
76
- def declaration_includes
77
- includes = @spec['equivalent-struct']['includes'].dup
99
+ @struct = StructSpec.new @spec[EQUIVALENT_STRUCT_KEYWORD]
78
100
 
79
- @functions.each do |func|
80
- includes.concat func.declaration_includes
81
- end
101
+ @functions = @spec['constructors'].map do |constructor_spec|
102
+ full_spec = constructor_spec.dup
103
+ full_spec['name'] = @spec['name']
104
+ full_spec['params'] = constructor_spec['wrapped-function']['params']
82
105
 
83
- @constants.each do |const|
84
- includes.concat const.declaration_includes
106
+ FunctionSpec.new(full_spec, self, constructor: true)
85
107
  end
86
108
 
87
- includes.uniq
88
- end
109
+ if @spec.key?('destructor')
110
+ destructor_spec = @spec['destructor'].dup
111
+ destructor_spec['name'] = "~#{@spec['name']}"
89
112
 
90
- def definition_includes
91
- includes = ["#{@spec['name']}.hpp"]
113
+ @functions << FunctionSpec.new(destructor_spec, self, destructor: true)
114
+ end
92
115
 
93
- @functions.each do |func|
94
- includes.concat func.definition_includes
116
+ @spec['functions'].each do |function_spec|
117
+ @functions << FunctionSpec.new(function_spec, self)
95
118
  end
96
119
 
97
- @constants.each do |const|
98
- includes.concat const.definition_includes
120
+ @constants = @spec['constants'].map do |constant_spec|
121
+ ConstantSpec.new(constant_spec)
99
122
  end
100
123
 
101
- includes.uniq
124
+ scope << self
125
+ @scope = scope
102
126
  end
103
127
 
104
- def pointer_wrapper?
105
- @spec['constructors'].each do |constructor_spec|
106
- return_type = constructor_spec['wrapped-function']['return']['type']
128
+ # Returns a cast of an instance of this class to the provided type, if
129
+ # possible.
130
+ def cast_to(name, type)
131
+ struct = "struct #{@struct.name}"
107
132
 
108
- return true if return_type == 'equivalent-struct-pointer'
133
+ if [EQUIVALENT_STRUCT_KEYWORD, struct].include?(type)
134
+ equivalent_struct(name)
135
+ elsif [EQUIVALENT_POINTER_KEYWORD, "#{struct} *"].include?(type)
136
+ equivalent_struct_pointer(name)
109
137
  end
110
-
111
- false
112
138
  end
113
139
 
114
- def equivalent_member(member)
115
- if pointer_wrapper?
116
- "this->equivalent->#{member}"
117
- else
118
- "this->equivalent.#{member}"
119
- end
140
+ # The equivalent struct of this class from an instance of it.
141
+ def equivalent_struct(instance_name)
142
+ "#{'*' if pointer_wrapper?}#{instance_name}.equivalent"
120
143
  end
121
144
 
122
- def equivalent_name
123
- if pointer_wrapper?
124
- '*equivalent'
125
- else
126
- 'equivalent'
127
- end
145
+ # A pointer to the equivalent struct of this class from an instance of it.
146
+ def equivalent_struct_pointer(instance_name)
147
+ "#{'&' unless pointer_wrapper?}#{instance_name}.equivalent"
128
148
  end
129
149
 
130
- def equivalent_struct
131
- if pointer_wrapper?
132
- '*(this->equivalent)'
133
- else
134
- 'this->equivalent'
135
- end
150
+ # Generates the wrapper class declaration and definition files.
151
+ def generate_wrappers
152
+ [generate_declaration_file, generate_definition_file]
136
153
  end
137
154
 
138
- def equivalent_struct_pointer
139
- if pointer_wrapper?
140
- 'this->equivalent'
141
- else
142
- '&this->equivalent'
143
- end
155
+ # The name of the class
156
+ def name
157
+ @spec['name']
144
158
  end
145
159
 
146
- def wrapped_constructor_signature(index)
147
- function_spec = @spec['constructors'][index]['wrapped-function']
148
-
149
- "#{@spec['name']}( #{FunctionSpec.param_list function_spec} )"
160
+ # True if this class overloads the given one. A class is considered an
161
+ # overload if its parent is the given class, it has the same equivalent
162
+ # struct name, and the equivalent struct has a set of rules. The overloaded
163
+ # class cannot have any rules in its equivalent struct, or it will not be
164
+ # overloaded.
165
+ def overloads?(parent_spec)
166
+ return false unless parent_spec.struct.rules.empty?
167
+
168
+ parent_spec.struct.name == struct_name &&
169
+ parent_spec.name == parent_name &&
170
+ !@struct.rules.empty?
150
171
  end
151
172
 
152
- def destructor_signature
153
- "~#{@spec['name']}( void )"
173
+ # The name of the parent of this class, or nil if there is no parent.
174
+ def parent_name
175
+ @spec['parent']['name'] if @spec.key?('parent')
154
176
  end
155
177
 
156
- def wrapped_constructor_definition(index)
157
- constructor_spec = @spec['constructors'][index]
158
- wrapped_function = constructor_spec['wrapped-function']
159
-
160
- yield "#{@spec['name']}::#{wrapped_constructor_signature(index)}{"
161
-
162
- result = resolve_param wrapped_function['return']['type']
163
-
164
- yield " #{result} = #{function_call wrapped_function};"
165
-
166
- yield '}'
178
+ # The name of the equivalent struct of this class.
179
+ def struct_name
180
+ @struct.name
167
181
  end
168
182
 
169
- def member_constructor_signature
170
- params = []
171
-
172
- @spec['equivalent-struct']['members'].each do |member|
173
- params << ClassSpec.typed_variable(member['type'], member['name'])
183
+ # Gives a code snippet that accesses the equivalent struct from within the
184
+ # class using the 'this' keyword.
185
+ def this_struct
186
+ if pointer_wrapper?
187
+ '*(this->equivalent)'
188
+ else
189
+ 'this->equivalent'
174
190
  end
175
-
176
- "#{@spec['name']}( #{params.join ', '} )"
177
191
  end
178
192
 
179
- def struct_constructor_signature
180
- struct_name = @spec['equivalent-struct']['name']
181
- "#{@spec['name']}( struct #{struct_name} equivalent )"
193
+ # Gives a code snippet that accesses the equivalent struct pointer from
194
+ # within the class using the 'this' keyword.
195
+ def this_struct_pointer
196
+ "#{'&' unless pointer_wrapper?}this->equivalent"
182
197
  end
183
198
 
184
- def pointer_constructor_signature
185
- struct_name = @spec['equivalent-struct']['name']
186
- "#{@spec['name']}( struct #{struct_name} *equivalent )"
199
+ # Returns the ClassSpec for the given type in this class's scope.
200
+ def type(type)
201
+ @scope.type(type)
187
202
  end
188
203
 
189
- def generate_declaration_file
190
- filename = "#{@spec['name']}.hpp"
191
-
192
- File.open(filename, 'w') do |file|
193
- declaration_contents do |line|
194
- file.puts line
195
- end
196
- end
197
-
198
- filename
204
+ # Returns true if the given type exists in this class's scope.
205
+ def type?(type)
206
+ @scope.type?(type)
199
207
  end
200
208
 
209
+ private
210
+
211
+ # Gives the content of the class declaration to a block, line by line.
201
212
  def declaration_contents
202
213
  yield "#ifndef #{header_guard}"
203
214
  yield "#define #{header_guard}"
@@ -210,33 +221,32 @@ module Wrapture
210
221
  yield
211
222
  yield "namespace #{@spec['namespace']} {"
212
223
  yield
213
- yield " class #{@spec['name']} {"
224
+
225
+ parent = if @spec.key?('parent')
226
+ ": public #{parent_name} "
227
+ else
228
+ ''
229
+ end
230
+ yield " class #{@spec['name']} #{parent}{"
231
+
214
232
  yield ' public:'
215
233
 
216
234
  yield unless @constants.empty?
217
235
  @constants.each do |const|
218
- yield " #{const.declaration};"
236
+ yield " #{const.declaration};"
219
237
  end
220
238
 
221
239
  yield
222
- struct_name = @spec['equivalent-struct']['name']
223
- yield " struct #{struct_name} #{equivalent_name};"
240
+ yield " #{@struct.declaration equivalent_name};"
224
241
  yield
225
242
 
226
- unless @spec['equivalent-struct']['members'].empty?
227
- yield " #{member_constructor_signature};"
228
- end
243
+ member_constructor_declaration { |line| yield " #{line}" }
229
244
 
230
- unless pointer_wrapper?
231
- yield " #{struct_constructor_signature};"
232
- yield " #{pointer_constructor_signature};"
233
- end
245
+ pointer_constructor_declaration { |line| yield " #{line}" }
234
246
 
235
- @spec['constructors'].each_index do |constructor|
236
- yield " #{wrapped_constructor_signature constructor};"
237
- end
247
+ yield " #{struct_constructor_signature};" unless pointer_wrapper?
238
248
 
239
- yield " #{destructor_signature};" if @spec.key? 'destructor'
249
+ overload_declaration { |line| yield " #{line}" }
240
250
 
241
251
  @functions.each do |func|
242
252
  yield " #{func.declaration};"
@@ -249,18 +259,26 @@ module Wrapture
249
259
  yield '#endif' # end of header guard
250
260
  end
251
261
 
252
- def generate_definition_file
253
- filename = "#{@spec['name']}.cpp"
262
+ # A list of includes needed for the declaration of the class.
263
+ def declaration_includes
264
+ includes = @spec['includes'].dup
254
265
 
255
- File.open(filename, 'w') do |file|
256
- definition_contents do |line|
257
- file.puts line
258
- end
266
+ includes.concat(@struct.includes)
267
+
268
+ @functions.each do |func|
269
+ includes.concat(func.declaration_includes)
259
270
  end
260
271
 
261
- filename
272
+ @constants.each do |const|
273
+ includes.concat(const.declaration_includes)
274
+ end
275
+
276
+ includes.concat(@spec['parent']['includes']) if @spec.key?('parent')
277
+
278
+ includes.uniq
262
279
  end
263
280
 
281
+ # Gives the content of the class definition to a block, line by line.
264
282
  def definition_contents
265
283
  definition_includes.each do |include_file|
266
284
  yield "#include <#{include_file}>"
@@ -271,68 +289,217 @@ module Wrapture
271
289
 
272
290
  yield unless @constants.empty?
273
291
  @constants.each do |const|
274
- yield " #{const.definition @spec['name']};"
292
+ yield " #{const.definition(@spec['name'])};"
275
293
  end
276
294
 
277
- unless @spec['equivalent-struct']['members'].empty?
278
- yield
279
- yield " #{@spec['name']}::#{member_constructor_signature} {"
295
+ member_constructor_definition { |line| yield " #{line}" }
280
296
 
281
- @spec['equivalent-struct']['members'].each do |member|
282
- member_decl = equivalent_member member['name']
283
- yield " #{member_decl} = #{member['name']};"
284
- end
285
-
286
- yield ' }'
287
- end
297
+ pointer_constructor_definition { |line| yield " #{line}" }
288
298
 
289
299
  unless pointer_wrapper?
290
300
  yield
291
301
  yield " #{@spec['name']}::#{struct_constructor_signature} {"
292
302
 
293
- @spec['equivalent-struct']['members'].each do |member|
294
- member_decl = equivalent_member member['name']
303
+ @struct.members.each do |member|
304
+ member_decl = this_member(member['name'])
295
305
  yield " #{member_decl} = equivalent.#{member['name']};"
296
306
  end
297
307
 
298
308
  yield ' }'
309
+ end
310
+
311
+ overload_definition { |line| yield " #{line}" }
299
312
 
313
+ @functions.each do |func|
300
314
  yield
301
- yield " #{@spec['name']}::#{pointer_constructor_signature} {"
302
315
 
303
- @spec['equivalent-struct']['members'].each do |member|
304
- member_decl = equivalent_member member['name']
305
- yield " #{member_decl} = equivalent->#{member['name']};"
316
+ func.definition(@spec['name']) do |def_line|
317
+ yield " #{def_line}"
306
318
  end
319
+ end
307
320
 
308
- yield ' }'
321
+ yield
322
+ yield '}' # end of namespace
323
+ end
324
+
325
+ # A list of includes needed for the definition of the class.
326
+ def definition_includes
327
+ includes = ["#{@spec['name']}.hpp"]
328
+
329
+ includes.concat(@spec['includes'])
330
+
331
+ @functions.each do |func|
332
+ includes.concat(func.definition_includes)
309
333
  end
310
334
 
311
- @spec['constructors'].each_index do |constructor|
312
- yield
313
- wrapped_constructor_definition(constructor) do |line|
314
- yield " #{line}"
315
- end
335
+ @constants.each do |const|
336
+ includes.concat(const.definition_includes)
316
337
  end
317
338
 
318
- if @spec.key? 'destructor'
319
- yield
320
- yield " #{@spec['name']}::#{destructor_signature} {"
321
- func_spec = @spec['destructor']['wrapped-function']
322
- yield " #{function_call func_spec};"
323
- yield ' }'
339
+ includes.concat(overload_definition_includes)
340
+
341
+ includes.uniq
342
+ end
343
+
344
+ # Gives the name of the equivalent struct.
345
+ def equivalent_name
346
+ "#{'*' if pointer_wrapper?}equivalent"
347
+ end
348
+
349
+ # Generates the declaration of the class.
350
+ def generate_declaration_file
351
+ filename = "#{@spec['name']}.hpp"
352
+
353
+ File.open(filename, 'w') do |file|
354
+ declaration_contents do |line|
355
+ file.puts(line)
356
+ end
324
357
  end
325
358
 
326
- @functions.each do |func|
327
- yield
359
+ filename
360
+ end
328
361
 
329
- func.definition(@spec['name']) do |def_line|
330
- yield " #{def_line}"
362
+ # Generates the definition of the class.
363
+ def generate_definition_file
364
+ filename = "#{@spec['name']}.cpp"
365
+
366
+ File.open(filename, 'w') do |file|
367
+ definition_contents do |line|
368
+ file.puts(line)
331
369
  end
332
370
  end
333
371
 
372
+ filename
373
+ end
374
+
375
+ # The header guard for the class.
376
+ def header_guard
377
+ "__#{@spec['name'].upcase}_HPP"
378
+ end
379
+
380
+ # Yields the declaration of the member constructor for a class. This will be
381
+ # empty if the wrapped struct is a pointer wrapper.
382
+ def member_constructor_declaration
383
+ return unless @struct.members?
384
+
385
+ yield "#{@spec['name']}( #{@struct.member_list_with_defaults} );"
386
+ end
387
+
388
+ # Yields the definition of the member constructor for a class. This will be
389
+ # empty if the wrapped struct is a pointer wrapper.
390
+ def member_constructor_definition
391
+ return unless @struct.members?
392
+
393
+ yield "#{@spec['name']}::#{@spec['name']}( #{@struct.member_list} ) {"
394
+
395
+ @struct.members.each do |member|
396
+ member_decl = this_member(member['name'])
397
+ yield " #{member_decl} = #{member['name']};"
398
+ end
399
+
400
+ yield '}'
401
+ end
402
+
403
+ # Yields the declaration of the overload function for this class. If there
404
+ # is no overload function for this class, then nothing is yielded.
405
+ def overload_declaration
406
+ return unless @scope.overloads?(self)
407
+
408
+ yield "static #{name} *new#{name}( struct #{@struct.name} *equivalent );"
409
+ end
410
+
411
+ # Yields each line of the definition of the overload function, with a
412
+ # leading empty yield. If there is no overload function for this class,
413
+ # then nothing is yielded.
414
+ def overload_definition
415
+ return unless @scope.overloads?(self)
416
+
334
417
  yield
335
- yield '}' # end of namespace
418
+
419
+ parameter = "struct #{@struct.name} *equivalent"
420
+ yield "#{name} *#{name}::new#{name}( #{parameter} ) {"
421
+
422
+ line_prefix = ' '
423
+ @scope.overloads(self).each do |overload|
424
+ check = overload.struct.rules_check('equivalent')
425
+ yield "#{line_prefix}if( #{check} ) {"
426
+ yield " return new #{overload.name}( equivalent );"
427
+ line_prefix = ' } else '
428
+ end
429
+
430
+ yield "#{line_prefix}{"
431
+ yield " return new #{name}( equivalent );"
432
+ yield ' }'
433
+ yield '}'
434
+ end
435
+
436
+ # A list of the includes needed for the overload definitions.
437
+ def overload_definition_includes
438
+ @scope.overloads(self).map { |overload| "#{overload.name}.hpp" }
439
+ end
440
+
441
+ # Yields the declaration of the pointer constructor for a class.
442
+ #
443
+ # If there is already a constructor provided with this signature, then this
444
+ # function will return with no output.
445
+ def pointer_constructor_declaration
446
+ signature_prefix = "#{@spec['name']}( #{@struct.pointer_declaration('')}"
447
+ return if @functions.any? do |func|
448
+ func.constructor? && func.signature.start_with?(signature_prefix)
449
+ end
450
+
451
+ yield "#{pointer_constructor_signature};"
452
+ end
453
+
454
+ # Yields the definition of the pointer constructor for a class.
455
+ #
456
+ # If there is already a constructor provided with this signature, then this
457
+ # function will return with no output.
458
+ #
459
+ # If this is a pointer wrapper class, then the constructor will simply set
460
+ # the underlying pointer to the provied one, and return the new object.
461
+ #
462
+ # If this is a struct wrapper class, then a constructor will be created that
463
+ # sets each member of the wrapped struct to the provided value.
464
+ def pointer_constructor_definition
465
+ signature_prefix = "#{@spec['name']}( #{@struct.pointer_declaration('')}"
466
+ return if @functions.any? do |func|
467
+ func.constructor? && func.signature.start_with?(signature_prefix)
468
+ end
469
+
470
+ yield "#{@spec['name']}::#{pointer_constructor_signature} {"
471
+
472
+ if pointer_wrapper?
473
+ yield ' this->equivalent = equivalent;'
474
+ else
475
+ @struct.members.each do |member|
476
+ member_decl = this_member(member['name'])
477
+ yield " #{member_decl} = equivalent->#{member['name']};"
478
+ end
479
+ end
480
+
481
+ yield '}'
482
+ end
483
+
484
+ # The signature of the constructor given an equivalent strucct pointer.
485
+ def pointer_constructor_signature
486
+ "#{@spec['name']}( #{@struct.pointer_declaration 'equivalent'} )"
487
+ end
488
+
489
+ # Determines if this class is a wrapper for a struct pointer or not.
490
+ def pointer_wrapper?
491
+ @spec['type'] == 'pointer'
492
+ end
493
+
494
+ # The signature of the constructor given an equivalent struct type.
495
+ def struct_constructor_signature
496
+ "#{@spec['name']}( #{@struct.declaration 'equivalent'} )"
497
+ end
498
+
499
+ # Gives a code snippet that accesses a member of the equivalent struct for
500
+ # this class within the class using the 'this' keyword.
501
+ def this_member(member)
502
+ "this->equivalent#{pointer_wrapper? ? '->' : '.'}#{member}"
336
503
  end
337
504
  end
338
505
  end