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.
Files changed (113) hide show
  1. data/{README → README.md} +33 -50
  2. data/Rakefile +6 -1
  3. data/bin/fyi +13 -10
  4. data/boot/fancy_ext.rb +1 -0
  5. data/boot/fancy_ext/block_env.rb +6 -2
  6. data/boot/fancy_ext/console.rb +4 -0
  7. data/boot/fancy_ext/object.rb +6 -0
  8. data/boot/rbx-compiler/compiler/ast/identifier.rb +5 -1
  9. data/boot/rbx-compiler/compiler/ast/match.rb +4 -5
  10. data/boot/rbx-compiler/compiler/ast/super.rb +1 -0
  11. data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
  12. data/boot/rbx-compiler/parser/lexer.c +2316 -0
  13. data/boot/rbx-compiler/parser/lexer.h +315 -0
  14. data/boot/rbx-compiler/parser/parser.c +3105 -0
  15. data/boot/rbx-compiler/parser/parser.h +114 -0
  16. data/boot/rbx-compiler/parser/parser.rb +2 -2
  17. data/boot/rbx-compiler/parser/parser.y +3 -3
  18. data/doc/api/fancy.jsonp +1 -1
  19. data/doc/features.md +14 -3
  20. data/examples/async_send.fy +11 -0
  21. data/examples/fibonacci.fy +1 -1
  22. data/examples/future.fy +30 -0
  23. data/examples/futures.fy +14 -0
  24. data/examples/game_of_life.fy +1 -1
  25. data/examples/matchers.fy +1 -1
  26. data/examples/pattern_matching.fy +3 -3
  27. data/examples/stupid_quicksort.fy +1 -1
  28. data/examples/threads.fy +1 -1
  29. data/extconf.rb +7 -0
  30. data/lib/array.fy +25 -5
  31. data/lib/block.fy +20 -18
  32. data/lib/boot.fy +7 -2
  33. data/lib/class.fy +33 -2
  34. data/lib/compiler/ast.fy +2 -0
  35. data/lib/compiler/ast/assign.fy +5 -5
  36. data/lib/compiler/ast/async_send.fy +25 -0
  37. data/lib/compiler/ast/block.fy +3 -3
  38. data/lib/compiler/ast/future_send.fy +20 -0
  39. data/lib/compiler/ast/identifier.fy +13 -7
  40. data/lib/compiler/ast/match.fy +32 -22
  41. data/lib/compiler/ast/message_send.fy +4 -4
  42. data/lib/compiler/ast/method_def.fy +1 -1
  43. data/lib/compiler/ast/script.fy +2 -2
  44. data/lib/compiler/ast/super.fy +1 -0
  45. data/lib/compiler/compiler.fy +2 -0
  46. data/lib/documentation.fy +4 -4
  47. data/lib/enumerable.fy +14 -7
  48. data/lib/fancy_spec.fy +2 -2
  49. data/lib/fdoc.fy +7 -7
  50. data/lib/fiber.fy +11 -0
  51. data/lib/fiber_pool.fy +78 -0
  52. data/lib/file.fy +1 -1
  53. data/lib/future.fy +32 -0
  54. data/lib/hash.fy +5 -5
  55. data/lib/lazy_array.fy +23 -0
  56. data/lib/main.fy +35 -25
  57. data/lib/method.fy +1 -1
  58. data/lib/nil_class.fy +4 -0
  59. data/lib/object.fy +59 -7
  60. data/lib/package.fy +11 -2
  61. data/lib/package/installer.fy +50 -20
  62. data/lib/package/list.fy +34 -0
  63. data/lib/package/specification.fy +19 -1
  64. data/lib/parser/ext/Makefile +162 -0
  65. data/lib/parser/ext/lexer.c +2360 -0
  66. data/lib/parser/ext/lexer.h +315 -0
  67. data/lib/parser/ext/lexer.lex +4 -0
  68. data/lib/parser/ext/parser.c +3382 -0
  69. data/lib/parser/ext/parser.h +118 -0
  70. data/lib/parser/ext/parser.y +43 -3
  71. data/lib/parser/methods.fy +34 -7
  72. data/lib/parser/parse_error.fy +10 -0
  73. data/lib/proxy.fy +16 -0
  74. data/lib/rbx.fy +3 -0
  75. data/lib/rbx/array.fy +78 -40
  76. data/lib/rbx/block.fy +35 -1
  77. data/lib/rbx/class.fy +5 -3
  78. data/lib/rbx/code_loader.fy +6 -6
  79. data/lib/rbx/console.fy +1 -1
  80. data/lib/rbx/date.fy +12 -0
  81. data/lib/rbx/documentation.fy +5 -5
  82. data/lib/rbx/exception.fy +1 -1
  83. data/lib/rbx/fiber.fy +4 -8
  84. data/lib/rbx/file.fy +4 -3
  85. data/lib/rbx/fixnum.fy +1 -0
  86. data/lib/rbx/float.fy +1 -0
  87. data/lib/rbx/hash.fy +3 -2
  88. data/lib/rbx/io.fy +5 -5
  89. data/lib/rbx/match_data.fy +10 -0
  90. data/lib/rbx/method.fy +4 -4
  91. data/lib/rbx/no_method_error.fy +1 -1
  92. data/lib/rbx/object.fy +8 -15
  93. data/lib/rbx/regexp.fy +4 -0
  94. data/lib/rbx/string.fy +39 -1
  95. data/lib/rbx/symbol.fy +1 -1
  96. data/lib/rbx/system.fy +0 -6
  97. data/lib/rbx/thread.fy +5 -0
  98. data/lib/rbx/time.fy +14 -0
  99. data/lib/rbx/tuple.fy +1 -0
  100. data/lib/set.fy +1 -1
  101. data/lib/stack.fy +1 -1
  102. data/lib/string.fy +2 -2
  103. data/lib/thread_pool.fy +101 -0
  104. data/lib/tuple.fy +37 -6
  105. data/ruby_lib/fancy.rb +46 -0
  106. data/tests/block.fy +39 -0
  107. data/tests/class.fy +40 -1
  108. data/tests/file.fy +2 -2
  109. data/tests/hash.fy +7 -7
  110. data/tests/nil_class.fy +2 -2
  111. data/tests/object.fy +10 -2
  112. data/tests/pattern_matching.fy +18 -7
  113. metadata +34 -7
