Created
September 17, 2013 13:20
-
-
Save risgk/6594208 to your computer and use it in GitHub Desktop.
TAPL 第10章より / 単純型のRuby別実装 (ブール値を伴った単純型付きラムダ計算のRuby別実装)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Modifying http://www.cis.upenn.edu/~bcpierce/tapl/checkers/simplebool/ for learning TAPL. | |
# TYPES AND PROGRAMMING LANGUAGES by Benjamin C. Pierce Copyright (c)2002 Benjamin C. Pierce | |
# $ gem install case_class | |
# ref: http://github.com/mame/case_class | |
require "case_class" | |
# CAUTION: Monkey patching! | |
module CaseClass | |
class Case < Struct | |
def ==(obj) | |
obj = obj.__getobj__ while PlaceHolder === obj | |
super | |
end | |
end | |
end | |
include CaseClass | |
# Datatypes | |
# ty | |
TyArr = Case[:ty1,:ty2] | |
TyBool_ = Case[:dummy] | |
TyBool = TyBool_[nil] | |
# term | |
TmVar = Case[:x,:n] | |
TmAbs = Case[:x,:tyT1,:t2] | |
TmApp = Case[:t1,:t2] | |
TmTrue_ = Case[:dummy] | |
TmTrue = TmTrue_[nil] | |
TmFalse_ = Case[:dummy] | |
TmFalse = TmFalse_[nil] | |
TmIf = Case[:t1,:t2,:t3] | |
# binding | |
NameBind_ = Case[:dummy] | |
NameBind = NameBind_[nil] | |
VarBind = Case[:ty] | |
# ------------------------ SYNTAX ------------------------ | |
# Context management | |
def addbinding(ctx,x,bind) | |
[[x,bind]] + ctx | |
end | |
# Shifting | |
def termShift(d,t) | |
def walkShift(c,t,d) | |
case t | |
when TmVar[x=_,n=_] | |
if x >= c | |
TmVar[x+d,n+d] | |
else | |
TmVar[x,n+d] | |
end | |
when TmAbs[x=_,tyT1=_,t2=_] | |
TmAbs[x,tyT1,walkShift(c+1,t2,d)] | |
when TmApp[t1=_,t2=_] | |
TmApp[walkShift(c,t1,d),walkShift(c,t2,d)] | |
when TmTrue | |
t | |
when TmFalse | |
t | |
when TmIf[t1=_,t2=_,t3=_] | |
TmIf[walkShift(c,t1,d),walkShift(c,t2,d),walkShift(c,t3,d)] | |
end | |
end | |
walkShift(0,t,d) | |
end | |
# Substitution | |
def termSubst(j,s,t) | |
def walkSubst(c,t,j,s) | |
case t | |
when TmVar[x=_,n=_] | |
if x == j+c | |
termShift(c,s) | |
else | |
TmVar[x,n] | |
end | |
when TmAbs[x=_,tyT1=_,t2=_] | |
TmAbs[x,tyT1,walkSubst(c+1,t2,j,s)] | |
when TmApp[t1=_,t2=_] | |
TmApp[walkSubst(c,t1,j,s),walkSubst(c,t2,j,s)] | |
when TmTrue | |
t | |
when TmFalse | |
t | |
when TmIf[t1=_,t2=_,t3=_] | |
TmIf[walkSubst(c,t1,j,s),walkSubst(c,t2,j,s),walkSubst(c,t3,j,s)] | |
end | |
end | |
walkSubst(0,t,j,s) | |
end | |
def termSubstTop(s,t) | |
termShift(-1,(termSubst(0,(termShift(1,s)),t))) | |
end | |
# Context management (continued) | |
def getbinding(ctx,i) | |
if i < ctx.length | |
ctx[i][1] | |
else | |
raise "Variable lookup failure" | |
end | |
end | |
def getTypeFromContext(ctx,i) | |
b = getbinding(ctx,i) | |
case b | |
when VarBind[tyT=_] | |
tyT | |
else | |
raise "Wrong kind of binding for variable" | |
end | |
end | |
# ------------------------ EVALUATION ------------------------ | |
def isval(ctx,t) | |
case t | |
when TmTrue | |
true | |
when TmFalse | |
true | |
when TmAbs[_,_,_] | |
true | |
else | |
false | |
end | |
end | |
class NoRuleApplies < Exception; end | |
def eval1(ctx,t) | |
case | |
when TmApp[TmAbs[_,_,t13=_],t2=_] === t && isval(ctx,t2) | |
termSubstTop(t2,t13) | |
when TmApp[t1=_,t2=_] === t && isval(ctx,t1) | |
t2p = eval1(ctx,t2) | |
TmApp[t1,t2p] | |
when TmApp[t1=_,t2=_] === t | |
t1p = eval1(ctx,t1) | |
TmApp[t1p,t2] | |
when TmIf[TmTrue,t2=_,_] === t | |
t2 | |
when TmIf[TmFalse,_,t3=_] === t | |
t3 | |
when TmIf[t1=_,t2=_,t3=_] === t | |
TmIf[eval1(ctx,t1),t2,t3] | |
else | |
raise NoRuleApplies | |
end | |
end | |
def eval(ctx,t) | |
begin | |
eval(ctx,eval1(ctx,t)) | |
rescue NoRuleApplies | |
t | |
end | |
end | |
# ------------------------ TYPING ------------------------ | |
def typeof(ctx,t) | |
case t | |
when TmVar[i=_,_] | |
getTypeFromContext(ctx,i) | |
when TmAbs[x=_,tyT1=_,t2=_] | |
ctxp = addbinding(ctx,x,VarBind[tyT1]) | |
tyT2 = typeof(ctxp,t2) | |
TyArr[tyT1,tyT2] | |
when TmApp[t1=_,t2=_] | |
tyT1 = typeof(ctx,t1) | |
tyT2 = typeof(ctx,t2) | |
case tyT1 | |
when TyArr[tyT11=_,tyT12=_] | |
if tyT2 === tyT11 | |
tyT12 | |
else | |
raise "parameter type mismatch" | |
end | |
else | |
raise "arrow type expected" | |
end | |
when TmTrue | |
TyBool | |
when TmFalse | |
TyBool | |
when TmIf[t1=_,t2=_,t3=_] | |
if typeof(ctx,t1) == TyBool | |
tyT2 = typeof(ctx,t2) | |
if tyT2 == typeof(ctx,t3) | |
tyT2 | |
else | |
raise "arms of conditional have different types" | |
end | |
else | |
raise "guard of conditional not a boolean" | |
end | |
end | |
end | |
# ------------------------ TEST ------------------------ | |
if ARGV[0] == "test" | |
printf "test1.1: %s\n",getbinding([[:x,VarBind[TyBool]]],0) == VarBind[TyBool] | |
printf "test1.2: %s\n",getTypeFromContext([[:x,VarBind[TyBool]]],0) == TyBool | |
# eval lambda x:Bool. x; | |
t = TmAbs["x",TyBool,TmVar[0,1]] | |
printf "test2.1: %s\n",typeof([],t) == TyArr[TyBool,TyBool] | |
printf "test2.2: %s\n",eval( [],t) == TmAbs["x",TyBool,TmVar[0,1]] | |
# eval (lambda x:Bool. if x then false else true) | |
t = TmAbs["x",TyBool,TmIf[TmVar[0,1],TmFalse,TmTrue]] | |
printf "test3.1: %s\n",typeof([],t) == TyArr[TyBool,TyBool] | |
printf "test3.2: %s\n",eval( [],t) == t | |
# eval (lambda x:Bool->Bool. if x false then true else false) | |
t = TmAbs["x",TyArr[TyBool,TyBool],TmIf[TmApp[TmVar[0,1],TmFalse],TmTrue,TmFalse]] | |
printf "test4.1: %s\n",typeof([],t) == TyArr[TyArr[TyBool,TyBool],TyBool] | |
printf "test4.2: %s\n",eval( [],t) == t | |
# eval (lambda x:Bool->Bool. if x false then true else false) (lambda x:Bool. if x then false else true) | |
t = TmApp[TmAbs["x",TyArr[TyBool,TyBool],TmIf[TmApp[TmVar[0,1],TmFalse],TmTrue,TmFalse]],TmAbs["x",TyBool,TmIf[TmVar[0,1],TmFalse,TmTrue]]] | |
printf "test5.1: %s\n",typeof([],t) == TyBool | |
printf "test5.2: %s\n",eval( [],t) == TmTrue | |
else | |
# eval term from stdin | |
puts eval([],Kernel.eval(gets())).to_s | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment