Skip to content
This repository has been archived by the owner on Jan 23, 2018. It is now read-only.

Commit

Permalink
nicer funcall syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
technomancy committed Feb 8, 2008
1 parent 2c167ae commit 1db89ad
Show file tree
Hide file tree
Showing 13 changed files with 128 additions and 24 deletions.
2 changes: 1 addition & 1 deletion COPYING
Original file line number Original file line Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2007, Phil Hagelberg Copyright (c) 2007 - 2008, Phil Hagelberg
All rights reserved. All rights reserved.


Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
Expand Down
10 changes: 10 additions & 0 deletions lib/array_extensions.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -15,7 +15,17 @@ def to_list
end end
end end


def %(n)
all = []
self.each_with_index do |x, i|
(all[i / n] ||= []) << x
end
return all
end
alias_method :sexp, :to_list alias_method :sexp, :to_list

# allows for (mylist 4) => mylist[4]
alias_method :call, :[]
end end


module Enumerable # for 1.9, zip is defined on Enumerable module Enumerable # for 1.9, zip is defined on Enumerable
Expand Down
6 changes: 4 additions & 2 deletions lib/bus_scheme.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
$LOAD_PATH << File.dirname(__FILE__) $LOAD_PATH << File.dirname(__FILE__)
require 'object_extensions' require 'object_extensions'
require 'array_extensions' require 'array_extensions'
require 'hash_extensions'
require 'parser' require 'parser'
require 'eval' require 'eval'
require 'primitives' require 'primitives'
Expand All @@ -22,8 +23,9 @@ module BusScheme
PROMPT = '> ' PROMPT = '> '


# symbol special form predicate # symbol special form predicate
def self.special_form?(symbol) def self.special_form?(form)
SPECIAL_FORMS.has_key?(symbol) form.is_a? Symbol and
SPECIAL_FORMS.has_key?(form)
end end


# Read-Eval-Print-Loop # Read-Eval-Print-Loop
Expand Down
5 changes: 5 additions & 0 deletions lib/cons.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ def inspect(open = '(', close = ')')
str + ' . ' + @cdr.inspect + close str + ' . ' + @cdr.inspect + close
end end
end end

# allows for (mylist 4) => (nth mylist 4)
def call(nth)
nth == 0 ? @car : @cdr.call(nth - 1)
end
end end


def cons(car, cdr = nil) def cons(car, cdr = nil)
Expand Down
17 changes: 17 additions & 0 deletions lib/hash_extensions.rb
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,17 @@
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)
end

def [](key)
if old_has_key(key)
old_lookup(key)
else
old_lookup(key.to_sym)
end
end
end
13 changes: 7 additions & 6 deletions lib/lambda.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class Lambda
@@stack = [] @@stack = []


attr_reader :scope attr_reader :scope
attr_accessor :defined_in attr_accessor :file, :line


# create new Lambda object # create new Lambda object
def initialize(formals, body) def initialize(formals, body)
Expand All @@ -61,14 +61,15 @@ def self.scope
end end


# shorthand for lookup in the currently relevant scope # shorthand for lookup in the currently relevant scope
def self.[](sym) def self.[](symbol)
self.scope[sym] self.scope[symbol]
end end


# shorthand for assignment in the currently relevant scope # shorthand for assignment in the currently relevant scope
def self.[]=(sym, val) def self.[]=(symbol, val)
val.defined_in = sym.defined_in if val.respond_to?(:defined_in) val.file = symbol.file if val.respond_to?(:file)
self.scope[sym] = val val.line = symbol.line if val.respond_to?(:line)
self.scope[symbol] = val
end end
end end
end end
42 changes: 41 additions & 1 deletion lib/object_extensions.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -11,6 +11,46 @@ def sexp
end end
end end


class String
def node
Node.new(self)
end
end

class Symbol class Symbol
attr_accessor :defined_in def node
Node.new(self.to_s)
end

def file
"(primitive)"
end

def line
nil
end

def file=(*args)
end

def line=(*args)
end
end

class Proc
def file
"(primitive)"
end

def line
nil
end
end

class Node < String
attr_accessor :file, :line

