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.
- data/.gitignore +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +2 -2
- data/HISTORY.md +12 -0
- data/README.md +10 -7
- data/Rakefile +92 -0
- data/bin/srrepl +2 -2
- data/k_lambda_spec/primitives/arithmetic_spec.rb +175 -0
- data/k_lambda_spec/primitives/assignments_spec.rb +44 -0
- data/k_lambda_spec/primitives/generic_functions_spec.rb +115 -2
- data/k_lambda_spec/primitives/lists_spec.rb +40 -0
- data/k_lambda_spec/primitives/strings_spec.rb +77 -0
- data/k_lambda_spec/primitives/symbols_spec.rb +24 -0
- data/k_lambda_spec/primitives/vectors_spec.rb +92 -0
- data/k_lambda_spec/support/shared_examples.rb +93 -2
- data/k_lambda_spec/tail_recursion_spec.rb +30 -0
- data/lib/kl/compiler.rb +19 -33
- data/lib/kl/environment.rb +1 -0
- data/lib/kl/primitives/assignments.rb +1 -0
- data/lib/kl/primitives/generic_functions.rb +7 -0
- data/lib/kl/primitives/lists.rb +2 -0
- data/lib/kl/primitives/strings.rb +13 -5
- data/lib/kl/primitives/symbols.rb +1 -0
- data/lib/kl/primitives/vectors.rb +5 -0
- data/lib/shen_ruby/version.rb +1 -1
- data/shen-ruby.gemspec +1 -1
- data/shen/lib/shen_ruby/shen.rb +5 -6
- data/shen/release/benchmarks/benchmarks.shen +0 -4
- data/shen/release/benchmarks/interpreter.shen +2 -2
- data/shen/release/benchmarks/plato.jpg +0 -0
- data/shen/release/k_lambda/core.kl +171 -1000
- data/shen/release/k_lambda/declarations.kl +90 -992
- data/shen/release/k_lambda/load.kl +69 -81
- data/shen/release/k_lambda/macros.kl +113 -478
- data/shen/release/k_lambda/prolog.kl +250 -1307
- data/shen/release/k_lambda/reader.kl +115 -996
- data/shen/release/k_lambda/sequent.kl +154 -554
- data/shen/release/k_lambda/sys.kl +246 -562
- data/shen/release/k_lambda/t-star.kl +114 -3643
- data/shen/release/k_lambda/toplevel.kl +136 -221
- data/shen/release/k_lambda/track.kl +101 -206
- data/shen/release/k_lambda/types.kl +143 -298
- data/shen/release/k_lambda/writer.kl +93 -106
- data/shen/release/k_lambda/yacc.kl +77 -252
- data/shen/release/test_programs/README.shen +1 -1
- data/shen/release/test_programs/classes-typed.shen +1 -1
- data/shen/release/test_programs/interpreter.shen +2 -2
- data/shen/release/test_programs/metaprog.shen +2 -2
- data/shen/release/test_programs/prolog.shen +79 -0
- data/shen/release/test_programs/structures-typed.shen +2 -2
- data/shen/release/test_programs/tests.shen +19 -80
- data/shen/release/test_programs/yacc.shen +11 -15
- metadata +14 -6
- data/Gemfile.lock +0 -20
- data/shen/release/benchmarks/br.shen +0 -13
data/.travis.yml
ADDED
data/Gemfile
CHANGED
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
|
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-
|
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.
|
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
|
29
|
+
www.shenlanguage.org, version 9
|
28
30
|
running under Ruby, implementation: ruby 1.9.3
|
29
|
-
port 0.
|
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.
|
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
|
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
|
data/Rakefile
ADDED
@@ -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
|
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
|
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 '
|
5
|
-
|
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
|