shen-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. data/.gitignore +4 -0
  2. data/.rspec +0 -0
  3. data/Gemfile +6 -0
  4. data/Gemfile.lock +20 -0
  5. data/MIT_LICENSE.txt +26 -0
  6. data/README.md +94 -0
  7. data/bin/shen_test_suite.rb +9 -0
  8. data/bin/srrepl +23 -0
  9. data/lib/kl.rb +7 -0
  10. data/lib/kl/absvector.rb +12 -0
  11. data/lib/kl/compiler.rb +253 -0
  12. data/lib/kl/cons.rb +51 -0
  13. data/lib/kl/empty_list.rb +12 -0
  14. data/lib/kl/environment.rb +123 -0
  15. data/lib/kl/error.rb +4 -0
  16. data/lib/kl/internal_error.rb +7 -0
  17. data/lib/kl/lexer.rb +186 -0
  18. data/lib/kl/primitives/arithmetic.rb +60 -0
  19. data/lib/kl/primitives/assignments.rb +18 -0
  20. data/lib/kl/primitives/booleans.rb +17 -0
  21. data/lib/kl/primitives/error_handling.rb +13 -0
  22. data/lib/kl/primitives/generic_functions.rb +22 -0
  23. data/lib/kl/primitives/lists.rb +21 -0
  24. data/lib/kl/primitives/streams.rb +38 -0
  25. data/lib/kl/primitives/strings.rb +55 -0
  26. data/lib/kl/primitives/symbols.rb +17 -0
  27. data/lib/kl/primitives/time.rb +17 -0
  28. data/lib/kl/primitives/vectors.rb +30 -0
  29. data/lib/kl/reader.rb +40 -0
  30. data/lib/kl/trampoline.rb +14 -0
  31. data/lib/shen_ruby.rb +7 -0
  32. data/lib/shen_ruby/version.rb +3 -0
  33. data/shen-ruby.gemspec +26 -0
  34. data/shen/README.txt +17 -0
  35. data/shen/lib/shen_ruby/shen.rb +124 -0
  36. data/shen/license.txt +34 -0
  37. data/shen/release/benchmarks/N_queens.shen +45 -0
  38. data/shen/release/benchmarks/README.shen +14 -0
  39. data/shen/release/benchmarks/benchmarks.shen +56 -0
  40. data/shen/release/benchmarks/bigprog +2173 -0
  41. data/shen/release/benchmarks/br.shen +13 -0
  42. data/shen/release/benchmarks/einstein.shen +33 -0
  43. data/shen/release/benchmarks/heatwave.gif +0 -0
  44. data/shen/release/benchmarks/interpreter.shen +219 -0
  45. data/shen/release/benchmarks/picture.jpg +0 -0
  46. data/shen/release/benchmarks/plato.jpg +0 -0
  47. data/shen/release/benchmarks/powerset.shen +10 -0
  48. data/shen/release/benchmarks/prime.shen +10 -0
  49. data/shen/release/benchmarks/short.shen +129 -0
  50. data/shen/release/benchmarks/text.txt +68 -0
  51. data/shen/release/k_lambda/core.kl +1002 -0
  52. data/shen/release/k_lambda/declarations.kl +1021 -0
  53. data/shen/release/k_lambda/load.kl +94 -0
  54. data/shen/release/k_lambda/macros.kl +479 -0
  55. data/shen/release/k_lambda/prolog.kl +1309 -0
  56. data/shen/release/k_lambda/reader.kl +1058 -0
  57. data/shen/release/k_lambda/sequent.kl +556 -0
  58. data/shen/release/k_lambda/sys.kl +582 -0
  59. data/shen/release/k_lambda/t-star.kl +3493 -0
  60. data/shen/release/k_lambda/toplevel.kl +223 -0
  61. data/shen/release/k_lambda/track.kl +208 -0
  62. data/shen/release/k_lambda/types.kl +455 -0
  63. data/shen/release/k_lambda/writer.kl +108 -0
  64. data/shen/release/k_lambda/yacc.kl +280 -0
  65. data/shen/release/test_programs/Chap13/problems.txt +26 -0
  66. data/shen/release/test_programs/README.shen +53 -0
  67. data/shen/release/test_programs/TinyLispFunctions.txt +16 -0
  68. data/shen/release/test_programs/TinyTypes.shen +55 -0
  69. data/shen/release/test_programs/binary.shen +24 -0
  70. data/shen/release/test_programs/bubble_version_1.shen +28 -0
  71. data/shen/release/test_programs/bubble_version_2.shen +22 -0
  72. data/shen/release/test_programs/calculator.shen +21 -0
  73. data/shen/release/test_programs/cartprod.shen +23 -0
  74. data/shen/release/test_programs/change.shen +25 -0
  75. data/shen/release/test_programs/classes-defaults.shen +94 -0
  76. data/shen/release/test_programs/classes-inheritance.shen +100 -0
  77. data/shen/release/test_programs/classes-typed.shen +74 -0
  78. data/shen/release/test_programs/classes-untyped.shen +46 -0
  79. data/shen/release/test_programs/depth_.shen +14 -0
  80. data/shen/release/test_programs/einstein.shen +33 -0
  81. data/shen/release/test_programs/fruit_machine.shen +46 -0
  82. data/shen/release/test_programs/interpreter.shen +219 -0
  83. data/shen/release/test_programs/metaprog.shen +85 -0
  84. data/shen/release/test_programs/minim.shen +193 -0
  85. data/shen/release/test_programs/mutual.shen +11 -0
  86. data/shen/release/test_programs/n_queens.shen +45 -0
  87. data/shen/release/test_programs/newton_version_1.shen +33 -0
  88. data/shen/release/test_programs/newton_version_2.shen +24 -0
  89. data/shen/release/test_programs/parse.prl +14 -0
  90. data/shen/release/test_programs/parser.shen +52 -0
  91. data/shen/release/test_programs/powerset.shen +10 -0
  92. data/shen/release/test_programs/prime.shen +10 -0
  93. data/shen/release/test_programs/proof_assistant.shen +81 -0
  94. data/shen/release/test_programs/proplog_version_1.shen +25 -0
  95. data/shen/release/test_programs/proplog_version_2.shen +27 -0
  96. data/shen/release/test_programs/qmachine.shen +67 -0
  97. data/shen/release/test_programs/red-black.shen +55 -0
  98. data/shen/release/test_programs/search.shen +56 -0
  99. data/shen/release/test_programs/semantic_net.shen +44 -0
  100. data/shen/release/test_programs/spreadsheet.shen +35 -0
  101. data/shen/release/test_programs/stack.shen +27 -0
  102. data/shen/release/test_programs/streams.shen +20 -0
  103. data/shen/release/test_programs/strings.shen +59 -0
  104. data/shen/release/test_programs/structures-typed.shen +71 -0
  105. data/shen/release/test_programs/structures-untyped.shen +42 -0
  106. data/shen/release/test_programs/tests.shen +294 -0
  107. data/shen/release/test_programs/types.shen +11 -0
  108. data/shen/release/test_programs/whist.shen +240 -0
  109. data/shen/release/test_programs/yacc.shen +136 -0
  110. data/spec/kl/cons_spec.rb +12 -0
  111. data/spec/kl/environment_spec.rb +306 -0
  112. data/spec/kl/lexer_spec.rb +149 -0
  113. data/spec/kl/primitives/generic_functions_spec.rb +29 -0
  114. data/spec/kl/primitives/symbols_spec.rb +21 -0
  115. data/spec/kl/reader_spec.rb +36 -0
  116. data/spec/spec_helper.rb +2 -0
  117. metadata +189 -0
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .rvmrc
2
+ .idea
3
+ *.gem
4
+ *.iml
data/.rspec ADDED
File without changes
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem 'rspec', '~> 2.12.0'
5
+ gem 'ZenTest', '~> 4.8.3'
6
+ end
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
@@ -0,0 +1,7 @@
1
+ require 'kl/error'
2
+ require 'kl/internal_error'
3
+ require 'kl/empty_list'
4
+ require 'kl/cons'
5
+ require 'kl/absvector'
6
+ require 'kl/reader'
7
+ require 'kl/environment'
@@ -0,0 +1,12 @@
1
+ module Kl
2
+ # Absvectors are just arrays. We give them their own subclass
3
+ # to support the absvector? primitive.
4
+ class Absvector < Array
5
+ attr_reader :upper_limit
6
+
7
+ def initialize(n)
8
+ super(n, :"shen-fail!")
9
+ @upper_limit = n
10
+ end
11
+ end
12
+ end
@@ -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