bus-scheme 0.7.5 → 0.7.6

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