fancy 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }