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 +4 -4
- data/bin/wrapture +9 -4
- data/lib/wrapture.rb +24 -1
- data/lib/wrapture/action_spec.rb +81 -0
- data/lib/wrapture/class_spec.rb +359 -192
- data/lib/wrapture/constant_spec.rb +22 -5
- data/lib/wrapture/constants.rb +32 -0
- data/lib/wrapture/errors.rb +57 -0
- data/lib/wrapture/function_spec.rb +207 -36
- data/lib/wrapture/normalize.rb +62 -0
- data/lib/wrapture/rule_spec.rb +87 -0
- data/lib/wrapture/scope.rb +80 -0
- data/lib/wrapture/struct_spec.rb +127 -0
- data/lib/wrapture/version.rb +11 -1
- data/lib/wrapture/wrapped_function_spec.rb +117 -0
- metadata +17 -3
@@ -1,34 +1,51 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'wrapture/normalize'
|
4
|
+
|
3
5
|
module Wrapture
|
4
|
-
##
|
5
6
|
# A description of a constant.
|
6
7
|
class ConstantSpec
|
8
|
+
# Normalizes a hash specification of a constant. Normalization will check
|
9
|
+
# for things like invalid keys, duplicate entries in include lists, and
|
10
|
+
# will set missing keys to their default value (for example, an empty list
|
11
|
+
# if no includes are given).
|
7
12
|
def self.normalize_spec_hash(spec)
|
8
|
-
|
13
|
+
normalized = spec.dup
|
9
14
|
|
10
|
-
|
11
|
-
|
15
|
+
normalized['version'] = Wrapture.spec_version(spec)
|
16
|
+
normalized['includes'] = Wrapture.normalize_includes spec['includes']
|
12
17
|
|
13
|
-
|
18
|
+
normalized
|
14
19
|
end
|
15
20
|
|
21
|
+
# Creates a constant spec based on the provided hash spec
|
22
|
+
#
|
23
|
+
# The hash must have the following keys:
|
24
|
+
# name:: the name of the constant
|
25
|
+
# type:: the type of the constant
|
26
|
+
# value:: the value to assign to the constant
|
27
|
+
# includes:: a list of includes that need to be added in order for this
|
28
|
+
# constant to be valid (for example, includes for the type and value).
|
16
29
|
def initialize(spec)
|
17
30
|
@spec = ConstantSpec.normalize_spec_hash spec
|
18
31
|
end
|
19
32
|
|
33
|
+
# A list of includes needed for the declaration of this constant.
|
20
34
|
def declaration_includes
|
21
35
|
@spec['includes'].dup
|
22
36
|
end
|
23
37
|
|
38
|
+
# A list of includes needed for the definition of this constant.
|
24
39
|
def definition_includes
|
25
40
|
@spec['includes'].dup
|
26
41
|
end
|
27
42
|
|
43
|
+
# The declaration of this constant.
|
28
44
|
def declaration
|
29
45
|
"static const #{ClassSpec.typed_variable(@spec['type'], @spec['name'])}"
|
30
46
|
end
|
31
47
|
|
48
|
+
# The definition of this constant.
|
32
49
|
def definition(class_name)
|
33
50
|
expanded_name = "#{class_name}::#{@spec['name']}"
|
34
51
|
"const #{@spec['type']} #{expanded_name} = #{@spec['value']}"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
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
|
+
|
19
|
+
module Wrapture
|
20
|
+
# A string denoting an equivalent struct type or value.
|
21
|
+
EQUIVALENT_STRUCT_KEYWORD = 'equivalent-struct'
|
22
|
+
|
23
|
+
# A string denoting a pointer to an equivalent struct type or value.
|
24
|
+
EQUIVALENT_POINTER_KEYWORD = 'equivalent-struct-pointer'
|
25
|
+
|
26
|
+
# A string denoting the return value of a wrapped function call.
|
27
|
+
RETURN_VALUE_KEYWORD = 'return-value'
|
28
|
+
|
29
|
+
# A list of all keywords.
|
30
|
+
KEYWORDS = [EQUIVALENT_STRUCT_KEYWORD, EQUIVALENT_POINTER_KEYWORD,
|
31
|
+
RETURN_VALUE_KEYWORD].freeze
|
32
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
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
|
+
|
19
|
+
module Wrapture
|
20
|
+
# An error from the Wrapture library
|
21
|
+
class WraptureError < StandardError
|
22
|
+
end
|
23
|
+
|
24
|
+
# The spec has a key that is not valid.
|
25
|
+
class InvalidSpecKey < WraptureError
|
26
|
+
# Creates an InvalidSpecKey with the given message. A list of valid values
|
27
|
+
# may optionally be passed to +valid_keys+ which will be added to the end
|
28
|
+
# of the message.
|
29
|
+
def initialize(message, valid_keys: [])
|
30
|
+
complete_message = message.dup
|
31
|
+
|
32
|
+
unless valid_keys.empty?
|
33
|
+
complete_message << ' (valid values are \''
|
34
|
+
complete_message << valid_keys.join('\', \'')
|
35
|
+
complete_message << '\')'
|
36
|
+
end
|
37
|
+
|
38
|
+
super(complete_message)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# The spec is missing a key that is required.
|
43
|
+
class MissingSpecKey < WraptureError
|
44
|
+
# Creates a MissingSpecKey with the given message.
|
45
|
+
def initialize(message)
|
46
|
+
super(message)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Missing a namespace in the class spec
|
51
|
+
class NoNamespace < WraptureError
|
52
|
+
end
|
53
|
+
|
54
|
+
# The spec version is not supported by this version of Wrapture.
|
55
|
+
class UnsupportedSpecVersion < WraptureError
|
56
|
+
end
|
57
|
+
end
|
@@ -1,74 +1,245 @@
|
|
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
|
+
|
19
|
+
require 'wrapture/constants'
|
20
|
+
require 'wrapture/errors'
|
21
|
+
require 'wrapture/scope'
|
22
|
+
require 'wrapture/wrapped_function_spec'
|
23
|
+
|
3
24
|
module Wrapture
|
4
|
-
##
|
5
25
|
# A description of a function to be generated, including details about the
|
6
26
|
# underlying implementation.
|
7
27
|
class FunctionSpec
|
28
|
+
# Normalizes a hash specification of a function. Normalization will check
|
29
|
+
# for things like invalid keys, duplicate entries in include lists, and will
|
30
|
+
# set missing keys to their default values (for example, an empty list if no
|
31
|
+
# includes are given).
|
8
32
|
def self.normalize_spec_hash(spec)
|
9
|
-
|
33
|
+
normalized = spec.dup
|
34
|
+
param_types = {}
|
10
35
|
|
11
|
-
|
12
|
-
|
13
|
-
normalized_spec['wrapped-function']['includes'] ||= []
|
36
|
+
normalized['version'] = Wrapture.spec_version(spec)
|
37
|
+
normalized['virtual'] = Wrapture.normalize_boolean(spec, 'virtual')
|
14
38
|
|
15
|
-
|
16
|
-
|
17
|
-
|
39
|
+
normalized['params'] ||= []
|
40
|
+
normalized['params'].each do |param_spec|
|
41
|
+
param_types[param_spec['name']] = param_spec['type']
|
42
|
+
includes = Wrapture.normalize_includes(param_spec['includes'])
|
43
|
+
param_spec['includes'] = includes
|
18
44
|
end
|
19
45
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
params = []
|
29
|
-
|
30
|
-
spec['params'].each do |param|
|
31
|
-
params << ClassSpec.typed_variable(param['type'], param['name'])
|
46
|
+
if normalized['return'].nil?
|
47
|
+
normalized['return'] = {}
|
48
|
+
normalized['return']['type'] = 'void'
|
49
|
+
normalized['return']['includes'] = []
|
50
|
+
else
|
51
|
+
normalized['return']['type'] ||= 'void'
|
52
|
+
includes = Wrapture.normalize_includes(spec['return']['includes'])
|
53
|
+
normalized['return']['includes'] = includes
|
32
54
|
end
|
33
55
|
|
34
|
-
|
56
|
+
overload = Wrapture.normalize_boolean(normalized['return'], 'overloaded')
|
57
|
+
normalized['return']['overloaded'] = overload
|
58
|
+
|
59
|
+
normalized
|
35
60
|
end
|
36
61
|
|
37
|
-
|
62
|
+
# Creates a function spec based on the provided function spec.
|
63
|
+
#
|
64
|
+
# The hash must have the following keys:
|
65
|
+
# name:: the name of the function
|
66
|
+
# params:: a list of parameter specifications
|
67
|
+
# wrapped-function:: a hash describing the function to be wrapped
|
68
|
+
#
|
69
|
+
# The wrapped-function must have a 'name' key with the name of the function,
|
70
|
+
# and a 'params' key with a list of parameters (each a hash with a 'name'
|
71
|
+
# and 'type' key). Optionally, it may also include an 'includes' key with a
|
72
|
+
# list of includes that are needed for this function to compile.
|
73
|
+
#
|
74
|
+
# The following keys are optional:
|
75
|
+
# static:: set to true if this is a static function.
|
76
|
+
def initialize(spec, owner = Scope.new, constructor: false,
|
77
|
+
destructor: false)
|
38
78
|
@owner = owner
|
39
79
|
@spec = FunctionSpec.normalize_spec_hash(spec)
|
80
|
+
@wrapped = WrappedFunctionSpec.new(spec['wrapped-function'])
|
81
|
+
@constructor = constructor
|
82
|
+
@destructor = destructor
|
40
83
|
end
|
41
84
|
|
42
|
-
|
43
|
-
|
85
|
+
# True if the function is a constructor, false otherwise.
|
86
|
+
def constructor?
|
87
|
+
@constructor
|
44
88
|
end
|
45
89
|
|
46
|
-
|
90
|
+
# A list of includes needed for the declaration of the function.
|
91
|
+
def declaration_includes
|
47
92
|
includes = @spec['return']['includes'].dup
|
48
|
-
includes.concat
|
93
|
+
includes.concat(param_includes)
|
94
|
+
includes.uniq
|
95
|
+
end
|
49
96
|
|
97
|
+
# A list of includes needed for the definition of the function.
|
98
|
+
def definition_includes
|
99
|
+
includes = @wrapped.includes
|
100
|
+
includes.concat(@spec['return']['includes'])
|
101
|
+
includes.concat(param_includes)
|
50
102
|
includes.uniq
|
51
103
|
end
|
52
104
|
|
105
|
+
# A comma-separated list of parameters and resolved types fit for use in a
|
106
|
+
# function signature or declaration.
|
107
|
+
def param_list
|
108
|
+
return 'void' if @spec['params'].empty?
|
109
|
+
|
110
|
+
params = []
|
111
|
+
|
112
|
+
@spec['params'].each do |param|
|
113
|
+
type = resolve_type(param['type'])
|
114
|
+
params << ClassSpec.typed_variable(type, param['name'])
|
115
|
+
end
|
116
|
+
|
117
|
+
params.join(', ')
|
118
|
+
end
|
119
|
+
|
120
|
+
# Gives an expression for calling a given parameter within this function.
|
121
|
+
# Equivalent structs and pointers are resolved, as well as casts between
|
122
|
+
# types if they are known within the scope of this function.
|
123
|
+
def resolve_wrapped_param(param_spec)
|
124
|
+
used_param = @spec['params'].find { |p| p['name'] == param_spec['value'] }
|
125
|
+
|
126
|
+
if param_spec['value'] == EQUIVALENT_STRUCT_KEYWORD
|
127
|
+
@owner.this_struct
|
128
|
+
elsif param_spec['value'] == EQUIVALENT_POINTER_KEYWORD
|
129
|
+
@owner.this_struct_pointer
|
130
|
+
elsif used_param &&
|
131
|
+
@owner.type?(used_param['type']) &&
|
132
|
+
!param_spec['type'].nil?
|
133
|
+
param_class = @owner.type(used_param['type'])
|
134
|
+
param_class.cast_to(used_param['name'], param_spec['type'])
|
135
|
+
else
|
136
|
+
param_spec['value']
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# The signature of the function.
|
53
141
|
def signature
|
54
|
-
"#{@spec['name']}( #{
|
142
|
+
"#{@spec['name']}( #{param_list} )"
|
55
143
|
end
|
56
144
|
|
145
|
+
# The declaration of the function.
|
57
146
|
def declaration
|
58
|
-
|
147
|
+
return signature if @constructor || @destructor
|
148
|
+
|
149
|
+
modifier_prefix = if @spec['static']
|
150
|
+
'static '
|
151
|
+
elsif virtual?
|
152
|
+
'virtual '
|
153
|
+
else
|
154
|
+
''
|
155
|
+
end
|
59
156
|
"#{modifier_prefix}#{@spec['return']['type']} #{signature}"
|
60
157
|
end
|
61
158
|
|
159
|
+
# Gives the definition of the function to a block, line by line.
|
62
160
|
def definition(class_name)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
161
|
+
yield "#{return_prefix}#{class_name}::#{signature} {"
|
162
|
+
|
163
|
+
if @wrapped.error_check?
|
164
|
+
yield " #{@wrapped.return_val_type} return_val;"
|
165
|
+
yield
|
166
|
+
end
|
167
|
+
|
168
|
+
call = @wrapped.call_from(self)
|
169
|
+
call_line = if @constructor
|
170
|
+
"this->equivalent = #{call}"
|
171
|
+
elsif @wrapped.error_check?
|
172
|
+
"return_val = #{call}"
|
173
|
+
elsif returns_value?
|
174
|
+
"return #{return_cast(call)}"
|
175
|
+
else
|
176
|
+
call
|
177
|
+
end
|
178
|
+
|
179
|
+
yield " #{call_line};"
|
180
|
+
|
181
|
+
if @wrapped.error_check?
|
182
|
+
yield
|
183
|
+
@wrapped.error_check { |line| yield " #{line}" }
|
184
|
+
end
|
185
|
+
|
71
186
|
yield '}'
|
72
187
|
end
|
188
|
+
|
189
|
+
# True if the function is virtual.
|
190
|
+
def virtual?
|
191
|
+
@spec['virtual']
|
192
|
+
end
|
193
|
+
|
194
|
+
private
|
195
|
+
|
196
|
+
# A list of includes needed for the parameters of the function.
|
197
|
+
def param_includes
|
198
|
+
includes = []
|
199
|
+
|
200
|
+
@spec['params'].each do |param_spec|
|
201
|
+
includes.concat(param_spec['includes'])
|
202
|
+
end
|
203
|
+
|
204
|
+
includes
|
205
|
+
end
|
206
|
+
|
207
|
+
# A resolved type name.
|
208
|
+
def resolve_type(type)
|
209
|
+
if type == EQUIVALENT_STRUCT_KEYWORD
|
210
|
+
"struct #{@owner.struct_name}"
|
211
|
+
elsif type == EQUIVALENT_POINTER_KEYWORD
|
212
|
+
"struct #{@owner.struct_name} *"
|
213
|
+
else
|
214
|
+
type
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# The function to use to create the return value of the function.
|
219
|
+
def return_cast(value)
|
220
|
+
if @spec['return']['type'] == @wrapped.return_val_type
|
221
|
+
value
|
222
|
+
elsif @spec['return']['overloaded']
|
223
|
+
"new#{@spec['return']['type'].chomp('*').strip} ( #{value} )"
|
224
|
+
else
|
225
|
+
"#{@spec['return']['type']} ( #{value} )"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# The return type prefix to use for the function definition.
|
230
|
+
def return_prefix
|
231
|
+
if @constructor || @destructor
|
232
|
+
''
|
233
|
+
elsif @spec['return']['type'].end_with?('*')
|
234
|
+
@spec['return']['type']
|
235
|
+
else
|
236
|
+
"#{@spec['return']['type']} "
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
# True if the function returns a value.
|
241
|
+
def returns_value?
|
242
|
+
!@constructor && !@destructor && @spec['return']['type'] != 'void'
|
243
|
+
end
|
73
244
|
end
|
74
245
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
4
|
+
|
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
|
+
|
19
|
+
require 'wrapture/version'
|
20
|
+
|
21
|
+
module Wrapture
|
22
|
+
# Normalizes a spec key to be boolean, raising an error if it is not. Keys
|
23
|
+
# that are not present are defaulted to false.
|
24
|
+
def self.normalize_boolean(spec, key)
|
25
|
+
is_boolean = [true, false].include?(spec[key])
|
26
|
+
error_msg = "'#{key}' key may only be true or false"
|
27
|
+
raise(InvalidSpecKey, error_msg) unless !spec.key?(key) || is_boolean
|
28
|
+
|
29
|
+
spec.key?(key) && spec[key]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Normalizes an include list for an element. A single string will be converted
|
33
|
+
# into an array containing the single string, and a nil will be converted to
|
34
|
+
# an empty array.
|
35
|
+
def self.normalize_includes(includes)
|
36
|
+
if includes.nil?
|
37
|
+
[]
|
38
|
+
elsif includes.is_a? String
|
39
|
+
[includes]
|
40
|
+
else
|
41
|
+
includes.uniq
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns the spec version for the provided spec. If the version is not
|
46
|
+
# provided in the spec, the newest version that the spec is compliant with
|
47
|
+
# will be returned instead.
|
48
|
+
#
|
49
|
+
# If this spec uses a version unsupported by this version of Wrapture or the
|
50
|
+
# spec is otherwise invalid, an exception is raised.
|
51
|
+
def self.spec_version(spec)
|
52
|
+
if spec.key?('version') && !Wrapture.supports_version?(spec['version'])
|
53
|
+
raise UnsupportedSpecVersion
|
54
|
+
end
|
55
|
+
|
56
|
+
if spec.key?('version')
|
57
|
+
spec['version']
|
58
|
+
else
|
59
|
+
Wrapture::VERSION
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|