bus-scheme 0.7 → 0.7.1

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.
@@ -13,4 +13,5 @@ lib/parser.rb
13
13
  test/foo.scm
14
14
  test/test_eval.rb
15
15
  test/test_helper.rb
16
+ test/test_lambda.rb
16
17
  test/test_parser.rb
data/README.txt CHANGED
@@ -33,15 +33,17 @@ $ bus # drop into a repl
33
33
 
34
34
  $ bus -e "(do some stuff)"
35
35
 
36
- $ bus foo.scm # load a file -- todo
36
+ $ bus foo.scm # load a file
37
37
 
38
38
  == Todo
39
39
 
40
40
  Bus Scheme is currently missing pieces of functionality:
41
41
 
42
- * changes to closure variables don't affect original scope
42
+ * move as many definitions into Scheme as possible
43
+ * compile to Rubinius bytecode
43
44
  * optimize tail call recursion
44
45
  * continuations (?!?)
46
+ * check for memory leaks
45
47
  * parse cons cells
46
48
  * parse character literals
47
49
  * parse quote, unquote
@@ -1,10 +1,4 @@
1
1
  class Array
2
- def to_hash
3
- {}.affect do |hash|
4
- self.each { |pair| hash[pair.first] = pair.last }
5
- end
6
- end
7
-
8
2
  # Lisp-style list access
9
3
  def rest
10
4
  self[1 .. -1]
@@ -13,3 +7,11 @@ class Array
13
7
  alias_method :car, :first
14
8
  alias_method :cdr, :rest
15
9
  end
10
+
11
+ module Enumerable # for 1.9, zip is defined on Enumerable
12
+ def to_hash
13
+ {}.affect do |hash|
14
+ self.each { |pair| hash[pair.first] = pair.last }
15
+ end
16
+ end
17
+ end
@@ -15,27 +15,26 @@ require 'definitions'
15
15
  require 'lambda'
16
16
 
17
17
  module BusScheme
18
- VERSION = "0.7"
18
+ VERSION = "0.7.1"
19
19
 
20
20
  SYMBOL_TABLE = {}.merge(PRIMITIVES).merge(SPECIAL_FORMS)
21
- LOCAL_SCOPES = []
22
21
  PROMPT = '> '
23
22
 
24
23
  # what scope is appropraite for this symbol
25
24
  def self.scope_of(symbol)
26
- ([LOCAL_SCOPES.last] + Lambda.environment + [SYMBOL_TABLE]).compact.detect { |scope| scope.has_key?(symbol) }
25
+ [Lambda.scope, SYMBOL_TABLE].compact.detect { |scope| scope.has_key?(symbol) }
27
26
  end
28
27
 
29
28
  # symbol lookup
30
29
  def self.[](symbol)
31
30
  scope = scope_of(symbol)
32
- raise EvalError.new("Undefined symbol: #{symbol}") if scope.nil?
33
- scope[symbol]
31
+ raise EvalError.new("Undefined symbol: #{symbol}") unless scope
32
+ scope && scope[symbol]
34
33
  end
35
34
 
36
35
  # symbol assignment to value
37
36
  def self.[]=(symbol, value)
38
- (scope_of(symbol) || SYMBOL_TABLE)[symbol] = value
37
+ (scope_of(symbol) || Lambda.scope || SYMBOL_TABLE)[symbol] = value
39
38
  end
40
39
 
41
40
  # symbol special form predicate
@@ -46,16 +45,16 @@ module BusScheme
46
45
  # Read-Eval-Print-Loop
47
46
  def self.repl
48
47
  loop do
49
- begin
50
- puts BusScheme.eval_string(Readline.readline(PROMPT))
51
- rescue Interrupt
52
- puts 'Type "(quit)" to leave Bus Scheme.'
53
- rescue BusSchemeError => e
54
- puts "Error: #{e}"
55
- rescue StandardError => e
56
- puts "You found a bug in Bus Scheme!"
57
- puts "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
58
- end
48
+ puts begin
49
+ BusScheme.eval_string(Readline.readline(PROMPT))
50
+ rescue Interrupt
51
+ 'Type "(quit)" to leave Bus Scheme.'
52
+ rescue BusSchemeError => e
53
+ "Error: #{e}"
54
+ rescue StandardError => e
55
+ "You found a bug in Bus Scheme!\n" +
56
+ "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
57
+ end
59
58
  end
60
59
  end
61
60
  end
@@ -15,11 +15,16 @@ module BusScheme
15
15
 
16
16
  :> => lambda { |x, y| x > y },
