heist 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
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