wruby 0.0.1
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 +7 -0
- data/lib/wruby/format_error.rb +5 -0
- data/lib/wruby/metadata.rb +154 -0
- data/lib/wruby/util/hash.rb +11 -0
- data/lib/wruby/util/normalize.rb +9 -0
- data/lib/wruby/util/type_wrap.rb +94 -0
- data/lib/wruby.old/lib/method_definition.rb +118 -0
- data/lib/wruby.old/lib/module_definition.rb +46 -0
- data/lib/wruby.old/lib/parameter_definition.rb +23 -0
- data/lib/wruby.old/lib/return_definition.rb +14 -0
- data/lib/wruby.old/lib/structure_definition.rb +110 -0
- data/lib/wruby.old/lib/util/hash.rb +11 -0
- data/lib/wruby.old/lib/util/normalize.rb +9 -0
- data/lib/wruby.old/lib/util/type_matchup.rb +13 -0
- data/lib/wruby.old/lib/util/type_wrap.rb +77 -0
- data/lib/wruby.old/wruby.rb +38 -0
- data/lib/wruby.rb +3 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e868c3c2d079107e665b0de7bb170c4ba9c68b69b505364eabc5435060083c20
|
4
|
+
data.tar.gz: 192c37b854e25974f1da3dd3ed451827982eb9ef4f87789d3b96673859ca70b8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4c9c383325bea8bbb76fb4b9c08dc02c4da95cf60ef3b90e24cacd5663ac0d2a304dbeaa02bf10f74bf0429f6bae543aae3d66de649e8ce79fca0afde55544a1
|
7
|
+
data.tar.gz: 4f712a63f78ecb3bd88adaf8bc32804ba92b8382c8fac90a52b37898b406af68c248e678cd9d4e1605e1516583a3a25ae618e201c732f91b30fa723759a28bb5
|
@@ -0,0 +1,154 @@
|
|
1
|
+
require "wruby/format_error"
|
2
|
+
module WRuby
|
3
|
+
class MetaData
|
4
|
+
class UnrecognizedTypeError < StandardError
|
5
|
+
def initialize(name)
|
6
|
+
super("Unrecognized type: #{name}")
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def self.parse(data)
|
12
|
+
me = self.new
|
13
|
+
data["includes"].each {|f| add_header(f)}
|
14
|
+
data["typedefs"].each do |name, wrappings|
|
15
|
+
aliased = wrappings.key?("alias")
|
16
|
+
wrapped = wrappings.key?("wrap")
|
17
|
+
unwrapped = wrappings.key?("unwrap")
|
18
|
+
unless aliased || wrapped || unwrapped
|
19
|
+
raise FormatError, "Type with no deifnition: #{name}"
|
20
|
+
end
|
21
|
+
if aliased && (wrapped || unwrapped)
|
22
|
+
raise FormatError, "Ambiguous type definition: #{name}"
|
23
|
+
end
|
24
|
+
if wrapped ^ unwrapped
|
25
|
+
raise FormatError, "Incomplete type definition: #{name}"
|
26
|
+
end
|
27
|
+
|
28
|
+
if aliased
|
29
|
+
register_alias(name, wrappings["alias"])
|
30
|
+
elsif wrapped || unwrapped
|
31
|
+
register_wrapping(name, wrappings["wrap"], wrappings["unwrap"])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
data["wrapped_classes"].each {|name| register_class(name)}
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
@headers = []
|
39
|
+
@types = {}
|
40
|
+
@aliases = {}
|
41
|
+
@aliases.default_proc = proc {|h, k| k}
|
42
|
+
|
43
|
+
@classes = {}
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :headers, :classes
|
47
|
+
|
48
|
+
def add_header(file_name)
|
49
|
+
@headers << file_name unless @headers.include?(file_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def register_class(class_name)
|
53
|
+
return unless @classes.key?(class_name)
|
54
|
+
@classes[class_name] << Class.new do
|
55
|
+
def alloc
|
56
|
+
"#{class_name}_alloc"
|
57
|
+
end
|
58
|
+
def ctype
|
59
|
+
"#{class_name}_type"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def register_alias(alias_name, type)
|
65
|
+
@aliases[alias_name.to_s] = type.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
def register_wrapping(type, wrapping, unwrapping)
|
69
|
+
@types[type.to_s] = {wrap: wrapping, unwrap: unwrapping}
|
70
|
+
end
|
71
|
+
|
72
|
+
def wrap(code, type)
|
73
|
+
raise UnrecognizedTypeError.new(type) unless @types.key?(type) || @aliases.key?(type)
|
74
|
+
resolved_type = @aliases[type.to_s]
|
75
|
+
@types[resolved_type.to_s][:wrap].gsub(/\bthis\b/, code)
|
76
|
+
end
|
77
|
+
|
78
|
+
def unwrap(code, type)
|
79
|
+
raise UnrecognizedTypeError.new(type) unless @types.key?(type) || @aliases.key?(type)
|
80
|
+
resolved_type = @aliases[type.to_s]
|
81
|
+
@types[resolved_type.to_s][:unwrap].gsub(/\bthis\b/, code)
|
82
|
+
end
|
83
|
+
|
84
|
+
def alias_valid?(type)
|
85
|
+
@aliases.key?(type) && @types.key?(@aliases[type])
|
86
|
+
end
|
87
|
+
|
88
|
+
def has_type?(type)
|
89
|
+
@types.key?(type) || alias_valid?(type)
|
90
|
+
end
|
91
|
+
|
92
|
+
def each_class(&block)
|
93
|
+
return to_enum(__method__) unless block_given?
|
94
|
+
@wrapped_classes.each(&block)
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
def self.register_default_wrappings
|
99
|
+
# Basic integer types
|
100
|
+
register_wrapping("char", "INT2NUM(this)", "NUM2CHR(this)")
|
101
|
+
register_wrapping("short", "INT2NUM(this)", "NUM2SHORT(this)")
|
102
|
+
register_wrapping("int", "INT2NUM(this)", "NUM2INT(this)")
|
103
|
+
register_wrapping("long", "LONG2NUM(this)", "NUM2LONG(this)")
|
104
|
+
register_wrapping("long long", "LL2NUM(this)", "NUM2LL(this)")
|
105
|
+
# Basic unsigned integer types
|
106
|
+
register_wrapping("unsigned char", "UINT2NUM(this)", "NUM2UCHR(this)")
|
107
|
+
register_wrapping("unsigned short", "UINT2NUM(this)", "NUM2USHORT(this)")
|
108
|
+
register_wrapping("unsigned int", "UINT2NUM(this)", "NUM2UINT(this)")
|
109
|
+
register_wrapping("unsigned long", "ULONG2NUM(this)", "NUM2ULONG(this)")
|
110
|
+
register_wrapping("unsigned long long", "ULL2NUM(this)", "NUM2ULL(this)")
|
111
|
+
# unsigned abbreviations
|
112
|
+
register_alias("uchar", "unsigned char")
|
113
|
+
register_alias("ushort", "unsigned short")
|
114
|
+
register_alias("uint", "unsigned int")
|
115
|
+
register_alias("ulong", "unsigned long")
|
116
|
+
register_alias("long ulong", "unsigned long long") # This one is weird, but it makes more sense than ulong long
|
117
|
+
|
118
|
+
# Fixed integer types
|
119
|
+
register_wrapping("int8_t", "INT2NUM(this)", "(int8_t)NUM2CHR(this)")
|
120
|
+
register_wrapping("int16_t", "INT2NUM(this)", "(int16_t)NUM2SHORT(this)")
|
121
|
+
register_wrapping("int32_t", "INT2NUM(this)", "(int32_t)NUM2INT(this)")
|
122
|
+
register_wrapping("int64_t", "LONG2NUM(this)", "(int64_t)NUM2LONG(this)")
|
123
|
+
register_wrapping("uint8_t", "UINT2NUM(this)", "(int8_t)NUM2UINT(this)")
|
124
|
+
register_wrapping("uint16_t", "UINT2NUM(this)", "(int16_t)NUM2UINT(this)")
|
125
|
+
register_wrapping("uint32_t", "UINT2NUM(this)", "(int32_t)NUM2UINT(this)")
|
126
|
+
register_wrapping("uint64_t", "ULONG2NUM(this)", "(int64_t)NUM2ULONG(this)")
|
127
|
+
register_alias("int8", "int8_t")
|
128
|
+
register_alias("int16", "int16_t")
|
129
|
+
register_alias("int32", "int32_t")
|
130
|
+
register_alias("int64", "int64_t")
|
131
|
+
register_alias("uint8", "uint8_t")
|
132
|
+
register_alias("uint16", "uint16_t")
|
133
|
+
register_alias("uint32", "uint32_t")
|
134
|
+
register_alias("uint64", "uint64_t")
|
135
|
+
|
136
|
+
# Floating-point types
|
137
|
+
register_wrapping("float", "DBL2NUM(this)", "(float)NUM2DBL(this)")
|
138
|
+
register_wrapping("double", "DBL2NUM(this)", "NUM2DBL(this)")
|
139
|
+
# Fixed-size floating-point aliases
|
140
|
+
register_alias("float32", "float")
|
141
|
+
register_alias("float64", "double")
|
142
|
+
|
143
|
+
# Boolean type
|
144
|
+
register_wrapping("bool", "(this ? QTrue : QFalse)", "RTEST(this)")
|
145
|
+
|
146
|
+
# String types
|
147
|
+
# C-style strings
|
148
|
+
register_wrapping("char*", "rb_str_new_cstr(this)", "StringValueCStr(this)")
|
149
|
+
register_alias("cstring", "char*")
|
150
|
+
register_alias("cstr", "char*")
|
151
|
+
# Non-C-style strings are not handled here, as it's more complex than a simple inline
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module WRuby
|
2
|
+
@@types = {}
|
3
|
+
@@aliases = {}
|
4
|
+
@@aliases.default_proc = proc {|h, k| k}
|
5
|
+
|
6
|
+
class UnrecognizedTypeError < StandardError
|
7
|
+
def initialize(name)
|
8
|
+
super("Unrecognized type: #{name}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register_alias(alias_name, type)
|
13
|
+
@@aliases[alias_name.to_s] = type.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_wrapping(type, wrapping, unwrapping)
|
17
|
+
@@types[type.to_s] = {wrap: wrapping, unwrap: unwrapping}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.wrap(code, type)
|
21
|
+
raise UnrecognizedTypeError.new(type) unless @@types.key?(type) || @@aliases.key?(type)
|
22
|
+
resolved_type = @@aliases[type.to_s]
|
23
|
+
@@types[resolved_type.to_s][:wrap].gsub(/\bthis\b/, code)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.unwrap(code, type)
|
27
|
+
raise UnrecognizedTypeError.new(type) unless @@types.key?(type) || @@aliases.key?(type)
|
28
|
+
resolved_type = @@aliases[type.to_s]
|
29
|
+
@@types[resolved_type.to_s][:unwrap].gsub(/\bthis\b/, code)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.allocator_for(name)
|
33
|
+
"#{name}_alloc"
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.native_name_for(name)
|
37
|
+
"#{name}_type"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Basic integer types
|
41
|
+
register_wrapping("char", "INT2NUM(this)", "NUM2CHR(this)")
|
42
|
+
register_wrapping("short", "INT2NUM(this)", "NUM2SHORT(this)")
|
43
|
+
register_wrapping("int", "INT2NUM(this)", "NUM2INT(this)")
|
44
|
+
register_wrapping("long", "LONG2NUM(this)", "NUM2LONG(this)")
|
45
|
+
register_wrapping("long long", "LL2NUM(this)", "NUM2LL(this)")
|
46
|
+
# Basic unsigned integer types
|
47
|
+
register_wrapping("unsigned char", "UINT2NUM(this)", "NUM2UCHR(this)")
|
48
|
+
register_wrapping("unsigned short", "UINT2NUM(this)", "NUM2USHORT(this)")
|
49
|
+
register_wrapping("unsigned int", "UINT2NUM(this)", "NUM2UINT(this)")
|
50
|
+
register_wrapping("unsigned long", "ULONG2NUM(this)", "NUM2ULONG(this)")
|
51
|
+
register_wrapping("unsigned long long", "ULL2NUM(this)", "NUM2ULL(this)")
|
52
|
+
# unsigned abbreviations
|
53
|
+
register_alias("uchar", "unsigned char")
|
54
|
+
register_alias("ushort", "unsigned short")
|
55
|
+
register_alias("uint", "unsigned int")
|
56
|
+
register_alias("ulong", "unsigned long")
|
57
|
+
register_alias("long ulong", "unsigned long long") # This one is weird, but it makes more sense than ulong long
|
58
|
+
|
59
|
+
# Fixed integer types
|
60
|
+
register_wrapping("int8_t", "INT2NUM(this)", "(int8_t)NUM2CHR(this)")
|
61
|
+
register_wrapping("int16_t", "INT2NUM(this)", "(int16_t)NUM2SHORT(this)")
|
62
|
+
register_wrapping("int32_t", "INT2NUM(this)", "(int32_t)NUM2INT(this)")
|
63
|
+
register_wrapping("int64_t", "LONG2NUM(this)", "(int64_t)NUM2LONG(this)")
|
64
|
+
register_wrapping("uint8_t", "UINT2NUM(this)", "(int8_t)NUM2UINT(this)")
|
65
|
+
register_wrapping("uint16_t", "UINT2NUM(this)", "(int16_t)NUM2UINT(this)")
|
66
|
+
register_wrapping("uint32_t", "UINT2NUM(this)", "(int32_t)NUM2UINT(this)")
|
67
|
+
register_wrapping("uint64_t", "ULONG2NUM(this)", "(int64_t)NUM2ULONG(this)")
|
68
|
+
register_alias("int8", "int8_t")
|
69
|
+
register_alias("int16", "int16_t")
|
70
|
+
register_alias("int32", "int32_t")
|
71
|
+
register_alias("int64", "int64_t")
|
72
|
+
register_alias("uint8", "uint8_t")
|
73
|
+
register_alias("uint16", "uint16_t")
|
74
|
+
register_alias("uint32", "uint32_t")
|
75
|
+
register_alias("uint64", "uint64_t")
|
76
|
+
|
77
|
+
# Floating-point types
|
78
|
+
register_wrapping("float", "DBL2NUM(this)", "(float)NUM2DBL(this)")
|
79
|
+
register_wrapping("double", "DBL2NUM(this)", "NUM2DBL(this)")
|
80
|
+
# Fixed-size floating-point aliases
|
81
|
+
register_alias("float32", "float")
|
82
|
+
register_alias("float64", "double")
|
83
|
+
|
84
|
+
# Boolean type
|
85
|
+
register_wrapping("bool", "(this ? QTrue : QFalse)", "RTEST(this)")
|
86
|
+
|
87
|
+
# String types
|
88
|
+
# C-style strings
|
89
|
+
register_wrapping("char*", "rb_str_new_cstr(this)", "StringValueCStr(this)")
|
90
|
+
register_alias("cstring", "char*")
|
91
|
+
register_alias("cstr", "char*")
|
92
|
+
# Non-C-style strings are not handled here, as it's more complex than a simple inline
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "wruby/util/hash"
|
2
|
+
require "wruby/parameter_definition"
|
3
|
+
require "wruby/return_definition"
|
4
|
+
require "wruby/util/normalize"
|
5
|
+
module WRuby
|
6
|
+
class MethodDefinition
|
7
|
+
|
8
|
+
attr_reader :name, :parameters, :return, :method_type, :wrapper, :wrapped_function, :definition, :alias
|
9
|
+
|
10
|
+
def initialize(data)
|
11
|
+
@name = data.require("name", "Method definition missing name")
|
12
|
+
@parameters = data.require("parameters", "Method \'#{data["name"]}' missing 'parameters' field")
|
13
|
+
@parameters.map!{|e| ParameterDefinition.new(e)} unless @parameters.nil?
|
14
|
+
@return = data.require("return", "Method \'#{data["name"]}' missing 'return' field")
|
15
|
+
@return = ReturnDefinition.new(@return) unless @return.nil?
|
16
|
+
@method_type = data.require("method_type", "Method \'#{data["name"]}' missing 'method_type' field")
|
17
|
+
raise FormatError, "method_type '#{@method_type}' unsupported" unless ["constructor", "method", "module", "singleton"].include?(@method_type)
|
18
|
+
@wrapper = data.key? "wrapped_function"
|
19
|
+
if @wrapper
|
20
|
+
@wrapped_function = data.require("wrapped_function", "Method \'#{data["name"]}' missing wrapped_function")
|
21
|
+
else
|
22
|
+
@definition = data.require("definition", "Method \'#{data["name"]}' missing definition")
|
23
|
+
end
|
24
|
+
@alias = data["alias"]
|
25
|
+
end
|
26
|
+
|
27
|
+
def param_count(include_out = false)
|
28
|
+
return -1 if optional_params?
|
29
|
+
@parameters.filter{|p| include_out || !p.out?}.count
|
30
|
+
end
|
31
|
+
|
32
|
+
def optional_params?
|
33
|
+
@parameters.any?{|p| p.optional?}
|
34
|
+
end
|
35
|
+
|
36
|
+
def out_params
|
37
|
+
@parameters.filter{|p| p.out?}
|
38
|
+
end
|
39
|
+
|
40
|
+
def aliased?
|
41
|
+
!@alias.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
def qualified_name(klass_name)
|
45
|
+
"#{klass_name}#{(module? || singleton?) ? "_m" : ""}_#{WRuby::normalize_ruby_name(name)}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def render(klass_spec)
|
49
|
+
"#{render_signature(klass_spec.qualified_name)} + {#{render_body(klass_spec)}}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def render_signature(klass_name)
|
53
|
+
param_list = @parameters.filter{|p| !p.out?}.map do |param|
|
54
|
+
"VALUE #{param.name}"
|
55
|
+
end.join(", ")
|
56
|
+
"VALUE #{qualified_name(klass_name)}(VALUE self#{", #{param_list}" unless param_list.empty?})"
|
57
|
+
end
|
58
|
+
|
59
|
+
def render_body(klass_spec)
|
60
|
+
if method?
|
61
|
+
render_method
|
62
|
+
elsif module?
|
63
|
+
render_module_function
|
64
|
+
elsif singleton?
|
65
|
+
render_singleton_function
|
66
|
+
elsif constructor?
|
67
|
+
render_constructor
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def singleton?
|
72
|
+
@method_type == "singleton"
|
73
|
+
end
|
74
|
+
|
75
|
+
def module?
|
76
|
+
@method_type == "module"
|
77
|
+
end
|
78
|
+
|
79
|
+
def method?
|
80
|
+
@method_type == "method"
|
81
|
+
end
|
82
|
+
|
83
|
+
def constructor?
|
84
|
+
@method_type == "constructor"
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
private
|
89
|
+
def render_method(klass_spec)
|
90
|
+
""
|
91
|
+
end
|
92
|
+
|
93
|
+
def render_module_function(klass_spec)
|
94
|
+
""
|
95
|
+
end
|
96
|
+
|
97
|
+
def render_singleton_function(klass_spec)
|
98
|
+
""
|
99
|
+
end
|
100
|
+
|
101
|
+
def render_constructor(klass_spec)
|
102
|
+
""
|
103
|
+
end
|
104
|
+
|
105
|
+
def declare_out_variables
|
106
|
+
out_params.map do |p|
|
107
|
+
"#{p.type} #{p.name};\n"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def get_self(klass_spec)
|
112
|
+
<<-SELF
|
113
|
+
#{klass_spec.underlying_type}* that;
|
114
|
+
TypedData_Get_Struct(self, #{klass_spec.underlying_type}, &#{klass_spec.type_name}, that);
|
115
|
+
SELF
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "wruby/util/hash"
|
2
|
+
require "wruby/method_definition"
|
3
|
+
module WRuby
|
4
|
+
class ModuleDefinition
|
5
|
+
attr_reader :name
|
6
|
+
def initialize(data)
|
7
|
+
@name = data.require("name", "Module definition requires a name")
|
8
|
+
@methods = data["methods"]
|
9
|
+
@methods&.map!{|e| MethodDefinition.new(e)}
|
10
|
+
@free_method = data["free"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def render_methods
|
14
|
+
@methods.map do |m|
|
15
|
+
m.render_signature(qualified_name)
|
16
|
+
end.join("\n\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
def qualified_name
|
20
|
+
return "m#{@name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def render_definitions(module_name)
|
24
|
+
module_def = "VALUE #{qualified_name} = rb_define_module"
|
25
|
+
module_def += module_name.nil? ? "(" : "_under(#{module_name}, "
|
26
|
+
module_def += "\"#{@name}\");\n"
|
27
|
+
|
28
|
+
definitions = @methods.map do |m|
|
29
|
+
rb_method = case m.method_type
|
30
|
+
when "singleton"
|
31
|
+
"rb_define_singleton_method"
|
32
|
+
else
|
33
|
+
"rb_define_method"
|
34
|
+
end
|
35
|
+
rb_method += "(#{qualified_name}, \"#{m.name}\", #{m.qualified_name(qualified_name)}, #{m.param_count});"
|
36
|
+
if m.aliased? && m.method_type != "singleton"
|
37
|
+
rb_method += "\nrb_define_alias(#{qualified_name}, \"#{m.alias}\", \"#{m.name}\""
|
38
|
+
end
|
39
|
+
|
40
|
+
rb_method;
|
41
|
+
end.join("\n")
|
42
|
+
|
43
|
+
module_def + definitions
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "wruby/util/hash"
|
2
|
+
module WRuby
|
3
|
+
class ParameterDefinition
|
4
|
+
attr_accessor :name, :type, :default
|
5
|
+
|
6
|
+
def initialize(data)
|
7
|
+
@name = data.require("name", "Parameter definition missing name")
|
8
|
+
raise FormatError, "Parameter cannot be named 'self'" if @name == "self"
|
9
|
+
@type = data.require("type", "Parameter #{@name} missing its type")
|
10
|
+
@default = data["default"]
|
11
|
+
@optional = data["optional"] || !!@default
|
12
|
+
@out_param = !!data["out"]
|
13
|
+
end
|
14
|
+
|
15
|
+
def out?
|
16
|
+
@out_param
|
17
|
+
end
|
18
|
+
|
19
|
+
def optional?
|
20
|
+
@optional
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require "wruby/util/hash"
|
2
|
+
require "wruby/method_definition"
|
3
|
+
module WRuby
|
4
|
+
class StructureDefinition
|
5
|
+
attr_reader :name
|
6
|
+
def initialize(data)
|
7
|
+
@type = data.require("type", "Structure definition requires a type (module or class)")
|
8
|
+
@name = data.require("name", "Structure definition requires a name")
|
9
|
+
@methods = data["methods"]
|
10
|
+
@methods.map!{|e| MethodDefinition.new(e)} unless @methods.nil?
|
11
|
+
@parent = data["parent"]
|
12
|
+
@underlying_type = data.require("basetype", "Class requires an underlying type") if @type == "class"
|
13
|
+
@free_function = data["free"]
|
14
|
+
raise FormatError, "Modules do not require freeing" if @free_method && @type == "module"
|
15
|
+
end
|
16
|
+
|
17
|
+
def render_methods
|
18
|
+
@methods.map do |m|
|
19
|
+
m.render(self)
|
20
|
+
end.join("\n\n")
|
21
|
+
end
|
22
|
+
|
23
|
+
def qualified_name
|
24
|
+
case @type
|
25
|
+
when "class" then "c#{name}"
|
26
|
+
when "module" then "m#{name}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def camel_case(_name = @name)
|
31
|
+
_name.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
32
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
33
|
+
downcase
|
34
|
+
end
|
35
|
+
|
36
|
+
def render_definitions(module_name)
|
37
|
+
|
38
|
+
module_def = "VALUE #{qualified_name} = rb_define_#{@type}"
|
39
|
+
module_def += module_name.nil? ? "(" : "_under(#{module_name}, "
|
40
|
+
module_def += "\"#{@name}\""
|
41
|
+
module_def += case @type
|
42
|
+
when "class" then ", #{@parent || "rb_cObject"}"
|
43
|
+
when "module" then ""
|
44
|
+
end + ");\n"
|
45
|
+
|
46
|
+
definitions = @methods.map do |m|
|
47
|
+
rb_method = case m.method_type
|
48
|
+
when "singleton"
|
49
|
+
"rb_define_singleton_method"
|
50
|
+
when "method", "constructor"
|
51
|
+
"rb_define_method"
|
52
|
+
when "module"
|
53
|
+
"rb_define_module_function"
|
54
|
+
end
|
55
|
+
rb_method += "(#{qualified_name}, \"#{m.name}\", #{m.qualified_name(qualified_name)}, #{m.param_count});"
|
56
|
+
if m.aliased? && !m.singleton?
|
57
|
+
rb_method += "\nrb_define_alias(#{qualified_name}, \"#{m.alias}\", \"#{m.name}\");"
|
58
|
+
end
|
59
|
+
|
60
|
+
rb_method;
|
61
|
+
end.join("\n")
|
62
|
+
|
63
|
+
module_def + definitions
|
64
|
+
end
|
65
|
+
|
66
|
+
def render_free
|
67
|
+
free_func = "void #{camel_case}_free(void* data) {\n"
|
68
|
+
free_func +=" #{@underlying_type}* self = (#{@underlying_type}*)data;\n"
|
69
|
+
if @free_function
|
70
|
+
free_func += if @free_function.start_with?(".")
|
71
|
+
" self->~#{@underlying_type}();\n"
|
72
|
+
else
|
73
|
+
" #{@free_function}(self);\n"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
free_func += " free(self);\n}\n"
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
def render_size
|
81
|
+
<<-SIZE
|
82
|
+
size_t #{camel_case}_size(const void*) {
|
83
|
+
return sizeof(#{@underlying_type});
|
84
|
+
}
|
85
|
+
SIZE
|
86
|
+
end
|
87
|
+
|
88
|
+
def type_name
|
89
|
+
"#{camel_case}_type"
|
90
|
+
end
|
91
|
+
|
92
|
+
def render_type(namespace = "")
|
93
|
+
<<-DEF
|
94
|
+
const rb_data_type_t #{type_name}} = {
|
95
|
+
.wrap_struct_name = "#{namespace}_#{@name}",
|
96
|
+
.function = {
|
97
|
+
.dmark = NULL,
|
98
|
+
.dfree = #{camel_case}_free,
|
99
|
+
.dsize = #{camel_case}_size,
|
100
|
+
},
|
101
|
+
.parent = #{@parent ? "&#{camel_case(@parent)}_type" : "NULL"},
|
102
|
+
.data = NULL,
|
103
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY
|
104
|
+
};
|
105
|
+
DEF
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module WRuby
|
2
|
+
def self.generalize_type(type_name)
|
3
|
+
case type_name
|
4
|
+
when /u.*64/ then "ulong"
|
5
|
+
when /64/ then "long"
|
6
|
+
when /^uint/ then "uint"
|
7
|
+
when /^int/ then "int"
|
8
|
+
when /char\*/ then "string"
|
9
|
+
when "float" then "double"
|
10
|
+
else type_name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module WRuby
|
2
|
+
@@types = {}
|
3
|
+
@@aliases = {}
|
4
|
+
@@aliases.default_proc = proc {|h, k| k}
|
5
|
+
|
6
|
+
class UnrecognizedTypeError < StandardError
|
7
|
+
def initialize(name)
|
8
|
+
super("Unrecognized type: #{name}")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register_alias(alias_name, type)
|
13
|
+
@@aliases[alias_name.to_s] = type.to_s
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.register_wrapping(type, wrapping, unwrapping)
|
17
|
+
@@types[type.to_s] = {wrap: wrapping, unwrap: unwrapping}
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.wrap(name, type)
|
21
|
+
raise UnrecognizedTypeError.new(type) unless @@types.key?(type) || @@aliases.key?(type)
|
22
|
+
@@types[type.to_s][:wrap].gsub(/\wthis\w/, name)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.unwrap(name, type)
|
26
|
+
raise UnrecognizedTypeError.new(type) unless @@types.key?(type) || @@aliases.key?(type)
|
27
|
+
resolved_name = @@aliases[type.to_s]
|
28
|
+
@@types[resolved_name.to_s][:unwrap].gsub(/\wthis\w/, name)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Basic integer types
|
32
|
+
register_wrapping("char", "INT2NUM(this)", "NUM2CHR(this)")
|
33
|
+
register_wrapping("short", "INT2NUM(this)", "NUM2SHORT(this)")
|
34
|
+
register_wrapping("int", "INT2NUM(this)", "NUM2INT(this)")
|
35
|
+
register_wrapping("long", "LONG2NUM(this)", "NUM2LONG(this)")
|
36
|
+
register_wrapping("long long", "LL2NUM(this)", "NUM2LL(this)")
|
37
|
+
# Basic unsigned integer types
|
38
|
+
register_wrapping("unsigned char", "UINT2NUM(this)", "NUM2UCHR(this)")
|
39
|
+
register_wrapping("unsigned short", "UINT2NUM(this)", "NUM2USHORT(this)")
|
40
|
+
register_wrapping("unsigned int", "UINT2NUM(this)", "NUM2UINT(this)")
|
41
|
+
register_wrapping("unsigned long", "ULONG2NUM(this)", "NUM2ULONG(this)")
|
42
|
+
register_wrapping("unsigned long long", "ULL2NUM(this)", "NUM2ULL(this)")
|
43
|
+
# unsigned abbreviations
|
44
|
+
register_alias("uchar", "unsigned char")
|
45
|
+
register_alias("ushort", "unsigned short")
|
46
|
+
register_alias("uint", "unsigned int")
|
47
|
+
register_alias("ulong", "unsigned long")
|
48
|
+
register_alias("long ulong", "unsigned long long") # This one is weird, but it makes more sense than ulong long
|
49
|
+
|
50
|
+
# Fixed integer types
|
51
|
+
register_wrapping("int8_t", "INT2NUM(this)", "(int8_t)NUM2CHR(this)")
|
52
|
+
register_wrapping("int16_t", "INT2NUM(this)", "(int16_t)NUM2SHORT(this)")
|
53
|
+
register_wrapping("int32_t", "INT2NUM(this)", "(int32_t)NUM2INT(this)")
|
54
|
+
register_wrapping("int64_t", "LONG2NUM(this)", "(int64_t)NUM2LONG(this)")
|
55
|
+
register_wrapping("uint8_t", "UINT2NUM(this)", "(int8_t)NUM2UINT(this)")
|
56
|
+
register_wrapping("uint16_t", "UINT2NUM(this)", "(int16_t)NUM2UINT(this)")
|
57
|
+
register_wrapping("uint32_t", "UINT2NUM(this)", "(int32_t)NUM2UINT(this)")
|
58
|
+
register_wrapping("uint64_t", "ULONG2NUM(this)", "(int64_t)NUM2ULONG(this)")
|
59
|
+
|
60
|
+
# Floating-point types
|
61
|
+
register_wrapping("float", "DBL2NUM(this)", "(float)NUM2DBL(this)")
|
62
|
+
register_wrapping("double", "DBL2NUM(this)", "NUM2DBL(this)")
|
63
|
+
# Fixed-size floating-point aliases
|
64
|
+
register_alias("float32", "float")
|
65
|
+
register_alias("float64", "double")
|
66
|
+
|
67
|
+
# Boolean type
|
68
|
+
register_wrapping("bool", "(this ? QTrue : QFalse)", "RTEST(this)")
|
69
|
+
|
70
|
+
# String types
|
71
|
+
# C-style strings
|
72
|
+
register_wrapping("char*", "rb_str_new_cstr(this)", "StringValueCStr(this)")
|
73
|
+
register_alias("cstring", "char*")
|
74
|
+
register_alias("cstr", "char*")
|
75
|
+
# Non-C-style strings are not handled here, as it's more complex than a simple inline
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "psych"
|
2
|
+
|
3
|
+
require "wruby/structure_definition"
|
4
|
+
require "wruby/module_definition"
|
5
|
+
require "wruby/format_error"
|
6
|
+
|
7
|
+
module WRuby
|
8
|
+
def self.parse(source)
|
9
|
+
data = Psych.load_stream(source)
|
10
|
+
raise FormatError, "Specification too short" if data.size < 2
|
11
|
+
data[1..].map do |d|
|
12
|
+
StructureDefinition.new(d)
|
13
|
+
end.unshift(data[0])
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse_file(file)
|
17
|
+
parse(File.read(file))
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.render_functions(spec)
|
21
|
+
spec[1..].map do |mspec|
|
22
|
+
mspec.render_methods
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.render_ruby_definitions(spec)
|
27
|
+
spec[1..].map.with_index do |mspec, i|
|
28
|
+
parent = nil
|
29
|
+
parent = spec[i].qualified_name unless i == 0
|
30
|
+
mspec.render_definitions(parent)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.render(spec)
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/wruby.rb
ADDED
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kellen Watt
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-11-27 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Generates code for C/C++ code based on YAML sepcifications.
|
14
|
+
email:
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- lib/wruby.old/lib/method_definition.rb
|
20
|
+
- lib/wruby.old/lib/module_definition.rb
|
21
|
+
- lib/wruby.old/lib/parameter_definition.rb
|
22
|
+
- lib/wruby.old/lib/return_definition.rb
|
23
|
+
- lib/wruby.old/lib/structure_definition.rb
|
24
|
+
- lib/wruby.old/lib/util/hash.rb
|
25
|
+
- lib/wruby.old/lib/util/normalize.rb
|
26
|
+
- lib/wruby.old/lib/util/type_matchup.rb
|
27
|
+
- lib/wruby.old/lib/util/type_wrap.rb
|
28
|
+
- lib/wruby.old/wruby.rb
|
29
|
+
- lib/wruby.rb
|
30
|
+
- lib/wruby/format_error.rb
|
31
|
+
- lib/wruby/metadata.rb
|
32
|
+
- lib/wruby/util/hash.rb
|
33
|
+
- lib/wruby/util/normalize.rb
|
34
|
+
- lib/wruby/util/type_wrap.rb
|
35
|
+
homepage:
|
36
|
+
licenses:
|
37
|
+
- MIT
|
38
|
+
metadata: {}
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
requirements: []
|
54
|
+
rubygems_version: 3.3.7
|
55
|
+
signing_key:
|
56
|
+
specification_version: 4
|
57
|
+
summary: Name reservation for a future project
|
58
|
+
test_files: []
|