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
@@ -0,0 +1,107 @@
|
|
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
|
+
# A comment that can be inserted in generated source code.
|
23
|
+
#
|
24
|
+
# Comments are primarily used to insert documentation about generated code for
|
25
|
+
# documentation generation tools such as Doxygen.
|
26
|
+
class Comment
|
27
|
+
# Validates a doc string.
|
28
|
+
def self.validate_doc(doc)
|
29
|
+
raise InvalidDoc, 'a doc must be a string' unless doc.is_a?(String)
|
30
|
+
end
|
31
|
+
|
32
|
+
# The raw text of the comment.
|
33
|
+
attr_reader :text
|
34
|
+
|
35
|
+
# Creates a comment from a string. If the provided string is nil, then an
|
36
|
+
# empty string is used.
|
37
|
+
def initialize(comment = '')
|
38
|
+
@text = comment.nil? ? '' : comment
|
39
|
+
end
|
40
|
+
|
41
|
+
# True if this comment is empty, false otherwise.
|
42
|
+
def empty?
|
43
|
+
@text.empty?
|
44
|
+
end
|
45
|
+
|
46
|
+
# Yields each line of the comment formatted as specified.
|
47
|
+
def format(line_prefix: '// ', first_line: nil, last_line: nil,
|
48
|
+
max_line_length: 80)
|
49
|
+
return if @text.empty?
|
50
|
+
|
51
|
+
yield first_line if first_line
|
52
|
+
|
53
|
+
paragraphs(max_line_length - line_prefix.length) do |line|
|
54
|
+
yield "#{line_prefix}#{line}".rstrip
|
55
|
+
end
|
56
|
+
|
57
|
+
yield last_line if last_line
|
58
|
+
end
|
59
|
+
|
60
|
+
# Calls the given block for each line of the comment formatted using Doxygen
|
61
|
+
# style.
|
62
|
+
def format_as_doxygen(max_line_length: 80, &block)
|
63
|
+
format(line_prefix: ' * ', first_line: '/**',
|
64
|
+
last_line: ' */', max_line_length: max_line_length) do |line|
|
65
|
+
block.call(line)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Yields the comment converted into paragraph-style blocks.
|
72
|
+
#
|
73
|
+
# Consecutive lines with text are concatenated together to the maximum line
|
74
|
+
# length, regardless of the original line length in the comment. One or more
|
75
|
+
# empty lines are written as a single empty line, separating paragraphs.
|
76
|
+
#
|
77
|
+
# Yielded lines may have trailing spaces, which are not considered part of
|
78
|
+
# the maximum length. The caller must strip these off.
|
79
|
+
def paragraphs(line_length)
|
80
|
+
running_line = String.new
|
81
|
+
newline_mode = true
|
82
|
+
@text.each_line do |line|
|
83
|
+
if line.strip.empty?
|
84
|
+
unless newline_mode
|
85
|
+
yield running_line
|
86
|
+
yield ''
|
87
|
+
running_line.clear
|
88
|
+
newline_mode = true
|
89
|
+
end
|
90
|
+
else
|
91
|
+
newline_mode = false
|
92
|
+
end
|
93
|
+
|
94
|
+
line.scan(/\S+/) do |word|
|
95
|
+
if running_line.length + word.length > line_length
|
96
|
+
yield running_line
|
97
|
+
running_line = String.new("#{word} ")
|
98
|
+
else
|
99
|
+
running_line << word << ' '
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
yield running_line
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -1,34 +1,76 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
|
1
3
|
# frozen_string_literal: true
|
2
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
|
+
#++
|
20
|
+
|
3
21
|
module Wrapture
|
4
|
-
##
|
5
22
|
# A description of a constant.
|
6
23
|
class ConstantSpec
|
24
|
+
# Normalizes a hash specification of a constant. Normalization will check
|
25
|
+
# for things like invalid keys, duplicate entries in include lists, and
|
26
|
+
# will set missing keys to their default value (for example, an empty list
|
27
|
+
# if no includes are given).
|
7
28
|
def self.normalize_spec_hash(spec)
|
8
|
-
|
29
|
+
Comment.validate_doc(spec['doc']) if spec.key?('doc')
|
30
|
+
|
31
|
+
normalized = spec.dup
|
9
32
|
|
10
|
-
|
11
|
-
|
33
|
+
normalized['version'] = Wrapture.spec_version(spec)
|
34
|
+
normalized['includes'] = Wrapture.normalize_includes spec['includes']
|
12
35
|
|
13
|
-
|
36
|
+
normalized
|
14
37
|
end
|
15
38
|
|
39
|
+
# Creates a constant spec based on the provided hash spec
|
40
|
+
#
|
41
|
+
# The hash must have the following keys:
|
42
|
+
# name:: the name of the constant
|
43
|
+
# type:: the type of the constant
|
44
|
+
# value:: the value to assign to the constant
|
45
|
+
# includes:: a list of includes that need to be added in order for this
|
46
|
+
# constant to be valid (for example, includes for the type and value).
|
47
|
+
#
|
48
|
+
# The following keys are optional:
|
49
|
+
# doc:: a string containing the documentation for this constant
|
16
50
|
def initialize(spec)
|
17
|
-
@spec = ConstantSpec.normalize_spec_hash
|
51
|
+
@spec = ConstantSpec.normalize_spec_hash(spec)
|
52
|
+
@doc = @spec.key?('doc') ? Comment.new(@spec['doc']) : nil
|
53
|
+
@type = TypeSpec.new(@spec['type'])
|
18
54
|
end
|
19
55
|
|
56
|
+
# A list of includes needed for the declaration of this constant.
|
20
57
|
def declaration_includes
|
21
58
|
@spec['includes'].dup
|
22
59
|
end
|
23
60
|
|
61
|
+
# A list of includes needed for the definition of this constant.
|
24
62
|
def definition_includes
|
25
63
|
@spec['includes'].dup
|
26
64
|
end
|
27
65
|
|
28
|
-
|
29
|
-
|
66
|
+
# Calls the given block once for each line of the declaration of this
|
67
|
+
# constant, including any documentation.
|
68
|
+
def declaration(&block)
|
69
|
+
@doc&.format_as_doxygen(max_line_length: 76) { |line| block.call(line) }
|
70
|
+
block.call("static const #{@type.variable(@spec['name'])};")
|
30
71
|
end
|
31
72
|
|
73
|
+
# The definition of this constant.
|
32
74
|
def definition(class_name)
|
33
75
|
expanded_name = "#{class_name}::#{@spec['name']}"
|
34
76
|
"const #{@spec['type']} #{expanded_name} = #{@spec['value']}"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
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
|
+
#++
|
20
|
+
|
21
|
+
module Wrapture
|
22
|
+
# A string denoting an equivalent struct type or value.
|
23
|
+
EQUIVALENT_STRUCT_KEYWORD = 'equivalent-struct'
|
24
|
+
|
25
|
+
# A string denoting a pointer to an equivalent struct type or value.
|
26
|
+
EQUIVALENT_POINTER_KEYWORD = 'equivalent-struct-pointer'
|
27
|
+
|
28
|
+
# A string denoting the return value of a wrapped function call.
|
29
|
+
RETURN_VALUE_KEYWORD = 'return-value'
|
30
|
+
|
31
|
+
# A string denoting a reference to the object a method is called on.
|
32
|
+
SELF_REFERENCE_KEYWORD = 'self-reference'
|
33
|
+
|
34
|
+
# A string denoting a reference to a template.
|
35
|
+
TEMPLATE_USE_KEYWORD = 'use-template'
|
36
|
+
|
37
|
+
# A list of all keywords.
|
38
|
+
KEYWORDS = [EQUIVALENT_STRUCT_KEYWORD, EQUIVALENT_POINTER_KEYWORD,
|
39
|
+
SELF_REFERENCE_KEYWORD, RETURN_VALUE_KEYWORD,
|
40
|
+
TEMPLATE_USE_KEYWORD].freeze
|
41
|
+
end
|
@@ -0,0 +1,155 @@
|
|
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
|
+
# A description of an enumeration.
|
23
|
+
class EnumSpec
|
24
|
+
# Returns a normalized copy of a hash specification of an enumeration in
|
25
|
+
# place. See normalize_spec_hash! for details.
|
26
|
+
def self.normalize_spec_hash(spec)
|
27
|
+
normalize_spec_hash!(Marshal.load(Marshal.dump(spec)))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Normalizes a hash specification of an enumeration in place. Normalization
|
31
|
+
# will remove duplicate entries in include lists and check for a name key.
|
32
|
+
def self.normalize_spec_hash!(spec)
|
33
|
+
unless spec.key?('name')
|
34
|
+
raise MissingSpecKey, 'a name is required for enumerations'
|
35
|
+
end
|
36
|
+
|
37
|
+
if spec.key?('elements')
|
38
|
+
unless spec['elements'].is_a?(Array)
|
39
|
+
raise InvalidSpecKey, 'the elements key must be an array'
|
40
|
+
end
|
41
|
+
else
|
42
|
+
raise MissingSpecKey, 'elements are required for enumerations'
|
43
|
+
end
|
44
|
+
|
45
|
+
spec['includes'] = Wrapture.normalize_includes(spec['includes'])
|
46
|
+
spec['elements'].each do |element|
|
47
|
+
element['includes'] = Wrapture.normalize_includes(element['includes'])
|
48
|
+
end
|
49
|
+
|
50
|
+
spec
|
51
|
+
end
|
52
|
+
|
53
|
+
# Creates an enumeration specification based on the provided hash spec.
|
54
|
+
def initialize(spec)
|
55
|
+
@spec = EnumSpec.normalize_spec_hash(spec)
|
56
|
+
|
57
|
+
@doc = Comment.new(@spec.fetch('doc', nil))
|
58
|
+
end
|
59
|
+
|
60
|
+
# Generates the wrapper definition file.
|
61
|
+
def generate_wrapper
|
62
|
+
filename = "#{@spec['name']}.hpp"
|
63
|
+
|
64
|
+
File.open(filename, 'w') do |file|
|
65
|
+
definition_contents do |line|
|
66
|
+
file.puts(line)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
[filename]
|
71
|
+
end
|
72
|
+
|
73
|
+
# The name of the enumeration.
|
74
|
+
def name
|
75
|
+
@spec['name']
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Yields each line of the definition of the wrapper for this enum.
|
81
|
+
def definition_contents
|
82
|
+
indent = 0
|
83
|
+
|
84
|
+
yield "#ifndef #{header_guard}"
|
85
|
+
yield "#define #{header_guard}"
|
86
|
+
yield
|
87
|
+
|
88
|
+
definition_includes.each { |filename| yield "#include <#{filename}>" }
|
89
|
+
yield
|
90
|
+
|
91
|
+
if @spec.key?('namespace')
|
92
|
+
yield "namespace #{@spec['namespace']} {"
|
93
|
+
yield
|
94
|
+
indent += 2
|
95
|
+
end
|
96
|
+
|
97
|
+
@doc.format_as_doxygen(max_line_length: 76) do |line|
|
98
|
+
yield "#{' ' * indent}#{line}"
|
99
|
+
end
|
100
|
+
|
101
|
+
yield "#{' ' * indent}enum class #{name} {"
|
102
|
+
indent += 2
|
103
|
+
|
104
|
+
elements = @spec['elements']
|
105
|
+
elements[0...-1].each do |element|
|
106
|
+
element_doc(element) { |line| yield "#{' ' * indent}#{line}" }
|
107
|
+
element_definition(element) { |line| yield "#{' ' * indent}#{line}," }
|
108
|
+
end
|
109
|
+
|
110
|
+
element_doc(elements.last) { |line| yield "#{' ' * indent}#{line}" }
|
111
|
+
element_definition(elements.last) do |line|
|
112
|
+
yield "#{' ' * indent}#{line}"
|
113
|
+
end
|
114
|
+
|
115
|
+
indent -= 2
|
116
|
+
yield "#{' ' * indent}};"
|
117
|
+
yield
|
118
|
+
yield '}' if @spec.key?('namespace')
|
119
|
+
yield
|
120
|
+
yield "#endif /* #{header_guard} */"
|
121
|
+
end
|
122
|
+
|
123
|
+
# A list of the includes needed for the definition of the enumeration.
|
124
|
+
def definition_includes
|
125
|
+
includes = @spec['includes'].dup
|
126
|
+
|
127
|
+
@spec['elements'].each do |element|
|
128
|
+
includes.concat(element['includes'])
|
129
|
+
end
|
130
|
+
|
131
|
+
includes.uniq
|
132
|
+
end
|
133
|
+
|
134
|
+
# Yields each line of the definition of an element.
|
135
|
+
def element_definition(element)
|
136
|
+
if element.key?('value')
|
137
|
+
yield "#{element['name']} = #{element['value']}"
|
138
|
+
else
|
139
|
+
yield element['name']
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Calls the given block once for each line of the documentation for an
|
144
|
+
# element.
|
145
|
+
def element_doc(element, &block)
|
146
|
+
doc = Comment.new(element.fetch('doc', nil))
|
147
|
+
doc.format_as_doxygen(max_line_length: 74) { |line| block.call(line) }
|
148
|
+
end
|
149
|
+
|
150
|
+
# The header guard for the enumeration.
|
151
|
+
def header_guard
|
152
|
+
"__#{@spec['name'].upcase}_HPP"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
2
|
+
|
3
|
+
# frozen_string_literal: true
|
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
|
+
#++
|
20
|
+
|
21
|
+
module Wrapture
|
22
|
+
# An error from the Wrapture library
|
23
|
+
class WraptureError < StandardError
|
24
|
+
end
|
25
|
+
|
26
|
+
# A documentation string is invalid.
|
27
|
+
class InvalidDoc < WraptureError
|
28
|
+
end
|
29
|
+
|
30
|
+
# A template has been invoked in an unsupported way.
|
31
|
+
class InvalidTemplateUsage < WraptureError
|
32
|
+
end
|
33
|
+
|
34
|
+
# The spec has a key that is not valid.
|
35
|
+
class InvalidSpecKey < WraptureError
|
36
|
+
# Creates an InvalidSpecKey with the given message. A list of valid values
|
37
|
+
# may optionally be passed to +valid_keys+ which will be added to the end
|
38
|
+
# of the message.
|
39
|
+
def initialize(message, valid_keys: [])
|
40
|
+
complete_message = message.dup
|
41
|
+
|
42
|
+
unless valid_keys.empty?
|
43
|
+
complete_message << ' (valid values are \''
|
44
|
+
complete_message << valid_keys.join('\', \'')
|
45
|
+
complete_message << '\')'
|
46
|
+
end
|
47
|
+
|
48
|
+
super(complete_message)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# The spec is missing a key that is required.
|
53
|
+
class MissingSpecKey < WraptureError
|
54
|
+
end
|
55
|
+
|
56
|
+
# Missing a namespace in the class spec
|
57
|
+
class NoNamespace < WraptureError
|
58
|
+
end
|
59
|
+
|
60
|
+
# The spec cannot be defined due to missing information.
|
61
|
+
class UndefinableSpec < WraptureError
|
62
|
+
end
|
63
|
+
|
64
|
+
# The spec version is not supported by this version of Wrapture.
|
65
|
+
class UnsupportedSpecVersion < WraptureError
|
66
|
+
end
|
67
|
+
end
|