public
Description: a Scheme written in Ruby, but implemented on the bus!
Homepage: http://bus-scheme.rubyforge.org
Clone URL: git://github.com/technomancy/bus-scheme.git
recursion fixes
technomancy (author)
Tue Feb 12 19:58:34 -0800 2008
commit  a3eb301a001dc8c18c8eb51fd4f18e71be9a9a89
tree    5718ca21e63ccfc35bcbe3ddccc9bcd1c25abc62
parent  a325e01c5c88841c3f8299c39dfb8ca6bddc5d01
...
85
86
87
88
89
90
91
92
93
94
 
 
 
 
 
 
95
96
97
...
85
86
87
 
 
 
88
 
 
 
89
90
91
92
93
94
95
96
97
0
@@ -85,13 +85,13 @@ For the source:
0
 
0
 Bus Scheme is currently missing pieces of functionality:
0
 
0
-=== Bugs
0
-* REPL should accept multiline strings
0
-
0
 === Parser
0
-* parse character literals
0
-* parse dotted cons cells
0
-* Ruby blocks
0
+* character literals
0
+* multiline strings
0
+* escape sequences in strings
0
+* string interpolation
0
+* dotted cons cells
0
+* Ruby blocks?
0
 * XML literals?
0
 
0
 === General
...
8
9
10
11
 
12
13
14
...
8
9
10
 
11
12
13
14
0
@@ -8,7 +8,7 @@ if ARGV.empty?
0
 elsif ARGV.first == '-e' and ARGV.length == 2
0
   puts BusScheme.eval_string(ARGV[1])
0
 elsif ARGV.length == 1 and File.exist?(ARGV.first)
0
- puts BusScheme.eval_string "(load \"#{ARGV.first}\")"
0
+ puts BusScheme.eval_string("(load \"#{ARGV.first}\")")
0
 else
0
   puts "Bus Scheme: a scheme interpreter written on the bus.
0
 Usage: bus [file | -e \"form\"]
...
20
21
22
 
23
24
 
 
 
 
 
 
 
 
25
26
27
28
29
30
31
 
 
 
 
 
 
32
33
34
...
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
 
40
41
42
43
44
45
46
47
48
0
@@ -20,15 +20,29 @@ module BusScheme
0
   VERSION = "0.7.5"
0
 
0
   PROMPT = '> '
0
+ INCOMPLETE_PROMPT = ' ... '
0
   LOAD_PATH = ["#{File.dirname(__FILE__)}/scheme/"]
0
   
0
+ class BusSchemeError < StandardError; end
0
+ class ParseError < BusSchemeError; end
0
+ class EvalError < BusSchemeError; end
0
+ class LoadError < BusSchemeError; end
0
+ class IncompleteError < BusSchemeError; end
0
+ class ArgumentError < BusSchemeError; end
0
+ class AssertionFailed < BusSchemeError; end
0
+
0
   # Read-Eval-Print-Loop
0
   def self.repl
0
     loop do
0
       puts begin
0
              input = Readline.readline(PROMPT)
0
              exit if input.nil? # only Ctrl-D produces nil here it seems
0
- BusScheme.eval_string input
0
+ begin # allow for multiline input
0
+ BusScheme.eval_string input
0
+ rescue IncompleteError
0
+ input += "\n" + Readline.readline(INCOMPLETE_PROMPT)
0
+ retry
0
+ end
0
            rescue Interrupt
0
              'Type "(quit)" or press Ctrl-D to leave Bus Scheme.'
0
            rescue BusSchemeError => e
...
21
22
23
24
25
 
 
 
 
 
 
 
 
26
27
28
...
47
48
49
50
 
51
52
53
...
21
22
23
 
 
24
25
26
27
28
29
30
31
32
33
34
...
53
54
55
 
56
57
58
59
0
@@ -21,8 +21,14 @@ module BusScheme
0
                end
0
 
0
       @scope = RecursiveHash.new(locals, @enclosing_scope)
0
- @@stack << self
0
- BusScheme.eval(@body.unshift(:begin.sym)).affect { @@stack.pop }
0
+ # we dupe the lambda so that @scope is unique for each call of the function
0
+ @@stack << self.dup
0
+
0
+ begin
0
+ return BusScheme.eval(@body.unshift(:begin.sym))
0
+ ensure
0
+ @@stack.pop
0
+ end
0
     end
0
 
0
     # What's the current scope?
0
@@ -47,7 +53,7 @@ module BusScheme
0
     end
0
 
0
     # where were we called from?
0
- def self.trace
0
+ def self.stacktrace
0
       @@stack.reverse.map { |fn| [fn.symbol, fn.file, fn.line] }
0
     end
0
   end
...
11
12
13
14
 
15
16
17
18
19
20
 
21
22
23
...
32
33
34
35
 
36
37
38
...
11
12
13
 
14
15
16
17
18
19
 
20
21
22
23
...
32
33
34
 
35
36
37
38
0
@@ -11,13 +11,13 @@ module BusScheme
0
       parse_tokens(tokenize(input).flatten).sexp
0
     end
0
 
0
- # Turn a list of tokens into a properly-nested S-expression
0
+ # Turn a list of tokens into a properly-nested array
0
     def parse_tokens(tokens)
0
       token = tokens.shift
0
       if token == :'('
0
         parse_list(tokens)
0
       else
0
- raise BusScheme::ParseError unless tokens.empty?
0
+ raise ParseError unless tokens.empty?
0
         token # atom
0
       end
0
     end
0
@@ -32,7 +32,7 @@ module BusScheme
0
             list << element
0
           end
0
         end
0
- raise ParseError unless element == :')'
0
+ raise IncompleteError unless element == :')'
0
       end
0
     end
0
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
...
32
33
34
35
 
 
36
37
38
...
1
2
3
 
 
 
 
 
 
 
4
5
6
...
25
26
27
 
28
29
30
31
32
0
@@ -1,13 +1,6 @@
0
 module BusScheme
0
   SYMBOL_TABLE = {}
0
 
0
- class BusSchemeError < StandardError; end
0
- class ParseError < BusSchemeError; end
0
- class EvalError < BusSchemeError; end
0
- class LoadError < BusSchemeError; end
0
- class ArgumentError < BusSchemeError; end
0
- class AssertionFailed < BusSchemeError; end
0
-
0
   def self.define(identifier, value)
0
     SYMBOL_TABLE[identifier.sym] = value
0
   end
0
@@ -32,7 +25,8 @@ module BusScheme
0
   define 'map', lambda { |fn, list| list.map(lambda { |n| fn.call(n) }).sexp }
0
   
0
   define 'eval', lambda { |code| eval(code) }
0
-
0
+ define 'stacktrace', lambda { Lambda.stacktrace }
0
+
0
   define 'ruby', lambda { |*code| Kernel.eval code.join('') }
0
   define 'send', lambda { |obj, *message| obj.send(*message) }
0
 
...
2
3
4
5
 
 
 
 
 
6
7
8
...
67
68
69
70
71
72
73
74
75
76
77
78
 
 
 
 
 
 
 
 
79
80
81
...
104
105
106
 
 
 
 
 
 
 
 
 
 
 
 
107
...
2
3
4
 
5
6
7
8
9
10
11
12
...
71
72
73
 
 
 
 
 
 
 
 
 
74
75
76
77
78
79
80
81
82
83
84
...
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
0
@@ -2,7 +2,11 @@ $LOAD_PATH << File.dirname(__FILE__)
0
 require 'test_helper'
0
 
0
 class BusScheme::Lambda
0
- attr_accessor :body, :formals, :environment
0
+ attr_accessor :body, :formals, :enclosing_scope
0
+
0
+ def self.stack
0
+ @@stack
0
+ end
0
 end
0
 
0
 class BusSchemeLambdaTest < Test::Unit::TestCase
0
@@ -67,15 +71,14 @@ class BusSchemeLambdaTest < Test::Unit::TestCase
0
   (f (quote b))))"
