shen-ruby 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/.gitignore +2 -0
  2. data/.travis.yml +5 -0
  3. data/Gemfile +2 -2
  4. data/HISTORY.md +12 -0
  5. data/README.md +10 -7
  6. data/Rakefile +92 -0
  7. data/bin/srrepl +2 -2
  8. data/k_lambda_spec/primitives/arithmetic_spec.rb +175 -0
  9. data/k_lambda_spec/primitives/assignments_spec.rb +44 -0
  10. data/k_lambda_spec/primitives/generic_functions_spec.rb +115 -2
  11. data/k_lambda_spec/primitives/lists_spec.rb +40 -0
  12. data/k_lambda_spec/primitives/strings_spec.rb +77 -0
  13. data/k_lambda_spec/primitives/symbols_spec.rb +24 -0
  14. data/k_lambda_spec/primitives/vectors_spec.rb +92 -0
  15. data/k_lambda_spec/support/shared_examples.rb +93 -2
  16. data/k_lambda_spec/tail_recursion_spec.rb +30 -0
  17. data/lib/kl/compiler.rb +19 -33
  18. data/lib/kl/environment.rb +1 -0
  19. data/lib/kl/primitives/assignments.rb +1 -0
  20. data/lib/kl/primitives/generic_functions.rb +7 -0
  21. data/lib/kl/primitives/lists.rb +2 -0
  22. data/lib/kl/primitives/strings.rb +13 -5
  23. data/lib/kl/primitives/symbols.rb +1 -0
  24. data/lib/kl/primitives/vectors.rb +5 -0
  25. data/lib/shen_ruby/version.rb +1 -1
  26. data/shen-ruby.gemspec +1 -1
  27. data/shen/lib/shen_ruby/shen.rb +5 -6
  28. data/shen/release/benchmarks/benchmarks.shen +0 -4
  29. data/shen/release/benchmarks/interpreter.shen +2 -2
  30. data/shen/release/benchmarks/plato.jpg +0 -0
  31. data/shen/release/k_lambda/core.kl +171 -1000
  32. data/shen/release/k_lambda/declarations.kl +90 -992
  33. data/shen/release/k_lambda/load.kl +69 -81
  34. data/shen/release/k_lambda/macros.kl +113 -478
  35. data/shen/release/k_lambda/prolog.kl +250 -1307
  36. data/shen/release/k_lambda/reader.kl +115 -996
  37. data/shen/release/k_lambda/sequent.kl +154 -554
  38. data/shen/release/k_lambda/sys.kl +246 -562
  39. data/shen/release/k_lambda/t-star.kl +114 -3643
  40. data/shen/release/k_lambda/toplevel.kl +136 -221
  41. data/shen/release/k_lambda/track.kl +101 -206
  42. data/shen/release/k_lambda/types.kl +143 -298
  43. data/shen/release/k_lambda/writer.kl +93 -106
  44. data/shen/release/k_lambda/yacc.kl +77 -252
  45. data/shen/release/test_programs/README.shen +1 -1
  46. data/shen/release/test_programs/classes-typed.shen +1 -1
  47. data/shen/release/test_programs/interpreter.shen +2 -2
  48. data/shen/release/test_programs/metaprog.shen +2 -2
  49. data/shen/release/test_programs/prolog.shen +79 -0
  50. data/shen/release/test_programs/structures-typed.shen +2 -2
  51. data/shen/release/test_programs/tests.shen +19 -80
  52. data/shen/release/test_programs/yacc.shen +11 -15
  53. metadata +14 -6
  54. data/Gemfile.lock +0 -20
  55. data/shen/release/benchmarks/br.shen +0 -13
data/.gitignore CHANGED
@@ -2,3 +2,5 @@
2
2
  .idea
3
3
  *.gem
4
4
  *.iml
5
+ .rbx
6
+ Gemfile.lock
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - rbx-nightly-19mode
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  group :development do
4
+ gem 'rake', '~> 0.9'
4
5
  gem 'rspec', '~> 2.12.0'
5
- gem 'ZenTest', '~> 4.8.3'
6
6
  end
data/HISTORY.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # ShenRuby Release History
2
2
 
