code-ruby 0.11.0 → 0.12.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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +2 -2
  3. data/bin/code +6 -0
  4. data/lib/code/node/base_10.rb +2 -2
  5. data/lib/code/node/base_16.rb +1 -5
  6. data/lib/code/node/base_2.rb +1 -5
  7. data/lib/code/node/base_8.rb +1 -5
  8. data/lib/code/node/call.rb +4 -4
  9. data/lib/code/node/code.rb +2 -1
  10. data/lib/code/node/decimal.rb +1 -4
  11. data/lib/code/node/dictionary.rb +6 -2
  12. data/lib/code/node/function.rb +3 -3
  13. data/lib/code/node/function_parameter.rb +5 -1
  14. data/lib/code/node/if.rb +2 -1
  15. data/lib/code/node/list.rb +2 -1
  16. data/lib/code/node/statement.rb +23 -23
  17. data/lib/code/node/string.rb +2 -1
  18. data/lib/code/object/argument.rb +4 -3
  19. data/lib/code/object/boolean.rb +5 -4
  20. data/lib/code/object/class.rb +4 -3
  21. data/lib/code/object/code.rb +33 -0
  22. data/lib/code/object/context.rb +7 -5
  23. data/lib/code/object/date.rb +31 -6
  24. data/lib/code/object/decimal.rb +36 -31
  25. data/lib/code/object/dictionary.rb +4 -4
  26. data/lib/code/object/duration.rb +8 -5
  27. data/lib/code/object/function.rb +42 -26
  28. data/lib/code/object/global.rb +58 -38
  29. data/lib/code/object/integer.rb +23 -26
  30. data/lib/code/object/list.rb +5 -5
  31. data/lib/code/object/nothing.rb +2 -4
  32. data/lib/code/object/parameter.rb +43 -0
  33. data/lib/code/object/range.rb +13 -11
  34. data/lib/code/object/string.rb +7 -7
  35. data/lib/code/object/time.rb +15 -8
  36. data/lib/code/object.rb +102 -5
  37. data/lib/code/parser.rb +1 -1
  38. data/lib/code/type.rb +10 -2
  39. data/lib/code/version.rb +1 -1
  40. data/lib/code-ruby.rb +6 -0
  41. data/lib/code.rb +8 -5
  42. data/spec/code/object/dictionary_spec.rb +0 -2
  43. data/spec/code_spec.rb +122 -16
  44. metadata +4 -3
  45. data/lib/code/object/number.rb +0 -11
data/lib/code/object.rb CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  class Code
4
4
  class Object
5
+ attr_reader :raw
6
+
7
+ def initialize(*_args, **_kargs, &_block)
8
+ @raw = nil unless defined?(@raw)
9
+ end
10
+
5
11
  def self.maybe
6
12
  Type::Maybe.new(self)
7
13
  end
@@ -29,6 +35,9 @@ class Code
29
35
  value = arguments.first&.value
30
36
 
31
37
  case operator.to_s
38
+ when "new"
39
+ sig(args) { Object.repeat }
40
+ code_new(*arguments.map(&:value))
32
41
  when "!", "not"
33
42
  sig(args)
34
43
  code_exclamation_point
@@ -65,6 +74,42 @@ class Code
65
74
  when "||", "or"
66
75
  sig(args) { Object }
67
76
  code_or_operator(value)
77
+ when "to_boolean"
78
+ sig(args)
79
+ Boolean.new(self)
80
+ when "to_class"
81
+ sig(args)
82
+ Class.new(self)
83
+ when "to_date"
84
+ sig(args)
85
+ Date.new(self)
86
+ when "to_decimal"
87
+ sig(args)
88
+ Decimal.new(self)
89
+ when "to_dictionary"
90
+ sig(args)
91
+ Dictionary.new(self)
92
+ when "to_duration"
93
+ sig(args)
94
+ Duration.new(self)
95
+ when "to_integer"
96
+ sig(args)
97
+ Integer.new(self)
98
+ when "to_list"
99
+ sig(args)
100
+ List.new(self)
101
+ when "to_nothing"
102
+ sig(args)
103
+ Nothing.new(self)
104
+ when "to_range"
105
+ sig(args)
106
+ Range.new(self)
107
+ when "to_string"
108
+ sig(args)
109
+ String.new(self)
110
+ when "to_time"
111
+ sig(args)
112
+ Time.new(self)
68
113
  when /=$/
69
114
  sig(args) { Object }
70
115
 
