apricot 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -1
  4. data/.travis.yml +1 -0
  5. data/Gemfile.lock +229 -11
  6. data/README.md +46 -29
  7. data/Rakefile +1 -1
  8. data/apricot.gemspec +7 -3
  9. data/benchmarks/factorial.rb +51 -0
  10. data/benchmarks/interpolate.rb +20 -0
  11. data/bin/apricot +5 -23
  12. data/examples/bot.apr +1 -4
  13. data/examples/cinch-bot.apr +3 -3
  14. data/examples/sinatra.apr +9 -0
  15. data/kernel/core.apr +124 -75
  16. data/kernel/repl.apr +37 -0
  17. data/lib/apricot.rb +7 -26
  18. data/lib/apricot/boot.rb +24 -0
  19. data/lib/apricot/code_loader.rb +108 -0
  20. data/lib/apricot/compiler.rb +265 -32
  21. data/lib/apricot/generator.rb +10 -3
  22. data/lib/apricot/identifier.rb +25 -10
  23. data/lib/apricot/list.rb +28 -41
  24. data/lib/apricot/macroexpand.rb +14 -8
  25. data/lib/apricot/misc.rb +2 -1
  26. data/lib/apricot/namespace.rb +20 -3
  27. data/lib/apricot/{parser.rb → reader.rb} +221 -194
  28. data/lib/apricot/repl.rb +67 -24
  29. data/lib/apricot/ruby_ext.rb +27 -16
  30. data/lib/apricot/scopes.rb +159 -0
  31. data/lib/apricot/seq.rb +43 -1
  32. data/lib/apricot/special_forms.rb +16 -695
  33. data/lib/apricot/special_forms/def.rb +32 -0
  34. data/lib/apricot/special_forms/do.rb +23 -0
  35. data/lib/apricot/special_forms/dot.rb +112 -0
  36. data/lib/apricot/special_forms/fn.rb +342 -0
  37. data/lib/apricot/special_forms/if.rb +31 -0
  38. data/lib/apricot/special_forms/let.rb +8 -0
  39. data/lib/apricot/special_forms/loop.rb +10 -0
  40. data/lib/apricot/special_forms/quote.rb +9 -0
  41. data/lib/apricot/special_forms/recur.rb +26 -0
  42. data/lib/apricot/special_forms/try.rb +146 -0
  43. data/lib/apricot/variables.rb +65 -0
  44. data/lib/apricot/version.rb +1 -1
  45. data/spec/compiler_spec.rb +53 -450
  46. data/spec/fn_spec.rb +206 -0
  47. data/spec/list_spec.rb +1 -1
  48. data/spec/reader_spec.rb +349 -0
  49. data/spec/spec_helper.rb +40 -4
  50. data/spec/special_forms_spec.rb +203 -0
  51. metadata +99 -133
  52. data/lib/apricot/ast.rb +0 -3
  53. data/lib/apricot/ast/identifier.rb +0 -111
  54. data/lib/apricot/ast/list.rb +0 -99
  55. data/lib/apricot/ast/literals.rb +0 -240
  56. data/lib/apricot/ast/node.rb +0 -45
  57. data/lib/apricot/ast/scopes.rb +0 -147
  58. data/lib/apricot/ast/toplevel.rb +0 -66
  59. data/lib/apricot/ast/variables.rb +0 -64
  60. data/lib/apricot/printers.rb +0 -12
  61. data/lib/apricot/stages.rb +0 -60
  62. data/spec/parser_spec.rb +0 -312
@@ -0,0 +1,20 @@
1
+ $LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
2
+ require 'apricot'
3
+
4
+ begin
5
+ require 'benchmark/ips'
6
+ rescue LoadError
7
+ $stderr.puts "The benchmark-ips gem is not installed."
8
+ exit 1
9
+ end
10
+
11
+ apr = Apricot::Compiler.eval <<CODE
12
+ #(str "hello. " 42 " is the best number")
13
+ CODE
14
+
15
+ rbx = lambda { "hello. #{42} is the best number" }
16
+
17
+ Benchmark.ips do |x|
18
+ x.report("rbx", &rbx)
19
+ x.report("apr", &apr)
20
+ end
@@ -1,20 +1,7 @@
1
- #!/usr/bin/env rbx
2
- # vim: ft=ruby:
3
-
4
- unless defined?(Rubinius)
5
- $stderr.puts "Error: Apricot must be run on Rubinius."
6
- exit 1
7
- end
8
-
9
- unless Rubinius.ruby19?
10
- $stderr.puts "Warning: Apricot must be run in Ruby 1.9 mode, executing rbx -X19..."
11
- exec("/usr/bin/env", "rbx", "-X19", file, *ARGV)
12
- end
13
-
1
+ #!/usr/bin/env ruby
14
2
  require 'apricot'
