fancy 0.6.0 → 0.7.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 (79) hide show
  1. data/LICENSE +1 -1
  2. data/README.md +4 -1
  3. data/Rakefile +0 -52
  4. data/bin/fspec +22 -12
  5. data/bin/ifancy +1 -1
  6. data/boot/fancy_ext/class.rb +1 -0
  7. data/boot/fancy_ext/object.rb +8 -6
  8. data/boot/rbx-compiler/compiler/ast/method_def.rb +2 -0
  9. data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
  10. data/boot/rbx-compiler/parser/parser.y +9 -0
  11. data/doc/api/fancy.jsonp +1 -1
  12. data/examples/stupid_quicksort.fy +11 -9
  13. data/lib/array.fy +26 -58
  14. data/lib/block.fy +0 -1
  15. data/lib/boot.fy +2 -2
  16. data/lib/class.fy +85 -0
  17. data/lib/compiler/ast/class_def.fy +1 -1
  18. data/lib/compiler/ast/expression_list.fy +4 -12
  19. data/lib/compiler/ast/identifier.fy +3 -3
  20. data/lib/compiler/ast/method_def.fy +3 -1
  21. data/lib/compiler/ast/singleton_method_def.fy +4 -1
  22. data/lib/contracts.fy +53 -56
  23. data/lib/dynamic_slot_object.fy +39 -3
  24. data/lib/enumerable.fy +144 -47
  25. data/lib/fancy_spec.fy +2 -6
  26. data/lib/file.fy +67 -0
  27. data/lib/future.fy +42 -3
  28. data/lib/hash.fy +35 -29
  29. data/lib/html.fy +1 -1
  30. data/lib/integer.fy +34 -0
  31. data/lib/main.fy +13 -7
  32. data/lib/message_sink.fy +1 -1
  33. data/lib/number.fy +10 -0
  34. data/lib/object.fy +27 -1
  35. data/lib/package.fy +2 -0
  36. data/lib/package/handler.fy +56 -0
  37. data/lib/package/installer.fy +21 -51
  38. data/lib/package/specification.fy +12 -5
  39. data/lib/package/uninstaller.fy +22 -3
  40. data/lib/parser/ext/parser.y +9 -0
  41. data/lib/proxy.fy +25 -2
  42. data/lib/rbx.fy +2 -1
  43. data/lib/rbx/array.fy +16 -1
  44. data/lib/rbx/class.fy +34 -9
  45. data/lib/rbx/file.fy +2 -2
  46. data/lib/rbx/fixnum.fy +1 -11
  47. data/lib/rbx/io.fy +4 -0
  48. data/lib/rbx/module.fy +11 -0
  49. data/lib/rbx/object.fy +12 -12
  50. data/lib/rbx/proc.fy +7 -0
  51. data/lib/rbx/string.fy +5 -1
  52. data/lib/rbx/symbol.fy +9 -0
  53. data/lib/string.fy +1 -1
  54. data/lib/tuple.fy +37 -35
  55. data/lib/version.fy +6 -5
  56. data/tests/array.fy +14 -2
  57. data/tests/class.fy +79 -0
  58. data/tests/dynamic_key_hash.fy +16 -0
  59. data/tests/dynamic_slot_object.fy +28 -0
  60. data/tests/dynamic_value_array.fy +12 -0
  61. data/tests/enumerable.fy +46 -0
  62. data/tests/file.fy +38 -0
  63. data/tests/fixnum.fy +22 -0
  64. data/tests/future.fy +40 -0
  65. data/tests/hash.fy +8 -7
  66. data/tests/object.fy +31 -5
  67. data/tests/set.fy +1 -1
  68. data/tests/string.fy +18 -2
  69. data/tests/tuple.fy +7 -0
  70. data/tools/fancy-mode.el +10 -0
  71. metadata +9 -12
  72. data/examples/99bottles.fy +0 -5
  73. data/examples/conditions_exceptions.fy +0 -9
  74. data/examples/conditions_parsing.fy +0 -68
  75. data/examples/dynamic.fy +0 -8
  76. data/examples/greeter.fy +0 -9
  77. data/examples/parsing.fy +0 -1
  78. data/lib/rbx/process.fy +0 -13
  79. data/lib/remote_object.fy +0 -59
