Skip to content

Commit

Permalink
#cond and other builtin procedures
Browse files Browse the repository at this point in the history
  • Loading branch information
Josep M. Bach committed Mar 8, 2011
1 parent e78daa5 commit 041ea29
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 56 deletions.
81 changes: 27 additions & 54 deletions lib/schemer/ast.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
module Schemer
module AST

class CharacterLiteral
class Node
def true?
true
end
def false?
!true?
end
end

class CharacterLiteral < Node
attr_reader :value

def initialize(character)
Expand All @@ -13,7 +22,7 @@ def inspect
end
end

class Identifier
class Identifier < Node
attr_reader :value

def initialize(identifier)
Expand All @@ -29,7 +38,7 @@ def inspect
end
end

class QuotedIdentifier
class QuotedIdentifier < Node
attr_reader :value

def initialize(identifier)
Expand All @@ -41,7 +50,7 @@ def inspect
end
end

class Expression
class Expression < Node
attr_reader :proc, :args

def initialize(expression)
Expand All @@ -56,7 +65,11 @@ def eval(context)
if arg.is_a?(Identifier)
arg = context.get_binding(arg.value) || arg
else
arg ||= arg.eval(context)
begin
arg = arg.eval(context)
rescue
arg
end
end
end if args

Expand All @@ -68,6 +81,12 @@ def eval(context)
end

block.call(*arguments)
rescue NoMethodError=>e
if e.message =~ /#{@proc.value}/
raise "#{@proc.value} is not a defined procedure."
else
raise e
end
end

def to_list
Expand All @@ -79,7 +98,7 @@ def inspect
end
end

class List
class List < Node
attr_reader :elements

def initialize(elements)
Expand All @@ -103,7 +122,7 @@ def inspect
end
end

class QuotedList
class QuotedList < Node
attr_reader :elements

def initialize(elements)
Expand All @@ -115,7 +134,7 @@ def inspect
end
end

class Vector
class Vector < Node
attr_reader :elements

def initialize(elements)
Expand All @@ -127,51 +146,5 @@ def inspect
end
end

class AddOperator
def inspect
"#<Operator::Add>"
end
end
class SubtractOperator
def inspect
"#<Operator::Subtract>"
end
end
class MultiplyOperator
def inspect
"#<Operator::Multiply>"
end
end
class DivideOperator
def inspect
"#<Operator::Divide>"
end
end
class GteOperator
def inspect
"#<Operator::GreaterThanOrEqual>"
end
end
class GtOperator
def inspect
"#<Operator::GreaterThan>"
end
end
class LteOperator
def inspect
"#<Operator::LowerThanOrEqual>"
end
end
class LtOperator
def inspect
"#<Operator::LowerThan>"
end
end
class EqualOperator
def inspect
"#<Operator::Equal>"
end
end

end
end
4 changes: 2 additions & 2 deletions lib/schemer/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ def initialize(parent = nil)
end

def add_binding(name, value)
@bindings.update({ name => value })
@bindings.update({ name.to_s => value })
end

def get_binding(name)
if got = @bindings[name]
if got = @bindings[name.to_s]
return got
elsif ! @parent.nil?
@parent.get_binding(name)
Expand Down
29 changes: 29 additions & 0 deletions lib/schemer/interpreter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,35 @@ def initialize(ast = nil)
list.to_list.elements.last
end)

env.add_binding(:list, lambda do |*args|
AST::List.new(args)
end)

env.add_binding(:null?, lambda do |object|
(object.respond_to?(:empty?) && object.empty?) || object.nil?
end)

env.add_binding("=", lambda do |one, another|
one == another
end)

env.add_binding(">", lambda do |one, another|
one > another
end)

env.add_binding("<", lambda do |one, another|
one < another
end)

env.add_binding(:cond, lambda do |*conditions|
conditions.map!(&:to_list).map!(&:to_a)
conditions.each do |condition, result|
return result.eval(env) if condition.eval(env)
end
nil
end)


end
end

Expand Down
96 changes: 96 additions & 0 deletions spec/schemer/interpreter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,102 @@ module Schemer
end
end

describe "#list" do
it 'converts elements to a list' do
expression = "(list 3 4 x y)"
lexer = Schemer::Lexer.new
parser = Schemer::Parser.new
ast = parser.apply(lexer.parse expression)

interpreter = Schemer::Interpreter.new(ast)
output = interpreter.walk
output.should be_a(AST::List)
output.should have(4).elements
end
end

describe "#null?" do
context 'if the object is nil' do
it 'returns true' do
expression = "(null? 3)"
lexer = Schemer::Lexer.new
parser = Schemer::Parser.new
ast = parser.apply(lexer.parse expression)

interpreter = Schemer::Interpreter.new(ast)
interpreter.walk.should be_false
end
end
context 'if the object is empty or nil' do
it 'returns true' do
expression = "(null? ())"
lexer = Schemer::Lexer.new
parser = Schemer::Parser.new
ast = parser.apply(lexer.parse expression)

interpreter = Schemer::Interpreter.new(ast)
interpreter.walk.should be_true
end
end
end

describe "#=" do
it 'returns the equality of two arguments' do
expression = "(= 3 (+ 1 2))"
lexer = Schemer::Lexer.new
parser = Schemer::Parser.new
ast = parser.apply(lexer.parse expression)

interpreter = Schemer::Interpreter.new(ast)
interpreter.walk.should be_true
end
end

describe "#<" do
it 'returns true if foo is less than bar' do
expression = "(< 3 (+ 2 2))"
lexer = Schemer::Lexer.new
parser = Schemer::Parser.new
ast = parser.apply(lexer.parse expression)

interpreter = Schemer::Interpreter.new(ast)
interpreter.walk.should be_true
end
end

describe "#>" do
it 'returns true if foo is more than bar' do
expression = "(> 3 (+ 2 2))"
lexer = Schemer::Lexer.new
parser = Schemer::Parser.new
ast = parser.apply(lexer.parse expression)

interpreter = Schemer::Interpreter.new(ast)
interpreter.walk.should be_false
end
end

describe "#cond" do
it 'returns the first condition that evaluates to true' do
expression = "(cond ((< 3 1) 9) ((>3 1) 7) )"
lexer = Schemer::Lexer.new
parser = Schemer::Parser.new
ast = parser.apply(lexer.parse expression)

interpreter = Schemer::Interpreter.new(ast)
interpreter.walk.should == 7
end
it 'even if the result is an expression' do
expression = "(cond ((< 3 1) 9) ((>3 8) 7) ((= 3 3) (+ 3 9)) )"
lexer = Schemer::Lexer.new
parser = Schemer::Parser.new
ast = parser.apply(lexer.parse expression)

interpreter = Schemer::Interpreter.new(ast)
interpreter.walk.should == 12
end
end

end

end
Expand Down

0 comments on commit 041ea29

Please sign in to comment.