shen-ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.rspec +0 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +20 -0
- data/MIT_LICENSE.txt +26 -0
- data/README.md +94 -0
- data/bin/shen_test_suite.rb +9 -0
- data/bin/srrepl +23 -0
- data/lib/kl.rb +7 -0
- data/lib/kl/absvector.rb +12 -0
- data/lib/kl/compiler.rb +253 -0
- data/lib/kl/cons.rb +51 -0
- data/lib/kl/empty_list.rb +12 -0
- data/lib/kl/environment.rb +123 -0
- data/lib/kl/error.rb +4 -0
- data/lib/kl/internal_error.rb +7 -0
- data/lib/kl/lexer.rb +186 -0
- data/lib/kl/primitives/arithmetic.rb +60 -0
- data/lib/kl/primitives/assignments.rb +18 -0
- data/lib/kl/primitives/booleans.rb +17 -0
- data/lib/kl/primitives/error_handling.rb +13 -0
- data/lib/kl/primitives/generic_functions.rb +22 -0
- data/lib/kl/primitives/lists.rb +21 -0
- data/lib/kl/primitives/streams.rb +38 -0
- data/lib/kl/primitives/strings.rb +55 -0
- data/lib/kl/primitives/symbols.rb +17 -0
- data/lib/kl/primitives/time.rb +17 -0
- data/lib/kl/primitives/vectors.rb +30 -0
- data/lib/kl/reader.rb +40 -0
- data/lib/kl/trampoline.rb +14 -0
- data/lib/shen_ruby.rb +7 -0
- data/lib/shen_ruby/version.rb +3 -0
- data/shen-ruby.gemspec +26 -0
- data/shen/README.txt +17 -0
- data/shen/lib/shen_ruby/shen.rb +124 -0
- data/shen/license.txt +34 -0
- data/shen/release/benchmarks/N_queens.shen +45 -0
- data/shen/release/benchmarks/README.shen +14 -0
- data/shen/release/benchmarks/benchmarks.shen +56 -0
- data/shen/release/benchmarks/bigprog +2173 -0
- data/shen/release/benchmarks/br.shen +13 -0
- data/shen/release/benchmarks/einstein.shen +33 -0
- data/shen/release/benchmarks/heatwave.gif +0 -0
- data/shen/release/benchmarks/interpreter.shen +219 -0
- data/shen/release/benchmarks/picture.jpg +0 -0
- data/shen/release/benchmarks/plato.jpg +0 -0
- data/shen/release/benchmarks/powerset.shen +10 -0
- data/shen/release/benchmarks/prime.shen +10 -0
- data/shen/release/benchmarks/short.shen +129 -0
- data/shen/release/benchmarks/text.txt +68 -0
- data/shen/release/k_lambda/core.kl +1002 -0
- data/shen/release/k_lambda/declarations.kl +1021 -0
- data/shen/release/k_lambda/load.kl +94 -0
- data/shen/release/k_lambda/macros.kl +479 -0
- data/shen/release/k_lambda/prolog.kl +1309 -0
- data/shen/release/k_lambda/reader.kl +1058 -0
- data/shen/release/k_lambda/sequent.kl +556 -0
- data/shen/release/k_lambda/sys.kl +582 -0
- data/shen/release/k_lambda/t-star.kl +3493 -0
- data/shen/release/k_lambda/toplevel.kl +223 -0
- data/shen/release/k_lambda/track.kl +208 -0
- data/shen/release/k_lambda/types.kl +455 -0
- data/shen/release/k_lambda/writer.kl +108 -0
- data/shen/release/k_lambda/yacc.kl +280 -0
- data/shen/release/test_programs/Chap13/problems.txt +26 -0
- data/shen/release/test_programs/README.shen +53 -0
- data/shen/release/test_programs/TinyLispFunctions.txt +16 -0
- data/shen/release/test_programs/TinyTypes.shen +55 -0
- data/shen/release/test_programs/binary.shen +24 -0
- data/shen/release/test_programs/bubble_version_1.shen +28 -0
- data/shen/release/test_programs/bubble_version_2.shen +22 -0
- data/shen/release/test_programs/calculator.shen +21 -0
- data/shen/release/test_programs/cartprod.shen +23 -0
- data/shen/release/test_programs/change.shen +25 -0
- data/shen/release/test_programs/classes-defaults.shen +94 -0
- data/shen/release/test_programs/classes-inheritance.shen +100 -0
- data/shen/release/test_programs/classes-typed.shen +74 -0
- data/shen/release/test_programs/classes-untyped.shen +46 -0
- data/shen/release/test_programs/depth_.shen +14 -0
- data/shen/release/test_programs/einstein.shen +33 -0
- data/shen/release/test_programs/fruit_machine.shen +46 -0
- data/shen/release/test_programs/interpreter.shen +219 -0
- data/shen/release/test_programs/metaprog.shen +85 -0
- data/shen/release/test_programs/minim.shen +193 -0
- data/shen/release/test_programs/mutual.shen +11 -0
- data/shen/release/test_programs/n_queens.shen +45 -0
- data/shen/release/test_programs/newton_version_1.shen +33 -0
- data/shen/release/test_programs/newton_version_2.shen +24 -0
- data/shen/release/test_programs/parse.prl +14 -0
- data/shen/release/test_programs/parser.shen +52 -0
- data/shen/release/test_programs/powerset.shen +10 -0
- data/shen/release/test_programs/prime.shen +10 -0
- data/shen/release/test_programs/proof_assistant.shen +81 -0
- data/shen/release/test_programs/proplog_version_1.shen +25 -0
- data/shen/release/test_programs/proplog_version_2.shen +27 -0
- data/shen/release/test_programs/qmachine.shen +67 -0
- data/shen/release/test_programs/red-black.shen +55 -0
- data/shen/release/test_programs/search.shen +56 -0
- data/shen/release/test_programs/semantic_net.shen +44 -0
- data/shen/release/test_programs/spreadsheet.shen +35 -0
- data/shen/release/test_programs/stack.shen +27 -0
- data/shen/release/test_programs/streams.shen +20 -0
- data/shen/release/test_programs/strings.shen +59 -0
- data/shen/release/test_programs/structures-typed.shen +71 -0
- data/shen/release/test_programs/structures-untyped.shen +42 -0
- data/shen/release/test_programs/tests.shen +294 -0
- data/shen/release/test_programs/types.shen +11 -0
- data/shen/release/test_programs/whist.shen +240 -0
- data/shen/release/test_programs/yacc.shen +136 -0
- data/spec/kl/cons_spec.rb +12 -0
- data/spec/kl/environment_spec.rb +306 -0
- data/spec/kl/lexer_spec.rb +149 -0
- data/spec/kl/primitives/generic_functions_spec.rb +29 -0
- data/spec/kl/primitives/symbols_spec.rb +21 -0
- data/spec/kl/reader_spec.rb +36 -0
- data/spec/spec_helper.rb +2 -0
- metadata +189 -0
data/.gitignore
ADDED
data/.rspec
ADDED
File without changes
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
ZenTest (4.8.3)
|
5
|
+
diff-lcs (1.1.3)
|
6
|
+
rspec (2.12.0)
|
7
|
+
rspec-core (~> 2.12.0)
|
8
|
+
rspec-expectations (~> 2.12.0)
|
9
|
+
rspec-mocks (~> 2.12.0)
|
10
|
+
rspec-core (2.12.1)
|
11
|
+
rspec-expectations (2.12.0)
|
12
|
+
diff-lcs (~> 1.1.3)
|
13
|
+
rspec-mocks (2.12.0)
|
14
|
+
|
15
|
+
PLATFORMS
|
16
|
+
ruby
|
17
|
+
|
18
|
+
DEPENDENCIES
|
19
|
+
ZenTest (~> 4.8.3)
|
20
|
+
rspec (~> 2.12.0)
|
data/MIT_LICENSE.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
This license applies to all of ShenRuby except for the portions found under
|
2
|
+
the 'shen' directory, which is subject to its own license.
|
3
|
+
|
4
|
+
-----
|
5
|
+
|
6
|
+
Copyright (c) 2012 Greg Spurrier
|
7
|
+
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
9
|
+
a copy of this software and associated documentation files (the
|
10
|
+
"Software"), to deal in the Software without restriction, including
|
11
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
12
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
13
|
+
permit persons to whom the Software is furnished to do so, subject to
|
14
|
+
the following conditions:
|
15
|
+
|
16
|
+
The above copyright notice and this permission notice shall be
|
17
|
+
included in all copies or substantial portions of the Software.
|
18
|
+
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
20
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
21
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
22
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
23
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
24
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
25
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
|
data/README.md
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# ShenRuby
|
2
|
+
ShenRuby is a Ruby port of the [Shen](http://shenlanguage.org/) programming language. Shen is a modern, functional Lisp that supports pattern matching, currying, and optional static type checking.
|
3
|
+
|
4
|
+
ShenRuby supports Shen version 7.1, which was released in December, 2012.
|
5
|
+
|
6
|
+
The ShenRuby project has two primary goals. The first is to be a low barrier-to-entry means for Rubyists to explore Shen. To someone with a working installation of Ruby 1.9.3, a Shen REPL is only a gem install away.
|
7
|
+
|
8
|
+
Second, ShenRuby aims to enable hybrid applications implemented using a combination of Ruby and Shen. Ruby methods should be able to invoke functions written in Shen and vice versa. Performance is a secondary part of this goal. It should be good enough that, for most tasks, the choice between Ruby and Shen is based primarily on which language is best suited for solving the problem at hand.
|
9
|
+
|
10
|
+
ShenRuby 0.1.0 begins to satisfy the first goal by providing a Shen REPL accessible from the command line. The second goal is more ambitious and is the subject of ongoing work leading to the eventual 1.0.0 release.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
NOTE: ShenRuby requires Ruby 1.9 language features. It has been tested with Ruby 1.9.3 and, to a lesser extent, Rubnius in 1.9 mode. It it not yet passing the Shen Test Suite under JRuby.
|
14
|
+
|
15
|
+
ShenRuby 0.1.0 is the current release. To install it as gem, use the following command:
|
16
|
+
|
17
|
+
gem install shen-ruby
|
18
|
+
|
19
|
+
## Getting started with the Shen REPL
|
20
|
+
|
21
|
+
Once the gem has been installed, the Shen REPL can be launched via the `srrepl` (short for ShenRuby REPL) command. For example:
|
22
|
+
|
23
|
+
|
24
|
+
% srrepl
|
25
|
+
Loading Shen.... Completed in 18.61 seconds.
|
26
|
+
|
27
|
+
Shen 2010, copyright (C) 2010 Mark Tarver
|
28
|
+
www.shenlanguage.org, version 7.1
|
29
|
+
running under Ruby, implementation: ruby 1.9.3
|
30
|
+
port 0.1.0 ported by Greg Spurrier
|
31
|
+
|
32
|
+
|
33
|
+
(0-)
|
34
|
+
|
35
|
+
Please be patient: the Shen REPL takes a while to load (about 20 seconds on a 2.66 GHz MacBook Pro). This will be addressed in future releases.
|
36
|
+
|
37
|
+
The `(0-)` seen above is the Shen REPL prompt. The number in the prompt increases after each expression that is entered.
|
38
|
+
|
39
|
+
Here is an example of defining a recursive factorial function via the REPL and trying it out:
|
40
|
+
|
41
|
+
(0-) (define factorial
|
42
|
+
0 -> 1
|
43
|
+
X -> (* X (factorial (- X 1))))
|
44
|
+
factorial
|
45
|
+
|
46
|
+
(1-) (factorial 5)
|
47
|
+
120
|
48
|
+
|
49
|
+
(2-) (factorial 20)
|
50
|
+
2432902008176640000
|
51
|
+
|
52
|
+
(3-) (factorial 100)
|
53
|
+
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
|
54
|
+
|
55
|
+
Shen functions are defined in terms of pattern matching rules. Above we say that the factorial of 0 is 1 and that the factorial of anything else--represented as the variable `X`--is `X` times the factorial of `(X - 1)`. This is very similar to how factorial would be described via mathematical equations.
|
56
|
+
|
57
|
+
As can be seen with `(factorial 100)`, ShenRuby uses Ruby's underlying number system and supports arbitrarily large integers.
|
58
|
+
|
59
|
+
For a quick tour of Shen via the REPL, please see the [Shen in 15 Minutes](http://www.shenlanguage.org/learn-shen/tutorials/shen_in_15mins.html) tutorial on the Shen Website. To learn more about using the Shen REPL itself, please see the [REPL](http://www.shenlanguage.org/learn-shen/repl.html) documentation, also on the Shen Website. Additional resources for learning about Shen are listed in the Shen Resources section below.
|
60
|
+
|
61
|
+
To exit the Shen REPL, execute the `quit` function:
|
62
|
+
|
63
|
+
(4-) (quit)
|
64
|
+
%
|
65
|
+
|
66
|
+
## Shen Resources
|
67
|
+
|
68
|
+
The following resources may be helpful for those wanting to learn more about the Shen programming language:
|
69
|
+
|
70
|
+
- [The Shen Website](http://shenlanguage.org/)
|
71
|
+
- [Learn Shen](http://www.shenlanguage.org/learn-shen/index.html) -- The Shen Website's suggested resources for--and approaches to--learning Shen, including the [Shen in 15 Minutes](http://www.shenlanguage.org/learn-shen/tutorials/shen_in_15mins.html) tutorial for experienced functional programmers.
|
72
|
+
- [The Shen Official Standard](http://www.shenlanguage.org/Documentation/shendoc.htm)
|
73
|
+
- [System Functions and their Types in Shen](http://www.shenlanguage.org/learn-shen/system.pdf) -- A reference for all of the standard Shen functions and their types.
|
74
|
+
- [The Book of Shen](http://www.shenlanguage.org/tbos.html) -- The official guide to the Shen programming language. The first printing quickly sold out, but another printing is expected in January, 2013. See this [discussion](https://groups.google.com/d/topic/qilang/V8so3Rirkk0/discussion) on the Shen News Group for the most up-to-date information.
|
75
|
+
- [Shen Google Group](https://groups.google.com/group/qilang?hl=en) -- This is the online forum for discussions related to Shen. Don't be confused by the group's name (Qilang). Qi was the predecessor of Shen and the group retains its name.
|
76
|
+
|
77
|
+
## Road Map to 1.0
|
78
|
+
|
79
|
+
The following features and improvements are among those planned for ShenRuby as it approaches its 1.0 release:
|
80
|
+
|
81
|
+
- Ability to call Shen functions directly from Ruby
|
82
|
+
- Ability to call Ruby methods directly from Shen
|
83
|
+
- Support for command-line Shen scripts that under ShenRuby
|
84
|
+
- Support for MRI, JRuby, and Rubinius
|
85
|
+
- Improved performance
|
86
|
+
|
87
|
+
## Known Limitations
|
88
|
+
|
89
|
+
The "Qi interpreter - chapter 13" test case in the Shen Test Suite and some of the benchmarks are currently failing with stack overflow errors.
|
90
|
+
|
91
|
+
## License
|
92
|
+
Shen is Copyright (c) 2010-2012 Mark Tarver and released under the Shen License. A copy of the Shen License may be found in [shen/license.txt](https://github.com/gregspurrier/shen-ruby/blob/master/shen/license.txt). A detailed description of the license, along with questions and answers, may be found at http://shenlanguage.org/license.html. The entire contents of the [shen directory](https://github.com/gregspurrier/shen-ruby/tree/master/shen), including the implementation of the `ShenRuby::Shen` class, is part of Shen and is subject to the Shen license.
|
93
|
+
|
94
|
+
The remainder of ShenRuby--i.e., everything outside of the shen directory--is Copyright (c) 2012 Greg Spurrier and released under the MIT License. A copy of the MIT License may be found in [MIT_LICENSE.txt](https://github.com/gregspurrier/shen-ruby/blob/master/MIT_LICENSE.txt).
|
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH << File.expand_path('../../lib', __FILE__)
|
3
|
+
$LOAD_PATH << File.expand_path('../../shen/lib', __FILE__)
|
4
|
+
require 'shen_ruby'
|
5
|
+
|
6
|
+
shen = ShenRuby::Shen.new
|
7
|
+
shen.__eval(Kl::Cons.list([:cd, "shen/release/test_programs"]))
|
8
|
+
shen.__eval(Kl::Cons.list([:load, "README.shen"]))
|
9
|
+
shen.__eval(Kl::Cons.list([:load, "tests.shen"]))
|
data/bin/srrepl
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'shen_ruby'
|
3
|
+
|
4
|
+
# Load the Shen Envinronment
|
5
|
+
print "Loading..."
|
6
|
+
STDOUT.flush
|
7
|
+
start = Time.now.to_f
|
8
|
+
shen = ShenRuby::Shen.new
|
9
|
+
now = Time.now.to_f
|
10
|
+
puts ". Completed in %0.2f seconds.\n" % (now - start)
|
11
|
+
|
12
|
+
# Launch the REPL
|
13
|
+
command = :"shen-shen"
|
14
|
+
begin
|
15
|
+
shen.__eval(Kl::Cons.list([command]))
|
16
|
+
rescue StandardError => e
|
17
|
+
# K Lambda simple errors are already handled by the Shen REPL. Therefore
|
18
|
+
# this must be another type of exception. Print it as such and reenter
|
19
|
+
# the REPL without re-display the initial credits.
|
20
|
+
puts "Ruby exception: #{e.message}"
|
21
|
+
command = :"shen-loop"
|
22
|
+
retry
|
23
|
+
end
|
data/lib/kl.rb
ADDED
data/lib/kl/absvector.rb
ADDED
data/lib/kl/compiler.rb
ADDED
@@ -0,0 +1,253 @@
|
|
1
|
+
module Kl
|
2
|
+
module Compiler
|
3
|
+
class << self
|
4
|
+
def compile(form, lexical_vars, in_tail_pos)
|
5
|
+
case form
|
6
|
+
when Symbol
|
7
|
+
if lexical_vars.has_key?(form)
|
8
|
+
lexical_vars[form]
|
9
|
+
else
|
10
|
+
escape_symbol(form)
|
11
|
+
end
|
12
|
+
when String
|
13
|
+
# Emit non-interpolating strings
|
14
|
+
"'" + escape_string(form) + "'"
|
15
|
+
when Kl::Cons
|
16
|
+
compile_form(form, lexical_vars, in_tail_pos)
|
17
|
+
when Kl::EmptyList
|
18
|
+
"::Kl::EmptyList.instance"
|
19
|
+
when Numeric
|
20
|
+
form.to_s
|
21
|
+
when true
|
22
|
+
'true'
|
23
|
+
when false
|
24
|
+
'false'
|
25
|
+
else
|
26
|
+
raise Kl::InternalError, "unexpected form: #{form}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def compile_form(form, lexical_vars, in_tail_pos)
|
32
|
+
case form.hd
|
33
|
+
when :defun
|
34
|
+
compile_defun(form, lexical_vars)
|
35
|
+
when :lambda
|
36
|
+
compile_lambda(form, lexical_vars)
|
37
|
+
when :let
|
38
|
+
compile_let(form, lexical_vars, in_tail_pos)
|
39
|
+
when :freeze
|
40
|
+
compile_freeze(form, lexical_vars)
|
41
|
+
when :type
|
42
|
+
compile_type(form, lexical_vars, in_tail_pos)
|
43
|
+
when :if
|
44
|
+
compile_if(form, lexical_vars, in_tail_pos)
|
45
|
+
when :and
|
46
|
+
compile_and(form, lexical_vars, in_tail_pos)
|
47
|
+
when :or
|
48
|
+
compile_or(form, lexical_vars, in_tail_pos)
|
49
|
+
when :cond
|
50
|
+
compile_cond(form, lexical_vars, in_tail_pos)
|
51
|
+
when :"trap-error"
|
52
|
+
compile_trap_error(form, lexical_vars, in_tail_pos)
|
53
|
+
else
|
54
|
+
compile_application(form, lexical_vars, in_tail_pos)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# (defun NAME ARGS BODY)
|
59
|
+
def compile_defun(form, lexical_vars)
|
60
|
+
name, arglist, body = destructure_form(form, 3)
|
61
|
+
unless name.kind_of? Symbol
|
62
|
+
raise Kl::Error, 'first argument to defun must be a symbol'
|
63
|
+
end
|
64
|
+
unless arglist.all? {|a| a.kind_of? Symbol}
|
65
|
+
raise Kl::Error, 'function argument list may only contain symbols'
|
66
|
+
end
|
67
|
+
|
68
|
+
extended_vars = add_vars(lexical_vars, arglist.to_a)
|
69
|
+
|
70
|
+
fn_name = escape_symbol(name)
|
71
|
+
fn_args = arglist.map { |arg| extended_vars[arg] }.join(",")
|
72
|
+
fn_arity = arglist.count
|
73
|
+
fn_body = compile(body, extended_vars, true)
|
74
|
+
|
75
|
+
"(@eigenklass.send(:define_method, #{fn_name}, ::Kernel.lambda { |#{fn_args}| #{fn_body}}); @arity_cache[#{fn_name}] = #{fn_arity}; #{fn_name})"
|
76
|
+
end
|
77
|
+
|
78
|
+
# (lambda VAR BODY)
|
79
|
+
def compile_lambda(form, lexical_vars)
|
80
|
+
var, body = destructure_form(form, 2)
|
81
|
+
unless var.kind_of? Symbol
|
82
|
+
raise Kl::Error, 'first argument to lambda must be a symbol'
|
83
|
+
end
|
84
|
+
|
85
|
+
extended_vars = add_var(lexical_vars, var)
|
86
|
+
fn_arg = extended_vars[var]
|
87
|
+
fn_body = compile(body, extended_vars, true)
|
88
|
+
|
89
|
+
"::Kernel.lambda { |#{fn_arg}| #{fn_body}}"
|
90
|
+
end
|
91
|
+
|
92
|
+
# (let VAR EXPR BODY)
|
93
|
+
def compile_let(form, lexical_vars, in_tail_pos)
|
94
|
+
var, expr, body = destructure_form(form, 3)
|
95
|
+
unless var.kind_of? Symbol
|
96
|
+
raise Kl::Error, 'first argument to let must be a symbol'
|
97
|
+
end
|
98
|
+
|
99
|
+
extended_vars = add_var(lexical_vars, var)
|
100
|
+
bound_var = extended_vars[var]
|
101
|
+
# The bound expression is evaluated in the original lexical scope
|
102
|
+
bound_expr = compile(expr, lexical_vars, false)
|
103
|
+
let_body = compile(body, extended_vars, in_tail_pos)
|
104
|
+
|
105
|
+
"(#{bound_var} = #{bound_expr}; #{let_body})"
|
106
|
+
end
|
107
|
+
|
108
|
+
# (freeze EXPR)
|
109
|
+
def compile_freeze(form, lexical_vars)
|
110
|
+
expr = destructure_form(form, 1).first
|
111
|
+
|
112
|
+
body = compile(expr, lexical_vars, true)
|
113
|
+
|
114
|
+
"::Kernel.lambda { #{body} }"
|
115
|
+
end
|
116
|
+
|
117
|
+
# (type EXPR T)
|
118
|
+
def compile_type(form, lexical_vars, in_tail_pos)
|
119
|
+
# Just ignore the type information for now
|
120
|
+
expr, _ = destructure_form(form, 2)
|
121
|
+
compile(expr, lexical_vars, in_tail_pos)
|
122
|
+
end
|
123
|
+
|
124
|
+
# (if TEST_EXPR TRUE_EXPR FALSE_EXPR)
|
125
|
+
def compile_if(form, lexical_vars, in_tail_pos)
|
126
|
+
test_expr, on_true_expr, on_false_expr = destructure_form(form, 3)
|
127
|
+
|
128
|
+
test_clause = compile(test_expr, lexical_vars, false)
|
129
|
+
true_clause = compile(on_true_expr, lexical_vars, in_tail_pos)
|
130
|
+
false_clause = compile(on_false_expr, lexical_vars, in_tail_pos)
|
131
|
+
|
132
|
+
"(#{test_clause} ? #{true_clause} : #{false_clause})"
|
133
|
+
end
|
134
|
+
|
135
|
+
# (and EXPR1 EXPR2)
|
136
|
+
def compile_and(form, lexical_vars, in_tail_pos)
|
137
|
+
expr1, expr2 = destructure_form(form, 2)
|
138
|
+
compile_if(Kl::Cons.list([:if, expr1, expr2, false]),
|
139
|
+
lexical_vars, in_tail_pos)
|
140
|
+
end
|
141
|
+
|
142
|
+
# (or EXPR1 EXPR2)
|
143
|
+
def compile_or(form, lexical_vars, in_tail_pos)
|
144
|
+
expr1, expr2 = destructure_form(form, 2)
|
145
|
+
compile_if(Kl::Cons.list([:if, expr1, true, expr2]),
|
146
|
+
lexical_vars, in_tail_pos)
|
147
|
+
end
|
148
|
+
|
149
|
+
def compile_cond(form, lexical_vars, in_tail_pos)
|
150
|
+
clauses = form.tl
|
151
|
+
if clauses.kind_of? Kl::EmptyList
|
152
|
+
'raise(::Kl::Error, "condition failure")'
|
153
|
+
else
|
154
|
+
clause = clauses.hd
|
155
|
+
test_expr = clause.hd
|
156
|
+
true_expr = clause.tl.hd
|
157
|
+
false_expr = Kl::Cons.new(:cond, clauses.tl)
|
158
|
+
compile_if(Kl::Cons.list([:if, test_expr, true_expr, false_expr]),
|
159
|
+
lexical_vars,
|
160
|
+
in_tail_pos)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# (trap-error EXPR ERR_HANDLER)
|
165
|
+
def compile_trap_error(form, lexical_vars, in_tail_pos)
|
166
|
+
expr, err_handler = destructure_form(form, 2)
|
167
|
+
|
168
|
+
# TODO: TCO for try and catch clauses
|
169
|
+
try_clause = compile(expr, lexical_vars, false)
|
170
|
+
extended_vars = add_var(lexical_vars, :err)
|
171
|
+
err_var = extended_vars[:err]
|
172
|
+
catch_clause = compile_application(
|
173
|
+
Kl::Cons.list([err_handler, :err]),
|
174
|
+
extended_vars,
|
175
|
+
false)
|
176
|
+
|
177
|
+
"(begin; #{try_clause}; rescue ::Kl::Error => #{err_var}; " +
|
178
|
+
"#{catch_clause}; end)"
|
179
|
+
end
|
180
|
+
|
181
|
+
# Normal function application
|
182
|
+
def compile_application(form, lexical_vars, in_tail_pos)
|
183
|
+
f = form.hd
|
184
|
+
args = form.tl
|
185
|
+
|
186
|
+
rator = compile(f, lexical_vars, false)
|
187
|
+
rands = args.map { |arg| compile(arg, lexical_vars, false) }.join(',')
|
188
|
+
|
189
|
+
tfn = gen_sym
|
190
|
+
targs = gen_sym
|
191
|
+
|
192
|
+
if in_tail_pos
|
193
|
+
"(
|
194
|
+
#{tfn} = #{rator};
|
195
|
+
#{targs} = [#{rands}];
|
196
|
+
@tramp_fn = #{tfn};
|
197
|
+
@tramp_args = #{targs}
|
198
|
+
)"
|
199
|
+
else
|
200
|
+
"__apply(#{rator}, [#{rands}])"
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Escape single quotes and backslashes
|
205
|
+
def escape_string(str)
|
206
|
+
new_str = ""
|
207
|
+
str.each_char do |c|
|
208
|
+
if c == "'"
|
209
|
+
new_str << "\\"
|
210
|
+
new_str << "'"
|
211
|
+
elsif c == '\\'
|
212
|
+
new_str << '\\'
|
213
|
+
new_str << '\\'
|
214
|
+
else
|
215
|
+
new_str << c
|
216
|
+
end
|
217
|
+
end
|
218
|
+
new_str
|
219
|
+
end
|
220
|
+
|
221
|
+
def escape_symbol(sym)
|
222
|
+
':"' + sym.to_s + '"'
|
223
|
+
end
|
224
|
+
|
225
|
+
def destructure_form(form, expected_arg_count)
|
226
|
+
array = form.to_a
|
227
|
+
unless array.length == expected_arg_count + 1
|
228
|
+
raise Kl::Error, "#{form.first} expects #{expected_arg_count} " +
|
229
|
+
"arguments but was given #{array.length - 1}"
|
230
|
+
end
|
231
|
+
array[1..-1]
|
232
|
+
end
|
233
|
+
|
234
|
+
def add_var(scope, var)
|
235
|
+
add_vars(scope, [var])
|
236
|
+
end
|
237
|
+
|
238
|
+
def add_vars(scope, vars)
|
239
|
+
scope.dup.tap do |new_scope|
|
240
|
+
vars.each do |var|
|
241
|
+
new_scope[var] = gen_sym
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def gen_sym
|
247
|
+
@sym_count ||= 0
|
248
|
+
@sym_count += 1
|
249
|
+
"__kl_VAR_#{@sym_count}"
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|