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.
@@ -0,0 +1,87 @@
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
+ require 'wrapture/constants'
20
+ require 'wrapture/errors'
21
+
22
+ module Wrapture
23
+ # A condition (or set of conditions) that a struct or its members must meet
24
+ # in order to conform to a given specification. This allows a single struct
25
+ # type to be equivalent to some class specifications, but not others.
26
+ class RuleSpec
27
+ # A list of valid condition strings.
28
+ CONDITIONS = %w[equals not-equals].freeze
29
+
30
+ # Normalizes a hash specification of a rule. Normalization checks for
31
+ # invalid keys and unrecognized conditions.
32
+ def self.normalize_spec_hash(spec)
33
+ normalized = spec.dup
34
+
35
+ required_keys = if spec.key?('member-name')
36
+ normalized['type'] = 'struct-member'
37
+ %w[member-name condition value].freeze
38
+ else
39
+ normalized['type'] = 'expression'
40
+ %w[left-expression condition right-expression].freeze
41
+ end
42
+
43
+ missing_keys = required_keys - spec.keys
44
+ unless missing_keys.empty?
45
+ missing_msg = "required keys are missing: #{missing_keys.join(', ')}"
46
+ raise(MissingSpecKey, missing_msg)
47
+ end
48
+
49
+ extra_keys = spec.keys - required_keys
50
+ unless extra_keys.empty?
51
+ extra_msg = "these keys are unrecognized: #{extra_keys.join(', ')}"
52
+ raise(InvalidSpecKey, extra_msg)
53
+ end
54
+
55
+ unless RuleSpec::CONDITIONS.include?(spec['condition'])
56
+ condition_msg = "#{spec['condition']} is an invalid condition"
57
+ raise(InvalidSpecKey, condition_msg)
58
+ end
59
+
60
+ normalized
61
+ end
62
+
63
+ # Creates a rule spec based on the provided spec.
64
+ #
65
+ # The hash must have the following keys:
66
+ # member-name:: the name of the struct member the rule applies to
67
+ # condition:: the condition this rule uses (supported values are held in the
68
+ # RuleSpec::CONDITIONS list)
69
+ # value:: the value to use in the condition check
70
+ def initialize(spec)
71
+ @spec = RuleSpec.normalize_spec_hash(spec)
72
+ end
73
+
74
+ # A string containing a check for a struct of the given name for this rule.
75
+ def check(variable: nil)
76
+ condition = @spec['condition'] == 'equals' ? '==' : '!='
77
+
78
+ if @spec['type'] == 'struct-member'
79
+ "#{variable}->#{@spec['member-name']} #{condition} #{@spec['value']}"
80
+ else
81
+ left = @spec['left-expression']
82
+ right = @spec['right-expression']
83
+ "#{left} #{condition} #{right}".sub(RETURN_VALUE_KEYWORD, 'return_val')
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,80 @@
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
+ # Describes a scope of one or more class specifications.
21
+ class Scope
22
+ # A list of classes currently in the scope.
23
+ attr_reader :classes
24
+
25
+ # Creates an empty scope with no classes in it.
26
+ def initialize(spec = nil)
27
+ @classes = []
28
+
29
+ return if spec.nil? || !spec.key?('classes')
30
+
31
+ @version = Wrapture.spec_version(spec)
32
+ spec['classes'].each do |class_hash|
33
+ ClassSpec.new(class_hash, scope: self)
34
+ end
35
+ end
36
+
37
+ # Adds a class specification to the scope.
38
+ #
39
+ # This does not set the scope as the owner of the class. This must be done
40
+ # during the construction of the class spec.
41
+ def <<(spec)
42
+ @classes << spec if spec.is_a?(ClassSpec)
43
+ end
44
+
45
+ # Generates the wrapper class files for all classes in the scope.
46
+ def generate_wrappers
47
+ files = []
48
+
49
+ @classes.each do |class_spec|
50
+ files.concat(class_spec.generate_wrappers)
51
+ end
52
+
53
+ files
54
+ end
55
+
56
+ # A list of ClassSpecs in this scope that are overloads of the given class.
57
+ def overloads(parent)
58
+ @classes.select { |class_spec| class_spec.overloads?(parent) }
59
+ end
60
+
61
+ # True if there is an overload of the given class in this scope.
62
+ def overloads?(parent)
63
+ @classes.any? { |class_spec| class_spec.overloads?(parent) }
64
+ end
65
+
66
+ # Returns the ClassSpec for the given type in the scope.
67
+ def type(type)
68
+ @classes.find { |class_spec| class_spec.name == type }
69
+ end
70
+
71
+ # Returns true if the given type is in the scope.
72
+ def type?(type)
73
+ @classes.each do |class_spec|
74
+ return true if class_spec.name == type
75
+ end
76
+
77
+ false
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,127 @@
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
+ # A description of a struct.
21
+ class StructSpec
22
+ # Normalizes a hash specification of a struct. Normalization will check for
23
+ # things like invalid keys, duplicate entries in include lists, and will set
24
+ # missing keys to their default value (for example, an empty list if no
25
+ # includes are given).
26
+ def self.normalize_spec_hash(spec)
27
+ normalized = spec.dup
28
+ normalized.default = []
29
+
30
+ normalized['includes'] = Wrapture.normalize_includes spec['includes']
31
+
32
+ normalized['members'] ||= []
33
+
34
+ normalized
35
+ end
36
+
37
+ # A list of rules defined for this struct.
38
+ attr_reader :rules
39
+
40
+ # Creates a struct spec based on the provided spec hash.
41
+ #
42
+ # The hash must have the following keys:
43
+ # name:: the name of the struct
44
+ #
45
+ # The following keys are optional:
46
+ # includes:: a list of includes required for the struct
47
+ # members:: a list of the members of the struct, each with a type and name
48
+ # field
49
+ # rules:: a list of conditions this struct and its members must meet (refer
50
+ # to the RuleSpec class for more details)
51
+ def initialize(spec)
52
+ @spec = StructSpec.normalize_spec_hash(spec)
53
+
54
+ @rules = @spec['rules'].map { |rule_spec| RuleSpec.new(rule_spec) }
55
+ end
56
+
57
+ # A declaration of the struct with the given variable name.
58
+ def declaration(name)
59
+ "struct #{@spec['name']} #{name}"
60
+ end
61
+
62
+ # A list of includes required for this struct.
63
+ def includes
64
+ @spec['includes'].dup
65
+ end
66
+
67
+ # A string containing the typed members of the struct, separated by commas.
68
+ def member_list
69
+ members = []
70
+
71
+ @spec['members'].each do |member|
72
+ members << ClassSpec.typed_variable(member['type'], member['name'])
73
+ end
74
+
75
+ members.join ', '
76
+ end
77
+
78
+ # A string containing the typed members of the struct, with their default
79
+ # values if provided, separated by commas.
80
+ def member_list_with_defaults
81
+ @spec['members'].map do |member|
82
+ member_str = ClassSpec.typed_variable(member['type'], member['name'])
83
+
84
+ if member.key?('default-value')
85
+ default_value = member['default-value']
86
+
87
+ member_str += ' = '
88
+ member_str += if member['type'] == 'const char *'
89
+ '"' + default_value + '"'
90
+ elsif member['type'].end_with?('char')
91
+ "'#{default_value}'"
92
+ else
93
+ default_value.to_s
94
+ end
95
+ end
96
+
97
+ member_str
98
+ end.join(', ')
99
+ end
100
+
101
+ # The members of the struct
102
+ def members
103
+ @spec['members']
104
+ end
105
+
106
+ # True if there are members included in the struct specification.
107
+ def members?
108
+ !@spec['members'].empty?
109
+ end
110
+
111
+ # The name of this struct
112
+ def name
113
+ @spec['name']
114
+ end
115
+
116
+ # A declaration of a pointer to the struct with the given variable name.
117
+ def pointer_declaration(name)
118
+ "struct #{@spec['name']} *#{name}"
119
+ end
120
+
121
+ # A string containing an expression that returns true if the struct with
122
+ # the given name meets all rules defined for this struct.
123
+ def rules_check(name)
124
+ @rules.map { |rule| rule.check(variable: name) }.join(' && ')
125
+ end
126
+ end
127
+ end
@@ -1,5 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Wrapture
4
- VERSION = '0.2.2'
4
+ # the current version of Wrapture
5
+ VERSION = '0.3.0'
6
+
7
+ # Returns true if the version of the spec is supported by this version of
8
+ # Wrapture. Otherwise returns false.
9
+ def self.supports_version?(version)
10
+ wrapture_version = Gem::Version.new(Wrapture::VERSION)
11
+ spec_version = Gem::Version.new(version)
12
+
13
+ spec_version <= wrapture_version
14
+ end
5
15
  end
