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