fancy 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +5 -4
  3. data/bin/fspec +19 -1
  4. data/bin/ifancy +139 -35
  5. data/boot/README +2 -9
  6. data/boot/extconf.rb +0 -1
  7. data/boot/fancy_ext/module.rb +5 -15
  8. data/boot/fancy_ext/thread.rb +22 -9
  9. data/boot/rbx-compiler/README +0 -4
  10. data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
  11. data/boot/rbx-compiler/parser/parser.y +1 -0
  12. data/doc/api/fancy.css +1 -6
  13. data/doc/api/fancy.jsonp +1 -1
  14. data/doc/api/fdoc.js +2 -4
  15. data/doc/api/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  16. data/doc/api/images/ui-bg_flat_0_eeeeee_40x100.png +0 -0
  17. data/doc/api/images/ui-bg_flat_55_ffffff_40x100.png +0 -0
  18. data/doc/api/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  19. data/doc/api/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  20. data/doc/api/images/ui-bg_highlight-soft_100_f6f6f6_1x100.png +0 -0
  21. data/doc/api/images/ui-bg_highlight-soft_25_0073ea_1x100.png +0 -0
  22. data/doc/api/images/ui-bg_highlight-soft_50_dddddd_1x100.png +0 -0
  23. data/doc/api/images/ui-icons_0073ea_256x240.png +0 -0
  24. data/doc/api/images/ui-icons_454545_256x240.png +0 -0
  25. data/doc/api/images/ui-icons_666666_256x240.png +0 -0
  26. data/doc/api/images/ui-icons_ff0084_256x240.png +0 -0
  27. data/doc/api/images/ui-icons_ffffff_256x240.png +0 -0
  28. data/doc/api/index.html +5 -4
  29. data/doc/api/jquery-1.8.2.min.js +2 -0
  30. data/doc/api/jquery-ui-1.9.0.custom.min.css +5 -0
  31. data/doc/api/jquery-ui-1.9.0.custom.min.js +6 -0
  32. data/doc/features.md +8 -3
  33. data/examples/argv.fy +1 -1
  34. data/examples/closures.fy +1 -4
  35. data/examples/echo.fy +2 -2
  36. data/examples/guess_number.fy +18 -0
  37. data/examples/nested_classes.fy +3 -15
  38. data/lib/argv.fy +23 -18
  39. data/lib/array.fy +18 -37
  40. data/lib/block.fy +125 -0
  41. data/lib/boot.fy +1 -0
  42. data/lib/compiler/ast/block.fy +1 -1
  43. data/lib/compiler/ast/identifier.fy +1 -1
  44. data/lib/compiler/ast/message_send.fy +0 -13
  45. data/lib/compiler/ast/method_def.fy +1 -1
  46. data/lib/compiler/ast/singleton_method_def.fy +1 -0
  47. data/lib/compiler/ast/tuple_literal.fy +1 -1
  48. data/lib/compiler/command.fy +1 -1
  49. data/lib/compiler/compiler.fy +8 -6
  50. data/lib/contracts.fy +1 -1
  51. data/lib/directory.fy +1 -1
  52. data/lib/dynamic_slot_object.fy +1 -1
  53. data/lib/enumerable.fy +316 -25
  54. data/lib/enumerator.fy +11 -8
  55. data/lib/eval.fy +0 -3
  56. data/lib/fancy_spec.fy +27 -0
  57. data/lib/fdoc.fy +8 -8
  58. data/lib/file.fy +25 -1
  59. data/lib/hash.fy +91 -0
  60. data/lib/html.fy +40 -11
  61. data/lib/integer.fy +4 -0
  62. data/lib/main.fy +18 -11
  63. data/lib/object.fy +33 -7
  64. data/lib/option_parser.fy +20 -1
  65. data/lib/package/dependency.fy +8 -0
  66. data/lib/package/dependency_installer.fy +3 -6
  67. data/lib/package/handler.fy +4 -4
  68. data/lib/package/installer.fy +2 -5
  69. data/lib/package/list.fy +3 -4
  70. data/lib/parser/ext/parser.y +1 -0
  71. data/lib/proxies.fy +0 -2
  72. data/lib/queue.fy +7 -0
  73. data/lib/rbx.fy +1 -0
  74. data/lib/rbx/actor.fy +3 -1
  75. data/lib/rbx/alpha.fy +24 -0
  76. data/lib/rbx/array.fy +3 -1
  77. data/lib/rbx/class.fy +5 -8
  78. data/lib/rbx/date_time.fy +14 -0
  79. data/lib/rbx/file.fy +6 -0
  80. data/lib/rbx/hash.fy +42 -0
  81. data/lib/rbx/thread.fy +5 -7
  82. data/lib/string.fy +56 -4
  83. data/lib/symbol.fy +29 -1
  84. data/lib/time.fy +17 -0
  85. data/lib/vars.fy +4 -3
  86. data/lib/version.fy +1 -1
  87. data/ruby_lib/interactive/hilight.rb +125 -0
  88. data/tests/array.fy +19 -7
  89. data/tests/block.fy +103 -4
  90. data/tests/class.fy +31 -26
  91. data/tests/control_flow.fy +0 -1
  92. data/tests/dynamic_key_hash.fy +22 -1
  93. data/tests/enumerable.fy +239 -7
  94. data/tests/enumerator.fy +7 -0
  95. data/tests/file.fy +16 -0
  96. data/tests/future.fy +1 -11
  97. data/tests/future_proxy.fy +8 -0
  98. data/tests/hash.fy +132 -9
  99. data/tests/html.fy +30 -13
  100. data/tests/integer.fy +3 -0
  101. data/tests/method.fy +6 -11
  102. data/tests/object.fy +12 -5
  103. data/tests/option_parser.fy +12 -3
  104. data/tests/string.fy +69 -1
  105. data/tests/symbol.fy +24 -0
  106. metadata +42 -12
  107. data/boot/rsexp_pretty_printer.rb +0 -76
  108. data/doc/api/jquery-ui.min.js +0 -401
  109. data/doc/api/jquery.tools.min.js +0 -192
  110. data/doc/api/themeswitchertool.js +0 -250
  111. data/examples/future_sends.fy +0 -15
