apricot 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +26 -0
- data/README.md +90 -0
- data/Rakefile +9 -0
- data/apricot.gemspec +22 -0
- data/bin/apricot +58 -0
- data/examples/bot.apr +23 -0
- data/examples/cinch-bot.apr +12 -0
- data/examples/hanoi.apr +10 -0
- data/examples/hello.apr +1 -0
- data/examples/plot.apr +28 -0
- data/examples/quine.apr +1 -0
- data/kernel/core.apr +928 -0
- data/lib/apricot/ast/identifier.rb +111 -0
- data/lib/apricot/ast/list.rb +99 -0
- data/lib/apricot/ast/literals.rb +240 -0
- data/lib/apricot/ast/node.rb +45 -0
- data/lib/apricot/ast/scopes.rb +147 -0
- data/lib/apricot/ast/toplevel.rb +66 -0
- data/lib/apricot/ast/variables.rb +64 -0
- data/lib/apricot/ast.rb +3 -0
- data/lib/apricot/compiler.rb +55 -0
- data/lib/apricot/cons.rb +27 -0
- data/lib/apricot/errors.rb +38 -0
- data/lib/apricot/generator.rb +15 -0
- data/lib/apricot/identifier.rb +91 -0
- data/lib/apricot/list.rb +96 -0
- data/lib/apricot/macroexpand.rb +47 -0
- data/lib/apricot/misc.rb +11 -0
- data/lib/apricot/namespace.rb +59 -0
- data/lib/apricot/parser.rb +541 -0
- data/lib/apricot/printers.rb +12 -0
- data/lib/apricot/repl.rb +254 -0
- data/lib/apricot/ruby_ext.rb +254 -0
- data/lib/apricot/seq.rb +44 -0
- data/lib/apricot/special_forms.rb +735 -0
- data/lib/apricot/stages.rb +60 -0
- data/lib/apricot/version.rb +3 -0
- data/lib/apricot.rb +30 -0
- data/spec/compiler_spec.rb +499 -0
- data/spec/identifier_spec.rb +58 -0
- data/spec/list_spec.rb +96 -0
- data/spec/parser_spec.rb +312 -0
- data/spec/spec_helper.rb +10 -0
- metadata +188 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --require spec_helper
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rbx-head
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
apricot (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.2.1)
|
10
|
+
rake (10.0.3)
|
11
|
+
rspec (2.13.0)
|
12
|
+
rspec-core (~> 2.13.0)
|
13
|
+
rspec-expectations (~> 2.13.0)
|
14
|
+
rspec-mocks (~> 2.13.0)
|
15
|
+
rspec-core (2.13.0)
|
16
|
+
rspec-expectations (2.13.0)
|
17
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
18
|
+
rspec-mocks (2.13.0)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
apricot!
|
25
|
+
rake (~> 10.0.3)
|
26
|
+
rspec (~> 2.13.0)
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# Apricot [![Build Status](https://secure.travis-ci.org/programble/apricot.png?branch=master)](http://travis-ci.org/programble/apricot) [![Dependency Status](https://gemnasium.com/programble/apricot.png?travis)](https://gemnasium.com/programble/apricot)
|
2
|
+
|
3
|
+
A Clojure-like Lisp on Rubinius.
|
4
|
+
|
5
|
+
Try to contain your excitement, please.
|
6
|
+
|
7
|
+
|
8
|
+
## Setup
|
9
|
+
To get Apricot up and running, make sure you have Rubinius and Bundler
|
10
|
+
installed. The easiest way to get Rubinius is with [RVM](https://rvm.io/).
|
11
|
+
Whenever you are using Apricot you need to be running Rubinius in Ruby 1.9
|
12
|
+
mode. We make this easy in the Apricot repository with the `.ruby-version`
|
13
|
+
file which RVM automatically reads to figure out which Ruby to switch to.
|
14
|
+
|
15
|
+
``` sh
|
16
|
+
$ rvm install rbx-head --1.9
|
17
|
+
$ rvm use rbx-head
|
18
|
+
$ gem install bundler
|
19
|
+
$ bundle
|
20
|
+
```
|
21
|
+
|
22
|
+
## The REPL
|
23
|
+
Apricot provides a nice read-eval-print-loop with line editing, history,
|
24
|
+
tab-completion, and some interesting commands like `!bytecode`. To enter the
|
25
|
+
REPL just run:
|
26
|
+
|
27
|
+
``` sh
|
28
|
+
$ bin/apricot
|
29
|
+
```
|
30
|
+
|
31
|
+
Once in the repl you can ask for help by using `!help` and you can exit with
|
32
|
+
`!exit`. See the documentation of any function with `(doc <name>)`. Play
|
33
|
+
around, read `kernel/core.apr` and try out our functions, and make some of
|
34
|
+
your own. Experiment. Tell us what you think!
|
35
|
+
|
36
|
+
``` clojure
|
37
|
+
apr> (+ 1 2 3)
|
38
|
+
=> 6
|
39
|
+
apr> (map (fn [x] (* x x)) (Range. 1 10))
|
40
|
+
=> [1 4 9 16 25 36 49 64 81 100]
|
41
|
+
apr> (defn square [x] (* x x))
|
42
|
+
=> #<Proc:0x330@(eval):3 (lambda)>
|
43
|
+
apr> (map square (Range. 1 10))
|
44
|
+
=> [1 4 9 16 25 36 49 64 81 100]
|
45
|
+
apr> (map (comp str square) (Range. 1 10))
|
46
|
+
=> ["1" "4" "9" "16" "25" "36" "49" "64" "81" "100"]
|
47
|
+
apr> (doc comp)
|
48
|
+
-------------------------
|
49
|
+
comp
|
50
|
+
([] [f] [f g] [f g h] [f1 f2 f3 & fs])
|
51
|
+
Take a set of functions and return a fn that is the composition of those
|
52
|
+
fns. The returned fn takes a variable number of args, applies the rightmost
|
53
|
+
of fns to the args, the next fn (right-to-left) to the result, etc.
|
54
|
+
=> nil
|
55
|
+
```
|
56
|
+
|
57
|
+
## Hello World
|
58
|
+
So you want to put your program in a file and not type it into the REPL? Sure:
|
59
|
+
|
60
|
+
``` sh
|
61
|
+
$ cat hello.apr
|
62
|
+
(puts "Hello, world!")
|
63
|
+
$ bin/apricot hello.apr
|
64
|
+
Hello, world!
|
65
|
+
```
|
66
|
+
|
67
|
+
## Contact / Bug Reports
|
68
|
+
Come visit us on [freenode](http://freenode.net/) in the #apricot channel, or
|
69
|
+
drop one of us an email. Please send your bug reports to the GitHub
|
70
|
+
[issue tracker](https://github.com/programble/apricot/issues). They will be
|
71
|
+
greatly appreciated!
|
72
|
+
|
73
|
+
|
74
|
+
## License
|
75
|
+
|
76
|
+
Copyright (c) 2012-2013, Curtis McEnroe <programble@gmail.com>
|
77
|
+
|
78
|
+
Copyright (c) 2012-2013, Scott Olson <scott@scott-olson.org>
|
79
|
+
|
80
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
81
|
+
purpose with or without fee is hereby granted, provided that the above
|
82
|
+
copyright notice and this permission notice appear in all copies.
|
83
|
+
|
84
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
85
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
86
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
87
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
88
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
89
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
90
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
data/Rakefile
ADDED
data/apricot.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
|
2
|
+
require "apricot/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "apricot"
|
6
|
+
s.version = Apricot::VERSION
|
7
|
+
s.authors = ["Curtis McEnroe", "Scott Olson"]
|
8
|
+
s.email = ["programble@gmail.com", "scott@scott-olson.org"]
|
9
|
+
s.homepage = "https://github.com/programble/apricot"
|
10
|
+
s.license = "ISC"
|
11
|
+
s.summary = "A Clojure-like programming language on Rubinius"
|
12
|
+
s.description = "A compiler for a Clojure-like programming language on the Rubinius VM"
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
s.add_development_dependency "rake", "~> 10.0.3"
|
20
|
+
s.add_development_dependency "rspec", "~> 2.13.0"
|
21
|
+
end
|
22
|
+
|
data/bin/apricot
ADDED
@@ -0,0 +1,58 @@
|
|
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
|
+
|
14
|
+
require 'apricot'
|
15
|
+
|
16
|
+
evals = []
|
17
|
+
bytecode = false
|
18
|
+
|
19
|
+
options = Rubinius::Options.new "Usage: #{$0} [options] [program]", 20
|
20
|
+
options.doc "OPTIONS:"
|
21
|
+
|
22
|
+
options.on "-e", "CODE", "evaluate CODE and print the result" do |code|
|
23
|
+
evals << [:eval, code]
|
24
|
+
end
|
25
|
+
|
26
|
+
options.on "-B", "--bytecode", "print bytecode after compiling" do
|
27
|
+
bytecode = true
|
28
|
+
end
|
29
|
+
|
30
|
+
options.on "-h", "--help", "display this help" do
|
31
|
+
puts options
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
|
35
|
+
options.parse(ARGV).each do |file|
|
36
|
+
evals << [:file, file]
|
37
|
+
end
|
38
|
+
|
39
|
+
if evals.empty?
|
40
|
+
if $stdin.tty?
|
41
|
+
require 'apricot/repl'
|
42
|
+
Apricot::REPL.new('apr> ', bytecode).run
|
43
|
+
else
|
44
|
+
evals << [:stdin]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
evals.each do |type, *args|
|
49
|
+
case type
|
50
|
+
when :eval
|
51
|
+
Apricot::Compiler.eval(args.first, "(eval)", 1, bytecode)
|
52
|
+
when :stdin
|
53
|
+
Apricot::Compiler.eval(STDIN.read, "(stdin)", 1, bytecode)
|
54
|
+
when :file
|
55
|
+
# The code is executed as it compiles.
|
56
|
+
Apricot::Compiler.compile(args.first, nil, bytecode)
|
57
|
+
end
|
58
|
+
end
|
data/examples/bot.apr
ADDED
@@ -0,0 +1,23 @@
|
|
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")
|
5
|
+
|
6
|
+
(defn send [io & parts]
|
7
|
+
(let [msg (apply str parts)]
|
8
|
+
(println "<< " msg)
|
9
|
+
(.print io msg "\r\n")))
|
10
|
+
|
11
|
+
(defn run-bot [nick username realname channel server [port 6667]]
|
12
|
+
(let [irc (TCPSocket. server port)]
|
13
|
+
(send irc "NICK " nick)
|
14
|
+
(send irc "USER " username " * * :" realname)
|
15
|
+
(while-let [line (.gets irc)]
|
16
|
+
(println ">> " line)
|
17
|
+
(when (.start_with? line "PING")
|
18
|
+
(send irc (.sub line "PING" "PONG")))
|
19
|
+
(when (.include? line "001")
|
20
|
+
(send irc "JOIN " channel)
|
21
|
+
(send irc "PRIVMSG " channel " :It works!")))))
|
22
|
+
|
23
|
+
(run-bot "apribot" "apr" "Apribot" "#apricot" "irc.freenode.net")
|
data/examples/hanoi.apr
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
; Print the solution of a towers of hanoi puzzle with n disks using pegs a, b,
|
2
|
+
; and c.
|
3
|
+
(defn hanoi [n a b c]
|
4
|
+
(when-not (zero? n)
|
5
|
+
(hanoi (dec n) a c b)
|
6
|
+
(println "Move disk " n " from peg " a " to peg " c)
|
7
|
+
(hanoi (dec n) b a c)))
|
8
|
+
|
9
|
+
; 5-disk example:
|
10
|
+
(hanoi 5 "A" "B" "C")
|
data/examples/hello.apr
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
(println "Hello, world!")
|
data/examples/plot.apr
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
; Just a quick and dirty graph plotter.
|
2
|
+
|
3
|
+
(defn plot [f x1 x2 xscale y1 y2 yscale]
|
4
|
+
(let [width (.ceil (* (- x2 x1) xscale))
|
5
|
+
height (.ceil (* (- y2 y1) yscale))
|
6
|
+
points (map (fn [x] [(.round (* xscale (- x x1)))
|
7
|
+
(.round (* yscale (- (f x) y1)))])
|
8
|
+
(.step x1 x2 (/ xscale)))
|
9
|
+
graph (Array. (inc height) | #(* " " width))]
|
10
|
+
(each [p points]
|
11
|
+
(let [x (first p)
|
12
|
+
y (second p)]
|
13
|
+
(when (<= 0 y height)
|
14
|
+
(#|.[]=| (graph (- height y)) x "*"))))
|
15
|
+
(each [line graph]
|
16
|
+
(println line))))
|
17
|
+
|
18
|
+
(println "Exponential:")
|
19
|
+
(plot Math/exp 0 5 10 0 20 1)
|
20
|
+
|
21
|
+
(println "Logarithm base 10:")
|
22
|
+
(plot Math/log10 1 15 5 0 1.5 10)
|
23
|
+
|
24
|
+
(println "Sine:")
|
25
|
+
(plot Math/sin 0 (* 4 Math::PI) 5 -1 1 5)
|
26
|
+
|
27
|
+
(println "Tangent:")
|
28
|
+
(plot Math/tan 0 (* 2 Math::PI) 10 -5 5 2)
|
data/examples/quine.apr
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
((fn [x] (println (list x (list (quote quote) x)))) (quote (fn [x] (println (list x (list (quote quote) x))))))
|