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/boot.fy CHANGED
@@ -1,4 +1,4 @@
1
- # boot.fnc
1
+ # boot.fy
2
2
  # This file gets loaded & run by Fancy automatically.
3
3
  # It loads in Fancy's standard library & core classes.
4
4
 
@@ -31,6 +31,11 @@ require: "set"
31
31
  require: "symbol"
32
32
  require: "method"
33
33
  require: "stack"
34
+ require: "proxy"
35
+ require: "thread_pool"
36
+ require: "fiber"
37
+ require: "fiber_pool"
38
+ require: "future"
34
39
 
35
40
  # version holds fancy's version number
36
41
  require: "version"
@@ -38,4 +43,4 @@ require: "argv"
38
43
 
39
44
  require: "documentation"
40
45
 
41
- require: "package.fy"
46
+ require: "package.fy"
data/lib/class.fy CHANGED
@@ -50,6 +50,16 @@ class Class {
50
50
  }
51
51
  }
52
52
 
53
+ def read_slot: slotname {
54
+ """
55
+ @slotname Name of slot to define a getter method for.
56
+
57
+ Defines a slot reader method for a given slotname.
58
+ """
59
+
60
+ define_slot_reader: slotname
61
+ }
62
+
53
63
  def write_slots: slots {
54
64
  """
55
65
  @slots @Array@ of slotnames to define setter methods for.
@@ -62,6 +72,16 @@ class Class {
62
72
  }
63
73
  }
64
74
 
75
+ def write_slot: slotname {
76
+ """
77
+ @slotname Name of slot to define a setter method for.
78
+
79
+ Defines a slot writer method for a given slotname.
80
+ """
81
+
82
+ define_slot_writer: slotname
83
+ }
84
+
65
85
  def read_write_slots: slots {
66
86
  """
67
87
  @slots @Array@ of slotnames to define getter & setter methods for.
@@ -75,6 +95,17 @@ class Class {
75
95
  }
76
96
  }
77
97
 
