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 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