TAPL练习:添加TmPLet

问题描述

本练习是北大本研合上课程编程语言的设计原理的一次上机作业,完整的代码可以参考这里。该课程教材是经典的TAPL,作业要为书中fullsimple类型系统(源码可以在TAPL官网下载)添加一种新的term:TmPLet

通俗一点讲,我们要对这样的语句进行扩展:

let x = y in exp

目前的fullsimple系统只支持x和y为单个变量,我们要修改代码使其支持x和y为tuple(元组),使得我们的新类型系统可以对下面的语句进行求值:

/* Examples for testing */

let {x, y, z} = {true, 1, {2}} in z;
let {x, y, z} = {true, 1, {2}} in (lambda x:Nat. x) y;
let {x, y, z} = let x = 1 in {true, x, {2}} in z;
lambda x:Nat. let {x, y} = {true, 1} in x;
let x = 0 in let {y, z} = {1, 2} in x;
let {y, z} = {1, 2} in let y = 3 in y;

解决方案

练习时语法输出部分已经提前实现

语法

syntax.ml文件中增加TmPLet语法定义:

type term =
  ...
  | TmPLet of info * string list * term * term

let tminfo t = match t with
  ...
  | TmPLed(fi,_,_,_) -> fi

parser.mly中增加:

Term :
  ...
  | LET Pattern EQ Term IN Term
    { fun ctx -> TmPLet($1, $2, $4 ctx, $6 (List.fold_left (fun x y -> addname x y) ctx $2)) }
  Pattern :
    LCURLY MetaVars RCURLY 
    { $2 }
    | LCURLY RCURLY 
    { [] }

输出

syntax.ml中增加打印信息:

let rec printtm_Term outer ctx t = match t with
  ...
  | TmPLet(fi, xs, t1, t2) -> 
     obox0();
     pr "let {";
     let rec print xs = match xs with
       x::x'::rest -> pr x; pr ","; print (x'::rest);
       | x::[] -> pr x;
       | [] -> pr ""; in
       
     print xs;
     pr "} = ";
     printtm_Term false ctx t1;
     print_space(); pr "in"; print_space();
     let ctx' = List.fold_left (fun ctx x -> addname ctx x) ctx xs 
       in printtm_Term false ctx' t2;
     cbox()

求值

求值前我们需要弄清楚一个TmPLet项的结构,回顾一下定义:

TmPLet of info * string list * term * term

对应let x = y in exp的形式,x是一个string列表,exp是完成绑定后需要继续求值的表达式,关键是y,在测试用例中我们发现y是一个大括号包含了一堆东西,在parser.mly中我们可以发现它对应一个TyRecord类型的项:

AType :
  ...
  | LCURLY FieldTypes RCURLY
      { fun ctx ->
          TyRecord($2 ctx 1) }

对于一个TmPLet(fi, xs, t1, t2),求值的思路是:先对t1求值,顺利的话会得到一个TmRecord;接着我们每次从xst1中取第一个元素,展开成一个TmLet,一个例子如下:

   let {x, y} = {true, 1} in x
-> let x = true in let {y} = {1} in x
-> let x = true in let y  = 1 in x 

TmLet的求值是fullsimple已经实现好的,因此也就完成了对TmPLet的求值,具体代码就是在core.ml中增加如下内容:

let rec eval1 ctx t = match t with
  ...
  | TmPLet(fi, xs, t1, t2) when (isval ctx t1) ->
      (match (xs, t1) with
        ([x], TmRecord(fi_, [(l, v)])) -> 
           TmLet(fi, x, v, t2)
      | (x::rest1, TmRecord(fi_, (l, v)::rest2)) -> 
           TmLet(fi, x, v, TmPLet(fi, rest1, TmRecord(fi_, rest2), t2))
      | (_, _) -> raise NoRuleApplies)
  | TmPLet(fi, xs, t1, t2) ->
      let t1' = eval1 ctx t1 in TmPLet(fi, xs, t1', t2)

类型推导

core.ml中增加如下内容:

let rec typeof ctx t =
  | TmPLet(fi, xs, t1, t2) ->
      let tyT1 = typeof ctx t1 in
        let rec typePLet ctx xs' t1' =
            (match (xs', t1') with
                ([x], TyRecord([(l, t)])) -> 
                    let ctx' = addbinding ctx x (VarBind(t)) in         
                    typeShift (-1) (typeof ctx' t2)
            | (x::rest1, TyRecord((l, t)::rest2)) ->
                    let ctx' = addbinding ctx x (VarBind(t)) in
                    typeShift (-1) (typePLet ctx' rest1 (TyRecord(rest2)))
            | (_, _) -> error fi "error in TyPLet")
      in typePLet ctx xs tyT1

写完之后发现有些奇怪的报错,提示我们还要修改syntax.mltmmap函数,照猫画虎增加对TmPLet的处理就好了,在syntax.ml中增加:

let tmmap onvar ontype c t = 
  ...
  | TmPLet(fi, [x], TmRecord(fi_, [(l, t)]), t2) -> 
          walk c (TmLet(fi, x, t, t2))
  | TmPLet(fi, x::rest1, TmRecord(fi_, (l, t)::rest2), t2) ->
          walk c (TmLet(fi, x, t, TmPLet(fi, rest1, TmRecord(fi_, rest2), t2)))

测试

对于开篇的测试语句,运行结果如下:

屏幕快照 2017-03-28 上午10.25.31.png

对于语句let {x, y} = {true} in x,上述代码会输出我们在类型推导时设定的错误信息:error in TyPLet

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • 个人笔记,方便自己查阅使用 Py.LangSpec.Contents Refs Built-in Closure ...
    freenik阅读 67,641评论 0 5
  • 第一部分Common Lisp介绍第1章 介绍一下Lisp你在学的时候觉得已经明白了,写的时候更加确信了解了,教别...
    geoeee阅读 2,734评论 5 9
  • 第三章 EVAL标记法 3.1 导引 在进一步深入学习Lisp之前,我们必须切换到一个更加适合的标记法,EVAL标...
    geoeee阅读 2,268评论 0 5
  • 明湖天中波澜轻,邑人争渡彩龙惊。菖蒲飞香浸百脉,角黍献波黑虎灵。灵均遗韵今犹在,举杯雄黄话升平。遗叹死直汨罗上,年...
    蓝海跃阅读 262评论 5 5
  • 网络跟人的距离究竟有多远呢?在网络的世界里,我们习惯于虚拟一个角色,但是在文字里,真性情的人往往把文字当做一个感情...
    易水寒一x阅读 233评论 0 0