rb-scheme 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0544689173026c8cb44f2dec68fad23ff1e98a59
4
+ data.tar.gz: c3e23e29b7f1bc84bf829cefd42b902b47a233df
5
+ SHA512:
6
+ metadata.gz: b35a2ace04fc9111600fe6d954ab880cdb6cae38f3099297e0660ff39fa4611a99961c5baed47a20fd81eca1e51f793016c17c38abcf185816d9d7622c9cd41f
7
+ data.tar.gz: 777d0df0d518ceebdab8c8eb64885ee6cadb1b7f9cf8be89f9abd1139c592f963fdc5d9c867dd79ce79d3c1b847fb4e3f5e7946940973d7ff131a087621ca4a9
data/.gitignore ADDED
@@ -0,0 +1,37 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ ## Specific to RubyMotion:
14
+ .dat*
15
+ .repl_history
16
+ build/
17
+
18
+ ## Documentation cache and generated files:
19
+ /.yardoc/
20
+ /_yardoc/
21
+ /doc/
22
+ /rdoc/
23
+
24
+ ## Environment normalization:
25
+ /.bundle/
26
+ /vendor/bundle
27
+ /lib/bundler/man/
28
+
29
+ # for a library or gem, you might want to ignore these files since the code is
30
+ # intended to run in multiple environments; otherwise, check them in:
31
+ # Gemfile.lock
32
+ # .ruby-version
33
+ # .ruby-gemset
34
+
35
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
+ .rvmrc
37
+ .ruby-version
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ # A sample Gemfile
2
+ source "https://rubygems.org"
3
+
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ rb-scheme (0.3.5)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ minitest (5.10.1)
10
+ rake (10.4.2)
11
+
12
+ PLATFORMS
13
+ ruby
14
+
15
+ DEPENDENCIES
16
+ bundler (~> 1.13)
17
+ minitest (~> 5.10)
18
+ rake (~> 10.0)
19
+ rb-scheme!
20
+
21
+ BUNDLED WITH
22
+ 1.13.6
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 tkhsh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ [![CircleCI](https://circleci.com/gh/tkhsh/rb-scheme/tree/master.svg?style=svg&circle-token=7033e7916629278437dfafc29e10f40973ee5cd3)](https://circleci.com/gh/tkhsh/rb-scheme/tree/master)
2
+
3
+ # RbScheme
4
+
5
+ An implementation of Scheme subset written in Ruby. It's based on the Stack-Based model introduced in [Three Implementation Models for Scheme](http://www.cs.indiana.edu/~dyb/papers/3imp.pdf) by R. Kent Dybvig. The model is implemented by a compiler and virtual machine.
6
+
7
+ # Features
8
+
9
+ - first class closures
10
+ - global variables
11
+ - integers/symbols/cons cell/true/false
12
+ - variadic function
13
+ - call/cc(limitation exists)
14
+ - if
15
+ - basic arithmetic functions(+ - * /)
16
+ - set!
17
+ - tail call optimization
18
+
19
+ # Install
20
+
21
+ ```
22
+ $ git clone https://github.com/tkhsh/rb-scheme.git
23
+ ```
24
+
25
+ # Usage
26
+
27
+ ## Run
28
+
29
+ ### repl
30
+ ```
31
+ $ bin/rb-scheme
32
+ ```
33
+
34
+ You can use [rlwrap](https://github.com/hanslub42/rlwrap) for readline
35
+ ```
36
+ $ rlwrap bin/rb-scheme
37
+ ```
38
+
39
+ ### with file
40
+ ```
41
+ $ bin/rb-scheme examples/nqueen.scm
42
+ ```
43
+
44
+ ## primitives
45
+
46
+ - numeric(`+`, `-`, `*`, `/`)
47
+ - predicate(`=`, `<`, `>`, `null?`)
48
+ - lisp operations(`cons`, `car`, `cdr`, `list`)
49
+ - print(`display`, `newline`, `print`)
50
+
51
+ ## examples
52
+
53
+ see `examples` folder
54
+
55
+ # Test
56
+
57
+ [bundler](http://bundler.io/) required
58
+ ```
59
+ $ bundle install
60
+ $ rake test
61
+ ```
62
+
63
+ # License
64
+
65
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ desc 'Run test'
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'test'
7
+ t.test_files = FileList['test/**/test*.rb']
8
+ t.verbose = true
9
+ end
10
+
11
+ task :default => [:test]
data/circle.yml ADDED
@@ -0,0 +1,6 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.2.3
4
+ test:
5
+ post:
6
+ - bundle exec rake test
@@ -0,0 +1,120 @@
1
+ ; n queen solver
2
+ (define nil '())
3
+
4
+ (define caddr
5
+ (lambda (lst)
6
+ (car (cddr lst))))
7
+
8
+ (define append
9
+ (lambda (lst1 lst2)
10
+ (if (null? lst1)
11
+ lst2
12
+ (cons (car lst1)
13
+ (append (cdr lst1) lst2)))))
14
+
15
+ (define accumulate
16
+ (lambda (op initial sequence)
17
+ (if (null? sequence)
18
+ initial
19
+ (op (car sequence)
20
+ (accumulate op initial (cdr sequence))))))
21
+
22
+ (define map
23
+ (lambda (proc lst)
24
+ (if (null? lst)
25
+ nil
26
+ (cons (proc (car lst))
27
+ (map proc (cdr lst))))))
28
+
29
+ (define map2
30
+ (lambda (proc lst1 lst2)
31
+ (if (null? lst1)
32
+ nil
33
+ (if (null? lst2)
34
+ nil
35
+ (cons (proc (car lst1) (car lst2))
36
+ (map2 proc (cdr lst1) (cdr lst2)))))))
37
+
38
+ (define filter
39
+ (lambda (pred lst)
40
+ (if (null? lst)
41
+ nil
42
+ (if (pred (car lst))
43
+ (cons (car lst) (filter pred (cdr lst)))
44
+ (filter pred (cdr lst))))))
45
+
46
+ (define flatmap
47
+ (lambda (proc seq)
48
+ (accumulate append nil (map proc seq))))
49
+
50
+ (define enumerate-interval
51
+ (lambda (low high)
52
+ (if (> low high)
53
+ nil
54
+ (cons low (enumerate-interval (+ low 1) high)))))
55
+
56
+ (define make-board-from-y
57
+ (lambda (y rest n)
58
+ (if (null? rest)
59
+ nil
60
+ (cons (+ y n) (make-board-from-y (+ y n) (cdr rest) n)))))
61
+
62
+ (define make-board
63
+ (lambda (board n)
64
+ (if (null? board)
65
+ nil
66
+ (cons nil (make-board-from-y (car board) (cdr board) n)))))
67
+
68
+ (define make-wrong-board
69
+ (lambda (board)
70
+ (cons (make-board board 1)
71
+ (cons (make-board board 0)
72
+ (cons (make-board board -1) nil)))))
73
+
74
+ (define compare-board-pair
75
+ (lambda (wrong-board board)
76
+ (if (null? (filter
77
+ (lambda (b) b)
78
+ (map2 (lambda (e1 e2)
79
+ (if (null? e1)
80
+ #f
81
+ (= e1 e2)))
82
+ wrong-board
83
+ board)))
84
+ #t
85
+ #f)))
86
+
87
+ (define safe?
88
+ (lambda (k positions)
89
+ ((lambda (wrong-board)
90
+ (if (compare-board-pair (car wrong-board) positions)
91
+ (if (compare-board-pair (cadr wrong-board) positions)
92
+ (compare-board-pair (caddr wrong-board) positions)
93
+ #f)
94
+ #f))
95
+ (make-wrong-board positions))))
96
+
97
+ (define empty-board nil)
98
+
99
+ (define adjoin-position
100
+ (lambda (new-row k rest-of-queens)
101
+ (cons new-row rest-of-queens)))
102
+
103
+ (define queen-cols
104
+ (lambda (k board-size)
105
+ (if (= k 0)
106
+ (cons empty-board nil)
107
+ (filter
108
+ (lambda (positions) (safe? k positions))
109
+ (flatmap
110
+ (lambda (rest-of-queens)
111
+ (map (lambda (new-row)
112
+ (adjoin-position new-row k rest-of-queens))
113
+ (enumerate-interval 1 board-size)))
114
+ (queen-cols (- k 1) board-size))))))
115
+
116
+ (define queens
117
+ (lambda (board-size)
118
+ (queen-cols board-size board-size)))
119
+
120
+ (print (queens 4))
@@ -0,0 +1,12 @@
1
+ (print ((lambda (y)
2
+ ((y (lambda (fn)
3
+ (lambda (n)
4
+ (if (< n 1)
5
+ 1
6
+ (+ n (fn (- n 1)))))))
7
+ 10))
8
+ (lambda (f)
9
+ ((lambda (g)
10
+ (f (lambda (arg) ((g g) arg))))
11
+ (lambda (g)
12
+ (f (lambda (arg) ((g g) arg))))))))
data/exe/rb-scheme ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'rb-scheme'
7
+
8
+ RbScheme::Executer.run(ARGF.file)
data/lib/rb-scheme.rb ADDED
@@ -0,0 +1,18 @@
1
+ require "forwardable"
2
+ require "set"
3
+
4
+ require 'rb-scheme/lisp-objects'
5
+ require 'rb-scheme/helpers'
6
+ require 'rb-scheme/symbol'
7
+ require 'rb-scheme/global'
8
+ require 'rb-scheme/parser'
9
+ require 'rb-scheme/printer'
10
+ require 'rb-scheme/evaluator'
11
+ require 'rb-scheme/primitive'
12
+ require 'rb-scheme/primitive/procedure'
13
+ require 'rb-scheme/extension'
14
+ require 'rb-scheme/compiler'
15
+ require 'rb-scheme/vm/box'
16
+ require 'rb-scheme/vm/stack'
17
+ require 'rb-scheme/vm'
18
+ require 'rb-scheme/executer'
@@ -0,0 +1,280 @@
1
+ module RbScheme
2
+ class Compiler
3
+ include Helpers
4
+ include Symbol
5
+
6
+ def compile(exp, env, sets, nxt)
7
+ case exp
8
+ when LSymbol
9
+ compile_refer(exp,
10
+ env,
11
+ sets.member?(exp) ? list(intern("indirect"), nxt) : nxt)
12
+ when LCell
13
+ case exp.car
14
+ when intern("quote")
15
+ check_length!(exp.cdr, 1, "quote")
16
+ obj = exp.cadr
17
+
18
+ list(intern("constant"), obj, nxt)
19
+ when intern("lambda")
20
+ check_min_length!(exp.cdr, 2, "lambda")
21
+ param_info = parse_parameters(exp.cadr)
22
+ vars = param_info[:vars]
23
+ *body = exp.cddr.to_a
24
+
25
+ local_bound = Set.new(vars)
26
+ global_bound = Set.new(Global.variables)
27
+ free = convert_to_list(find_free_body(body, local_bound.union(global_bound)))
28
+ sets_body = find_sets_body(body, Set.new(vars))
29
+ c = compile_lambda_body(body,
30
+ cons(vars, free),
31
+ sets_body.union(sets.intersection(free)),
32
+ list(intern("return"), vars.count))
33
+ collect_free(free,
34
+ env,
35
+ list(intern("close"),
36
+ vars.count,
37
+ param_info[:variadic?] ? 1 : 0,
38
+ free.count,
39
+ make_boxes(sets_body, vars, c),
40
+ nxt))
41
+ when intern("begin")
42
+ check_min_length!(exp.cdr, 1, "begin")
43
+ *body = exp.cdr.to_a
44
+
45
+ compile_lambda_body(body, env, sets, nxt)
46
+ when intern("if")
47
+ check_length!(exp.cdr, 3, "if")
48
+ test, then_exp, else_exp = exp.cdr.to_a
49
+
50
+ thenc = compile(then_exp, env, sets, nxt)
51
+ elsec = compile(else_exp, env, sets, nxt)
52
+ compile(test, env, sets, list(intern("test"), thenc, elsec))
53
+ when intern("set!")
54
+ check_length!(exp.cdr, 2, "set!")
55
+ var, x = exp.cdr.to_a
56
+
57
+ compile_lookup(var,
58
+ env,
59
+ lambda { |n| compile(x, env, sets, list(intern("assign-local"), n, nxt)) },
60
+ lambda { |n| compile(x, env, sets, list(intern("assign-free"), n, nxt)) },
61
+ lambda { |k| compile(x, env, sets, list(intern("assign-global"), k, nxt)) })
62
+ when intern("define")
63
+ check_length!(exp.cdr, 2, "define")
64
+ var, x = exp.cdr.to_a
65
+
66
+ Global.put(var, nil)
67
+ compile(x, env, sets, list(intern("assign-global"), var, nxt))
68
+ when intern("call/cc")
69
+ check_length!(exp.cdr, 1, "call/cc")
70
+ x = exp.cadr
71
+
72
+ cn = tail?(nxt) ?
73
+ list(intern("shift"), 1, nxt.cadr, list(intern("apply"), 1)) :
74
+ list(intern("apply"), 1)
75
+ c = list(intern("conti"),
76
+ list(intern("argument"),
77
+ compile(x, env, sets, cn)))
78
+ tail?(nxt) ? c : list(intern("frame"), nxt, c)
79
+ else
80
+ args = exp.cdr
81
+ cn = tail?(nxt) ?
82
+ list(intern("shift"), exp.cdr.count, nxt.cadr, list(intern("apply"), args.count)) :
83
+ list(intern("apply"), args.count)
84
+ c = compile(exp.car, env, sets, cn)
85
+
86
+ args.each do |arg|
87
+ c = compile(arg, env, sets, list(intern("argument"), c))
88
+ end
89
+ tail?(nxt) ? c : list(intern("frame"), nxt, c)
90
+ end
91
+ else
92
+ list(intern("constant"), exp, nxt)
93
+ end
94
+ end
95
+
96
+ def tail?(nxt)
97
+ nxt.car == intern("return")
98
+ end
99
+
100
+ def parse_parameters(param)
101
+ case param
102
+ when LSymbol
103
+ return { vars: list(param), variadic?: true }
104
+ when LCell
105
+ return { vars: list, variadic?: false } if param.null?
106
+ result = []
107
+ target = param
108
+ loop do
109
+ result.push(target.car)
110
+ target = target.cdr
111
+ if !target.is_a?(LCell)
112
+ result.push(target)
113
+ return { vars: convert_to_list(result), variadic?: true }
114
+ elsif target.null?
115
+ return { vars: convert_to_list(result), variadic?: false }
116
+ end
117
+ end
118
+ else
119
+ raise "error"
120
+ end
121
+ end
122
+
123
+ def compile_lambda_body(body, env, sets, ret)
124
+ c = ret
125
+ body.reverse_each do |exp|
126
+ c = compile(exp, env, sets, c)
127
+ end
128
+ c
129
+ end
130
+
131
+ def find_sets_body(body, sets_vars)
132
+ body.reduce(Set.new) do |whole_sets, exp|
133
+ whole_sets.union(find_sets(exp, sets_vars))
134
+ end
135
+ end
136
+
137
+ def find_sets(exp, vars)
138
+ case exp
139
+ when LSymbol
140
+ Set.new
141
+ when LCell
142
+ case exp.car
143
+ when intern("quote")
144
+ Set.new
145
+ when intern("lambda")
146
+ check_min_length!(exp.cdr, 2, "find_sets(lambda)")
147
+ new_vars, *body = exp.cdr.to_a
148
+
149
+ find_sets_body(body, vars.subtract(new_vars))
150
+ when intern("if")
151
+ check_length!(exp.cdr, 3, "find_sets(if)")
152
+ test, then_x, else_x = exp.cdr.to_a
153
+
154
+ [test, then_x, else_x].inject(Set.new) do |res, x|
155
+ res.union(find_sets(x, vars))
156
+ end
157
+ when intern("set!")
158
+ check_length!(exp.cdr, 2, "find_sets(set!)")
159
+ var, x = exp.cdr.to_a
160
+
161
+ s = vars.member?(var) ? Set.new([var]) : Set.new
162
+ s.union(find_sets(x, vars))
163
+ when intern("define")
164
+ raise "Only top level define is supported"
165
+ when intern("call/cc")
166
+ check_length!(exp.cdr, 1, "find_sets(call/cc)")
167
+ x = exp.cadr
168
+
169
+ find_sets(x, vars)
170
+ else
171
+ exp.inject(Set.new) do |res, x|
172
+ res.union(find_sets(x, vars))
173
+ end
174
+ end
175
+ else
176
+ Set.new
177
+ end
178
+ end
179
+
180
+ def make_boxes(sets, vars, nxt)
181
+ n = vars.count - 1
182
+ res = nxt
183
+
184
+ vars.reverse_each do |v|
185
+ if sets.member?(v)
186
+ res = list(intern("box"), n, res)
187
+ end
188
+ n -= 1
189
+ end
190
+ res
191
+ end
192
+
193
+ def find_free_body(body, bound_variables)
194
+ body.reduce(Set.new) do |whole_free, exp|
195
+ whole_free.union(find_free(exp, bound_variables))
196
+ end
197
+ end
198
+
199
+ def find_free(exp, bound_variables)
200
+ case exp
201
+ when LSymbol
202
+ bound_variables.member?(exp) ? Set.new : Set.new(list(exp))
203
+ when LCell
204
+ case exp.car
205
+ when intern("quote")
206
+ Set.new
207
+ when intern("lambda")
208
+ check_min_length!(exp.cdr, 2, "find_free")
209
+ vars, *body = exp.cdr.to_a
210
+
211
+ find_free_body(body, bound_variables.union(Set.new(vars)))
212
+ when intern("if")
213
+ check_length!(exp.cdr, 3, "find_free(if)")
214
+ test_x, then_x, else_x = exp.cdr.to_a
215
+
216
+ find_free(test_x, bound_variables)
217
+ .union(find_free(then_x, bound_variables))
218
+ .union(find_free(else_x, bound_variables))
219
+ when intern("set!")
220
+ check_length!(exp.cdr, 2, "find_free(set!)")
221
+ var, exp = exp.cdr.to_a
222
+
223
+ free = find_free(exp, bound_variables)
224
+ bound_variables.member?(var) ? free : Set[var].union(free)
225
+ when intern("define")
226
+ raise "Only top level define is supported"
227
+ when intern("call/cc")
228
+ check_length!(exp.cdr, 1, "find_free(call/cc)")
229
+ x = exp.cadr
230
+
231
+ find_free(x, bound_variables)
232
+ else
233
+ exp.inject(Set.new) do |result, item|
234
+ result.union(find_free(item, bound_variables))
235
+ end
236
+ end
237
+ else
238
+ Set.new
239
+ end
240
+ end
241
+
242
+ def collect_free(vars, env, nxt)
243
+ return nxt if vars.null?
244
+
245
+ collect_free(vars.cdr,
246
+ env,
247
+ compile_refer(vars.car,
248
+ env,
249
+ list(intern("argument"), nxt)))
250
+ end
251
+
252
+ def compile_refer(var, env, nxt)
253
+ compile_lookup(var,
254
+ env,
255
+ lambda { |n| list(intern("refer-local"), n, nxt) },
256
+ lambda { |n| list(intern("refer-free"), n, nxt) },
257
+ lambda { |k| list(intern("refer-global"), k, nxt) })
258
+ end
259
+
260
+ def compile_lookup(var, env, return_local, return_free, return_global)
261
+ unless env.null?
262
+ locals = env.car
263
+ locals.each_with_index do |l, n|
264
+ return return_local.call(n) if l == var
265
+ end
266
+
267
+ free = env.cdr
268
+ free.each_with_index do |f, n|
269
+ return return_free.call(n) if f == var
270
+ end
271
+ end
272
+
273
+ if Global.defined?(var)
274
+ return return_global.call(var)
275
+ end
276
+
277
+ raise "#{var.name} isn't found in environment"
278
+ end
279
+ end # Compiler
280
+ end # RbScheme