@@ -86,12 +131,16 @@ class Code
86
131
  context.code_fetch(self)
87
132
  else
88
133
  raise(
89
- Code::Error::Undefined,
134
+ Error::Undefined,
90
135
  "#{operator} not defined on #{inspect}:Class"
91
136
  )
92
137
  end
93
138
  end
94
139
 
140
+ def self.code_new(*arguments)
141
+ new(*arguments)
142
+ end
143
+
95
144
  def self.code_and_operator(other)
96
145
  truthy? ? other : self
97
146
  end
@@ -220,6 +269,42 @@ class Code
220
269
  when "||", "or"
221
270
  sig(args) { Object }
222
271
  code_or_operator(value)
272
+ when "to_boolean"
273
+ sig(args)
274
+ Boolean.new(self)
275
+ when "to_class"
276
+ sig(args)
277
+ Class.new(self)
278
+ when "to_date"
279
+ sig(args)
280
+ Date.new(self)
281
+ when "to_decimal"
282
+ sig(args)
283
+ Decimal.new(self)
284
+ when "to_duration"
285
+ sig(args)
286
+ Duration.new(self)
287
+ when "to_dictionary"
288
+ sig(args)
289
+ Dictionary.new(self)
290
+ when "to_integer"
291
+ sig(args)
292
+ Integer.new(self)
293
+ when "to_list"
294
+ sig(args)
295
+ List.new(self)
296
+ when "to_nothing"
297
+ sig(args)
298
+ Nothing.new(self)
299
+ when "to_range"
300
+ sig(args)
301
+ Range.new(self)
302
+ when "to_string"
303
+ sig(args)
304
+ String.new(self)
305
+ when "to_time"
306
+ sig(args)
307
+ Time.new(self)
223
308
  when /=$/
224
309
  sig(args) { Object }
225
310
 
@@ -241,8 +326,8 @@ class Code
241
326
  context.code_fetch(self)
242
327
  else
243
328
  raise(
244
- Code::Error::Undefined,
245
- "#{operator} not defined on #{inspect}:#{self.class.name}"
329
+ Error::Undefined,
330
+ "#{operator.inspect} not defined on #{inspect}:#{self.class.name}"
246
331
  )
247
332
  end
248
333
  end
@@ -264,11 +349,23 @@ class Code
264
349
  end
265
350
 
266
351
  def code_exclusive_range(value)
267
- Range.new(self, value, exclude_end: true)
352
+ Range.new(
353
+ self,
354
+ value,
355
+ Dictionary.new({
356
+ String.new(:exclude_end) => Boolean.new(true)
357
+ })
358
+ )
268
359
  end
269
360
 
270
361
  def code_inclusive_range(value)
271
- Range.new(self, value, exclude_end: false)
362
+ Range.new(
363
+ self,
364
+ value,
365
+ Dictionary.new({
366
+ String.new(:exclude_end) => Boolean.new(false)
367
+ })
368
+ )
272
369
  end
273
370
 
274
371
  def code_or_operator(other)
data/lib/code/parser.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Code
4
4
  class Parser
5
- class Error < ::Code::Error
5
+ class Error < Error
6
6
  end
7
7
 
8
8
  def initialize(input)
data/lib/code/type.rb CHANGED
@@ -2,8 +2,16 @@
2
2
 
3
3
  class Code
4
4
  class Type
5
- def name
6
- "Type"
5
+ def maybe
6
+ Maybe.new(self)
7
+ end
8
+
9
+ def repeat(minimum = 0, maximum = nil)
10
+ Repeat.new(self, minimum:, maximum:)
11
+ end
12
+
13
+ def |(other)
14
+ Or.new(self, other)
7
15
  end
8
16
 
9
17
  def valid?(_argument)
data/lib/code/version.rb CHANGED
@@ -2,4 +2,4 @@
2
2
 
3
3
  require_relative "../code"
4
4
 
5
- Code::Version = Gem::Version.new("0.11.0")
5
+ Code::Version = Gem::Version.new("0.12.0")
data/lib/code-ruby.rb CHANGED
@@ -5,7 +5,9 @@ require "active_support/core_ext/date/conversions"
5
5
  require "active_support/core_ext/numeric/time"
6
6
  require "active_support/core_ext/object/json"
7
7
  require "active_support/core_ext/object/blank"
8
+ require "active_support/core_ext/array/access"
8
9
  require "bigdecimal"
10
+ require "bigdecimal/util"
9
11
  require "json"
