crystalruby 0.1.13 → 0.2.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/.dockerignore +10 -0
- data/README.md +37 -17
- data/exe/crystalruby +12 -14
- data/lib/crystalruby/adapter.rb +98 -70
- data/lib/crystalruby/compilation.rb +13 -82
- data/lib/crystalruby/config.rb +14 -17
- data/lib/crystalruby/function.rb +228 -0
- data/lib/crystalruby/library.rb +198 -0
- data/lib/crystalruby/reactor.rb +32 -61
- data/lib/crystalruby/template.rb +0 -1
- data/lib/crystalruby/templates/index.cr +33 -12
- data/lib/crystalruby/typebuilder.rb +2 -0
- data/lib/crystalruby/typemaps.rb +87 -1
- data/lib/crystalruby/types/type_serializer/json.rb +3 -2
- data/lib/crystalruby/version.rb +1 -1
- data/lib/crystalruby.rb +24 -253
- metadata +5 -2
data/lib/crystalruby/typemaps.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module CrystalRuby
|
2
4
|
module Typemaps
|
3
5
|
CRYSTAL_TYPE_MAP = {
|
@@ -66,6 +68,90 @@ module CrystalRuby
|
|
66
68
|
void: {
|
67
69
|
to: "nil"
|
68
70
|
}
|
69
|
-
|
71
|
+
}.tap do |hash|
|
72
|
+
hash.define_singleton_method(:convert) do |type, dir, expr|
|
73
|
+
if hash.key?(type)
|
74
|
+
conversion_string = hash[type][dir]
|
75
|
+
conversion_string =~ /%/ ? conversion_string % expr : conversion_string
|
76
|
+
else
|
77
|
+
expr
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_type_map(crystalruby_type)
|
83
|
+
{
|
84
|
+
ffi_type: ffi_type(crystalruby_type),
|
85
|
+
ffi_ret_type: ffi_type(crystalruby_type),
|
86
|
+
crystal_type: crystal_type(crystalruby_type),
|
87
|
+
lib_type: lib_type(crystalruby_type),
|
88
|
+
error_value: error_value(crystalruby_type),
|
89
|
+
arg_mapper: if crystalruby_type.is_a?(Types::TypeSerializer)
|
90
|
+
lambda { |arg|
|
91
|
+
crystalruby_type.prepare_argument(arg)
|
92
|
+
}
|
93
|
+
end,
|
94
|
+
retval_mapper: if crystalruby_type.is_a?(Types::TypeSerializer)
|
95
|
+
lambda { |arg|
|
96
|
+
crystalruby_type.prepare_retval(arg)
|
97
|
+
}
|
98
|
+
end,
|
99
|
+
convert_crystal_to_lib_type: ->(expr) { convert_crystal_to_lib_type(expr, crystalruby_type) },
|
100
|
+
convert_lib_to_crystal_type: ->(expr) { convert_lib_to_crystal_type(expr, crystalruby_type) }
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
def ffi_type(type)
|
105
|
+
case type
|
106
|
+
when Symbol then type
|
107
|
+
when Types::TypeSerializer then type.ffi_type
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def lib_type(type)
|
112
|
+
if type.is_a?(Types::TypeSerializer)
|
113
|
+
type.lib_type
|
114
|
+
else
|
115
|
+
C_TYPE_MAP.fetch(type)
|
116
|
+
end
|
117
|
+
rescue StandardError
|
118
|
+
raise "Unsupported type #{type}"
|
119
|
+
end
|
120
|
+
|
121
|
+
def error_value(type)
|
122
|
+
if type.is_a?(Types::TypeSerializer)
|
123
|
+
type.error_value
|
124
|
+
else
|
125
|
+
ERROR_VALUE.fetch(type)
|
126
|
+
end
|
127
|
+
rescue StandardError
|
128
|
+
raise "Unsupported type #{type}"
|
129
|
+
end
|
130
|
+
|
131
|
+
def crystal_type(type)
|
132
|
+
if type.is_a?(Types::TypeSerializer)
|
133
|
+
type.crystal_type
|
134
|
+
else
|
135
|
+
CRYSTAL_TYPE_MAP.fetch(type)
|
136
|
+
end
|
137
|
+
rescue StandardError
|
138
|
+
raise "Unsupported type #{type}"
|
139
|
+
end
|
140
|
+
|
141
|
+
def convert_lib_to_crystal_type(expr, type)
|
142
|
+
if type.is_a?(Types::TypeSerializer)
|
143
|
+
type.lib_to_crystal_type_expr(expr)
|
144
|
+
else
|
145
|
+
C_TYPE_CONVERSIONS.convert(type, :from, expr)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def convert_crystal_to_lib_type(expr, type)
|
150
|
+
if type.is_a?(Types::TypeSerializer)
|
151
|
+
type.crystal_to_lib_type_expr(expr)
|
152
|
+
else
|
153
|
+
C_TYPE_CONVERSIONS.convert(type, :to, expr)
|
154
|
+
end
|
155
|
+
end
|
70
156
|
end
|
71
157
|
end
|
data/lib/crystalruby/version.rb
CHANGED
data/lib/crystalruby.rb
CHANGED
@@ -15,277 +15,48 @@ require_relative "crystalruby/template"
|
|
15
15
|
require_relative "crystalruby/compilation"
|
16
16
|
require_relative "crystalruby/adapter"
|
17
17
|
require_relative "crystalruby/reactor"
|
18
|
+
require_relative "crystalruby/library"
|
19
|
+
require_relative "crystalruby/function"
|
20
|
+
require_relative "module"
|
18
21
|
|
19
22
|
module CrystalRuby
|
20
|
-
CR_SRC_FILES_PATTERN = "./**/*.cr"
|
21
|
-
CR_COMPILE_MUX = Mutex.new
|
22
|
-
|
23
23
|
module_function
|
24
24
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
arg_types = args.transform_values(&method(:build_type_map))
|
29
|
-
return_type = build_type_map(returns)
|
30
|
-
lib_fn_args = arg_types.map { |k, arg_type| "_#{k} : #{arg_type[:lib_type]}" }.join(",")
|
31
|
-
lib_fn_args += ", " unless lib_fn_args.empty?
|
32
|
-
lib_fn_arg_names = arg_types.map { |k, _arg_type| "_#{k}" }.join(",")
|
33
|
-
lib_fn_arg_names += ", " unless lib_fn_args.empty?
|
34
|
-
|
35
|
-
function_body = Template::Function.render(
|
36
|
-
{
|
37
|
-
module_name: owner.name,
|
38
|
-
lib_fn_name: lib_fn_name,
|
39
|
-
fn_name: name,
|
40
|
-
fn_body: body,
|
41
|
-
callback_call: returns == :void ? "callback.call(thread_id)" : "callback.call(thread_id, converted)",
|
42
|
-
callback_type: return_type[:ffi_type] == :void ? "UInt32 -> Void" : " UInt32, #{return_type[:lib_type]} -> Void",
|
43
|
-
fn_args: arg_types.map { |k, arg_type| "#{k} : #{arg_type[:crystal_type]}" }.join(","),
|
44
|
-
fn_ret_type: return_type[:crystal_type],
|
45
|
-
lib_fn_args: lib_fn_args,
|
46
|
-
lib_fn_arg_names: lib_fn_arg_names,
|
47
|
-
lib_fn_ret_type: return_type[:lib_type],
|
48
|
-
convert_lib_args: arg_types.map do |k, arg_type|
|
49
|
-
"#{k} = #{arg_type[:convert_lib_to_crystal_type]["_#{k}"]}"
|
50
|
-
end.join("\n "),
|
51
|
-
arg_names: args.keys.join(","),
|
52
|
-
convert_return_type: return_type[:convert_crystal_to_lib_type]["return_value"],
|
53
|
-
error_value: return_type[:error_value]
|
54
|
-
}
|
55
|
-
)
|
56
|
-
{
|
57
|
-
name: name,
|
58
|
-
body: function_body,
|
59
|
-
retval_map: returns.is_a?(Types::TypeSerializer) ? ->(rv) { returns.prepare_retval(rv) } : nil,
|
60
|
-
ffi_types: arg_types.map { |_k, arg_type| arg_type[:ffi_type] },
|
61
|
-
arg_maps: arg_types.map { |_k, arg_type| arg_type[:mapper] },
|
62
|
-
ffi_ret_type: return_type[:ffi_ret_type]
|
63
|
-
}
|
64
|
-
end
|
65
|
-
|
66
|
-
def build_type_map(crystalruby_type)
|
67
|
-
if crystalruby_type.is_a?(Types::TypeSerializer) && !crystalruby_type.anonymous?
|
68
|
-
CrystalRuby.register_type!(crystalruby_type)
|
69
|
-
end
|
70
|
-
|
71
|
-
{
|
72
|
-
ffi_type: ffi_type(crystalruby_type),
|
73
|
-
ffi_ret_type: ffi_type(crystalruby_type),
|
74
|
-
crystal_type: crystal_type(crystalruby_type),
|
75
|
-
lib_type: lib_type(crystalruby_type),
|
76
|
-
error_value: error_value(crystalruby_type),
|
77
|
-
mapper: crystalruby_type.is_a?(Types::TypeSerializer) ? ->(arg) { crystalruby_type.prepare_argument(arg) } : nil,
|
78
|
-
convert_crystal_to_lib_type: ->(expr) { convert_crystal_to_lib_type(expr, crystalruby_type) },
|
79
|
-
convert_lib_to_crystal_type: ->(expr) { convert_lib_to_crystal_type(expr, crystalruby_type) }
|
80
|
-
}
|
81
|
-
end
|
82
|
-
|
83
|
-
def ffi_type(type)
|
84
|
-
case type
|
85
|
-
when Symbol then type
|
86
|
-
when Types::TypeSerializer then type.ffi_type
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def lib_type(type)
|
91
|
-
if type.is_a?(Types::TypeSerializer)
|
92
|
-
type.lib_type
|
93
|
-
else
|
94
|
-
Typemaps::C_TYPE_MAP.fetch(type)
|
95
|
-
end
|
96
|
-
rescue StandardError => e
|
97
|
-
raise "Unsupported type #{type}"
|
98
|
-
end
|
99
|
-
|
100
|
-
def error_value(type)
|
101
|
-
if type.is_a?(Types::TypeSerializer)
|
102
|
-
type.error_value
|
103
|
-
else
|
104
|
-
Typemaps::ERROR_VALUE.fetch(type)
|
105
|
-
end
|
106
|
-
rescue StandardError => e
|
107
|
-
raise "Unsupported type #{type}"
|
108
|
-
end
|
109
|
-
|
110
|
-
def crystal_type(type)
|
111
|
-
if type.is_a?(Types::TypeSerializer)
|
112
|
-
type.crystal_type
|
113
|
-
else
|
114
|
-
Typemaps::CRYSTAL_TYPE_MAP.fetch(type)
|
115
|
-
end
|
116
|
-
rescue StandardError => e
|
117
|
-
raise "Unsupported type #{type}"
|
118
|
-
end
|
119
|
-
|
120
|
-
def convert_lib_to_crystal_type(expr, type)
|
121
|
-
if type.is_a?(Types::TypeSerializer)
|
122
|
-
type.lib_to_crystal_type_expr(expr)
|
123
|
-
else
|
124
|
-
Typemaps::C_TYPE_CONVERSIONS[type] ? Typemaps::C_TYPE_CONVERSIONS[type][:from] % expr : expr
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
def convert_crystal_to_lib_type(expr, type)
|
129
|
-
if type.is_a?(Types::TypeSerializer)
|
130
|
-
type.crystal_to_lib_type_expr(expr)
|
131
|
-
else
|
132
|
-
Typemaps::C_TYPE_CONVERSIONS[type] ? Typemaps::C_TYPE_CONVERSIONS[type][:to] % expr : expr
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def self.instantiate_crystal_ruby!
|
137
|
-
unless system("which crystal > /dev/null 2>&1")
|
138
|
-
raise "Crystal executable not found. Please ensure Crystal is installed and in your PATH."
|
139
|
-
end
|
140
|
-
|
141
|
-
@instantiated = true
|
142
|
-
%w[crystal_lib_dir crystal_main_file crystal_src_dir crystal_lib_name].each do |config_key|
|
143
|
-
unless config.send(config_key)
|
144
|
-
raise "Missing config option `#{config_key}`. \nProvide this inside crystalruby.yaml (run `bundle exec crystalruby init` to generate this file with detaults)"
|
145
|
-
end
|
146
|
-
end
|
147
|
-
FileUtils.mkdir_p config.crystal_codegen_dir_abs
|
148
|
-
FileUtils.mkdir_p config.crystal_lib_dir_abs
|
149
|
-
FileUtils.mkdir_p config.crystal_src_dir_abs
|
150
|
-
unless File.exist?(config.crystal_src_dir_abs / config.crystal_main_file)
|
151
|
-
IO.write(
|
152
|
-
config.crystal_src_dir_abs / config.crystal_main_file,
|
153
|
-
"require \"./#{config.crystal_codegen_dir}/index\"\n"
|
154
|
-
)
|
155
|
-
end
|
156
|
-
|
157
|
-
return if File.exist?(config.crystal_src_dir / "shard.yml")
|
158
|
-
|
159
|
-
IO.write("#{config.crystal_src_dir}/shard.yml", <<~YAML)
|
160
|
-
name: src
|
161
|
-
version: 0.1.0
|
162
|
-
YAML
|
25
|
+
def initialized?
|
26
|
+
!!@initialized
|
163
27
|
end
|
164
28
|
|
165
|
-
def
|
166
|
-
|
167
|
-
end
|
29
|
+
def initialize_crystal_ruby!
|
30
|
+
return if initialized?
|
168
31
|
|
169
|
-
|
170
|
-
|
171
|
-
@
|
32
|
+
check_crystal_ruby!
|
33
|
+
check_config!
|
34
|
+
@initialized = true
|
172
35
|
end
|
173
36
|
|
174
|
-
def
|
175
|
-
|
176
|
-
end
|
37
|
+
def check_crystal_ruby!
|
38
|
+
return if system("which crystal > /dev/null 2>&1")
|
177
39
|
|
178
|
-
|
179
|
-
|
180
|
-
@types_cache[type.name] = type.type_defn
|
40
|
+
raise "Crystal executable not found. Please ensure Crystal is installed and in your PATH." \
|
41
|
+
"See https://crystal-lang.org/install/"
|
181
42
|
end
|
182
43
|
|
183
|
-
def
|
184
|
-
|
185
|
-
parts = type_name.split("::")
|
186
|
-
typedef = parts[0...-1].each_with_index.reduce("") do |acc, (part, index)|
|
187
|
-
acc + "#{" " * index}module #{part}\n"
|
188
|
-
end
|
189
|
-
typedef += "#{" " * (parts.size - 1)}alias #{parts.last} = #{expr}\n"
|
190
|
-
typedef + parts[0...-1].reverse.each_with_index.reduce("") do |acc, (_part, index)|
|
191
|
-
acc + "#{" " * (parts.size - 2 - index)}end\n"
|
192
|
-
end
|
193
|
-
end.join("\n")
|
194
|
-
end
|
44
|
+
def check_config!
|
45
|
+
return if config.crystal_src_dir
|
195
46
|
|
196
|
-
|
197
|
-
|
198
|
-
function_data = function[:body]
|
199
|
-
file_digest = Digest::MD5.hexdigest function_data
|
200
|
-
fname = function[:name]
|
201
|
-
"require \"./#{function[:owner].name}/#{fname}_#{file_digest}.cr\"\n"
|
202
|
-
end.join("\n")
|
47
|
+
raise "Missing config option `crystal_src_dir`. \nProvide this inside crystalruby.yaml "\
|
48
|
+
"(run `bundle exec crystalruby init` to generate this file with detaults)"
|
203
49
|
end
|
204
50
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
CR_COMPILE_MUX.synchronize do
|
209
|
-
return if @compiled
|
51
|
+
%w[debug info warn error].each do |level|
|
52
|
+
define_method("log_#{level}") do |*msg|
|
53
|
+
prefix = config.colorize_log_output ? "\e[33mcrystalruby\e[0m\e[90m [#{Thread.current.object_id}]\e[0m" : "[crystalruby] #{Thread.current.object_id}"
|
210
54
|
|
211
|
-
|
212
|
-
type_modules: type_modules,
|
213
|
-
requires: requires
|
214
|
-
)
|
215
|
-
if @compiled = CrystalRuby::Compilation.compile!(
|
216
|
-
verbose: config.verbose,
|
217
|
-
debug: config.debug
|
218
|
-
)
|
219
|
-
IO.write(digest_file_name, get_cr_src_files_digest)
|
220
|
-
else
|
221
|
-
File.delete(digest_file_name) if File.exist?(digest_file_name)
|
222
|
-
raise "Error compiling crystal code"
|
223
|
-
end
|
55
|
+
config.logger.send(level, "#{prefix} #{msg.join(", ")}")
|
224
56
|
end
|
225
57
|
end
|
226
58
|
|
227
|
-
def
|
228
|
-
|
229
|
-
@chunk_store.each do |function|
|
230
|
-
function[:compile_callback]&.call
|
231
|
-
end
|
232
|
-
log_debug(".attach_crystal_ruby_lib. Single thread mode: #{config.single_thread_mode}")
|
233
|
-
if config.single_thread_mode
|
234
|
-
Reactor.init_single_thread_mode!
|
235
|
-
else
|
236
|
-
Reactor.start!
|
237
|
-
end
|
238
|
-
@attached = true
|
239
|
-
end
|
240
|
-
|
241
|
-
def self.get_cr_src_files_digest
|
242
|
-
file_digests = Dir.glob(CR_SRC_FILES_PATTERN).sort.map do |file_path|
|
243
|
-
content = File.read(file_path)
|
244
|
-
Digest::MD5.hexdigest(content)
|
245
|
-
end.join
|
246
|
-
Digest::MD5.hexdigest(file_digests)
|
247
|
-
end
|
248
|
-
|
249
|
-
def self.digest_file_name
|
250
|
-
@digest_file_name ||= config.crystal_lib_dir_abs / "#{config.crystal_lib_name}.digest"
|
251
|
-
end
|
252
|
-
|
253
|
-
def self.chunk_store
|
254
|
-
@chunk_store ||= []
|
255
|
-
end
|
256
|
-
|
257
|
-
def self.get_current_crystal_lib_digest
|
258
|
-
File.read(digest_file_name) if File.exist?(digest_file_name)
|
259
|
-
end
|
260
|
-
|
261
|
-
def self.write_chunk(owner, body:, name: Digest::MD5.hexdigest(body), &compile_callback)
|
262
|
-
log_debug(".write_chunk!")
|
263
|
-
chunk_store.delete_if { |chnk| chnk[:owner].name == owner.name && chnk[:name] == name }
|
264
|
-
chunk_store << { owner: owner, name: name, body: body, compile_callback: compile_callback }
|
265
|
-
FileUtils.mkdir_p(config.crystal_codegen_dir_abs)
|
266
|
-
existing = Dir.glob("#{config.crystal_codegen_dir_abs}/**/*.cr")
|
267
|
-
chunk_store.each do |function|
|
268
|
-
log_debug(".processing_chunk", function[0..60])
|
269
|
-
owner_name = function[:owner].name
|
270
|
-
FileUtils.mkdir_p(config.crystal_codegen_dir_abs / owner_name)
|
271
|
-
function_data = function[:body]
|
272
|
-
fname = function[:name]
|
273
|
-
file_digest = Digest::MD5.hexdigest function_data
|
274
|
-
filename = config.crystal_codegen_dir_abs / owner_name / "#{fname}_#{file_digest}.cr"
|
275
|
-
|
276
|
-
unless existing.delete(filename.to_s)
|
277
|
-
log_debug("Chunk invalidated", filename.to_s)
|
278
|
-
@attached = false
|
279
|
-
@compiled = false
|
280
|
-
File.write(filename, function_data)
|
281
|
-
end
|
282
|
-
existing.select do |f|
|
283
|
-
f =~ /#{config.crystal_codegen_dir / owner_name / "#{fname}_[a-f0-9]{32}\.cr"}/
|
284
|
-
end.each do |fl|
|
285
|
-
File.delete(fl) unless fl.eql?(filename.to_s)
|
286
|
-
end
|
287
|
-
end
|
59
|
+
def compile!
|
60
|
+
CrystalRuby::Library.all.each(&:build!)
|
288
61
|
end
|
289
62
|
end
|
290
|
-
|
291
|
-
require_relative "module"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crystalruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-04-
|
11
|
+
date: 2024-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: digest
|
@@ -74,6 +74,7 @@ executables:
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- ".dockerignore"
|
77
78
|
- ".rubocop.yml"
|
78
79
|
- CHANGELOG.md
|
79
80
|
- CODE_OF_CONDUCT.md
|
@@ -88,6 +89,8 @@ files:
|
|
88
89
|
- lib/crystalruby/adapter.rb
|
89
90
|
- lib/crystalruby/compilation.rb
|
90
91
|
- lib/crystalruby/config.rb
|
92
|
+
- lib/crystalruby/function.rb
|
93
|
+
- lib/crystalruby/library.rb
|
91
94
|
- lib/crystalruby/reactor.rb
|
92
95
|
- lib/crystalruby/template.rb
|
93
96
|
- lib/crystalruby/templates/function.cr
|