apricot 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/.travis.yml +1 -0
- data/Gemfile.lock +229 -11
- data/README.md +46 -29
- data/Rakefile +1 -1
- data/apricot.gemspec +7 -3
- data/benchmarks/factorial.rb +51 -0
- data/benchmarks/interpolate.rb +20 -0
- data/bin/apricot +5 -23
- data/examples/bot.apr +1 -4
- data/examples/cinch-bot.apr +3 -3
- data/examples/sinatra.apr +9 -0
- data/kernel/core.apr +124 -75
- data/kernel/repl.apr +37 -0
- data/lib/apricot.rb +7 -26
- data/lib/apricot/boot.rb +24 -0
- data/lib/apricot/code_loader.rb +108 -0
- data/lib/apricot/compiler.rb +265 -32
- data/lib/apricot/generator.rb +10 -3
- data/lib/apricot/identifier.rb +25 -10
- data/lib/apricot/list.rb +28 -41
- data/lib/apricot/macroexpand.rb +14 -8
- data/lib/apricot/misc.rb +2 -1
- data/lib/apricot/namespace.rb +20 -3
- data/lib/apricot/{parser.rb → reader.rb} +221 -194
- data/lib/apricot/repl.rb +67 -24
- data/lib/apricot/ruby_ext.rb +27 -16
- data/lib/apricot/scopes.rb +159 -0
- data/lib/apricot/seq.rb +43 -1
- data/lib/apricot/special_forms.rb +16 -695
- data/lib/apricot/special_forms/def.rb +32 -0
- data/lib/apricot/special_forms/do.rb +23 -0
- data/lib/apricot/special_forms/dot.rb +112 -0
- data/lib/apricot/special_forms/fn.rb +342 -0
- data/lib/apricot/special_forms/if.rb +31 -0
- data/lib/apricot/special_forms/let.rb +8 -0
- data/lib/apricot/special_forms/loop.rb +10 -0
- data/lib/apricot/special_forms/quote.rb +9 -0
- data/lib/apricot/special_forms/recur.rb +26 -0
- data/lib/apricot/special_forms/try.rb +146 -0
- data/lib/apricot/variables.rb +65 -0
- data/lib/apricot/version.rb +1 -1
- data/spec/compiler_spec.rb +53 -450
- data/spec/fn_spec.rb +206 -0
- data/spec/list_spec.rb +1 -1
- data/spec/reader_spec.rb +349 -0
- data/spec/spec_helper.rb +40 -4
- data/spec/special_forms_spec.rb +203 -0
- metadata +99 -133
- data/lib/apricot/ast.rb +0 -3
- data/lib/apricot/ast/identifier.rb +0 -111
- data/lib/apricot/ast/list.rb +0 -99
- data/lib/apricot/ast/literals.rb +0 -240
- data/lib/apricot/ast/node.rb +0 -45
- data/lib/apricot/ast/scopes.rb +0 -147
- data/lib/apricot/ast/toplevel.rb +0 -66
- data/lib/apricot/ast/variables.rb +0 -64
- data/lib/apricot/printers.rb +0 -12
- data/lib/apricot/stages.rb +0 -60
- 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
|
data/bin/apricot
CHANGED
@@ -1,20 +1,7 @@
|
|
1
|
-
#!/usr/bin/env
|
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
|
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
|
34
|
+
Apricot::Compiler.eval(args.first, "(eval)", 1)
|
52
35
|
when :stdin
|
53
|
-
Apricot::Compiler.eval(STDIN.read, "(stdin)", 1
|
36
|
+
Apricot::Compiler.eval(STDIN.read, "(stdin)", 1)
|
54
37
|
when :file
|
55
|
-
|
56
|
-
Apricot::Compiler.compile(args.first, nil, bytecode)
|
38
|
+
Apricot::CodeLoader.require(File.expand_path(args.first))
|
57
39
|
end
|
58
40
|
end
|
data/examples/bot.apr
CHANGED
data/examples/cinch-bot.apr
CHANGED
@@ -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.
|
5
|
+
(.server= "irc.freenode.net")
|
6
6
|
(.nick= "apribot")
|
7
|
-
(.channels= ["#
|
7
|
+
(.channels= ["#apricot"])))
|
8
8
|
|
9
9
|
(.on :message #r/^apribot[:,]?\s+(.+)/
|
10
10
|
| (fn [m msg] (.reply m msg true)))
|
data/kernel/core.apr
CHANGED
@@ -25,10 +25,10 @@
|
|
25
25
|
{})
|
26
26
|
arglists (if (.is_a? (.first body) Array)
|
27
27
|
(list (.first body))
|
28
|
-
(.
|
29
|
-
(.map body | #(if (.is_a? % Apricot::
|
28
|
+
(.to_seq
|
29
|
+
(.map body | #(if (.is_a? % Apricot::Seq)
|
30
30
|
(.first %)))))
|
31
|
-
f (
|
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 (
|
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
|
-
"
|
75
|
-
[
|
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
|
-
[
|
160
|
+
[? (prefix "g")]
|
114
161
|
(identifier (.gensym Apricot prefix)))
|
115
162
|
|
116
|
-
(defn require
|
117
|
-
"Require the given Ruby files
|
118
|
-
|
119
|
-
[&
|
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
|
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]
|
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
|
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 (
|
887
|
-
(println (
|
888
|
-
(if (
|
948
|
+
(println (:name m))
|
949
|
+
(println (:arglists m))
|
950
|
+
(if (:macro m)
|
889
951
|
(println "Macro"))
|
890
|
-
(println " " (
|
891
|
-
|
892
|
-
;
|
893
|
-
|
894
|
-
(defn
|
895
|
-
"
|
896
|
-
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
(defmacro
|
903
|
-
"
|
904
|
-
|
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
|
-
(
|
907
|
-
(
|
908
|
-
|
909
|
-
(
|
910
|
-
|
911
|
-
|
912
|
-
|
913
|
-
|
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")))))
|
data/kernel/repl.apr
ADDED
@@ -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)))
|
data/lib/apricot.rb
CHANGED
@@ -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
|
5
|
-
|
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
|