10
12
  require "language-ruby"
11
13
  require "stringio"
@@ -15,3 +17,7 @@ require "zeitwerk"
15
17
  loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
16
18
  loader.ignore("#{__dir__}/code-ruby.rb")
17
19
  loader.setup
20
+
21
+ class Object
22
+ alias is_an? is_a?
23
+ end
data/lib/code.rb CHANGED
@@ -17,6 +17,12 @@ class Code
17
17
  @context = Object::Context.new
18
18
  end
19
19
 
20
+ def self.parse(input, timeout: DEFAULT_TIMEOUT)
21
+ Timeout.timeout(timeout) do
22
+ Parser.parse(input).to_raw
23
+ end
24
+ end
25
+
20
26
  def self.evaluate(
21
27
  input,
22
28
  output: StringIO.new,
@@ -28,11 +34,8 @@ class Code
28
34
 
29
35
  def evaluate
30
36
  Timeout.timeout(timeout) do
31
- Node::Code.new(::Code::Parser.parse(input).to_raw).evaluate(
32
- context:,
33
- output:,
34
- error:
35
- )
37
+ parsed = Code.parse(input)
38
+ Node::Code.new(parsed).evaluate(context:, output:, error:)
36
39
  end
37
40
  end
38
41
 
@@ -76,10 +76,8 @@ RSpec.describe Code::Object::Dictionary do
76
76
  ["{ a: nothing }.compact", "{}"],
77
77
  ["{ age: 31 }.delete_unless { |key| key == :age }", "{ age: 31 }"],
78
78
  ["{ age: 31 }.delete_unless(Integer)", "{ age: 31 }"],
79
- ["{ age: 31 }.delete_unless(Number)", "{ age: 31 }"],
80
79
  ["{ age: 31 }.keep_unless { |key| key == :age }", "{}"],
81
80
  ["{ age: 31 }.keep_unless(Integer)", "{}"],
82
- ["{ age: 31 }.keep_unless(Number)", "{}"],
83
81
  ["{ first_name: :Dorian } < dictionary", "true"],
84
82
  ["{ first_name: :Dorian }.any? { |_, value| value == :Dorian }", "true"],
85
83
  ["{ first_name: :Dorian }.delete_if { |_, value| value == :Dorian }", "{}"],
data/spec/code_spec.rb CHANGED
@@ -31,13 +31,65 @@ RSpec.describe Code do
31
31
  %w[0x10 16],
32
32
  %w[1.0e1 10.0],
33
33
  %w[1.1 1.1],
34
+ %w[1.day.ago.after? false],
35
+ %w[1.day.ago.after?(1.day.from_now) false],
36
+ %w[1.day.ago.before? true],
37
+ %w[1.day.ago.before?(1.day.from_now) true],
38
+ %w[1.day.from_now.after? true],
39
+ %w[1.day.from_now.after?(1.day.ago) true],
40
+ %w[1.day.from_now.before? false],
41
+ %w[1.day.from_now.before?(1.day.ago) false],
34
42
  %w[10e1.0 100.0],
35
43
  %w[1e1 10],
44
+ %w[2.days.ago.future? false],
45
+ %w[2.days.ago.past? true],
46
+ %w[2.days.from_now.future? true],
47
+ %w[2.days.from_now.past? false],
36
48
  %w[9975×14÷8 17456.25],
37
49
  %w[:Hello.include?(:H) true],
38
50
  %w[:admin? :admin?],
39
51
  %w[:hello :hello],
40
52
  %w[:update!.to_string :update!],
53
+ %w[Boolean(1) true],
54
+ %w[Boolean(2.days.ago) true],
55
+ %w[Boolean(2.days.ago) true],
56
+ %w[Boolean(false) false],
57
+ %w[Boolean(nothing) false],
58
+ %w[Boolean(true) true],
59
+ %w[Boolean({}) true],
60
+ %w[Boolean.new false],
61
+ %w[Boolean.new(1) true],
62
+ %w[Boolean.new(2.days.ago) true],
63
+ %w[Boolean.new(2.days.ago) true],
64
+ %w[Boolean.new(false) false],
65
+ %w[Boolean.new(nothing) false],
66
+ %w[Boolean.new(true) true],
67
+ %w[Boolean.new({}) true],
68
+ %w[Class(2.days.ago) Time],
69
+ %w[Class(Boolean) Boolean],
70
+ %w[Class(Time) Time],
71
+ %w[Class(nothing) Nothing],
72
+ %w[Class(true) Boolean],
73
+ %w[Class.new Nothing],
74
+ %w[Class.new Nothing],
75
+ %w[Class.new(2.days.ago) Time],
76
+ %w[Class.new(Boolean) Boolean],
77
+ %w[Class.new(Date) Date],
78
+ %w[Class.new(Time) Time],
79
+ %w[Class.new(nothing) Nothing],
80
+ %w[Class.new(true) Boolean],
81
+ %w[Date Date],
82
+ %w[Date("0001-01-01").to_string :0001-01-01],
83
+ %w[Date("2024-03-02").to_string :2024-03-02],
84
+ %w[Decimal(1) 1.0],
85
+ %w[Decimal(1) 1],
86
+ %w[Decimal(:1) 1.0],
87
+ %w[Decimal(:1) 1],
88
+ %w[Decimal.new 0],
89
+ %w[Decimal.new(1) 1.0],
90
+ %w[Decimal.new(1) 1],
91
+ %w[Decimal.new(:1) 1.0],
92
+ %w[Decimal.new(:1) 1],
41
93
  %w[false false],
42
94
  %w[true true],
43
95
  %w[{} {}],
@@ -57,14 +109,6 @@ RSpec.describe Code do
57
109
  ["1 >> 1", "0"],
58
110
  ["1 ^ 2", "3"],
59
111
  ["1 | 2", "3"],
60
- %w[1.day.ago.after?(1.day.from_now) false],
61
- %w[1.day.ago.before?(1.day.from_now) true],
62
- %w[1.day.from_now.after?(1.day.ago) true],
63
- %w[1.day.from_now.before?(1.day.ago) false],
64
- %w[1.day.ago.after? false],
65
- %w[1.day.ago.before? true],
66
- %w[1.day.from_now.after? true],
67
- %w[1.day.from_now.before? false],
68
112
  ["2 * 3 + 2", "8"],
69
113
  ["2 * 3", "6"],
70
114
  ["2 ** 3 ** 2", "512"],
@@ -73,6 +117,20 @@ RSpec.describe Code do
73
117
  ["2 / 4", "0.5"],
74
118
  ["3.times {}", "3"],
75
119
  ["4 % 3", "1"],
120
+ ["Boolean([])", "true"],
121
+ ["Boolean(true, false)", "true"],
122
+ ["Boolean.new([])", "true"],
123
+ ["Boolean.new(false, true)", "false"],
124
+ ["Boolean.new(true, false)", "true"],
125
+ ["Class(true, 1)", "Boolean"],
126
+ ["Class.new(Boolean, Time)", "Boolean"],
127
+ ["Class.new(Time, Boolean)", "Time"],
128
+ ["Date(2024, 3, 2).to_string", ":2024-03-02"],
129
+ ["Date(2024,3, 2).to_string", ":2024-03-02"],
130
+ ["Decimal(1, :2)", "100"],
131
+ ["Decimal(:1, 2)", "100.0"],
132
+ ["Decimal.new(1, :2)", "100"],
133
+ ["Decimal.new(:1, 2)", "100.0"],
76
134
  ["[,,].include?(4)", "false"],
77
135
  ["[,].include?(4)", "false"],
78
136
  ["[1, 2, 3, \n ].include?(4)", "false"],
@@ -80,6 +138,8 @@ RSpec.describe Code do
80
138
  ["[1, 2, 3]", "[1, 2, 3]"],
81
139
  ["[1, 2, 3].include?(2)", "true"],
82
140
  ["[1, 2, 3].include?(4)", "false"],
141
+ ["[1, 2, 3].map { |i| next if i == 2 i ** 2}", "[1, nothing, 9]"],
142
+ ["[1, 2, 3].map { |i| next(0) if i.even? i ** 2}", "[1, 0, 9]"],
83
143
  ["[1, 2, 3].select { |n| n.even? }", "[2]"],
84
144
  ["[1, 2, 3].select { |n| n.even? }.select { |n| n.odd? }", "[]"],
85
145
  ["[1, 2].map(&:to_string)", "[:1, :2]"],
@@ -89,6 +149,8 @@ RSpec.describe Code do
89
149
  ["[[true]]", "[[true]]"],
90
150
  ["[]", "[]"],
91
151
  ["\r\n", "nothing"],
152
+ ["a = 0 [1, 2, 3].each { |i| next if i == 2 a += i } a", "4"],
153
+ ["a = 0 loop a += 1 break end a", "1"],
92
154
  ["a = 0\nuntil a > 10 a += 1 end a", "11"],
93
155
  ["a = 0\nwhile a < 10 a += 1 end a", "10"],
94
156
  ["a = 1 3.times { a += 1 } a", "4"],
@@ -142,14 +204,7 @@ RSpec.describe Code do
142
204
  ['user = {} user.name = "Dorian" user.name', ":Dorian"],
143
205
  ['user = {} user[:name] = "Dorian" user[:name]', ":Dorian"],
144
206
  ['{ "first_name": "Dorian" }', '{"first_name" => "Dorian"}'],
145
- ["a = 0 loop a += 1 break end a", "1"],
146
- ["a = 0 [1, 2, 3].each { |i| next if i == 2 a += i } a", "4"],
147
- ["[1, 2, 3].map { |i| next if i == 2 i ** 2}", "[1, nothing, 9]"],
148
- ["[1, 2, 3].map { |i| next(0) if i.even? i ** 2}", "[1, 0, 9]"],
149
- %w[2.days.ago.past? true],
150
- %w[2.days.from_now.past? false],
151
- %w[2.days.ago.future? false],
152
- %w[2.days.from_now.future? true]
207
+ ["", ""]
153
208
  ].each do |input, expected|