15
3
 
16
4
  evals = []
17
- bytecode = false
18
5
 
19
6
  options = Rubinius::Options.new "Usage: #{$0} [options] [program]", 20
20
7
  options.doc "OPTIONS:"
@@ -23,10 +10,6 @@ options.on "-e", "CODE", "evaluate CODE and print the result" do |code|
23
10
  evals << [:eval, code]
24
11
  end
25
12
 
26
- options.on "-B", "--bytecode", "print bytecode after compiling" do
27
- bytecode = true
28
- end
29
-
30
13
  options.on "-h", "--help", "display this help" do
31
14
  puts options
32
15
  exit
@@ -39,7 +22,7 @@ end
39
22
  if evals.empty?
40
23
  if $stdin.tty?
41
24
  require 'apricot/repl'
42
- Apricot::REPL.new('apr> ', bytecode).run
25
+ Apricot::REPL.new.run
43
26
  else
44
27
  evals << [:stdin]
45
28
  end
@@ -48,11 +31,10 @@ end
48
31
  evals.each do |type, *args|
49
32
  case type
50
33
  when :eval
51
- Apricot::Compiler.eval(args.first, "(eval)", 1, bytecode)
34
+ Apricot::Compiler.eval(args.first, "(eval)", 1)
52
35
  when :stdin
53
- Apricot::Compiler.eval(STDIN.read, "(stdin)", 1, bytecode)
36
+ Apricot::Compiler.eval(STDIN.read, "(stdin)", 1)
54
37
  when :file
55
- # The code is executed as it compiles.
56
- Apricot::Compiler.compile(args.first, nil, bytecode)
38
+ Apricot::CodeLoader.require(File.expand_path(args.first))
57
39
  end
58
40
  end
@@ -1,7 +1,4 @@
1
- ; An example IRC bot. This does not reflect our vision for Apricot, just what
2
- ; features we currently have working.
3
-
4
- (require "socket")
1
+ (require-ruby "socket")
5
2
 
6
3
  (defn send [io & parts]
7
4
  (let [msg (apply str parts)]
@@ -1,10 +1,10 @@
1
- (require "cinch")
1
+ (require-ruby "cinch")
2
2
 
3
3
  (doto (Cinch::Bot.)
4
4
  (.configure | #(doto %
5
- (.server= "irc.tenthbit.net")
5
+ (.server= "irc.freenode.net")
6
6
  (.nick= "apribot")
7
- (.channels= ["#programming"])))
7
+ (.channels= ["#apricot"])))
8
8
 
9
9
  (.on :message #r/^apribot[:,]?\s+(.+)/
10
10
  | (fn [m msg] (.reply m msg true)))