98
+ def read_write_slot: slotname {
99
+ """
100
+ @slotname Name of slot to define getter & setter methods for.
101
+
102
+ Defines slot reader & writer methods for a given slotname.
103
+ """
104
+
105
+ define_slot_reader: slotname
106
+ define_slot_writer: slotname
107
+ }
108
+
78
109
  def subclass?: class_obj {
79
110
  """
80
111
  @class_obj Class object to check for, if @self is a subclass of @class_obj.
@@ -87,8 +118,8 @@ class Class {
87
118
  true
88
119
  } else: {
89
120
  # take care of Object class, as Object is its own superclass
90
- unless: (self superclass nil?) do: {
91
- self superclass subclass?: class_obj
121
+ unless: (superclass nil?) do: {
122
+ superclass subclass?: class_obj
92
123
  }
93
124
  }
94
125
  }
data/lib/compiler/ast.fy CHANGED
@@ -25,6 +25,8 @@ require: "ast/script"
25
25
  require: "ast/expression_list"
26
26
  require: "ast/identifier"
27
27
  require: "ast/message_send"
28
+ require: "ast/future_send"
29
+ require: "ast/async_send"
28
30
  require: "ast/method_def"
29
31
  require: "ast/singleton_method_def"
30
32
  require: "ast/super"
@@ -54,7 +54,7 @@ class Fancy AST {
54
54
  @idents each_with_index: |ident idx| {
55
55
  var = ident
56
56
  value = MultipleAssignmentExpr new: @line index: idx
57
- match ident string -> {
57
+ match ident string {
58
58
  case /^\*/ ->
59
59
  value = SplatAssignmentExpr new: @line start_index: idx
60
60
  var = Identifier from: (ident string rest) line: (ident line)
@@ -68,28 +68,28 @@ class Fancy AST {
68
68
  class Identifier {
69
69
  def bytecode: g assign: value {
70
70
  pos(g)
71
- Rubinius AST LocalVariableAssignment new(@line, self name, value) bytecode(g)
71
+ Rubinius AST LocalVariableAssignment new(@line, name, value) bytecode(g)
72
72
  }
73
73
  }
74
74
 
75
75
  class InstanceVariable {
76
76
  def bytecode: g assign: value {
77
77
  pos(g)
78
- Rubinius AST InstanceVariableAssignment new(@line, self name, value) bytecode(g)
78
+ Rubinius AST InstanceVariableAssignment new(@line, name, value) bytecode(g)
79
79
  }
80
80
  }
81
81
 
82
82
  class ClassVariable {
83
83
  def bytecode: g assign: value {
84
84
  pos(g)
85
- Rubinius AST ClassVariableAssignment new(@line, self name, value) bytecode(g)
85
+ Rubinius AST ClassVariableAssignment new(@line, name, value) bytecode(g)
86
86
  }
87
87
  }
88
88
 
89
89
  class Constant {
90
90
  def bytecode: g assign: value {
91
91
  pos(g)
92
- Rubinius AST ConstantAssignment new(@line, self name, value) bytecode(g)
92
+ Rubinius AST ConstantAssignment new(@line, name, value) bytecode(g)
93
93
  }
94
94
  }
95
95
 
@@ -0,0 +1,25 @@
1
+ class Fancy AST {
2
+ class AsyncSend : Node {
3
+ def initialize: @line message_send: @message_send {
4
+ }
5
+
6
+ def bytecode: g {
7
+ pos(g)
8
+
9
+ body = ExpressionList new: @line list: [@message_send]
10
+ block = BlockLiteral new: @line args: (BlockArgs new: @line) body: body
11
+
12
+ fiber = MessageSend new: @line \
13
+ message: (Identifier from: "new:" line: @line) \
14
+ to: (Identifier from: "Fiber" line: @line) \
15
+ args: (MessageArgs new: @line args: [block])
16
+
17
+ schedule_send = MessageSend new: @line \
18
+ message: (Identifier from: "add:" line: @line) \
19
+ to: (Identifier from: "Scheduler" line: @line) \
20
+ args: (MessageArgs new: @line args: [fiber])
21
+
22
+ schedule_send bytecode: g
23
+ }
24
+ }
25
+ }
@@ -24,12 +24,12 @@ class Fancy AST {
24
24
  # an identifier, use that as the message name in a send to
25
25
  # self and use the result as the receiver value for the rest.
26
26
  new_receiver = Identifier from: (@args args first to_s) line: @line
27
- match first_expr -> {
27
+ match first_expr {
28
28
  case Identifier ->
29
29
  @body expressions shift()
30
30
  @body unshift_expression: $ MessageSend new: @line message: first_expr to: (new_receiver) args: (MessageArgs new: @line args: [])
31
31
  case MessageSend ->
32
- match first_expr receiver -> {
32
+ match first_expr receiver {
33
33
  case Self ->
34
34
  first_expr receiver: new_receiver
35
35
  case Identifier ->
@@ -72,7 +72,7 @@ class Fancy AST {
72
72
  }
73
73
 
74
74
  def required_args {
75
- self total_args
75
+ total_args
76
76
  }
77
77
 
78
78
  def create_locals: block {
@@ -0,0 +1,20 @@
1
+ class Fancy AST {
2
+ class FutureSend : Node {
3
+ def initialize: @line message_send: @message_send {
4
+ }
5
+
6
+ def bytecode: g {
7
+ pos(g)
8
+
9
+ body = ExpressionList new: @line list: [@message_send]
10
+ block = BlockLiteral new: @line args: (BlockArgs new: @line) body: body
11
+
12
+ future_send = MessageSend new: @line \
13
+ message: (Identifier from: "new:" line: @line) \
14
+ to: (Identifier from: "Future" line: @line) \
15
+ args: (MessageArgs new: @line args: [block])
16
+
17
+ future_send bytecode: g
18
+ }
19
+ }
20
+ }
@@ -37,7 +37,7 @@ class Fancy AST {
37
37
  }
38
38
 
39
39
  def self from: string line: line filename: filename (nil) {
40
- type = match string -> {
40
+ type = match string {
41
41
  case "__FILE__" -> return CurrentFile new: line filename: filename
42
42
  case "__LINE__" -> return CurrentLine new: line
43
43
  case "self" -> return Self new: line
@@ -52,11 +52,17 @@ class Fancy AST {
52
52
 
53
53
  def bytecode: g {
54
54
  pos(g)
55
- match @string -> {
55
+ match @string {
56
56
  case "true" -> g push_true()
57
57
  case "false" -> g push_false()
58
58
  case "nil" -> g push_nil()
59
- case _ -> Rubinius AST LocalVariableAccess new(@line, self name) bytecode(g)
59
+ case _ ->
60
+ if: (g state() scope() search_local(name)) then: {
61
+ Rubinius AST LocalVariableAccess new(@line, name) bytecode(g)
62
+ } else: {
63
+ ms = MessageSend new: @line message: self to: (Self new: @line) args: (MessageArgs new: @line args: [])
64
+ ms bytecode: g
65
+ }
60
66
  }
61
67
  }
62
68
  }
@@ -65,7 +71,7 @@ class Fancy AST {
65
71
  def initialize: @line string: @string {}
66
72
  def bytecode: g {
67
73
  pos(g)
68
- Rubinius AST InstanceVariableAccess new(@line, self name) bytecode(g)
74
+ Rubinius AST InstanceVariableAccess new(@line, name) bytecode(g)
69
75
  }
70
76
  }
71
77
 
@@ -73,7 +79,7 @@ class Fancy AST {
73
79
  def initialize: @line string: @string {}
74
80
  def bytecode: g {
75
81
  pos(g)
76
- Rubinius AST ClassVariableAccess new(@line, self name) bytecode(g)
82
+ Rubinius AST ClassVariableAccess new(@line, name) bytecode(g)
77
83
  }
78
84
  }
79
85
 
@@ -81,7 +87,7 @@ class Fancy AST {
81
87
  def initialize: @line string: @string {}
82
88
  def bytecode: g {
83
89
  pos(g)
84
- Rubinius AST ConstantAccess new(@line, self name) bytecode(g)
90
+ Rubinius AST ConstantAccess new(@line, name) bytecode(g)
85
91
  }
86
92
  }
87
93
 
@@ -106,7 +112,7 @@ class Fancy AST {
106
112
 
107
113
  def bytecode: g {
108
114
  pos(g)
109
- self scoped bytecode(g)
115
+ scoped bytecode(g)
110
116
  }
111
117
  }
112
118
 
@@ -5,6 +5,37 @@ class Fancy AST {
5
5
  }
6
6
 
7
7
 
8
+ def bytecode: g set_match_args: clause {
9
+ "Generates bytecode for setting match clause arguments, if needed"
10
+
11
+ g dup()
12
+ skip_create_locals_label = g new_label()
13
+ g gif(skip_create_locals_label)
14
+
15
+ # if match_arg is given, get a localvar slot and set the
16
+ # result of the === call to it
17
+ if: (clause match_args first) then: |marg| {
18
+ match_arg_var = g state() scope() new_local(marg)
19
+ @match_arg_vars << match_arg_var
20
+ g set_local(match_arg_var slot())
21
+ }
22
+
23
+ # for any remaining match arguments, set their values to
24
+ # whatever matcher[idx] returns (should be the matched data)
25
+ clause match_args rest each_with_index: |match_arg idx| {
26
+ idx = idx + 1 # we only want from index 1 onwards
27
+ g dup() # dup the matcher object
28
+ match_arg_var = g state() scope() new_local(match_arg)
29
+ @match_arg_vars << match_arg_var
30
+ FixnumLiteral new: @line value: idx . bytecode: g
31
+ g send('at:, 1)
32
+ g set_local(match_arg_var slot())
33
+ g pop()
34
+ }
35
+
36
+ skip_create_locals_label set!()
37
+ }
38
+
8
39
  def bytecode: g {
9
40
  pos(g)
10
41
 
@@ -25,28 +56,7 @@ class Fancy AST {
25
56
  c expr bytecode: g
26
57
  g swap()
27
58
  g send(':===, 1)
28
-
29
- # if match_arg is given, get a localvar slot and set the
30
- # result of the === call to it
31
- if: (c match_args first) then: |marg| {
32
- match_arg_var = g state() scope() new_local(marg)
33
- @match_arg_vars << match_arg_var
34
- g set_local(match_arg_var slot())
35
- }
36
-
37
- # for any remaining match arguments, set their values to
38
- # whatever matcher[idx] returns (should be the matched data)
39
- c match_args rest each_with_index: |match_arg idx| {
40
- idx = idx + 1 # we only want from index 1 onwards
41
- g dup() # dup the matcher object
42
- match_arg_var = g state() scope() new_local(match_arg)
43
- @match_arg_vars << match_arg_var
44
- FixnumLiteral new: @line value: idx . bytecode: g
45
- g send('at:, 1)
46
- g set_local(match_arg_var slot())
47
- g pop()
48
- }
49
-
59
+ bytecode: g set_match_args: c
50
60
  g git(clause_labels[i])
51
61
  }
52
62
  g pop()
@@ -13,13 +13,13 @@ class Fancy AST {
13
13
  @args bytecode: g
14
14
  pos(g)
15
15
  { g allow_private() } if: (@receiver is_a?: Self)
16
- sym = @name method_name: @receiver ruby_send: (self ruby_send?)
17
- if: (self has_splat?) then: {
18
- { g push_nil() } unless: $ self ruby_block?
16
+ sym = @name method_name: @receiver ruby_send: ruby_send?
17
+ if: has_splat? then: {
18
+ { g push_nil() } unless: ruby_block?
19
19
  g send_with_splat(sym, @args size, false)
20
20
  return nil
21
21
  }
22
- if: (self ruby_block?) then: {
22
+ if: ruby_block? then: {
23
23
  g send_with_block(sym, @args size, false)
24
24
  } else: {
25
25
  g send(sym, @args size, false)
@@ -3,7 +3,7 @@ class Fancy AST {
3
3
  class MethodDef : Rubinius AST Define {
4
4
  def initialize: @line name: @name args: @arguments body: @body access: @access {
5
5
  @name = @name method_name: nil
6
- self generate_ivar_assignment
6
+ generate_ivar_assignment
7
7
 
8
8
  if: (@body empty?) then: {
9
9
  @body unshift_expression: $ NilLiteral new: @line
@@ -23,7 +23,7 @@ class Fancy AST {
23
23
  def bytecode: g {
24
24
  pos(g)
25
25
  try {
26
- self push_script
26
+ push_script
27
27
 
28
28
  # docs, code = body.expressions.partition do |s|
29
29
  # s.kind_of?(Rubinius::AST::StringLiteral)
@@ -44,7 +44,7 @@ class Fancy AST {
44
44
  # documentation.
45
45
  # TODO: implement file documentation here.
46
46
  } finally {
47
- self pop_script
47
+ pop_script
48
48
  }
49
49
  }
50
50
  }
@@ -10,6 +10,7 @@ class Fancy AST {
10
10
  pos(g)
11
11
  @args bytecode: g
12
12
  name = @name method_name: nil
13
+ g push_block()
13
14
  g send_super(name, @args size)
14
15
  }
15
16
  }
@@ -63,6 +63,8 @@ class Fancy {
63
63
  }
64
64
  try {
65
65
  compiler run()
66
+ } catch Fancy Parser ParseError => e {
67
+ e raise!
66
68
  } catch Exception => e {
67
69
  compiler_error("Error trying to compile " ++ file, e)
68
70
  }
data/lib/documentation.fy CHANGED
@@ -62,23 +62,23 @@ class Fancy Documentation {
62
62
  Append docstring to the documentation for obj.
63
63
  If obj has no documentation, one is created for it.
64
64
  """
65
- doc = self for: obj
65
+ doc = for: obj
66
66
  doc if_do: {
67
67
  doc docs << docstring
68
68
  } else: {
69
- doc = self for: obj is: docstring
69
+ doc = for: obj is: docstring
70
70
  }
71
71
  doc
72
72
  }
