code-ruby 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
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