data/lib/rbx/symbol.fy CHANGED
@@ -3,7 +3,7 @@ class Symbol {
3
3
  binding = Binding setup(Rubinius VariableScope of_sender(),
4
4
  Rubinius CompiledMethod of_sender(),
5
5
  Rubinius StaticScope of_sender())
6
- Fancy eval: (self to_s) binding: binding
6
+ Fancy eval: to_s binding: binding
7
7
  }
8
8
 
9
9
  def inspect {
data/lib/rbx/system.fy CHANGED
@@ -28,10 +28,4 @@ class System {
28
28
 
29
29
  IO popen(command_str)
30
30
  }
31
-
32
- def System sleep: n_ms {
33
- "Sets the Fancy process for a given amount of milliseconds to sleep."
34
-
35
- Kernel sleep(n_ms / 1000)
36
- }
37
31
  }
data/lib/rbx/thread.fy CHANGED
@@ -72,4 +72,9 @@ class Thread {
72
72
  start(&block)
73
73
  }
74
74
 
75
+ def Thread sleep: seconds {
76
+ "Sets the Fancy process for a given amount of seconds to sleep."
77
+
78
+ Kernel sleep(seconds)
79
+ }
75
80
  }
data/lib/rbx/time.fy ADDED
@@ -0,0 +1,14 @@
1
+ class Time {
2
+ metaclass ruby_alias: 'now
3
+ ruby_alias: '==
4
+ ruby_alias: '-
5
+ ruby_alias: '+
6
+ ruby_alias: '>
7
+ ruby_alias: '<
8
+ ruby_alias: '>=
9
+ ruby_alias: '<=
10
+
11
+ def != other {
12
+ self == other not
13
+ }
14
+ }
data/lib/rbx/tuple.fy CHANGED
@@ -1,6 +1,7 @@
1
1
  Tuple = Rubinius Tuple
2
2
  class Tuple {
3
3
  ruby_alias: 'size
4
+ ruby_alias: 'to_a
4
5
 
5
6
  def initialize: size {
6
7
  """
data/lib/set.fy CHANGED
@@ -56,6 +56,6 @@ class Set {
56
56
 
57
57
  def inspect {
58
58
  "Returns a detailed String representation of a Set."
59
- self to_s ++ " : Set"
59
+ to_s ++ " : Set"
60
60
  }
61
61
  }
data/lib/stack.fy CHANGED
@@ -28,7 +28,7 @@ class Stack {
28
28
  def pop {
29
29
  "Pops the top-of-stack element from the Stack and returns it."
30
30
 
31
- @arr remove_at: (self size - 1)
31
+ @arr remove_at: (size - 1)
32
32
  }
33
33
 
34
34
  def top {
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
- self empty? or: (self == " ")
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
- self split
46
+ split
47
47
  }
48
48
 
49
49
  def raise! {
@@ -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#at:@.
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
- self size times: |i| {
19
- block call: [self at: i]
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: (self size == (other size)) then: {
33
- self size times: |i| {
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
- self each: |v| {
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 nil if the key isn't defined" when: {
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
  }