heist 0.3.1 → 0.3.2

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.
Files changed (43) hide show
  1. data/History.txt +12 -0
  2. data/Manifest.txt +25 -23
  3. data/Rakefile +7 -0
  4. data/lib/builtin/library.rb +1 -0
  5. data/lib/builtin/library.scm +15 -19
  6. data/lib/heist.rb +3 -3
  7. data/lib/parser/nodes.rb +4 -4
  8. data/lib/parser/ruby.rb +6 -1
  9. data/lib/repl.rb +2 -2
  10. data/lib/runtime/binding.rb +11 -0
  11. data/lib/runtime/callable/macro/matches.rb +1 -1
  12. data/lib/runtime/callable/macro/tree.rb +2 -1
  13. data/lib/runtime/data/cons.rb +20 -4
  14. data/lib/runtime/data/identifier.rb +2 -1
  15. data/lib/runtime/runtime.rb +29 -9
  16. data/lib/runtime/scope.rb +32 -9
  17. data/lib/trie.rb +14 -0
  18. data/test/{lib.scm → helpers/lib.scm} +0 -0
  19. data/test/{macro-helpers.scm → helpers/macro-helpers.scm} +0 -0
  20. data/test/{vars.scm → helpers/vars.scm} +0 -0
  21. data/test/{arithmetic.scm → scheme_tests/arithmetic.scm} +0 -0
  22. data/test/{benchmarks.scm → scheme_tests/benchmarks.scm} +0 -0
  23. data/test/{booleans.scm → scheme_tests/booleans.scm} +0 -0
  24. data/test/{closures.scm → scheme_tests/closures.scm} +0 -0
  25. data/test/{conditionals.scm → scheme_tests/conditionals.scm} +0 -0
  26. data/test/{continuations.scm → scheme_tests/continuations.scm} +0 -0
  27. data/test/{define_functions.scm → scheme_tests/define_functions.scm} +0 -0
  28. data/test/{define_values.scm → scheme_tests/define_values.scm} +0 -0
  29. data/test/{delay.scm → scheme_tests/delay.scm} +0 -0
  30. data/test/{equivalence.scm → scheme_tests/equivalence.scm} +0 -0
  31. data/test/{file_loading.scm → scheme_tests/file_loading.scm} +2 -2
  32. data/test/{functional.scm → scheme_tests/functional.scm} +0 -0
  33. data/test/{hygienic.scm → scheme_tests/hygienic.scm} +1 -1
  34. data/test/{let.scm → scheme_tests/let.scm} +0 -0
  35. data/test/{lists.scm → scheme_tests/lists.scm} +0 -0
  36. data/test/{macros.scm → scheme_tests/macros.scm} +42 -1
  37. data/test/{numbers.scm → scheme_tests/numbers.scm} +0 -0
  38. data/test/scheme_tests/protection.scm +13 -0
  39. data/test/{strings.scm → scheme_tests/strings.scm} +0 -0
  40. data/test/{unhygienic.scm → scheme_tests/unhygienic.scm} +1 -1
  41. data/test/{vectors.scm → scheme_tests/vectors.scm} +0 -0
  42. data/test/test_heist.rb +43 -36
  43. metadata +52 -28
data/History.txt CHANGED
@@ -1,3 +1,15 @@
1
+ === Version 0.3.2 (2010-01-16)
2
+
3
+ * Built-in library code is executed in a protected scope so that redefinitions of
4
+ core functions in user code do not break implementations of library functions
5
+ * The core library is compiled to a Ruby representation that loads faster
6
+ * Macros correctly transcribe empty lists appearing in the input
7
+ * Macro variables may appear at greater ellipsis depth in templates than in corresponding patterns
8
+ * Fix bug in macro literal identifier recognition
9
+ * Identifier comparison is now case-insensitive
10
+ * The REPL prints quote/unquote expressions using shorthand notation
11
+
12
+
1
13
  === Version 0.3.1 (2009-08-23)
2
14
 
3
15
  * Added the (rationalize) procedure
data/Manifest.txt CHANGED
@@ -8,6 +8,7 @@ lib/heist.rb
8
8
  lib/repl.rb
9
9
  lib/trie.rb
10
10
  lib/builtin/library.scm
11
+ lib/builtin/library.rb
11
12
  lib/builtin/primitives.rb
12
13
  lib/builtin/syntax.scm
13
14
  lib/parser/nodes.rb
@@ -34,29 +35,30 @@ lib/runtime/stack.rb
34
35
  lib/runtime/stackless.rb
35
36
  lib/stdlib/benchmark.scm
36
37
  lib/stdlib/birdhouse.scm
37
- test/arithmetic.scm
38
- test/benchmarks.scm
39
- test/booleans.scm
40
- test/closures.scm
41
- test/conditionals.scm
42
- test/continuations.scm
43
- test/define_functions.scm
44
- test/define_values.scm
45
- test/delay.scm
46
- test/equivalence.scm
47
- test/file_loading.scm
48
- test/functional.scm
49
- test/hygienic.scm
50
- test/let.scm
51
- test/lib.scm
52
- test/lists.scm
53
- test/macro-helpers.scm
54
- test/macros.scm
55
- test/numbers.scm
38
+ test/helpers/lib.scm
39
+ test/helpers/macro-helpers.scm
40
+ test/helpers/vars.scm
41
+ test/scheme_tests/arithmetic.scm
42
+ test/scheme_tests/benchmarks.scm
43
+ test/scheme_tests/booleans.scm
44
+ test/scheme_tests/closures.scm
45
+ test/scheme_tests/conditionals.scm
46
+ test/scheme_tests/continuations.scm
47
+ test/scheme_tests/define_functions.scm
48
+ test/scheme_tests/define_values.scm
49
+ test/scheme_tests/delay.scm
50
+ test/scheme_tests/equivalence.scm
51
+ test/scheme_tests/file_loading.scm
52
+ test/scheme_tests/functional.scm
53
+ test/scheme_tests/hygienic.scm
54
+ test/scheme_tests/let.scm
55
+ test/scheme_tests/lists.scm
56
+ test/scheme_tests/macros.scm
57
+ test/scheme_tests/numbers.scm
58
+ test/scheme_tests/protection.scm
59
+ test/scheme_tests/strings.scm
60
+ test/scheme_tests/unhygienic.scm
61
+ test/scheme_tests/vectors.scm
56
62
  test/plt-macros.txt
57
- test/strings.scm
58
63
  test/test_heist.rb
59
- test/unhygienic.scm
60
- test/vars.scm
61
- test/vectors.scm
62
64
 
data/Rakefile CHANGED
@@ -9,6 +9,13 @@ Hoe.spec('heist') do |p|
9
9
  p.extra_deps = %w(oyster treetop)
10
10
  end
11
11
 
12
+ file "lib/builtin/library.rb" => "lib/builtin/library.scm" do |t|
13
+ program = Heist.parse(File.read t.prerequisites.first).convert!
14
+ File.open(t.name, 'w') { |f| f.write 'program ' + program.to_ruby.inspect }
15
+ end
16
+
17
+ task :compile => "lib/builtin/library.rb"
18
+
12
19
  namespace :spec do