154
209
  it "#{input} == #{expected}" do
155
210
  expect(Code.evaluate(input)).to eq(Code.evaluate(expected))
@@ -168,8 +223,59 @@ RSpec.describe Code do
168
223
  2.days.from_now
169
224
  2.hours.ago
170
225
  2.hours.from_now
226
+ Boolean.new
227
+ Boolean.new(true)
228
+ Boolean.new(false)
229
+ Class.new
230
+ Class.new(Boolean)
231
+ Class.new(Class)
232
+ Context.new
233
+ Context.new(a:1)
234
+ Date.new
235
+ Date.new("2024-03-05")
236
+ Date.today
237
+ Date.yesterday
171
238
  Date.tomorrow
239
+ Decimal.new
240
+ Decimal.new(0)
241
+ Decimal.new(1.2)
242
+ Dictionary.new
243
+ Dictionary.new(a:1)
244
+ Duration.new
245
+ Duration.new(1.day)
246
+ Duration.new("P1D")
247
+ Function.new
248
+ Integer.new
249
+ Integer.new(0)
250
+ Integer.new(1)
251
+ Integer.new(1.2)
252
+ List.new
253
+ List.new([])
254
+ List.new([1,2])
255
+ Nothing.new
256
+ Nothing.new(1)
257
+ Object.new
258
+ Object.new(1)
259
+ Range.new
260
+ Range.new(1,2)
261
+ Range.new(-1)
262
+ Range.new(1,2,exclude_end:false)
263
+ Range.new(1,2,exclude_end:true)
264
+ String.new
265
+ String.new(:hello)
266
+ Time.new
267
+ Time.new("2024-03-05.06:10:59.UTC")
268
+ Time.now
269
+ Time.tomorrow
270
+ Time.yesterday
172
271
  Time.tomorrow