@@ -0,0 +1,117 @@
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 description of a function to be wrapped by another language.
21
+ class WrappedFunctionSpec
22
+ # Normalizes a hash specification of a wrapped function. Normalization will
23
+ # check for things like invalid keys, duplicate entries in include lists,
24
+ # and will set missing keys to their default values (for example, an empty
25
+ # list if no includes are given).
26
+ def self.normalize_spec_hash(spec)
27
+ normalized = spec.dup
28
+
29
+ normalized['params'] ||= []
30
+ normalized['params'].each do |param_spec|
31
+ param_spec['value'] = param_spec['name'] if param_spec['value'].nil?
32
+ end
33
+
34
+ normalized['includes'] = Wrapture.normalize_includes(spec['includes'])
35
+
36
+ normalized['error-check'] ||= {}
37
+ normalized['error-check']['rules'] ||= []
38
+
39
+ unless spec.key?('return')
40
+ normalized['return'] = {}
41
+ normalized['return']['type'] = 'void'
42
+ end
43
+
44
+ normalized
45
+ end
46
+
47
+ # Creates a wrapped function spec based on the provided spec.
48
+ #
49
+ # The hash must have the following keys:
50
+ # name:: the name of the wrapped function
51
+ # params:: a list of parameters to supply when calling
52
+ #
53
+ # Each member of the params list must be a hash, with a mandatory key of
54
+ # 'value' holding the value to be supplied as the parameter. If only a
55
+ # 'name' key is provided, this will be used as the value. A 'type' may be
56
+ # supplied as well, and is necessary if an equivalent struct or pointer is
57
+ # to be supplied as the value so that casting can be performed correctly.
58
+ #
59
+ # The following key is optional:
60
+ # includes:: a list of includes needed for this function
61
+ def initialize(spec)
62
+ @spec = self.class.normalize_spec_hash(spec)
63
+
64
+ check = @spec['error-check']
65
+
66
+ @error_rules = check['rules'].map do |rule_spec|
67
+ RuleSpec.new(rule_spec)
68
+ end
69
+
70
+ action = check['error-action']
71
+ @error_action = ActionSpec.new(action) unless @error_rules.empty?
72
+ end
73
+
74
+ # Generates a function call from a provided FunctionSpec. Paremeters and
75
+ # types are resolved using this function's context.
76
+ def call_from(function_spec)
77
+ resolved_params = []
78
+
79
+ @spec['params'].each do |param|
80
+ resolved_params << function_spec.resolve_wrapped_param(param)
81
+ end
82
+
83
+ "#{@spec['name']}( #{resolved_params.join(', ')} )"
84
+ end
85
+
86
+ # Yields each line of the error check and any actions taken for this wrapped
87
+ # function. If this function does not have any error check defined, then
88
+ # this function returns without yielding anything.
89
+ def error_check
90
+ return if @error_rules.empty?
91
+
92
+ checks = @error_rules.map(&:check)
93
+ yield "if( #{checks.join(' && ')} ){"
94
+ yield " #{@error_action.take};"
95
+ yield '}'
96
+ end
97
+
98
+ # True if the wrapped function has an error check associated with it.
99
+ def error_check?
100
+ !@error_rules.empty?
101
+ end
102
+
103
+ # A list of includes required for this function call.
104
+ def includes
105
+ includes = @spec['includes'].dup
106
+
107
+ includes.concat(@error_action.includes) if error_check?
108
+
109
+ includes
110
+ end
111
+
112
+ # A string with the type of the return value.
113
+ def return_val_type
114
+ @spec['return']['type']
115
+ end
116
+ end
117
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wrapture
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Anderson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-04-01 00:00:00.000000000 Z
11
+ date: 2020-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -17,13 +17,19 @@ dependencies:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 1.6.4
20
- type: :runtime
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.2'
23
+ type: :development
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
27
  - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: 1.6.4
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.2'
27
33
  description: Wraps C code in C++.
28
34
  email: joelanderson333@gmail.com
29
35
  executables:
@@ -33,10 +39,18 @@ extra_rdoc_files: []
33
39
  files:
34
40
  - bin/wrapture
35
41
  - lib/wrapture.rb
42
+ - lib/wrapture/action_spec.rb
36
43
  - lib/wrapture/class_spec.rb
37
44
  - lib/wrapture/constant_spec.rb
45
+ - lib/wrapture/constants.rb
46
+ - lib/wrapture/errors.rb
38
47
  - lib/wrapture/function_spec.rb
48
+ - lib/wrapture/normalize.rb
49
+ - lib/wrapture/rule_spec.rb
50
+ - lib/wrapture/scope.rb
51
+ - lib/wrapture/struct_spec.rb
39
52
  - lib/wrapture/version.rb
53
+ - lib/wrapture/wrapped_function_spec.rb
40
54
  homepage: http://rubygems.org/gems/wrapture
41
55
  licenses:
42
56
  - Apache-2.0