data/lib/string.fy CHANGED
@@ -57,10 +57,9 @@ class String {
57
57
  Indicates, if a @String@ consists only of whitespace.
58
58
  """
59
59
 
60
- self =~ /^\s*$/ if_true: {
61
- true
62
- } else: {
63
- false
60
+ match self {
61
+ case /^\s*$/ -> true
62
+ case _ -> false
64
63
  }
65
64
  }
66
65
 
@@ -232,4 +231,57 @@ class String {
232
231
 
233
232
  if: main? then: main_block else: else_block
234
233
  }
234
+
235
+ def snake_cased {
236
+ """
237
+ Returns a snake cased version of @self.
238
+ """
239
+
240
+ r1 = Regexp new("([A-Z]+)([A-Z][a-z])")
241
+ r2 = Regexp new("([a-z\d])([A-Z])")
242
+ gsub(r1,"\1_\2") gsub(r2,"\1_\2") tr("-", "_") lowercase
243
+ }
244
+
245
+ def camel_cased {
246
+ """
247
+ Returns camel cased version of @self which is expected
248
+ to be a snake cased @String@.
249
+ """
250
+
251
+ self split: "_" . map: @{ capitalize } . join
252
+ }
253
+
254
+ def uppercase? {
255
+ """
256
+ @return @true if @self is completely uppercase, @false otherwise.
257
+
258
+ Example:
259
+ \"F\" uppercase? # => true
260
+ \"FOO\" uppercase? # => true
261
+ \"f\” uppercase? # => false
262
+ \"Foo\" uppercase? # => false
263
+ """
264
+
265
+ { return false } if: blank?
266
+ uppercase == self
267
+ }
268
+
269
+ def lowercase? {
270
+ """
271
+ @return @true if @self is completely lowercase, @false otherwise.
272
+
273
+ Example:
274
+ \"f\” lowercase? # => true
275
+ \"foo\" lowercase? # => true
276
+ \"F\" lowercase? # => false
277
+ \"Foo\" lowercase? # => false
278
+ """
279
+
280
+ { return false } if: blank?
281
+ lowercase == self
282
+ }
283
+
284
+ def starts_with?: string {
285
+ from: 0 to: (string size - 1) == string
286
+ }
235
287
  }
data/lib/symbol.fy CHANGED
@@ -45,11 +45,39 @@ class Symbol {
45
45
  recv receive_message: self
46
46
  }
47
47
 
48
+ def call_with_receiver: receiver {
49
+ call: [receiver]
50
+ }
51
+
52
+ def call: args with_receiver: receiver {
53
+ call: $ args unshift: receiver
54
+ }
55
+
48
56
  def arity {
49
- 1
57
+ m = message_name to_s
58
+ match m {
59
+ case /^:[a-zA-Z0-9_]+$/ -> m count: |c| { c == ":" }
60
+ case /^:\W+$/ -> 2
61
+ case _ -> m count: |c| { c == ":" } + 1
62
+ }
50
63
  }
51
64
 
52
65
  def to_sym {
53
66
  self
54
67
  }
68
+
69
+ def to_block {
70
+ """
71
+ @return @Block@ that sends @self to its first argument, passing along any remaining arguments.
72
+
73
+ Example:
74
+ 'inspect to_block
75
+ # is equal to:
76
+ @{ inspect }
77
+ """
78
+
79
+ |args| {
80
+ call: args
81
+ }
82
+ }
55
83
  }
data/lib/time.fy ADDED
@@ -0,0 +1,17 @@
1
+ class Time {
2
+ def Time duration: block {
3
+ """
4
+ @block @Block to be called & timed.
5
+ @return @Float@ that is the duration (in seconds) of calling @block.
6
+
7
+ Calls @block and times the runtime duration of doing so in seconds.
8
+
9
+ Example:
10
+ Time duration: { Thread sleep: 1 } # => >= 1.0
11
+ """
12
+
13
+ start = Time now
14
+ block call
15
+ Time now - start
16
+ }
17
+ }
data/lib/vars.fy CHANGED
@@ -1,6 +1,7 @@
1
- *stdin* = STDIN
2
- *stdout* = STDOUT
3
- *stderr* = STDERR
1
+ *stdin* = STDIN
2
+ *stdout* = STDOUT
3
+ *stderr* = STDERR
4
+ *fancy_root* = File absolute_path: $ File join: (File dirname: __FILE__, "..")
4
5
 
5
6
  __AFTER__BOOTSTRAP__: {
6
7
  *stdin* documentation: """
data/lib/version.fy CHANGED
@@ -1,6 +1,6 @@
1
1
  class Fancy {
2
2
  VERSION_MAJOR = 0
3
- VERSION_MINOR = 8
3
+ VERSION_MINOR = 9
4
4
  VERSION_PATCH = 0
5
5
 
6
6
  VERSION = [VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH] join: "."
@@ -0,0 +1,125 @@
1
+ # encoding: utf-8
2
+ module CodeRay
3
+ module Scanners
4
+
5
+ # Fancy scanner by swarley.
6
+ class Fancy < Scanner
7
+
8
+ register_for :fancy
9
+ file_extension 'fy'
10
+
11
+ SPECIAL_FORMS = %w[
12
+ def throw try catch class
13
+ ] # :nodoc:
14
+
15
+ CORE_FORMS = %w[
16
+ + - > < == != >= <= % ** * = && || =~
17
+ ] # :nodoc:
18
+
19
+ PREDEFINED_CONSTANTS = %w[
20
+ true false nil
21
+ ] # :nodoc:
22
+
23
+ IDENT_KIND = WordList.new(:ident).
24
+ add(SPECIAL_FORMS, :keyword).
25
+ add(CORE_FORMS, :keyword).
26
+ add(PREDEFINED_CONSTANTS, :predefined_constant)
27
+
28
+ KEYWORD_NEXT_TOKEN_KIND = WordList.new(nil).
29
+ add(%w[ def defn defn- definline defmacro defmulti defmethod defstruct defonce declare ], :function).
30
+ add(%w[ ns ], :namespace).
31
+ add(%w[ defprotocol defrecord ], :class)
32
+
33
+ BASIC_IDENTIFIER = /[a-zA-Z$%*\/_+!?&<>\-=]=?[a-zA-Z0-9$&*+!\/_?<>\-\#]*/
34
+ CLASS_IDENTIFIER = /[A-Z]+[A-Za-z0-9]*|[A-Z]+[A-Za-z0-9]\:\:|\:\:[A-Z]+[A-Za-z0-9]*/
35
+ IDENTIFIER = /(?!-\d)(?:(?:#{BASIC_IDENTIFIER}\.)*#{BASIC_IDENTIFIER}(?:\/#{BASIC_IDENTIFIER})?\.?)|\.\.?/
36
+ SYMBOL = /\'[A-z]+[A-z0-9\:\!\%\^\&\*\_\-\+\=\|\?\\\/\>\<\.]*/o
37
+ DIGIT = /\d+/
38
+ DIGIT10 = DIGIT
39
+ DIGIT16 = /[0-9a-f]/i
40
+ DIGIT8 = /[0-7]/
41
+ DIGIT2 = /[01]/
42
+ DECIMAL = /#{DIGIT}\.#{DIGIT}|-#{DIGIT}\.#{DIGIT}/
43
+ NUM = /(?:\-)*(?:#{DIGIT}|#{DIGIT16}|#{DIGIT8}|#{DIGIT2}|#{DECIMAL})/
44
+ MESSAGE = /[A-Za-z0-9\&\_]+?(?:\:|\?\:)/
45
+ CAPTURE = /\|.+?\|/
46
+
47
+ protected
48
+
49
+ def scan_tokens(encoder, options)
50
+ state = :initial
51
+ kind = nil
52
+
53
+ until eos?
54
+ case state
55
+ when :initial
56
+ if match = scan(/ \s+ | \n | , /x)
57
+ encoder.text_token match, :space
58
+ elsif match = scan(/\#.+?$/)
59
+ encoder.text_token match, :comment
60
+ elsif match = scan(/\{|\}|\@\{|\[|\]|\(|\)/)
61
+ encoder.text_token match, :operator
62
+ elsif match = scan(Regexp.new((%W(+ - @ > < == != >= <= % ** * = && || =~).map {|x| Regexp.escape x }).join '|'))
63
+ encoder.text_token match, :operator
64
+ elsif match = scan(/\.|\$/)
65
+ encoder.text_token match, :predefined_constant
66
+ elsif match = scan(CAPTURE)
67
+ encoder.text_token match, :predefined_constant
68
+ elsif match = scan(CLASS_IDENTIFIER)
69
+ encoder.text_token match, :constant
70
+ elsif match = scan(/\:\:/)
71
+ encoder.text_token match, :constant
72
+ elsif match = scan(MESSAGE)
73
+ encoder.text_token match, :keyword
74
+ elsif match = scan(/#{IDENTIFIER}/o)
75
+ kind = IDENT_KIND[match]
76
+ encoder.text_token match, kind
77
+ if rest? && kind == :keyword
78
+ if kind = KEYWORD_NEXT_TOKEN_KIND[match]
79
+ encoder.text_token match, :space if match = scan(/\s+/o)
80
+ encoder.text_token match, kind if match = scan(/#{IDENTIFIER}/o)
81
+ end
82
+ end
83
+ elsif match = scan(MESSAGE)
84
+ encoder.text_token match, :keyword
85
+ elsif match = scan(/#{SYMBOL}/o)
86
+ encoder.text_token match, :symbol
87
+ elsif match = scan(/\./)
88
+ encoder.text_token match, :operator
89
+ elsif match = scan(/ \# \^ #{IDENTIFIER} /ox)
90
+ encoder.text_token match, :type
91
+ elsif match = scan(/ (\#)? " /x)
92
+ state = self[1] ? :regexp : :string
93
+ encoder.begin_group state
94
+ encoder.text_token match, :delimiter
95
+ elsif match = scan(/#{NUM}/o) and not matched.empty?
96
+ encoder.text_token match, match[/[.e\/]/i] ? :float : :integer
97
+ else
98
+ encoder.text_token getch, :error
99
+ end
100
+
101
+ when :string, :regexp
102
+ if match = scan(/[^"\\]+|\\.?/)
103
+ encoder.text_token match, :content
104
+ elsif match = scan(/"/)
105
+ encoder.text_token match, :delimiter
106
+ encoder.end_group state
107
+ state = :initial
108
+ else
109
+ raise_inspect "else case \" reached; %p not handled." % peek(1),
110
+ encoder, state
111
+ end
112
+ else
113
+ raise 'else case reached'
114
+ end
115
+ end
116
+
117
+ if [:string, :regexp].include? state
118
+ encoder.end_group state
119
+ end
120
+
121
+ encoder
122
+ end
123
+ end
124
+ end
125
+ end
data/tests/array.fy CHANGED
@@ -356,10 +356,6 @@ FancySpec describe: Array with: {
356
356
  [1,2,3,4] any?: |x| { x > 4 } . is: false
357
357
  }
358
358
 
359
- it: "is selected from it with each index" with: 'select_with_index: when: {
360
- ["yooo",2,3,1,'foo,"bar"] select_with_index: |x i| { x is_a?: Fixnum } . is: [[2,1], [3,2], [1,3]]
361
- }
362
-
363
359
  it: "returns its remaining (all but the first) elements as a new Array" with: 'rest when: {
364
360
  [1,2,3,4] rest is: [2,3,4]
365
361
  [] rest is: []
@@ -529,15 +525,20 @@ FancySpec describe: Array with: {
529
525
  arr is: [1,5,4,2,3]
530
526
  }
531
527
 
532
- it: "sorts the array with a given comparison block" with: 'sort_by: when: {
528
+ it: "sorts the array with a given comparison block" with: 'sort: when: {
533
529
  arr = [1,5,4,2,3]
534
530
  sorted = [1,2,3,4,5]
535
- arr sort_by: |a b| { a <=> b } . is: sorted
531
+ arr sort: |a b| { a <=> b } . is: sorted
536
532
  arr is: [1,5,4,2,3]
537
533
 
538
534
  arr = [(1,2), (0,1), (3,0)]
539
535
  sorted = [(3,0), (0,1), (1,2)]
540
- arr sort_by: |a b| { a second <=> (b second) } . is: sorted
536
+ arr sort: |a b| { a second <=> (b second) } . is: sorted
537
+ }
538
+
539
+ it: "sorts the array by a given block" with: 'sort_by: when: {
540
+ arr = [(1,2), (0,1), (3,0)]
541
+ sorted = [(3,0), (0,1), (1,2)]
541
542
  arr sort_by: 'second . is: sorted
542
543
  }
543
544
 
@@ -550,4 +551,15 @@ FancySpec describe: Array with: {
550
551
  [] to_hash: @{ size } . is: <[]>
551
552
  [[1,2],[3,4,5]] to_hash: @{ size } . is: <[2 => [1,2], 3 => [3,4,5]]>
552
553
  }
554
+
555
+ it: "returns an sub-array within a given range" with: 'from:to: when: {
556
+ [] from: 0 to: 1 . is: []
557
+ [1] from: 0 to: 0 . is: [1]
558
+ [1] from: 0 to: 1 . is: [1]
559
+ [1] from: 0 to: 2 . is: [1]
560
+ [1] from: 0 to: -1 . is: [1]
561
+ [1] from: 0 to: -2 . is: []
562
+ [0,1,2,3] from: 0 to: 3 . is: [0,1,2,3]
563
+ [0,1,2,3] from: -1 to: 3 . is: [3]
564
+ }
553
565
  }
data/tests/block.fy CHANGED
@@ -32,6 +32,24 @@ FancySpec describe: Block with: {
32
32
  i is be: { i >= 10 }
33
33
  }
34
34
 
35
+ it: "calls a block while another is true or calls the alternative" with: 'while_true:else: when: {
36
+ i = 0
37
+ { i < 10 } while_true: {
38
+ i = i + 1
39
+ } else: {
40
+ i = "nope"
41
+ }
42
+ i is: 10
43
+
44
+ i = 0
45
+ { i > 10 } while_true: {
46
+ i = i + 1
47
+ } else: {
48
+ i = "nope"
49
+ }
50
+ i is: "nope"
51
+ }
52
+
35
53
  it: "calls a block while another is not true (boolean false)" with: 'while_false: when: {
36
54
  i = 0
37
55
  {i == 10} while_false: {
@@ -206,17 +224,17 @@ FancySpec describe: Block with: {
206
224
  block call: [42] with_receiver: (ClassD new) . is: "in ClassD#inspect: 42"
207
225
  }
208
226
 
209
- it: "calls a block using the ruby-send syntax" with: 'call: when: {
227
+ it: "calls a block using []" with: '[] when: {
210
228
  b = |x y| {
211
229
  x + y
212
230
  }
213
231
 
214
232
  b call: [2,3] . is: 5
215
- b(2,3) . is: 5
233
+ b[(2,3)] . is: 5
216
234
 
217
235
  b2 = |x| { x * 5 }
218
- b2("hello") is: ("hello" * 5)
219
- b2("foo") is: (b2 call: ["foo"])
236
+ b2["hello"] is: ("hello" * 5)
237
+ b2["foo"] is: (b2 call: ["foo"])
220
238
  }
221
239
 
222
240
  it: "dynamically creates a object with slots defined in a Block" with: 'to_object when: {
@@ -239,6 +257,26 @@ FancySpec describe: Block with: {
239
257
  }
240
258
  }
241
259
 
260
+ it: "dynamically creates a new object with slots recursively defined in blocks" with: 'to_object_deep when: {
261
+ o = {
262
+ name: "Sarah Connor"
263
+ age: 42
264
+ city: "Los Angeles"
265
+ persecuted_by: {
266
+ name: "The Terminator"
267
+ age: 'unknown
268
+ }
269
+ } to_object_deep
270
+
271
+ o name is: "Sarah Connor"
272
+ o age is: 42
273
+ o city is: "Los Angeles"
274
+ o persecuted_by do: {
275
+ name is: "The Terminator"
276
+ age is: 'unknown
277
+ }
278
+ }
279
+
242
280
  it: "dynamically creates a hash with keys and values defined in a Block" with: 'to_hash when: {
243
281
  { } to_hash is: <[]>
244
282
  { foo: "bar" } to_hash is: <['foo => "bar"]>
@@ -302,4 +340,65 @@ FancySpec describe: Block with: {
302
340
  ['city, "San Francisco"],
303
341
  'male, 'programmer, 'happy]
304
342
  }
343
+
344
+ it: "returns a Block that calls self then a given Block" with: 'then: when: {
345
+ a = []
346
+ block = { a << 1 } then: { a << 2 }
347
+ block call
348
+ a is: [1,2]
349
+ block call
350
+ a is: [1,2,1,2]
351
+ }
352
+
353
+ it: "returns a Block that calls itself after a given Block" with: 'after: when: {
354
+ a = []
355
+ block = { a << 1 } after: { a << 2}
356
+ block call
357
+ a is: [2,1]
358
+ block call
359
+ a is: [2,1,2,1]
360
+ }
361
+
362
+ it: "calls itself while logging errors to *stdout*" with: 'call_with_errors_logged when: {
363
+ io = StringIO new
364
+ let: '*stdout* be: io in: {
365
+ {
366
+ "hello" println
367
+ 2 / 0
368
+ } call_with_errors_logged
369
+ }
370
+ io string is: "hello\ndivided by 0\n"
371
+ }
372
+
373
+ it: "calls itself with arguments while logging errors to *stdout*" with: 'call_with_errors_logged: when: {
374
+ io = StringIO new
375
+ let: '*stdout* be: io in: {
376
+ |x y| {
377
+ "x: #{x} y: #{y}" println
378
+ 2 / 0
379
+ } call_with_errors_logged: [10, 20]
380
+ }
381
+ io string is: "x: 10 y: 20\ndivided by 0\n"
382
+ }
383
+
384
+ it: "calls itself while logging errors to a given IO object" with: 'call_with_errors_logged_to: when: {
385
+ io = StringIO new
386
+ {
387
+ 2 / 0
388
+ } call_with_errors_logged_to: io
389
+ io string is: "divided by 0\n"
390
+
391
+ io = StringIO new
392
+ { "fail!" raise! } call_with_errors_logged_to: io
393
+ io string is: "fail!\n"
394
+ }
395
+
396
+ it: "calls itself with arguments while logging errors to a given IO object" with: 'call:with_errors_logged_to: when: {
397
+ io = StringIO new
398
+ |x y| {
399
+ io println: "x: #{x} y: #{y}"
400
+ 2 / 0
401
+ } call: [10, 20] with_errors_logged_to: io
402
+ io string is: "x: 10 y: 20\ndivided by 0\n"
403
+ }
305
404
  }