heist 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +2 -0
- data/lib/builtin/library.scm +49 -4
- data/lib/builtin/primitives.rb +3 -40
- data/lib/heist.rb +1 -1
- data/lib/parser/nodes.rb +1 -1
- data/lib/parser/scheme.rb +120 -6
- data/lib/parser/scheme.tt +5 -1
- data/lib/runtime/callable/macro/expansion.rb +1 -1
- data/lib/runtime/data/identifier.rb +13 -5
- data/test/hygienic.scm +7 -0
- data/test/lists.scm +5 -0
- metadata +8 -7
data/README.txt
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
= Heist
|
2
2
|
|
3
|
+
http://github.com/jcoglan/heist
|
4
|
+
|
3
5
|
Heist is a Scheme interpreter written in Ruby. It provides an executable
|
4
6
|
for running Scheme code directly, and also allows the interpreter to be
|
5
7
|
easily embedded in, and extended using, Ruby applications. It nominally
|
data/lib/builtin/library.scm
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
; in Scheme should go here. If at all possible, write
|
3
3
|
; builtins in Scheme rather than Ruby.
|
4
4
|
|
5
|
+
|
5
6
|
; (newline)
|
6
7
|
; prints a new-line character
|
7
8
|
(define (newline)
|
@@ -53,6 +54,19 @@
|
|
53
54
|
; Returns true iff x is not an exact number
|
54
55
|
(define inexact? (negate exact?))
|
55
56
|
|
57
|
+
; Returns true iff all arguments are numerically equal
|
58
|
+
(define (= . args)
|
59
|
+
(define (iter x rest)
|
60
|
+
(if (null? rest)
|
61
|
+
#t
|
62
|
+
(let ([y (car rest)])
|
63
|
+
(if (or (not (number? x))
|
64
|
+
(not (number? y))
|
65
|
+
(not (equal? x y)))
|
66
|
+
#f
|
67
|
+
(iter x (cdr rest))))))
|
68
|
+
(iter (car args) (cdr args)))
|
69
|
+
|
56
70
|
; (zero? x)
|
57
71
|
; Returns true iff x is zero
|
58
72
|
(define (zero? x)
|
@@ -99,6 +113,37 @@
|
|
99
113
|
(- x)
|
100
114
|
x))
|
101
115
|
|
116
|
+
; (quotient) and (remainder) satisfy
|
117
|
+
;
|
118
|
+
; (= n1 (+ (* n2 (quotient n1 n2))
|
119
|
+
; (remainder n1 n2)))
|
120
|
+
|
121
|
+
; (quotient x y)
|
122
|
+
; Returns the quotient of two numbers, i.e. performs n1/n2
|
123
|
+
; and rounds toward zero.
|
124
|
+
(define (quotient x y)
|
125
|
+
(let ([result (/ x y)])
|
126
|
+
((if (positive? result)
|
127
|
+
floor
|
128
|
+
ceiling)
|
129
|
+
result)))
|
130
|
+
|
131
|
+
; (remainder x y)
|
132
|
+
; Returns the remainder after dividing the first operand
|
133
|
+
; by the second
|
134
|
+
(define (remainder x y)
|
135
|
+
(- (round x)
|
136
|
+
(* (round y)
|
137
|
+
(quotient x y))))
|
138
|
+
|
139
|
+
; (modulo x y)
|
140
|
+
; Returns the first operand modulo the second
|
141
|
+
(define (modulo x y)
|
142
|
+
(+ (remainder x y)
|
143
|
+
(if (negative? (* x y))
|
144
|
+
(round y)
|
145
|
+
0)))
|
146
|
+
|
102
147
|
; (gcd x y)
|
103
148
|
; Returns the greatest common divisor of two numbers
|
104
149
|
; http://en.wikipedia.org/wiki/Euclidean_algorithm
|
@@ -184,10 +229,10 @@
|
|
184
229
|
; The final argument is not copied and the return value of
|
185
230
|
; (append) shares structure with it.
|
186
231
|
(define (append first . rest)
|
187
|
-
(if (null?
|
188
|
-
|
189
|
-
(if (null?
|
190
|
-
|
232
|
+
(if (null? rest)
|
233
|
+
first
|
234
|
+
(if (null? first)
|
235
|
+
(apply append rest)
|
191
236
|
(let ([copy (apply list first)])
|
192
237
|
(do ([pair copy (cdr pair)])
|
193
238
|
((null? (cdr pair))
|
data/lib/builtin/primitives.rb
CHANGED
@@ -32,14 +32,6 @@ syntax('define-syntax') do |scope, cells|
|
|
32
32
|
scope[cells.car] = Heist.evaluate(cells.cdr.car, scope)
|
33
33
|
end
|
34
34
|
|
35
|
-
syntax('let-syntax') do |*args|
|
36
|
-
call('let', *args)
|
37
|
-
end
|
38
|
-
|
39
|
-
syntax('letrec-syntax') do |*args|
|
40
|
-
call('letrec', *args)
|
41
|
-
end
|
42
|
-
|
43
35
|
syntax('syntax-rules') do |scope, cells|
|
44
36
|
Macro.new(scope, cells.car, cells.cdr)
|
45
37
|
end
|
@@ -74,7 +66,8 @@ end
|
|
74
66
|
|
75
67
|
# Control structures
|
76
68
|
|
77
|
-
# (begin) simply executes a series of
|
69
|
+
# (begin) simply executes a series of expressions in the
|
70
|
+
# current scope and returns the value of the last one
|
78
71
|
syntax('begin') do |scope, cells|
|
79
72
|
Body.new(cells, scope)
|
80
73
|
end
|
@@ -146,12 +139,6 @@ define('exact?') do |value|
|
|
146
139
|
call('rational?', value.imag))
|
147
140
|
end
|
148
141
|
|
149
|
-
# TODO raise an exception if they're not numeric
|
150
|
-
# Returns true iff all arguments are numerically equal
|
151
|
-
define('=') do |*args|
|
152
|
-
args.all? { |arg| arg == args.first }
|
153
|
-
end
|
154
|
-
|
155
142
|
# Returns true iff the arguments are monotonically decreasing
|
156
143
|
define('>') do |*args|
|
157
144
|
result = true
|
@@ -219,8 +206,7 @@ end
|
|
219
206
|
#----------------------------------------------------------------
|
220
207
|
|
221
208
|
# Numerical functions
|
222
|
-
# TODO implement exact
|
223
|
-
# complex functions, exact->inexact and vice versa
|
209
|
+
# TODO implement rationalize, exact->inexact and vice versa
|
224
210
|
|
225
211
|
# Returns the sum of all arguments passed
|
226
212
|
define('+') do |*args|
|
@@ -245,29 +231,6 @@ define('/') do |op1, op2|
|
|
245
231
|
op2.nil? ? Heist.divide(1, op1) : Heist.divide(op1, op2)
|
246
232
|
end
|
247
233
|
|
248
|
-
# (quotient) and (remainder) satisfy
|
249
|
-
#
|
250
|
-
# (= n1 (+ (* n2 (quotient n1 n2))
|
251
|
-
# (remainder n1 n2)))
|
252
|
-
|
253
|
-
# Returns the quotient of two numbers, i.e. performs n1/n2
|
254
|
-
# and rounds toward zero.
|
255
|
-
define('quotient') do |op1, op2|
|
256
|
-
result = op1.to_i.to_f / op2.to_i
|
257
|
-
result > 0 ? result.floor : result.ceil
|
258
|
-
end
|
259
|
-
|
260
|
-
# Returns the remainder after dividing the first operand
|
261
|
-
# by the second
|
262
|
-
define('remainder') do |op1, op2|
|
263
|
-
op1.to_i - op2.to_i * call('quotient', op1, op2)
|
264
|
-
end
|
265
|
-
|
266
|
-
# Returns the first operand modulo the second
|
267
|
-
define('modulo') do |op1, op2|
|
268
|
-
op1.to_i % op2.to_i
|
269
|
-
end
|
270
|
-
|
271
234
|
# Returns the numerator of a number
|
272
235
|
define('numerator') do |value|
|
273
236
|
Rational === value ? value.numerator : value
|
data/lib/heist.rb
CHANGED
@@ -9,7 +9,7 @@ require 'treetop'
|
|
9
9
|
# utility methods that don't belong anywhere else. See the README for an
|
10
10
|
# overview of Heist's features.
|
11
11
|
module Heist
|
12
|
-
VERSION = '0.2.
|
12
|
+
VERSION = '0.2.1'
|
13
13
|
|
14
14
|
ROOT_PATH = File.expand_path(File.dirname(__FILE__))
|
15
15
|
PARSER_PATH = ROOT_PATH + '/parser/'
|
data/lib/parser/nodes.rb
CHANGED
@@ -32,7 +32,7 @@ module Heist
|
|
32
32
|
# and raw Ruby data ready for interpretation using a +Runtime+.
|
33
33
|
def convert!
|
34
34
|
return if @data
|
35
|
-
@data = Runtime::Cons.construct(elements, true) { |c| c.eval }
|
35
|
+
@data = Runtime::Cons.construct(elements[1].elements, true) { |c| c.eval }
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
data/lib/parser/scheme.rb
CHANGED
@@ -6,6 +6,9 @@ module Heist
|
|
6
6
|
@root || :program
|
7
7
|
end
|
8
8
|
|
9
|
+
module Program0 #:nodoc:
|
10
|
+
end
|
11
|
+
|
9
12
|
def _nt_program
|
10
13
|
start_index = index
|
11
14
|
if node_cache[:program].has_key?(index)
|
@@ -14,18 +17,129 @@ module Heist
|
|
14
17
|
return cached
|
15
18
|
end
|
16
19
|
|
17
|
-
|
20
|
+
i0, s0 = index, []
|
21
|
+
r2 = _nt_shebang
|
22
|
+
if r2
|
23
|
+
r1 = r2
|
24
|
+
else
|
25
|
+
r1 = SyntaxNode.new(input, index...index)
|
26
|
+
end
|
27
|
+
s0 << r1
|
28
|
+
if r1
|
29
|
+
s3, i3 = [], index
|
30
|
+
loop do
|
31
|
+
r4 = _nt_cell
|
32
|
+
if r4
|
33
|
+
s3 << r4
|
34
|
+
else
|
35
|
+
break
|
36
|
+
end
|
37
|
+
end
|
38
|
+
r3 = SyntaxNode.new(input, i3...index, s3)
|
39
|
+
s0 << r3
|
40
|
+
end
|
41
|
+
if s0.last
|
42
|
+
r0 = (Program).new(input, i0...index, s0)
|
43
|
+
r0.extend(Program0)
|
44
|
+
else
|
45
|
+
self.index = i0
|
46
|
+
r0 = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
node_cache[:program][start_index] = r0
|
50
|
+
|
51
|
+
return r0
|
52
|
+
end
|
53
|
+
|
54
|
+
module Shebang0 #:nodoc:
|
55
|
+
end
|
56
|
+
|
57
|
+
module Shebang1 #:nodoc:
|
58
|
+
end
|
59
|
+
|
60
|
+
def _nt_shebang
|
61
|
+
start_index = index
|
62
|
+
if node_cache[:shebang].has_key?(index)
|
63
|
+
cached = node_cache[:shebang][index]
|
64
|
+
@index = cached.interval.end if cached
|
65
|
+
return cached
|
66
|
+
end
|
67
|
+
|
68
|
+
i0, s0 = index, []
|
69
|
+
s1, i1 = [], index
|
18
70
|
loop do
|
19
|
-
|
20
|
-
if
|
21
|
-
|
71
|
+
r2 = _nt_space
|
72
|
+
if r2
|
73
|
+
s1 << r2
|
22
74
|
else
|
23
75
|
break
|
24
76
|
end
|
25
77
|
end
|
26
|
-
|
78
|
+
r1 = SyntaxNode.new(input, i1...index, s1)
|
79
|
+
s0 << r1
|
80
|
+
if r1
|
81
|
+
if input.index("#!", index) == index
|
82
|
+
r3 = (SyntaxNode).new(input, index...(index + 2))
|
83
|
+
@index += 2
|
84
|
+
else
|
85
|
+
terminal_parse_failure("#!")
|
86
|
+
r3 = nil
|
87
|
+
end
|
88
|
+
s0 << r3
|
89
|
+
if r3
|
90
|
+
s4, i4 = [], index
|
91
|
+
loop do
|
92
|
+
i5, s5 = index, []
|
93
|
+
i6 = index
|
94
|
+
if input.index(Regexp.new('[\\n\\r]'), index) == index
|
95
|
+
r7 = (SyntaxNode).new(input, index...(index + 1))
|
96
|
+
@index += 1
|
97
|
+
else
|
98
|
+
r7 = nil
|
99
|
+
end
|
100
|
+
if r7
|
101
|
+
r6 = nil
|
102
|
+
else
|
103
|
+
self.index = i6
|
104
|
+
r6 = SyntaxNode.new(input, index...index)
|
105
|
+
end
|
106
|
+
s5 << r6
|
107
|
+
if r6
|
108
|
+
if index < input_length
|
109
|
+
r8 = (SyntaxNode).new(input, index...(index + 1))
|
110
|
+
@index += 1
|
111
|
+
else
|
112
|
+
terminal_parse_failure("any character")
|
113
|
+
r8 = nil
|
114
|
+
end
|
115
|
+
s5 << r8
|
116
|
+
end
|
117
|
+
if s5.last
|
118
|
+
r5 = (SyntaxNode).new(input, i5...index, s5)
|
119
|
+
r5.extend(Shebang0)
|
120
|
+
else
|
121
|
+
self.index = i5
|
122
|
+
r5 = nil
|
123
|
+
end
|
124
|
+
if r5
|
125
|
+
s4 << r5
|
126
|
+
else
|
127
|
+
break
|
128
|
+
end
|
129
|
+
end
|
130
|
+
r4 = SyntaxNode.new(input, i4...index, s4)
|
131
|
+
s0 << r4
|
132
|
+
end
|
133
|
+
end
|
134
|
+
if s0.last
|
135
|
+
r0 = (SyntaxNode).new(input, i0...index, s0)
|
136
|
+
r0.extend(Shebang1)
|
137
|
+
else
|
138
|
+
self.index = i0
|
139
|
+
r0 = nil
|
140
|
+
end
|
27
141
|
|
28
|
-
node_cache[:
|
142
|
+
node_cache[:shebang][start_index] = r0
|
29
143
|
|
30
144
|
return r0
|
31
145
|
end
|
data/lib/parser/scheme.tt
CHANGED
@@ -5,8 +5,9 @@ module Heist
|
|
5
5
|
# code. We might throw it away and use symbols at some point. I had this
|
6
6
|
# idea for storing metadata on +Identifier+ objects to speed things up
|
7
7
|
# but it pretty much came to nothing. Its one saving grace is that it
|
8
|
-
# needs to be treated as an +Expression+ class and
|
9
|
-
#
|
8
|
+
# needs to be treated as an +Expression+ class, and also that we need
|
9
|
+
# to store some extra metadata on +Identifier+ objects, and I don't want
|
10
|
+
# to modify core Ruby classes
|
10
11
|
class Identifier
|
11
12
|
include Expression
|
12
13
|
|
@@ -15,14 +16,21 @@ module Heist
|
|
15
16
|
alias :inspect :to_s
|
16
17
|
|
17
18
|
# An +Identifier+ is initialized using a string that becomes the
|
18
|
-
# name of the identifier.
|
19
|
-
|
19
|
+
# name of the identifier. The optional second parameter is used to
|
20
|
+
# specify the original name of an identifier if it has been renamed
|
21
|
+
# during a macro expansion.
|
22
|
+
def initialize(name, orginal_name = nil)
|
20
23
|
@name = name.to_s
|
24
|
+
@orginal_name = (orginal_name || name).to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
def name
|
28
|
+
@orginal_name
|
21
29
|
end
|
22
30
|
|
23
31
|
# Returns +true+ if the receiver has the same name as the argument.
|
24
32
|
def ==(other)
|
25
|
-
Identifier === other and @
|
33
|
+
Identifier === other and @orginal_name == other.name
|
26
34
|
end
|
27
35
|
|
28
36
|
# Returns a raw Ruby representation of the identifier, for which
|
data/test/hygienic.scm
CHANGED
data/test/lists.scm
CHANGED
@@ -56,6 +56,11 @@
|
|
56
56
|
(assert-equal '(a (b) (c)) (append '(a (b)) '((c))))
|
57
57
|
(assert-equal '(a b c . d) (append '(a b) '(c . d)))
|
58
58
|
(assert-equal 'a (append '() 'a))
|
59
|
+
(assert-equal '(a b) (append '(a b) '()))
|
60
|
+
(assert-equal '(a (b) (c) (1 2) 3) (append '(a (b)) '((c)) '() '((1 2) 3)))
|
61
|
+
|
62
|
+
(let ([base '(a b)])
|
63
|
+
(assert (eqv? (cddr (append '(1 2) base)) base)))
|
59
64
|
|
60
65
|
(assert-equal '(4 3 2 1) (reverse '(1 2 3 4)))
|
61
66
|
(assert-equal '(4 (3 5) 2 1) (reverse '(1 2 (3 5) 4)))
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Coglan
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-19 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -40,7 +40,7 @@ dependencies:
|
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 1.
|
43
|
+
version: 1.12.2
|
44
44
|
version:
|
45
45
|
description: ""
|
46
46
|
email:
|
@@ -53,7 +53,6 @@ extra_rdoc_files:
|
|
53
53
|
- History.txt
|
54
54
|
- Manifest.txt
|
55
55
|
- README.txt
|
56
|
-
- test/plt-macros.txt
|
57
56
|
files:
|
58
57
|
- History.txt
|
59
58
|
- Manifest.txt
|
@@ -112,7 +111,9 @@ files:
|
|
112
111
|
- test/unhygienic.scm
|
113
112
|
- test/vars.scm
|
114
113
|
has_rdoc: true
|
115
|
-
homepage:
|
114
|
+
homepage: http://github.com/jcoglan/heist
|
115
|
+
licenses: []
|
116
|
+
|
116
117
|
post_install_message:
|
117
118
|
rdoc_options:
|
118
119
|
- --main
|
@@ -134,9 +135,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
135
|
requirements: []
|
135
136
|
|
136
137
|
rubyforge_project: heist
|
137
|
-
rubygems_version: 1.3.
|
138
|
+
rubygems_version: 1.3.3
|
138
139
|
signing_key:
|
139
|
-
specification_version:
|
140
|
+
specification_version: 3
|
140
141
|
summary: ""
|
141
142
|
test_files:
|
142
143
|
- test/test_heist.rb
|