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
@@ -7,7 +7,7 @@
7
7
  if: (ARGV size == 1) then: {
8
8
  ARGV for_options: ["-v", "--version"] do: {
9
9
  "Fancy " ++ FANCY_VERSION println
10
- "(C) 2010, 2011 Christopher Bertels <chris@fancy-lang.org>" println
10
+ "(C) 2010, 2011, 2012 Christopher Bertels <chris@fancy-lang.org>" println
11
11
  System exit
12
12
  }
13
13
 
@@ -24,6 +24,7 @@ if: (ARGV size == 1) then: {
24
24
  "",
25
25
  "Fancy package management:",
26
26
  " install [packagename] Install a Fancy package with a given name to $FANCYPACK_DIR",
27
+ " install --deps Install dependencies specified in .fancypack file (expected in current directory).",
27
28
  " uninstall [packagename] Uninstall a Fancy package with a given name from $FANCYPACK_DIR"] println
28
29
  System exit # quit when running --help
29
30
  }
@@ -59,7 +60,10 @@ ARGV for_option: "-cv" do: {
59
60
  }
60
61
 
61
62
  ARGV for_option: "install" do: |package_name| {
62
- Fancy Package install: package_name
63
+ match package_name {
64
+ case "--deps" -> Fancy Package install_dependencies
65
+ case _ -> Fancy Package install: package_name
66
+ }
63
67
  System exit
64
68
  }
65
69
 
@@ -0,0 +1,19 @@
1
+ class Fancy {
2
+ class MessageSink : BasicObject {
3
+ """
4
+ A MessageSink just swallows all messages that are sent to it.
5
+ """
6
+
7
+ def unknown_message: m with_params: p {
8
+ """
9
+ @m Message sent to @self.
10
+ @p @Array@ of parameters sent along with @m.
11
+ @return @self.
12
+
13
+ Catches all messages and arguments and simply always returns @self.
14
+ """
15
+
16
+ self
17
+ }
18
+ }
19
+ }
@@ -39,6 +39,25 @@ class Number {
39
39
  self
40
40
  }
41
41
 