13
20
  task :r5rs do
14
21
  procedures = Dir['r5rs/*.html'].
@@ -0,0 +1 @@
1
+ program [[:define, :quit, :exit], [:define, [:newline], [:display, "\n"]], [:define, [:force, :promise], [:promise]], [:define, :"call/cc", :"call-with-current-continuation"], [:define, :eq?, :eqv?], [:define, [:not, :x], [:if, :x, false, true]], [:define, :true, true], [:define, :false, false], [:define, [:boolean?, :x], [:or, [:eqv?, :x, true], [:eqv?, :x, false]]], [:define, :number?, :complex?], [:define, [:exact?, :x], [:or, [:rational?, :x], [:and, [:not, [:zero?, [:"imag-part", :x]]], [:exact?, [:"real-part", :x]], [:exact?, [:"imag-part", :x]]]]], [:define, [:inexact?, :x], [:not, [:exact?, :x]]], [:define, [:"=", :".", :args], [:define, [:iter, :x, :rest], [:if, [:null?, :rest], true, [:let, [[:y, [:car, :rest]]], [:if, [:or, [:not, [:number?, :x]], [:not, [:number?, :y]], [:not, [:equal?, :x, :y]]], false, [:iter, :x, [:cdr, :rest]]]]]], [:iter, [:car, :args], [:cdr, :args]]], [:define, [:zero?, :x], [:eqv?, :x, 0]], [:define, [:positive?, :x], [:>, :x, 0]], [:define, [:negative?, :x], [:<, :x, 0]], [:define, [:odd?, :x], [:"=", 1, [:remainder, :x, 2]]], [:define, [:even?, :x], [:zero?, [:remainder, :x, 2]]], [:define, [:max, :".", :values], [:foldr, [:lambda, [:a, :b], [:if, [:>=, :a, :b], :a, :b]], [:car, :values], [:cdr, :values]]], [:define, [:min, :".", :values], [:foldr, [:lambda, [:a, :b], [:if, [:<=, :a, :b], :a, :b]], [:car, :values], [:cdr, :values]]], [:define, [:abs, :x], [:if, [:negative?, :x], [:-, :x], :x]], [:define, [:quotient, :x, :y], [:let, [[:result, [:/, :x, :y]]], [[:if, [:positive?, :result], :floor, :ceiling], :result]]], [:define, [:remainder, :x, :y], [:-, [:round, :x], [:*, [:round, :y], [:quotient, :x, :y]]]], [:define, [:modulo, :x, :y], [:+, [:remainder, :x, :y], [:if, [:negative?, [:*, :x, :y]], [:round, :y], 0]]], [:define, [:gcd, :x, :y, :".", :rest], [:if, [:null?, :rest], [:if, [:zero?, :y], [:abs, :x], [:gcd, :y, [:remainder, :x, :y]]], [:apply, :gcd, [:cons, [:gcd, :x, :y], :rest]]]], [:define, [:lcm, :x, :y, :".", :rest], [:if, [:null?, :rest], [:/, [:abs, [:*, :x, :y]], [:gcd, :x, :y]], [:apply, :lcm, [:cons, [:lcm, :x, :y], :rest]]]], [:define, :ceiling, :ceil], [:define, [:rationalize, :x, :tolerance], [:cond, [[:rational?, :x], :x], [[:not, [:zero?, [:"imag-part", :x]]], [:"make-rectangular", [:rationalize, [:"real-part", :x], :tolerance], [:rationalize, [:"imag-part", :x], :tolerance]]], [:else, [:"let*", [[:t, [:abs, :tolerance]], [:a, [:-, :x, :t]], [:b, [:+, :x, :t]]], [:do, [[:i, 1, [:+, :i, 1]], [:z, false]], [[:number?, :z], :z], [:let, [[:p, [:ceiling, [:*, :a, :i]]], [:q, [:floor, [:*, :b, :i]]]], [:if, [:<=, :p, :q], [:set!, :z, [:/, [:if, [:positive?, :p], :p, :q], :i]]]]]]]]], [:define, [:"make-polar", :magnitude, :angle], [:let, [[:re, [:*, :magnitude, [:cos, :angle]]], [:im, [:*, :magnitude, [:sin, :angle]]]], [:"make-rectangular", :re, :im]]], [:define, [:magnitude, :z], [:let, [[:re, [:"real-part", :z]], [:im, [:"imag-part", :z]]], [:sqrt, [:+, [:*, :re, :re], [:*, :im, :im]]]]], [:define, [:angle, :z], [:let, [[:re, [:"real-part", :z]], [:im, [:"imag-part", :z]]], [:atan, :im, :re]]], [:define, [:factorial, :x], [:define, [:iter, :y, :acc], [:if, [:zero?, :y], :acc, [:iter, [:-, :y, 1], [:*, :y, :acc]]]], [:iter, :x, 1]], [:define, [:null?, :object], [:eqv?, [:quote, []], :object]], [:define, [:list?, :object], [:or, [:null?, :object], [:and, [:pair?, :object], [:list?, [:cdr, :object]]]]], [:define, [:list, :".", :args], :args], [:define, [:length, :object], [:define, [:iter, :list, :acc], [:if, [:null?, :list], :acc, [:iter, [:cdr, :list], [:+, 1, :acc]]]], [:iter, :object, 0]], [:define, [:append, :first, :".", :rest], [:cond, [[:null?, :rest], :first], [[:null?, :first], [:apply, :append, :rest]], [:else, [:cons, [:car, :first], [:append, [:cdr, :first], [:apply, :append, :rest]]]]]], [:define, [:reverse, :object], [:if, [:null?, :object], :object, [:append, [:reverse, [:cdr, :object]], [:list, [:car, :object]]]]], [:define, [:"list-tail", :list, :k], [:do, [[:pair, :list, [:cdr, :pair]], [:i, :k, [:-, :i, 1]]], [[:zero?, :i], :pair]]], [:define, [:"list-ref", :list, :k], [:car, [:"list-tail", :list, :k]]], [:define, [:"list-transform-search", :transform], [:lambda, [:predicate], [:lambda, [:object, :list], [:do, [[:pair, :list, [:cdr, :pair]]], [[:or, [:null?, :pair], [:predicate, [:car, [:transform, :pair]], :object]], [:if, [:null?, :pair], false, [:transform, :pair]]]]]]], [:define, :"list-search", [:"list-transform-search", [:lambda, [:x], :x]]], [:define, :memq, [:"list-search", :eq?]], [:define, :memv, [:"list-search", :eqv?]], [:define, :member, [:"list-search", :equal?]], [:define, :"assoc-list-search", [:"list-transform-search", :car]], [:define, :assq, [:"assoc-list-search", :eq?]], [:define, :assv, [:"assoc-list-search", :eqv?]], [:define, :assoc, [:"assoc-list-search", :equal?]], [:define, [:map, :proc, :list1, :".", :list2], [:if, [:null?, :list1], :list1, [:if, [:null?, :list2], [:cons, [:proc, [:car, :list1]], [:map, :proc, [:cdr, :list1]]], [:"let*", [[:all, [:cons, :list1, :list2]], [:args, [:map, :car, :all]], [:rest, [:map, :cdr, :all]]], [:cons, [:apply, :proc, :args], [:apply, :map, [:cons, :proc, :rest]]]]]]], [:define, [:"for-each", :proc, :list1, :".", :list2], [:do, [[:pair, :list1, [:cdr, :pair]], [:others, :list2, [:map, :cdr, :others]]], [[:null?, :pair], [:quote, []]], [:apply, :proc, [:cons, [:car, :pair], [:map, :car, :others]]]]], [:define, [:foldr, :proc, :value, :list], [:if, [:null?, :list], :value, [:proc, [:car, :list], [:foldr, :proc, :value, [:cdr, :list]]]]], [:define, [:sublist, :list, :start, :end], [:cond, [[:null?, :list], [:quote, []]], [[:>, :start, 0], [:sublist, [:cdr, :list], [:-, :start, 1], [:-, :end, 1]]], [[:<=, :end, 0], [:quote, []]], [:else, [:cons, [:car, :list], [:sublist, [:cdr, :list], 0, [:-, :end, 1]]]]]], [:define, [:char, :string], [:if, [:and, [:string?, :string], [:"=", [:"string-length", :string], 1]], [:"string-ref", :string, 0], [:quote, []]]], [:define, [:"char-upper-case?", :letter], [:and, [:char?, :letter], [:let, [[:code, [:"char->integer", :letter]]], [:and, [:>=, :code, 65], [:<=, :code, 90]]]]], [:define, [:"char-lower-case?", :letter], [:and, [:char?, :letter], [:let, [[:code, [:"char->integer", :letter]]], [:and, [:>=, :code, 97], [:<=, :code, 122]]]]], [:define, [:"char-alphabetic?", :char], [:or, [:"char-upper-case?", :char], [:"char-lower-case?", :char]]], [:define, [:"char-numeric?", :char], [:and, [:char?, :char], [:let, [[:code, [:"char->integer", :char]]], [:and, [:>=, :code, 48], [:<=, :code, 57]]]]], [:define, [:"char-whitespace?", :char], [:and, [:char?, :char], [:if, [:member, [:"char->integer", :char], [:quote, [9, 10, 32]]], true, false]]], [:define, [:"char-upcase", :char], [:let, [[:code, [:"char->integer", :char]]], [:if, [:and, [:>=, :code, 97], [:<=, :code, 122]], [:"integer->char", [:-, :code, 32]], [:"integer->char", :code]]]], [:define, [:"char-downcase", :char], [:let, [[:code, [:"char->integer", :char]]], [:if, [:and, [:>=, :code, 65], [:<=, :code, 90]], [:"integer->char", [:+, :code, 32]], [:"integer->char", :code]]]], [:define, [:"char-compare-ci", :operator], [:lambda, [:x, :y], [:operator, [:"char-downcase", :x], [:"char-downcase", :y]]]], [:define, :"char-ci=?", [:"char-compare-ci", :"char=?"]], [:define, :"char-ci<?", [:"char-compare-ci", :"char<?"]], [:define, :"char-ci>?", [:"char-compare-ci", :"char>?"]], [:define, :"char-ci<=?", [:"char-compare-ci", :"char<=?"]], [:define, :"char-ci>=?", [:"char-compare-ci", :"char>=?"]], [:define, [:string, :".", :chars], [:"list->string", :chars]], [:define, [:"string-compare", :string1, :string2, :"char-less?", :"char-greater?"], [:if, [:or, [:not, [:string?, :string1]], [:not, [:string?, :string2]]], [:error, "Expected two strings as arguments"], [:do, [[:pair1, [:"string->list", :string1], [:cdr, :pair1]], [:pair2, [:"string->list", :string2], [:cdr, :pair2]], [:diff, [:quote, []]]], [[:integer?, :diff], :diff], [:set!, :diff, [:cond, [[:null?, :pair1], [:if, [:null?, :pair2], 0, -1]], [[:null?, :pair2], 1], [:else, [:let, [[:char1, [:car, :pair1]], [:char2, [:car, :pair2]]], [:cond, [[:"char-less?", :char1, :char2], -1], [[:"char-greater?", :char1, :char2], 1], [:else, [:quote, []]]]]]]]]]], [:define, [:"string=?", :string1, :string2], [:zero?, [:"string-compare", :string1, :string2, :"char<?", :"char>?"]]], [:define, [:"string-ci=?", :string1, :string2], [:zero?, [:"string-compare", :string1, :string2, :"char-ci<?", :"char-ci>?"]]], [:define, [:"string<?", :string1, :string2], [:"=", [:"string-compare", :string1, :string2, :"char<?", :"char>?"], -1]], [:define, [:"string>?", :string1, :string2], [:"=", [:"string-compare", :string1, :string2, :"char<?", :"char>?"], 1]], [:define, [:"string<=?", :string1, :string2], [:not, [:"string>?", :string1, :string2]]], [:define, [:"string>=?", :string1, :string2], [:not, [:"string<?", :string1, :string2]]], [:define, [:"string-ci<?", :string1, :string2], [:"=", [:"string-compare", :string1, :string2, :"char-ci<?", :"char-ci>?"], -1]], [:define, [:"string-ci>?", :string1, :string2], [:"=", [:"string-compare", :string1, :string2, :"char-ci<?", :"char-ci>?"], 1]], [:define, [:"string-ci<=?", :string1, :string2], [:not, [:"string-ci>?", :string1, :string2]]], [:define, [:"string-ci>=?", :string1, :string2], [:not, [:"string-ci<?", :string1, :string2]]], [:define, [:substring, :string, :start, :end], [:"list->string", [:sublist, [:"string->list", :string], :start, :end]]], [:define, [:"list->string", :chars], [:"let*", [[:size, [:length, :chars]], [:str, [:"make-string", :size]]], [:do, [[:list, :chars, [:cdr, :list]], [:i, 0, [:+, :i, 1]]], [[:"=", :i, :size], :str], [:"string-set!", :str, :i, [:car, :list]]]]], [:define, [:"string->list", :string], [:let, [[:size, [:"string-length", :string]]], [:do, [[:i, :size, [:-, :i, 1]], [:list, [:quote, []], [:cons, [:"string-ref", :string, [:-, :i, 1]], :list]]], [[:zero?, :i], :list]]]], [:define, [:"string-copy", :string], [:"list->string", [:"string->list", :string]]], [:define, [:"string-fill!", :string, :char], [:let, [[:size, [:"string-length", :string]]], [:do, [[:i, :size, [:-, :i, 1]]], [[:zero?, :i], :string], [:"string-set!", :string, [:-, :i, 1], :char]]]], [:define, [:"string-append", :".", :strings], [:"list->string", [:apply, :append, [:map, :"string->list", :strings]]]], [:define, [:vector, :".", :args], [:"list->vector", :args]], [:define, [:"list->vector", :list], [:"let*", [[:size, [:length, :list]], [:"new-vector", [:"make-vector", :size]]], [:do, [[:i, 0, [:+, :i, 1]], [:pair, :list, [:cdr, :pair]]], [[:"=", :i, :size], :"new-vector"], [:"vector-set!", :"new-vector", :i, [:car, :pair]]]]], [:define, [:"vector->list", :vector], [:do, [[:i, [:"vector-length", :vector], [:-, :i, 1]], [:pair, [:quote, []], [:cons, [:"vector-ref", :vector, [:-, :i, 1]], :pair]]], [[:zero?, :i], :pair]]], [:define, [:"vector-fill!", :vector, :fill], [:do, [[:i, [:"vector-length", :vector], [:-, :i, 1]]], [[:zero?, :i], :vector], [:"vector-set!", :vector, [:-, :i, 1], :fill]]]]
@@ -258,15 +258,12 @@
258
258
  ; The final argument is not copied and the return value of
