Module: BusScheme
- Defined in:
- lib/eval.rb,
lib/lambda.rb,
lib/parser.rb,
lib/bus_scheme.rb,
lib/definitions.rb
Defined Under Namespace
Classes: ArgumentError, BusSchemeError, EvalError, Lambda, ParseError, Scope
Constant Summary collapse
- VERSION =
"0.7.1"
- SYMBOL_TABLE =
{}.merge(PRIMITIVES).merge(SPECIAL_FORMS)
- PROMPT =
'> '
- PRIMITIVES =
{ '#t'.intern => true, # :'#t' screws up emacs' ruby parser '#f'.intern => false, :+ => lambda { |*args| args.inject(0) { |sum, i| sum + i } }, :- => lambda { |x, y| x - y }, :* => lambda { |*args| args.inject(1) { |product, i| product * i } }, '/'.intern => lambda { |x, y| x / y }, :> => lambda { |x, y| x > y }, :< => lambda { |x, y| x < y }, :'=' => lambda { |x, y| x == y }, # may not honor scheme's equality notions :null? => lambda { |x| x.nil? }, :intern => lambda { |x| x.intern }, :substring => lambda { |x, from, to| x[from .. to] }, :car => lambda { |list| list.car }, :cdr => lambda { |list| list.cdr }, :ruby => lambda { |code| eval(code) }, :load => lambda { |filename| eval_string(File.read(filename)) }, :exit => lambda { exit }, :quit => lambda { exit }, }
- SPECIAL_FORMS =
if we add in macros, can some of these be defined in scheme?
{ :quote => lambda { |arg| arg }, # TODO: check that nil, () and #f all behave according to spec :if => lambda { |q, yes, *no| eval_form(q) ? eval_form(yes) : eval_form([:begin] + no) }, :begin => lambda { |*args| args.map{ |arg| eval_form(arg) }.last }, :set! => lambda { |sym, value| BusScheme[sym] and BusScheme[sym] = eval_form(value); sym }, :lambda => lambda { |args, *form| Lambda.new(args, form) }, :define => lambda { |sym, definition| BusScheme[sym] = eval_form(definition); sym }, }
Class Method Summary collapse
-
.[](symbol) ⇒ Object
symbol lookup.
-
.[]=(symbol, value) ⇒ Object
symbol assignment to value.
-
.apply(function, *args) ⇒ Object
Call a function with given args.
-
.eval_form(form) ⇒ Object
Eval a form passed in as an array.
-
.eval_string(string) ⇒ Object
Parse a string, then eval the result.
-
.normalize_whitespace(string) ⇒ Object
Treat all whitespace in a string as spaces.
-
.parse(input) ⇒ Object
Turn an input string into an S-expression.
-
.parse_list(tokens) ⇒ Object
Nest a list from a 1-dimensional list of tokens.
-
.parse_tokens(tokens) ⇒ Object
Turn a list of tokens into a properly-nested S-expression.
-
.pop_token(input) ⇒ Object
Take a token off the input string and return it.
-
.repl ⇒ Object
Read-Eval-Print-Loop.
-
.scope_of(symbol) ⇒ Object
what scope is appropraite for this symbol.
-
.special_form?(symbol) ⇒ Boolean
symbol special form predicate.
-
.tokenize(input) ⇒ Object
Split an input string into lexically valid tokens.
Class Method Details
.[](symbol) ⇒ Object
symbol lookup
29 30 31 32 33 |
# File 'lib/bus_scheme.rb', line 29 def self.[](symbol) scope = scope_of(symbol) raise EvalError.new("Undefined symbol: #{symbol}") unless scope scope && scope[symbol] end |
.[]=(symbol, value) ⇒ Object
symbol assignment to value
36 37 38 |
# File 'lib/bus_scheme.rb', line 36 def self.[]=(symbol, value) (scope_of(symbol) || Lambda.scope || SYMBOL_TABLE)[symbol] = value end |
.apply(function, *args) ⇒ Object
Call a function with given args
22 23 24 25 |
# File 'lib/eval.rb', line 22 def apply(function, *args) args.map!{ |arg| eval_form(arg) } unless special_form?(function) eval_form(function).call(*args) end |
.eval_form(form) ⇒ Object
Eval a form passed in as an array
9 10 11 12 13 14 15 16 17 18 19 |
# File 'lib/eval.rb', line 9 def eval_form(form) if form == [] nil elsif form.is_a? Array apply(form.first, *form.rest) elsif form.is_a? Symbol BusScheme[form] else # well it must be a literal then form end end |
.eval_string(string) ⇒ Object
Parse a string, then eval the result
4 5 6 |
# File 'lib/eval.rb', line 4 def eval_string(string) eval_form(parse(string)) end |
.normalize_whitespace(string) ⇒ Object
Treat all whitespace in a string as spaces
65 66 67 |
# File 'lib/parser.rb', line 65 def normalize_whitespace(string) string && string.gsub(/\t/, ' ').gsub(/\n/, ' ').gsub(/ +/, ' ') end |
.parse(input) ⇒ Object
Turn an input string into an S-expression
4 5 6 |
# File 'lib/parser.rb', line 4 def parse(input) parse_tokens tokenize(normalize_whitespace(input)) end |
.parse_list(tokens) ⇒ Object
Nest a list from a 1-dimensional list of tokens
20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/parser.rb', line 20 def parse_list(tokens) [].affect do |list| while element = tokens.shift and element != :')' if element == :'(' list << parse_list(tokens) else list << element end end end end |
.parse_tokens(tokens) ⇒ Object
Turn a list of tokens into a properly-nested S-expression
9 10 11 12 13 14 15 16 17 |
# File 'lib/parser.rb', line 9 def parse_tokens(tokens) token = tokens.shift if token == :'(' parse_list(tokens) else raise BusScheme::ParseError unless tokens.empty? token # atom end end |
.pop_token(input) ⇒ Object
Take a token off the input string and return it
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/parser.rb', line 42 def pop_token(input) token = case input when /^ +/ # whitespace input[0 ... 1] = '' return pop_token(input) when /^\(/ # open paren :'(' when /^\)/ # closing paren :')' when /^(\d+)/ # positive integer Regexp.last_match[1].to_i when /^"(.*?)"/ # string Regexp.last_match[1] when /^([^ \)]+)/ # symbol Regexp.last_match[1].intern end # compensate for quotation marks length = token.is_a?(String) ? token.length + 2 : token.to_s.length input[0 .. length - 1] = '' return token end |
.repl ⇒ Object
Read-Eval-Print-Loop
46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/bus_scheme.rb', line 46 def self.repl loop do puts begin BusScheme.eval_string(Readline.readline(PROMPT)) rescue Interrupt 'Type "(quit)" to leave Bus Scheme.' rescue BusSchemeError => e "Error: #{e}" rescue StandardError => e "You found a bug in Bus Scheme!\n" + "#{e.class}: #{e}\n#{e.backtrace.join("\n")}" end end end |
.scope_of(symbol) ⇒ Object
what scope is appropraite for this symbol
24 25 26 |
# File 'lib/bus_scheme.rb', line 24 def self.scope_of(symbol) [Lambda.scope, SYMBOL_TABLE].compact.detect { |scope| scope.has_key?(symbol) } end |
.special_form?(symbol) ⇒ Boolean
symbol special form predicate
41 42 43 |
# File 'lib/bus_scheme.rb', line 41 def self.special_form?(symbol) SPECIAL_FORMS.has_key?(symbol) end |
.tokenize(input) ⇒ Object
Split an input string into lexically valid tokens
33 34 35 36 37 38 39 |
# File 'lib/parser.rb', line 33 def tokenize(input) [].affect do |tokens| while token = pop_token(input) tokens << token end end end |