Skip to content

Commit

Permalink
Merge branch 'function'
Browse files Browse the repository at this point in the history
* function:
  Added anonymous function without parameters
  Renamed let to define and added Function class to handle functions
  Commiting last working version
  • Loading branch information
pbalduino committed Jul 23, 2011
2 parents cea7c45 + 1e62415 commit 3d3b62c
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ rdoc
# yard generated
doc
.yardoc
yard

# bundler
.bundle
Expand Down
1 change: 1 addition & 0 deletions lib/lucio.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'sparse'
require 'lucio/lexicon'
require 'lucio/function'
require 'lucio/lucio'
12 changes: 12 additions & 0 deletions lib/lucio/function.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Function
def initialize(code, lexicon, lucio)
@code = code
@lucio = lucio
end

def call(global_lexicon, list)
local_lexicon = Lexicon.new
tree = Lucio.behead(Lucio.behead(@code[0])[1])[1]
@lucio.evaluate_tree(tree[0], local_lexicon)
end
end
6 changes: 6 additions & 0 deletions lib/lucio/lexicon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,10 @@ def add_macro(token, code)
def get(operator)
@operator_list[operator]
end

def operator_list
list = []
@operator_list.each{|item| list << item[0] }
list
end
end
91 changes: 44 additions & 47 deletions lib/lucio/lucio.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
class Array
def to_sym
to_s.to_sym
end
end

class Lucio
def initialize(lexicon = Lexicon.new)
@lexicon = lexicon
@lexicon.add_function :+ , lambda{|items| items.reduce(0, :+)}
@lexicon.add_function :* , lambda{|items| items.reduce(1, :*)}
@lexicon.add_function :- , lambda{|items| case when items.size == 0; 0; else; head, tail = Lucio.behead(items); tail.empty? ? (head * -1) : tail.inject(head) {|subtraction, item| subtraction -= item}; end}
@lexicon.add_function :/ , lambda{|items| case when items.size == 0; raise 'expects at least 1 arguments, given 0'; when items.size == 1; 1.0 / items[0]; else items.reduce{|result, item| result = result / item.to_f}; end}
@lexicon.add_function :true , lambda{ true }
@lexicon.add_function :false, lambda{ false }
@lexicon.add_function :eql? , lambda{|items| items.map{|item| item == items[0]}.reduce{|memo, item| memo && item}}
@lexicon.add_function :lt , lambda{|items| items[0] < items[1] }

@lexicon.add_macro :let , lambda{|lexicon, items| lexicon.add_function(items[0].to_sym, lambda{|it| (items[1].kind_of? Array) ? evaluate_tree(it.empty? ? items[1] : items[1] + [it]) : items[1]})}
@lexicon.add_macro :if , lambda{|lexicon, items| evaluate_tree(items[0]) ? evaluate_tree(items[1]) : evaluate_tree(items[2]) }
@lexicon.add_macro :fn , lambda{|lexicon, items| create_function(items) }
@lexicon.add_function :+ , lambda{|lexicon, items| items.reduce(0, :+)}
@lexicon.add_function :* , lambda{|lexicon, items| items.reduce(1, :*)}
@lexicon.add_function :- , lambda{|lexicon, items| case when items.size == 0; 0; else; head, tail = Lucio.behead(items); tail.empty? ? (head * -1) : tail.inject(head) {|subtraction, item| subtraction -= item}; end}
@lexicon.add_function :/ , lambda{|lexicon, items| case when items.size == 0; raise 'expects at least 1 arguments, given 0'; when items.size == 1; 1.0 / items[0]; else items.reduce{|result, item| result = result / item.to_f}; end}
@lexicon.add_function :true , lambda{|lexicon, items| true }
@lexicon.add_function :false , lambda{|lexicon, items| false }
@lexicon.add_function :eql? , lambda{|lexicon, items| items.map{|item| item == items[0]}.reduce{|memo, item| memo && item}}
@lexicon.add_function :lt , lambda{|lexicon, items| items[0] < items[1]}

@lexicon.add_macro :define , lambda{|lexicon, items| define lexicon, items }
@lexicon.add_macro :if , lambda{|lexicon, items| evaluate_tree(items[0]) ? evaluate_tree(items[1]) : evaluate_tree(items[2]) }
@lexicon.add_macro :fun , lambda{|lexicon, items| lexicon.add_function items.to_sym, Function.new(items, lexicon, self) }
end

