crystalruby 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8a2ddcb45d988973d1ac42910bd9fa0f9f40dbae52e7dc11f512d3a0bd660790
4
- data.tar.gz: ef9718a77deb3e68bade0ffbee62bd6678414aa1b39dca92c08fccf547637b4a
3
+ metadata.gz: fde36eb6893b41f156d747996c94e803227552f4ef12097ead408bb823619d59
4
+ data.tar.gz: 95e9860ee479571f1f38f8f7507a7f5eb921256116894c01ce358a8c0f22dc70
5
5
  SHA512:
6
- metadata.gz: 4af4c23074658cb78ab68f330a3cd5bd3439c77d6af481ebac0ac9d5b8ba69b6fe3bbd6e6c5ac771a26ab8beb5db8f224fe85e03e0649f90d4f9f62e60a82fa6
7
- data.tar.gz: 7d42ad8ab523ff33612b9e48ef0f222c5f8365811c5de896c1a4b9c29cd4efaeedccd9fff8f2719c8a5ede7e0320a8666ba60154c6860fffad37f318264cfaad
6
+ metadata.gz: 9b6c00ce61edf03f729bcd4f6605aa0cecb6042d9c046700a351d1ccf39b861fd5c259f7955ea02459984ee497533483adf3affbbae697fe5937ddd6ab01c625
7
+ data.tar.gz: 383e9f782c49da8751535ea8ba8ac1fd5638f17fe4d5d6b163c7b9df9ea4171794effa51df0bde51ad892ddbb58c545d542455ea973cf60fe2cb530a898e34c4
data/Dockerfile CHANGED
@@ -1,16 +1,37 @@
1
+ # Stage 1: Use the Crystal official image to build Crystal
2
+ FROM crystallang/crystal:1.14 AS crystal-builder
3
+
4
+ # Stage 2: Use the Ruby image and copy Crystal from the previous stage
1
5
  FROM ruby:3.3
2
6
 
3
7
  MAINTAINER Wouter Coppieters <wc@pico.net.nz>
4
8
 
5
- RUN apt-get update && apt-get install -y curl gnupg2 software-properties-common lsb-release
6
- RUN curl -fsSL https://crystal-lang.org/install.sh | bash
9
+ # Install necessary dependencies for Ruby and Crystal
10
+ RUN apt-get update && apt-get install -y \
11
+ curl \
12
+ gnupg2 \
13
+ software-properties-common \
14
+ build-essential \
15
+ lsb-release
16
+
17
+ # Copy Crystal binaries and libraries from the Crystal image
18
+ COPY --from=crystal-builder /usr/share/crystal /usr/share/crystal
19
+ COPY --from=crystal-builder /usr/lib/crystal /usr/lib/crystal
20
+ COPY --from=crystal-builder /usr/bin/crystal /usr/bin/crystal
21
+ COPY --from=crystal-builder /usr/bin/shards /usr/bin/shards
22
+
23
+ # Set the working directory
7
24
  WORKDIR /usr/src/app
8
25
 
26
+ # Copy the Ruby dependencies
9
27
  COPY Gemfile Gemfile.lock ./
10
28
  COPY crystalruby.gemspec ./
11
29
  COPY lib/crystalruby/version.rb ./lib/crystalruby/version.rb
12
30
 
31
+ # Install Ruby dependencies
13
32
  RUN bundle install
33
+
34
+ # Copy the rest of your application
14
35
  COPY . .
15
36
 
16
37
  # Define the command to run your application
data/README.md CHANGED
@@ -26,7 +26,7 @@ require 'crystalruby'
26
26
  # The below method will be replaced by a compiled Crystal version
27
27
  # linked using FFI.
28
28
 
29
- crystalize
29
+ crystallize
30
30
  def add(a: Int32, b: Int32, returns: Int32)
31
31
  a + b
32
32
  end
@@ -42,7 +42,7 @@ E.g.
42
42
  require 'crystalruby'
43
43
  require 'benchmark'
44
44
 
45
- crystalize :int32
45
+ crystallize :int32
46
46
  def count_primes_upto_cr(n: Int32)
47
47
  (2..n).each.count do |i|
48
48
  is_prime = true
@@ -81,8 +81,8 @@ puts Benchmark.realtime { count_primes_upto_cr(1_000_000) }
81
81
  _Note_: The first, unprimed run of the Crystal code will be slower, as it needs to compile the code first. The subsequent runs will be much faster.
82
82
 
83
83
  You can call embedded crystal code, from within other embedded crystal code.
84
- The below crystalized method `redis_set_and_return` calls the `redis_get` method, which is also crystalized.
85
- Note the use of the shard command to define the Redis shard dependency of the crystalized code.
84
+ The below crystallized method `redis_set_and_return` calls the `redis_get` method, which is also crystallized.
85
+ Note the use of the shard command to define the Redis shard dependency of the crystallized code.
86
86
  E.g.
87
87
 
