code-ruby 1.9.10 → 1.9.11

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5d0ceecc686aef6964768b675a3be83b75127a0b425bd452745ed06101d4cc2b
4
- data.tar.gz: 5778826718ab6614081ad28e008cd885d2f1c4ddeb303d5fafa87a67befa63d8
3
+ metadata.gz: 192c4afdcb1d4a87d146ef92fe6241fb510b61b564843c62d07420a2f53caa44
4
+ data.tar.gz: b081b22d57f5582fea6de13e5c24f9c1f33b86a28cd1da97f37449cad40dc5f9
5
5
  SHA512:
6
- metadata.gz: 531f8de970b16632012ea7cdb0836974d3333c24f506026e128ad317b02845fb5576c10529d833b8223cbc099366c9bddff96b794595939c7afdf7944b2c02f1
7
- data.tar.gz: 4ca6148814db2b2e3dce20ccc122f96edbbec1cd1c217ae3700c089159df388b98dbde7e85e897045e136688f0ec9fb2520b02b88b913c7e7dc5a8ca80e359e9
6
+ metadata.gz: ae5c1a1146088cea5f5c0bf7c871bfeb1f89f863960eb4be68944ce273498f578abba6c2b7fcf990ef4208c9cb936fa8a7f9c8d2bb5c7dcb7490497cb69cfdae
7
+ data.tar.gz: cab243aa74f149e2a4e7b7c3523a14aa74e42bf55b9ca11e751682b359377447ac5c3ce5da5b3f29c4c4124d793fad6a2d126a6721979900e443e887f2a4f17d
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- code-ruby (1.9.10)
4
+ code-ruby (1.9.11)
5
5
  activesupport
6
6
  base64
7
7
  bigdecimal
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.9.10
1
+ 1.9.11
data/lib/code/error.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Code
4
4
  class Error < StandardError
5
- class Break < Error
5
+ class ControlFlow < Error
6
6
  attr_reader :code_value
7
7
 
8
8
  def initialize(value = nil)
@@ -10,12 +10,14 @@ class Code
10
10
  end
11
11
  end
12
12
 
13
- class Next < Error
14
- attr_reader :code_value
13
+ class Break < ControlFlow; end
15
14
 
16
- def initialize(value = nil)
17
- @code_value = value.to_code
18
- end
19
- end
15
+ class Next < ControlFlow; end
16
+
17
+ class Continue < Next; end
18
+
19
+ class Return < ControlFlow; end
20
+
21
+ class Retry < ControlFlow; end
20
22
  end
21
23
  end
data/lib/code/format.rb CHANGED
@@ -318,7 +318,7 @@ class Code
318
318
 
319
319
  def format_call(call, indent:)
320
320
  name = call[:name]
321
- raw_arguments = Array(call[:arguments])
321
+ raw_arguments = call[:arguments].presence || []
322
322
  arguments = raw_arguments.map { |arg| format_call_argument(arg) }
323
323
  statement =
324
324
  if arguments.empty?
@@ -336,6 +336,8 @@ class Code
336
336
  end
337
337
 
338
338
  def format_call_argument(argument)
339
+ return format_code_inline(Array(argument), indent: 0) unless argument.is_a?(Hash)
340
+
339
341
  value = format_code_inline(argument[:value], indent: 0)
340
342
  return value unless argument.key?(:name)
341
343
 
@@ -11,13 +11,37 @@ class Code
11
11
  end
12
12
 
13
13
  def evaluate(**args)
14
+ global_control_flow_root = args.fetch(:global_control_flow_root, false)
15
+ control_flow_scope = args.fetch(:control_flow_scope, nil)
16
+ statement_args =
17
+ if global_control_flow_root
18
+ args.merge(global_control_flow_root: false)
19
+ else
20
+ args
21
+ end
14
22
  last = Object::Nothing.new
15
23
 
16
- (@statements || []).each do |statement|
17
- last = statement.evaluate(**args, object: Object::Global.new)
24
+ begin
25
+ (@statements || []).each do |statement|
26
+ last = statement.evaluate(**statement_args, object: Object::Global.new)
27
+ end
28
+ rescue Error::Retry
29
+ retry if control_flow_scope == :group
30
+
31
+ raise
32
+ rescue Error::Break => e
33
+ return e.code_value if control_flow_scope == :group
34
+
35
+ raise
18
36
  end
19
37
 
20
38
  last
39
+ rescue Error::ControlFlow => e
40
+ raise unless global_control_flow_root
41
+
42
+ retry if e.is_a?(Error::Retry)
43
+
44
+ e.code_value
21
45
  end
22
46
 
23
47
  def resolve(**args)
@@ -28,21 +28,9 @@ class Code
28
28
  @left.evaluate(**args)
29
29
  end
30
30
  when "while"
