fancy 0.3.0 → 0.3.1
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.
- data/{README → README.md} +33 -50
- data/Rakefile +6 -1
- data/bin/fyi +13 -10
- data/boot/fancy_ext.rb +1 -0
- data/boot/fancy_ext/block_env.rb +6 -2
- data/boot/fancy_ext/console.rb +4 -0
- data/boot/fancy_ext/object.rb +6 -0
- data/boot/rbx-compiler/compiler/ast/identifier.rb +5 -1
- data/boot/rbx-compiler/compiler/ast/match.rb +4 -5
- data/boot/rbx-compiler/compiler/ast/super.rb +1 -0
- data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
- data/boot/rbx-compiler/parser/lexer.c +2316 -0
- data/boot/rbx-compiler/parser/lexer.h +315 -0
- data/boot/rbx-compiler/parser/parser.c +3105 -0
- data/boot/rbx-compiler/parser/parser.h +114 -0
- data/boot/rbx-compiler/parser/parser.rb +2 -2
- data/boot/rbx-compiler/parser/parser.y +3 -3
- data/doc/api/fancy.jsonp +1 -1
- data/doc/features.md +14 -3
- data/examples/async_send.fy +11 -0
- data/examples/fibonacci.fy +1 -1
- data/examples/future.fy +30 -0
- data/examples/futures.fy +14 -0
- data/examples/game_of_life.fy +1 -1
- data/examples/matchers.fy +1 -1
- data/examples/pattern_matching.fy +3 -3
- data/examples/stupid_quicksort.fy +1 -1
- data/examples/threads.fy +1 -1
- data/extconf.rb +7 -0
- data/lib/array.fy +25 -5
- data/lib/block.fy +20 -18
- data/lib/boot.fy +7 -2
- data/lib/class.fy +33 -2
- data/lib/compiler/ast.fy +2 -0
- data/lib/compiler/ast/assign.fy +5 -5
- data/lib/compiler/ast/async_send.fy +25 -0
- data/lib/compiler/ast/block.fy +3 -3
- data/lib/compiler/ast/future_send.fy +20 -0
- data/lib/compiler/ast/identifier.fy +13 -7
- data/lib/compiler/ast/match.fy +32 -22
- data/lib/compiler/ast/message_send.fy +4 -4
- data/lib/compiler/ast/method_def.fy +1 -1
- data/lib/compiler/ast/script.fy +2 -2
- data/lib/compiler/ast/super.fy +1 -0
- data/lib/compiler/compiler.fy +2 -0
- data/lib/documentation.fy +4 -4
- data/lib/enumerable.fy +14 -7
- data/lib/fancy_spec.fy +2 -2
- data/lib/fdoc.fy +7 -7
- data/lib/fiber.fy +11 -0
- data/lib/fiber_pool.fy +78 -0
- data/lib/file.fy +1 -1
- data/lib/future.fy +32 -0
- data/lib/hash.fy +5 -5
- data/lib/lazy_array.fy +23 -0
- data/lib/main.fy +35 -25
- data/lib/method.fy +1 -1
- data/lib/nil_class.fy +4 -0
- data/lib/object.fy +59 -7
- data/lib/package.fy +11 -2
- data/lib/package/installer.fy +50 -20
- data/lib/package/list.fy +34 -0
- data/lib/package/specification.fy +19 -1
- data/lib/parser/ext/Makefile +162 -0
- data/lib/parser/ext/lexer.c +2360 -0
- data/lib/parser/ext/lexer.h +315 -0
- data/lib/parser/ext/lexer.lex +4 -0
- data/lib/parser/ext/parser.c +3382 -0
- data/lib/parser/ext/parser.h +118 -0
- data/lib/parser/ext/parser.y +43 -3
- data/lib/parser/methods.fy +34 -7
- data/lib/parser/parse_error.fy +10 -0
- data/lib/proxy.fy +16 -0
- data/lib/rbx.fy +3 -0
- data/lib/rbx/array.fy +78 -40
- data/lib/rbx/block.fy +35 -1
- data/lib/rbx/class.fy +5 -3
- data/lib/rbx/code_loader.fy +6 -6
- data/lib/rbx/console.fy +1 -1
- data/lib/rbx/date.fy +12 -0
- data/lib/rbx/documentation.fy +5 -5
- data/lib/rbx/exception.fy +1 -1
- data/lib/rbx/fiber.fy +4 -8
- data/lib/rbx/file.fy +4 -3
- data/lib/rbx/fixnum.fy +1 -0
- data/lib/rbx/float.fy +1 -0
- data/lib/rbx/hash.fy +3 -2
- data/lib/rbx/io.fy +5 -5
- data/lib/rbx/match_data.fy +10 -0
- data/lib/rbx/method.fy +4 -4
- data/lib/rbx/no_method_error.fy +1 -1
- data/lib/rbx/object.fy +8 -15
- data/lib/rbx/regexp.fy +4 -0
- data/lib/rbx/string.fy +39 -1
- data/lib/rbx/symbol.fy +1 -1
- data/lib/rbx/system.fy +0 -6
- data/lib/rbx/thread.fy +5 -0
- data/lib/rbx/time.fy +14 -0
- data/lib/rbx/tuple.fy +1 -0
- data/lib/set.fy +1 -1
- data/lib/stack.fy +1 -1
- data/lib/string.fy +2 -2
- data/lib/thread_pool.fy +101 -0
- data/lib/tuple.fy +37 -6
- data/ruby_lib/fancy.rb +46 -0
- data/tests/block.fy +39 -0
- data/tests/class.fy +40 -1
- data/tests/file.fy +2 -2
- data/tests/hash.fy +7 -7
- data/tests/nil_class.fy +2 -2
- data/tests/object.fy +10 -2
- data/tests/pattern_matching.fy +18 -7
- metadata +34 -7
data/lib/rbx/symbol.fy
CHANGED
data/lib/rbx/system.fy
CHANGED
data/lib/rbx/thread.fy
CHANGED
data/lib/rbx/time.fy
ADDED
data/lib/rbx/tuple.fy
CHANGED
data/lib/set.fy
CHANGED
data/lib/stack.fy
CHANGED
data/lib/string.fy
CHANGED
@@ -19,7 +19,7 @@ class String {
|
|
19
19
|
def whitespace? {
|
20
20
|
"Indicates, if a String is empty or a single whitespace character."
|
21
21
|
|
22
|
-
|
22
|
+
empty? or: (self == " ")
|
23
23
|
}
|
24
24
|
|
25
25
|
def blank? {
|
@@ -43,7 +43,7 @@ class String {
|
|
43
43
|
}
|
44
44
|
|
45
45
|
def words {
|
46
|
-
|
46
|
+
split
|
47
47
|
}
|
48
48
|
|
49
49
|
def raise! {
|
data/lib/thread_pool.fy
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# This ThreadPool class is adapted from the Ruby code at:
|
2
|
+
# https://github.com/fizx/thread_pool/
|
3
|
+
|
4
|
+
class ThreadPool {
|
5
|
+
class Executor {
|
6
|
+
read_slot: 'active
|
7
|
+
|
8
|
+
def initialize: queue mutex: mutex {
|
9
|
+
@thread = Thread new: {
|
10
|
+
loop: {
|
11
|
+
mutex synchronize() { @tuple = queue shift() }
|
12
|
+
if: @tuple then: {
|
13
|
+
args, block = @tuple
|
14
|
+
@active = true
|
15
|
+
val = nil
|
16
|
+
try {
|
17
|
+
val = block call: args
|
18
|
+
} catch Exception => e {
|
19
|
+
e message println
|
20
|
+
e backtrace() join: "\n" . println
|
21
|
+
}
|
22
|
+
block complete: true
|
23
|
+
block completed_value: val
|
24
|
+
} else: {
|
25
|
+
@active = false
|
26
|
+
Thread sleep: 0.1
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
def close {
|
33
|
+
@thread exit
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
read_write_slot: 'queue_limit
|
38
|
+
|
39
|
+
# Initialize with number of threads to run
|
40
|
+
def initialize: @count limit: @queue_limit (0) {
|
41
|
+
@mutex = Mutex new()
|
42
|
+
@executors = []
|
43
|
+
@queue = []
|
44
|
+
@count times: { @executors << (Executor new: @queue mutex: @mutex) }
|
45
|
+
}
|
46
|
+
|
47
|
+
# Runs the block at some time in the near future
|
48
|
+
def execute: block with_args: args ([]) {
|
49
|
+
init_completable: block
|
50
|
+
|
51
|
+
if: (@queue_limit > 0) then: {
|
52
|
+
{ Thread sleep: 0.1 } until: { @queue size < @queue_limit }
|
53
|
+
}
|
54
|
+
|
55
|
+
@mutex synchronize() {
|
56
|
+
@queue << [args, block]
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
# Runs the block at some time in the near future, and blocks until complete
|
61
|
+
def execute_synchronous: block with_args: args ([]) {
|
62
|
+
execute: block with_args: args
|
63
|
+
{ Thread sleep: 0.1 } until: { block complete? }
|
64
|
+
block completed_value
|
65
|
+
}
|
66
|
+
|
67
|
+
# Size of the task queue
|
68
|
+
def waiting {
|
69
|
+
@queue size
|
70
|
+
}
|
71
|
+
|
72
|
+
# Size of the thread pool
|
73
|
+
def size {
|
74
|
+
@count
|
75
|
+
}
|
76
|
+
|
77
|
+
# Kills all threads
|
78
|
+
def close {
|
79
|
+
@executors each: |e| { e close }
|
80
|
+
}
|
81
|
+
|
82
|
+
# Sleeps and blocks until the task queue is finished executing
|
83
|
+
def join {
|
84
|
+
{ Thread sleep: 0.1 } until: { { @queue empty? } && { @executors all?: |e| { e active not } } }
|
85
|
+
}
|
86
|
+
|
87
|
+
class Completable {
|
88
|
+
read_write_slot: 'completed_value
|
89
|
+
def complete: @complete {
|
90
|
+
}
|
91
|
+
|
92
|
+
def complete? {
|
93
|
+
@complete not not
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
def protected init_completable: block {
|
98
|
+
block extend(Completable)
|
99
|
+
block complete: false
|
100
|
+
}
|
101
|
+
}
|
data/lib/tuple.fy
CHANGED
@@ -8,16 +8,45 @@ class Tuple {
|
|
8
8
|
|
9
9
|
def [] idx {
|
10
10
|
"""
|
11
|
-
Forwards to @Tuple
|
11
|
+
Forwards to @Tuple@#at:.
|
12
12
|
"""
|
13
13
|
|
14
14
|
at: idx
|
15
15
|
}
|
16
16
|
|
17
|
+
def first {
|
18
|
+
"Returns the first element in the @Tuple@."
|
19
|
+
at: 0
|
20
|
+
}
|
21
|
+
|
22
|
+
def second {
|
23
|
+
"Returns the second element in the @Tuple@."
|
24
|
+
at: 1
|
25
|
+
}
|
26
|
+
|
27
|
+
def third {
|
28
|
+
"Returns the third element in the @Tuple@."
|
29
|
+
at: 2
|
30
|
+
}
|
31
|
+
|
32
|
+
def fourth {
|
33
|
+
"Returns the fourth element in the @Tuple@"
|
34
|
+
at: 3
|
35
|
+
}
|
36
|
+
|
17
37
|
def each: block {
|
18
|
-
|
19
|
-
|
38
|
+
"""
|
39
|
+
@block @Block@ to be called for each element in @self.
|
40
|
+
@return Return value of calling @block on the last item in @self.
|
41
|
+
|
42
|
+
Calls a given @Block@ with each element in the @Tuple@.
|
43
|
+
"""
|
44
|
+
|
45
|
+
val = nil
|
46
|
+
size times: |i| {
|
47
|
+
val = block call: [at: i]
|
20
48
|
}
|
49
|
+
val
|
21
50
|
}
|
22
51
|
|
23
52
|
def == other {
|
@@ -29,8 +58,8 @@ class Tuple {
|
|
29
58
|
"""
|
30
59
|
|
31
60
|
if: (other is_a?: Tuple) then: {
|
32
|
-
if: (
|
33
|
-
|
61
|
+
if: (size == (other size)) then: {
|
62
|
+
size times: |i| {
|
34
63
|
unless: (self[i] == (other[i])) do: {
|
35
64
|
return false
|
36
65
|
}
|
@@ -42,8 +71,10 @@ class Tuple {
|
|
42
71
|
}
|
43
72
|
|
44
73
|
def inspect {
|
74
|
+
"Returns a @String@ representation of a @Tuple@"
|
75
|
+
|
45
76
|
str = "("
|
46
|
-
|
77
|
+
each: |v| {
|
47
78
|
str = str ++ v
|
48
79
|
} in_between: {
|
49
80
|
str = str ++ ", "
|
data/ruby_lib/fancy.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
base = File.dirname(__FILE__)
|
2
|
+
require File.expand_path("../boot/fancy_ext", base)
|
3
|
+
require File.expand_path("../boot/load", base)
|
4
|
+
|
5
|
+
do_retry = true
|
6
|
+
begin
|
7
|
+
Fancy::CodeLoader.load_compiled_file File.expand_path("../lib/boot", base)
|
8
|
+
rescue RuntimeError => e
|
9
|
+
# need to bootstrap first time
|
10
|
+
if e.message =~ /File not found (.*)lib\/boot.fyc/
|
11
|
+
if do_retry
|
12
|
+
puts "Fancy hasn't been bootstrapped yet. Doing that now.\n\n"
|
13
|
+
`cd #{base}/../ && rbx -S rake clean && rbx -S rake`
|
14
|
+
do_retry = false
|
15
|
+
retry
|
16
|
+
else
|
17
|
+
raise e
|
18
|
+
end
|
19
|
+
else
|
20
|
+
raise e
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Remove the bootstrapping code loader
|
25
|
+
bcl = Fancy.send :remove_const, :CodeLoader
|
26
|
+
bcl.load_compiled_file File.expand_path("../lib/rbx/code_loader", base)
|
27
|
+
|
28
|
+
# Initialize the load path
|
29
|
+
Fancy::CodeLoader.push_loadpath File.expand_path("../lib", base)
|
30
|
+
|
31
|
+
# Load compiler+eval support
|
32
|
+
Fancy::CodeLoader.load_compiled_file File.expand_path("../lib/eval", base)
|
33
|
+
|
34
|
+
class Object
|
35
|
+
def fy(hash_or_name)
|
36
|
+
if hash_or_name.is_a? Hash
|
37
|
+
self.send("#{hash_or_name.keys.join(':')}:", *hash_or_name.values)
|
38
|
+
else
|
39
|
+
self.send(":#{hash_or_name}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def fancy_require(fancy_file)
|
44
|
+
Fancy::CodeLoader.load_compiled_file fancy_file
|
45
|
+
end
|
46
|
+
end
|
data/tests/block.fy
CHANGED
@@ -57,6 +57,18 @@ FancySpec describe: Block with: {
|
|
57
57
|
}
|
58
58
|
}
|
59
59
|
|
60
|
+
it: "should call another block while a block yields false" for: 'until_do: when: {
|
61
|
+
i = 0
|
62
|
+
{ i > 10 } until_do: { i <= 10 should == true; i = i + 1 }
|
63
|
+
i should == 11
|
64
|
+
}
|
65
|
+
|
66
|
+
it: "should call a block until another yields true" for: 'until: when: {
|
67
|
+
i = 0
|
68
|
+
{ i <= 10 should == true; i = i + 1 } until: { i > 10 }
|
69
|
+
i should == 11
|
70
|
+
}
|
71
|
+
|
60
72
|
it: "should call itself only when the argument is nil" for: 'unless: when: {
|
61
73
|
try {
|
62
74
|
{ StdError new: "got_run!" . raise! } unless: nil
|
@@ -100,4 +112,31 @@ FancySpec describe: Block with: {
|
|
100
112
|
[1,2,3] map: @{to_s} . should == ["1", "2", "3"]
|
101
113
|
[1,2,3] map: @{to_s * 3} . should == ["111", "222", "333"]
|
102
114
|
}
|
115
|
+
|
116
|
+
it: "should execute a match clause if the block returns a true-ish value" for: '=== when: {
|
117
|
+
def do_match: val {
|
118
|
+
match val {
|
119
|
+
case |x| { x even? } -> "yup, it's even"
|
120
|
+
case _ -> "nope, not even"
|
121
|
+
}
|
122
|
+
}
|
123
|
+
do_match: 2 . should == "yup, it's even"
|
124
|
+
do_match: 1 . should == "nope, not even"
|
125
|
+
}
|
126
|
+
|
127
|
+
it: "should return the receiver of a block" for: 'receiver when: {
|
128
|
+
class Foo { def foo { { self } } } # return block
|
129
|
+
f = Foo new
|
130
|
+
f foo receiver should == f
|
131
|
+
}
|
132
|
+
|
133
|
+
it: "should set the receiver correctly to a new value" for: 'receiver: when: {
|
134
|
+
b = { "hey" }
|
135
|
+
|
136
|
+
b receiver: 10
|
137
|
+
b receiver should == 10
|
138
|
+
|
139
|
+
b receiver: "Hello, World!"
|
140
|
+
b receiver should == "Hello, World!"
|
141
|
+
}
|
103
142
|
}
|
data/tests/class.fy
CHANGED
@@ -64,7 +64,7 @@ FancySpec describe: Class with: {
|
|
64
64
|
instance normal_method should == 'new_normal_found
|
65
65
|
}
|
66
66
|
|
67
|
-
it: "should have dynamically generated getter methods" for: 'responds_to?: when: {
|
67
|
+
it: "should have dynamically generated getter and setter methods" for: 'responds_to?: when: {
|
68
68
|
instance = ClassWithNoMixin new
|
69
69
|
instance responds_to?: 'foo . should == true
|
70
70
|
instance responds_to?: 'bar . should == true
|
@@ -77,6 +77,45 @@ FancySpec describe: Class with: {
|
|
77
77
|
instance responds_to?: "noes:" . should == true
|
78
78
|
}
|
79
79
|
|
80
|
+
it: "should define getter methods for single slots" for: 'read_slot: when: {
|
81
|
+
class Getters {
|
82
|
+
read_slot: 'foo
|
83
|
+
read_slot: 'bar
|
84
|
+
}
|
85
|
+
|
86
|
+
g = Getters new
|
87
|
+
g responds_to?: 'foo . should == true
|
88
|
+
g responds_to?: 'foo: . should == false
|
89
|
+
g responds_to?: 'bar . should == true
|
90
|
+
g responds_to?: 'bar: . should == false
|
91
|
+
}
|
92
|
+
|
93
|
+
it: "should define setter methods for single slots" for: 'write_slot: when: {
|
94
|
+
class Setters {
|
95
|
+
write_slot: 'foo
|
96
|
+
write_slot: 'bar
|
97
|
+
}
|
98
|
+
|
99
|
+
s = Setters new
|
100
|
+
s responds_to?: 'foo . should == false
|
101
|
+
s responds_to?: 'foo: . should == true
|
102
|
+
s responds_to?: 'bar . should == false
|
103
|
+
s responds_to?: 'bar: . should == true
|
104
|
+
}
|
105
|
+
|
106
|
+
it: "should define getter & setter methods for single slots" for: 'read_write_slot: when: {
|
107
|
+
class GettersAndSetters {
|
108
|
+
read_write_slot: 'foo
|
109
|
+
read_write_slot: 'bar
|
110
|
+
}
|
111
|
+
|
112
|
+
gs = GettersAndSetters new
|
113
|
+
gs responds_to?: 'foo . should == true
|
114
|
+
gs responds_to?: 'foo: . should == true
|
115
|
+
gs responds_to?: 'bar . should == true
|
116
|
+
gs responds_to?: 'bar: . should == true
|
117
|
+
}
|
118
|
+
|
80
119
|
it: "should find the instance variable correctly" when: {
|
81
120
|
class AClass {
|
82
121
|
def initialize: foo {
|
data/tests/file.fy
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
FancySpec describe: File with: {
|
2
2
|
it: "should return an array with the openmodes symbols" for: 'open:modes: when: {
|
3
|
-
file = File open: "README" modes: ['read]
|
3
|
+
file = File open: "README.md" modes: ['read]
|
4
4
|
file modes should == ['read]
|
5
5
|
file close
|
6
6
|
}
|
7
7
|
|
8
8
|
it: "should be open after opening it and closed after closing" for: 'close when: {
|
9
|
-
file = File open: "README" modes: ['read]
|
9
|
+
file = File open: "README.md" modes: ['read]
|
10
10
|
file open? should == true
|
11
11
|
file close
|
12
12
|
file open? should == false
|
data/tests/hash.fy
CHANGED
@@ -52,6 +52,12 @@ FancySpec describe: Hash with: {
|
|
52
52
|
hash['foobar] should == 112.21
|
53
53
|
}
|
54
54
|
|
55
|
+
it: "should return nil if the key isn't defined" for: '[] when: {
|
56
|
+
<['foo => "bar"]> ['bar] . should == nil
|
57
|
+
<[]> ['foobar] . should == nil
|
58
|
+
<['foo => "bar"]> [nil] . should == nil
|
59
|
+
}
|
60
|
+
|
55
61
|
it: "should call the Block for each key and value" for: 'each: when: {
|
56
62
|
hash = <['foo => "bar", 'bar => "baz", 'foobar => 112.21]>
|
57
63
|
hash each: |key val| {
|
@@ -89,13 +95,7 @@ FancySpec describe: Hash with: {
|
|
89
95
|
map: 'second . should == ["is cool"]
|
90
96
|
}
|
91
97
|
|
92
|
-
it: "should return
|
93
|
-
<['foo => "bar"]> ['bar] . should == nil
|
94
|
-
<[]> ['foobar] . should == nil
|
95
|
-
<['foo => "bar"]> [nil] . should == nil
|
96
|
-
}
|
97
|
-
|
98
|
-
it: "should return an Array of the key-value pairs" when: {
|
98
|
+
it: "should return an Array of the key-value pairs" for: 'to_a when: {
|
99
99
|
<['foo => "bar", 'bar => "baz"]> to_a should =? [['foo, "bar"], ['bar, "baz"]]
|
100
100
|
}
|
101
101
|
}
|