heist 0.2.0 → 0.2.1

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.
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
@@ -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? first)
188
- (apply append rest)
189
- (if (null? rest)
190
- first
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))
@@ -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 lists in the current scope.
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?, inexact?, numerator, denominator, rationalize,
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.0'
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
- s0, i0 = [], index
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
- r1 = _nt_cell
20
- if r1
21
- s0 << r1
71
+ r2 = _nt_space
72
+ if r2
73
+ s1 << r2
22
74
  else
23
75
  break
24
76
  end
25
77
  end
26
- r0 = Program.new(input, i0...index, s0)
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[:program][start_index] = r0
142
+ node_cache[:shebang][start_index] = r0
29
143
 
30
144
  return r0
31
145
  end
data/lib/parser/scheme.tt CHANGED
@@ -1,7 +1,11 @@
1
1
  module Heist
2
2
  grammar Scheme
3
3
  rule program
4
- cell* <Program>
4
+ shebang? cell* <Program>
5
+ end
6
+
7
+ rule shebang
8
+ space* "#!" (![\n\r] .)*
5
9
  end
6
10
 
7
11
  rule cell
@@ -140,7 +140,7 @@ module Heist
140
140
  return id unless @calling_scope.defined?(id)
141
141
  i = 1
142
142
  i += 1 while @calling_scope.defined?("#{id}#{i}")
143
- Identifier.new("#{id}#{i}")
143
+ Identifier.new("#{id}#{i}", id)
144
144
  end
145
145
  end
146
146
 
@@ -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 I don't want to
9
- # modify core Ruby classes.
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
- def initialize(name)
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 @name == other.to_s
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
@@ -37,3 +37,10 @@
37
37
  (set! plus -)
38
38
  (assert-equal -1 (dyn-plus-call))
39
39
 
40
+ (let-syntax ([ok? (syntax-rules ()
41
+ [(_ expr)
42
+ (cond [expr 'ok]
43
+ [else 'no])])])
44
+ (let ([else #t])
45
+ (assert-equal 'no (ok? #f))))
46
+
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.0
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-04-01 00:00:00 +01:00
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.8.2
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: Heist is a Scheme interpreter written in Ruby. It provides an executable
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.1
138
+ rubygems_version: 1.3.3
138
139
  signing_key:
139
- specification_version: 2
140
+ specification_version: 3
140
141
  summary: ""
141
142
  test_files:
142
143
  - test/test_heist.rb