88
88
  ```ruby
@@ -92,13 +92,13 @@ module Cache
92
92
 
93
93
  shard :redis, github: 'jgaskins/redis'
94
94
 
95
- crystalize :string
95
+ crystallize :string
96
96
  def redis_get(key: String)
97
97
  rds = Redis::Client.new
98
98
  value = rds.get(key).to_s
99
99
  end
100
100
 
101
- crystalize :string
101
+ crystallize :string
102
102
  def redis_set_and_return(key: String, value: String)
103
103
  redis = Redis::Client.new
104
104
  redis.set(key, value)
@@ -115,7 +115,7 @@ $ abc
115
115
 
116
116
  ## Syntax
117
117
 
118
- To define a method that will be compiled as Crystal, you can use the `crystalize` method.
118
+ To define a method that will be compiled as Crystal, you can use the `crystallize` method.
119
119
  You must also provide types, for the parameters and return type.
120
120
 
121
121
  ### Method Signatures
@@ -125,24 +125,25 @@ E.g.
125
125
  def foo(a: Int32, b: Array(Int), c: String)
126
126
  ```
127
127
 
128
- Return types are specified using either a lambda, returning the type, as the first argument to the crystalize method, or the special `returns` kwarg.
128
+ Return types are specified using either a lambda, returning the type, as the first argument to the crystallize method, or the special `returns` kwarg.
129
129
 
130
130
  E.g.
131
131
 
132
132
  ```ruby
133
133
  # Returns an Int32
134
- crystalize ->{ Int32 }
134
+ crystallize ->{ Int32 }
135
135
  def returns_int32
136
136
  3
137
137
  end
138
138
 
139
139
  # You can use the symbol shortcode for primitive types
140
- crystalize :int32
140
+ crystallize :int32
141
141
  def returns_int32
142
142
  3
143
+ end
143
144
 
144
145
  # Define the return type directly using the `returns` kwarg
145
- crystalize
146
+ crystallize
146
147
  def returns_int32(returns: Int32)
147
148
  3
148
149
  end
@@ -155,7 +156,7 @@ It'll be compiled as Crystal automatically.
155
156
  E.g.
156
157
 
157
158
  ```ruby
158
- crystalize :int
159
+ crystallize :int
159
160
  def add(a: :int, b: :int)
160
161
  puts "Adding #{a} and #{b}"
161
162
  a + b
@@ -167,7 +168,7 @@ Some Crystal syntax is not valid Ruby, for methods of this form, we need to
167
168
  define our functions using the `raw: true` option
168
169
 
169
170
  ```ruby
170
- crystalize raw: true
171
+ crystallize raw: true
171
172
  def add(a: :int, b: :int)
172
173
  <<~CRYSTAL
173
174
  c = 0_u64
@@ -179,24 +180,24 @@ end
179
180
  ### Upgrading from version 0.2.x
180
181
 
181
182
  #### Change in type signatures
182
- In version 0.2.x, argument and return types were passed to the `crystalize` method using a different syntax:
183
+ In version 0.2.x, argument and return types were passed to the `crystallize` method using a different syntax:
183
184
 
184
185
  ```ruby
185
186
  # V <= 0.2.x
186
- crystalize [arg1: :arg1_type , arg2: :arg2_type] => :return_type
187
+ crystallize [arg1: :arg1_type , arg2: :arg2_type] => :return_type
187
188
  def foo(arg1, arg2)
188
189
  ```
189
190
 
190
191
  In crystalruby > 0.3.x, argument types are now passed as keyword arguments, and the return type is passed either as a keyword argument
191
- or as the first argument to crystalize (either using symbol shorthand, or a Lambda returning a Crystal type).
192
+ or as the first argument to crystallize (either using symbol shorthand, or a Lambda returning a Crystal type).
192
193
 
193
194
  ```ruby
194
195
  # V >= 0.3.x
195
- crystalize :return_type
196
+ crystallize :return_type
196
197
  def foo(arg1: :arg1_type, arg2: :arg2_type)
197
198
 
198
199
  # OR use the `returns` kwarg
199
- crystalize
200
+ crystallize
200
201
  def foo(arg1: :arg1_type, arg2: :arg2_type, returns: :return_type)
201
202
  ```
202
203
 
@@ -216,7 +217,7 @@ end
216
217
 
217
218
  require 'crystalruby'
218
219
 
219
- crystalize :int
220
+ crystallize :int
220
221
  def add(a: :int, b: :int)
221
222
  a + b
222
223
  end
@@ -249,13 +250,13 @@ E.g.
249
250
  ```ruby
250
251
  require 'crystalruby'
251
252
 
252
- crystalize
253
+ crystallize
253
254
  def complex_argument_types(a: Int64 | Float64 | Nil, b: String | Array(Bool))
254
255
  puts "Got #{a} and #{b}"
255
256
  end
256
257
 
257
258
 
258
- crystalize
259
+ crystallize
259
260
  def complex_return_type(returns: Int32 | String | Hash(String, Array(NamedTuple(hello: Int32)) | Time))