259
259
  ; (append) shares structure with it.
260
260
  (define (append first . rest)
261
- (if (null? rest)
262
- first
263
- (if (null? first)
264
- (apply append rest)
265
- (let ([copy (apply list first)])
266
- (do ([pair copy (cdr pair)])
267
- ((null? (cdr pair))
268
- (set-cdr! pair (apply append rest))))
269
- copy))))
261
+ (cond [(null? rest) first]
262
+ [(null? first) (apply append rest)]
263
+ [else
264
+ (cons (car first)
265
+ (append (cdr first)
266
+ (apply append rest)))]))
270
267
 
271
268
  ; (reverse list)
272
269
  ; Returns a newly allocated list consisting of the
@@ -365,6 +362,14 @@
365
362
  (proc (car list)
366
363
  (foldr proc value (cdr list)))))
367
364
 
365
+ ; (sublist list start end)
366
+ (define (sublist list start end)
367
+ (cond [(null? list) '()]
368
+ [(> start 0) (sublist (cdr list) (- start 1) (- end 1))]
369
+ [(<= end 0) '()]
370
+ [else (cons (car list)
371
+ (sublist (cdr list) 0 (- end 1)))]))
372
+
368
373
  ;----------------------------------------------------------------
369
374
 
370
375
  ; Character functions
@@ -528,16 +533,7 @@
528
533
  ; Returns a string composed of the characters from start (inclusive)
529
534
  ; to end (exclusive) in string
530
535
  (define (substring string start end)
531
- (let ([size (string-length string)])
532
- (cond [(< start 0) (error "start index must be positive")]
533
- [(> end size) (error "end index must be <= the length of string")]
534
- [(> start end) (error "start must be <= end index")]
535
- [else
536
- (let* ([subsize (- end start)]
537
- [substr (make-string subsize)])
538
- (do ([i 0 (+ i 1)])
539
- ((= i subsize) substr)
540
- (string-set! substr i (string-ref string (+ start i)))))])))
536
+ (list->string (sublist (string->list string) start end)))
541
537
 
542
538
  ; (list->string chars)
543
539
  ; Returns a new string formed by combining the list
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.3.1'
12
+ VERSION = '0.3.2'
13
13
 
14
14
  ROOT_PATH = File.expand_path(File.dirname(__FILE__))
15
15
  PARSER_PATH = ROOT_PATH + '/parser/'
@@ -24,8 +24,8 @@ module Heist
24
24
  require ROOT_PATH + '/trie'
25
25
  require ROOT_PATH + '/repl'
26
26
 
27
- LOAD_PATH = [LIB_PATH]
28
- FILE_EXT = ".scm"
27
+ LOAD_PATH = [BUILTIN_PATH, LIB_PATH]
28
+ FILE_EXTS = [""] + %w[.rb .scm .ss]
29
29
 
30
30
  class HeistError < StandardError; end
31
31
  class RuntimeError < HeistError; end
data/lib/parser/nodes.rb CHANGED
@@ -12,10 +12,10 @@ module Heist
12
12
  # In Scheme, this list includes the various quoting symbols that can be used
13
13
  # as shorthands for calling quoting functions.
14
14
  SHORTHANDS = {
15
- "'" => :quote,
16
- "`" => :quasiquote,
17
- "," => :unquote,
18
- ",@" => :'unquote-splicing'
15
+ "'" => 'quote',
16
+ "`" => 'quasiquote',
17
+ "," => 'unquote',
18
+ ",@" => 'unquote-splicing'
19
19
  }
20
20
 
21
21
  # +Program+ is the root of the parse tree; parsing any string of Scheme code
data/lib/parser/ruby.rb CHANGED
@@ -12,11 +12,16 @@ module Heist
12
12
  #
13
13
  class RubyParser
14
14
 
15
+ DOT = :'.'
16
+
15
17
  # Parses a single piece of Ruby data in
16
18
  def parse(source)
17
19
  case source
18
20
  when Array then
19
- Runtime::Cons.construct(source) { |cell| parse(cell) }
21
+ members, tail = *(source[-2] == DOT ? [source[0..-3], source.last] : [source, nil])
22
+ list = Runtime::Cons.construct(members) { |cell| parse(cell) }
23
+ list.tail.cdr = parse(tail) if tail
24
+ list
20
25
  when Symbol then
21
26
  Runtime::Identifier.new(source)
22
27
  else
data/lib/repl.rb CHANGED
@@ -5,7 +5,7 @@ module Heist
5
5
 
6
6
  def initialize(options = {})
7
7
  @runtime = Runtime.new(options)
8
- @scope = Runtime::FileScope.new(@runtime.top_level, File.expand_path('.'))
8
+ @scope = Runtime::FileScope.new(@runtime.user_scope, File.expand_path('.'))
9
9
  @results = []
10
10
 
11
11
  @runtime.define('~') { |x| @results[-x] }
@@ -17,7 +17,7 @@ module Heist
17
17
  puts @runtime.info
18
18
 
19
19
  Readline.completion_append_character = nil
20
- Readline.completion_proc = @runtime.top_level.method(:longest_prefix)
20
+ Readline.completion_proc = @runtime.user_scope.method(:longest_prefix)
21
21
 
22
22
  loop do
23
23
  begin
@@ -42,6 +42,17 @@ module Heist
42
42
  force!
43
43
  end
44
44
 
45
+ # We provide an equality method so that a bound +Identifier+ produced
46
+ # by expanding a macro can be matched against literal identifiers in
47
+ # another macro pattern.
48
+ def ==(identifier)
49
+ @expression == identifier
50
+ end
51
+
52
+ def innermost_binding(identifier)
53
+ @scope
54
+ end
55
+
45
56
  # Returns a string representation of the binding's +Expression+.
46
57
  def to_s
47
58
  @expression.to_s
@@ -138,7 +138,7 @@ module Heist
138
138
  sizes << tree.size(depth) if names.include?(name)
139
139
  end
140
140
 
141
- sizes.uniq!
141
+ sizes = sizes.compact.uniq
142
142
  return sizes.first if sizes.size == 1
143
143
 
144
144
  raise MacroTemplateMismatch.new(
@@ -88,7 +88,6 @@ module Heist
88
88
  # seeing as the pattern should be followed by the same number of ellipses
89
89
  # every time it is encountered.
90
90
  def <<(value)
91
- return if Cons::NULL == value
92
91
  tail(@depth) << value
93
92
  end
94
93
 
@@ -102,6 +101,7 @@ module Heist
102
101
  # to one of the values in <tt>@indexes</tt>. The macro expander calls this
103
102
  # while walking a template to iterate over repetition branches.
104
103
  def shift!(depth)
104
+ return if depth > @depth
105
105
  indexes[depth] += 1
106
106
  indexes[depth] = 0 if indexes[depth] >= current(depth).size
107
107
  end
@@ -110,6 +110,7 @@ module Heist
110
110
  # read branch at the given +depth+. Returns zero if no branch exists at
111
111
  # the given indexes.
112
112
  def size(depth)
113
+ return nil if depth > @depth
113
114
  current(depth).size rescue 0
114
115
  end
115
116
 
@@ -60,6 +60,10 @@ module Heist
60
60
  # An array of all the c[ad]+r functions supported by Heist
61
61
  ACCESSORS = cadr_combos
62
62
 
63
+ # For stringifying purposes, we need an inverted copy of the table
64
+ # of quoting shorthand symbols
65
+ SHORTHANDS = Scheme::SHORTHANDS.invert
66
+
63
67
  class << self
64
68
  # Creates a new list from the elements of the enumerable object +enum+,
65
69
  # and returns the +Cons+ that forms the head of the list. If +enum+ is
@@ -214,15 +218,27 @@ module Heist
214
218
  # Returns a pure Ruby representation of the list, with any Heist
215
219
  # specific objects converted to basic Ruby equivalents.
216
220
  def to_ruby
217
- map { |cell| cell.respond_to?(:to_ruby) ? cell.to_ruby : cell }
221
+ members = []
222
+ tail = each do |cell|
223
+ members << (cell.respond_to?(:to_ruby) ? cell.to_ruby : cell)
224
+ end
225
+ if NULL != tail.cdr
226
+ members << RubyParser::DOT
227
+ members << (tail.cdr.respond_to?(:to_ruby) ? tail.cdr.to_ruby : tail.cdr)
228
+ end
229
+ members
218
230
  end
219
231
 
220
232
  # Returns a Scheme-style string representation of the list.
221
233
  def to_s
222
234
  strings = []
223
- tail = each { |value| strings << Heist.stringify(value) }.cdr
224
- '(' + (strings * ' ') +
225
- (tail == NULL ? '' : ' . ' + tail.to_s) + ')'
235
+ if Identifier === @car and SHORTHANDS.has_key?(@car.to_s) and list? and length == 2
236
+ SHORTHANDS[@car.to_s] + Heist.stringify(@cdr.car)
237
+ else
238
+ tail = each { |value| strings << Heist.stringify(value) }.cdr
239
+ '(' + (strings * ' ') +
240
+ (tail == NULL ? '' : ' . ' + Heist.stringify(tail)) + ')'
241
+ end
226
242
  end
227
243
  alias :inspect :to_s
228
244
  end
@@ -30,7 +30,8 @@ module Heist
30
30
 
31
31
  # Returns +true+ if the receiver has the same name as the argument.
32
32
  def ==(other)
33
- Identifier === other and @orginal_name == other.name
33
+ return true if Binding === other and other == self
34
+ Identifier === other and @orginal_name.downcase == other.name.downcase
34
35
  end
35
36
 
36
37
  # Returns a raw Ruby representation of the identifier, for which
@@ -5,7 +5,14 @@ module Heist
5
5
  # the standard set of primitive functions and special forms as defined
6
6
  # in <tt>lib/builtin</tt>.
7
7
  #
8
- # +Runtime+ exposes several methods from the top-level +Scope+ object,
8
+ # User code runs in another scope descended from the top-level. This is
9
+ # done so that user-level redefinitions of built-in functions do not break
10
+ # other built-in functions that refer to the redefined names; lexical
11
+ # scoping ensures that built-in functions can only refer to other bindings
12
+ # in the top-level scope so user-level bindings do not affect the built-in
13
+ # library functions.
14
+ #
15
+ # +Runtime+ exposes several methods from the user-level +Scope+ object,
9
16
  # allowing runtime objects to be used as interfaces for defining
10
17
  # functions, eval'ing code and running source files.
11
18
  #
@@ -22,9 +29,11 @@ module Heist
22
29
  end
23
30
 
24
31
  extend Forwardable
25
- def_delegators(:@top_level, :[], :eval, :exec, :define, :syntax, :run)
32
+ def_delegators(:@user_scope, :[], :eval, :exec, :program, :define, :syntax, :run, :load)
33
+
34
+ attr_accessor :stack, :top_level, :user_scope
26
35
 
27
- attr_accessor :stack, :top_level
36
+ BUILTIN_LIBRARIES = %w[primitives syntax library]
28
37
 
29
38
  # A +Runtime+ is initialized using a set of options. The available
30
39
  # options include the following, all of which are +false+ unless
@@ -39,16 +48,27 @@ module Heist
39
48
  @continuations = !!options[:continuations]
40
49
  @hygienic = !options[:unhygienic]
41
50
 
42
- @top_level = Scope.new(self)
43
- @stack = stackless? ? Stackless.new : Stack.new
44
-
45
- run("#{ BUILTIN_PATH }primitives.rb")
46
- run("#{ BUILTIN_PATH }syntax.scm")
47
- run("#{ BUILTIN_PATH }library.scm")
51
+ @top_level = Scope.new(self)
52
+ @user_scope = Scope.new(@top_level)
53
+ @stack = stackless? ? Stackless.new : Stack.new
48
54
 
55
+ load_builtins(options)
49
56
  @start_time = Time.now.to_f
50
57
  end
51
58
 
59
+ # To stop user-space redefinitions of built-in functions from breaking
60
+ # the standard library, we define builtins in a privileged scope, one up
61
+ # from the scope that user code runs in. We then bind the names in 'user
62
+ # space' to stop (set!) from reaching into our privileged top level.
63
+ def load_builtins(options = {})
64
+ libraries = (options[:only] || BUILTIN_LIBRARIES) - (options[:except] || [])
65
+
66
+ libraries.each do |library|
67
+ @top_level.run(@top_level.expand_path(library))
68
+ end
69
+ @user_scope.each_var(&@user_scope.method(:[]=))
70
+ end
71
+
52
72
  # Returns the length of time the +Runtime+ has been alive for, as a
53
73
  # number in microseconds.
54
74
  def elapsed_time
data/lib/runtime/scope.rb CHANGED
@@ -135,6 +135,15 @@ module Heist
135
135
  def syntax(name, &block)
136
136
  self[name] = Syntax.new(self, &block)
137
137
  end
138
+
139
+ # Calls the given +block+ with the name of every variable visible in
140
+ # the +Scope+ (given as a +Symbol+) and its corresponding value.
141
+ def each_var(&block)
142
+ @parent.each_var(&block) if @parent.respond_to?(:each_var)
143
+ @symbols.each do |key, value|
144
+ block.call((key * '').to_sym, value)
145
+ end
146
+ end
138
147
 
139
148
  # Parses and executes the given string of source code in the receiving
140
149
  # +Scope+. Accepts strings of Scheme source and arrays of Ruby data to
@@ -145,6 +154,11 @@ module Heist
145
154
  end
146
155
  alias :exec :eval
147
156
 
157
+ # Executes an array of Scheme statements expressed as Ruby data.
158
+ def program(expressions)
159
+ expressions.map { |expr| exec(expr) }.last
160
+ end
161
+
148
162
  # Returns the longest shared prefix match for the given variable name
149
163
  # stub, used to support autocompletion in the REPL.
150
164
  def longest_prefix(name)
@@ -157,7 +171,6 @@ module Heist
157
171
  # sure we use 'obscure' names here.
158
172
  def run(_path)
159
173
  return instance_eval(File.read(_path)) if File.extname(_path) == '.rb'
160
- _path = _path + FILE_EXT unless File.file?(_path)
161
174
  _source = Heist.parse(File.read(_path))
162
175
  _scope = FileScope.new(self, _path)
163
176
  _source.eval(_scope)
@@ -168,13 +181,9 @@ module Heist
168
181
  # is found, the path is assumed to refer to a module from the Heist
169
182
  # standard library. The <tt>(load)</tt> primitive is a wrapper
170
183
  # around this method.
171
- def load(path)
172
- dir = load_path.find do |dir|
173
- File.file?("#{dir}/#{path}") or File.file?("#{dir}/#{path}#{FILE_EXT}")
174
- end
175
- return false unless dir
176
- runtime.run("#{dir}/#{path}")
177
- true
184
+ def load(file)
185
+ path = expand_path(file)
186
+ runtime.run(path) if path
178
187
  end
179
188
 
180
189
  # Returns the path of the current file. The receiving scope must have
@@ -183,6 +192,20 @@ module Heist
183
192
  @path || @parent.current_file rescue nil
184
193
  end
185
194
 
195
+ # Returns an absolute path to a requested library based on searching
196
+ # the current load path. The file extension may be omitted, suitable
197
+ # extensions being listed in Heist::FILE_EXTS.
198
+ def expand_path(path)
199
+ load_path.each do |dir|
200
+ test_path = File.expand_path(File.join(dir, path))
201
+ FILE_EXTS.each do |ext|
202
+ full_path = test_path + ext
203
+ return full_path if File.file?(full_path)
204
+ end
205
+ end
206
+ nil
207
+ end
208
+
186
209
  private
187
210
 
188
211
  # Calls the named primitive function with the given arguments, and
@@ -206,7 +229,7 @@ module Heist
206
229
  # the directory of the current file if the receiving +Scope+ has a
207
230
  # +FileScope+ as an ancestor.
208
231
  def load_path
209
- paths, file = [], current_file
232
+ paths, file = [""], current_file
210
233
  paths << File.dirname(file) if file
211
234
  paths + LOAD_PATH
212
235
  end
data/lib/trie.rb CHANGED
@@ -38,6 +38,20 @@ module Heist
38
38
  @children = {}
39
39
  end
40
40
 
41
+ # Iterates over each child key of the +Trie+, calling the given +block+
42
+ # with each key and corresponding value. Like iterating over a flat +Hash+.
43
+ def each_child
44
+ @children.each { |key, subtree| yield(key, subtree) }
45
+ end
46
+
47
+ # Iterates over the whole +Trie+, calling the given +block+ with each
48
+ # composite key and corresponding value. Each key will be an +Array+ matching
49
+ # the route to get from the root of the +Trie+ to the current node.
50
+ def each(prefix = [], &block)
51
+ each_child { |path, subtree| subtree.each(prefix + [path], &block) }
52
+ yield(prefix, @value) unless @value.nil?
53
+ end
54
+
41
55
  # Returns +true+ iff the given +key+ is present in the +Trie+. They key
42
56
  # must match a complete key sequence that maps to a value, not a partial
43
57
  # prefix with no value attached.
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,9 +1,9 @@
1
1
  (define (my-loader)
2
- (cond ((> 5 2) (load "lib"))))
2
+ (cond ((> 5 2) (load "../helpers/lib"))))
3
3
 
4
4
  (my-loader)
5
5
  (assert-equal 42 secret-rule-you-didnt-know)
6
6
 
7
- (load "vars")
7
+ (load "../helpers/vars")
8
8
  (assert-equal 1/137 alpha)
9
9
 
@@ -1,4 +1,4 @@
1
- (load "macro-helpers")
1
+ (load "../helpers/macro-helpers")
2
2
 
3
3
  (assert-equal 100 (let ([first 9])
4
4
  (square-sum 1 first)))
File without changes
File without changes
@@ -1,4 +1,4 @@
1
- (load "macro-helpers")
1
+ (load "../helpers/macro-helpers")
2
2
 
3
3
  ; Basic test: no subpatterns or ellipses
4
4
 
@@ -43,6 +43,13 @@
43
43
 
44
44
  (assert-equal 8 (dont-rename-else #f 6 8))
45
45
 
46
+ ; Check that empty lists are stored as matches and appear in output
47
+ (let-syntax ([quote-match (syntax-rules (quote)
48
+ [(_ 'arg ...) '(arg ...)])])
49
+ (assert-equal '(4 5 6) (quote-match '4 '5 '6))
50
+ (assert-equal '(4 5 ()) (quote-match '4 '5 '()))
51
+ (assert-equal '(4 () 6) (quote-match '4 '() '6)))
52
+
46
53
  ; Check that keywords are ignored if locally bound
47
54
  ; example from R6RS -- http://www.r6rs.org/final/html/r6rs/r6rs-Z-H-14.html#node_sec_11.19
48
55
  (assert-equal 'ok (let ((=> #f))
@@ -83,6 +90,15 @@
83
90
  (assert-equal 3 (iffy 7 #f 3))
84
91
 
85
92
 
93
+ ; Test that bound and non-bound symbols can be matched correctly
94
+
95
+ (letrec-syntax ((foo (syntax-rules (=)
96
+ ((foo = x) x)))
97
+ (bar (syntax-rules ()
98
+ ((bar x) (foo = x)))))
99
+ (assert-equal 5 (bar 5)))
100
+
101
+
86
102
  ; Test improper patterns
87
103
  (define-syntax rest (syntax-rules ()
88
104
  [(_ foo bar . rest)
@@ -314,6 +330,31 @@
314
330
  (assert-equal '() (double-up))
315
331
 
316
332
 
333
+ ; Test that pattern variables may appear at a greater
334
+ ; repetition depth in the template than they do in the pattern
335
+
336
+ (let-syntax ((repeater (syntax-rules ()
337
+ [(_ one (many ...))
338
+ '((one many) ...)]))
339
+
340
+ (root-v-2 (syntax-rules ()
341
+ [(_ one (many ...) ...)
342
+ '(((one many) ...) ...)]))
343
+
344
+ (1-v-2 (syntax-rules ()
345
+ [(_ (one ...) (many ...) ...)
346
+ '(((one many) ...) ...)])))
347
+
348
+ (assert-equal '((a 2) (a 3) (a 4))
349
+ (repeater a (2 3 4)))
350
+
351
+ (assert-equal '(((a 1) (a 2)) ((a 6)) () ((a 3) (a 6) (a 8)))
352
+ (root-v-2 a (1 2) (6) () (3 6 8)))
353
+
354
+ (assert-equal '(((a 1) (a 2)) ((b 6)) () ((d 3) (d 6) (d 8)))
355
+ (1-v-2 (a b c d) (1 2) (6) () (3 6 8))))
356
+
357
+
317
358
  ; R5RS version of (let), uses ellipsis after lists in patterns
318
359
 
319
360
  (define-syntax r5rs-let
File without changes
@@ -0,0 +1,13 @@
1
+ ; Check that user-level redefinitions of core functions does
2
+ ; not break other core functions implemented in Scheme
3
+
4
+ (define (check-map-integrity)
5
+ (assert-equal '(1 4 9 16)
6
+ (map (lambda (x) (* x x))
7
+ '(1 2 3 4))))
8
+ (define cons #f)
9
+ (check-map-integrity)
10
+
11
+ (set! null? #f)
12
+ (check-map-integrity)
13
+
File without changes
@@ -1,4 +1,4 @@
1
- (load "macro-helpers")
1
+ (load "../helpers/macro-helpers")
2
2
 
3
3
  (assert-equal 4 (let ([first 9])
4
4
  (square-sum 1 first)))
File without changes
data/test/test_heist.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  $VERBOSE = nil
2
- $dir = File.dirname(__FILE__)
2
+ $dir = File.expand_path(File.dirname(__FILE__))
3
3
 
4
4
  $args = ARGV.map { |opt| "-#{opt}" }
5
5
 
@@ -8,20 +8,22 @@ require $dir + "/../lib/bin_spec"
8
8
  require "test/unit"
9
9
 
10
10
  Class.new(Test::Unit::TestCase) do
11
- @@env = nil
11
+ @env = nil
12
12
 
13
13
  def setup
14
- return @@env if @@env
15
- @@env = Heist::Runtime.new(Heist::BIN_SPEC.parse($args))
16
- puts @@env.info
14
+ return @env if @env
15
+ @env = Heist::Runtime.new(Heist::BIN_SPEC.parse($args))
17
16
 
18
- @@env.define('assert') do |value|
17
+ puts @env.info unless defined?(@@info_printed)
18
+ @@info_printed = true
19
+
20
+ @env.define('assert') do |value|
19
21
  assert(value)
20
22
  end
21
- @@env.define('assert-equal') do |expected, actual|
23
+ @env.define('assert-equal') do |expected, actual|
22
24
  assert_equal(expected, actual)
23
25
  end
24
- @@env.syntax('assert-raise') do |scope, cells|
26
+ @env.syntax('assert-raise') do |scope, cells|
25
27
  exception = Heist.const_get(cells.car.to_s)
26
28
  assert_raise(exception) { scope.eval(cells.cdr.car) }
27
29
  end
@@ -43,10 +45,11 @@ Class.new(Test::Unit::TestCase) do
43
45
  file_loading
44
46
  macros
45
47
  delay
48
+ protection
46
49
 
47
50
  ].each do |test|
48
51
  define_method('test_' + test) do
49
- @@env.run($dir + '/' + test)
52
+ @env.run($dir + '/scheme_tests/' + test + '.scm')
50
53
  end
51
54
  end
52
55
 
@@ -84,58 +87,62 @@ Class.new(Test::Unit::TestCase) do
84
87
  end
85
88
 
86
89
  def test_macro_hygiene
87
- @@env.run($dir + '/' + (@@env.hygienic? ? 'hygienic' : 'unhygienic'))
90
+ @env.run($dir + '/scheme_tests/' + (@env.hygienic? ? 'hygienic' : 'unhygienic') + '.scm')
88
91
  end
89
92
 
90
93
  def test_continuations
91
- return if @@env.stackless?
92
- @@env.run($dir + '/continuations')
94
+ return if @env.stackless?
95
+ @env.run($dir + '/scheme_tests/continuations.scm')
93
96
  end
94
97
 
95
- def test_quotes
98
+ def test_quotation
96
99
  cons = Heist::Runtime::Cons
97
100
  c = cons.method(:new)
98
101
 
99
- assert_equal 7, @@env.eval("(+ 3 4)")
100
- assert_equal [:+, 3, 4], @@env.eval("'(+ 3 4)").to_ruby
101
- assert cons === @@env.eval("'(+ 3 4)")
102
- assert_equal 7, @@env.eval("(+ '3 4)")
103
- assert_equal c[1,8], @@env.eval("'(1 . 8)")
104
- assert_equal c[1,8], @@env.eval("`(1 . 8)")
105
- assert_equal [:+, [:-, 7, 9], 4], @@env.eval("'(+ (- 7 9) 4)").to_ruby
106
- assert_equal [7, 9, 6], @@env.eval("`(7 ,(+ 4 5) 6)").to_ruby
107
- assert cons === @@env.eval("`(7 ,(+ 4 5) 6)")
108
- assert_equal [3, 7, 6, 2, 6, 9], @@env.eval("`(3 7 6 ,@((lambda () '(2 6))) 9)").to_ruby
109
- assert_equal [1, 2, 10], @@env.eval("`(1 2 ,(+ 4 6))").to_ruby
110
- assert_equal [3, 2, 9, 8], @@env.eval("`(,(/ 9 3) 2 ,@(list 9 8))").to_ruby
111
- assert_equal [1, 2, 4, 9, 8, 5], @@env.eval("`(,@(list 1 2) 4 ,@(list 9 8) 5)").to_ruby
112
- assert_equal c[9,c[8,c[5,7]]], @@env.eval("`(,@(list 9 8) 5 . 7)")
113
- assert_equal c[9,c[8,24]], @@env.eval("`(,@(list 9 8) . ,(* 4 6))")
114
- assert_equal [:quote, []], @@env.eval("''()").to_ruby
102
+ assert_equal 7, @env.eval("(+ 3 4)")
103
+ assert_equal [:+, 3, 4], @env.eval("'(+ 3 4)").to_ruby
104
+ assert cons === @env.eval("'(+ 3 4)")
105
+ assert_equal 7, @env.eval("(+ '3 4)")
106
+ assert_equal c[1,8], @env.eval("'(1 . 8)")
107
+ assert_equal c[1,8], @env.eval("`(1 . 8)")
108
+ assert_equal [:+, [:-, 7, 9], 4], @env.eval("'(+ (- 7 9) 4)").to_ruby
109
+ assert_equal [7, 9, 6], @env.eval("`(7 ,(+ 4 5) 6)").to_ruby
110
+ assert cons === @env.eval("`(7 ,(+ 4 5) 6)")
111
+ assert_equal [3, 7, 6, 2, 6, 9], @env.eval("`(3 7 6 ,@((lambda () '(2 6))) 9)").to_ruby
112
+ assert_equal [1, 2, 10], @env.eval("`(1 2 ,(+ 4 6))").to_ruby
113
+ assert_equal [3, 2, 9, 8], @env.eval("`(,(/ 9 3) 2 ,@(list 9 8))").to_ruby
114
+ assert_equal [1, 2, 4, 9, 8, 5], @env.eval("`(,@(list 1 2) 4 ,@(list 9 8) 5)").to_ruby
115
+ assert_equal c[9,c[8,c[5,7]]], @env.eval("`(,@(list 9 8) 5 . 7)")
116
+ assert_equal c[9,c[8,24]], @env.eval("`(,@(list 9 8) . ,(* 4 6))")
117
+ assert_equal [:quote, []], @env.eval("''()").to_ruby
115
118
  end
116
119
 
117
- def test_birds
118
- return unless @@env.lazy?
119
- @@env.eval('(load "birdhouse")')
120
+ def test_birdhouse
121
+ return unless @env.lazy?
122
+ @env.eval('(load "birdhouse")')
120
123
 
121
- @@env.eval <<-CODE
124
+ @env.eval <<-CODE
122
125
  (define factorial (Y
123
126
  (lambda (rec)
124
127
  (lambda (x)
125
128
  (if (= x 0) 1 (* x (rec (- x 1))))))))
126
129
  CODE
127
130
  assert_equal (1..6).inject { |a,b| a*b },
128
- @@env.eval("(factorial 6)")
131
+ @env.eval("(factorial 6)")
129
132
 
130
- assert_equal 45, @@env.eval("((K 45) 6)")
133
+ assert_equal 45, @env.eval("((K 45) 6)")
131
134
  end
132
135
 
133
136
  def test_ruby_execution
134
137
  expr = [[:lambda, [:x], [:+, 1, :x]], 3]
135
- assert_equal 4, @@env.exec(expr)
138
+ assert_equal 4, @env.exec(expr)
136
139
  list = Heist.parse(expr)
137
140
  assert Heist::Runtime::Cons === list
138
141
  assert_equal expr, list.to_ruby
142
+
143
+ cons = Heist::Runtime::Cons.method(:new)
144
+ assert_equal cons[3,cons[4,5]], Heist.parse([3, 4, :'.', 5])
145
+ assert_equal [3, 4, :'.', 5], cons[3,cons[4,5]].to_ruby
139
146
  end
140
147
  end
141
148
 
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.3.1
4
+ version: 0.3.2
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-08-23 00:00:00 +01:00
12
+ date: 2010-01-16 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -32,6 +32,26 @@ dependencies:
32
32
  - !ruby/object:Gem::Version
33
33
  version: "0"
34
34
  version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: rubyforge
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.0.3
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: gemcutter
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.3.0
54
+ version:
35
55
  - !ruby/object:Gem::Dependency
36
56
  name: hoe
37
57
  type: :development
@@ -40,7 +60,7 @@ dependencies:
40
60
  requirements:
41
61
  - - ">="
42
62
  - !ruby/object:Gem::Version
43
- version: 2.0.0
63
+ version: 2.5.0
44
64
  version:
45
65
  description: ""
46
66
  email:
@@ -64,6 +84,7 @@ files:
64
84
  - lib/repl.rb
65
85
  - lib/trie.rb
66
86
  - lib/builtin/library.scm
87
+ - lib/builtin/library.rb
67
88
  - lib/builtin/primitives.rb
68
89
  - lib/builtin/syntax.scm
69
90
  - lib/parser/nodes.rb
@@ -90,33 +111,36 @@ files:
90
111
  - lib/runtime/stackless.rb
91
112
  - lib/stdlib/benchmark.scm
92
113
  - lib/stdlib/birdhouse.scm
93
- - test/arithmetic.scm
94
- - test/benchmarks.scm
95
- - test/booleans.scm
96
- - test/closures.scm
97
- - test/conditionals.scm
98
- - test/continuations.scm
99
- - test/define_functions.scm
100
- - test/define_values.scm
101
- - test/delay.scm
102
- - test/equivalence.scm
103
- - test/file_loading.scm
104
- - test/functional.scm
105
- - test/hygienic.scm
106
- - test/let.scm
107
- - test/lib.scm
108
- - test/lists.scm
109
- - test/macro-helpers.scm
110
- - test/macros.scm
111
- - test/numbers.scm
114
+ - test/helpers/lib.scm
115
+ - test/helpers/macro-helpers.scm
116
+ - test/helpers/vars.scm
117
+ - test/scheme_tests/arithmetic.scm
118
+ - test/scheme_tests/benchmarks.scm
119
+ - test/scheme_tests/booleans.scm
120
+ - test/scheme_tests/closures.scm
121
+ - test/scheme_tests/conditionals.scm
122
+ - test/scheme_tests/continuations.scm
123
+ - test/scheme_tests/define_functions.scm
124
+ - test/scheme_tests/define_values.scm
125
+ - test/scheme_tests/delay.scm
126
+ - test/scheme_tests/equivalence.scm
127
+ - test/scheme_tests/file_loading.scm
128
+ - test/scheme_tests/functional.scm
129
+ - test/scheme_tests/hygienic.scm
130
+ - test/scheme_tests/let.scm
131
+ - test/scheme_tests/lists.scm
132
+ - test/scheme_tests/macros.scm
133
+ - test/scheme_tests/numbers.scm
134
+ - test/scheme_tests/protection.scm
135
+ - test/scheme_tests/strings.scm
136
+ - test/scheme_tests/unhygienic.scm
137
+ - test/scheme_tests/vectors.scm
112
138
  - test/plt-macros.txt
113
- - test/strings.scm
114
139
  - test/test_heist.rb
115
- - test/unhygienic.scm
116
- - test/vars.scm
117
- - test/vectors.scm
118
140
  has_rdoc: true
119
141
  homepage: http://github.com/jcoglan/heist
142
+ licenses: []
143
+
120
144
  post_install_message:
121
145
  rdoc_options:
122
146
  - --main
@@ -138,9 +162,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
162
  requirements: []
139
163
 
140
164
  rubyforge_project: heist
141
- rubygems_version: 1.3.1
165
+ rubygems_version: 1.3.5
142
166
  signing_key:
143
- specification_version: 2
167
+ specification_version: 3
144
168
  summary: ""
145
169
  test_files:
146
170
  - test/test_heist.rb