@@ -18,7 +18,7 @@ class Fancy AST {
18
18
 
19
19
  def bytecode: g {
20
20
  pos(g)
21
- docstring = body() body() shift_docstring
21
+ docstring = body() body() docstring
22
22
  docstring if_true: {
23
23
  setdoc = MessageSend new: @line \
24
24
  message: (Identifier from: "for:append:" line: @line) \
@@ -20,11 +20,7 @@ class Fancy AST {
20
20
  def bytecode: g {
21
21
  pos(g)
22
22
  size = @expressions size
23
- @expressions each: |expr| {
24
- size = size - 1
25
- expr bytecode: g
26
- { g pop() } if: (size > 0)
27
- }
23
+ @expressions each: @{ bytecode: g } in_between: { g pop() }
28
24
  }
29
25
 
30
26
  # This method is only used by Rubinius' compiler classes and
@@ -33,13 +29,9 @@ class Fancy AST {
33
29
  []
34
30
  }
35
31
 
36
- # If this expression list contains more than one expression
37
- # and the first one is an string literal, it'll be used as doc.
38
- # This method removes the first documentation string.
39
- def shift_docstring {
40
- if: ((@expressions first kind_of?(Rubinius AST StringLiteral)) && \
41
- (@expressions size > 1)) then: {
42
- @expressions shift()
32
+ def docstring {
33
+ if: (@expressions first kind_of?(Rubinius AST StringLiteral)) then: {
34
+ @expressions first
43
35
  }
44
36
  }
45
37
  }
@@ -57,9 +57,9 @@ class Fancy AST {
57
57
  def bytecode: g {
58
58
  pos(g)
59
59
  match @string {
60
- # case "true" -> g push_true()
61
- # case "false" -> g push_false()
62
- # case "nil" -> g push_nil()
60
+ case "true" -> g push_true()
61
+ case "false" -> g push_false()
62
+ case "nil" -> g push_nil()
63
63
  case _ ->
64
64
  if: (g state() scope() search_local(name)) then: {
65
65
  Rubinius AST LocalVariableAccess new(@line, name) bytecode(g)
@@ -3,7 +3,7 @@ class Fancy AST {
3
3
  def initialize: @line name: @name args: @arguments (MethodArgs new: @line) body: @body (ExpressionList new: @line) access: @access ('public) {
4
4
  { @body = ExpressionList new: @line } unless: @body
5
5
  @name = @name method_name: nil
6
- @docstring = @body shift_docstring
6
+ @docstring = @body docstring
7
7
  generate_ivar_assignment
8
8
 
9
9
  if: (@body empty?) then: {
@@ -71,6 +71,7 @@ class Fancy AST {
71
71
  to: (Self new: @line) \
72
72
  args: (MessageArgs new: @line args: [method_ident])
73
73
  ms bytecode: g
74
+ g pop()
74
75
  }
75
76
 
76
77
  def define_method_missing: g {
@@ -79,6 +80,7 @@ class Fancy AST {
79
80
  to: (Self new: @line) \
80
81
  args: (MessageArgs new: @line)
81
82
  ms bytecode: g
83
+ g pop()
82
84
  }
83
85
  }
84
86
 
@@ -16,6 +16,9 @@ class Fancy AST {
16
16
 
17
17
  class SingletonMethodDefScope : Rubinius AST DefineSingletonScope {
18
18
  def initialize: @line name: @name args: @arguments body: @body {
19
+ if: (@body empty?) then: {
20
+ @body unshift_expression: $ NilLiteral new: @line
21
+ }
19
22
  }
20
23
 
21
24
  define_method("bytecode") |g, recv| {
@@ -24,7 +27,7 @@ class Fancy AST {
24
27
 
25
28
  def bytecode: g receiver: receiver {
26
29
  pos(g)
27
- docstring = @body shift_docstring
30
+ docstring = @body docstring
28
31
  sup = Rubinius AST DefineSingletonScope instance_method('bytecode)
29
32
  sup bind(self) call(g, receiver)
30
33
  MethodDef set: g docstring: docstring line: @line argnames: $ @arguments names()
@@ -1,60 +1,57 @@
1
- class Object {
2
- def require: require_block ensure: ensure_block body: body_block {
3
- requirement = Contract Requirement new: require_block
4
- ensurance = Contract Requirement new: ensure_block
5
- retval = nil
6
- requirement if_fullfilled: {
7
- retval = body_block call
8
- }
9
- ensure_block if_fullfilled: {
10
- retval
11
- }
12
- }
13
-
14
- def require: require_block body: body_block {
15
- requirement = Contract Requirement new: require_block
16
- requirement if_fullfilled: {
17
- body_block call
18
- }
19
- }
20
-
21
- def ensure: ensure_block body: body_block {
22
- require: ensure_block body: body_block
23
- }
24
- }
25
-
26
- class Contract {
27
- class RequirementError : RuntimeError
28
- class Requirement {
29
- def initialize: @block
30
- def if_fullfilled: block {
31
- @block call: [self]
32
- block call
33
- }
34
- def true: expr {
35
- { RequirementError new: "#{expr} not true" . raise! } unless: expr
36
- }
37
- def false: expr {
38
- { RequirementError new: "#{expr} not false" . raise! } if: expr
39
- }
40
- }
41
- }
42
-
43
- # # usage:
44
- # class Fixnum {
45
- # def / other {
46
- # require: @{
47
- # true: $ other is_a?: Fixnum
48
- # true: $ other > 0
49
- # # better:
50
- # other class is: Fixnum
51
- # other is > 0
52
- #
53
- # } body: {
54
- # "foo" println
1
+ # class Class {
2
+ # class Contracts {
3
+ # class ConditionBuilder : Proxy {
4
+ # def initialize: block for: @method class: @class {
5
+ # @conditions = <[]>
6
+ # block call: [self]
7
+ # insert_validations
8
+ # }
9
+
10
+ # def unknown_message: m with_params: p {
11
+ # if: (p size > 0 ) then: {
12
+ # @conditions[m to_s[[0,-2]]]: $ p first
13
+ # }
14
+ # }
15
+
16
+ # def insert_validations {
17
+ # name = @method name()
18
+ # alias_name = "__#{name}__orig__"
19
+ # conditions = @conditions
20
+
21
+ # @class alias_method: alias_name for: name
22
+ # @class define_method: name with: |p| {
23
+ # conditions each: |attr block| {
24
+ # # TODO
25
+ # }
26
+ # receive_message: alias_name with_params: p
27
+ # }
28
+ # }
55
29
  # }
56
30
  # }
31
+
32
+ # def preconditions: block for: method {
33
+ # Contracts ConditionBuilder new: block for: method class: self
34
+ # }
35
+
36
+ # def postconditions: block for: method {
37
+ # Contracts ConditionBuilder new: block for: method class: self
38
+ # }
57
39
  # }
58
40
 
59
- # f = 10 / 2
60
- # g = 10 / 0 # requirement error
41
+ # # example
42
+
43
+ # class Person {
44
+ # read_slots: ('name, 'age)
45
+
46
+ # preconditions: @{
47
+ # name: @{ blank? not }
48
+ # age: @{ > 0 }
49
+ # } for: $ def initialize: @name age: @age
50
+ # }
51
+
52
+
53
+ # p = Person new: "chris" age: 25
54
+ # p inspect println
55
+
56
+
57
+ nil
@@ -1,9 +1,21 @@
1
- class DynamicSlotObject : BasicObject {
1
+ class DynamicSlotObject : Fancy BasicObject {
2
+ """
3
+ Helper class to dynamically create @Object@s with slots defined by sending messages to it.
4
+
5
+ Example:
6
+ dso = DynamicSlotObject new
7
+ dso name: \"Chris\"
8
+ dso age: 25
9
+ dso country: \"Germany\"
10
+ dso object # => Object with slots 'name, 'age and 'country defined
11
+ """
12
+
2
13
  def initialize {
3
14
  @object = Object new
4
15
  }
5
16
 
6
17
  def object {
18
+ @object metaclass read_write_slots: (@object slots)
7
19
  @object
8
20
  }
9
21
 
@@ -14,7 +26,18 @@ class DynamicSlotObject : BasicObject {
14
26
  }
15
27
  }
16
28
 
17
- class DynamicKeyHash : BasicObject {
29
+ class DynamicKeyHash : Fancy BasicObject {
30
+ """
31
+ Helper class to dynamically create @Hash@es with keys and values defined by sending messages to it.
32
+
33
+ Example:
34
+ dkh = DynamicKeyHash new
35
+ dkh name: \"Chris\"
36
+ dkh age: 25
37
+ dkh country: \"Germany\"
38
+ dkh hash # => <['name => \"Chris\", 'age => 25, 'country => \"Germany\"]>
39
+ """
40
+
18
41
  def initialize: @deep (false) {
19
42
  @hash = <[]>
20
43
  }
@@ -36,7 +59,20 @@ class DynamicKeyHash : BasicObject {
36
59
  }
37
60
  }
38
61
 
39
- class DynamicValueArray : BasicObject {
62
+ class DynamicValueArray : Fancy BasicObject {
63
+ """
64
+ Helper class to dynamically create @Array@s with values defined by sending messages to it.
65
+
66
+ Example:
67
+ dva = DynamicValueArray new
68
+ dva name: \"Chris\"
69
+ dva age: 25
70
+ dva country: \"Germany\"
71
+ dva something_else
72
+
73
+ dva array # => [['name, \"Chris\"], ['age, 25], ['country, \"Germany\"], 'something_else]
74
+ """
75
+
40
76
  def initialize {
41
77
  @arr = []
42
78
  }
@@ -4,6 +4,24 @@ class Fancy {
4
4
  Mixin-Class with useful methods for collections that implement an @each:@ method.
5
5
  """
6
6
 
7
+ def at: index {
8
+ """
9
+ @index @Fixnum@ that is the 0-based index into @self.
10
+ @return Value in @self at 0-based position defined by @index.
11
+
12
+ Example:
13
+ \"foo\” at: 2 # => \"o\"
14
+ \"foo\” at: 3 # => nil
15
+ """
16
+
17
+ i = 0
18
+ each: |x| {
19
+ { return x } if: $ i == index
20
+ i = i + 1
21
+ }
22
+ return nil
23
+ }
24
+
7
25
  def each_with_index: block {
8
26
  """
9
27
  @block @Block@ to be called with each element and its index in the @self.
@@ -20,6 +38,48 @@ class Fancy {
20
38
  }
21
39
  }
22
40
 
41
+ def first {
42
+ """
43
+ @return The first element in the @Fancy::Enumerable@.
44
+ """
45
+ at: 0
46
+ }
47
+
48
+ def second {
49
+ """
50
+ @return The second element in the @Fancy::Enumerable@.
51
+ """
52
+ at: 1
53
+ }
54
+
55
+ def third {
56
+ """
57
+ @return The third element in the @Fancy::Enumerable@.
58
+ """
59
+ at: 2
60
+ }
61
+
62
+ def fourth {
63
+ """
64
+ @return The fourth element in the @Fancy::Enumerable@.
65
+ """
66
+ at: 3
67
+ }
68
+
69
+ def last {
70
+ """
71
+ @return Last element in @self or @nil, if empty.
72
+
73
+ Returns the last element in a @Fancy::Enumerable@.
74
+ """
75
+
76
+ item = nil
77
+ each: |x| {
78
+ item = x
79
+ }
80
+ item
81
+ }
82
+
23
83
  def includes?: item {
24
84
  """
25
85
  @item Item to check if it's included in @self.
@@ -28,7 +88,7 @@ class Fancy {
28
88
  Indicates, if a collection includes a given element.
29
89
  """
30
90
 
31
- any?: |x| { item == x }
91
+ any?: |x| { x == item }
32
92
  }
33
93
 
34
94
  def each: each_block in_between: between_block {
@@ -44,7 +104,7 @@ class Fancy {
44
104
  }
45
105
  }
46
106
 
47
- def join: str {
107
+ def join: str ("") {
48
108
  """
49
109
  @str Value (usually a @String@) to be used for the joined @String@.
50
110
  @return @String@ containing all elements in @self interspersed with @str.
@@ -144,6 +204,38 @@ class Fancy {
144
204
  coll
145
205
  }
146
206
 
207
+ def map_with_index: block {
208
+ """
209
+ @block A @Block@ that gets called with each element and its index in @self.
210
+ @return An @Array@ containing all values of calling @block with each element and its index in @self.
211
+
212
+ Returns a new @Array@ with the results of calling a given block for every element and its index.
213
+ """
214
+
215
+ coll = []
216
+ each_with_index: |x i| {
217
+ coll << (block call: [x, i])
218
+ }
219
+ coll
220
+ }
221
+
222
+ def map_chained: blocks {
223
+ """
224
+ @blocks Collection of @Block@s to be called sequentially for every element in @self.
225
+ @return Collection of all values in @self successively called with all blocks in @blocks.
226
+
227
+ Example:
228
+ (1,2,3) map_chained: (@{ + 1 }, 'to_s, @{ * 2 })
229
+ # => [\"22\", \"33\", \"44\"]
230
+ """
231
+
232
+ map: |v| {
233
+ blocks inject: v into: |acc b| {
234
+ b call: [acc]
235
+ }
236
+ }
237
+ }
238
+
147
239
  def select: condition {
148
240
  """
149
241
  @condition A @Block@ that is used as a filter on all elements in @self.
@@ -285,23 +377,23 @@ class Fancy {
285
377
  reduce: block init_val: val
286
378
  }
287
379
 
288
- def uniq {
380
+ def unique {
289
381
  """
290
382
  @return @Array@ of all unique elements in @self.
291
383
 
292
384
  Returns a new Array with all unique values (double entries are skipped).
293
385
 
294
386
  Example:
295
- [1,2,1,2,3] uniq # => [1,2,3]
387
+ [1,2,1,2,3] unique # => [1,2,3]
296
388
  """
297
389
 
298
- uniq_vals = []
390
+ unique_vals = []
299
391
  each: |x| {
300
- unless: (uniq_vals includes?: x) do: {
301
- uniq_vals << x
392
+ unless: (unique_vals includes?: x) do: {
393
+ unique_vals << x
302
394
  }
303
395
  }
304
- uniq_vals
396
+ unique_vals
305
397
  }
306
398
 
307
399
  def size {
@@ -312,7 +404,7 @@ class Fancy {
312
404
  """
313
405
 
314
406
  i = 0
315
- each: |x| {
407
+ each: {
316
408
  i = i + 1
317
409
  }
318
410
  i
@@ -328,31 +420,6 @@ class Fancy {
328
420
  size == 0
329
421
  }
330
422
 
331
- def first {
332
- """
333
- @return First element in @self or @nil, if empty.
334
- """
335
-
336
- each: |x| {
337
- return x
338
- }
339
- nil
340
- }
341
-
342
- def last {
343
- """
344
- @return Last element in @self or @nil, if empty.
345
-
346
- Returns the last element in an Enumerable.
347
- """
348
-
349
- item = nil
350
- each: |x| {
351
- item = x
352
- }
353
- item
354
- }
355
-
356
423
  def compact {
357
424
  """
358
425
  @return @Array@ with all non-nil elements in @self.
@@ -363,16 +430,16 @@ class Fancy {
363
430
  [1,2,nil,3,nil] compact # => [1,2,3]
364
431
  """
365
432
 
366
- reject: |x| { x nil? }
433
+ reject: @{ nil? }
367
434
  }
368
435
 
369
- def superior_by: comparison_block taking: selection_block ('identity) {
436
+ def superior_by: comparison_block taking: selection_block (@{ identity }) {
370
437
  """
371
438
  @comparison_block @Block@ to be used for comparison.
372
- @selection_block @Block@ to be used for selecting the values to be used for comparison by @comparison_bock.
439
+ @selection_block @Block@ to be used for selecting the values to be used for comparison by @comparison_block.
373
440
  @return Superior element in @self in terms of @comparison_block.
374
441
 
375
- Returns the superior element in the @Enumerable that has met
442
+ Returns the superior element in the @Fancy::Enumerable@ that has met
376
443
  the given comparison block with all other elements,
377
444
  applied to whatever @selection_block returns for each element.
378
445
  @selection_block defaults to @identity.
@@ -424,7 +491,7 @@ class Fancy {
424
491
  (assuming them to be @Number@s (implementing '+' & '*')).
425
492
  """
426
493
 
427
- reduce: '+ init_val: 0
494
+ inject: 0 into: '+
428
495
  }
429
496
 
430
497
  def product {
@@ -433,7 +500,7 @@ class Fancy {
433
500
  (assuming them to be @Number@s (implementing @+ & @*)).
434
501
  """
435
502
 
436
- reduce: '* init_val: 1
503
+ inject: 1 into: '*
437
504
  }
438
505
 
439
506
  def average {
@@ -451,7 +518,7 @@ class Fancy {
451
518
  @return @Array@ of @Array@s, partitioned by equal return values of calling @block with each element
452
519
 
453
520
  Example:
454
- 0 upto: 10 . partition_by: |x| { x < 3 } # => [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9, 10]]
521
+ (0..10) partition_by: @{ < 3 } # => [[0, 1, 2], [3, 4, 5, 6, 7, 8, 9, 10]]
455
522
  """
456
523
  last = block call: [first]
457
524
  coll = []
@@ -475,7 +542,7 @@ class Fancy {
475
542
  @return Random element in @self.
476
543
  """
477
544
 
478
- at: (rand(size))
545
+ at: $ size random
479
546
  }
480
547
 
481
548
  def sort_by: block {
@@ -534,8 +601,7 @@ class Fancy {
534
601
  In either case, it simply converts @self to an @Array@ and returns it in reversed order.
535
602
  """
536
603
 
537
- rev = self to_a
538
- rev reverse
604
+ self to_a reverse
539
605
  }
540
606
 
541
607
  def to_hash: block {
@@ -548,12 +614,12 @@ class Fancy {
548
614
  # => <[3 => \"foo\", 5 => \"hello\", 2 => \"ok\", 0 => \"\"]>
549
615
  """
550
616
 
551
- h = <[]>
552
- self each: |val| {
617
+
618
+ inject: <[]> into: |h val| {
553
619
  key = block call: [val]
554
620
  h[key]: val
621
+ h
555
622
  }
556
- h
557
623
  }
558
624
 
559
625
  def reverse_each: block {
@@ -567,5 +633,36 @@ class Fancy {
567
633
 
568
634
  reverse each: block
569
635
  }
636
+
637
+ def count: block {
638
+ """
639
+ @block Predicate @Block@ called with each element.
640
+ @return @Fixnum@ that is the amount of elements in @self for which @block yields @true.
641
+
642
+ Example:
643
+ (0..10) count: @{ even? } # => 6 (even numbers are: 0,2,4,6,8,10)
644
+ [1,2,3] count: @{ odd? } # => 2
645
+ [1,2, \"foo\"] count: @{ class == String } # => 1
646
+ """
647
+
648
+ count = 0
649
+ each: |x| {
650
+ { count = count + 1 } if: $ block call: [x]
651
+ }
652
+ count
653
+ }
654
+
655
+ def to_s {
656
+ """
657
+ @return @String@ concatenation of elements in @self.
658
+
659
+ Example:
660
+ (1,2,3) to_s # => \"123\"
661
+ [1,2,3] to_s # => \"123\"
662
+ \"foo\" to_s # => \"foo\"
663
+ """
664
+
665
+ join
666
+ }
570
667
  }
571
668
  }