31
- left = Object::Nothing.new
32
-
33
- left = @left&.evaluate(**args) || Object::Nothing.new while (
34
- @right&.evaluate(**args) || Object::Nothing.new
35
- ).truthy?
36
-
37
- left
31
+ evaluate_conditional_loop(condition_truthy: true, **args)
38
32
  when "until"
39
- left = Object::Nothing.new
40
-
41
- left = @left&.evaluate(**args) || Object::Nothing.new while (
42
- @right&.evaluate(**args) || Object::Nothing.new
43
- ).falsy?
44
-
45
- left
33
+ evaluate_conditional_loop(condition_truthy: false, **args)
46
34
  when "rescue"
47
35
  begin
48
36
  @left&.evaluate(**args) || Object::Nothing.new
@@ -69,6 +57,32 @@ class Code
69
57
  )
70
58
  end
71
59
  end
60
+
61
+ private
62
+
63
+ def evaluate_conditional_loop(condition_truthy:, **args)
64
+ left = Object::Nothing.new
65
+
66
+ while loop_condition_truthy?(condition_truthy, **args)
67
+ begin
68
+ left = @left&.evaluate(**args) || Object::Nothing.new
69
+ rescue Error::Next, Error::Continue => e
70
+ left = e.code_value
71
+ next
72
+ rescue Error::Retry
73
+ retry
74
+ rescue Error::Break => e
75
+ return e.code_value
76
+ end
77
+ end
78
+
79
+ left
80
+ end
81
+
82
+ def loop_condition_truthy?(condition_truthy, **args)
83
+ condition = (@right&.evaluate(**args) || Object::Nothing.new).truthy?
84
+ condition_truthy ? condition : !condition
85
+ end
72
86
  end
73
87
  end
74
88
  end
@@ -11,6 +11,7 @@ class Code
11
11
  elsif parsed.key?(:boolean)
12
12
  @statement = Boolean.new(parsed.delete(:boolean))
13
13
  elsif parsed.key?(:group)
14
+ @control_flow_scope = :group
14
15
  @statement = Code.new(parsed.delete(:group))
15
16
  elsif parsed.key?(:call)
16
17
  @statement = Call.new(parsed.delete(:call))
@@ -56,7 +57,12 @@ class Code
56
57
  end
57
58
 
58
59
  def evaluate(**args)
59
- @statement&.evaluate(**args) || Object::Nothing.new
60
+ if @control_flow_scope == :group
61
+ @statement&.evaluate(**args, control_flow_scope: @control_flow_scope) ||
62
+ Object::Nothing.new
63
+ else
64
+ @statement&.evaluate(**args) || Object::Nothing.new
65
+ end
60
66
  end
61
67
 
62
68
  def resolve(**args)
@@ -20,29 +20,61 @@ class Code
20
20
  def evaluate(**args)
21
21
  case @operator
22
22
  when WHILE_KEYWORD
23
- last = Object::Nothing.new
24
-
25
- last = @body&.evaluate(**args) || Object::Nothing.new while (
26
- @statement&.evaluate(**args) || Object::Nothing.new
27
- ).truthy?
28
-
29
- last
23
+ evaluate_conditional_loop(condition_truthy: true, **args)
30
24
  when UNTIL_KEYWORD
31
- last = Object::Nothing.new
32
-
33
- last = @body&.evaluate(**args) || Object::Nothing.new while (
34
- @statement&.evaluate(**args) || Object::Nothing.new
35
- ).falsy?
36
-
37
- last
25
+ evaluate_conditional_loop(condition_truthy: false, **args)
38
26
  when LOOP_KEYWORD
39
- loop { @body&.evaluate(**args) || Object::Nothing.new }
40
- Object::Nothing.new
27
+ evaluate_infinite_loop(**args)
41
28
  else
42
29
  Object::Nothing.new
43
30
  end
44
- rescue Error::Break => e
45
- e.code_value
31
+ end
32
+
33
+ private
34
+
35
+ def evaluate_conditional_loop(condition_truthy:, **args)
36
+ last = Object::Nothing.new
37
+
38
+ while loop_condition_truthy?(condition_truthy, **args)
39
+ begin
40
+ last = @body&.evaluate(**args) || Object::Nothing.new
41
+ rescue Error::Next, Error::Continue => e
42
+ last = e.code_value
43
+ next
44
+ rescue Error::Retry
45
+ retry
46
+ rescue Error::Break => e
47
+ return e.code_value
48
+ end
49
+ end
50
+
51
+ last
52
+ end
53
+
54
+ def evaluate_infinite_loop(**args)
55
+ last = Object::Nothing.new
56
+
57
+ loop do
58
+ begin
59
+ last = @body&.evaluate(**args) || Object::Nothing.new
60
+ rescue Error::Next, Error::Continue => e
61
+ last = e.code_value
62
+ next
63
+ rescue Error::Retry
64
+ retry
65
+ rescue Error::Break => e
66
+ return e.code_value
67
+ end
68
+ end
69
+
70
+ last
71
+ end
72
+
73
+ def loop_condition_truthy?(condition_truthy, **args)
74
+ condition =
75
+ (@statement&.evaluate(**args) || Object::Nothing.new).truthy?
76
+
77
+ condition_truthy ? condition : !condition
46
78
  end
