bus-scheme 0.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.
data/COPYING ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2007, Phil Hagelberg
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name of Phil Hagelberg nor the names of its contributors
13
+ may be used to endorse or promote products derived from this software
14
+ without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,14 @@
1
+ COPYING
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/bus
6
+ lib/array_extensions.rb
7
+ lib/bus_scheme.rb
8
+ lib/eval.rb
9
+ lib/object_extensions.rb
10
+ lib/parser.rb
11
+ test/foo.scm
12
+ test/test_eval.rb
13
+ test/test_helper.rb
14
+ test/test_parser.rb
@@ -0,0 +1,63 @@
1
+ Bus Scheme
2
+ by Phil Hagelberg (c) 2007
3
+ http://bus-scheme.rubyforge.org
4
+
5
+ == Description
6
+
7
+ Bus Scheme is a Scheme written in Ruby, but implemented on the bus!
8
+ Every programmer must implement Scheme as a rite of passage; this is
9
+ mine. Note that all the implementation of Bus Scheme must be written
10
+ while on a bus. Documentation, tests, and administrivia may be
11
+ accomplished elsewhere, but all actual implementation code is strictly
12
+ bus-driven. Patches are welcome as long as they were written while
13
+ riding a bus. (If your daily commute does not involve a bus but you
14
+ want to submit a patch, we may be able to work something out regarding
15
+ code written on trains, ferries, or perhaps even carpool lanes.) Bus
16
+ Scheme is primarily a toy; using it for anything serious is (right
17
+ now) ill-advised.
18
+
19
+ Bus Scheme aims for general Scheme usefulness optimized for learning
20
+ and fun. It's not targeting R5RS or anything like that.
21
+
22
+ == Install
23
+
24
+ * sudo gem install bus-scheme
25
+
26
+ For the source:
27
+
28
+ * git clone git://git.caboo.se/bus_scheme.git
29
+
30
+ == Usage
31
+
32
+ $ bus # drop into a repl
33
+
34
+ $ bus -e "(do some stuff)"
35
+
36
+ $ bus foo.scm # load a file -- todo
37
+
38
+ == Todo
39
+
40
+ Bus Scheme is currently missing huge pieces of functionality:
41
+
42
+ * lexical scoping
43
+ * closures
44
+ * loading files
45
+ * eval input from command line, stdin
46
+ * parse cons cells
47
+ * parse character literals
48
+ * numeric tower?
49
+
50
+ Failing tests for most of these are already included (commented out,
51
+ mostly) in the relevant test files.
52
+
53
+ == Requirements
54
+
55
+ Bus Scheme should run on (at least) Ruby 1.8, Ruby 1.9, and Rubinius.
56
+
57
+ == Bonus Fact
58
+
59
+ I haven't actually used a real Scheme yet. Everything I know about it
60
+ I've gathered from reading The Little Schemer, watching the Structure
61
+ and Interpretation of Computer Programs videos, and reading lots about
62
+ Common Lisp and Emacs Lisp. If there are huge gaping flaws in the
63
+ implementation, this is likely to be why.
@@ -0,0 +1,25 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/bus_scheme.rb'
6
+ require 'rake/testtask'
7
+
8
+ Hoe.new('bus-scheme', BusScheme::VERSION) do |p|
9
+ p.rubyforge_name = 'bus-scheme'
10
+ p.author = 'Phil Hagelberg'
11
+ p.email = 'technomancy@gmail.com'
12
+ p.summary = 'Bus Scheme is a Scheme in Ruby, imlemented on the bus.'
13
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
14
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
15
+ p.remote_rdoc_dir = ''
16
+ end
17
+
18
+ task :stats do
19
+ require 'code_statistics'
20
+ CodeStatistics.new(['lib'], ['Unit tests', 'test']).to_s
21
+ end
22
+
23
+ task :flog do
24
+ system "flog lib/*rb"
25
+ end
data/bin/bus ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH << File.dirname(__FILE__) + '/../lib'
3
+
4
+ require 'bus_scheme'
5
+
6
+ if ARGV.empty?
7
+ BusScheme.repl
8
+ elsif ARGV.first == '-e' and ARGV.length == 2
9
+ puts BusScheme.eval_string(ARGV[1])
10
+ elsif ARGV.length == 1 and File.exist?(ARGV.first) # must be a file
11
+ puts BusScheme.load(ARGV.first)
12
+ else
13
+ puts "Bus Scheme: a scheme interpreter written on the bus.
14
+ Usage: bus [file | -e \"form\"]
15
+ Invoking with no arguments enters a REPL
16
+ With one argument it loads that as a file
17
+ -e : evals a single form"
18
+ exit 1
19
+ end
@@ -0,0 +1,19 @@
1
+ class Array
2
+ # Lisp-style list access
3
+ def rest
4
+ self[1 .. -1]
5
+ end
6
+
7
+ alias_method :car, :first
8
+ alias_method :cdr, :rest
9
+
10
+ # Treat the array as a lambda and call it with given args
11
+ def call(*args)
12
+ BusScheme::eval_lambda(self, args)
13
+ end
14
+
15
+ # Simple predicate for convenience
16
+ def lambda?
17
+ first == :lambda
18
+ end
19
+ end
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'readline'
5
+ require 'yaml'
6
+ rescue LoadError
7
+ end
8
+
9
+ $LOAD_PATH << File.dirname(__FILE__)
10
+ require 'object_extensions'
11
+ require 'array_extensions'
12
+ require 'parser'
13
+ require 'eval'
14
+
15
+ module BusScheme
16
+ class ParseError < StandardError; end
17
+ class EvalError < StandardError; end
18
+ class ArgumentError < StandardError; end
19
+
20
+ VERSION = "0.6"
21
+
22
+ PRIMITIVES = {
23
+ '#t'.intern => true, # :'#t' screws up emacs' ruby parser
24
+ '#f'.intern => false,
25
+
26
+ :+ => lambda { |*args| args.inject(0) { |sum, i| sum + i } },
27
+ :- => lambda { |x, y| x - y },
28
+ '/'.intern => lambda { |x, y| x / y },
29
+ :* => lambda { |*args| args.inject(1) { |product, i| product * i } },
30
+
31
+ :> => lambda { |x, y| x > y },
32
+ :< => lambda { |x, y| x < y },
33
+
34
+ :intern => lambda { |x| x.intern },
35
+ :concat => lambda { |x, y| x + y },
36
+ :substring => lambda { |x, from, to| x[from .. to] },
37
+
38
+ :exit => lambda { exit }, :quit => lambda { exit },
39
+ }
40
+
41
+ SPECIAL_FORMS = {
42
+ :quote => lambda { |arg| arg },
43
+ :if => lambda { |condition, yes, *no| eval_form(condition) ? eval_form(yes) : eval_form([:begin] + no) },
44
+ :begin => lambda { |*args| args.map{ |arg| eval_form(arg) }.last },
45
+ :set! => lambda { },
46
+ :lambda => lambda { |args, *form| [:lambda, args] + form },
47
+ :define => lambda { |sym, definition| BusScheme[sym] = eval_form(definition); sym },
48
+ }
49
+
50
+ SYMBOL_TABLE = {}.merge(PRIMITIVES).merge(SPECIAL_FORMS)
51
+ SCOPES = [SYMBOL_TABLE]
52
+ PROMPT = '> '
53
+
54
+ # symbol existence predicate
55
+ def self.in_scope?(symbol)
56
+ SCOPES.last.has_key?(symbol) or SCOPES.first.has_key?(symbol)
57
+ end
58
+
59
+ # symbol lookup
60
+ def self.[](symbol)
61
+ SCOPES.last[symbol] or SCOPES.first[symbol]
62
+ end
63
+
64
+ # symbol assignment to value
65
+ def self.[]=(symbol, value)
66
+ SCOPES.last[symbol] = value
67
+ end
68
+
69
+ # remove symbols from all scopes
70
+ def self.clear_symbols(*symbols)
71
+ SCOPES.map{ |scope| symbols.map{ |sym| scope.delete sym } }
72
+ end
73
+
74
+ # symbol special form predicate
75
+ def self.special_form?(symbol)
76
+ SPECIAL_FORMS.has_key?(symbol)
77
+ end
78
+
79
+ # Read-Eval-Print-Loop
80
+ def self.repl
81
+ loop do
82
+ begin
83
+ puts BusScheme.eval_string(Readline.readline(PROMPT))
84
+ rescue Interrupt
85
+ puts 'Type "(quit)" to leave Bus Scheme.'
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,51 @@
1
+ module BusScheme
2
+ class << self
3
+ # Parse a string, then eval the result
4
+ def eval_string(string)
5
+ eval_form(parse(string))
6
+ end
7
+
8
+ # Eval a form passed in as an array
9
+ def eval_form(form)
10
+ if form == []
11
+ nil
12
+ elsif form.is_a? Array
13
+ apply(form.first, *form.rest)
14
+ elsif form.is_a? Symbol
15
+ raise "Undefined symbol: #{form}" unless in_scope?(form)
16
+ BusScheme[form]
17
+ else
18
+ form
19
+ end
20
+ end
21
+
22
+ # Call a function with given args
23
+ def apply(function, *args)
24
+ args.map!{ |arg| eval_form(arg) } unless special_form?(function)
25
+
26
+ # refactor me
27
+ if function.is_a?(Array) and function.lambda?
28
+ function.call(*args)
29
+ else
30
+ raise "Undefined symbol: #{function}" unless in_scope?(function)
31
+ BusScheme[function].call(*args)
32
+ end
33
+ end
34
+
35
+ # All the super lambda magic happens (or fails to happen) here
36
+ def eval_lambda(lambda, args)
37
+ raise BusScheme::EvalError unless lambda.shift == :lambda
38
+
39
+ arg_list = lambda.shift
40
+ raise BusScheme::ArgumentError if !arg_list.is_a?(Array) or arg_list.length != args.length
41
+
42
+ SCOPES << {} # new scope
43
+ until arg_list.empty?
44
+ BusScheme[arg_list.shift] = args.shift
45
+ end
46
+
47
+ # using affect as a non-return-value-affecting callback
48
+ BusScheme[:begin].call(*lambda).affect { SCOPES.pop }
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,8 @@
1
+ class Object
2
+ # Return self after evaling block
3
+ # see http://www.ruby-forum.com/topic/131340
4
+ def affect
5
+ yield self
6
+ return self
7
+ end
8
+ end
@@ -0,0 +1,69 @@
1
+ module BusScheme
2
+ class << self
3
+ # Turn an input string into an S-expression
4
+ def parse(input)
5
+ parse_tokens tokenize(normalize_whitespace(input))
6
+ end
7
+
8
+ # Turn a list of tokens into a properly-nested S-expression
9
+ def parse_tokens(tokens)
10
+ token = tokens.shift
11
+ if token == :'('
12
+ parse_list(tokens)
13
+ else
14
+ raise BusScheme::ParseError unless tokens.empty?
15
+ token # atom
16
+ end
17
+ end
18
+
19
+ # Nest a list from a 1-dimensional list of tokens
20
+ def parse_list(tokens)
21
+ [].affect do |list|
22
+ while element = tokens.shift and element != :')'
23
+ if element == :'('
24
+ list << parse_list(tokens)
25
+ else
26
+ list << element
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ # Split an input string into lexically valid tokens
33
+ def tokenize(input)
34
+ [].affect do |tokens|
35
+ while token = pop_token(input)
36
+ tokens << token
37
+ end
38
+ end
39
+ end
40
+
41
+ # Take a token off the input string and return it
42
+ def pop_token(input)
43
+ token = case input
44
+ when /^ +/ # whitespace
45
+ input[0 ... 1] = ''
46
+ return pop_token(input)
47
+ when /^\(/ # open paren
48
+ :'('
49
+ when /^\)/ # closing paren
50
+ :')'
51
+ when /^(\d+)/ # positive integer
52
+ Regexp.last_match[1].to_i
53
+ when /^"(.*?)"/ # string
54
+ Regexp.last_match[1]
55
+ when /^([^ \)]+)/ # symbol
56
+ Regexp.last_match[1].intern
57
+ end
58
+ # compensate for quotation marks
59
+ length = token.is_a?(String) ? token.length + 2 : token.to_s.length
60
+ input[0 .. length - 1] = ''
61
+ return token
62
+ end
63
+
64
+ # Treat all whitespace in a string as spaces
65
+ def normalize_whitespace(string)
66
+ string && string.gsub(/\t/, ' ').gsub(/\n/, ' ').gsub(/ +/, ' ')
67
+ end
68
+ end
69
+ end
@@ -0,0 +1 @@
1
+ (define foo 3)
@@ -0,0 +1,158 @@
1
+ $LOAD_PATH << File.dirname(__FILE__)
2
+ require 'test_helper'
3
+
4
+ class BusSchemeEvalTest < Test::Unit::TestCase
5
+ def test_eval_empty_list
6
+ assert_evals_to nil, []
7
+ end
8
+
9
+ def test_eval_number
10
+ assert_evals_to 2, 2
11
+ end
12
+
13
+ def test_eval_symbol
14
+ eval "(define hi 13)"
15
+ assert_evals_to 13, :hi
16
+ end
17
+
18
+ def test_eval_string
19
+ assert_evals_to "hi", "\"hi\""
20
+ end
21
+
22
+ def test_eval_function_call
23
+ assert_evals_to 2, [:+, 1, 1]
24
+ end
25
+
26
+ def test_many_args_for_arithmetic
27
+ assert_evals_to 4, [:+, 1, 1, 1, 1]
28
+ assert_evals_to 2, [:*, 1, 2, 1, 1]
29
+ end
30
+
31
+ def test_arithmetic
32
+ assert_evals_to 2, [:'-', 4, 2]
33
+ assert_evals_to 2, [:'/', 4, 2]
34
+ assert_evals_to 2, [:'*', 1, 2]
35
+ end
36
+
37
+ def test_define
38
+ BusScheme.clear_symbols :foo
39
+ eval("(define foo 5)")
40
+ assert_equal 5, BusScheme[:foo]
41
+ eval("(define foo (quote (5 5 5))")
42
+ assert_evals_to [5, 5, 5], :foo
43
+ end
44
+
45
+ def test_define_returns_defined_term
46
+ assert_evals_to :foo, "(define foo 2)"
47
+ # can't use the eval convenience testing method since it assumes strings are unparsed
48
+ assert_equal 2, eval("foo")
49
+ end
50
+
51
+ def test_string_primitives
52
+ assert_evals_to :hi, [:intern, 'hi']
53
+ assert_evals_to 'helloworld', [:concat, 'hello', 'world']
54
+ assert_evals_to 'lo', [:substring, 'hello', 3, -1]
55
+ end
56
+
57
+ def test_booleans
58
+ assert_evals_to false, '#f'
59
+ assert_evals_to true, '#t'
60
+ end
61
+
62
+ def test_eval_quote
63
+ assert_evals_to [:'+', 2, 2], [:quote, [:'+', 2, 2]]
64
+ end
65
+
66
+ def test_quote
67
+ assert_evals_to :hi, [:quote, :hi]
68
+ end
69
+
70
+ def test_nested_arithmetic
71
+ assert_evals_to 6, [:+, 1, [:+, 1, [:*, 2, 2]]]
72
+ end
73
+
74
+ def test_blows_up_with_undefined_symbol
75
+ assert_raises(RuntimeError) { eval("undefined-symbol") }
76
+ end
77
+
78
+ def test_variable_substitution
79
+ eval "(define foo 7)"
80
+ assert_evals_to 7, :foo
81
+ assert_evals_to 21, [:*, 3, :foo]
82
+ end
83
+
84
+ def test_if
85
+ assert_evals_to 7, [:if, '#f'.intern, 3, 7]
86
+ assert_evals_to 3, [:if, [:>, 8, 2], 3, 7]
87
+ end
88
+
89
+ def test_begin
90
+ eval([:begin,
91
+ [:define, :foo, 779],
92
+ 9])
93
+ assert_equal 779, BusScheme[:foo]
94
+ end
95
+
96
+ def test_set!
97
+ # i dunno... what does set! do? how's it different from define?
98
+ end
99
+
100
+ def test_simple_lambda
101
+ assert_equal [:lambda, [], [:+, 1, 1]], eval("(lambda () (+ 1 1))")
102
+ eval("(define foo (lambda () (+ 1 1)))")
103
+ assert_equal :lambda, BusScheme[:foo].first
104
+ assert_evals_to 2, [:foo]
105
+ end
106
+
107
+ def test_lambda_with_arg
108
+ eval("(define foo (lambda (x) (+ x 1)))")
109
+ assert_evals_to 2, [:foo, 1]
110
+ end
111
+
112
+ def test_eval_literal_lambda
113
+ assert_evals_to 4, "((lambda (x) (* x x)) 2)"
114
+ end
115
+
116
+ def test_lambda_with_incorrect_arity
117
+ eval("(define foo (lambda (x) (+ x 1)))")
118
+ assert_raises(BusScheme::ArgumentError) { assert_evals_to 2, [:foo, 1, 3] }
119
+ end
120
+
121
+ def test_lambda_args_dont_stay_in_scope
122
+ BusScheme.clear_symbols(:x, :foo)
123
+ eval("(define foo (lambda (x) (+ x 1)))")
124
+ assert !BusScheme.in_scope?(:x)
125
+ assert_evals_to 2, [:foo, 1]
126
+ assert !BusScheme.in_scope?(:x)
127
+ end
128
+
129
+ # def test_lexical_scoping
130
+ # assert_raises(BusScheme::EvalError) do
131
+ # eval "???"
132
+ # end
133
+ # end
134
+
135
+ # def test_lambda_closures
136
+ # eval "(define foo (lambda (x) ((lambda (y) (+ x y)) (* x 2))))"
137
+ # assert_evals_to 3, [:foo, 1]
138
+ # end
139
+
140
+ # def test_load_file
141
+ # eval "(load \"#{File.dirname(__FILE__)}/foo.scm\")"
142
+ # assert_evals_to 3, :foo
143
+ # end
144
+
145
+ private
146
+
147
+ def eval(form) # convenience method that accepts string or form
148
+ if form.is_a?(String)
149
+ BusScheme.eval_string(form)
150
+ else
151
+ BusScheme.eval_form(form)
152
+ end
153
+ end
154
+
155
+ def assert_evals_to(expected, form)
156
+ assert_equal expected, eval(form)
157
+ end
158
+ end
@@ -0,0 +1,10 @@
1
+ begin
2
+ require 'rubygems'
3
+ gem 'miniunit'
4
+ rescue LoadError
5
+ puts "Proceeding with standard test/unit instead of miniunit."
6
+ end
7
+
8
+ $LOAD_PATH << File.dirname(__FILE__) + '/../lib/'
9
+ require 'test/unit'
10
+ require 'bus_scheme'
@@ -0,0 +1,122 @@
1
+ $LOAD_PATH << File.dirname(__FILE__)
2
+ require 'test_helper'
3
+
4
+ class BusSchemeParserTest < Test::Unit::TestCase
5
+ def test_pop_token
6
+ string = "(+ 2 2)"
7
+ assert_equal :'(', BusScheme.pop_token(string)
8
+
9
+ assert_equal :'+', BusScheme.pop_token(string)
10
+ assert_equal 2, BusScheme.pop_token(string)
11
+ assert_equal 2, BusScheme.pop_token(string)
12
+ assert_equal :")", BusScheme.pop_token(string)
13
+
14
+ string = "\"two\")"
15
+ assert_equal "two", BusScheme.pop_token(string)
16
+ assert_equal ")", string
17
+ end
18
+
19
+ def test_tokenize
20
+ assert_equal [:'(', :'+', 2, 2, :')'], BusScheme.tokenize("(+ 2 2)")
21
+ assert_equal [:'(', :'+', 2, :'(', :'+', 22, 2, :')', :')'], BusScheme.tokenize("(+ 2 (+ 22 2))")
22
+ assert_equal [:'(', :plus, 2, 2, :')'], BusScheme.tokenize('(plus 2 2)')
23
+ end
24
+
25
+ def test_parse_numbers
26
+ assert_parses_to "99", 99
27
+ end
28
+
29
+ def test_parses_strings
30
+ assert_parses_to "\"hello world\"", "hello world"
31
+ end
32
+
33
+ def test_parses_two_strings
34
+ assert_parses_to "(concat \"hello\" \"world\")", [:concat, "hello", "world"]
35
+ end
36
+
37
+ def test_parse_list_of_numbers
38
+ assert_parses_to "(2 2)", [2, 2]
39
+ end
40
+
41
+ def test_parse_list_of_atoms
42
+ assert_parses_to "(+ 2 2)", [:+, 2, 2]
43
+ end
44
+
45
+ def test_parse_list_of_atoms_with_string
46
+ assert_parses_to "(+ 2 \"two\")", [:+, 2, "two"]
47
+ end
48
+
49
+ def test_parse_list_of_nested_sexprs
50
+ assert_parses_to "(+ 2 (+ 2))", [:+, 2, [:+, 2]]
51
+ end
52
+
53
+ def test_parse_list_of_deeply_nested_sexprs
54
+ assert_parses_to "(+ 2 (+ 2 (+ 2 2)))", [:+, 2, [:+, 2, [:+, 2, 2]]]
55
+ end
56
+
57
+ def test_parse_two_consecutive_parens_simple
58
+ assert_parses_to "(let ((foo 2)))", [:let, [[:foo, 2]]]
59
+ end
60
+
61
+ def test_parse_two_consecutive_parens
62
+ assert_parses_to "(let ((foo 2)) (+ foo 2))", [:let, [[:foo, 2]], [:+, :foo, 2]]
63
+ end
64
+
65
+ def test_whitespace_indifferent
66
+ assert_parses_equal "(+ 2 2)", "(+ 2 \n \t 2)"
67
+ end
68
+
69
+ # def test_parses_dotted_cons
70
+ # assert_parses_to "(22 . 11)", [:cons, 22, 11]
71
+ # assert_parses_to "((+ 2 2) . 11)", [:cons, [:+, 2, 2], 11]
72
+ # end
73
+
74
+ # def test_floats
75
+ # assert_parses_to "44.9", 44.9
76
+ # assert_parses_to "0.22", 0.22
77
+ # assert_parses_to ".22", 0.22
78
+ # assert_parses_to "2.220", 2.22
79
+ # end
80
+
81
+ # def test_negative_numbers
82
+ # assert_parses_to "-1", -1
83
+ # assert_parses_to "-0", 0
84
+ # assert_parses_to "-02", -2
85
+ # end
86
+
87
+ # def test_negative_floats
88
+ # assert_parses_to "-0.22", -0.22
89
+ # assert_parses_to "-.22", -0.22
90
+ # assert_parses_to "-0.10", -0.1
91
+ # end
92
+
93
+ # def test_character_literals
94
+ # assert_parses_to "?e", "e"
95
+ # assert_parses_to "?A", "A"
96
+ # # what else?
97
+ # end
98
+
99
+ def test_parse_random_elisp_form_from_my_dot_emacs
100
+ lisp = "(let ((system-specific-config
101
+ (concat \"~/.emacs.d/\"
102
+ (shell-command-to-string \"hostname\"))))
103
+ (if (file-exists-p system-specific-config)
104
+ (load system-specific-config)))"
105
+ assert_parses_to(lisp,
106
+ [:let, [[:'system-specific-config',
107
+ [:concat, "~/.emacs.d/",
108
+ [:'shell-command-to-string', "hostname"]]]],
109
+ [:if, [:'file-exists-p', :'system-specific-config'],
110
+ [:load, :'system-specific-config']]])
111
+ end
112
+
113
+ private
114
+
115
+ def assert_parses_to(actual_string, expected)
116
+ assert_equal expected, BusScheme.parse(actual_string)
117
+ end
118
+
119
+ def assert_parses_equal(one, two)
120
+ assert_equal BusScheme.parse(one), BusScheme.parse(two)
121
+ end
122
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bus-scheme
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.6"
5
+ platform: ""
6
+ authors:
7
+ - Phil Hagelberg
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2007-12-17 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.3.0
23
+ version:
24
+ description: Bus Scheme is a Scheme written in Ruby, but implemented on the bus! Every programmer must implement Scheme as a rite of passage; this is mine. Note that all the implementation of Bus Scheme must be written while on a bus. Documentation, tests, and administrivia may be accomplished elsewhere, but all actual implementation code is strictly bus-driven. Patches are welcome as long as they were written while riding a bus. (If your daily commute does not involve a bus but you want to submit a patch, we may be able to work something out regarding code written on trains, ferries, or perhaps even carpool lanes.) Bus Scheme is primarily a toy; using it for anything serious is (right now) ill-advised. Bus Scheme aims for general Scheme usefulness optimized for learning and fun. It's not targeting R5RS or anything like that. == Install * sudo gem install bus-scheme
25
+ email: technomancy@gmail.com
26
+ executables:
27
+ - bus
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - Manifest.txt
32
+ - README.txt
33
+ files:
34
+ - COPYING
35
+ - Manifest.txt
36
+ - README.txt
37
+ - Rakefile
38
+ - bin/bus
39
+ - lib/array_extensions.rb
40
+ - lib/bus_scheme.rb
41
+ - lib/eval.rb
42
+ - lib/object_extensions.rb
43
+ - lib/parser.rb
44
+ - test/foo.scm
45
+ - test/test_eval.rb
46
+ - test/test_helper.rb
47
+ - test/test_parser.rb
48
+ has_rdoc: true
49
+ homepage: " by Phil Hagelberg (c) 2007"
50
+ post_install_message:
51
+ rdoc_options:
52
+ - --main
53
+ - README.txt
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project: bus-scheme
71
+ rubygems_version: 0.9.5
72
+ signing_key:
73
+ specification_version: 2
74
+ summary: Bus Scheme is a Scheme in Ruby, imlemented on the bus.
75
+ test_files:
76
+ - test/test_parser.rb
77
+ - test/test_helper.rb
78
+ - test/test_eval.rb