heist 0.1.0 → 0.2.0

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/test/numbers.scm CHANGED
@@ -1,19 +1,41 @@
1
- (assert (eqv? 42 42))
2
- (assert (not (eqv? 42 #f)))
3
- (assert (not (eqv? 42 42.0)))
4
1
  (assert (= 42 42))
5
2
  (assert (= 42 42.0))
6
3
 
7
4
  (assert (number? 42))
8
5
  (assert (not (number? #t)))
9
- ;(assert (complex? 2+3i))
10
- ;(assert (not (real? 2+3i)))
6
+ (assert (complex? 2+3i))
7
+ (assert (not (real? 2+3i)))
11
8
  (assert (real? 3.1416))
12
9
  (assert (real? 22/7))
13
10
  (assert (real? 42))
14
- ;(assert (not (rational? 2+3i)))
15
- ;(assert (not (rational? 3.1416)))
16
- ;(assert (rational? 22/7))
11
+ (assert (not (rational? 2+3i)))
12
+ (assert (not (rational? 3.1416)))
13
+ (assert (rational? 22/7))
17
14
  (assert (not (integer? 22/7)))
18
15
  (assert (integer? 42))
19
16
 
17
+ (assert (zero? 0))
18
+ (assert (not (zero? 1)))
19
+ (assert (odd? 1))
20
+ (assert (not (odd? 2)))
21
+ (assert (even? 6))
22
+ (assert (not (even? 7)))
23
+ (assert (positive? 5))
24
+ (assert (not (positive? -4)))
25
+ (assert (negative? -13))
26
+ (assert (not (negative? 0)))
27
+
28
+ (assert-equal 9 (max 8 2 7 3 9 5))
29
+ (assert-equal 2 (min 8 2 7 3 9 5))
30
+
31
+ (assert (exact? 8))
32
+ (assert (exact? 4/3))
33
+ (assert (exact? 5+3i))
34
+ (assert (inexact? 8.5))
35
+ (assert (inexact? 8.5+4i))
36
+
37
+ (assert (rational? (/ 4 3)))
38
+ (assert (rational? (/ 4)))
39
+
40
+ (assert-equal 1 (make-polar 1 0))
41
+
data/test/test_heist.rb CHANGED
@@ -13,7 +13,7 @@ Class.new(Test::Unit::TestCase) do
13
13
  def setup
14
14
  return @@env if @@env
15
15
  @@env = Heist::Runtime.new(Heist::BIN_SPEC.parse($args))
16
- Heist.info(@@env)
16
+ puts @@env.info
17
17
 
18
18
  @@env.define('assert') do |value|
19
19
  assert(value)
@@ -21,18 +21,21 @@ Class.new(Test::Unit::TestCase) do
21
21
  @@env.define('assert-equal') do |expected, actual|
22
22
  assert_equal(expected, actual)
23
23
  end
24
- @@env.syntax('assert-raise') do |scope, name, expression|
25
- exception = Heist.const_get(name.to_s)
26
- assert_raise(exception) { @@env.eval(expression) }
24
+ @@env.syntax('assert-raise') do |scope, cells|
25
+ exception = Heist.const_get(cells.car.to_s)
26
+ assert_raise(exception) { scope.eval(cells.cdr.car) }
27
27
  end
28
28
  end
29
29
 
30
30
  %w[ booleans
31
31
  numbers
32
+ lists
33
+ equivalence
32
34
  arithmetic
33
35
  define_values
34
36
  define_functions
35
37
  closures
38
+ functional
36
39
  let
37
40
  conditionals
38
41
  file_loading
@@ -45,6 +48,39 @@ Class.new(Test::Unit::TestCase) do
45
48
  end
46
49
  end
47
50
 
51
+ def test_cons
52
+ cons = Heist::Runtime::Cons
53
+
54
+ empty = cons[[]]
55
+ assert empty.list?
56
+ assert !empty.pair?
57
+ assert_equal cons::NULL, empty
58
+ assert_equal 0, empty.length
59
+
60
+ single = cons[[4]]
61
+ assert single.list?
62
+ assert single.pair?
63
+ assert_equal 1, single.length
64
+
65
+ multi = cons[[2,4,7]]
66
+ assert multi.list?
67
+ assert multi.pair?
68
+ assert_equal 3, multi.length
69
+
70
+ multi.tail.cdr = 8
71
+ assert multi.pair?
72
+ assert !multi.list?
73
+ assert_raise(Heist::TypeError) { multi.size }
74
+
75
+ nested = cons[[2,4,6,cons.new(7,8)]]
76
+ assert nested.list?
77
+ assert nested.pair?
78
+ assert_equal 4, nested.length
79
+ assert !nested.cdr.cdr.cdr.car.list?
80
+ assert nested.cdr.cdr.cdr.car.pair?
81
+ assert_equal 8, nested.cdr.cdr.cdr.car.cdr
82
+ end
83
+
48
84
  def test_macro_hygiene
49
85
  @@env.run($dir + '/' + (@@env.hygienic? ? 'hygienic' : 'unhygienic'))
50
86
  end
@@ -55,14 +91,25 @@ Class.new(Test::Unit::TestCase) do
55
91
  end
56
92
 
57
93
  def test_quotes
58
- assert_equal 7, @@env.eval("(+ 3 4)")
59
- assert_equal [:+, 3, 4], @@env.eval("'(+ 3 4)").to_a
60
- assert Heist::Runtime::List === @@env.eval("'(+ 3 4)")
61
- assert_equal 7, @@env.eval("(+ '3 4)")
62
- assert_equal [:+, [:-, 7, 9], 4], @@env.eval("'(+ (- 7 9) 4)").to_a
63
- assert_equal [7, 9, 6], @@env.eval("`(7 ,(+ 4 5) 6)").to_a
64
- assert Heist::Runtime::List === @@env.eval("`(7 ,(+ 4 5) 6)")
65
- assert_equal [3, 7, 6, 2, 6, 9], @@env.eval("`(3 7 6 ,@((lambda () '(2 6))) 9)").to_a
94
+ cons = Heist::Runtime::Cons
95
+ c = cons.method(:new)
96
+
97
+ assert_equal 7, @@env.eval("(+ 3 4)")
98
+ assert_equal [:+, 3, 4], @@env.eval("'(+ 3 4)").to_ruby
99
+ assert cons === @@env.eval("'(+ 3 4)")
100
+ assert_equal 7, @@env.eval("(+ '3 4)")
101
+ assert_equal c[1,8], @@env.eval("'(1 . 8)")
102
+ assert_equal c[1,8], @@env.eval("`(1 . 8)")
103
+ assert_equal [:+, [:-, 7, 9], 4], @@env.eval("'(+ (- 7 9) 4)").to_ruby
104
+ assert_equal [7, 9, 6], @@env.eval("`(7 ,(+ 4 5) 6)").to_ruby
105
+ assert cons === @@env.eval("`(7 ,(+ 4 5) 6)")
106
+ assert_equal [3, 7, 6, 2, 6, 9], @@env.eval("`(3 7 6 ,@((lambda () '(2 6))) 9)").to_ruby
107
+ assert_equal [1, 2, 10], @@env.eval("`(1 2 ,(+ 4 6))").to_ruby
108
+ assert_equal [3, 2, 9, 8], @@env.eval("`(,(/ 9 3) 2 ,@(list 9 8))").to_ruby
109
+ assert_equal [1, 2, 4, 9, 8, 5], @@env.eval("`(,@(list 1 2) 4 ,@(list 9 8) 5)").to_ruby
110
+ assert_equal c[9,c[8,c[5,7]]], @@env.eval("`(,@(list 9 8) 5 . 7)")
111
+ assert_equal c[9,c[8,24]], @@env.eval("`(,@(list 9 8) . ,(* 4 6))")
112
+ assert_equal [:quote, []], @@env.eval("''()").to_ruby
66
113
  end
67
114
 
68
115
  def test_birds
@@ -80,5 +127,13 @@ Class.new(Test::Unit::TestCase) do
80
127
 
81
128
  assert_equal 45, @@env.eval("((K 45) 6)")
82
129
  end
130
+
131
+ def test_ruby_execution
132
+ expr = [[:lambda, [:x], [:+, 1, :x]], 3]
133
+ assert_equal 4, @@env.exec(expr)
134
+ list = Heist.parse(expr)
135
+ assert Heist::Runtime::Cons === list
136
+ assert_equal expr, list.to_ruby
137
+ end
83
138
  end
84
139
 
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.1.0
4
+ version: 0.2.0
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-02-24 00:00:00 +00:00
12
+ date: 2009-04-01 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -57,37 +57,37 @@ extra_rdoc_files:
57
57
  files:
58
58
  - History.txt
59
59
  - Manifest.txt
60
- - README.txt
61
60
  - Rakefile
61
+ - README.txt
62
62
  - bin/heist
63
- - lib/heist.rb
64
63
  - lib/bin_spec.rb
64
+ - lib/heist.rb
65
65
  - lib/repl.rb
66
+ - lib/builtin/library.scm
66
67
  - lib/builtin/primitives.rb
67
- - lib/builtin/syntax.rb
68
68
  - lib/builtin/syntax.scm
69
- - lib/builtin/library.scm
70
- - lib/parser/scheme.tt
71
- - lib/parser/scheme.rb
72
69
  - lib/parser/nodes.rb
73
- - lib/runtime/runtime.rb
74
- - lib/runtime/data/expression.rb
75
- - lib/runtime/data/identifier.rb
76
- - lib/runtime/data/list.rb
70
+ - lib/parser/ruby.rb
71
+ - lib/parser/scheme.rb
72
+ - lib/parser/scheme.tt
73
+ - lib/runtime/callable/continuation.rb
77
74
  - lib/runtime/callable/function.rb
75
+ - lib/runtime/callable/syntax.rb
78
76
  - lib/runtime/callable/macro.rb
79
77
  - lib/runtime/callable/macro/matches.rb
80
- - lib/runtime/callable/macro/splice.rb
78
+ - lib/runtime/callable/macro/tree.rb
81
79
  - lib/runtime/callable/macro/expansion.rb
82
- - lib/runtime/callable/continuation.rb
83
- - lib/runtime/stack.rb
84
- - lib/runtime/stackless.rb
80
+ - lib/runtime/data/cons.rb
81
+ - lib/runtime/data/expression.rb
82
+ - lib/runtime/data/identifier.rb
83
+ - lib/runtime/binding.rb
85
84
  - lib/runtime/frame.rb
85
+ - lib/runtime/runtime.rb
86
86
  - lib/runtime/scope.rb
87
- - lib/runtime/binding.rb
87
+ - lib/runtime/stack.rb
88
+ - lib/runtime/stackless.rb
88
89
  - lib/stdlib/benchmark.scm
89
90
  - lib/stdlib/birdhouse.scm
90
- - test/test_heist.rb
91
91
  - test/arithmetic.scm
92
92
  - test/benchmarks.scm
93
93
  - test/booleans.scm
@@ -97,15 +97,19 @@ files:
97
97
  - test/define_functions.scm
98
98
  - test/define_values.scm
99
99
  - test/delay.scm
100
+ - test/equivalence.scm
100
101
  - test/file_loading.scm
102
+ - test/functional.scm
103
+ - test/hygienic.scm
101
104
  - test/let.scm
102
105
  - test/lib.scm
106
+ - test/lists.scm
103
107
  - test/macro-helpers.scm
104
108
  - test/macros.scm
105
- - test/hygienic.scm
106
- - test/unhygienic.scm
107
- - test/plt-macros.txt
108
109
  - test/numbers.scm
110
+ - test/plt-macros.txt
111
+ - test/test_heist.rb
112
+ - test/unhygienic.scm
109
113
  - test/vars.scm
110
114
  has_rdoc: true
111
115
  homepage: Heist is a Scheme interpreter written in Ruby. It provides an executable
@@ -1,166 +0,0 @@
1
- # Control structures
2
-
3
- # (cond) goes through a list of tests, evaluating each one
4
- # in order of appearance. Once a matching precondition is
5
- # found, its consequent is tail-called and no further
6
- # preconditions are evaluated.
7
- syntax('cond') do |scope, *pairs|
8
- result = nil
9
- pairs.each do |list|
10
- next if result
11
- test = list.first.to_s == 'else' || Heist.evaluate(list.first, scope)
12
- next unless test
13
- result = list[1].to_s == '=>' ?
14
- Heist.evaluate(list[2], scope).call(scope, [test]) :
15
- Body.new(list.rest, scope)
16
- end
17
- result
18
- end
19
-
20
- # (case) acts like Ruby's case statement. The value of the
21
- # given expression is compared against a series of lists;
22
- # once a list is found to include the value, the expressions
23
- # following the list are evaluated and no further lists
24
- # are tested.
25
- syntax('case') do |scope, key, *clauses|
26
- value = Heist.evaluate(key, scope)
27
- result = nil
28
- clauses.each do |list|
29
- next if result
30
- values = call('quote', scope, list.first)
31
- result = Body.new(list.rest, scope) if values == :else or
32
- values.include?(value)
33
- end
34
- result
35
- end
36
-
37
- #----------------------------------------------------------------
38
-
39
- # Binding constructs
40
-
41
- # (let), (let*) and (letrec) each create a new scope and bind
42
- # values to some symbols before executing a series of lists.
43
- # They differ according to how they evaluate the bound values.
44
-
45
- # (let) evaluates values in the enclosing scope, so lambdas will
46
- # not be able to refer to other values assigned using the (let).
47
- syntax('let') do |scope, assignments, *body|
48
- if Identifier === assignments
49
- name = assignments
50
- assignments = body.first
51
- formals = assignments.map { |pair| pair.first }
52
- values = assignments.map { |pair| pair.last }
53
- closure = Scope.new(scope)
54
- closure[name] = Function.new(closure, formals, body[1..-1])
55
- closure[name].call(scope, values)
56
- else
57
- closure = Scope.new(scope)
58
- assignments.each do |assign|
59
- closure[assign.first] = Heist.evaluate(assign.last, scope)
60
- end
61
- call('begin', closure, *body)
62
- end
63
- end
64
-
65
- # (let*) creates a new scope for each variable and evaluates
66
- # each expression in its enclosing scope. Basically a shorthand
67
- # for several nested (let)s. Variables may refer to those that
68
- # preceed them but not vice versa.
69
- syntax('let*') do |scope, assignments, *body|
70
- closure = assignments.inject(scope) do |outer, assign|
71
- inner = Scope.new(outer)
72
- inner[assign.first] = Heist.evaluate(assign.last, outer)
73
- inner
74
- end
75
- call('begin', closure, *body)
76
- end
77
-
78
- # (letrec) evaluates values in the inner scope, so lambdas are
79
- # able to refer to other values assigned using the (letrec).
80
- syntax('letrec') do |scope, assignments, *body|
81
- closure = Scope.new(scope)
82
- assignments.each do |assign|
83
- closure[assign.first] = Heist.evaluate(assign.last, closure)
84
- end
85
- call('begin', closure, *body)
86
- end
87
-
88
- syntax('let-syntax') do |*args|
89
- call('let', *args)
90
- end
91
-
92
- syntax('letrec-syntax') do |*args|
93
- call('letrec', *args)
94
- end
95
-
96
- #----------------------------------------------------------------
97
-
98
- # Iteration
99
-
100
- # (do) is similar to the 'while' construct in procedural
101
- # languages. It assigns initial values to a set of variables,
102
- # then performs the list of given commands in a loop. If
103
- # before any iteration the test is found to be false, the
104
- # loop is halted and the value of the expression following
105
- # the test is returned.
106
- syntax('do') do |scope, assignments, test, *commands|
107
- closure = Scope.new(scope)
108
- assignments.each do |assign|
109
- closure[assign.first] = Heist.evaluate(assign[1], scope)
110
- end
111
- while not Heist.evaluate(test.first, closure)
112
- commands.each { |expr| Heist.evaluate(expr, closure) }
113
- temp = {}
114
- assignments.each do |assign|
115
- step = assign[2] || assign[0]
116
- temp[assign.first] = Heist.evaluate(step, closure)
117
- end
118
- assignments.each do |assign|
119
- closure[assign.first] = temp[assign.first]
120
- end
121
- end
122
- call('begin', closure, *test.rest)
123
- end
124
-
125
- #----------------------------------------------------------------
126
-
127
- # Boolean combinators
128
-
129
- # (and) returns the first falsey value returned by the list
130
- # of expressions, or returns the value of the last expression
131
- # if all values are truthy.
132
- syntax('and') do |scope, *args|
133
- result = true
134
- args.each do |arg|
135
- next if not result
136
- result = Heist.evaluate(arg, scope)
137
- end
138
- result
139
- end
140
-
141
- # (or) returns the first truthy value returned by the list
142
- # of expressions, or returns the value of the last expression
143
- # if all values are falsey.
144
- syntax('or') do |scope, *args|
145
- result = false
146
- args.each do |arg|
147
- next if result
148
- result = Heist.evaluate(arg, scope)
149
- end
150
- result
151
- end
152
-
153
- #----------------------------------------------------------------
154
-
155
- # Delayed evaluation
156
-
157
- # (delay) allows the evaluation of an expression to be delayed
158
- # by wrapping it in a promise. Use (force) to evaluate the promise
159
- # at a later time. The expression inside a promise is only
160
- # ever evaluated once, so a promise can be implemented as a
161
- # memoized closure.
162
- syntax('delay') do |scope, expression|
163
- promise = Binding.new(expression, scope)
164
- Function.new(scope) { promise.extract }
165
- end
166
-
@@ -1,56 +0,0 @@
1
- module Heist
2
- class Runtime
3
- class Macro
4
-
5
- class Splice
6
- attr_reader :name, :depth
7
-
8
- def initialize(name, depth)
9
- @name, @depth = name, depth
10
- @data = []
11
- (0...@depth).inject(@data) { |list, d| list << []; list.last }
12
- @indexes = (0..@depth).map { 0 }
13
- @stack = []
14
- end
15
-
16
- def <<(value)
17
- @stack.pop.call() while not @stack.empty?
18
- tail(@depth) << value
19
- end
20
-
21
- def mark!(depth)
22
- @stack << lambda { tail(depth) << [] }
23
- end
24
-
25
- def size(depth)
26
- current(depth).size
27
- end
28
-
29
- def read
30
- current(@depth)[@indexes[@depth]]
31
- end
32
-
33
- def shift!(depth)
34
- @indexes[depth] += 1
35
- @indexes[depth] = 0 if @indexes[depth] >= current(depth).size
36
- end
37
-
38
- def to_s(depth = 0)
39
- "#{@name}#{' ...' * (@depth - depth)}"
40
- end
41
-
42
- private
43
-
44
- def tail(depth)
45
- (0...depth).inject(@data) { |list, d| list.last }
46
- end
47
-
48
- def current(depth)
49
- @indexes[0...depth].inject(@data) { |list, i| list[i] }
50
- end
51
- end
52
-
53
- end
54
- end
55
- end
56
-