fancy 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.md +4 -1
- data/Rakefile +0 -52
- data/bin/fspec +22 -12
- data/bin/ifancy +1 -1
- data/boot/fancy_ext/class.rb +1 -0
- data/boot/fancy_ext/object.rb +8 -6
- data/boot/rbx-compiler/compiler/ast/method_def.rb +2 -0
- data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
- data/boot/rbx-compiler/parser/parser.y +9 -0
- data/doc/api/fancy.jsonp +1 -1
- data/examples/stupid_quicksort.fy +11 -9
- data/lib/array.fy +26 -58
- data/lib/block.fy +0 -1
- data/lib/boot.fy +2 -2
- data/lib/class.fy +85 -0
- data/lib/compiler/ast/class_def.fy +1 -1
- data/lib/compiler/ast/expression_list.fy +4 -12
- data/lib/compiler/ast/identifier.fy +3 -3
- data/lib/compiler/ast/method_def.fy +3 -1
- data/lib/compiler/ast/singleton_method_def.fy +4 -1
- data/lib/contracts.fy +53 -56
- data/lib/dynamic_slot_object.fy +39 -3
- data/lib/enumerable.fy +144 -47
- data/lib/fancy_spec.fy +2 -6
- data/lib/file.fy +67 -0
- data/lib/future.fy +42 -3
- data/lib/hash.fy +35 -29
- data/lib/html.fy +1 -1
- data/lib/integer.fy +34 -0
- data/lib/main.fy +13 -7
- data/lib/message_sink.fy +1 -1
- data/lib/number.fy +10 -0
- data/lib/object.fy +27 -1
- data/lib/package.fy +2 -0
- data/lib/package/handler.fy +56 -0
- data/lib/package/installer.fy +21 -51
- data/lib/package/specification.fy +12 -5
- data/lib/package/uninstaller.fy +22 -3
- data/lib/parser/ext/parser.y +9 -0
- data/lib/proxy.fy +25 -2
- data/lib/rbx.fy +2 -1
- data/lib/rbx/array.fy +16 -1
- data/lib/rbx/class.fy +34 -9
- data/lib/rbx/file.fy +2 -2
- data/lib/rbx/fixnum.fy +1 -11
- data/lib/rbx/io.fy +4 -0
- data/lib/rbx/module.fy +11 -0
- data/lib/rbx/object.fy +12 -12
- data/lib/rbx/proc.fy +7 -0
- data/lib/rbx/string.fy +5 -1
- data/lib/rbx/symbol.fy +9 -0
- data/lib/string.fy +1 -1
- data/lib/tuple.fy +37 -35
- data/lib/version.fy +6 -5
- data/tests/array.fy +14 -2
- data/tests/class.fy +79 -0
- data/tests/dynamic_key_hash.fy +16 -0
- data/tests/dynamic_slot_object.fy +28 -0
- data/tests/dynamic_value_array.fy +12 -0
- data/tests/enumerable.fy +46 -0
- data/tests/file.fy +38 -0
- data/tests/fixnum.fy +22 -0
- data/tests/future.fy +40 -0
- data/tests/hash.fy +8 -7
- data/tests/object.fy +31 -5
- data/tests/set.fy +1 -1
- data/tests/string.fy +18 -2
- data/tests/tuple.fy +7 -0
- data/tools/fancy-mode.el +10 -0
- metadata +9 -12
- data/examples/99bottles.fy +0 -5
- data/examples/conditions_exceptions.fy +0 -9
- data/examples/conditions_parsing.fy +0 -68
- data/examples/dynamic.fy +0 -8
- data/examples/greeter.fy +0 -9
- data/examples/parsing.fy +0 -1
- data/lib/rbx/process.fy +0 -13
- data/lib/remote_object.fy +0 -59
data/lib/rbx/object.fy
CHANGED
@@ -97,7 +97,7 @@ class Object {
|
|
97
97
|
Dynamically sends a given message (without parameters) to @self.
|
98
98
|
"""
|
99
99
|
|
100
|
-
|
100
|
+
__send__(message message_name)
|
101
101
|
}
|
102
102
|
|
103
103
|
def receive_message: message with_params: params {
|
@@ -108,16 +108,7 @@ class Object {
|
|
108
108
|
Dynamically sends a given message with parameters to @self.
|
109
109
|
"""
|
110
110
|
|
111
|
-
|
112
|
-
}
|
113
|
-
|
114
|
-
def message_name: symbol {
|
115
|
-
symbol = symbol to_s
|
116
|
-
val = symbol include?(":")
|
117
|
-
match val {
|
118
|
-
case true -> symbol
|
119
|
-
case false -> ":" <<(symbol)
|
120
|
-
}
|
111
|
+
ruby: (message message_name) args: params
|
121
112
|
}
|
122
113
|
|
123
114
|
def responds_to?: message {
|
@@ -128,7 +119,7 @@ class Object {
|
|
128
119
|
Indicates if an object responds to a given message.
|
129
120
|
"""
|
130
121
|
|
131
|
-
respond_to?(message_name
|
122
|
+
respond_to?(message message_name)
|
132
123
|
}
|
133
124
|
|
134
125
|
def extend: class {
|
@@ -140,4 +131,13 @@ class Object {
|
|
140
131
|
|
141
132
|
metaclass include: class
|
142
133
|
}
|
134
|
+
|
135
|
+
def lambda: block {
|
136
|
+
"""
|
137
|
+
@block @Block@ to be used as the lambda's body.
|
138
|
+
@return @Proc@ with Ruby's lambda semantics (e.g. @return always becomes @return_local)
|
139
|
+
"""
|
140
|
+
|
141
|
+
lambda(&block)
|
142
|
+
}
|
143
143
|
}
|
data/lib/rbx/proc.fy
ADDED
data/lib/rbx/string.fy
CHANGED
@@ -24,7 +24,7 @@ class String {
|
|
24
24
|
match index {
|
25
25
|
case Array -> from: (index[0]) to: (index[1])
|
26
26
|
case Tuple -> ruby_idx: index
|
27
|
-
case _ -> ruby_idx: index
|
27
|
+
case _ -> if: (ruby_idx: index) then: @{ chr() }
|
28
28
|
}
|
29
29
|
}
|
30
30
|
|
@@ -162,4 +162,8 @@ class String {
|
|
162
162
|
|
163
163
|
gsub(substring, substitution)
|
164
164
|
}
|
165
|
+
|
166
|
+
def message_name {
|
167
|
+
self to_sym message_name to_s
|
168
|
+
}
|
165
169
|
}
|
data/lib/rbx/symbol.fy
CHANGED
data/lib/string.fy
CHANGED
@@ -5,7 +5,7 @@ class String {
|
|
5
5
|
class.
|
6
6
|
|
7
7
|
They also include @Fancy::Enumerable@, which means you can use all the
|
8
|
-
common sequence methods on them, like
|
8
|
+
common sequence methods on them, like @Fancy::Enumerable#map:@, @Fancy::Enumerable#select:@ etc.
|
9
9
|
"""
|
10
10
|
|
11
11
|
include: Fancy Enumerable
|
data/lib/tuple.fy
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
ObjectBoundsExceededError = Rubinius ObjectBoundsExceededError
|
2
|
+
|
1
3
|
class Tuple {
|
2
4
|
"""
|
3
5
|
Tuples are fixed-size containers providing index-based access to its
|
@@ -23,44 +25,54 @@ class Tuple {
|
|
23
25
|
t
|
24
26
|
}
|
25
27
|
|
26
|
-
def
|
27
|
-
"""
|
28
|
-
Forwards to @Tuple@#at:.
|
28
|
+
def Tuple === obj {
|
29
29
|
"""
|
30
|
+
Matches @Tuple@ class against an object.
|
31
|
+
If the given object is a Tuple instance, return a Tuple object.
|
30
32
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
def first {
|
35
|
-
"""
|
36
|
-
@return The first element in @self.
|
33
|
+
@obj Object to be matched against
|
34
|
+
@return Tuple instance containing the values of @obj to be used in pattern matching.
|
37
35
|
"""
|
38
36
|
|
39
|
-
|
37
|
+
if: (obj is_a?: Tuple) then: {
|
38
|
+
[obj] + (obj map: 'identity)
|
39
|
+
}
|
40
40
|
}
|
41
41
|
|
42
|
-
def
|
43
|
-
""
|
44
|
-
@return The second element in @self.
|
45
|
-
"""
|
46
|
-
|
47
|
-
at: 1
|
42
|
+
def Tuple name {
|
43
|
+
"Tuple"
|
48
44
|
}
|
49
45
|
|
50
|
-
def
|
46
|
+
def [idx] {
|
51
47
|
"""
|
52
|
-
|
48
|
+
Forwards to @Tuple@#at:.
|
53
49
|
"""
|
54
50
|
|
55
|
-
at:
|
51
|
+
at: idx
|
56
52
|
}
|
57
53
|
|
58
|
-
def
|
54
|
+
def from: from to: to {
|
59
55
|
"""
|
60
|
-
@
|
56
|
+
@from Start index for sub-array.
|
57
|
+
@to End index ofr sub-array.
|
58
|
+
|
59
|
+
Returns sub-array starting at from: and going to to:
|
61
60
|
"""
|
62
61
|
|
63
|
-
|
62
|
+
if: (from < 0) then: {
|
63
|
+
from = size + from
|
64
|
+
}
|
65
|
+
if: (to < 0) then: {
|
66
|
+
to = size + to
|
67
|
+
}
|
68
|
+
subarr = []
|
69
|
+
try {
|
70
|
+
from upto: to do: |i| {
|
71
|
+
subarr << (at: i)
|
72
|
+
}
|
73
|
+
} catch ObjectBoundsExceededError {
|
74
|
+
}
|
75
|
+
subarr
|
64
76
|
}
|
65
77
|
|
66
78
|
def each: block {
|
@@ -129,21 +141,11 @@ class Tuple {
|
|
129
141
|
str
|
130
142
|
}
|
131
143
|
|
132
|
-
def
|
144
|
+
def to_s {
|
133
145
|
"""
|
134
|
-
|
135
|
-
If the given object is a Tuple instance, return a Tuple object.
|
136
|
-
|
137
|
-
@obj Object to be matched against
|
138
|
-
@return Tuple instance containing the values of @obj to be used in pattern matching.
|
146
|
+
@return @String@ concatenation of elements in @self.
|
139
147
|
"""
|
140
148
|
|
141
|
-
|
142
|
-
[obj] + (obj map: 'identity)
|
143
|
-
}
|
144
|
-
}
|
145
|
-
|
146
|
-
def Tuple name {
|
147
|
-
"Tuple"
|
149
|
+
join
|
148
150
|
}
|
149
151
|
}
|
data/lib/version.fy
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
class Fancy {
|
2
|
+
VERSION_MAJOR = 0
|
3
|
+
VERSION_MINOR = 7
|
4
|
+
VERSION_PATCH = 0
|
5
5
|
|
6
|
-
|
6
|
+
VERSION = [VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH] join: "."
|
7
|
+
}
|
data/tests/array.fy
CHANGED
@@ -195,9 +195,9 @@ FancySpec describe: Array with: {
|
|
195
195
|
arr values_at: [1, 3, 4, 10] . is: [2, 'foo, "bar", nil]
|
196
196
|
}
|
197
197
|
|
198
|
-
it: "returns unique values only" with: '
|
198
|
+
it: "returns unique values only" with: 'unique when: {
|
199
199
|
arr = ['foo, 'bar, "baz", 'foo, "baz", "hello", 1, 0, 0, 1, 'bar, 'foo, "hello"]
|
200
|
-
arr
|
200
|
+
arr unique is: ['foo, 'bar, "baz", "hello", 1, 0]
|
201
201
|
}
|
202
202
|
|
203
203
|
it: "prepends self to another array" with: '>> when: {
|
@@ -225,6 +225,10 @@ FancySpec describe: Array with: {
|
|
225
225
|
arr[[-1,-1]] is: [arr last]
|
226
226
|
arr[[-2,-1]] is: ['bar, 'baz]
|
227
227
|
arr[[-2,-1]] is: (arr last: 2)
|
228
|
+
# works with any Enumerable as argument:
|
229
|
+
arr[(0,2)] is: $ arr[[0,2]]
|
230
|
+
arr[(1,2)] is: $ arr[[1,2]]
|
231
|
+
arr[(0,-1)] is: $ arr[[0,-1]]
|
228
232
|
}
|
229
233
|
|
230
234
|
it: "joins all elements with a string to a new string" with: 'join: when: {
|
@@ -329,6 +333,14 @@ FancySpec describe: Array with: {
|
|
329
333
|
([1,2,3,4] + [-1,-2,-3,-4]) is: [1,2,3,4,-1,-2,-3,-4]
|
330
334
|
}
|
331
335
|
|
336
|
+
it: "returns all elements not in another collection" with: '- when: {
|
337
|
+
[] - [] is: []
|
338
|
+
[] - [0] is: []
|
339
|
+
[1,2,3,4] - [2,4] is: [1,3]
|
340
|
+
[1,2,3] - [1,2,3,5] is: []
|
341
|
+
["foo", "bar", "baz"] - ["bar"] is: ["foo", "baz"]
|
342
|
+
}
|
343
|
+
|
332
344
|
it: "returns true for all elements" with: 'all?: when: {
|
333
345
|
[1,2,3,4] all?: |x| { x < 5 } . is: true
|
334
346
|
[1,2,3,4] all?: |x| { x > 0 } . is: true
|
data/tests/class.fy
CHANGED
@@ -567,4 +567,83 @@ FancySpec describe: Class with: {
|
|
567
567
|
WithConstants["Nested"] is_a?: Class . is: true
|
568
568
|
WithConstants["Nested"] is: WithConstants Nested
|
569
569
|
}
|
570
|
+
|
571
|
+
it: "sets a constants value" with: '[]: when: {
|
572
|
+
Kernel["Object"] is: Object
|
573
|
+
{ Kernel["Something"] } raises: NameError
|
574
|
+
Kernel["Something"]: Array
|
575
|
+
{ Kernel["Something"] is: Array } does_not raise: NameError
|
576
|
+
}
|
577
|
+
|
578
|
+
it: "delegates methods correctly" with: 'delegate:to_slot: when: {
|
579
|
+
class Delegation {
|
580
|
+
delegate: ('[], '[]:, '<<, 'to_s, 'to_s:, 'inspect, 'each:in_between:) to_slot: 'object
|
581
|
+
def initialize: @object
|
582
|
+
}
|
583
|
+
|
584
|
+
d = Delegation new: "hello, world!"
|
585
|
+
d to_s is: "hello, world!"
|
586
|
+
d inspect is: $ "hello, world!" inspect
|
587
|
+
d = Delegation new: 2
|
588
|
+
d to_s is: "2"
|
589
|
+
d inspect is: "2"
|
590
|
+
d to_s: 2 . is: "10"
|
591
|
+
d = Delegation new: [1,2,3]
|
592
|
+
d << 5
|
593
|
+
d get_slot: 'object . is: [1,2,3,5]
|
594
|
+
d << nil
|
595
|
+
d get_slot: 'object . is: [1,2,3,5, nil]
|
596
|
+
d[2]: "foo"
|
597
|
+
d get_slot: 'object . is: [1,2,"foo",5,nil]
|
598
|
+
d[1] is: 2
|
599
|
+
}
|
600
|
+
|
601
|
+
it: "allows delegating only a single method" with: 'delegate:to_slot: when: {
|
602
|
+
class Delegation {
|
603
|
+
delegate: 'to_s to_slot: 'number
|
604
|
+
read_write_slot: 'number
|
605
|
+
}
|
606
|
+
|
607
|
+
d = Delegation new
|
608
|
+
d number: 5
|
609
|
+
d to_s is: $ 5 to_s
|
610
|
+
}
|
611
|
+
|
612
|
+
it: "defines a lazy slot" with: 'lazy_slot:value: when: {
|
613
|
+
class LazyClass {
|
614
|
+
lazy_slot: 'foo value: { Thread sleep: 0.01; 42 * @count }
|
615
|
+
def initialize: @count
|
616
|
+
}
|
617
|
+
|
618
|
+
f = LazyClass new: 2
|
619
|
+
start = Time now
|
620
|
+
f foo is: 84
|
621
|
+
Time now - start >= 0.01 is: true
|
622
|
+
start = Time now
|
623
|
+
f foo is: 84
|
624
|
+
Time now - start <= 0.01 is: true
|
625
|
+
}
|
626
|
+
|
627
|
+
it: "returns a string representation of itself and its superclass, if any" with: 'inspect when: {
|
628
|
+
class MySuperClass
|
629
|
+
class MySubClass : MySuperClass
|
630
|
+
|
631
|
+
Fixnum inspect is: "Fixnum : Integer"
|
632
|
+
MySuperClass inspect is: "MySuperClass : Object"
|
633
|
+
MySubClass inspect is: "MySubClass : MySuperClass"
|
634
|
+
Object inspect is: "Object"
|
635
|
+
}
|
636
|
+
|
637
|
+
it: "returns the right amount of instance methods" with: 'instance_methods: when: {
|
638
|
+
class NoMethods
|
639
|
+
class OneMethod {
|
640
|
+
def bar
|
641
|
+
}
|
642
|
+
|
643
|
+
NoMethods instance_methods: false . size is: 0
|
644
|
+
OneMethod instance_methods: false . size is: 1
|
645
|
+
|
646
|
+
NoMethods instance_methods is: $ Object instance_methods
|
647
|
+
Set[OneMethod instance_methods] is: $ Set[Object instance_methods + (OneMethod instance_methods: false)]
|
648
|
+
}
|
570
649
|
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
FancySpec describe: DynamicKeyHash with: {
|
2
|
+
it: "has the correct keys & values defined" when: {
|
3
|
+
dkh = DynamicKeyHash new
|
4
|
+
dkh tap: @{
|
5
|
+
name: "Chris"
|
6
|
+
age: 25
|
7
|
+
country: "Germany"
|
8
|
+
}
|
9
|
+
|
10
|
+
hash = dkh hash
|
11
|
+
Set[hash keys] is: $ Set[['name, 'age, 'country]]
|
12
|
+
hash['name] is: "Chris"
|
13
|
+
hash['age] is: 25
|
14
|
+
hash['country] is: "Germany"
|
15
|
+
}
|
16
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
FancySpec describe: DynamicSlotObject with: {
|
2
|
+
it: "has the correct slots defined" when: {
|
3
|
+
dso = DynamicSlotObject new
|
4
|
+
dso tap: @{
|
5
|
+
name: "Chris"
|
6
|
+
age: 25
|
7
|
+
country: "Germany"
|
8
|
+
}
|
9
|
+
|
10
|
+
dso object tap: @{
|
11
|
+
slots is: ['name, 'age, 'country]
|
12
|
+
class is: Object
|
13
|
+
|
14
|
+
# getters
|
15
|
+
name is: "Chris"
|
16
|
+
age is: 25
|
17
|
+
country is: "Germany"
|
18
|
+
|
19
|
+
# setters
|
20
|
+
name: "Jack"
|
21
|
+
name is: "Jack"
|
22
|
+
age: 26
|
23
|
+
age is: 26
|
24
|
+
country: "USA"
|
25
|
+
country is: "USA"
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
FancySpec describe: DynamicValueArray with: {
|
2
|
+
it: "has the correct values defined" when: {
|
3
|
+
dva = DynamicValueArray new
|
4
|
+
dva tap: @{
|
5
|
+
name: "Chris"
|
6
|
+
age: 25
|
7
|
+
country: "Germany"
|
8
|
+
another_symbol
|
9
|
+
and_another_one
|
10
|
+
} . array is: [['name, "Chris"], ['age, 25], ['country, "Germany"], 'another_symbol, 'and_another_one]
|
11
|
+
}
|
12
|
+
}
|
data/tests/enumerable.fy
CHANGED
@@ -5,4 +5,50 @@ FancySpec describe: Fancy Enumerable with: {
|
|
5
5
|
[[1,2], [2,3,4], [], [1]] superior_by: '> taking: 'size . is: [2,3,4]
|
6
6
|
[[1,2], [2,3,4], [-1]] superior_by: '< taking: 'first . is: [-1]
|
7
7
|
}
|
8
|
+
|
9
|
+
it: "chain-maps all blocks on all values" with: 'map_chained: when: {
|
10
|
+
(1,2,3) map_chained: ('doubled, 'squared, 'to_s) . is: ["4", "16", "36"]
|
11
|
+
(1,2,3) map_chained: (@{ + 1 }, 'to_s) . is: ["2", "3", "4"]
|
12
|
+
[] map_chained: ('class, 'name) . is: []
|
13
|
+
[1, "foo", 'hello] map_chained: ('class, 'name) . is: ["Fixnum", "String", "Symbol"]
|
14
|
+
}
|
15
|
+
|
16
|
+
it: "maps over its elements with their index" with: 'map_with_index: when: {
|
17
|
+
(1,2,3) map_with_index: |x i| {
|
18
|
+
x + i
|
19
|
+
} . is: [1,3,5]
|
20
|
+
|
21
|
+
[1,2,3,4] map_with_index: |x i| {
|
22
|
+
i
|
23
|
+
} . is: [0,1,2,3]
|
24
|
+
|
25
|
+
[] map_with_index: |x i| { i } . is: []
|
26
|
+
}
|
27
|
+
|
28
|
+
it: "counts the amount of elements for which a block yields true" with: 'count: when: {
|
29
|
+
[1,2,3] count: @{ even? } . is: 1
|
30
|
+
(0..10) count: @{ even? } . is: 6
|
31
|
+
(0..10) count: @{ odd? } . is: 5
|
32
|
+
"foo" count: @{ == "o" } . is: 2
|
33
|
+
"foo" count: @{ != "o" } . is: 1
|
34
|
+
(1,2,3) count: @{ < 2 } . is: 1
|
35
|
+
[] count: { true } . is: 0
|
36
|
+
[1] count: { true } . is: 1
|
37
|
+
[1] count: { false } . is: 0
|
38
|
+
}
|
39
|
+
|
40
|
+
it: "returns a string concatenation of all elements in self" with: 'to_s when: {
|
41
|
+
(1,2,3) to_s is: "123"
|
42
|
+
[1,2,3] to_s is: "123"
|
43
|
+
"foo" to_s is: "foo"
|
44
|
+
[] to_s is: ""
|
45
|
+
|
46
|
+
class MyCollection {
|
47
|
+
include: Fancy Enumerable
|
48
|
+
def each: block {
|
49
|
+
(0..5) each: block
|
50
|
+
}
|
51
|
+
}
|
52
|
+
MyCollection new to_s is: "012345"
|
53
|
+
}
|
8
54
|
}
|