272
+ Code.new
273
+ Parameter.new
274
+ Argument.new
275
+ Argument.new(1)
276
+ Argument.new(1,name:"index")
277
+ IdentifierList.new
278
+ IdentifierList.new([])
173
279
  ].each { |input| it(input) { Code.evaluate(input) } }
174
280
 
175
281
  [["puts(true)", "true\n"], %w[print(false) false]].each do |input, expected|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Marié
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-03-02 00:00:00.000000000 Z
11
+ date: 2024-03-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -132,6 +132,7 @@ files:
132
132
  - lib/code/object/argument.rb
133
133
  - lib/code/object/boolean.rb
134
134
  - lib/code/object/class.rb
135
+ - lib/code/object/code.rb
135
136
  - lib/code/object/context.rb
136
137
  - lib/code/object/date.rb
137
138
  - lib/code/object/decimal.rb
@@ -143,7 +144,7 @@ files:
143
144
  - lib/code/object/integer.rb
144
145
  - lib/code/object/list.rb
145
146
  - lib/code/object/nothing.rb
146
- - lib/code/object/number.rb
147
+ - lib/code/object/parameter.rb
147
148
  - lib/code/object/range.rb
148
149
  - lib/code/object/string.rb
149
150
  - lib/code/object/time.rb
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Code
4
- class Object
5
- class Number < Object
6
- def self.name
7
- "Number"
8
- end
9
- end
10
- end
11
- end