@@ -0,0 +1,9 @@
1
+ (require-ruby "sinatra")
2
+
3
+ (defmacro get [route & body]
4
+ `(.send MAIN :get ~route | (fn ~@body)))
5
+
6
+ (get "/hello/:name" [name]
7
+ (str "Hello " name))
8
+
9
+ (.run! Sinatra::Application)
@@ -25,10 +25,10 @@
25
25
  {})
26
26
  arglists (if (.is_a? (.first body) Array)
27
27
  (list (.first body))
28
- (.to_list
29
- (.map body | #(if (.is_a? % Apricot::List)
28
+ (.to_seq
29
+ (.map body | #(if (.is_a? % Apricot::Seq)
30
30
  (.first %)))))
31
- f (.intern Apricot::Identifier (.gensym Apricot))]
31
+ f (Apricot/gensym)]
32
32
  (list 'let [f (concat (list 'fn name) body)]
33
33
  (list 'def name f)
34
34
  (list '.apricot_meta=
@@ -53,7 +53,7 @@
53
53
  [name doc-string? metadata? ([params ...] body) ...+])
54
54
  :macro true}
55
55
  [name & body]
56
- (let [f (.intern Apricot::Identifier (.gensym Apricot))]
56
+ (let [f (Apricot/gensym)]
57
57
  (list 'let [f (concat (list 'defn name) body)]
58
58
  (list '.store (list '.apricot_meta f) :macro true)
59
59
  f)))
@@ -62,6 +62,26 @@
62
62
  "Create a new list containing the items."
63
63
  [& items] (.to_list items))
64
64
 
65
+ (defn seq
66
+ "Return a seq on the collection. If the collection is empty, return nil.
67
+ (seq nil) returns nil."
68
+ [coll] (.to_seq coll))
69
+
70
+ (defn first
71
+ "Return the first item in the collection. Call seq on the argument. If coll
72
+ is nil, return nil."
73
+ [coll] (.first (seq coll)))
74
+
75
+ (defn rest
76
+ "Return a possibly empty seq of the items after the first. Call seq on the
77
+ argument."
78
+ [coll] (.rest (seq coll)))
79
+
80
+ (defn next
81
+ "Return a seq of the items after the first. Call seq on the argument. If
82
+ there are no more items, return nil."
83
+ [coll] (.next (seq coll)))
84
+
65
85
  (defn concat
66
86
  "Concatenate the items in the supplied colls into a single list."
67
87
  [& colls] (.to_list (.reduce (.map colls | :to_a) [] :+)))
@@ -71,8 +91,16 @@
71
91
  [& items] items)
72
92
 
73
93
  (defn set
74
- "Create a new set containing the items."
75
- [& items] (Set. items))
94
+ "Return a set of the distinct elements of coll."
95
+ [coll] (Set. coll))
96
+
97
+ (defn hash-set
98
+ "Return a new hash set with supplied keys."
99
+ [& keys] (Set. keys))
100
+
101
+ (defn sorted-set
102
+ "Return a new sorted set with supplied keys."
103
+ [& keys] (SortedSet. keys))
76
104
 
77
105
  (defn hash
78
106
  "Create a new hash map from the items. The items are interpreted as a list of
@@ -88,6 +116,25 @@
88
116
  "Return a new list where head is the first element and tail is the rest."
89
117
  [head tail] (Apricot::Cons. head tail))
90
118
 
119
+ (defn spread
120
+ {:private true}
121
+ [arglist]
122
+ (if (.nil? arglist)
123
+ nil
124
+ (if (.nil? (next arglist))
125
+ (seq (first arglist))
126
+ (cons (first arglist) (spread (next arglist))))))
127
+
128
+ (defn list*
129
+ "Creates a new list containing the items prepended to the rest, the last of
130
+ which will be treated as a sequence."
131
+ ([args] (seq args))
132
+ ([a args] (cons a args))
133
+ ([a b args] (cons a (cons b args)))
134
+ ([a b c args] (cons a (cons b (cons c args))))
135
+ ([a b c d & more]
136
+ (cons a (cons b (cons c (cons d (spread more)))))))
137
+
91
138
  (defn apply
92
139
  "Applies fn f to the argument list formed by prepending intervening
93
140
  arguments to args."
@@ -110,16 +157,30 @@
110
157
  "Return a new identifier with a unique name. If a prefix string is supplied,
111
158
  the name is prefix__# where # is some unique number. If prefix is not
112
159
  supplied, the prefix is 'g'."
113
- [[prefix "g"]]
160
+ [? (prefix "g")]
114
161
  (identifier (.gensym Apricot prefix)))
115
162
 
116
- (defn require
117
- "Require the given Ruby files. Works just like using Ruby's 'require' on
118
- each of the arguments."
119
- [& files]
163
+ (defn require-ruby
164
+ "Require the given Ruby files, skipping any which are already loaded. Just
165
+ like calling Ruby's require method on each argument."
166
+ [& names]
120
167
  ; (. Kernel require %) does not call the Rubygems custom require for some
121
168
  ; reason, so we use this method. (MAIN is the special toplevel object).
122
- (.each files | #(. MAIN send :require %)))
169
+ (.each names | #(. MAIN send :require %))
170
+ nil)
171
+
172
+ (defn load
173
+ "Load the given Apricot files, unconditionally. To skip loading files which
174
+ are already loaded, see 'require'."
175
+ [& names]
176
+ (.each names | #(Apricot/load %))
177
+ nil)
178
+
179
+ (defn require
180
+ "Load the given Apricot files, skipping any which are already loaded."
181
+ [& names]
182
+ (.each names | #(Apricot/require %))
183
+ nil)
123
184
 
124
185
  (defn str
125
186
  "With no args, return the empty string. With one arg x, return x converted
@@ -130,6 +191,8 @@
130
191
  ([x & args]
131
192
  (.reduce args (.apricot_str x) | #(.concat %1 (.apricot_str %2)))))
132
193
 
194
+ (def format Kernel/format)
195
+
133
196
  (defn print
134
197
  "Print the object(s) to standard output."
135
198
  [& args] (Kernel/print (apply str args)))
@@ -273,26 +336,6 @@
273
336
 
274
337
  ; Collection functions
275
338
 
276
- (defn seq
277
- "Returns a seq on the collection. If the collection is empty, returns nil.
278
- (seq nil) returns nil."
279
- [coll] (.to_seq coll))
280
-
281
- (defn first
282
- "Return the first item in the collection. Call seq on the argument. If coll
283
- is nil, return nil."
284
- [coll] (.first (seq coll)))
285
-
286
- (defn rest
287
- "Return a possibly empty seq of the items after the first. Call seq on the
288
- argument."
289
- [coll] (.rest (seq coll)))
290
-
291
- (defn next
292
- "Return a seq of the items after the first. Call seq on the argument. If
293
- there are no more items, return nil."
294
- [coll] (.next (seq coll)))
295
-
296
339
  (defn empty?
297
340
  "Return true if coll has no items - same as (not (seq coll)). Please use the
298
341
  idiom (seq x) rather than (not (empty? x))"
@@ -527,6 +570,8 @@
527
570
  (defn =
528
571
  "Return true if all of the arguments are equal, otherwise false. (=) returns
529
572
  true."
573
+ {:inline (fn [x y] `(. ~x == ~y))
574
+ :inline-arities #{2}}
530
575
  ([x] true)