3
+ ## 0.4.0 - March 15, 2013
4
+ ### Features
5
+ - Upgrade to Shen 9.0
6
+ ### Bug Fixes
7
+ - [Issue 8](https://github.com/gregspurrier/shen-ruby/issues/8) -- `quit` no longer raises a type error when invoked with type checking enabled.
8
+ - Fixed many instances of ShenRuby's behavior not matching the Shen CLisp reference implementation:
9
+ - `absvector`, `address->`, `<-address`, `intern`, `n->string`, `pos`, `string->n`, and `tlstr` now throw errors catchable by `trap-error` when type violations occur with their arguments.
10
+ - `tlstr` now raises an error when applied to an empty string
11
+ - `set` and `value` now require their first arugment to be a symbol
12
+ - `hd` and `tl` now raise an error when applied to non-lists
13
+ - `freeze` now supports partial application with zero arguments
14
+
3
15
  ## 0.3.1 - January 24, 2013
4
16
  - No code changes. Updates Gemspec to refer to Shen 8.0.
5
17
 
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # ShenRuby
2
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
3
 
4
- ShenRuby supports Shen version 8.0, which was released in January, 2013.
4
+ ShenRuby supports Shen version 9.0, which was released in March, 2013.
5
5
 
6
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
7
 
@@ -9,10 +9,12 @@ Second, ShenRuby aims to enable hybrid applications implemented using a combinat
9
9
 
10
10
  ShenRuby 0.1.0 began 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 beginning with the 0.2.0 release and leading up to the eventual 1.0.0 release.
11
11
 
12
+ [![Build Status](https://travis-ci.org/gregspurrier/shen-ruby.png)](https://travis-ci.org/gregspurrier/shen-ruby)
13
+
12
14
  ## Installation
13
- NOTE: ShenRuby requires Ruby 1.9 language features. It has been tested with Ruby 1.9.3-p362. It is not yet working under JRuby or Rubinius.
15
+ NOTE: ShenRuby requires Ruby 1.9 language features. It has been tested with Ruby 1.9.3-p392. It has been lightly tested with Rubinius 2.0.0-head running in 1.9 mode. It is not yet working under JRuby.
14
16
 
15
- ShenRuby 0.3.1 is the current release. To install it as gem, use the following command:
17
+ ShenRuby 0.4.0 is the current release. To install it as gem, use the following command:
16
18
 
17
19
  gem install shen-ruby
18
20
 
@@ -24,9 +26,9 @@ Once the gem has been installed, the Shen REPL can be launched via the `srrepl`
24
26
  Loading.... Completed in 10.29 seconds.
25
27
 
26
28
  Shen 2010, copyright (C) 2010 Mark Tarver
27
- www.shenlanguage.org, version 8
29
+ www.shenlanguage.org, version 9
28
30
  running under Ruby, implementation: ruby 1.9.3
29
- port 0.3.1 ported by Greg Spurrier
31
+ port 0.4.0 ported by Greg Spurrier
30
32
 
31
33
 
32
34
  (0-)
@@ -137,7 +139,7 @@ The following resources may be helpful for those wanting to learn more about the
137
139
  - [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.
138
140
  - [The Shen Official Standard](http://www.shenlanguage.org/Documentation/shendoc.htm)
139
141
  - [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.
140
- - [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.
142
+ - [The Book of Shen](http://www.fast-print.net/bookshop/1278/the-book-of-shen) -- The official guide to the Shen programming language.
141
143
  - [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.
142
144
 
143
145
  ## Road Map to 1.0
@@ -156,8 +158,9 @@ The following features and improvements are among those planned for ShenRuby as
156
158
  - ShenRuby fails to load under JRuby ([Issue #6](https://github.com/gregspurrier/shen-ruby/issues/6)) and Rubinius ([Issue #])(https://github.com/gregspurrier/shen-ruby/issues/7)].
157
159
 
158
160
  ## Contributors
159
- The following people are gratefully acknowledged for their contributions of code to ShenRuby:
161
+ The following people are gratefully acknowledged for their contributions to ShenRuby:
160
162
 
163
+ - Brian Shirai
161
164
  - Bruno Deferrari
162
165
 
163
166
  ## License
@@ -0,0 +1,92 @@
1
+ require 'rubygems'
2
+ require 'rspec/core/rake_task'
3
+
4
+ SHENRUBY_ROOT = File.expand_path(File.dirname(__FILE__))
5
+
6
+ # Run implementation specs
7
+ RSpec::Core::RakeTask.new(:spec)
8
+
9
+ # Run K Lambda specs
10
+ RSpec::Core::RakeTask.new(:k_lambda_spec) do |t|
11
+ t.rspec_opts = '-I k_lambda_spec'
12
+ t.pattern = 'k_lambda_spec/**/*_spec.rb'
13
+ end
14
+
15
+ # Import Shen Release
16
+ RELEASE_DIR = File.join(SHENRUBY_ROOT, 'shen/release')
17
+ IMPORT_DIR = File.join(SHENRUBY_ROOT, 'import')
18
+ SHEN_ZIP = File.join(IMPORT_DIR, 'Shen.zip')
19
+
20
+ namespace :shen do
21
+ namespace :import do
22
+ task :release => [:remove_old_release, :import_dirs, :cleanup]
23
+ task :import_dirs => [:k_lambda, :test_programs, :benchmarks]
24
+
25
+ task :remove_old_release do
26
+ rm_rf IMPORT_DIR
27
+ rm_rf RELEASE_DIR
28
+ end
29
+
30
+ task :cleanup do
31
+ rm_r IMPORT_DIR
32
+ end
33
+
34
+ directory IMPORT_DIR
35
+ directory RELEASE_DIR
36
+
37
+ file SHEN_ZIP => [IMPORT_DIR] do
38
+ sh "curl -s http://www.shenlanguage.org/Download/Shen.zip > '#{SHEN_ZIP}'"
39
+ end
40
+
41
+ task :unzip => SHEN_ZIP do
42
+ sh "(cd #{IMPORT_DIR}; unzip Shen.zip)"
43
+ end
44
+
45
+ def dst_path(src_root, src_path, dst_root)
46
+ path_tail = src_path[src_root.length..-1]
47
+ File.join(dst_root, path_tail.gsub(/[\s ']/, '_'))
48
+ end
49
+
50
+ def import_dir(dirname)
51
+ src_root = Dir.glob(File.join('import/Shen *', dirname)).first
52
+ dst_root = File.join(RELEASE_DIR, dirname.downcase.gsub(/\s/, '_'))
53
+
54
+ src_paths = Dir.glob(File.join(src_root, '**/*'))
55
+ src_dirs, src_files = src_paths.partition { |p| File.directory?(p) }
56
+
57
+ mkdir_p dst_root
58
+ src_dirs.each { |dir| mkdir_p dst_path(src_root, dir, dst_root) }
59
+ src_files.each { |file| cp file, dst_path(src_root, file, dst_root) }
60
+ end
61
+
62
+ def fix_load_paths(path)
63
+ lines = File.readlines(path)
64
+ File.open(path, 'w') do |f|
65
+ lines.each do |l|
66
+ if l =~ /\(load\s+"([^"]+)"\)/
67
+ path = $1
68
+ new_path = path.gsub(/[\s']/, '_')
69
+ l.sub!(path, new_path)
70
+ end
71
+ f.puts l
72
+ end
73
+ end
74
+ end
75
+
76
+ task :k_lambda => [:unzip, RELEASE_DIR] do
77
+ import_dir('K Lambda')
78
+ end
79
+
80
+ task :test_programs => [:unzip, RELEASE_DIR] do
81
+ import_dir('Test Programs')
82
+ fix_load_paths(File.join(RELEASE_DIR, 'test_programs/tests.shen'))
83
+ end
84
+
85
+ task :benchmarks => [:unzip, RELEASE_DIR] do
86
+ import_dir('Benchmarks')
87
+ fix_load_paths(File.join(RELEASE_DIR, 'benchmarks/benchmarks.shen'))
88
+ end
89
+ end
90
+ end
91
+
92
+ task :default => [:spec, :k_lambda_spec]
data/bin/srrepl CHANGED
@@ -23,7 +23,7 @@ class ReplInterrupt < StandardError; end
23
23
  Signal.trap("INT") { raise ReplInterrupt }
24
24
 
25
25
  # Launch the REPL
26
- command = :"shen-shen"
26
+ command = :"shen.shen"
27
27
  begin
28
28
  shen.__eval(Kl::Cons.list([command]))
29
29
  rescue StandardError => e
@@ -35,6 +35,6 @@ rescue StandardError => e
35
35
  else
36
36
  puts "Ruby exception: #{e.message}"
37
37
  end
38
- command = :"shen-loop"
38
+ command = :"shen.loop"
39
39
  retry
40
40
  end
@@ -0,0 +1,175 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Primitives for Arithmetic' do
4
+ describe '+' do
5
+ it 'adds its arguments' do
6
+ kl_eval('(+ 1 2)').should == 3
7
+ end
8
+
9
+ it 'returns an integer when both arguments are integers' do
10
+ kl_eval('(+ 1 2)').should be_kind_of Fixnum
11
+ end
12
+
13
+ it ('returns a real when either argument is a real') do
14
+ kl_eval('(+ 1.0 2)').should be_kind_of Float
15
+ kl_eval('(+ 1 2.0)').should be_kind_of Float
16
+ end
17
+
18
+ include_examples 'argument types', %w(+ 1 2),
19
+ 1 => [:integer, :real],
20
+ 2 => [:integer, :real]
21
+ include_examples 'applicative order evaluation', %w(+ 1 2)
22
+ include_examples 'partially-applicable function', %w(+ 1 2)
23
+ end
24
+
25
+ describe '-' do
26
+ it 'subtracts its second argument from its first' do
27
+ kl_eval('(- 3 2)').should == 1
28
+ end
29
+
30
+ it 'returns an integer when both arguments are integers' do
31
+ kl_eval('(- 3 2)').should be_kind_of Fixnum
32
+ end
33
+
34
+ it ('returns a real when either argument is a real') do
35
+ kl_eval('(- 3.0 2)').should be_kind_of Float
36
+ kl_eval('(- 3 2.0)').should be_kind_of Float
37
+ end
38
+
39
+ include_examples 'argument types', %w(- 3 2),
40
+ 1 => [:integer, :real],
41
+ 2 => [:integer, :real]
42
+ include_examples 'applicative order evaluation', %w(- 3 2)
43
+ include_examples 'partially-applicable function', %w(- 3 2)
44
+ end
45
+
46
+ describe '*' do
47
+ it 'multiplies its arguments' do
48
+ kl_eval('(* 2 3)').should == 6
49
+ end
50
+
51
+ it 'returns an integer when both arguments are integers' do
52
+ kl_eval('(* 2 3)').should be_kind_of Fixnum
53
+ end
54
+
55
+ it ('returns a real when either argument is a real') do
56
+ kl_eval('(* 2.0 3)').should be_kind_of Float
57
+ kl_eval('(* 2 3.0)').should be_kind_of Float
58
+ end
59
+
60
+ include_examples 'argument types', %w(* 2 3),
61
+ 1 => [:integer, :real],
62
+ 2 => [:integer, :real]
63
+ include_examples 'applicative order evaluation', %w(* 2 3)
64
+ include_examples 'partially-applicable function', %w(* 2 3)
65
+ end
66
+
67
+ describe '/' do
68
+ it 'divides its first argument by its second argument' do
69
+ kl_eval('(/ 6 3)').should == 2
70
+ end
71
+
72
+ it 'returns an integer when an integer evenly divides another integer' do
73
+ kl_eval('(/ 6 3)').should be_kind_of Fixnum
74
+ end
75
+
76
+ it 'returns a real when an integer does not evenly divide another integer' do
77
+ kl_eval('(/ 4 3)').should be_kind_of Float
78
+ end
79
+
80
+ it ('returns a real when either argument is a real') do
81
+ kl_eval('(/ 6.0 3)').should be_kind_of Float
82
+ kl_eval('(/ 6 3.0)').should be_kind_of Float
83
+ end
84
+
85
+ include_examples 'argument types', %w(/ 6 3),
86
+ 1 => [:integer, :real],
87
+ 2 => [:integer, :real]
88
+ include_examples 'applicative order evaluation', %w(/ 6 3)
89
+ include_examples 'partially-applicable function', %w(/ 6 3)
90
+ end
91
+
92
+ describe '>' do
93
+ it 'returns false if its first argument is less than its second argument' do
94
+ kl_eval('(> 1 2)').should == false
95
+ end
96
+
97
+ it 'returns false if its first argument is equal to its second argument' do
98
+ kl_eval('(> 2 2)').should == false
99
+ end
100
+
101
+ it 'returns true if its first argument is greater than its second argument' do
102
+ kl_eval('(> 3 2)').should == true
103
+ end
104
+
105
+ include_examples 'argument types', %w(> 1 2),
106
+ 1 => [:integer, :real],
107
+ 2 => [:integer, :real]
108
+ include_examples 'applicative order evaluation', %w(> 1 2)
109
+ include_examples 'partially-applicable function', %w(> 1 2)
110
+ end
111
+
112
+ describe '<' do
113
+ it 'returns true if its first argument is less than its second argument' do
114
+ kl_eval('(< 1 2)').should == true
115
+ end
116
+
117
+ it 'returns false if its first argument is equal to its second argument' do
118
+ kl_eval('(< 2 2)').should == false
119
+ end
120
+
121
+ it 'returns false if its first argument is greater than its second argument' do
122
+ kl_eval('(< 3 2)').should == false
123
+ end
124
+
125
+ include_examples 'argument types', %w(< 1 2),
126
+ 1 => [:integer, :real],
127
+ 2 => [:integer, :real]
128
+ include_examples 'applicative order evaluation', %w(> 1 2)
129
+ include_examples 'partially-applicable function', %w(> 1 2)
130
+ end
131
+
132
+ describe '>=' do
133
+ it 'returns false if its first argument is less than its second argument' do
134
+ kl_eval('(>= 1 2)').should == false
135
+ end
136
+
137
+ it 'returns true if its first argument is equal to its second argument' do
138
+ kl_eval('(>= 2 2)').should == true
139
+ end
140
+
141
+ it 'returns true if its first argument is greater than its second argument' do
142
+ kl_eval('(>= 3 2)').should == true
143
+ end
144
+
145
+ include_examples 'argument types', %w(>= 1 2),
146
+ 1 => [:integer, :real],
147
+ 2 => [:integer, :real]
148
+ include_examples 'applicative order evaluation', %w(> 1 2)
149
+ include_examples 'partially-applicable function', %w(> 1 2)
150
+ end
151
+
152
+ describe '<=' do
153
+ it 'returns true if its first argument is less than its second argument' do
154
+ kl_eval('(<= 1 2)').should == true
155
+ end
156
+
157
+ it 'returns true if its first argument is equal to its second argument' do
158
+ kl_eval('(<= 2 2)').should == true
159
+ end
160
+
161
+ it 'returns false if its first argument is greater than its second argument' do
162
+ kl_eval('(<= 3 2)').should == false
163
+ end
164
+
165
+ include_examples 'argument types', %w(<= 1 2),
166
+ 1 => [:integer, :real],
167
+ 2 => [:integer, :real]
168
+ include_examples 'applicative order evaluation', %w(> 1 2)
169
+ include_examples 'partially-applicable function', %w(> 1 2)
170
+ end
171
+
172
+ describe 'number?' do
173
+ include_examples 'type predicate', 'number?', [:integer, :real]
174
+ end
175
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Primitives for assignment' do
4
+ describe '(set Name Value)' do
5
+ it 'associates Value with Name' do
6
+ kl_eval('(set foo bar)')
7
+ kl_eval('(value foo)').should == :bar
8
+ end
9
+
10
+ it 'returns Value' do
11
+ kl_eval('(set foo bar)').should == :bar
12
+ end
13
+
14
+ it 'overwrites the previous value when called again with same Name' do
15
+ kl_eval('(set foo bar)')
16
+ kl_eval('(set foo baz)').should == :baz
17
+ end
18
+
19
+ include_examples 'argument types', %w(set foo bar), 1 => [:symbol]
20
+ include_examples 'partially-applicable function', %w(set foo bar)
21
+ include_examples 'applicative order evaluation', %w(set foo bar)
22
+ end
23
+
24
+ describe '(value Name)' do
25
+ before(:each) do
26
+ # a-symbol is the example symbol used by the argument type specs.
27
+ # Set it so that they do not throw and unset variable error.
28
+ kl_eval('(set a-symbol bar)')
29
+ end
30
+
31
+ it 'returns the value associated with Name' do
32
+ kl_eval('(value a-symbol)').should == :bar
33
+ end
34
+
35
+ it 'raises an error if Name has not previously been set' do
36
+ expect {
37
+ kl_eval('(value baz)')
38
+ }.to raise_error(Kl::Error, 'variable baz has no value')
39
+ end
40
+
41
+ include_examples 'argument types', %w(value a-symbol), 1 => [:symbol]
42
+ include_examples 'partially-applicable function', %w(value a-symbol)
43
+ end
44
+ end
@@ -1,7 +1,120 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'Primitives for Generic Functions' do
4
- describe 'freeze' do
5
- # Should the frozen result be re-evaluated on each thaw?
4
+ describe '(defun Name ArgList Expr)' do
5
+ it 'does not evaulate Expr' do
6
+ kl_eval('(set flag clear)')
7
+ kl_eval('(defun foo () (set flag set))')
8
+ kl_eval('(value flag)').should == :clear
9
+ end
10
+
11
+ it 'returns Name' do
12
+ kl_eval('(defun foo () true)').should == :foo
13
+ end
14
+
15
+ it 'binds Name to a function having ArgList as its formals and Expr as its body' do
16
+ kl_eval('(defun my-add (A B) (+ A B))')
17
+ kl_eval('(my-add 1 2)').should == 3
18
+ end
19
+
20
+ it 'allows ArgList to be the empty list' do
21
+ kl_eval('(defun foo () success)').should == :foo
22
+ kl_eval('(foo)').should == :success
23
+ end
24
+
25
+ it 'allows previously defined non-primitive functions to be redefined' do
26
+ kl_eval('(defun my-add (A B) (+ A B))')
27
+ kl_eval('(defun my-add (A B) surprise!)')
28
+ kl_eval('(my-add 1 2)').should == :surprise!
29
+ end
30
+
31
+ it 'raises an error when attempting to redefine a primitive' do
32
+ expect {
33
+ kl_eval('(defun + (A B) :surprise!)')
34
+ }.to raise_error(Kl::Error, '+ is primitive and may not be redefined')
35
+ end
36
+
37
+ include_examples 'argument types', %w[defun foo () success],
38
+ 1 => [:symbol]
39
+
40
+ it 'raises an error if ArgList is not a list' do
41
+ expect {
42
+ kl_eval('(defun foo bar baz)')
43
+ }.to raise_error(Kl::Error, 'bar is not a list')
44
+ end
45
+
46
+ it 'raises an error if ArgList contains non-symbols' do
47
+ expect {
48
+ kl_eval('(defun foo (1) baz)')
49
+ }.to raise_error(Kl::Error, '1 is not a symbol')
50
+ end
51
+ include_examples "non-partially-applicable function",
52
+ %w[defun foo () success]
53
+ end
54
+
55
+ describe '(lambda X Expr)' do
56
+ it 'does not evaulate Expr' do
57
+ kl_eval('(set flag clear)')
58
+ kl_eval('(lambda X 37)')
59
+ kl_eval('(value flag)').should == :clear
60
+ end
61
+
62
+ it 'returns a function' do
63
+ kl_eval('(lambda X 37)').should be_kind_of Proc
64
+ end
65
+
66
+ describe 'the returned function, when applied' do
67
+ it 'evaluates Expr with X bound to its argument' do
68
+ kl_eval('((lambda X (+ 1 X)) 2)').should == 3
69
+ end
70
+ end
71
+
72
+ include_examples 'argument types', %w(lambda X 37),
73
+ 1 => [:symbol]
74
+ include_examples "non-partially-applicable function", %w(lambda X 37)
75
+ end
76
+
77
+ describe '(freeze Expr)' do
78
+ it 'does not evaluate Expr' do
79
+ kl_eval('(set flag clear)')
80
+ kl_eval('(freeze (set flag set))')
81
+ kl_eval('(value flag)').should == :clear
82
+ end
83
+
84
+ it 'returns a continuation' do
85
+ kl_eval('(freeze a)').should be_kind_of Proc
86
+ end
87
+
88
+ describe 'the returned continuation, upon thawing' do
89
+ before(:each) do
90
+ kl_eval('(defun my-thaw (X) (X))')
91
+ end
92
+
93
+ it 'evaluates and returns the value of Expr' do
94
+ kl_eval('(my-thaw (freeze (+ 1 2)))').should == 3
95
+ end
96
+
97
+ it 're-evaluates Expr with each thawing' do
98
+ define_kl_do
99
+ kl_eval('(set count 0)')
100
+ kl_eval('(let C (freeze (set count (+ (value count) 1)))
101
+ (kl-do (my-thaw C) (my-thaw C)))')
102
+ kl_eval('(value count)').should == 2
103
+ end
104
+ end
105
+
106
+ describe 'partial application' do
107
+ # Can't compare two functions so we implement this manually
108
+ it 'supports partial application of 0 arguments' do
109
+ kl_eval('(defun my-thaw (X) (X))')
110
+ kl_eval('(my-thaw ((freeze) 37))').should == 37
111
+ end
112
+
113
+ it 'results in it no longer delaying execution of Expr' do
114
+ kl_eval('(set flag clear)')
115
+ kl_eval('((freeze) (set flag set))').should
116
+ kl_eval('(value flag)').should == :set
117
+ end
118
+ end
6
119
  end
7
120
  end