bus-scheme 0.7 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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