fancy 0.5.0 → 0.6.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 (111) hide show
  1. data/AUTHORS +2 -0
  2. data/README.md +6 -1
  3. data/bin/fancy +6 -0
  4. data/bin/ifancy +44 -3
  5. data/boot/fancy_ext/module.rb +4 -0
  6. data/boot/fancy_ext/object.rb +4 -0
  7. data/boot/rbx-compiler/compiler/ast/block.rb +29 -1
  8. data/boot/rbx-compiler/compiler/ast/identifier.rb +6 -0
  9. data/boot/rbx-compiler/compiler/ast/message_send.rb +1 -0
  10. data/boot/rbx-compiler/parser/fancy_parser.bundle +0 -0
  11. data/boot/rbx-compiler/parser/lexer.lex +2 -0
  12. data/boot/rbx-compiler/parser/parser.rb +6 -0
  13. data/boot/rbx-compiler/parser/parser.y +14 -1
  14. data/doc/api/fancy.jsonp +1 -1
  15. data/doc/features.md +24 -0
  16. data/examples/99bottles.fy +5 -0
  17. data/examples/conditions_exceptions.fy +9 -0
  18. data/examples/conditions_parsing.fy +68 -0
  19. data/examples/greeter.fy +9 -0
  20. data/examples/html_generator.fy +59 -29
  21. data/examples/webserver/webserver.fy +8 -11
  22. data/lib/argv.fy +6 -0
  23. data/lib/array.fy +17 -35
  24. data/lib/block.fy +82 -1
  25. data/lib/boot.fy +4 -2
  26. data/lib/compiler.fy +2 -2
  27. data/lib/compiler/ast/block.fy +24 -20
  28. data/lib/compiler/ast/message_send.fy +11 -0
  29. data/lib/contracts.fy +60 -0
  30. data/lib/dynamic_slot_object.fy +61 -0
  31. data/lib/enumerable.fy +432 -394
  32. data/lib/enumerator.fy +152 -150
  33. data/lib/fdoc.fy +4 -17
  34. data/lib/fiber.fy +4 -10
  35. data/lib/file.fy +33 -25
  36. data/lib/future.fy +59 -5
  37. data/lib/hash.fy +54 -1
  38. data/lib/html.fy +107 -0
  39. data/lib/kvo.fy +173 -0
  40. data/lib/main.fy +6 -2
  41. data/lib/message_sink.fy +19 -0
  42. data/lib/number.fy +48 -0
  43. data/lib/object.fy +65 -13
  44. data/lib/package.fy +12 -2
  45. data/lib/package/dependency.fy +13 -0
  46. data/lib/package/dependency_installer.fy +27 -0
  47. data/lib/package/installer.fy +4 -10
  48. data/lib/package/uninstaller.fy +1 -3
  49. data/lib/parser/ext/lexer.lex +8 -3
  50. data/lib/parser/ext/parser.y +4 -1
  51. data/lib/parser/methods.fy +7 -3
  52. data/lib/range.fy +1 -1
  53. data/lib/rbx.fy +2 -1
  54. data/lib/rbx/array.fy +28 -12
  55. data/lib/rbx/bignum.fy +1 -1
  56. data/lib/rbx/block.fy +27 -0
  57. data/lib/rbx/console.fy +6 -6
  58. data/lib/rbx/date.fy +6 -1
  59. data/lib/rbx/documentation.fy +8 -3
  60. data/lib/rbx/exception.fy +5 -0
  61. data/lib/rbx/file.fy +40 -7
  62. data/lib/rbx/fixnum.fy +12 -1
  63. data/lib/rbx/method.fy +9 -2
  64. data/lib/rbx/module.fy +24 -0
  65. data/lib/rbx/regexp.fy +8 -0
  66. data/lib/rbx/string.fy +23 -7
  67. data/lib/rbx/tcp_server.fy +4 -2
  68. data/lib/rbx/tcp_socket.fy +14 -0
  69. data/lib/remote_object.fy +59 -0
  70. data/lib/set.fy +15 -4
  71. data/lib/string.fy +38 -5
  72. data/lib/stringio.fy +1 -0
  73. data/lib/symbol.fy +4 -0
  74. data/lib/system.fy +22 -0
  75. data/lib/thread_pool.fy +2 -2
  76. data/lib/tuple.fy +18 -1
  77. data/lib/vars.fy +17 -0
  78. data/lib/version.fy +1 -1
  79. data/ruby_lib/fancy +6 -0
  80. data/tests/array.fy +30 -0
  81. data/tests/block.fy +106 -0
  82. data/tests/class.fy +19 -0
  83. data/tests/enumerable.fy +1 -1
  84. data/tests/enumerator.fy +5 -5
  85. data/tests/file.fy +28 -0
  86. data/tests/fixnum.fy +0 -50
  87. data/tests/future.fy +9 -24
  88. data/tests/hash.fy +35 -0
  89. data/tests/html.fy +33 -0
  90. data/tests/kvo.fy +101 -0
  91. data/tests/number.fy +75 -0
  92. data/tests/object.fy +50 -3
  93. data/tests/string.fy +19 -10
  94. data/tests/symbol.fy +5 -0
  95. data/tests/tuple.fy +7 -0
  96. data/tools/fancy-mode.el +5 -1
  97. metadata +22 -21
  98. data/boot/compiler/parser/ext/fancy_parser.bundle +0 -0
  99. data/boot/rbx-compiler/parser/Makefile +0 -156
  100. data/boot/rbx-compiler/parser/lexer.c +0 -2310
  101. data/boot/rbx-compiler/parser/lexer.h +0 -315
  102. data/boot/rbx-compiler/parser/parser.c +0 -2946
  103. data/boot/rbx-compiler/parser/parser.h +0 -151
  104. data/lib/fiber_pool.fy +0 -78
  105. data/lib/method.fy +0 -6
  106. data/lib/parser/ext/Makefile +0 -156
  107. data/lib/parser/ext/fancy_parser.bundle +0 -0
  108. data/lib/parser/ext/lexer.c +0 -2392
  109. data/lib/parser/ext/lexer.h +0 -315
  110. data/lib/parser/ext/parser.c +0 -3251
  111. data/lib/parser/ext/parser.h +0 -161
