Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

using Syms for parse tree; sad

  • Loading branch information...
commit 18dad5cb5645a5b0583b5ef86a846c88372c8f5d 1 parent 16b33a5
@technomancy technomancy authored
View
5 lib/bus_scheme.rb
@@ -24,8 +24,9 @@ module BusScheme
# symbol special form predicate
def self.special_form?(form)
- form.is_a? Symbol or form.is_a? Node and
- SPECIAL_FORMS.has_key?(form)
+ form.is_a?(Sym) and
+ # TODO: hacky; has_key? has different equality semantics from ==
+ SPECIAL_FORMS.keys.any?{ |k| k == form }
end
# Read-Eval-Print-Loop
View
6 lib/eval.rb
@@ -10,8 +10,10 @@ def eval(form)
# puts "evaling #{form.inspect}"
if (form.is_a?(Cons) or form.is_a?(Array)) and form.first
apply(form.first, form.rest)
- elsif form.is_a? Symbol or form.is_a? Node
- raise EvalError.new("Undefined symbol: #{form}") unless Lambda.scope.has_key?(form)
+ # TODO: should we still allow symbols?
+ elsif form.is_a? Sym or form.is_a? Symbol
+ form = form.sym if form.is_a? Symbol
+ raise EvalError.new("Undefined symbol: #{form.inspect}") unless Lambda.in_scope?(form)
Lambda[form]
else # well it must be a literal then
form
View
14 lib/hash_extensions.rb
@@ -1,17 +1,3 @@
class Hash
alias_method :call, :[]
- alias_method :old_lookup, :[]
- alias_method :old_has_key, :has_key?
-
- def has_key?(key)
- old_has_key(key) or old_has_key(key.to_sym) or old_has_key(key.node)
- end
-
- def [](key)
- if old_has_key(key)
- old_lookup(key)
- else
- old_lookup(key.to_sym)
- end
- end
end
View
14 lib/lambda.rb
@@ -43,7 +43,7 @@ def initialize(formals, body)
# execute Lambda with given arg_values
def call(*args)
- locals = if @formals.is_a? Symbol # rest args
+ locals = if @formals.is_a? Sym # rest args
{ @formals => args.to_list }
else # regular arg list
raise BusScheme::ArgumentError if @formals.length != args.length
@@ -52,7 +52,7 @@ def call(*args)
@scope = RecursiveHash.new(locals, @enclosing_scope)
@@stack << self
- BusScheme.eval(@body.unshift(:begin)).affect { @@stack.pop }
+ BusScheme.eval(@body.unshift(:begin.sym)).affect { @@stack.pop }
end
# What's the current scope?
@@ -60,19 +60,21 @@ def self.scope
@@stack.empty? ? SYMBOL_TABLE : @@stack.last.scope
end
+ # is the symbol in scope?
+ def self.in_scope?(symbol)
+ self.scope.has_key?(symbol) or SYMBOL_TABLE.has_key?(symbol)
+ end
+
# shorthand for lookup in the currently relevant scope
def self.[](symbol)
return self.scope[symbol] if self.scope.has_key?(symbol)
- return self.scope[symbol.to_sym] if self.scope.has_key?(symbol.to_sym)
- return self.scope[symbol.node] if self.scope.has_key?(symbol.node)
end
# shorthand for assignment in the currently relevant scope
def self.[]=(symbol, val)
- puts "#{symbol.inspect} = #{val.inspect}"
val.file = symbol.file if val.respond_to?(:file)
val.line = symbol.line if val.respond_to?(:line)
- self.scope[symbol.node] = val
+ self.scope[symbol] = val
end
end
end
View
16 lib/object_extensions.rb
@@ -12,14 +12,14 @@ def sexp
end
class String
- def node
- Node.new(self)
+ def sym
+ Sym.new(self)
end
end
class Symbol
- def node
- Node.new(self.to_s)
+ def sym
+ Sym.new(self.to_s)
end
def file
@@ -47,12 +47,12 @@ def line
end
end
-class Node < String
+class Sym < String
attr_accessor :file, :line
- def eql?(other)
- self.intern.eql?(other)
- end
+# def eql?(other)
+# self.intern.eql?(other)
+# end
def inspect
";#{self}"
View
8 lib/parser.rb
@@ -57,10 +57,10 @@ def pop_token(input)
Regexp.last_match[1].intern
when /\A#\(/ # vector
input[0 ... 2] = ''
- return [:'(', :vector, tokenize(input)]
+ return [:'(', :vector.sym, tokenize(input)]
when /\A'/ # single-quote
input[0 ... 1] = ''
- return [:'(', :quote,
+ return [:'(', :quote.sym,
if input[0 ... 1] == '('
tokenize(input)
else
@@ -75,9 +75,7 @@ def pop_token(input)
Regexp.last_match[2]
when /\A(#{IDENTIFIER_BEGIN}+#{IDENTIFIER_CHARS}*)/ # symbol
# puts "#{Regexp.last_match[1]} - #{@@lines}"
- # bugger--this is *not* going to work. every reference to the symbol
- # resets the defined_in properties. ick! i don't want a ParseNode class!
- Regexp.last_match[1].node.affect{ |sym| sym.file, sym.line = [BusScheme.loaded_files.last, @@lines] }
+ Regexp.last_match[1].sym.affect{ |sym| sym.file, sym.line = [BusScheme.loaded_files.last, @@lines] }
end
# Remove the matched part from the string
View
52 lib/primitives.rb
@@ -9,42 +9,42 @@ class AssertionFailed < BusSchemeError; end
# right now I believe there are as few things implemented primitively as possible
# except for functions that require splat args. do we need something like &rest?
- '#t'.intern => true, # :'#t' screws up emacs' ruby parser
- '#f'.intern => false,
+ '#t'.sym => true, # :'#t' screws up emacs' ruby parser
+ '#f'.sym => false,
- :+ => lambda { |*args| args.inject { |sum, i| sum + i } },
- :- => lambda { |x, y| x - y },
- :* => lambda { |*args| args.inject { |product, i| product * i } },
- '/'.intern => lambda { |x, y| x / y },
+ :+.sym => lambda { |*args| args.inject { |sum, i| sum + i } },
+ :-.sym => lambda { |x, y| x - y },
+ :*.sym => lambda { |*args| args.inject { |product, i| product * i } },
+ '/'.sym => lambda { |x, y| x / y },
- :concat => lambda { |*args| args.join('') },
- :cons => lambda { |car, cdr| Cons.new(car, cdr) },
- :list => lambda { |*members| members.to_list },
- :vector => lambda { |*members| members },
+ :concat.sym => lambda { |*args| args.join('') },
+ :cons.sym => lambda { |car, cdr| Cons.new(car, cdr) },
+ :list.sym => lambda { |*members| members.to_list },
+ :vector.sym => lambda { |*members| members },
- :ruby => lambda { |*code| Kernel.eval code.join('') },
- :eval => lambda { |code| eval(code) },
- :send => lambda { |obj, *message| obj.send(*message) },
- :assert => lambda { |cond| raise AssertionFailed unless cond },
- :load => lambda { |filename| BusScheme.load filename },
- :exit => lambda { exit }, :quit => lambda { exit },
+ :ruby.sym => lambda { |*code| Kernel.eval code.join('') },
+ :eval.sym => lambda { |code| eval(code) },
+ :send.sym => lambda { |obj, *message| obj.send(*message) },
+ :assert.sym => lambda { |cond| raise AssertionFailed unless cond },
+ :load.sym => lambda { |filename| BusScheme.load filename },
+ :exit.sym => lambda { exit }, :quit.sym => lambda { exit },
}
# if we add in macros, can some of these be defined in scheme?
SPECIAL_FORMS = {
# TODO: hacky to coerce everything to sexps... won't work once we start using vectors
- :quote => lambda { |arg| arg.sexp },
- :if => lambda { |q, yes, *no| eval(q) ? eval(yes) : eval([:begin] + no) },
- :begin => lambda { |*args| args.map{ |arg| eval(arg) }.last },
- :set! => lambda { |sym, value| raise EvalError.new unless Lambda.scope.has_key?(sym) or Lambda.scope.has_key?(sym.intern) and
+ :quote.sym => lambda { |arg| arg.sexp },
+ :if.sym => lambda { |q, yes, *no| eval(q) ? eval(yes) : eval([:begin] + no) },
+ :begin.sym => lambda { |*args| args.map{ |arg| eval(arg) }.last },
+ :set!.sym => lambda { |sym, value| raise EvalError.new unless Lambda.in_scope?(sym)
Lambda[sym] = eval(value); sym },
- :lambda => lambda { |args, *form| Lambda.new(args, form) },
- :define => lambda { |sym, definition| Lambda[sym] = eval(definition); sym },
+ :lambda.sym => lambda { |args, *form| Lambda.new(args, form) },
+ :define.sym => lambda { |sym, definition| Lambda[sym] = eval(definition); sym },
# once we have macros, this can be defined in scheme
- :and => lambda { |*args| args.all? { |x| eval(x) } },
- :or => lambda { |*args| args.any? { |x| eval(x) } },
- :let => lambda { |defs, *body| Lambda.new(defs.map{ |d| d.car }, body).call(*defs.map{ |d| eval d.last }) },
- :hash => lambda { |*args| args.to_hash }, # accepts an alist
+ :and.sym => lambda { |*args| args.all? { |x| eval(x) } },
+ :or.sym => lambda { |*args| args.any? { |x| eval(x) } },
+ :let.sym => lambda { |defs, *body| Lambda.new(defs.map{ |d| d.car }, body).call(*defs.map{ |d| eval d.last }) },
+ :hash.sym => lambda { |*args| args.to_hash }, # accepts an alist
}
end
View
2  lib/scheme/core.scm
@@ -1,4 +1,4 @@
-(define intern
+(define string->symbol
(lambda (sym) (send sym 'intern)))
(define substring
View
14 test/test_core.rb
@@ -14,18 +14,18 @@ def test_comparison
end
def test_string_functions
- assert_evals_to :hi, [:intern, 'hi']
- assert_evals_to 'lo', [:substring, 'hello', 3, 5]
+ assert_evals_to :hi, ['string->symbol'.sym, 'hi']
+ assert_evals_to 'lo', [:substring.sym, 'hello', 3, 5]
end
def test_list_functions
- assert_evals_to :foo, "(car (cons (quote foo) (quote bar)))"
- assert_evals_to :bar, "(cdr (cons (quote foo) (quote bar)))"
- assert_equal(Cons.new(:foo, Cons.new(:bar, nil)),
- [:foo, :bar].to_list)
+ assert_evals_to :foo.sym, "(car (cons (quote foo) (quote bar)))"
+ assert_evals_to :bar.sym, "(cdr (cons (quote foo) (quote bar)))"
+ assert_equal(Cons.new(:foo.sym, Cons.new(:bar.sym, nil)),
+ [:foo.sym, :bar.sym].to_list)
assert_evals_to(Cons.new(2, Cons.new(3, nil)),
"(list 2 3)")
assert_evals_to "bar", "(cadr (list \"foo\" \"bar\"))"
- assert_evals_to [1, :foo].to_list, "(list 1 'foo)"
+ assert_evals_to [1, :foo.sym].to_list, "(list 1 'foo)"
end
end
View
19 test/test_eval.rb
@@ -17,8 +17,8 @@ def test_set_symbol
end
def test_eval_symbol
- Lambda.scope[:hi] = 13
- assert_evals_to 13, :hi
+ Lambda.scope[:hi.sym] = 13
+ assert_evals_to 13, :hi.sym
end
def test_eval_string
@@ -39,18 +39,19 @@ def test_blows_up_with_undefined_symbol
def test_variable_substitution
eval "(define foo 7)"
- assert_evals_to 7, :foo.node
- assert_evals_to 21, [:*, 3, :foo.node]
+ assert_evals_to 7, :foo.sym
+ assert_evals_to 21, [:*.sym, 3, :foo.sym]
end
def test_single_quote
- assert_evals_to :foo.node, "'foo"
- assert_evals_to [:foo.node, :biz.node, :bbb.node].to_list, "'(foo biz bbb)"
+ assert BusScheme.special_form?(:quote.sym)
+ assert_evals_to :foo.sym, "'foo"
+ assert_evals_to [:foo.sym, :biz.sym, :bbb.sym].to_list, "'(foo biz bbb)"
end
def test_array_of_args_or_list_of_args
- assert_evals_to 5, cons(:+, cons(2, cons(3)))
- assert_evals_to 5, cons(:+, cons(2, cons(3)).to_a)
+ assert_evals_to 5, cons(:+.sym, cons(2, cons(3)))
+ assert_evals_to 5, cons(:+.sym, cons(2, cons(3)).to_a)
end
def test_eval_multiple_forms
@@ -62,7 +63,7 @@ def test_eval_multiple_forms
def test_define_after_load
BusScheme.eval_string "(load \"#{File.dirname(__FILE__)}/../examples/fib.scm\")
(define greeting \"hi\")"
- assert Lambda[:greeting]
+ assert Lambda.in_scope?(:greeting.sym)
end
def test_funcall_list_means_nth
View
24 test/test_lambda.rb
@@ -9,12 +9,12 @@ class BusSchemeLambdaTest < Test::Unit::TestCase
def test_simple_lambda
l = eval("(lambda () (+ 1 1))")
assert l.is_a?(Lambda)
- assert_equal [[:+, 1, 1]], l.body
+ assert_equal [[:+.sym, 1, 1]], l.body
assert_equal [], l.formals
eval("(define foo (lambda () (+ 1 1)))")
- assert Lambda[:foo].is_a?(Lambda)
- assert_evals_to 2, [:foo]
+ assert Lambda[:foo.sym].is_a?(Lambda)
+ assert_evals_to 2, [:foo.sym]
end
def test_lambda_with_arg
@@ -57,11 +57,11 @@ def test_changes_to_enclosed_variables_are_in_effect_after_lambda_execution
end
def test_implicit_begin
- assert_evals_to 3, "((lambda () (intern \"hi\") (+ 2 2) (* 1 3)))"
+ assert_evals_to 3, "((lambda () (string->symbol \"hi\") (+ 2 2) (* 1 3)))"
end
def test_shadowed_vars_dont_stay_in_scope
- assert_evals_to Cons.new(:a, :b), "(let ((f (let ((x (quote a)))
+ assert_evals_to Cons.new(:a.sym, :b.sym), "(let ((f (let ((x (quote a)))
(lambda (y) (cons x y)))))
(let ((x (quote not-a)))
(f (quote b))))"
@@ -69,18 +69,18 @@ def test_shadowed_vars_dont_stay_in_scope
def test_lambda_rest_args
eval "(define rest (lambda args args))"
- assert_evals_to [:a, :b, :c].to_list, "(rest 'a 'b 'c)"
+ assert_evals_to [:a.sym, :b.sym, :c.sym].to_list, "(rest 'a 'b 'c)"
end
def test_lambdas_know_what_file_they_were_defined_in
- assert_equal "(primitive)", Lambda[:if].file
+ assert_equal "(primitive)", Lambda[:if.sym].file
eval "(define fab (lambda () \"warble\"))"
- assert_equal "(eval)", Lambda[:fab.node].file
+ assert_equal "(eval)", Lambda[:fab.sym].file
filename = File.expand_path("#{File.dirname(__FILE__)}/../examples/fib.scm")
eval "(load \"#{filename}\")"
- assert_equal filename, Lambda[:fib.node].file
+ assert_equal filename, Lambda[:fib.sym].file
end
# def test_lambdas_know_what_line_they_were_defined_in
@@ -88,10 +88,10 @@ def test_lambdas_know_what_file_they_were_defined_in
# filename = File.expand_path("#{File.dirname(__FILE__)}/../examples/fib.scm")
# eval "(load \"#{filename}\")"
-# assert Lambda.scope[:fib.node].is_a?(Lambda)
-# assert_equal 1, Lambda.scope[:fib.node].line
+# assert Lambda.scope[:fib.sym].is_a?(Lambda)
+# assert_equal 1, Lambda.scope[:fib.sym].line
# eval "#{"\n" * 7} (define fab 'warble)"
-# assert_equal 7, Lambda[:fab.node].line
+# assert_equal 7, Lambda[:fab.sym].line
# end
end
View
48 test/test_parser.rb
@@ -6,7 +6,7 @@ def test_pop_token
string = "(+ 2 2)"
assert_equal :'(', BusScheme.pop_token(string)
- assert_equal :'+'.node, BusScheme.pop_token(string)
+ assert_equal :'+'.sym, BusScheme.pop_token(string)
assert_equal 2, BusScheme.pop_token(string)
assert_equal 2, BusScheme.pop_token(string)
assert_equal :")", BusScheme.pop_token(string)
@@ -17,9 +17,9 @@ def test_pop_token
end
def test_tokenize
- assert_equal [:'(', :'+'.node, 2, 2, :')'], BusScheme.tokenize("(+ 2 2)")
- assert_equal [:'(', :'+'.node, 2, :'(', :'+'.node, 22, 2, :')', :')'], BusScheme.tokenize("(+ 2 (+ 22 2))")
- assert_equal [:'(', :plus.node, 2, 2, :')'], BusScheme.tokenize('(plus 2 2)')
+ assert_equal [:'(', :'+'.sym, 2, 2, :')'], BusScheme.tokenize("(+ 2 2)")
+ assert_equal [:'(', :'+'.sym, 2, :'(', :'+'.sym, 22, 2, :')', :')'], BusScheme.tokenize("(+ 2 (+ 22 2))")
+ assert_equal [:'(', :plus.sym, 2, 2, :')'], BusScheme.tokenize('(plus 2 2)')
end
def test_parse_numbers
@@ -31,7 +31,7 @@ def test_parses_strings
end
def test_parses_two_strings
- assert_parses_to "(concat \"hello\" \"world\")", [:concat.node, "hello", "world"]
+ assert_parses_to "(concat \"hello\" \"world\")", [:concat.sym, "hello", "world"]
end
def test_parse_list_of_numbers
@@ -39,27 +39,27 @@ def test_parse_list_of_numbers
end
def test_parse_list_of_atoms
- assert_parses_to "(+ 2 2)", [:+.node, 2, 2]
+ assert_parses_to "(+ 2 2)", [:+.sym, 2, 2]
end
def test_parse_list_of_atoms_with_string
- assert_parses_to "(+ 2 \"two\")", [:+.node, 2, "two"]
+ assert_parses_to "(+ 2 \"two\")", [:+.sym, 2, "two"]
end
def test_parse_list_of_nested_sexprs
- assert_parses_to "(+ 2 (+ 2))", [:+.node, 2, [:+.node, 2]]
+ assert_parses_to "(+ 2 (+ 2))", [:+.sym, 2, [:+.sym, 2]]
end
def test_parse_list_of_deeply_nested_sexprs
- assert_parses_to "(+ 2 (+ 2 (+ 2 2)))", [:+.node, 2, [:+.node, 2, [:+.node, 2, 2]]]
+ assert_parses_to "(+ 2 (+ 2 (+ 2 2)))", [:+.sym, 2, [:+.sym, 2, [:+.sym, 2, 2]]]
end
def test_parse_two_consecutive_parens_simple
- assert_parses_to "(let ((foo 2)))", [:let.node, [[:foo.node, 2]]]
+ assert_parses_to "(let ((foo 2)))", [:let.sym, [[:foo.sym, 2]]]
end
def test_parse_two_consecutive_parens
- assert_parses_to "(let ((foo 2)) (+ foo 2))", [:let.node, [[:foo.node, 2]], [:+.node, :foo.node, 2]]
+ assert_parses_to "(let ((foo 2)) (+ foo 2))", [:let.sym, [[:foo.sym, 2]], [:+.sym, :foo.sym, 2]]
end
def test_whitespace_indifferent
@@ -70,9 +70,9 @@ def test_whitespace_indifferent
end
def test_parses_vectors
- assert_equal [:'(', :vector, 1, 2, :')'], BusScheme::tokenize("#(1 2)").flatten
- assert_parses_to "#(1 2)", [:vector, 1, 2]
- assert_parses_to "#(1 (2 3 4))", [:vector, 1, [2, 3, 4]]
+ assert_equal [:'(', :vector.sym, 1, 2, :')'], BusScheme::tokenize("#(1 2)").flatten
+ assert_parses_to "#(1 2)", [:vector.sym, 1, 2]
+ assert_parses_to "#(1 (2 3 4))", [:vector.sym, 1, [2, 3, 4]]
end
# def test_parses_dotted_cons
@@ -105,10 +105,10 @@ def test_negative_floats
# end
def test_quote
- assert_parses_to "'foo", [:quote, :foo.node]
- assert_equal [:'(', :quote, :'(', :foo.node, :bar.node, :baz.node, :')', :')'], BusScheme::tokenize("'(foo bar baz)").flatten
- assert_parses_to "'(foo bar baz)", [:quote, [:foo.node, :bar.node, :baz.node]]
- assert_parses_to "'(+ 20 3)", [:quote, [:+.node, 20, 3]]
+ assert_parses_to "'foo", [:quote.sym, :foo.sym]
+ assert_equal [:'(', :quote.sym, :'(', :foo.sym, :bar.sym, :baz.sym, :')', :')'], BusScheme::tokenize("'(foo bar baz)").flatten
+ assert_parses_to "'(foo bar baz)", [:quote.sym, [:foo.sym, :bar.sym, :baz.sym]]
+ assert_parses_to "'(+ 20 3)", [:quote.sym, [:+.sym, 20, 3]]
end
# have to change normalize_whitespace to not turn newlines into spaces for this to work
@@ -116,7 +116,7 @@ def test_ignore_comments
assert_parses_to ";; hello", nil
assert_parses_to "12 ;; comment", 12
assert_parses_to "(+ 2;; this is a mid-sexp comment
-2)", [:+.node, 2, 2]
+2)", [:+.sym, 2, 2]
end
def test_requires_closed_lists
@@ -145,11 +145,11 @@ def test_parse_random_elisp_form_from_my_dot_emacs
(if (file-exists-p system-specific-config)
(load system-specific-config)))"
assert_parses_to(lisp,
- [:let.node, [[:'system-specific-config'.node,
- [:concat.node, "~/.emacs.d/",
- [:'shell-command-to-string'.node, "hostname"]]]],
- [:if.node, [:'file-exists-p'.node, :'system-specific-config'.node],
- [:load.node, :'system-specific-config'.node]]])
+ [:let.sym, [[:'system-specific-config'.sym,
+ [:concat.sym, "~/.emacs.d/",
+ [:'shell-command-to-string'.sym, "hostname"]]]],
+ [:if.sym, [:'file-exists-p'.sym, :'system-specific-config'.sym],
+ [:load.sym, :'system-specific-config'.sym]]])
end
def test_parser_saves_file_info
View
1  test/test_primitives.rb
@@ -37,6 +37,7 @@ def test_message_passing
def test_load_file
eval "(load \"#{File.dirname(__FILE__)}/../examples/fib.scm\")"
+ assert Lambda.scope.has_key?(:fib.node)
assert_evals_to 5, "(fib 5)"
end
Please sign in to comment.
Something went wrong with that request. Please try again.