bus-scheme 0.7.5 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,35 +1,38 @@
1
- (define intern
2
- (lambda (sym) (send sym (quote intern))))
1
+ (define string->symbol
2
+ (lambda (sym) (send sym 'sym)))
3
+
4
+ (define number->string
5
+ (lambda (number) (send number 'to_s)))
3
6
 
4
7
  (define substring
5
8
  (lambda (string to from) (send string (quote []) to from)))
6
9
 
7
10
  (define null?
8
- (lambda (expr) (= expr ())))
11
+ (lambda (expr) (or
12
+ (= expr ()) ;; hacky?
13
+ (= expr (ruby "nil")))))
9
14
 
10
15
  (define >
11
- (lambda (x y) (send x (intern ">") y)))
16
+ (lambda (x y) (send x '> y)))
12
17
 
13
18
  (define <
14
- (lambda (x y) (send x (intern "<") y)))
19
+ (lambda (x y) (send x '< y)))
15
20
 
16
21
  (define =
17
- (lambda (x y) (send x (intern "==") y)))
18
-
19
- (define and
20
- (lambda (x y) (if x (if y y #f) #f)))
21
-
22
- (define or
23
- (lambda (x y) (if x x (if y y #f))))
22
+ (lambda (x y) (send x '== y)))
24
23
 
25
24
  (define not
26
25
  (lambda (expr) (if expr #f #t)))
27
26
 
28
27
  (define car
29
- (lambda (lst) (send lst (quote first))))
28
+ (lambda (lst) (send lst 'first)))
30
29
 
31
30
  (define cdr
32
- (lambda (lst) (send lst (quote rest))))
31
+ (lambda (lst) (send lst 'rest)))
33
32
 
33
+ ;; and friends
34
34
  (define cadr
35
- (lambda (lst) (car (cdr lst))))
35
+ (lambda (lst) (car (cdr lst))))
36
+
37
+ ;; cond
38
+ (define else #t)
@@ -0,0 +1,12 @@
1
+ (define length (lambda (l)
2
+ (if (null? l) 0 (+ 1 (length (cdr l))))))
3
+
4
+ (define append
5
+ (lambda (l1 l2)
6
+ (if (null? l1)
7
+ l2
8
+ (cons (car l1) (append (cdr l1) l2)))))
9
+
10
+ (define reverse
11
+ (lambda (l)
12
+ (if (null? l) '() (append (reverse (cdr l)) (list (car l))))))
@@ -0,0 +1,19 @@
1
+ (define isa? (lambda (x type)
2
+ (send x 'is_a? (ruby type))))
3
+
4
+ (define boolean? (lambda (x) (or (= x #t)
5
+ (= x #f))))
6
+ (define symbol? (lambda (x) (isa? x "Sym")))
7
+ (define cons? (lambda (x) (isa? x "Cons")))
8
+ (define pair? cons?)
9
+
10
+ (define string? (lambda (x) (and (isa? x "String")
11
+ (not (isa? x "Sym")))))
12
+ (define number? (lambda (x) (isa? x "Fixnum")))
13
+ (define vector? (lambda (x) (isa? x "Array")))
14
+ (define procedure? (lambda (x) (isa? x "Lambda")))
15
+ (define char? (lambda (x) (and (isa? x "String")
16
+ (= 1 (length x)))))
17
+
18
+ ;;; TODO:
19
+ ;;; port?
@@ -0,0 +1,12 @@
1
+ (define assert
2
+ (lambda (bool)
3
+ (if bool
4
+ #t
5
+ (fail "Assertion failed."))))
6
+
7
+ (define assert-equal
8
+ (lambda (expected actual)
9
+ (if (= expected actual)
10
+ #t
11
+ (fail (concat "Expected " (send expected 'inspect) ", got "
12
+ (send actual 'inspect))))))
@@ -0,0 +1,57 @@
1
+ module BusScheme
2
+ class StackFrame < Hash
3
+ attr_reader :called_as, :file, :line, :called_from
4
+
5
+ # takes a hash and a parent
6
+ def initialize(locals, parent, called_as)
7
+ @parent, @called_as = [parent, called_as]
8
+ @file = @called_as.respond_to?(:file) ? @called_as.file : '(eval)'
9
+ @line = @called_as.respond_to?(:line) ? @called_as.line : 0
10
+ @called_as = '(anonymous)' if called_as.is_a?(Cons) or called_as.is_a?(Array)
11
+
12
+ @called_from = if BusScheme.stack.empty? or !BusScheme.stack.last.respond_to? :called_as
13
+ '(top-level)'
14
+ else
15
+ BusScheme.stack.last.called_as
16
+ end
17
+
18
+ locals.each { |k, v| immediate_set k, v }
19
+ end
20
+
21
+ alias_method :immediate_has_key?, :has_key?
22
+ alias_method :immediate_set, :[]=
23
+ alias_method :immediate_lookup, :[]
24
+
25
+ # Just your regular hash stuff, only it takes the parent into account
26
+ def has_key?(symbol)
27
+ immediate_has_key?(symbol) or @parent && @parent.has_key?(symbol)
28
+ end
29
+
30
+ def [](symbol)
31
+ if immediate_has_key?(symbol)
32
+ immediate_lookup(symbol)
33
+ else
34
+ @parent && @parent[symbol]
35
+ end
36
+ end
37
+
38
+ def []=(symbol, value)
39
+ if !immediate_has_key?(symbol) and @parent && @parent.has_key?(symbol)
40
+ @parent[symbol] = value
41
+ else
42
+ immediate_set symbol, value
43
+ end
44
+ end
45
+
46
+ def trace
47
+ "#{@file}:#{@line} in #{@called_from}" unless filtered?
48
+ end
49
+
50
+ # special forms should not be shown in stack traces
51
+ # neither should 'begin'
52
+ def filtered?
53
+ return false unless @called_as
54
+ @called_as.special_form or @called_as == :begin.sym
55
+ end
56
+ end
57
+ end
@@ -14,30 +14,18 @@ class CoreTest < Test::Unit::TestCase
14
14
  end
15
15
 
16
16
  def test_string_functions
17
- assert_evals_to :hi, [:intern, 'hi']
18
- assert_evals_to 'lo', [:substring, 'hello', 3, 5]
17
+ assert_evals_to :hi.sym, ['string->symbol'.sym, 'hi']
18
+ assert_evals_to 'lo', [:substring.sym, 'hello', 3, 5]
19
19
  end
20
20
 
21
21
  def test_list_functions
22
- assert_evals_to :foo, "(car (cons (quote foo) (quote bar)))"
23
- assert_evals_to :bar, "(cdr (cons (quote foo) (quote bar)))"
24
- assert_equal(BusScheme::Cons.new(:foo, BusScheme::Cons.new(:bar, nil)),
25
- [:foo, :bar].to_list)
26
- assert_evals_to(BusScheme::Cons.new(2, BusScheme::Cons.new(3, nil)),
22
+ assert_evals_to :foo.sym, "(car (cons (quote foo) (quote bar)))"
23
+ assert_evals_to :bar.sym, "(cdr (cons (quote foo) (quote bar)))"
24
+ assert_equal(Cons.new(:foo.sym, Cons.new(:bar.sym, nil)),
25
+ [:foo.sym, :bar.sym].to_list)
26
+ assert_evals_to(Cons.new(2, Cons.new(3, nil)),
27
27
  "(list 2 3)")
28
- assert_evals_to "bar", "(cadr (list \"foo\" \"bar\")"
29
- assert_evals_to [1, :foo].to_list, "(list 1 'foo)"
30
- end
31
-
32
- def test_boolean_logic
33
- assert_evals_to true, "(and #t #t)"
34
- assert_evals_to false, "(and #t #f)"
35
- assert_evals_to false, "(and #f #t)"
36
- assert_evals_to false, "(and #f #f)"
37
-
38
- assert_evals_to true, "(or #t #t)"
39
- assert_evals_to true, "(or #t #f)"
40
- assert_evals_to true, "(or #f #t)"
41
- assert_evals_to false, "(or #f #f)"
28
+ assert_evals_to "bar", "(cadr (list \"foo\" \"bar\"))"
29
+ assert_evals_to [1, :foo.sym].to_list, "(list 1 'foo)"
42
30
  end
43
31
  end
@@ -1,5 +1,6 @@
1
1
  $LOAD_PATH << File.dirname(__FILE__)
2
2
  require 'test_helper'
3
+ require 'timeout'
3
4
 
4
5
  class BusSchemeEvalTest < Test::Unit::TestCase
5
6
  def test_eval_empty_list
@@ -12,13 +13,13 @@ class BusSchemeEvalTest < Test::Unit::TestCase
12
13
  end
13
14
 
14
15
  def test_set_symbol
15
- BusScheme::Lambda.scope[:hi] = 'hi'
16
- assert BusScheme::Lambda.scope[:hi]
16
+ BusScheme.current_scope[:hi] = 'hi'
17
+ assert BusScheme.current_scope[:hi]
17
18
  end
18
19
 
19
20
  def test_eval_symbol
20
- BusScheme::Lambda.scope[:hi] = 13
21
- assert_evals_to 13, :hi
21
+ BusScheme.current_scope[:hi.sym] = 13
22
+ assert_evals_to 13, :hi.sym
22
23
  end
23
24
 
24
25
  def test_eval_string
@@ -26,25 +27,69 @@ class BusSchemeEvalTest < Test::Unit::TestCase
26
27
  end
27
28
 
28
29
  def test_eval_function_call
29
- assert_evals_to 2, [:+, 1, 1]
30
+ assert_evals_to 2, "(+ 1 1)"
30
31
  end
31
32
 
32
33
  def test_nested_arithmetic
33
- assert_evals_to 6, [:+, 1, [:+, 1, [:*, 2, 2]]]
34
+ assert_evals_to 6, "(+ 1 (+ 1 (* 2 2)))"
34
35
  end
35
36
 
36
37
  def test_blows_up_with_undefined_symbol
37
- assert_raises(BusScheme::EvalError) { eval("undefined-symbol") }
38
+ assert_raises(EvalError) { eval("undefined-symbol") }
38
39
  end
39
40
 
40
41
  def test_variable_substitution
41
42
  eval "(define foo 7)"
42
- assert_evals_to 7, :foo
43
- assert_evals_to 21, [:*, 3, :foo]
43
+ assert_evals_to 7, :foo.sym
44
+ assert_evals_to 21, [:*.sym, 3, :foo.sym]
44
45
  end
45
46
 
46
47
  def test_single_quote
47
- assert_evals_to :foo, "'foo"
48
- assert_evals_to [:foo, :biz, :bbb].to_list, "'(foo biz bbb)"
48
+ assert_evals_to :foo.sym, "'foo"
49
+ assert_evals_to [:foo.sym, :biz.sym, :bbb.sym].to_list, "'(foo biz bbb)"
49
50
  end
51
+
52
+ def test_quote
53
+ assert_evals_to :hi.sym, "(quote hi)"
54
+ assert_evals_to [:a.sym, :b.sym, :c.sym].to_list, "'(a b c)"
55
+ assert_evals_to [:a.sym].to_list, "(list 'a)"
56
+ assert_evals_to [:a.sym, :b.sym].to_list, "(list 'a 'b)"
57
+ assert_evals_to [:a.sym, :b.sym, :c.sym].to_list, "(list 'a 'b 'c)"
58
+ assert_evals_to [:+.sym, 2, 3].to_list, "'(+ 2 3)"
59
+ end
60
+
61
+ def test_array_of_args_or_list_of_args
62
+ assert_evals_to 5, cons(:+.sym, cons(2, cons(3)))
63
+ assert_evals_to 5, cons(:+.sym, cons(2, cons(3)).to_a)
64
+ end
65
+
66
+ def test_eval_multiple_forms
67
+ assert_raises(EvalError) do
68
+ BusScheme.eval_string "(+ 2 2) (undefined-symbol)"
69
+ end
70
+ end
71
+
72
+ def test_define_after_load
73
+ BusScheme.eval_string "(load \"#{File.dirname(__FILE__)}/../examples/fib.scm\")
74
+ (define greeting \"hi\")"
75
+ assert BusScheme.in_scope?(:greeting.sym)
76
+ end
77
+
78
+ def test_funcall_list_means_nth
79
+ assert_evals_to 3, "((list 1 2 3) 2)"
80
+ end
81
+
82
+ def test_funcall_vector_means_nth
83
+ assert_evals_to 3, "((vector 1 2 3) 2)"
84
+ end
85
+
86
+ def test_funcall_hash_means_lookup
87
+ assert_evals_to 3, "((hash (1 1) (2 2) (3 3)) 3)"
88
+ end
89
+
90
+ # def test_tail_call_optimization
91
+ # Timeout.timeout(1) do
92
+ # assert_nothing_raised { eval "((lambda (x) (x x)) (lambda (x) (x x)))" }
93
+ # end
94
+ # end
50
95
  end
@@ -2,13 +2,34 @@ $LOAD_PATH << File.dirname(__FILE__) + '/../lib/'
2
2
  require 'test/unit'
3
3
  require 'bus_scheme'
4
4
 
5
+ module BusScheme
6
+ def self.reset_stack
7
+ @@stack = []
8
+ end
9
+ end
10
+
11
+ class BusScheme::Lambda
12
+ attr_accessor :body, :formals, :enclosing_scope
13
+ end
14
+
15
+ class RecursiveHash
16
+ attr_reader :parent
17
+ end
18
+
5
19
  class Test::Unit::TestCase
20
+ include BusScheme
21
+
6
22
  # convenience method that accepts string or form
7
23
  def eval(form)
8
- if form.is_a?(String)
9
- BusScheme.eval_string(form)
10
- else
11
- BusScheme.eval_form(form)
24
+ begin
25
+ if form.is_a?(String)
26
+ BusScheme.eval_string(form)
27
+ else
28
+ BusScheme.eval(form)
29
+ end
30
+ rescue => e
31
+ BusScheme.reset_stack
32
+ raise e
12
33
  end
13
34
  end
14
35
 
@@ -18,6 +39,6 @@ class Test::Unit::TestCase
18
39
 
19
40
  # remove symbols from all scopes
20
41
  def clear_symbols(*symbols)
21
- [BusScheme::Lambda.scope, BusScheme::SYMBOL_TABLE].compact.map{ |scope| symbols.map{ |sym| scope.delete sym } }
42
+ [BusScheme.current_scope, BusScheme::SYMBOL_TABLE].compact.map{ |scope| symbols.map{ |sym| scope.delete sym } }
22
43
  end
23
44
  end
@@ -1,25 +1,21 @@
1
1
  $LOAD_PATH << File.dirname(__FILE__)
2
2
  require 'test_helper'
3
3
 
4
- class BusScheme::Lambda
5
- attr_accessor :body, :arg_names, :environment
6
- end
7
-
8
4
  class BusSchemeLambdaTest < Test::Unit::TestCase
9
5
  def test_simple_lambda
10
6
  l = eval("(lambda () (+ 1 1))")
11
- assert l.is_a?(BusScheme::Lambda)
12
- assert_equal [[:+, 1, 1]], l.body
13
- assert_equal [], l.arg_names
7
+ assert l.is_a?(Lambda)
8
+ assert_equal [[:+.sym, 1, 1].to_list], l.body
9
+ assert_equal 0, l.formals.length
14
10
 
15
11
  eval("(define foo (lambda () (+ 1 1)))")
16
- assert BusScheme::Lambda.scope[:foo].is_a?(BusScheme::Lambda)
17
- assert_evals_to 2, [:foo]
12
+ assert BusScheme[:foo.sym].is_a?(Lambda)
13
+ assert_evals_to 2, [:foo.sym]
18
14
  end
19
15
 
20
16
  def test_lambda_with_arg
21
17
  eval("(define foo (lambda (x) (+ x 1)))")
22
- assert_evals_to 2, [:foo, 1]
18
+ assert_evals_to 2, "(foo 1)"
23
19
  end
24
20
 
25
21
  def test_eval_literal_lambda
@@ -28,35 +24,101 @@ class BusSchemeLambdaTest < Test::Unit::TestCase
28
24
 
29
25
  def test_lambda_with_incorrect_arity
30
26
  eval("(define foo (lambda (x) (+ x 1)))")
31
- assert_raises(BusScheme::ArgumentError) { assert_evals_to 2, [:foo, 1, 3] }
27
+ assert_raises(ArgumentError) { assert_evals_to 2, "(foo 1 3)" }
32
28
  end
33
29
 
34
30
  def test_lambda_args_dont_stay_in_scope
35
31
  clear_symbols(:x, :foo)
36
32
  eval("(define foo (lambda (x) (+ x 1)))")
37
- assert_nil BusScheme::Lambda.scope[:x]
38
- assert_evals_to 2, [:foo, 1]
39
- assert_nil BusScheme::Lambda.scope[:x]
33
+ assert ! BusScheme.in_scope?(:x)
34
+ assert_evals_to 2, "(foo 1)"
35
+ assert ! BusScheme.in_scope?(:x)
40
36
  end
41
37
 
42
38
  def test_lambda_calls_lambda
43
39
  eval "(define f (lambda (x) (+ 3 x)))"
44
40
  eval "(define g (lambda (y) (* 3 y)))"
45
41
  assert_evals_to 12, "(f (g 3))"
42
+ assert_evals_to 1, "((lambda () ((lambda () 1))))"
46
43
  end
47
44
 
45
+ def test_enforces_arg_count
46
+ assert_equal 3, eval("(lambda (x y z) z)").formals.size
47
+ assert_raises(ArgumentError) do
48
+ eval "((lambda (x) x))"
49
+ end
50
+ end
51
+
48
52
  def test_lambda_closures
49
- eval "(define foo (lambda (x) ((lambda (y) (+ x y)) (* x 2))))"
50
- assert_evals_to 3, [:foo, 1]
53
+ assert_evals_to 3, "((lambda (x) ((lambda (y) 3) 1)) 1)"
54
+ eval "(define foo (lambda (xx) ((lambda (y) (+ xx y)) (* xx 2))))"
55
+ assert foo = BusScheme[:foo.sym]
56
+
57
+ assert_evals_to 3, foo.call(1)
51
58
  eval "(define holder ((lambda (x) (lambda () x)) 2))"
52
59
  assert_evals_to 2, "(holder)"
53
60
  end
54
61
 
55
- def test_changes_to_enclosed_variables_are_in_effect_after_lambda_execution
56
- assert_evals_to 2, "((lambda (x) (begin ((lambda () (set! x 2))) x)) 1)"
62
+ def test_changes_to_enclosed_variables_alter_original_bindings
63
+ BusScheme.reset_stack # TODO: shouldn't reqire this
64
+ assert_evals_to 2, "((lambda (x) ((lambda () (set! x 2))) x) 1)"
65
+ assert BusScheme.stack.empty?
66
+ end
67
+
68
+ def test_implicit_begin
69
+ assert_evals_to 3, "((lambda () (string->symbol \"hi\") (+ 2 2) (* 1 3)))"
70
+ end
71
+
72
+ def test_let
73
+ assert_evals_to 2, "(let ((n 2)) n)"
74
+ assert_evals_to 5, "(let ((n 2) (m 3)) (+ n m))"
75
+ end
76
+
77
+ def test_shadowed_vars_dont_stay_in_scope
78
+ assert_evals_to Cons.new(:a.sym, :b.sym), "(let ((f (let ((x (quote a)))
79
+ (lambda (y) (cons x y)))))
80
+ (let ((x (quote not-a)))
81
+ (f (quote b))))"
82
+ end
83
+
84
+ def test_nested_function_calls_dont_affect_caller
85
+ eval "(define fib (lambda (x)
86
+ (if (< x 3)
87
+ 1
88
+ (+ (fib (- x 1)) (fib (- x 2))))))"
89
+
90
+ assert BusScheme.in_scope?(:fib.sym)
91
+ assert_evals_to 5, "(fib 5)"
92
+ end
93
+
94
+ def test_lambda_rest_args
95
+ eval "(define rest (lambda args args))"
96
+ assert_evals_to [:a.sym, :b.sym, :c.sym].to_list, "(rest 'a 'b 'c)"
97
+ end
98
+
99
+ def test_stacktrace
100
+ eval '(load "test/tracer.scm")'
101
+ assert_equal ["(eval):1 in top-level"], eval("(stacktrace)")
102
+
103
+ assert_equal(["test/tracer.scm:1 in f",
104
+ "test/tracer.scm:4 in g",
105
+ "(eval):1 in (anonymous)",
106
+ "(eval):0 in top-level"
107
+ ],
108
+ eval("((lambda () (g)))"))
57
109
  end
58
110
 
59
- def test_implicit_begin
60
- assert_evals_to 3, "((lambda () (intern \"hi\") (+ 2 2) (* 1 3)))"
61
- end
111
+ # TODO: check the stack traces that the scheme tests give
112
+
113
+ def test_stack_grows
114
+ eval "(define stack-growth
115
+ (lambda () (ruby \"raise 'wtf' if BusScheme.stack.size < 1\")))"
116
+ eval "(stack-growth)"
117
+ end
118
+
119
+ def test_primitives_live_on_stack
120
+ BusScheme.define 'stack-growth', lambda { assert BusScheme.stack.size > 1 }
121
+ assert SYMBOL_TABLE.has_key?('stack-growth'.sym)
122
+ eval "(stack-growth)"
123
+ end
62
124
  end