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
bus-scheme / lib / bus_scheme.rb
100644 90 lines (78 sloc) 2.514 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/usr/bin/env ruby
 
$LOAD_PATH << File.dirname(__FILE__)
require 'readline'
require 'object_extensions'
require 'array_extensions'
 
require 'parser'
require 'eval'
require 'cons'
require 'lambda'
require 'stack_frame'
require 'primitives'
 
begin
  require 'web'
  require 'xml'
rescue LoadError
  puts "Could not load web functionality."
end
 
module BusScheme
  VERSION = "0.7.7"
 
  PROMPT = '> '
  INCOMPLETE_PROMPT = ' ... '
  BusScheme['load-path'.sym] = Cons.new("#{File.dirname(__FILE__)}/scheme/",
                                        Cons.new(File.expand_path('.'), nil))
  
  class BusSchemeError < StandardError; end
  class ParseError < BusSchemeError; end
  class EvalError < BusSchemeError; end
  class LoadError < BusSchemeError; end
  class IncompleteError < BusSchemeError; end
  class ArgumentError < BusSchemeError; end
  class AssertionFailed < BusSchemeError; end
 
  # Read-Eval-Print-Loop
  def self.repl
    loop do
      puts begin
             input = Readline.readline(PROMPT)
             exit if input.nil? # only Ctrl-D produces nil here it seems
             begin # allow for multiline input
               result = BusScheme.eval_string(input).inspect
             rescue IncompleteError
               input += "\n" + Readline.readline(INCOMPLETE_PROMPT)
               retry
             end
             Readline::HISTORY.push(input)
             result
           rescue Interrupt
             'Type "(quit)" or press Ctrl-D to leave Bus Scheme.'
           rescue BusSchemeError => e
             "Error: #{e}"
           rescue StandardError => e
             "You found a bug in Bus Scheme!\n" +
               "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
           end
    end
  end
 
  # Load a file if on the load path or absolute
  def self.load(filename)
    begin
      loaded_files.push filename
      eval_string File.read(add_load_path(filename))
      loaded_files.pop
    rescue
      loaded_files.pop
      raise
    end
  end
 
  def self.add_load_path(filename, load_path = BusScheme['load-path'.sym])
    return filename if filename.match(/^\//) or File.exist? filename
    raise LoadError, "File not found: #{filename}" if load_path.empty?
    return load_path.car + '/' + filename if File.exist? load_path.car + '/' + filename
    return add_load_path(filename, load_path.cdr)
  end
 
  # For stack traces
  def self.loaded_files
    (@loaded_files ||= ["(eval)"])
  end
 
  ['core.scm', 'test.scm', 'list.scm', 'predicates.scm'
  ].each { |f| load(f) }
end