47
79
  end
48
80
  end
@@ -72,6 +72,8 @@ class Code
72
72
  end
73
73
 
74
74
  code_body.code_evaluate(**globals, context: code_context)
75
+ rescue Error::Return => e
76
+ e.code_value
75
77
  end
76
78
 
77
79
  def signature_for_call
@@ -23,9 +23,18 @@ class Code
23
23
  when "break"
24
24
  sig(args) { Object.repeat }
25
25
  raise Error::Break, code_value || Nothing.new
26
+ when "continue"
27
+ sig(args) { Object.repeat }
28
+ raise Error::Continue, code_value || Nothing.new
26
29
  when "next"
27
30
  sig(args) { Object.repeat }
28
31
  raise Error::Next, code_value || Nothing.new
32
+ when "redo", "retry"
33
+ sig(args) { Object.repeat }
34
+ raise Error::Retry, code_value || Nothing.new
35
+ when "return"
36
+ sig(args) { Object.repeat }
37
+ raise Error::Return, code_value || Nothing.new
29
38
  when "Class"
30
39
  sig(args) { Object.repeat }
31
40
  if code_arguments.any?
data/lib/code.rb CHANGED
@@ -49,6 +49,7 @@ class Code
49
49
  Node::Code.new(Code.parse(source)).evaluate(
50
50
  context: context,
51
51
  error: error,
52
+ global_control_flow_root: true,
52
53
  input: input,
53
54
  object: object,
54
55
  output: output,
data/spec/code_spec.rb CHANGED
@@ -313,6 +313,7 @@ RSpec.describe Code do
313
313
  ["[1, 2, 3]", "[1, 2, 3]"],
314
314
  ["[1, 2, 3].include?(2)", "true"],
315
315
  ["[1, 2, 3].include?(4)", "false"],
316
+ ["[1, 2, 3].map { |i| continue(0) if i.even? i ** 2}", "[1, 0, 9]"],
316
317
  ["[1, 2, 3].map { |i| next if i == 2 i ** 2}", "[1, nothing, 9]"],
317
318
  ["[1, 2, 3].map { |i| next(0) if i.even? i ** 2}", "[1, 0, 9]"],
318
319
  ["[1, 2, 3].select { |n| n.even? }", "[2]"],
@@ -328,9 +329,17 @@ RSpec.describe Code do
328
329
  ["[]", "[]"],
329
330
  ["\r\n", "nothing"],
330
331
  ["a = 0 [1, 2, 3].each { |i| next if i == 2 a += i } a", "4"],
332
+ ["a = 0\nb = 0\nwhile a < 4\n a += 1\n continue if a == 2\n b += a\nend\nb", "8"],
331
333
  ["a = 0 loop a += 1 break end a", "1"],
334
+ ["a = 1\nbegin\n a += 1\n break if a > 3\n retry\nend\na", "4"],
335
+ ["x = loop break(42) end x", "42"],
336
+ ["a = 0 a += 1 retry if a < 3 a", "3"],
332
337
  ["a = 0\nuntil a > 10 a += 1 end a", "11"],
333
338
  ["a = 0\nwhile a < 10 a += 1 end a", "10"],
339
+ [
340
+ "a = 0\nretried = false\nwhile a < 2\n a += 1\n retry if a == 1 && !retried && (retried = true)\nend\na",
341
+ "2"
342
+ ],
334
343
  ["a = 1 3.times { a += 1 } a", "4"],
335
344
  ["a = 1 a *= 2 a", "2"],
336
345
  ["a = 1 a += 1 a", "2"],
@@ -360,9 +369,15 @@ RSpec.describe Code do
360
369
  ["if false 1 elsif false 2", "nothing"],
361
370
  ["if false 1 elsif true 2", "2"],
362
371
  ["if false 1", "nothing"],
372
+ ["break(5)", "5"],
373
+ ["continue(6)", "6"],
363
374
  ["if true 1", "1"],
375
+ ["next(7)", "7"],
364
376
  ["not not false", "false"],
365
377
  ["not true", "false"],
378
+ ["retry(8)", "8"],
379
+ ["return(9)", "9"],
380
+ ["f = () => { return(3) 4 } f()", "3"],
366
381
  ["a = 1 orirginal = 2 orirginal", "2"],
367
382
  ["a = 1 andy = 2 andy", "2"],
368
383
  ["ifonly = 1 ifonly", "1"],
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.9.10
4
+ version: 1.9.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Marié