def eql?(other)
self.intern.eql?(other)
end
end end
4 changes: 2 additions & 2 deletions lib/parser.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -74,10 +74,10 @@ def pop_token(input)
when /\A("(.*?)")/ # string when /\A("(.*?)")/ # string
Regexp.last_match[2] Regexp.last_match[2]
when /\A(#{IDENTIFIER_BEGIN}+#{IDENTIFIER_CHARS}*)/ # symbol when /\A(#{IDENTIFIER_BEGIN}+#{IDENTIFIER_CHARS}*)/ # symbol
puts "#{Regexp.last_match[1]} - #{@@lines}" # puts "#{Regexp.last_match[1]} - #{@@lines}"
# bugger--this is *not* going to work. every reference to the symbol # 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! # resets the defined_in properties. ick! i don't want a ParseNode class!
Regexp.last_match[1].intern.affect{ |sym| sym.defined_in = [BusScheme.loaded_files.last, @@lines] } Regexp.last_match[1].intern.affect{ |sym| sym.file, sym.line = [BusScheme.loaded_files.last, @@lines] }
end end


# Remove the matched part from the string # Remove the matched part from the string
Expand Down
1 change: 1 addition & 0 deletions lib/primitives.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -45,5 +45,6 @@ class AssertionFailed < BusSchemeError; end
:and => lambda { |*args| args.all? { |x| eval(x) } }, :and => lambda { |*args| args.all? { |x| eval(x) } },
:or => lambda { |*args| args.any? { |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 }) }, :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
} }
end end
12 changes: 12 additions & 0 deletions test/test_eval.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -64,4 +64,16 @@ def test_define_after_load
(define greeting \"hi\")" (define greeting \"hi\")"
assert Lambda[:greeting] assert Lambda[:greeting]
end end

def test_funcall_list_means_nth
assert_evals_to 3, "((list 1 2 3) 2)"
end

def test_funcall_vector_means_nth
assert_evals_to 3, "((vector 1 2 3) 2)"
end

def test_funcall_hash_means_lookup
assert_evals_to 3, "((hash (1 1) (2 2) (3 3)) 3)"
end
end end
29 changes: 17 additions & 12 deletions test/test_lambda.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -73,20 +73,25 @@ def test_lambda_rest_args
end end


def test_lambdas_know_what_file_they_were_defined_in def test_lambdas_know_what_file_they_were_defined_in
filename = File.expand_path("#{File.dirname(__FILE__)}/../examples/fib.scm") assert_equal "(primitive)", Lambda[:if].file
eval "(load \"#{filename}\")"
assert_equal filename, Lambda[:fib].defined_in.first eval "(define fab (lambda () \"warble\"))"

assert_equal "(eval)", Lambda[:fab.node].file
eval "(define fab 'warble)"
assert_equal "(eval)", Lambda[:fab].defined_in.first
end

def test_lambdas_know_what_line_they_were_defined_in
eval "#{"\n" * 7} (define fab 'warble)"
assert_equal 7, Lambda[:fab].defined_in.last


filename = File.expand_path("#{File.dirname(__FILE__)}/../examples/fib.scm") filename = File.expand_path("#{File.dirname(__FILE__)}/../examples/fib.scm")
eval "(load \"#{filename}\")" eval "(load \"#{filename}\")"
assert_equal 1, Lambda.scope[:fib].defined_in.last assert_equal filename, Lambda[:fib.node].file
end end

# def test_lambdas_know_what_line_they_were_defined_in
# assert_equal nil, Lambda[:if].line

# 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

# eval "#{"\n" * 7} (define fab 'warble)"
# assert_equal 7, Lambda[:fab.node].line
# end
end end
5 changes: 5 additions & 0 deletions test/test_parser.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ def test_parse_random_elisp_form_from_my_dot_emacs
[:load, :'system-specific-config']]]) [:load, :'system-specific-config']]])
end end


def test_parser_saves_file_info
tree = BusScheme.parse("(define foo 23)")
assert_equal "(eval)", tree.cdr.car.file
end

# def test_accepts_multiline_strings_in_repl # def test_accepts_multiline_strings_in_repl
# # oh crap # # oh crap
# end # end
Expand Down
6 changes: 6 additions & 0 deletions test/test_primitives.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -143,4 +143,10 @@ def test_boolean_short_circuit
assert_evals_to false, "(and #f (assert #f))" assert_evals_to false, "(and #f (assert #f))"
assert_evals_to true, "(or #t (assert #f))" assert_evals_to true, "(or #t (assert #f))"
end end

def test_array_modulo
assert_equal [[1, 2], [3, 4], [5]], (1 .. 5).to_a % 2
assert_equal [[1, 2], [3, 4], [5, 6]], (1 .. 6).to_a % 2
assert_equal [[1, 2, 3], [4, 5, 6]], (1 .. 6).to_a % 3
end
end end

0 comments on commit 1db89ad

Please sign in to comment.