Download code - Download code Ruby
Back to Lambda_Calculus_Interpreter_(Ruby)
Download for Windows: single file, zip
Download for UNIX: single file, zip, tar.gz, tar.bz2
lambda.rb
1 # Copyright (c) 2012 the authors listed at the following URL, and/or 2 # the authors of referenced articles or incorporated external code: 3 # http://en.literateprograms.org/Lambda_Calculus_Interpreter_(Ruby)?action=history&offset=20070924114443 4 # 5 # Permission is hereby granted, free of charge, to any person obtaining 6 # a copy of this software and associated documentation files (the 7 # "Software"), to deal in the Software without restriction, including 8 # without limitation the rights to use, copy, modify, merge, publish, 9 # distribute, sublicense, and/or sell copies of the Software, and to 10 # permit persons to whom the Software is furnished to do so, subject to 11 # the following conditions: 12 # 13 # The above copyright notice and this permission notice shall be 14 # included in all copies or substantial portions of the Software. 15 # 16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 # 24 # Retrieved from: http://en.literateprograms.org/Lambda_Calculus_Interpreter_(Ruby)?oldid=10974 25 26 class Var 27 def initialize(name) 28 @name = name 29 end 30 31 def eval(env=Hash.new) 32 # lookup the value of the variable in the environment 33 env[@name] 34 end 35 36 def to_s 37 @name 38 end 39 end 40 41 class BinaryOp 42 def initialize(op, t1, t2) 43 @op, @t1, @t2 = op, t1, t2 44 end 45 46 def eval(env=Hash.new) 47 case @op 48 when "+" 49 @t1.eval(env) + @t2.eval(env) 50 when "*" 51 @t1.eval(env) * @t2.eval(env) 52 when "and" 53 @t1.eval(env) and @t2.eval(env) 54 when "or" 55 @t1.eval(env) or @t2.eval(env) 56 else 57 raise "Unknown binary operation" 58 end 59 end 60 61 def to_s 62 "#{@t1} #{@op} #{@t2}" 63 end 64 end 65 66 class UnaryOp 67 def initialize(op, t) 68 @op, @t = op, t 69 end 70 71 def eval(env=Hash.new) 72 case @op 73 when "-" 74 -(@t.eval(env)) 75 when "not" 76 !(@t.eval(env)) 77 else 78 raise "Unknown unary operation" 79 end 80 end 81 82 def to_s 83 "#{@op} #{@t}" 84 end 85 end 86 87 class Let 88 def initialize(var, t1, t2) 89 @var, @t1, @t2 = var, t1, t2 90 end 91 92 def eval(env=Hash.new) 93 env[@var] = @t1.eval(env) 94 @t2.eval(env) 95 end 96 97 def to_s 98 "let (#{@var} = #{@t1}) in (#{@t2})" 99 end 100 end 101 102 class Lambda 103 def initialize(var, term) 104 @var, @term = var, term 105 end 106 107 def eval(env=Hash.new) 108 var, term = @var, @term 109 # returns a procedure that will perform 110 # the body of the lambda function 111 112 proc = Proc.new { |t| 113 env[var] = t # substitution into body of function 114 term.eval(env) 115 } 116 117 # add a to_s method to the newly created Proc 118 def proc.to_s 119 "lambda" 120 end 121 122 proc 123 end 124 125 def to_s 126 "\\#{@var}.#{@term}" 127 end 128 end 129 130 class Apply 131 def initialize(t1, t2) 132 @t1, @t2 = t1, t2 133 end 134 135 def eval(env=Hash.new) 136 # the first term must evaluate to a ruby Proc 137 @t1.eval(env).call(@t2.eval(env)) 138 end 139 140 def to_s 141 "(#{@t1}) #{@t2}" 142 end 143 end 144 145 class If 146 def initialize(t1, t2, t3) 147 @t1, @t2, @t3 = t1, t2, t3 148 end 149 150 def eval(env=Hash.new) 151 if @t1.eval(env) 152 @t2.eval(env) 153 else 154 @t3.eval(env) 155 end 156 end 157 158 def to_s 159 "if (#{@t1}) then #{@t2} else #{@t3}" 160 end 161 end 162 163 module Eval 164 def eval(env=Nil) 165 self 166 end 167 end 168 169 class Fixnum 170 include Eval 171 end 172 173 class Bignum 174 include Eval 175 end 176 177 class TrueClass 178 include Eval 179 end 180 181 class FalseClass 182 include Eval 183 end 184 185 term1 = BinaryOp.new("*", 99, 100) 186 187 # the else clause contains a type error but won't be executed 188 term2 = If.new(BinaryOp.new("or", true, false), -10, UnaryOp.new("not", 99)) 189 190 term3 = Let.new("x", 25, BinaryOp.new("+", Var.new("x"), 50)) 191 192 double = Lambda.new("n", BinaryOp.new("+", Var.new("n"), Var.new("n"))) 193 term4 = Apply.new(double, 12) 194 195 # \x.\y. x + y 196 add = Lambda.new("x", Lambda.new("y", BinaryOp.new("+", Var.new("x"), Var.new("y")))) 197 198 term5 = Let.new("a", add, Apply.new(Apply.new(Var.new("a"), 10), 9)) 199 200 badterm = BinaryOp.new("+", 50, false) 201 wierdterm = BinaryOp.new("+", Object.new, Object.new) 202