531
576
  ([x y] (. x == y))
532
577
  ([x y & more]
@@ -545,7 +590,14 @@
545
590
  (defn compare
546
591
  "Return a negative number, zero, or a positive number when x is logically
547
592
  'less than', 'equal to', or 'greater than' y, respectively."
548
- [x y] (. x <=> y))
593
+ [x y]
594
+ (if (nil? x)
595
+ (if (nil? y)
596
+ 0
597
+ -1)
598
+ (if (nil? y)
599
+ 1
600
+ (. x <=> y))))
549
601
 
550
602
  (defn >
551
603
  "Return true if nums are in monotonically decreasing order, otherwise false."
@@ -774,7 +826,7 @@
774
826
 
775
827
  If test is true, evaluate then with var bound to the value of test,
776
828
  otherwise yield else."
777
- [bindings then [else nil]]
829
+ [bindings then ? (else nil)]
778
830
  `(let [temp# ~(bindings 1)]
779
831
  (if temp#
780
832
  (let [~(bindings 0) temp#]
@@ -839,6 +891,16 @@
839
891
 
840
892
  ; Miscellaneous (to be sorted)
841
893
 
894
+ (defn read
895
+ "Read the next object from io, which must be an instance of IO. The default
896
+ io is stdin."
897
+ [? (io STDIN)]
898
+ (.read_one (Apricot::Reader. io)))
899
+
900
+ (defn read-string
901
+ "Read one object from the string s."
902
+ [s] (read (StringIO. s)))
903
+
842
904
  (defn eval
843
905
  "Evaluate the form data structure (not text!) and return the result."
844
906
  [form] (Apricot::Compiler/eval_form form))
@@ -883,46 +945,33 @@
883
945
  [f]
884
946
  (let [m (meta f)]
885
947
  (println "-------------------------")
886
- (println (m :name))
887
- (println (m :arglists))
888
- (if (m :macro)
948
+ (println (:name m))
949
+ (println (:arglists m))
950
+ (if (:macro m)
889
951
  (println "Macro"))
890
- (println " " (m :doc))))
891
-
892
- ; REPL Utilities
893
-
894
- (defn decode
895
- "Print the Rubinius bytecode for the given Proc or Method."
896
- [f]
897
- (case f
898
- [Proc] (Kernel/puts (.. f block compiled_code decode))
899
- [Method] (Kernel/puts (.. f executable decode))
900
- (raise (str "Don't know how to decode " (.inspect f)))))
901
-
902
- (defmacro time
903
- "Evaluate the forms in body and return the time it took."
904
- [& body]
952
+ (println " " (:doc m))))
953
+
954
+ ; Namespaces
955
+
956
+ (defn refer
957
+ "For each public interned var in the namespace named by the symbol, adds a
958
+ mapping from the name of the var to the var to the current namespace."
959
+ [ns]
960
+ (each [var (.keys (.vars ns))]
961
+ (.add_alias *ns* var ns))
962
+ nil)
963
+
964
+ (defmacro ns
965
+ "Sets *ns* to the namespace named by name (unevaluated), creating it if
966
+ needed. Optionally takes a uses clause like (:use Foo Bar Baz) to important
967
+ vars from other namespaces."
968
+ [ns-sym ? (uses nil)]
905
969
  `(do
906
- (require "benchmark")
907
- (.realtime Benchmark | (fn [] ~@body))))
908
-
909
- (defmacro benchmark-ips
910
- "clause => [label form ...]
911
-
912
- Measure how many times per second each of the clause's bodies can be
913
- executed. Output is organized using the given label strings.
914
-
915
- This requires the benchmark-ips gem:
916
- gem install benchmark-ips"
917
- [& clauses]
918
- (let [bm (gensym)
919
- make-report (fn [clause]
920
- `(.report ~bm ~(first clause) | (fn [] ~@(rest clause))))
921
- reports (map make-report clauses)]
922
- `(do
923
- (try
924
- (require "benchmark/ips")
925
- (.ips Benchmark | (fn [~bm] ~@reports))
926
- (rescue [_ LoadError]
927
- (raise "benchmark-ips requires the benchmark-ips gem")))
928
- nil)))
970
+ (in-ns '~ns-sym)
971
+ ~(if (not= ns-sym 'Apricot::Core)
972
+ '(Apricot::Core/refer Apricot::Core))
973
+ ~@(if uses
974
+ (if (= :use (first uses))
975
+ (map (fn [ns] `(Apricot::Core/refer ~ns))
976
+ (rest uses))
977
+ (raise "Invalid clause in ns macro")))))
@@ -0,0 +1,37 @@
1
+ ; REPL Utilities
2
+
3
+ (defn decode
4
+ "Print the Rubinius bytecode for the given Proc or Method."
5
+ [f]
6
+ (case f
7
+ [Proc] (Kernel/puts (.. f block compiled_code decode))
8
+ [Method] (Kernel/puts (.. f executable decode))
9
+ (raise (str "Don't know how to decode " (.inspect f)))))
10
+
11
+ (defmacro time
12
+ "Evaluate the forms in body and return the time it took."
13
+ [& body]
14
+ `(do
15
+ (require-ruby "benchmark")
16
+ (.realtime Benchmark | (fn [] ~@body))))
17
+
18
+ (defmacro benchmark-ips
19
+ "clause => [label form ...]
20
+
21
+ Measure how many times per second each of the clause's bodies can be
22
+ executed. Output is organized using the given label strings.
23
+
24
+ This requires the benchmark-ips gem:
25
+ gem install benchmark-ips"
26
+ [& clauses]
27
+ (let [bm (gensym)
28
+ make-report (fn [clause]
29
+ `(.report ~bm ~(first clause) | (fn [] ~@(rest clause))))
30
+ reports (map make-report clauses)]
31
+ `(do
32
+ (try
33
+ (require-ruby "benchmark/ips")
34
+ (.ips Benchmark | (fn [~bm] ~@reports))
35
+ (rescue [_ LoadError]
36
+ (raise "benchmark-ips requires the benchmark-ips gem")))
37
+ nil)))
@@ -1,30 +1,11 @@
1
+ unless RUBY_VERSION.start_with?("2") && RUBY_ENGINE == "rbx" && Rubinius::VERSION.start_with?("2")
2
+ $stderr.puts "Apricot must be run on the stable Rubinius 2.0.0 release or newer."
3
+ exit 1
4
+ end
5
+
1
6
  require 'set'
2
7
 
3
8
  %w[
4
- version misc parser compiler ast macroexpand generator stages printers
5
- special_forms errors seq cons list identifier ruby_ext namespace
9
+ version misc namespace seq cons list identifier ruby_ext reader compiler
10
+ scopes variables macroexpand generator special_forms errors code_loader boot
6
11
  ].each {|r| require "apricot/#{r}" }
7
-
8
- # Start "booting" apricot. Set up core namespace and load the core library.
9
- module Apricot
10
- Core = Namespace.new
11
-
12
- Core.set_var(:"*ns*", Core)
13
-
14
- Core.set_var(:"in-ns", lambda do |constant|
15
- Apricot.current_namespace = Namespace.find_or_create constant
16
- end)
17
-
18
- Core.set_var(:ns, lambda do |constant|
19
- List[Identifier.intern(:"in-ns"),
20
- List[Identifier.intern(:quote), constant]]
21
- end)
22
- Core.get_var(:ns).apricot_meta = {macro: true}
23
-
24
- # TODO: add and use a proper code loader
25
- Apricot::Compiler.compile(File.expand_path('../../kernel/core.apr', __FILE__))
26
-
27
- # ::User = Namespace.new
28
- Apricot.current_namespace = Core
29
- # TODO: make Apricot::Core public vars visible in User, default to User
30
- end