@@ -5,6 +5,7 @@ class FutureSend {
5
5
  @condvar = ConditionVariable new
6
6
  @completed = false
7
7
  @failed = false
8
+ @continuations = []
8
9
  @actor ! ('future, (@message, @params), self)
9
10
  }
10
11
 
@@ -26,11 +27,19 @@ class FutureSend {
26
27
 
27
28
  def completed! {
28
29
  @condvar broadcast
30
+ unless: @failed do: {
31
+ @continuations each: @{ call: [@value] }
32
+ }
33
+ @continuations = []
29
34
  }
30
35
 
31
36
  private: 'completed!
32
37
 
33
38
  def completed? {
39
+ """
40
+ @return @true if FutureSend completed (success or failure), @false otherwise.
41
+ """
42
+
34
43
  completed = false
35
44
  @completed_mutex synchronize: {
36
45
  completed = @completed
@@ -38,7 +47,25 @@ class FutureSend {
38
47
  return completed true?
39
48
  }
40
49
 
50
+ def succeeded? {
51
+ """
52
+ @return @true if FutureSend completed without failure, @false otherwise.
53
+ """
54
+
55
+ completed = false
56
+ failed = false
57
+ @completed_mutex synchronize: {
58
+ completed = @completed
59
+ failed = @failed
60
+ }
61
+ return completed true? && (failed false?)
62
+ }
63
+
41
64
  def failed? {
65
+ """
66
+ @return @true if FutureSend failed, @false otherwise.
67
+ """
68
+
42
69
  failed = false
43
70
  @completed_mutex synchronize: {
44
71
  failed = @failed
@@ -47,6 +74,13 @@ class FutureSend {
47
74
  }
48
75
 
49
76
  def failure {
77
+ """
78
+ @return @Exception@ that caused the FutureSend to fail, or @nil, if no failure.
79
+
80
+ Returns the @Exception@ that caused @self to fail, or @nil, if it didn't fail.
81
+ Will block the calling @Thread@ if @self hasn't completed or failed yet.
82
+ """
83
+
50
84
  @completed_mutex synchronize: {
51
85
  if: @failed then: {
52
86
  return @fail_reason
@@ -58,6 +92,13 @@ class FutureSend {
58
92
  }
59
93
 
60
94
  def value {
95
+ """
96
+ @return Return value of performing @self.
97
+
98
+ Returns the value returned by performing @self.
99
+ Will block the calling @Thread@ if @self hasn't completed or failed yet.
100
+ """
101
+
61
102
  @completed_mutex synchronize: {
62
103
  if: @completed then: {
63
104
  return @value
@@ -73,9 +114,24 @@ class FutureSend {
73
114
  }
74
115
 
75
116
  def when_done: block {
76
- block send_future: 'call: with_params: [value]
117
+ """
118
+ @block @Block@ to be registered as a continuation when @self succeeds.
119
+
120
+ Registers @block as a continuation to be called with @self's value on success.
121
+ """
122
+
123
+ { return nil } if: failed?
124
+ @completed_mutex synchronize: {
125
+ if: @completed then: {
126
+ block call: [@value]
127
+ } else: {
128
+ @continuations << block
129
+ }
130
+ }
77
131
  }
78
132
 
133
+ alias_method: 'with_value: for: 'when_done:
134
+
79
135
  def && block {
80
136
  when_done: block
81
137
  }
@@ -132,15 +188,13 @@ class PooledFuture {
132
188
  }
133
189
 
134
190
  class FutureCollection {
135
- include: FancyEnumerable
191
+ include: Fancy Enumerable
136
192
 
137
193
  def initialize: @futures {
138
194
  }
139
195
 
140
196
  def each: block {
141
- @futures each: |f| {
142
- f when_done: block
143
- }
197
+ @futures each: @{ when_done: block }
144
198
  }
145
199
 
146
200
  def await_all {
@@ -4,7 +4,7 @@ class Hash {
4
4
  Maps a key to a value.
5
5
  """
6
6
 
7
- include: FancyEnumerable
7
+ include: Fancy Enumerable
8
8
 
9
9
  def [key] {
10
10
  """
@@ -17,6 +17,21 @@ class Hash {
17
17
  at: key
18
18
  }
19
19
 
20
+ def at: key else: else_block {
21
+ """
22
+ @key Key of the value to get.
23
+ @else_block @Block@ to be called if @key is not found.
24
+ @return Value for @key or value of calling @else_block, if @key is not found.
25
+
26
+ Returns the value for a given key.
27
+ If the key is not found, calls @else_block and returns the value it yields.
28
+ """
29
+
30
+ if: (includes?: key) then: {
31
+ at: key
32
+ } else: else_block
33
+ }
34
+
20
35
  def each: block {
21
36
  """
22
37
  @block @Block@ to be called with each key and value in @self.
@@ -80,6 +95,26 @@ class Hash {
80
95
  to_a to_s
81
96
  }
82
97
 
98
+ def to_object {
99
+ """
100
+ @return New @Object@ with slots defined by keys and values in @self.
101
+
102
+ Creates and returns a new @Object@ with slot names and values based on keys and values in @self.
103
+
104
+ Example:
105
+ o = <['name => \"Christopher Bertels\", 'interest => \"programming languages\"]> to_object
106
+ o name # => \"Christopher Bertels\"
107
+ o interest # => 42
108
+ """
109
+
110
+ o = Object new
111
+ self each: |k v| {
112
+ o set_slot: k value: v
113
+ }
114
+ o metaclass read_write_slots: keys
115
+ o
116
+ }
117
+
83
118
  def inspect {
84
119
  str = "<["
85
120
  each: |key val| {
@@ -104,4 +139,22 @@ class Hash {
104
139
 
105
140
  keys map: |k| { at: k }
106
141
  }
142
+
143
+ def fetch: key else: else_block {
144
+ """
145
+ @key Key of value to get.
146
+ @else_block @Block@ that gets called if @key not in @self.
147
+
148
+ Example:
149
+ <['foo => 'bar]> fetch: 'foo else: { 42 } # => 'bar
150
+ <['foo => 'bar]> fetch: 'unknown else: { 42 } # => 42
151
+ <['nil => nil]> fetch: 'nil else: { 'not_found } # => nil
152
+ """
153
+
154
+ if: (includes?: key) then: {
155
+ at: key
156
+ } else: {
157
+ else_block call: [key]
158
+ }
159
+ }
107
160
  }
@@ -0,0 +1,107 @@
1
+ class HTML {
2
+ """
3
+ HTML generator class.
4
+
5
+ Example:
6
+ require: \"html\"
7
+ html = HTML new: @{
8
+ html: @{
9
+ head: @{ title: \"My Fancy Website\" }
10
+ body: @{
11
+ div: { id: \”main\” } with: \"Hello, Fancy World!\"
12
+ }
13
+ }
14
+ } . to_s
15
+
16
+ # html is now:
17
+ \"\"\"
18
+ <html>
19
+ <head>
20
+ <title>
21
+ My Fancy Website
22
+ </title>
23
+ </head>
24
+ <body>
25
+ <div id=\"main\">
26
+ Hello, Fancy World!
27
+ </div>
28
+ </body>
29
+ </html>
30
+ \"\"\"
31
+
32
+ """
33
+
34
+ def initialize {
35
+ @buf = ""
36
+ @indent = 0
37
+ }
38
+
39
+ def initialize: block {
40
+ initialize
41
+ block call: [self]
42
+ }
43
+
44
+ def open_tag: name attrs: attrs (<[]>) indent: indent (true) {
45
+ @buf << "\n"
46
+ @buf << (" " * @indent)
47
+ @indent = @indent + 2
48
+
49
+ @buf << "<" << name
50
+ unless: (attrs empty?) do: {
51
+ @buf << " "
52
+ attrs each: |k v| {
53
+ @buf << k << "=" << (v to_s inspect)
54
+ } in_between: {
55
+ @buf << " "
56
+ }
57
+ }
58
+ @buf << ">"
59
+
60
+ { @indent = @indent - 2 } unless: indent
61
+ }
62
+
63
+ def close_tag: name {
64
+ @buf << "\n"
65
+ @indent = @indent - 2
66
+ @buf << (" " * @indent)
67
+
68
+ @buf << "</" << name << ">"
69
+ }
70
+
71
+ def html_block: tag body: body attrs: attrs (<[]>) {
72
+ tag = tag from: 0 to: -2
73
+ open_tag: tag attrs: attrs
74
+ match body first {
75
+ case Block -> @buf << (body first call: [self])
76
+ case _ -> @buf << "\n" << (" " * @indent) << (body first)
77
+ }
78
+ close_tag: tag
79
+ nil
80
+ }
81
+
82
+ def unknown_message: m with_params: p {
83
+ match m to_s {
84
+ case /with:$/ ->
85
+ tag = m to_s substitute: /with:$/ with: ""
86
+ html_block: tag body: (p rest) attrs: (p first to_hash)
87
+ case _ ->
88
+ html_block: (m to_s) body: p
89
+ }
90
+ nil
91
+ }
92
+
93
+ def br {
94
+ @buf << "\n" << (" " * @indent)
95
+ @buf << "<br/>"
96
+ nil
97
+ }
98
+
99
+ def input: attrs {
100
+ open_tag: "input" attrs: (attrs to_hash) indent: false
101
+ nil
102
+ }
103
+
104
+ def to_s {
105
+ @buf from: 1 to: -1 . to_s
106
+ }
107
+ }
@@ -0,0 +1,173 @@
1
+ class KVO {
2
+ """
3
+ Key-Value Observing Mixin class.
4
+ Include this Class into any class to add support for Key-Value Observing.
5
+ Inspired by Objective-C's KVO, but using @Block@s, as it fits nicer
6
+ with Fancy's semantics.
7
+
8
+ Example:
9
+ class Person {
10
+ include: KVO
11
+ read_write_slots: ('name, 'age, 'city)
12
+ }
13
+
14
+ tom = Person new tap: @{
15
+ name: \"Tom Cruise\"
16
+ age: 55
17
+ city: \"Hollywood\"
18
+ }
19
+
20
+ tom observe: 'name with: |new old| {
21
+ new println
22
+ }
23
+ tom name: \"Tommy Cruise\" # will cause \"Tommy Cruise\" to be printed
24
+ tom age: 56 # No observer Blocks defined, so nothing will happen
25
+ """
26
+
27
+ class ClassMethods {
28
+ def define_slot_writer: slotname {
29
+ slotname = slotname to_sym
30
+ define_method: "#{slotname}:" with: |new_val| {
31
+ old_val = get_slot: slotname
32
+ set_slot: slotname value: new_val
33
+ match new_val {
34
+ case old_val -> nil # do nothing if no change
35
+ case _ ->
36
+ __kvo_slot_change__: slotname new: new_val old: old_val
37
+ }
38
+ }
39
+ }
40
+
41
+ def define_slot_reader: slotname {
42
+ slotname = slotname to_sym
43
+ define_method: slotname with: {
44
+ val = get_slot: slotname
45
+ if: (val is_a?: Fancy Enumerable) then: {
46
+ unless: (val get_slot: '__kvo_wrappers_defined?__) do: {
47
+ __kvo_wrap_collection_methods__: val for_slot: slotname
48
+ }
49
+ }
50
+ val
51
+ }
52
+ }
53
+ }
54
+
55
+ def KVO included: class {
56
+ class extend: ClassMethods
57
+ }
58
+
59
+ def observe: slotname with: block {
60
+ """
61
+ @slotname Name of slot to be observed with @block.
62
+ @block @Block@ to be called with old and new value of @slotname in @self.
63
+
64
+ Registers a new observer @Block@ for @slotname in @self.
65
+ """
66
+
67
+ __kvo_add_observer__: block for: slotname to: __kvo_slot_observers__
68
+ }
69
+
70
+ def observe_insertion: slotname with: block {
71
+ """
72
+ @slotname Name of collection slot to be observed with @block.
73
+ @block @Block@ to be called with value inserted in collection named @slotname in @self.
74
+
75
+ Registers a new insertion observer @Block@ for collection named @slotname in @self.
76
+ """
77
+
78
+ __kvo_add_observer__: block for: slotname to: __kvo_insertion_observers__
79
+ }
80
+
81
+ def observe_removal: slotname with: block {
82
+ """
83
+ @slotname Name of collection slot to be observed with @block.
84
+ @block @Block@ to be called with value removed from collection named @slotname in @self.
85
+
86
+ Registers a new removal observer @Block@ for collection named @slotname in @self.
87
+ """
88
+
89
+ __kvo_add_observer__: block for: slotname to: __kvo_removal_observers__
90
+ }
91
+
92
+ # PRIVATE METHODS
93
+ # OMG this looks FUGLY but this shall never be seen anyway
94
+
95
+ def __kvo_slot_observers__ {
96
+ { @__kvo_slot_observers__ = <[]> } unless: @__kvo_slot_observers__
97
+ @__kvo_slot_observers__
98
+ }
99
+
100
+ private: '__kvo_slot_observers__
101
+
102
+ def __kvo_insertion_observers__ {
103
+ { @__kvo_insertion_observers__ = <[]> } unless: @__kvo_insertion_observers__
104
+ @__kvo_insertion_observers__
105
+ }
106
+ private: '__kvo_insertion_observers__
107
+
108
+ def __kvo_removal_observers__ {
109
+ { @__kvo_removal_observers__ = <[]> } unless: @__kvo_removal_observers__
110
+ @__kvo_removal_observers__
111
+ }
112
+ private: '__kvo_removal_observers__
113
+
114
+ def __kvo_add_observer__: block for: slotname to: observer_list {
115
+ slotname = slotname to_sym
116
+ if: (observer_list[slotname]) then: |set| {
117
+ set << block
118
+ } else: {
119
+ observer_list[slotname]: $ Set new: [block]
120
+ }
121
+ }
122
+ private: '__kvo_add_observer__:for:to:
123
+
124
+ def __kvo_slot_change__: slotname new: new_val old: old_val {
125
+ if: (__kvo_slot_observers__[slotname]) then: @{
126
+ each: @{ call: [new_val, old_val] }
127
+ }
128
+ }
129
+ private: '__kvo_slot_change__:new:old:
130
+
131
+ def __kvo_insertion__: value for_slot: slotname {
132
+ if: (__kvo_insertion_observers__[slotname]) then: @{
133
+ each: @{ call: [value] }
134
+ }
135
+ }
136
+
137
+ def __kvo_removal__: value for_slot: slotname {
138
+ if: (__kvo_removal_observers__[slotname]) then: @{
139
+ each: @{ call: [value] }
140
+ }
141
+ }
142
+
143
+ def __kvo_wrap_collection_methods__: collection for_slot: slotname {
144
+ object = self
145
+ collection metaclass tap: |c| {
146
+ try {
147
+ c alias_method: '__insert__: for: '<<
148
+ c define_method: '<< with: |val| {
149
+ __insert__: val
150
+ object __kvo_insertion__: val for_slot: slotname
151
+ }
152
+ } catch {}
153
+
154
+ try {
155
+ c alias_method: '__remove__: for: 'remove:
156
+ c define_method: 'remove: with: |val| {
157
+ __remove__: val
158
+ object __kvo_removal__: val for_slot: slotname
159
+ }
160
+ } catch {}
161
+
162
+ try {
163
+ c alias_method: '__remove_at__: for: 'remove_at:
164
+ c define_method: 'remove_at: with: |index| {
165
+ obj = __remove_at__: index
166
+ object __kvo_removal__: obj for_slot: slotname
167
+ }
168
+ } catch {}
169
+ }
170
+ collection set_slot: '__kvo_wrappers_defined?__ value: true
171
+ }
172
+ private: '__kvo_wrap_collection_methods__:for_slot:
173
+ }