17
17
  :< => lambda { |x, y| x < y },
18
-
18
+ :'=' => lambda { |x, y| x == y }, # may not honor scheme's equality notions
19
+ :null? => lambda { |x| x.nil? },
20
+
19
21
  :intern => lambda { |x| x.intern },
20
- :concat => lambda { |x, y| x + y },
21
22
  :substring => lambda { |x, from, to| x[from .. to] },
22
23
 
24
+ :car => lambda { |list| list.car },
25
+ :cdr => lambda { |list| list.cdr },
26
+
27
+ :ruby => lambda { |code| eval(code) },
23
28
  :load => lambda { |filename| eval_string(File.read(filename)) },
24
29
  :exit => lambda { exit }, :quit => lambda { exit },
25
30
  }
@@ -1,32 +1,50 @@
1
1
  module BusScheme
2
+ class Scope < Hash
3
+ def initialize(table, parent)
4
+ @parent = parent
5
+ table.each { |k, v| self[k] = v }
6
+ end
7
+
8
+ alias_method :old_has_key?, :has_key?
9
+ def has_key?(symbol)
10
+ old_has_key?(symbol) or @parent && @parent.has_key?(symbol)
11
+ end
12
+
13
+ alias_method :lookup, :[]
14
+ def [](symbol)
15
+ lookup(symbol) or @parent && @parent[symbol]
16
+ end
17
+
18
+ alias_method :old_set, :[]=
19
+ def []=(symbol, value)
20
+ if !old_has_key?(symbol) and @parent and @parent.has_key?(symbol)
21
+ @parent[symbol] = value
22
+ else
23
+ old_set symbol, value
24
+ end
25
+ end
26
+ end
27
+
2
28
  class Lambda
3
- @@current = nil
29
+ @@stack = []
30
+
31
+ attr_reader :scope
4
32
 
5
33
  # create new lambda object
6
34
  def initialize(arg_names, body)
7
- @arg_names, @body, @environment = [arg_names, body, LOCAL_SCOPES]
35
+ @arg_names, @body, @enclosing_scope = [arg_names, body, Lambda.scope]
8
36
  end
9
-
10
- attr_reader :environment
11
37
 
12
38
  # execute lambda with given arg_values
13
39
  def call(*arg_values)
14
40
  raise BusScheme::ArgumentError if @arg_names.length != arg_values.length
15
- with_local_scope(@arg_names.zip(arg_values).to_hash) { BusScheme[:begin].call(*@body) }
41
+ @scope = Scope.new(@arg_names.zip(arg_values).to_hash, @enclosing_scope)
42
+ @@stack << self
43
+ BusScheme[:begin].call(*@body).affect { @@stack.pop }
16
44
  end
17
45
 
18
- def self.environment
19
- @@current ? @@current.environment : []
20
- end
21
-
22
- # execute a block with a given local scope
23
- def with_local_scope(scope, &block)
24
- BusScheme::LOCAL_SCOPES << scope
25
- @@current = self
26
- block.call.affect do
27
- BusScheme::LOCAL_SCOPES.delete(scope)
28
- @@current = nil
29
- end
46
+ def self.scope
47
+ @@stack.empty? ? nil : @@stack.last.scope
30
48
  end
31
49
  end
32
50
  end
@@ -0,0 +1,19 @@
1
+ $LOAD_PATH << File.dirname(__FILE__)
2
+ require 'test_helper'
3
+
4
+ # must be run in Rubinius
5
+ # how to check?
6
+
7
+ class BusSchemeCompilerTest < Test::Unit::TestCase
8
+ # def test_compiles_arithmetic
9
+ # assert_equivalent "(+ 1 2)", "1 + 2"
10
+ # end
11
+
12
+ private
13
+ def assert_equivalent(scheme, ruby)
14
+ scheme = BusScheme.parse(scheme) if scheme.is_a? String # for convenience
15
+ parsed_ruby = ruby.to_sexp.last
16
+ compiled_scheme = BusScheme::Compiler.compile(scheme)
17
+ assert_equal
18
+ end
19
+ end
@@ -10,6 +10,11 @@ class BusSchemeEvalTest < Test::Unit::TestCase
10
10
  assert_evals_to 2, 2
11
11
  end
12
12
 
13
+ def test_set_symbol
14
+ BusScheme[:hi] = 'hi'
15
+ assert BusScheme::SYMBOL_TABLE[:hi]
16
+ end
17
+
13
18
  def test_eval_symbol
14
19
  eval "(define hi 13)"
15
20
  assert_evals_to 13, :hi