def eval(source_code)
Expand All @@ -22,42 +28,41 @@ def eval(source_code)
ret
end

private
def evaluate_tree(tree)
def self.behead(list)
[list[0], list.drop(1)]
end

def evaluate_tree(tree, local_lexicon = Lexicon.new)
unless tree.empty?
operator, list = Lucio.behead tree

if operator.kind_of?(Symbol) || operator.kind_of?(Array)
instruction = @lexicon.get operator
operator = evaluate_tree(operator) if operator.kind_of?(Array)

instruction = (operator.kind_of? Hash) ? operator : @lexicon.get(operator)

if instruction
if instruction[:type] == :function
list.map! {|item| (item.kind_of? Array) ? item = evaluate_tree(item) : item}
list.map! do |item|
list.map! do |item|
if item.kind_of? Symbol
op = @lexicon.get item
if op.nil?
raise UnboundSymbolException.new("Unbound symbol #{item.to_s}")
end
item = op[:code].call []
unbound item if op.nil?
item = op[:code].call @lexicon, []
else
item
end
end

begin
instruction[:code].call list
rescue ArgumentError
instruction[:code].call
end
instruction[:code].call @lexicon, list

elsif instruction[:type] == :macro
instruction[:code].call @lexicon, list

end

else
raise UnboundSymbolException.new "Unbound symbol #{operator.to_s}"
unbound operator

end
else
Expand All @@ -67,32 +72,24 @@ def evaluate_tree(tree)
end
end

def self.behead(list)
[list[0], list.drop(1)]
end

def create_function(items)
items[1] = replace_parameters(items[1], items[0], items[2])
evaluate_tree(items[1])
end

def replace_parameters(list, variables, values)
unless variables.empty?
(0 .. (variables.size - 1)).each do |i|
list = list.map do |item|
if item.kind_of? Array
item = replace_parameters(item, variables, values)
else
item == variables[i] ? values[i] : item
end
end
private
def define(lexicon, items)
lmb = lambda do |lexicon, it|
if items[1].kind_of? Array
evaluate_tree(items[1])
elsif items[1].kind_of? Function
items[1].call lexicon, it
else
items[1]
end
end

list

lexicon.add_function(items[0].to_sym, lmb)
end

def unbound symbol
raise UnboundSymbolException.new("Unbound symbol: #{symbol.to_s}")
end
end

class UnboundSymbolException < Exception
Expand Down
36 changes: 34 additions & 2 deletions spec/anonymous_function_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@
@lucio = Lucio.new
end

it 'declaration without parameters' do
code = '(fun ([] (42)))'
lambda {@lucio.eval(code) }.should_not raise_error
end

it 'execute declaration without parameters' do
code = '((fun ([] (+ 1 12))))'
@lucio.eval(code).should == 13
end

=begin
it 'without parameters' do
code = '
(let boo
Expand Down Expand Up @@ -66,6 +77,23 @@
(factorial 5)'
@lucio.eval(code).should == 120
end
=begin
it 'currying' do
code = '
(let adder
(fn (x)
(fn (y)
(+ x y))))
(let add5
(fn(x)
(adder 5)))
(add5 10)'
p @lucio.eval code
end
=end
=begin
it 'with a named function as parameter' do
code = "
Expand All @@ -77,9 +105,13 @@
(fn (x y)
(+ x y)))
(operate add 2 3)"
(let add5
(fn (x)
(add x 5)))
(operate add5 2)"
p @lucio.eval code
p 'result', @lucio.eval(code)
end
=end
end
Expand Down
12 changes: 6 additions & 6 deletions spec/let_spec.rb → spec/define_spec.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
require 'spec_helper'

describe Lucio do
context 'let' do
before :each do
context 'define' do
before :each do
@lucio = Lucio.new
end

it 'should define x' do
code = '
(let x 10)
(define x 10)
(eql? x 10)'
@lucio.eval(code).should be_true
end

it 'should define x as a arithmetic operation' do
code = '
(let x (* 10 2))
(eql? x (* 10 2))'
(define x (* 10 2))
(eql? x 20)'
@lucio.eval(code).should be_true
end

it 'should define x as a comparison result' do
code = '(eql? (* 10 2) 20)'
code = '(define result (eql? (* 10 2) 20)) (result)'
@lucio.eval(code).should be_true
end

Expand Down

0 comments on commit 3d3b62c

Please sign in to comment.