42
+ def upto: num in_steps_of: steps do: block {
43
+ """
44
+ @num Maximum @Number@ to call @block with.
45
+ @steps @Number@ of numbers to skip each step.
46
+ @block A @Block@ that should be called every @steps steps between @self and @num.
47
+ @return @self
48
+
49
+ Calls @block every @steps steps between @self and @num with the current @Number@.
50
+ Expects @num to be greater or equal to @self.
51
+ """
52
+
53
+ i = self
54
+ while: { i <= num } do: {
55
+ block call: [i]
56
+ i = i + steps
57
+ }
58
+ self
59
+ }
60
+
42
61
  def downto: num {
43
62
  """
44
63
  @num @Number@ to create an @Array@ down to.
@@ -75,6 +94,25 @@ class Number {
75
94
  self
76
95
  }
77
96
 
97
+ def downto: num in_steps_of: steps do: block {
98
+ """
99
+ @num Minimum @Number@ to call @block with.
100
+ @steps @Number@ of numbers to skip each step.
101
+ @block A @Block@ that should be called every @steps steps between @self and @num.
102
+ @return @self
103
+
104
+ Calls @block every @steps steps between @self and @num with the current @Number@.
105
+ Expects @num to be smaller or equal to @self.
106
+ """
107
+
108
+ i = self
109
+ while: { i >= num } do: {
110
+ block call: [i]
111
+ i = i - steps
112
+ }
113
+ self
114
+ }
115
+
78
116
  def squared {
79
117
  """
80
118
  @return Squared value of @self.
@@ -95,6 +133,16 @@ class Number {
95
133
  self + self
96
134
  }
97
135
 
136
+ def cubed {
137
+ """
138
+ @return Cubed value of @self.
139
+
140
+ Returns the cubed value of a Number.
141
+ """
142
+
143
+ self ** 3
144
+ }
145
+
98
146
  def abs {
99
147
  """
100
148
  @return Absolute (positive) value of @self.
@@ -29,9 +29,9 @@ class Object {
29
29
  def println {
30
30
  """
31
31
  Same as:
32
- Console println: self
32
+ *stdout* println: self
33
33
 
34
- Prints @self on @STDOUT, followed by a newline.
34
+ Prints @self on @*stdout*, followed by a newline.
35
35
  """
36
36
 
37
37
  *stdout* println: to_s
@@ -40,9 +40,9 @@ class Object {
40
40
  def print {
41
41
  """
42
42
  Same as:
43
- Console print: self
43
+ *stdout* print: self
44
44
 
45
- Prints @self on STDOUT.
45
+ Prints @self on @*stdout*.
46
46
  """
47
47
 
48
48
  *stdout* print: to_s
@@ -149,19 +149,19 @@ class Object {
149
149
 
150
150
  def to_enum {
151
151
  """
152
- @return @FancyEnumerator@ for @self using 'each: for iteration.
152
+ @return @Fancy::Enumerator@ for @self using 'each: for iteration.
153
153
  """
154
154
 
155
- FancyEnumerator new: self
155
+ Fancy Enumerator new: self
156
156
  }
157
157
 
158
158
  def to_enum: iterator {
159
159
  """
160
160
  @iterator Message to use for iteration on @self.
161
- @return @FancyEnumerator@ for @self using @iterator for iteration.
161
+ @return @Fancy::Enumerator@ for @self using @iterator for iteration.
162
162
  """
163
163
 
164
- FancyEnumerator new: self with: iterator
164
+ Fancy Enumerator new: self with: iterator
165
165
  }
166
166
 
167
167
  def and: other {
@@ -539,11 +539,28 @@ class Object {
539
539
  }
540
540
 
541
541
  def copy_slots: slots from: object {
542
+ """
543
+ @slots @Fancy::Enumerable@ of slot names to copy from @object.
544
+ @object Target @Object@ to copy slots from.
545
+
546
+ Copies slots from @object to @self.
547
+ """
548
+
542
549
  slots each: |s| {
543
550
  set_slot: s value: (object get_slot: s)
544
551
  }
545
552
  }
546
553
 
554
+ def copy_slots_from: object {
555
+ """
556
+ @object @Object@ to copy slots from.
557
+
558
+ Copies all slots from @object to @self.
559
+ """
560
+
561
+ copy_slots: (object slots) from: object
562
+ }
563
+
547
564
  def get_slots: slots {
548
565
  """
549
566
  @slots @Array@ of slot names to retrieve from @self.
@@ -590,14 +607,24 @@ class Object {
590
607
  self
591
608
  }
592
609
 
610
+ def tap: block {
611
+ """
612
+ @block @Block@ to be called with @self.
613
+ @return @self.
614
+
615
+ Calls a given @Block@ with @self before returning @self.
616
+ """
617
+
618
+ block call: [self]
619
+ self
620
+ }
621
+
593
622
  def slots {
594
623
  """
595
- @return @Set@ of slot names that @self has.
624
+ @return @Array@ of slot names that @self has.
596
625
  """
597
626
 
598
- Set new: $ instance_variables map: |s| {
599
- s rest to_sym
600
- }
627
+ instance_variables map: @{ rest to_sym }
601
628
  }
602
629
 
603
630
  def sleep: seconds {
@@ -620,7 +647,7 @@ class Object {
620
647
  Dynamically rebinds @var_name as dynamic variable with @value as the value within @block.
621
648
 
622
649
  Example:
623
- File open: \"/tmp/output.txt\" modes: ['write] with: |f| {
650
+ File write: \"/tmp/output.txt\" with: |f| {
624
651
  let: '*stdout* be: f in: {
625
652
  \"hello, world!\" println # writes it to file not STDOUT
626
653
  }
@@ -638,9 +665,34 @@ class Object {
638
665
  try {
639
666
  Thread current[var_name]: value
640
667
  retval = block call
668
+ } catch Exception => e {
669
+ e raise!
641
670
  } finally {
642
671
  Thread current[var_name]: oldval
643
672
  return retval
644
673
  }
645
674
  }
675
+
676
+ def with_output_to: filename do: block {
677
+ """
678
+ @filename Filename of file to write to.
679
+ @block @Block@ to be executed with *stdout* being bound to the output file.
680
+
681
+ Opens @filename and rebinds `*stdout*` to it within @block.
682
+
683
+ Example:
684
+ with_output_to: \"/tmp/hello_world.txt\" do: {
685
+ \"hello\" println
686
+ \"world\" println
687
+ }
688
+ This writes
689
+ hello
690
+ world
691
+ to /tmp/hello_world.txt
692
+ """
693
+
694
+ File write: filename with: |f| {
695
+ let: '*stdout* be: f in: block
696
+ }
697
+ }
646
698
  }
@@ -1,4 +1,5 @@
1
1
  require: "package/installer"
2
+ require: "package/dependency_installer"
2
3
  require: "package/uninstaller"
3
4
  require: "package/dependency"
4
5
  require: "package/specification"
@@ -29,7 +30,7 @@ class Fancy Package {
29
30
  DEFAULT_PACKAGES_PATH = DEFAULT_FANCY_ROOT ++ "/packages"
30
31
  DEFAULT_PACKAGES_LIB_PATH = DEFAULT_PACKAGES_PATH ++ "/lib"
31
32
 
32
- def self install: package_name {
33
+ def self install: package_name version: version ('latest) {
33
34
  """
34
35
  @package_name Name of package to install.
35
36
 
@@ -39,7 +40,16 @@ class Fancy Package {
39
40
  Which would get the package code from github.com/user/repo
40
41
  """
41
42
 
42
- Installer new: package_name . run
43
+ Installer new: package_name version: version . run
44
+ }
45
+
46
+ def self install_dependencies {
47
+ """
48
+ Installs dependencies found in .fancypack file in the current directory.
49
+ If no .fancypack file is found, fails and quits.
50
+ """
51
+
52
+ DependencyInstaller new run
43
53
  }
44
54
 
45
55
  def self uninstall: package_name {
@@ -20,5 +20,18 @@ class Fancy Package {
20
20
 
21
21
  def initialize: @gem_name version: @version ('latest) {
22
22
  }
23
+
24
+ def install {
25
+ """
26
+ Installs the RubyDependency (a RubyGem) via rbx -S gem on the system.
27
+ """
28
+
29
+ match @version {
30
+ case 'latest ->
31
+ System do: "rbx gem install #{@gem_name}"
32
+ case _ ->
33
+ System do: "rbx gem install -v=#{@version} #{@gem_name}"
34
+ }
35
+ }
23
36
  }
24
37
  }
@@ -0,0 +1,27 @@
1
+ class Fancy {
2
+ class Package {
3
+ class DependencyInstaller {
4
+ def run {
5
+ packfile = Dir glob("*.fancypack") first
6
+ unless: packfile do: {
7
+ *stderr* println: "No .fancypack file found. Quitting."
8
+ return nil
9
+ }
10
+
11
+ require: packfile
12
+
13
+ spec_name = packfile split: ".fancypack" . first
14
+ if: (Specification[spec_name]) then: |s| {
15
+ s dependencies each: |dep| {
16
+ "Installing dependency: #{dep name} (#{dep version})" println
17
+ Fancy Package install: (dep name) version: (dep version)
18
+ }
19
+ s rubygem_dependencies each: |dep| {
20
+ "Installing Ruby dependency: #{dep gem_name} (#{dep version})" println
21
+ dep install
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
@@ -13,10 +13,10 @@ class Fancy Package {
13
13
 
14
14
  """
15
15
 
16
- def initialize: @package_name version: @version ("latest") install_path: @install_path (ENV["FANCY_PACKAGE_DIR"]) {
16
+ def initialize: @package_name version: @version ('latest) install_path: @install_path (ENV["FANCY_PACKAGE_DIR"]) {
17
17
  """
18
18
  Creates a new @Package Installer@ for a given package name, an
19
- optional version (default is 'latest') and an optional
19
+ optional version (default is 'latest) and an optional
20
20
  installation path (default is the standard installation path for
21
21
  Fancy packages).
22
22
  """
@@ -50,7 +50,7 @@ class Fancy Package {
50
50
  """
51
51
 
52
52
  filename = nil
53
- if: (@version == "latest") then: {
53
+ if: (@version == 'latest) then: {
54
54
  if: latest_tag then: |tag| {
55
55
  @version = tag
56
56
  } else: {
@@ -200,13 +200,7 @@ class Fancy Package {
200
200
  Package install: dep
201
201
  }
202
202
 
203
- spec rubygem_dependencies each: |dep| {
204
- if: (dep version == 'latest) then: {
205
- System do: $ "rbx gem install " ++ (dep gem_name)
206
- } else: {
207
- System do: $ "rbx gem install -v=" ++ (dep version) ++ " " ++ (dep gem_name)
208
- }
209
- }
203
+ spec rubygem_dependencies each: |dep| { dep install }
210
204
  }
211
205
  }
212
206
  }
@@ -1,9 +1,7 @@
1
1
  class Fancy Package {
2
2
  class Uninstaller {
3
3
  """
4
-
5
- @Fancy Package@ Uninstaller.
6
-
4
+ @Fancy::Package@ Uninstaller.
7
5
  """
8
6
 
9
7
  def initialize: @package_name {
@@ -2,7 +2,7 @@
2
2
  #include "ruby.h"
3
3
  #include "parser.h"
4
4
 
5
- int yyerror(char *s);
5
+ int yyerror(VALUE self, char *s);
6
6
  %}
7
7
 
8
8
  %option yylineno
@@ -54,7 +54,7 @@ identifier @?@?({lower}|[_&*])({letter}|{digit}|{special_under})*
54
54
  selector ({letter}|[_&*])({letter}|{digit}|{special_under})*":"
55
55
  constant {capital}({letter}|{digit}|{special_under})*
56
56
  nested_constant ({constant}::)+{constant}
57
- symbol_lit \'({identifier}|{operator}|{constant}|:|"[]"|"|")+
57
+ symbol_lit \'({identifier}|{operator}|{constant}|:|"[]"|"|"|".")+
58
58
  ruby_send_open ({constant}|{identifier}){lparen}
59
59
  ruby_oper_open {operator}{lparen}
60
60
  regexp_lit "/".*"/"
@@ -186,5 +186,10 @@ escaped_newline "\\".*\n
186
186
  {escaped_newline} {}
187
187
  [\n] { return NL; }
188
188
 
189
- . { fprintf(stderr, "SCANNER %d", yyerror("")); exit(1); }
189
+ . {
190
+ fprintf(stderr, "Invalid token: %s\n", yytext);
191
+ VALUE fancy = rb_const_get(rb_cObject, rb_intern("Fancy"));
192
+ VALUE parser = rb_const_get(fancy, rb_intern("Parser"));
193
+ yyerror(parser, yytext);
194
+ }
190
195
 
@@ -273,6 +273,9 @@ identifier: IDENTIFIER {
273
273
  | CLASS {
274
274
  $$ = fy_terminal_node_from(self, "ast:identifier:", "class");
275
275
  }
276
+ | RETURN {
277
+ $$ = fy_terminal_node_from(self, "ast:identifier:", "return");
278
+ }
276
279
  ;
277
280
 
278
281
  any_identifier: const_identifier
@@ -821,7 +824,7 @@ VALUE fy_terminal_node_from(VALUE self, char* method, char* text) {
821
824
 
822
825
  int yyerror(VALUE self, char *s)
823
826
  {
824
- rb_funcall(self, rb_intern("ast:parse_error:"), 2, INT2NUM(yylineno), rb_str_new2(yytext));
827
+ rb_funcall(self, rb_intern("ast:parse_error:"), 2, INT2NUM(yylineno), rb_str_new2(s));
825
828
  return 1;
826
829
  }
827
830
 
@@ -13,9 +13,13 @@ class Fancy {
13
13
  new: filename line: line . parse_string: code . script
14
14
  }
15
15
 
16
+ def self ast: line parse_error: text {
17
+ ParseError new: line message: text filename: (Thread current['__fancy__parser__filename__]) . raise!
18
+ }
19
+
16
20
  read_write_slots: ['filename, 'line, 'script]
17
21
 
18
- def initialize: @filename line: @line { }
22
+ def initialize: @filename line: @line { Thread current['__fancy__parser__filename__]: @filename }
19
23
 
20
24
  def body: body {
21
25
  @script = AST Script new: @line file: @filename body: body
@@ -69,7 +73,7 @@ class Fancy {
69
73
  # TODO: Clean this up or make it simpler...
70
74
 
71
75
  # this case handles string interpolation
72
- case /(.*)#{(.*)}(.*)/ -> |matches|
76
+ case /(.*)#{(.*)}(.*)/m -> |matches|
73
77
  prefix = matches[1]
74
78
  interpol_str = matches[2]
75
79
  suffix = matches[3]
@@ -78,7 +82,7 @@ class Fancy {
78
82
  suffix_str = ast: line string: (" " + suffix + " ")
79
83
  interpol_ast = AST StringInterpolation new: line code: interpol_str
80
84
  # create messagesend to concatenate:
81
- concat_ident = ast: line identifier: "++"
85
+ concat_ident = ast: line identifier: "<<"
82
86
  interpol_send = AST MessageSend new: line message: concat_ident to: prefix_str args: (AST MessageArgs new: line args: [interpol_ast])
83
87
 
84
88
  # don't concatenate suffix if it's empty..