73
73
 
74
74
  def self formatter: name {
75
75
  "Obtain a formatter by the given name. Returns a callable object"
76
- self formatters at: name
76
+ formatters at: name
77
77
  }
78
78
 
79
79
  def self formatter: name is: callable {
80
80
  "Register a callable object as formatter under name."
81
- self formatters at: name put: callable
81
+ formatters at: name put: callable
82
82
  }
83
83
 
84
84
  def self formatters {
data/lib/enumerable.fy CHANGED
@@ -154,7 +154,7 @@ class FancyEnumerable {
154
154
 
155
155
  def take: amount {
156
156
  i = 0
157
- self take_while: {
157
+ take_while: {
158
158
  i = i + 1
159
159
  i <= amount
160
160
  }
@@ -162,7 +162,7 @@ class FancyEnumerable {
162
162
 
163
163
  def drop: amount {
164
164
  i = 0
165
- self drop_while: {
165
+ drop_while: {
166
166
  i = i + 1
167
167
  i <= amount
168
168
  }
@@ -178,6 +178,13 @@ class FancyEnumerable {
178
178
  acc
179
179
  }
180
180
 
181
+ def inject: val into: block {
182
+ """
183
+ Same as reduce:init_val: but taking the initial value as first and the reducing block as second parameter.
184
+ """
185
+ reduce: block init_val: val
186
+ }
187
+
181
188
  def uniq {
182
189
  "Returns a new Array with all unique values (double entries are skipped)."
183
190
 
@@ -202,11 +209,11 @@ class FancyEnumerable {
202
209
 
203
210
  def empty? {
204
211
  "Indicates, if the Enumerable is empty (has no elements)."
205
- self size == 0
212
+ size == 0
206
213
  }
207
214
 
208
215
  def first {
209
- self each: |x| {
216
+ each: |x| {
210
217
  return x
211
218
  }
212
219
  }
@@ -230,7 +237,7 @@ class FancyEnumerable {
230
237
  def superior_by: comparison_block {
231
238
  "Returns the superiour element in the @Enumerable that has met the given comparison block with all other elements."
232
239
 
233
- retval = self first
240
+ retval = first
234
241
  each: |x| {
235
242
  if: (comparison_block call: [x, retval]) then: {
236
243
  retval = x
@@ -250,10 +257,10 @@ class FancyEnumerable {
250
257
  }
251
258
 
252
259
  def partition_by: block {
253
- last = block call: [self first]
260
+ last = block call: [first]
254
261
  coll = []
255
262
  tmp_coll = []
256
- self each: |x| {
263
+ each: |x| {
257
264
  tmp = block call: [x]
258
265
  if: (tmp != last) then: {
259
266
  coll << tmp_coll