260
261
  return {
261
262
  "hello" => [
@@ -303,7 +304,7 @@ E.g.
303
304
 
304
305
  IntArrOrBoolArr = CRType{ Array(Bool) | Array(Int32) }
305
306
 
306
- crystalize
307
+ crystallize
307
308
  def method_with_named_types(a: IntArrOrBoolArr, returns: IntArrOrBoolArr)
308
309
  return a
309
310
  end
@@ -324,7 +325,7 @@ require 'crystalruby'
324
325
 
325
326
  IntArray = CRType{ Array(Int32) }
326
327
 
327
- crystalize
328
+ crystallize
328
329
  def array_doubler(a: IntArray)
329
330
  a.map! { |x| x * 2 }
330
331
  end
@@ -354,7 +355,7 @@ class Person < CRType{ NamedTuple(name: String, age: Int32) }
354
355
  "Hello from Ruby. My name is #{self.name.value}"
355
356
  end
356
357
 
357
- crystalize :string
358
+ crystallize :string
358
359
  def greet_cr
359
360
  "Hello from Crystal, My name is #{self.name.value}"
360
361
  end
@@ -379,7 +380,7 @@ module Adder
379
380
  a + b
380
381
  end
381
382
 
382
- crystalize :int32
383
+ crystallize :int32
383
384
  def add_crystal(a: Int32, b: Int32)
384
385
  return add_rb(a, b)
385
386
  end
@@ -398,7 +399,7 @@ require 'crystalruby'
398
399
 
399
400
  shard :kemal, github: 'kemalcr/kemal'
400
401
 
401
- crystalize async: true
402
+ crystallize async: true
402
403
  def start_server
403
404
  Kemal.run(3000, [""])
404
405
  end
@@ -459,7 +460,7 @@ See notes on how to define a Proc type in Crystal [here](https://crystal-lang.or
459
460
  ```ruby
460
461
  require 'crystalruby'
461
462
 
462
- crystalize
463
+ crystallize
463
464
  def yielder_cr(a: Int32, b: Int32, yield: Proc(Int32, Nil))
464
465
  yield a + b
465
466
  end
@@ -469,7 +470,7 @@ def yielder_rb(a: Int32, b: Int32, yield: Proc(Int32, Nil))
469
470
  yield a + b
470
471
  end
471
472
 
472
- crystalize
473
+ crystallize
473
474
  def invoke_yielder_rb(a: Int32, b: Int32)
474
475
  yielder_rb(a, b) do |sum|
475
476
  puts sum
@@ -504,10 +505,10 @@ If your shard file gets out of sync with your Ruby file, you can run `crystalrub
504
505
  ## Wrapping Crystal code in Ruby
505
506
 
506
507
  Sometimes you may want to wrap a Crystal method in Ruby, so that you can use Ruby before the Crystal code to prepare arguments, or after the Crystal code, to apply transformations to the result. A real-life example of this might be an ActionController method, where you might want to use Ruby to parse the request, perform auth etc., and then use Crystal to perform some heavy computation, before returning the result from Ruby.
507
- To do this, you simply pass a block to the `crystalize` method, which will serve as the Ruby entry point to the function. From within this block, you can invoke `super` to call the Crystal method, and then apply any Ruby transformations to the result.
508
+ To do this, you simply pass a block to the `crystallize` method, which will serve as the Ruby entry point to the function. From within this block, you can invoke `super` to call the Crystal method, and then apply any Ruby transformations to the result.
508
509
 
509
510
  ```ruby
510
- crystalize :int32 do |a, b|
511
+ crystallize :int32 do |a, b|
511
512
  # In this example, we perform automated conversion to integers inside Ruby.
512
513
  # Then add 1 to the result of the Crystal method.
513
514
  result = super(a.to_i, b.to_i)
@@ -524,7 +525,7 @@ puts convert_to_i_and_add_and_succ("1", "2")
524
525
 
525
526
  `crystalruby` also allows you to write top-level Crystal code outside of method definitions. This can be useful for e.g. performing setup operations or initializations.
526
527
 
527
- Follow these steps for a toy example of how we can use crystalized ruby and inline chunks to expose the [crystal-redis](https://github.com/stefanwille/crystal-redis) library to Ruby.
528
+ Follow these steps for a toy example of how we can use crystallized ruby and inline chunks to expose the [crystal-redis](https://github.com/stefanwille/crystal-redis) library to Ruby.
528
529
 
529
530
  1. Start our toy project
530
531
 
@@ -565,12 +566,12 @@ module CrystalRedis
565
566
  end
566
567
  end
567
568
 
568
- crystalize
569
+ crystallize
569
570
  def set(key: String, value: String)
570
571
  client.set(key, value)
571
572
  end
572
573
 
573
- crystalize :string
574
+ crystallize :string
574
575
  def get(key: String)
575
576
  client.get(key).to_s
576
577
  end
@@ -604,12 +605,12 @@ module CrystalRedis
604
605
  end
605
606
  end
606
607
 
607
- crystalize
608
+ crystallize
608
609
  def set(key: String, value: String)
609
610
  client.set(key, value)
610
611
  end
611
612
 
612
- crystalize :string
613
+ crystallize :string
613
614
  def get(key: String)
614
615
  client.get(key).to_s
615
616
  end
@@ -678,18 +679,18 @@ To safely utilise `crystalruby` in a multithreaded environment, `crystalruby` im
678
679
 
679
680
  By default `crystalruby` methods are blocking/synchronous, this means that for blocking operations, a single crystalruby call can block the entire reactor across _all_ threads.
680
681
 
681
- To allow you to benefit from Crystal's fiber based concurrency, you can use the `async: true` option on crystalized ruby methods. This allows several Ruby threads to invoke Crystal code simultaneously.
682
+ To allow you to benefit from Crystal's fiber based concurrency, you can use the `async: true` option on crystallized ruby methods. This allows several Ruby threads to invoke Crystal code simultaneously.
682
683
 
683
684
  E.g.
684
685
 
685
686
  ```ruby
686
687
  module Sleeper
687
- crystalize
688
+ crystallize
688
689
  def sleep_sync
689
690
  sleep 2.seconds
690
691
  end
691
692
 
692
- crystalize async: true
693
+ crystallize async: true
693
694
  def sleep_async
694
695
  sleep 2.seconds
695
696
  end
@@ -723,12 +724,12 @@ recompile Crystal code only when it detects changes to the embedded function or
723
724
  ## Multi-library support
724
725
 
725
726
  Large Crystal projects are known to have long compile times. To mitigate this, `crystalruby` supports splitting your Crystal code into multiple libraries. This allows you to only recompile any libraries that have changed, rather than all crystal code within the project.
726
- To indicate which library a piece of embedded Crystal code belongs to, you can use the `lib` option in the `crystalize` and `crystal` methods.
727
+ To indicate which library a piece of embedded Crystal code belongs to, you can use the `lib` option in the `crystallize` and `crystal` methods.
727
728
  If the `lib` option is not provided, the code will be compiled into the default library (simply named `crystalruby`).
728
729
 
729
730
  ```ruby
730
731
  module Foo
731
- crystalize lib: "foo"
732
+ crystallize lib: "foo"
732
733
  def bar
733
734
  puts "Hello from Foo"
734
735
  end
data/crystalruby.gemspec CHANGED
@@ -35,7 +35,7 @@ Gem::Specification.new do |spec|
35
35
  spec.add_dependency "digest"
36
36
  spec.add_dependency "ffi"
37
37
  spec.add_dependency "fileutils"
38
- spec.add_dependency "syntax_tree"
38
+ spec.add_dependency "prism"
39
39
  # For more information and examples about making a new gem, check out our
40
40
  # guide at: https://bundler.io/guides/creating_gem.html
41
41
  end
@@ -1,7 +1,7 @@
1
1
  require "crystalruby"
2
2
 
3
3
  module Adder
4
- crystalize [a: :int, b: :int] => :int
4
+ crystallize [a: :int, b: :int] => :int
5
5
  def add(a, b)
6
6
  a + b
7
7
  end
@@ -1,14 +1,14 @@
1
1
  module CrystalRuby
2
2
  module Adapter
3
- # Use this method to annotate a Ruby method that should be crystalized.
3
+ # Use this method to annotate a Ruby method that should be crystallized.
4
4
  # Compilation and attachment of the method is done lazily.
5
5
  # You can force compilation by calling `CrystalRuby.compile!`
6
- # It's important that all code using crystalized methods is
6
+ # It's important that all code using crystallized methods is
7
7
  # loaded before any manual calls to compile.
8
8
  #
9
9
  # E.g.
10
10
  #
11
- # crystalize :int32
11
+ # crystallize :int32
12
12
  # def add(a: :int32, b: :int32)
13
13
  # a + b
14
14
  # end
@@ -16,7 +16,7 @@ module CrystalRuby
16
16
  # Pass `raw: true` to pass Raw crystal code to the compiler as a string instead.
17
17
  # (Useful for cases where the Crystal method body is not valid Ruby)
18
18
  # E.g.
19
- # crystalize :int32, raw: true
19
+ # crystallize :int32, raw: true
20
20
  # def add(a: :int32, b: :int32)
21
21
  # <<~CRYSTAL
22
22
  # a + b
@@ -36,9 +36,9 @@ module CrystalRuby
36
36
  # @option options [Boolean] :async (false) Mark the method as async (allows multiplexing).
37
37
  # @option options [String] :lib ("crystalruby") The name of the library to compile the Crystal code into.
38
38
  # @option options [Proc] :block An optional wrapper Ruby block that wraps around any invocations of the crystal code
39
- def crystalize( returns=:void, raw: false, async: false, lib: "crystalruby", &block)
39
+ def crystallize( returns=:void, raw: false, async: false, lib: "crystalruby", &block)
40
40
  (self == TOPLEVEL_BINDING.receiver ? Object : self).instance_eval do
41
- @crystalize_next = {
41
+ @crystallize_next = {
42
42
  raw: raw,
43
43
  async: async,
44
44
  returns: returns,
@@ -48,8 +48,11 @@ module CrystalRuby
48
48
  end
49
49
  end
50
50
 
51
+ # Alias for `crystallize`
52
+ alias :crystalize :crystallize
53
+
51
54
  # Exposes a Ruby method to one or more Crystal libraries.
52
- # Type annotations follow the same rules as the `crystalize` method, but are
55
+ # Type annotations follow the same rules as the `crystallize` method, but are
53
56
  # applied in reverse.
54
57
  # @param returns The return type of the method. Optional (defaults to :void).
55
58
  # @param [Hash] options The options hash.
@@ -73,7 +76,7 @@ module CrystalRuby
73
76
 
74
77
  # Use this method to define inline Crystal code that does not need to be bound to a Ruby method.
75
78
  # This is useful for defining classes, modules, performing set-up tasks etc.
76
- # See: docs for .crystalize to understand the `raw` and `lib` parameters.
79
+ # See: docs for .crystallize to understand the `raw` and `lib` parameters.
77
80
  def crystal(raw: false, lib: "crystalruby", &block)
78
81
  inline_crystal_body = respond_to?(:name) ? Template::InlineChunk.render(
79
82
  {
@@ -84,7 +87,7 @@ module CrystalRuby
84
87
  }) :
85
88
  SourceReader.extract_source_from_proc(block, raw: raw)
86
89
 
87
- CrystalRuby::Library[lib].crystalize_chunk(
90
+ CrystalRuby::Library[lib].crystallize_chunk(
88
91
  self,
89
92
  Digest::MD5.hexdigest(inline_crystal_body),
90
93
  inline_crystal_body
@@ -101,29 +104,29 @@ module CrystalRuby
101
104
 
102
105
  private
103
106
 
104
- # We trigger attaching of crystalized instance methods here.
105
- # If a method is added after a crystalize annotation we assume it's the target of the crystalize annotation.
107
+ # We trigger attaching of crystallized instance methods here.
108
+ # If a method is added after a crystallize annotation we assume it's the target of the crystallize annotation.
106
109
  # @param [Symbol] method_name The name of the method being added.
107
110
  def method_added(method_name)
108
- define_crystalized_method(instance_method(method_name)) if should_crystalize_next?
111
+ define_crystallized_method(instance_method(method_name)) if should_crystallize_next?
109
112
  expose_ruby_method_to_crystal(instance_method(method_name)) if should_expose_next?
110
113
  super
111
114
  end
112
115
 
113
- # We trigger attaching of crystalized class methods here.
114
- # If a method is added after a crystalize annotation we assume it's the target of the crystalize annotation.
116
+ # We trigger attaching of crystallized class methods here.
117
+ # If a method is added after a crystallize annotation we assume it's the target of the crystallize annotation.
115
118
  # @note This method is called when a method is added to the singleton class of the object.
116
119
  # @param [Symbol] method_name The name of the method being added.
117
120
  def singleton_method_added(method_name)
118
- define_crystalized_method(singleton_method(method_name)) if should_crystalize_next?
121
+ define_crystallized_method(singleton_method(method_name)) if should_crystallize_next?
119
122
  expose_ruby_method_to_crystal(singleton_method(method_name)) if should_expose_next?
120
123
  super
121
124
  end
122
125
 
123
- # Helper method to determine if the next method added should be crystalized.
124
- # @return [Boolean] True if the next method added should be crystalized.
125
- def should_crystalize_next?
126
- defined?(@crystalize_next) && @crystalize_next
126
+ # Helper method to determine if the next method added should be crystallized.
127
+ # @return [Boolean] True if the next method added should be crystallized.
128
+ def should_crystallize_next?
129
+ defined?(@crystallize_next) && @crystallize_next
127
130
  end
128
131
 
129
132
  # Helper method to determine if the next method added should be exposed to Crystal libraries.
@@ -161,7 +164,7 @@ module CrystalRuby
161
164
  end
162
165
  end
163
166
 
164
- # We attach crystalized class methods here.
167
+ # We attach crystallized class methods here.
165
168
  # This function is responsible for
166
169
  # - Generating the Crystal source code
167
170
  # - Overwriting the method and class methods by the same name in the caller.
@@ -169,20 +172,21 @@ module CrystalRuby
169
172
  # - We also optionally prepend a block (if given) to the owner, to allow Ruby code to wrap around Crystal code.
170
173
  # @param [Symbol] method_name The name of the method being added.
171
174
  # @param [UnboundMethod] method The method being added.
172
- def define_crystalized_method(method)
173
- CrystalRuby.log_debug("Defining crystalized method #{name}.#{method.name}")
175
+ def define_crystallized_method(method)
176
+ CrystalRuby.log_debug("Defining crystallized method #{name}.#{method.name}")
174
177
 
175
- returns, block, async, lib, raw = @crystalize_next.values_at(:returns, :block, :async, :lib, :raw)
176
- @crystalize_next = nil
178
+ returns, block, async, lib, raw = @crystallize_next.values_at(:returns, :block, :async, :lib, :raw)
179
+ @crystallize_next = nil
177
180
 
178
181
  args, source = SourceReader.extract_args_and_source_from_method(method, raw: raw)
179
182
 
180
183
  # We can safely claim the `yield` argument name for typing the yielded block
181
184
  # because this is an illegal identifier in Crystal anyway.
182
185
  args[:__yield_to] = args.delete(:yield) if args[:yield]
186
+
183
187
  returns = args.delete(:returns) if args[:returns] && returns == :void
184
188
 
185
- CrystalRuby::Library[lib].crystalize_method(
189
+ CrystalRuby::Library[lib].crystallize_method(
186
190
  method,
187
191
  args,
188
192
  returns,
@@ -37,12 +37,12 @@ module CrystalRuby
37
37
  end
38
38
 
39
39
  # This is where we write/overwrite the class and instance methods
40
- # with their crystalized equivalents.
40
+ # with their crystallized equivalents.
41
41
  # We also perform JIT compilation and JIT attachment of the FFI functions.
42
42
  # Crystalized methods can be redefined without restarting, if running in a live-reloading environment.
43
43
  # If they are redefined with a different function body, the new function body
44
44
  # will result in a new digest and the FFI function will be recompiled and reattached.
45
- def define_crystalized_methods!(lib)
45
+ def define_crystallized_methods!(lib)
46
46
  func = self
47
47
  receivers = instance_method ? [owner] : [owner, owner.singleton_class]
48
48
  receivers.each do |receiver|
@@ -109,9 +109,9 @@ module CrystalRuby
109
109
  Reactor.schedule_work!(lib, :"register_#{name.to_s.gsub("?", "q").gsub("=", "eq").gsub("!", "bang")}_callback", @callback_func, :void, blocking: true, async: false)
110
110
  end
111
111
 
112
- # Attaches the crystalized FFI functions to their related Ruby modules and classes.
113
- # If a wrapper block has been passed to the crystalize function,
114
- # then the we also wrap the crystalized function using a prepended Module.
112
+ # Attaches the crystallized FFI functions to their related Ruby modules and classes.
113
+ # If a wrapper block has been passed to the crystallize function,
114
+ # then the we also wrap the crystallized function using a prepended Module.
115
115
  def attach_ffi_func!
116
116
  argtypes = ffi_types
117
117
  rettype = ffi_ret_type
@@ -61,7 +61,7 @@ module CrystalRuby
61
61
 
62
62
  # Generates and stores a reference to a new CrystalRuby::Function
63
63
  # and triggers the generation of the crystal code. (See write_chunk)
64
- def crystalize_method(method, args, returns, function_body, async, &block)
64
+ def crystallize_method(method, args, returns, function_body, async, &block)
65
65
  CR_ATTACH_MUX.synchronize do
66
66
  methods.each_value(&:unattach!)
67
67
  method_key = "#{method.owner.name}/#{method.name}"
@@ -74,7 +74,7 @@ module CrystalRuby
74
74
  lib: self,
75
75
  &block
76
76
  ).tap do |func|
77
- func.define_crystalized_methods!(self)
77
+ func.define_crystallized_methods!(self)
78
78
  func.register_custom_types!(self)
79
79
  write_chunk(func.owner_name, method.name, func.chunk)
80
80
  end
@@ -110,7 +110,7 @@ module CrystalRuby
110
110
  src_dir / "shard.yml"
111
111
  end
112
112
 
113
- def crystalize_chunk(mod, chunk_name, body)
113
+ def crystallize_chunk(mod, chunk_name, body)
114
114
  write_chunk(mod.respond_to?(:name) ? name : "main", chunk_name, body)
115
115
  end
116
116
 
@@ -271,6 +271,7 @@ module CrystalRuby
271
271
  end
272
272
 
273
273
  def write_chunk(module_name, chunk_name, body)
274
+ module_name = module_name.gsub("::", "__MOD__")
274
275
  chunks.delete_if { |chnk| chnk[:module_name] == module_name && chnk[:chunk_name] == chunk_name }
275
276
  chunk = { module_name: module_name, chunk_name: chunk_name, body: body }
276
277
  chunks << chunk
@@ -61,15 +61,16 @@ module CrystalRuby
61
61
  end
62
62
 
63
63
  def await_result!
64
- mux, cond = thread_conditions.values_at(:mux, :cond)
65
- cond.wait(mux)
66
- if error = thread_conditions[:error]
67
- combined_backtrace = error.backtrace[0..(error.backtrace.index{|m| m.include?('call_blocking_function')} || 2) - 3] + caller[5..-1]
68
- error.set_backtrace(combined_backtrace)
69
- raise error
64
+ mux, cond, result, err = thread_conditions.values_at(:mux, :cond, :result, :error)
65
+ cond.wait(mux) unless (result || err)
66
+ result, err, thread_conditions[:result], thread_conditions[:error] = thread_conditions.values_at(:result, :error)
67
+ if err
68
+ combined_backtrace = err.backtrace[0..(err.backtrace.index{|m| m.include?('call_blocking_function')} || 2) - 3] + caller[5..-1]
69
+ err.set_backtrace(combined_backtrace)
70
+ raise err
70
71
  end
71
72
 
72
- thread_conditions[:result]
73
+ result
73
74
  end
74
75
 
75
76
  def halt_loop!
@@ -90,7 +91,10 @@ module CrystalRuby
90
91
  @main_thread_id = Thread.current.object_id
91
92
  CrystalRuby.log_debug("Starting reactor")
92
93
  CrystalRuby.log_debug("CrystalRuby initialized")
93
- REACTOR_QUEUE.pop[] while true
94
+ while true
95
+ handler, *args = REACTOR_QUEUE.pop
96
+ send(handler, *args)
97
+ end
94
98
  rescue StopReactor => e
95
99
  rescue StandardError => e
96
100
  CrystalRuby.log_error "Error: #{e}"
@@ -107,6 +111,29 @@ module CrystalRuby
107
111
  nil
108
112
  end
109
113
 
114
+ def invoke_async!(receiver, op_name, *args, thread_id, callback, lib)
115
+ receiver.send(op_name, *args, thread_id, callback)
116
+ yield!(lib: lib, time: 0)
117
+ end
118
+
119
+ def invoke_blocking!(receiver, op_name, *args, tvars)
120
+ tvars[:error] = nil
121
+ begin
122
+ tvars[:result] = receiver.send(op_name, *args)
123
+ rescue StopReactor => e
124
+ tvars[:cond].signal
125
+ raise
126
+ rescue StandardError => e
127
+ tvars[:error] = e
128
+ end
129
+ tvars[:cond].signal
130
+ end
131
+
132
+ def invoke_await!(receiver, op_name, *args, lib)
133
+ outstanding_jobs = receiver.send(op_name, *args)
134
+ yield!(lib: lib, time: 0) unless outstanding_jobs == 0
135
+ end
136
+
110
137
  def schedule_work!(receiver, op_name, *args, return_type, blocking: true, async: true, lib: nil)
111
138
  if @single_thread_mode || (Thread.current.object_id == @main_thread_id && op_name != :yield)
112
139
  unless Thread.current.object_id == @main_thread_id
@@ -121,34 +148,9 @@ module CrystalRuby
121
148
  tvars[:mux].synchronize do
122
149
  REACTOR_QUEUE.push(
123
150
  case true
124
- when async
125
- lambda {
126
- receiver.send(
127
- op_name, *args, tvars[:thread_id],
128
- CALLBACKS_MAP[return_type]
129
- )
130
- yield!(lib: lib, time: 0)
131
- }
132
- when blocking
133
- lambda {
134
- tvars[:error] = nil
135
- should_halt = false
136
- begin
137
- result = receiver.send(op_name, *args)
138
- rescue StopReactor => e
139
- should_halt = true
140
- rescue StandardError => e
141
- tvars[:error] = e
142
- end
143
- tvars[:result] = result unless tvars[:error]
144
- tvars[:cond].signal
145
- raise StopReactor if should_halt
146
- }
147
- else
148
- lambda {
149
- outstanding_jobs = receiver.send(op_name, *args)
150
- yield!(lib: lib, time: 0) unless outstanding_jobs == 0
151
- }
151
+ when async then [:invoke_async!, receiver, op_name, *args, tvars[:thread_id], CALLBACKS_MAP[return_type], lib]
152
+ when blocking then [:invoke_blocking!, receiver, op_name, *args, tvars]
153
+ else [:invoke_await!, receiver, op_name, *args, lib]
152
154
  end
153
155
  )
154
156
  return await_result! if blocking
@@ -6,46 +6,47 @@ module CrystalRuby
6
6
  def extract_expr_from_source_location(source_location)
7
7
  lines = source_location.then{|f,l| IO.readlines(f)[l-1..]}
8
8
  lines[0] = lines[0][/CRType.*/] if lines[0] =~ /<\s+CRType/ || lines[0] =~ /= CRType/
9
- lines.each.with_object("") do |line, expr_source|
10
- break expr_source if (SyntaxTree.parse(expr_source << line) rescue nil)
9
+ lines.each.with_object([]) do |line, expr_source|
10
+ break expr_source.join("") if (Prism.parse((expr_source << line).join("")).success?)
11
11
  end
12
12
  rescue
13
13
  raise "Failed to extract expression from source location: #{source_location}. Ensure the file exists and the line number is correct. Extraction from a REPL is not supported"
14
14
  end
15
15
 
16
+ def search_node(result, node_type)
17
+ result.breadth_first_search do |node|
18
+ node_type === node
19
+ end
20
+ end
21
+
16
22
  # Given a proc, extracts the source code of the block passed to it
17
23
  # If raw is true, the source is expected to be Raw Crystal code captured
18
24
  # in a string or Heredoc literal. Otherwise the Ruby code (assumed to be valid Crystal)
19
25
  # is extracted.
20
26
  def extract_source_from_proc(block, raw: false)
21
27
  block_source = extract_expr_from_source_location(block.source_location)
22
- body_node = SyntaxTree.search(block_source, :Statements).to_a[1]
28
+ parsed_source = Prism.parse(block_source).value
29
+
30
+ node = parsed_source.statements.body[0].arguments&.arguments&.find{|x| search_node(x, Prism::StatementsNode) }
31
+ node ||= parsed_source.statements.body[0]
32
+ body_node = search_node(node, Prism::StatementsNode)
33
+
23
34
  return raw ?
24
- SyntaxTree.search(node_to_s(body_node), :TStringContent).map(&method(:node_to_s)).join :
35
+ extract_raw_string_node(body_node) :
25
36
  node_to_s(body_node)
26
37
  end
27
38
 
28
- @visitor = SyntaxTree::MutationVisitor.new
29
-
30
- # Specify that it should mutate If nodes with assignments in their predicates
31
- @visitor.mutate("ReturnNode") do |node|
32
- node
39
+ def extract_raw_string_node(node)
40
+ search_node(node, Prism::InterpolatedStringNode)&.parts&.map(&:unescaped)&.join("") ||
41
+ search_node(node, Prism::StringNode).unescaped
33
42
  end
34
43
 
44
+
35
45
  # Simple helper function to turn a SyntaxTree node back into a Ruby string
36
46
  # The default formatter will turn a break/return of [1,2,3] into a brackless 1,2,3
37
47
  # Can't have that in Crystal as it turns it into a Tuple
38
48
  def node_to_s(node)
39
- @_syxt_transform ||= SyntaxTree::FlowControlFormatter.prepend(Module.new do
40
- def format(quer)
41
- first_arg = self.node.arguments.child_nodes
42
- if first_arg[0].kind_of?(SyntaxTree::ArrayLiteral)
43
- return format_arguments(quer, " [", "]")
44
- end
45
- super(quer)
46
- end
47
- end)
48
- SyntaxTree::Formatter.format("", node.accept(@visitor))
49
+ node&.slice || ''
49
50
  end
50
51
 
51
52
  # Given a method, extracts the source code of the block passed to it
@@ -67,11 +68,16 @@ module CrystalRuby
67
68
  # is extracted.
68
69
  def extract_args_and_source_from_method(method, raw: false)
69
70
  method_source = extract_expr_from_source_location(method.source_location)
70
- params = SyntaxTree.search(method_source, :Params).first
71
- args = params ? params.keywords.map{|k,v| [k.value[0...-1].to_sym, node_to_s(v)] }.to_h : {}
72
- body_node = SyntaxTree.search(method_source, :BodyStmt).first || SyntaxTree.search(method_source, :Statements).to_a[1]
73
- body = node_to_s(body_node)
74
- body = SyntaxTree.search(body, :TStringContent).map(&method(:node_to_s)).join if raw
71
+ parsed_source = Prism.parse(method_source).value
72
+ params = search_node(parsed_source, Prism::ParametersNode)
73
+ args = params ? params.keywords.map{|kw| [kw.name, node_to_s(kw.value)] }.to_h : {}
74
+ body_node = parsed_source.statements.body[0].body
75
+ if body_node.respond_to?(:rescue_clause) && body_node.rescue_clause
76
+ wrapped = %{begin\n#{body_node.statements.slice}\n#{body_node.rescue_clause.slice}\nend}
77
+ body_node = Prism.parse(wrapped).value
78
+ end
79
+ body = raw ? extract_raw_string_node(body_node) : node_to_s(body_node)
80
+
75
81
  args.transform_values! do |type_exp|
76
82
  if CrystalRuby::Typemaps::CRYSTAL_TYPE_MAP.key?(type_exp[1..-1].to_sym)
77
83
  type_exp[1..-1].to_sym
@@ -93,6 +93,10 @@ module CrystalRuby::Types
93
93
  union_types.map(&:native_type_expr).join(" | ")
94
94
  end
95
95
 
96
+ define_singleton_method(:named_type_expr) do
97
+ union_types.map(&:named_type_expr).join(" | ")
98
+ end
99
+
96
100
  define_singleton_method(:type_expr) do
97
101
  anonymous? ? native_type_expr : name
98
102
  end
@@ -80,6 +80,16 @@ module CrystalRuby
80
80
  end
81
81
  end
82
82
 
83
+ def self.named_type_expr
84
+ if !inner_types
85
+ "::#{name || typename}"
86
+ elsif !inner_keys
87
+ "::#{name || typename}(#{inner_types.map(&:named_type_expr).join(", ")})"
88
+ else
89
+ "::#{name || typename}(#{inner_keys.zip(inner_types).map { |k, v| "#{k}: #{v.named_type_expr}" }.join(", ")})"
90
+ end
91
+ end
92
+
83
93
  def self.valid_cast?(raw)
84
94
  raw.is_a?(self) || convert_if.any? { |type| raw.is_a?(type) }
85
95
  end
@@ -94,12 +104,12 @@ module CrystalRuby
94
104
  end
95
105
 
96
106
  def self.crystal_class_name
97
- name || native_type_expr.split(",").join("_and_")
98
- .split("|").join("_or_")
99
- .split("(").join("_of_")
100
- .gsub(/[^a-zA-Z0-9_]/, "")
101
- .split("_")
102
- .map(&:capitalize).join << "_#{type_digest[0..6]}"
107
+ name || named_type_expr.split(",").join("_and_")
108
+ .split("|").join("_or_")
109
+ .split("(").join("_of_")
110
+ .gsub(/[^a-zA-Z0-9_]/, "")
111
+ .split("_")
112
+ .map(&:capitalize).join << "_#{type_digest[0..6]}"
103
113
  end
104
114
 
105
115
  def self.base_crystal_class_name
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CrystalRuby
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
data/lib/crystalruby.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require "ffi"
4
4
  require "digest"
5
5
  require "fileutils"
6
- require "syntax_tree"
6
+ require "prism"
7
7
  require "pathname"
8
8
 
9
9
  require_relative "crystalruby/config"
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.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
+ original_platform: ''
6
7
  authors:
7
8
  - Wouter Coppieters
8
- autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-11-06 00:00:00.000000000 Z
11
+ date: 2024-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: digest
@@ -53,7 +53,7 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: syntax_tree
56
+ name: prism
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
@@ -147,7 +147,6 @@ metadata:
147
147
  homepage_uri: https://github.com/wouterken/crystalruby
148
148
  source_code_uri: https://github.com/wouterken/crystalruby
149
149
  changelog_uri: https://github.com/wouterken/crystalruby/CHANGELOG.md
150
- post_install_message:
151
150
  rdoc_options: []
152
151
  require_paths:
153
152
  - lib
@@ -162,8 +161,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
161
  - !ruby/object:Gem::Version
163
162
  version: '0'
164
163
  requirements: []
165
- rubygems_version: 3.5.3
166
- signing_key:
164
+ rubygems_version: 3.6.0.dev
167
165
  specification_version: 4
168
166
  summary: Embed Crystal code directly in Ruby.
169
167
  test_files: []