0
   end
0
 
0
-# def test_args_work_right
0
-# eval "(define fib (lambda (x)
0
-# (ruby \"p Lambda.scope\")
0
-# (assert (> x 0))
0
-# (if (< x 3)
0
-# 1
0
-# (+ (fib (- x 1)) (fib (- x 2))))))"
0
-# assert_evals_to 3, "(fib 3)"
0
-# end
0
+ def test_nested_function_calls_dont_affect_caller
0
+ eval "(define fib (lambda (x)
0
+ (if (< x 3)
0
+ 1
0
+ (+ (fib (- x 1)) (fib (- x 2))))))"
0
+
0
+ assert_evals_to 5, "(fib 5)"
0
+ end
0
 
0
   def test_lambda_rest_args
0
     eval "(define rest (lambda args args))"
0
@@ -104,4 +107,16 @@ class BusSchemeLambdaTest < Test::Unit::TestCase
0
     eval "#{"\n" * 7} (define fab 'warble)"
0
     assert_equal 7, Lambda[:fab.sym].line
0
   end
0
+
0
+ def test_stack_gets_popped
0
+ # TODO:
0
+ end
0
+
0
+ def test_stacktrace
0
+ eval "(define gimme-trace (lambda () (stacktrace)))"
0
+ eval "(define nest-trace (lambda () (gimme-trace)))"
0
+ assert_equal([[:'gimme-trace'.sym, "(eval)", 0],
0
+ [:'nest-trace'.sym, "(eval)", 0]],
0
+ eval("(nest-trace)"))
0
+ end
0
 end
...
120
121
122
123
124
 
 
125
126
127
...
120
121
122
 
 
123
124
125
126
127
0
@@ -120,8 +120,8 @@ class BusSchemeParserTest < Test::Unit::TestCase
0
   end
0
 
0
   def test_requires_closed_lists
0
- assert_raises(ParseError) { BusScheme.parse "(+ 2 2" }
0
- assert_raises(ParseError) { BusScheme.parse "(+ (* 3 4) 2 2" }
0
+ assert_raises(IncompleteError) { BusScheme.parse "(+ 2 2" }
0
+ assert_raises(IncompleteError) { BusScheme.parse "(+ (* 3 4) 2 2" }
0
   end
0
   
0
 # def test_reject_bad_identifiers

Comments

    No one has commented yet.