@@ -49,11 +54,11 @@ class BusSchemeEvalTest < Test::Unit::TestCase
49
54
 
50
55
  def test_string_primitives
51
56
  assert_evals_to :hi, [:intern, 'hi']
52
- assert_evals_to 'helloworld', [:concat, 'hello', 'world']
53
57
  assert_evals_to 'lo', [:substring, 'hello', 3, -1]
54
58
  end
55
59
 
56
60
  def test_booleans
61
+ assert BusScheme::PRIMITIVES.has_key? '#f'.intern
57
62
  assert_evals_to false, '#f'
58
63
  assert_evals_to true, '#t'
59
64
  end
@@ -105,4 +110,8 @@ class BusSchemeEvalTest < Test::Unit::TestCase
105
110
  eval "(load \"#{File.dirname(__FILE__)}/foo.scm\")"
106
111
  assert_evals_to 3, :foo
107
112
  end
113
+
114
+ def test_eval_ruby
115
+ assert_evals_to "foofoofoo", "(ruby \"'foo' * 3\")"
116
+ end
108
117
  end
@@ -9,19 +9,22 @@ $LOAD_PATH << File.dirname(__FILE__) + '/../lib/'
9
9
  require 'test/unit'
10
10
  require 'bus_scheme'
11
11
 
12
- def eval(form) # convenience method that accepts string or form
13
- if form.is_a?(String)
14
- BusScheme.eval_string(form)
15
- else
16
- BusScheme.eval_form(form)
12
+ class Test::Unit::TestCase
13
+ # convenience method that accepts string or form
14
+ def eval(form)
15
+ if form.is_a?(String)
16
+ BusScheme.eval_string(form)
17
+ else
18
+ BusScheme.eval_form(form)
19
+ end
17
20
  end
18
- end
19
21
 
20
- def assert_evals_to(expected, form)
21
- assert_equal expected, eval(form)
22
- end
22
+ def assert_evals_to(expected, form)
23
+ assert_equal expected, eval(form)
24
+ end
23
25
 
24
- # remove symbols from all scopes
25
- def clear_symbols(*symbols)
26
- (BusScheme::LOCAL_SCOPES << BusScheme::SYMBOL_TABLE).map{ |scope| symbols.map{ |sym| scope.delete sym } }
26
+ # remove symbols from all scopes
27
+ def clear_symbols(*symbols)
28
+ [BusScheme::Lambda.scope, BusScheme::SYMBOL_TABLE].compact.map{ |scope| symbols.map{ |sym| scope.delete sym } }
29
+ end
27
30
  end
@@ -48,13 +48,15 @@ class BusSchemeLambdaTest < Test::Unit::TestCase
48
48
  def test_lambda_closures
49
49
  eval "(define foo (lambda (x) ((lambda (y) (+ x y)) (* x 2))))"
50
50
  assert_evals_to 3, [:foo, 1]
51
+ eval "(define holder ((lambda (x) (lambda () x)) 2))"
52
+ assert_evals_to 2, "(holder)"
51
53
  end
52
54
 
53
55
  def test_changes_to_enclosed_variables_are_in_effect_after_lambda_execution
54
56
  assert_evals_to 2, "((lambda (x) (begin ((lambda () (set! x 2))) x)) 1)"
55
57
  end
56
58
 
57
- def test_implicit_begin
58
- assert_evals_to 3, "((lambda () (intern \"hi\") (+ 2 2) (* 1 3)))"
59
- end
59
+ def test_implicit_begin
60
+ assert_evals_to 3, "((lambda () (intern \"hi\") (+ 2 2) (* 1 3)))"
61
+ end
60
62
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bus-scheme
3
3
  version: !ruby/object:Gem::Version
4
- version: "0.7"
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phil Hagelberg
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-01-10 00:00:00 -08:00
12
+ date: 2008-01-17 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -46,6 +46,7 @@ files:
46
46
  - test/foo.scm
47
47
  - test/test_eval.rb
48
48
  - test/test_helper.rb
49
+ - test/test_lambda.rb
49
50
  - test/test_parser.rb
50
51
  has_rdoc: true
51
52
  homepage: " by Phil Hagelberg (c) 2007"
@@ -76,6 +77,7 @@ specification_version: 2
76
77
  summary: Bus Scheme is a Scheme in Ruby, imlemented on the bus.
77
78
  test_files:
78
79
  - test/test_lambda.rb
80
+ - test/test_compiler.rb
79
81
  - test/test_parser.rb
80
82
  - test/test_eval.rb
81
83
  - test/test_helper.rb