??xml version="1.0" encoding="utf-8" standalone="yes"?>久久精品国产亚洲一区二区三区 ,国产午夜福利精品久久,亚洲伊人久久综合影院http://www.shnenglu.com/vczh/category/20808.html【QQQ?43056143】【EmailQvczh@163.com】【新微博:http://weibo.com/vczh?/description>zh-cnTue, 11 Feb 2014 05:19:59 GMTTue, 11 Feb 2014 05:19:59 GMT60跟vczh看实例学~译原理——一QTinymoe的设计哲?/title><link>http://www.shnenglu.com/vczh/archive/2014/02/11/205702.html</link><dc:creator>陈梓?vczh)</dc:creator><author>陈梓?vczh)</author><pubDate>Tue, 11 Feb 2014 04:53:00 GMT</pubDate><guid>http://www.shnenglu.com/vczh/archive/2014/02/11/205702.html</guid><wfw:comment>http://www.shnenglu.com/vczh/comments/205702.html</wfw:comment><comments>http://www.shnenglu.com/vczh/archive/2014/02/11/205702.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.shnenglu.com/vczh/comments/commentRss/205702.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/vczh/services/trackbacks/205702.html</trackback:ping><description><![CDATA[<p>自从《序》胡扯了快一个月之后Q终于迎来了正片。之所以系列文章叫《看实例学编译原理》,是因为整个系列会通过带大家一步一步实现Tinymoe的过E,来介l编译原理的一些知识点? </p><p>  </p><p>但是W一个系列还没到开始处理Tinymoe源代码的时候,首先的跟大家讲一讲我设计Tinymoe的故事。ؓ什么这U东西要{到现在才讲呢,因ؓ之前没有文档Q将了也是白讲啊。Tinymoe在github的wiki分ؓ两部分,一部分是介l语法的Q另一部分是介l一个最的标准库是如何实现出来的,地址?<a >https://github.com/vczh/tinymoe/wiki</a> 不带问号的那些都是写完了的? </p><p style="text-align: justify"><h2>pd文章的目? </h2></p><p>在介lTinymoe之前Q先说一下这个系列文章的目标。IdeallyQ只要一个h看完了这个系列,他就可以在下面这些地方得?strong>入门</strong>Q? </p><ul><li><div style="text-align: justify"><span style="font-size:10pt">词法分析 </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">歧义与不歧义的语法分? </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">语义分析 </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">W号? </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">全文CPS变换 </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">~译生成高效的其他语a的代? </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">~译生成自己的指令集 </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">带GC的虚拟机 </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">cd推导Qintersection typeQunion typeQconcept mappingQ? </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">跨过E分析(inter-procedural analyzingQ? </span></div></li></ul><p>  </p><p>当然Q这q不能让你成Z个大牛,但是臛_自己做做实验Q搞一炚w大上的东襉K师妹们是没有问题了? </p><p style="text-align: justify"><h2>Tinymoe设计的目? </h2></p><p>虽然x很多q前已l有了,但是q次我想把它实现出来Q是Z完成《如何设计一门语a》的后箋。光讲大道理是没有意义的Q至得有一个例子,让大家知道这些事情到底是什么样子的。因此Tinymoe有一Ҏ学的意义Q不是使用它还是实现它? </p><p>  </p><p>首先Q?strong>处理Tinymoe需要的知识点多Q用于编译原理教?/strong>。既然是Z展示~译原理的基知识Q因此语a本n不可能是那种烂大街的Cpd的东ѝ当焉了知识点以外Q还会让大家深刻的理解到Q?strong>隑֮现和隄Q是完全没有关系?/strong>QTinymoe用v来可爽了Q啊哈哈哈哈哈? </p><p>  </p><p>其次Q?strong>TinymoeҎ嵌入其他语言的程序,作ؓDSL使用Q可以调用宿ȝ序提供的功能</strong>。这严格的来讲不语a本n的功能,而是实现本n的功能。就是C++也可以设计ؓ嵌入式,lua也可以被设计为编译成exe的。一个语a本n的设计ƈ不会对如何用它有多大的限制。ؓ了让大家看了q个pd之后Q可以写可用的东西Q而不仅仅是写玩具Q因此这也是设计的目标之一? </p><p>  </p><p>W三Q?strong>Tinymoe语法优化于描q复杂的逻辑</strong>Q而不是优化与复杂的数据结构和法Q虽然也可以Q。Tinymoe本n是不存在Ml粒度控制内存的能力的,而且虽然可以实现复杂的数据结构和法Q但是本w描q这些东西最多也pJavaScript一样容易——其实就是不Ҏ。但是Tinymoe设计的时候,是ؓ了让大家把Tinymoe当成是一门可以设计DSL的语aQ因此对复杂逻辑的描q能力特别强。唯一的前提就是,你懂得如何给Tinymoe写库。很好的使用和很好地实现一个东西是相辅相成的。我在设计Tinymoe之初Q很多pattern我也不知道,只是因ؓ设计Tinymoe遵@了科学的ҎQ因此最后我发现Tinymoe竟然h如此强大的描q能力。当然对于读者们本nQ也会在阅读pd文章的有cM的感觉? </p><p>  </p><p>最后,<strong>Tinymoe是一个动态类型语a</strong>。这Ua是我的个人爱好了。对一门动态类型语a做静态分析那该多有趣啊,啊哈哈哈哈哈哈? </p><p style="text-align: justify"><h2>Tinymoe的设计哲? </h2></p><p>当然我ƈ不会Z写文章就无线提高Tinymoe的实现难度的。ؓ了把他控制在一个正常水qI因此设计Tinymoe的第一条就是: </p><p>  </p><p><strong>一、小规模的语a核心+大规模的标准? </strong></p><p>  </p><p>其实q跟C++差不多。但是C++׃惛_的事情实在是太多了,譬如说视囑֌涉|有范式等{,因此qq么做,仍然让C++本n包含的东西过于巨大(其实我还是觉得C++不难怎么办)? </p><p>  </p><p>语言核心,实现h当然Ҏ?strong>但是你ƈ不能Z让语a核心就牺牲什么功?/strong>。因此精心设计一个核心是必须的,因ؓ所有你惌但是不想加入语言的功能,从此可以用库来实现了? </p><p>  </p><p>譬如_Tinymoe通过有条件地暴露continuationQ要求编译器在编译Tinymoe的时候做一ơ全文CPS变换。这个东西说Ҏ也不是那么容易,但是臛_比你做分支@环异常处理什么的全部加v来要单多了吧。所以我只提供continuationQ剩下的控制全部用库来做。这h三个好处Q? </p><ol><li><div style="text-align: justify"><span style="font-size:10pt"><strong>语言单,实现隑ֺ降低</strong>? </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt"><strong>Z让库可以发挥应有的作用,语言的功能的选择十分的正交化</strong>。不q这仍然在一定的E度上提高了学习的难度。但是ƈ不是所有h都需要写库对吧,很多人只需要会用库够了。通过一点点的牺Ԍ正交化可以充分发挥程序员的想象能力。这对于以DSL为目的的语言来说是不可或~的? </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt"><strong>标准库本w可以作为编译器的测试用?/strong>。你只需要准备够多的测试用例来q行标准库,那么你只要用C++Q假设你用C++来实现TinymoeQ来跑他们,那所有的标准库都会得到运行。运行结果如果对Q那你对~译器的实现也就有信心了。ؓ什么呢Q因为标准库大量的用了语言的各U功能,而且是无节操的用。如果这样都能过Q那普通的E序更能过了? </span></div></li></ol><p>  </p><p>说了q么多,那到底什么是规模的语言核心呢?q在Tinymoe上有两点体现? </p><p>  </p><p>W一点,是<strong>Tinymoe的语法元素少</strong>。一个Tinymoe表达式无非就只有三类Q函数调用、字面量和变量、操作符。字面量是那些数字字符串什么的。当Tinymoe的函数的某一个参数指定ؓ不定个数的时候你q得提供一个tuple。委托(在这里是函数指针和闭包的l称Q和数组虽然也是Tinymoe的原生功能之一Q但是对他们的操作都是通过函数调用来实现的Q没有特D的语法? </p><p>  </p><p>单地Ԍ是除了下面q些东西以外你不会见到别的种cȝ表达式了Q? </p><p style="text-align: justify"><span style="font-size:10pt">1 </span></p><p style="text-align: justify"><span style="font-size:10pt">"text" </span></p><p style="text-align: justify"><span style="font-size:10pt">sum from 1 to 100 </span></p><p style="text-align: justify"><span style="font-size:10pt">sum of (1, 2, 3, 4, 5) </span></p><p style="text-align: justify"><span style="font-size:10pt">(1+2)*(3+4) </span></p><p style="text-align: justify"><span style="font-size:10pt">true </span></p><p>  </p><p>一个Tinymoe语句的种cd更少了,要么是一个函数调用,要么是blockQ要么是q在一L几个blockQ? </p><p style="text-align: justify"><span style="font-size:10pt">do something bad </span></p><p style="text-align: justify">  </p><p style="text-align: justify"><span style="font-size:10pt">repeat with x from 1 to 100 </span></p><p style="text-align: justify"><span style="font-size:10pt">    do something bad with x </span></p><p style="text-align: justify"><span style="font-size:10pt">end </span></p><p style="text-align: justify">  </p><p style="text-align: justify"><span style="font-size:10pt">try </span></p><p style="text-align: justify"><span style="font-size:10pt">    do something bad </span></p><p style="text-align: justify"><span style="font-size:10pt">catch exception </span></p><p style="text-align: justify"><span style="font-size:10pt">    do something worse </span></p><p style="text-align: justify"><span style="font-size:10pt">end </span></p><p>  </p><p>有h可能会说Q那repeat和try-catch׃是语法元素吗Q这个真不是Q他们是标准库定义好的函敎ͼ跟你自己声明的函数没有Q何特D的地方? </p><p>  </p><p>q里其实q有一个有意思的地方Q?repeat with x from 1 to 100"的x其实是@环体的参数。Tinymoe是如何给你自定义的block开z的呢?不仅如此QTinymoe的函数还可以声明"引用参数"Q也是说调用这个函数的时候你只能把一个变量放q去Q函数里面可以读写这个变量。这些都是怎么实现的呢Q学下去q道了Q啊哈哈哈哈? </p><p>  </p><p>Tinymoe的声明也只有两种Q第一U是函数Q第二种是符受函数的声明可能会略微复杂一点,不过除了函数头以外,其他的都是类似配|一L东西Q几乎都是用来定?catch函数在用的时候必Lq在try函数后面"啊,"break只能在repeat里面?啊,诸如此类的信息? </p><p>  </p><p>Tinymoe的符号十分简单,譬如说你要定义一q四季的W号Q只需要这么写Q? </p><p style="text-align: justify"><span style="font-size:10pt">symbol spring </span></p><p style="text-align: justify"><span style="font-size:10pt">symbol summer </span></p><p style="text-align: justify"><span style="font-size:10pt">symbol autumn </span></p><p style="text-align: justify"><span style="font-size:10pt">symbol winter </span></p><p>  </p><p>symbol是一?与众不同的?Q也是说你在两个module下面定义同名的symbol他们也是不一L。所有symbol之间都是不一LQ可以用=?lt;>来判断。symbol是?不一?来定义其自n的? </p><p>  </p><p>至于_那ؓ什么不用enum呢?因ؓTinymoe是动态类型语aQenum的类型本w是Ҏ没有用武之地的,所以干脆就设计成了symbol? </p><p>  </p><p>W二点,<strong>Tinymoe除了continuation和select-case以外Q没有其他原生的控制支?/strong>? </p><p>  </p><p>q基本上归功于先辈发明continuation passing style transformation的功劻Il节在以后的pd里面会讲。心急的人可以先?<a >https://github.com/vczh/tinymoe/blob/master/Development/Library/StandardLibrary.txt</a> 。这个文件暂时包含了Tinymoe的整个标准库Q里面定义了很多if-else/repeat/try-catch-finally{控制流Q甚臌coroutine都可以用continuation、select-case和递归来做? </p><p>  </p><p>q也?strong>规模的语言核心+大规模的标准?/strong>所要表辄意思。如果可以提供一个feature AQ通过他来完成其他必要的feature B0, B1, B2…的同Ӟ来说不定还有h可以Z自己的需求,开发DSL的时候定义feature CQ那么只有A需要保留下来,所有的B和C都将使用库的Ҏ来实现? </p><p>  </p><p>q么做ƈ不是完全有益无害的,只是坏处很小Q在"Tinymoe的实现难?里面会详l说明?strong> </strong></p><p>  </p><p><strong>二、扩展后的东西跟原生的东西外观一? </strong></p><p>  </p><p>q是很重要的。如果扩展出来的东西跟原生的东西长得不一P用v来就觉得很傻逹{Java的string不能?=来判断内容就是这L一个例子。虽然他们有的是理由证明==的反直觉设计是对的——但是反直觉是反直觉,是一个大坑? </p><p>  </p><p>q种例子q有很多Q譬如说go的数l和表的cd啦,go本n如果不要数组和表的话Q是写不出长得跟原生数组和表一L数组和表的。其实这也不是一个大问题Q问题是gol数l和表的样子搞特D化Q还有那个反直觉的slice的赋值问题(会合法溢出!Q,cM的东西实在是太多了。一个东西特例太多,坑就无法避免。所以其实在我看来,goq不如给C语言加上erlang的actor功能了事? </p><p>  </p><p>反而C++在这件事情上做得很好。如果你对C++不熟悉的话,有时候根本分不清什么是~译器干的,什么是标准库干的。譬如说static_cast和dynamic_cast长得像一个模板函敎ͼ因此boost可以用cM的手法加入lexical_cast和针对shared_ptr的static_pointer_cast和dynamic_pointer_castQ整个标准库和语a本n然一体。这样子做的好处是,当你在培d语言本n的直觉的时候,你也在培d标准库的直觉Q培ȝ觉这件事情你不用做两ơ。你对一个东西的直觉准Q学习新东西的速度p快。所以C++的设计刚好可以让你在熬过W一个阶D늚学习之后Q后面都觉得无比的轻松? </p><p>  </p><p>不过具体到TinymoeQ因为Tinymoe本n的语法元素太了Q所以这个做法在Tinymoew上体现得不明显? </p><p style="text-align: justify"><h2>Tinymoe的实现难? </h2></p><p>首先Q?strong>语法分析需要对TinymoeE序处理三遍</strong>。Tinymoe对于语句设计使得对一个TinymoeE序做语法分析不是那么直接(虽然比C++什么的q是Ҏ多了Q。D个例子: </p><p style="text-align: justify"><span style="font-size:10pt">module hello world </span></p><p style="text-align: justify">  </p><p style="text-align: justify"><span style="font-size:10pt">phrase sum from (lower bound) to (upper bound) </span></p><p style="text-align: justify"><span style="font-size:10pt">… </span></p><p style="text-align: justify"><span style="font-size:10pt">end </span></p><p style="text-align: justify">  </p><p style="text-align: justify"><span style="font-size:10pt">sentence print (message) </span></p><p style="text-align: justify"><span style="font-size:10pt">… </span></p><p style="text-align: justify"><span style="font-size:10pt">end </span></p><p style="text-align: justify">  </p><p style="text-align: justify"><span style="font-size:10pt">phrase main </span></p><p style="text-align: justify"><span style="font-size:10pt">    print sum from 1 to 100 </span></p><p style="text-align: justify"><span style="font-size:10pt">end </span></p><p>  </p><p>W一遍分析是词法分析Q这个时候得把每一个token的行可住。第二遍分析是不带歧义的语法分析Q目标是把所有的函数头抽取出来,然后l成一个全局W号表。第三遍分析是对函C里面的语句做带歧义的语法分析了。因为Tinymoe允许你定义变量,所以符可肯定是一边分析一边修改的。于是对?print sum from 1 to 100"q一句,如果你没有发?phrase sum from (lower bound) to (upper bound)"?sentence print (message)"Q那Ҏ无从下手? </p><p>  </p><p>q有另一个例子: </p><p style="text-align: justify"><span style="font-size:10pt">module exception handling </span></p><p style="text-align: justify">  </p><p style="text-align: justify"><span style="font-size:10pt">… </span></p><p style="text-align: justify">  </p><p style="text-align: justify"><span style="font-size:10pt">phrase main </span></p><p style="text-align: justify"><span style="font-size:10pt">    try </span></p><p style="text-align: justify"><span style="font-size:10pt">        do something bad </span></p><p style="text-align: justify"><span style="font-size:10pt">    catch </span></p><p style="text-align: justify"><span style="font-size:10pt">        print "bad thing happened" </span></p><p style="text-align: justify"><span style="font-size:10pt">    end </span></p><p style="text-align: justify"><span style="font-size:10pt">end </span></p><p>  </p><p>当语法分析做?try"的时候,因ؓ发现存在<strong>try函数的定?/strong>Q所以Tinymoe知道接下来的"do something bad"属于调用tryq个块函数所需提供的代码块里面的代码。接下来?catch"QTinymoe怎么知道catch是接在try后面Q而不是放在try里面的呢Q这仍然是由?strong>catch函数的定?/strong>告诉我们的。关于这斚w的语法知识可?a >点击q里查看</a>? </p><p>  </p><p>正因为如此,我们需要首先知道函数的定义Q然后才能分析函C里面的代码。虽然这在一定程度上造成了Tinymoe的语法分析复杂度的提升,但是其复杂度本nq不高。比C++单就不说了,q是C、C#和JavaQ由于其语法元素太多Q导?strong>不需要多ơ分?/strong>所降低的复杂度被完全的抉|Q结果跟实现Tinymoe的语法分析器的难度不怸下? </p><p>  </p><p>其次Q?strong>CPS变换后的代码需要特D处理,否则直接执行ҎDcall stackU篏的没用的东西q多</strong>。因为Tinymoe可以自定义操作符Q所以操作符跟C++一样在~译的时候被转换成了函数调用。每一个函数调用都是会被CPS变换的。尽每一行的函数调用ơ数不多Q但是如果你的程序a循环Q?strong>循环是通过递归来描q?/strong>Q而不是实玎ͼ׃CPS变换后Tinymoe做了优化Q所以不存在实际上的递归Q的Q如果直接执行CPS变换后的代码Q算一?加到1000都会Dstack overflow。可见其call stack里面堆积的closure数量之巨大? </p><p>  </p><p>我在做Tinymoe代码生成的实验的时候,Z单我在单元测试里面直接生了对应的C#代码。一开始没有处理CPS而直接调用,E序不仅慢,而且Ҏstack overflow。但是我们知道(其实你们以后才会知道Q,CPS变换后的代码里面几乎所有的call stackw是浪费的Q因此我把整个在生成C#代码的时候修ҎQ如果需要调用continuationQ就q回<strong>调用continuation的语句组成的lambda表达?/strong>Q在最外层用一个@环去驱动他直到返回null为止。这样做了之后,qTinymoe的代码有递归Qcall stack里面也不会因为递归而积累call stack item了。于是生成的C#代码执行飞快Q而且无论你怎么递归也永q不会造成stack overflow了。这个美妙的Ҏ几乎所有语a都做不到Q啊哈哈哈哈哈? </p><p>  </p><p>当然q也是有代h的,因ؓ本质上我只是把保存在stack上的context转移到heap上。不q多亏了.net 4.0的强大的background GCQ这样做丝毫没有多余的性能上的损耗。当然这也意味着Q一个高性能的Tinymoe虚拟机,需要一个牛逼的垃圾攉器作为靠山。context产生的closure在函C真的被执行完之后׃被很快地攉Q所以CPS加上q种做法q不会对GC产生额外的压力,所有的压力仍然来源于你自己所创徏的数据结构? </p><p>  </p><p>W三Q?strong>Tinymoe需要动态类型语a的类型推?/strong>。当然你不这么做而把Tinymoe的程序当JavaScript那样的程序处理也没有问题。但是我们知道,正是因ؓV8对JavaScript的代码进行了cd推导Q才产生了那么优异的性能。因此这是一个优化上的措施? </p><p>  </p><p>最后,<strong>Tinymoeq需要跨q程分析和对E序的控制流的化</strong>Q譬如continuation转状态机{)。目前具体怎么做我q在学习当中。不q我们想Q既然repeat函数是通过递归来描q的Q那我们能不能通过Ҏ有代码进行inter-procedural analyzingQ从而发现诸? </p><p style="text-align: justify"><span style="font-size:10pt">repeat 3 times </span></p><p style="text-align: justify"><span style="font-size:10pt">    do something good </span></p><p style="text-align: justify"><span style="font-size:10pt">end </span></p><p>是一个@环,从而生成用真正的@环指令(譬如说gotoQ呢Q这个问题是个很有意思的问题Q我觉得我如果可以通过学习静态分析从而解军_Q不q我的能力会得到提升Q我对你们的U普也会做得更好? </p><p style="text-align: justify"><h2>后记 </h2></p><p>虽然q不C千字Q但是总觉得写了好多的样子。M我希望读者在看完《零》和《一》之后,Ҏ下来需要学习的东西有一个较为清晰的认识?/p> <img src ="http://www.shnenglu.com/vczh/aggbug/205702.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/vczh/" target="_blank">陈梓?vczh)</a> 2014-02-11 12:53 <a href="http://www.shnenglu.com/vczh/archive/2014/02/11/205702.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>跟vczh看实例学~译原理——零Q序ahttp://www.shnenglu.com/vczh/archive/2014/01/19/205468.html陈梓?vczh)陈梓?vczh)Sat, 18 Jan 2014 17:21:00 GMThttp://www.shnenglu.com/vczh/archive/2014/01/19/205468.htmlhttp://www.shnenglu.com/vczh/comments/205468.htmlhttp://www.shnenglu.com/vczh/archive/2014/01/19/205468.html#Feedback4http://www.shnenglu.com/vczh/comments/commentRss/205468.htmlhttp://www.shnenglu.com/vczh/services/trackbacks/205468.html在《如何设计一门语a》里面,我讲了一些语a斚w的东西,q有痛快的喷了一些XX_什么的。不q单U讲q个也是很无聊的Q所以我开了这个《跟vczh看实例学~译原理》系列,意在U普一些编译原理的知识Q尽量让大家可以在创造语a之后Q自己写一个原型。在q里我拿我创造的一门很有趣的语a https://github.com/vczh/tinymoe/ 作ؓ实例?

 

商业~译器对功能和质量的要求都是很高的,里面大量的东西其实都跟编译原理没关系。一个典型的~译原理的原型有什么特征呢Q?

  1. 性能?
  2. 错误信息隄
  3. 没有查所有情况就生成代码
  4. 优化做得?
  5. 几乎没有~译选项

 

{等。Tinymoe满了上面?U情况,因ؓ我的目标也只是想做一个原型,向大家介l编译原理的基础知识。当Ӟ我对语法的设计还是尽量靠q工业质量的Q只是实现没有花太多心思?

 

Z么我要用Tinymoe来作为实例呢Q因为Tinymoe是少有的一U用h单,而且库可以有多复杂写多复杂的语言Q就跟C++一栗C++11额标准库在一L直是愉快啊,Tinymoe的代码也是这么写的。但是这q不妨碍你可以在写C++库的时候发挥你的想象力。Tinymoe也是一L。ؓ什么呢Q我来D个例子?

 

Hello, world!

Tinymoe的hello worldE序是很单的Q?

 

module hello world

using standard library

 

sentence print (message)

    redirect to "printf"

end

 

phrase main

    print "Hello, world!"

end

 

module指的是模块的名字Q普通的E序也是一个模块。using指的是你要引用的模块——standard library是Tinymoe的STL了——当然这个程序ƈ没有用到Mstandard library的东ѝ说到这里大家可能意识到了,Tinymoe的名字可以是不定长的tokenl成的!没错Q后面大家会慢慢意识到这U做法有多么的强大?

 

后面是print函数和main函数。Tinymoe是严格区分语句和表达式的Q只有sentence和block开头的函数才能作ؓ语句Q而且同时只有phrase开头的函数才能作ؓ表达式。所以下面的E序是不合法的:

 

phrase main

    (print "Hello, world!") + 1

end

 

原因是Qprint是sentenceQ不能作辑ּ使用Q因此他不能?1?

 

Tinymoe的函数参数都被写在括号里面,一个参数需要一个括受到了这里大家可能会觉得很奇怪,不过很快׃有解{了。ؓ什么要q么做,下一个例子就会告诉我们?

 

print函数用的redirect to是Tinymoe声明FFIQForeign Function InterfaceQ的ҎQ也是_当你q行了printQ他׃去host里面找一个叫做printf的函数来q行。不q大家不要误会,Tinymoeq没有被设计成可以直接调用C函数Q所以这个名字其实是随便写的Q只要host提供了一个叫做printf的函数完成printf该做的事情就行了。main函数׃用解释了Q很直白?

1加到100{于5050

q个例子可以在Tinymoe的主(https://github.com/vczh/tinymoe/Q上面看刎ͼ

 

module hello world

using standard library

 

sentence print (message)

redirect to "printf"

end

 

phrase sum from (start) to (end)

set the result to 0

repeat with the current number from start to end

add the current number to the result

end

end

 

phrase main

print "1+ ... +100 = " & sum from 1 to 100

end

 

Z么名字可以是多个tokenQؓ什么每一个参数都要一个括P看加_的部分q道了Q正是因为Tinymoe惌每一行代码都可以被念出来Q所以才q么设计的。当Ӟ大家肯定都知道怎么start + (start+1) + … + (end-1) + end了,所以应该很Ҏ可以看懂这个函数里面的代码具体是什么意思?

 

在这里可以稍微多做一下解释。the result是一个预定义的变量,代表函数的返回倹{只要你往the result里面写东西,只要函数一l束Q他变成函数的q回g。Tinymoe的括h有什么特D意思,是改变优先U,所以那一句@环则可以通过d括号的方法写成这P

 

repeat with (the current number) from (start) to (end)

 

大家可能会想Qrepeat with是不是关键字Q当然不是!repeat with是standard library里面定义的一个block函数。大家知道block函数的意思了吧,是q个函数可以带一个block。block有一些特性可以让你写出类似try-catch那样的几个blockq在一L大blockQ特别适合写库?

 

Cq里大家心中可能会有疑问Q@环ؓ什么可以做成库呢?q有更加令h震惊的是Qbreak和continue也不是关键字Q是sentenceQ因为repeat with是有代码的:

 

category

    start REPEAT

    closable

block (sentence deal with (item)) repeat with (argument item) from (lower bound) to (upper bound)

    set the current number to lower bound

    repeat while the current number <= upper bound

        deal with the current number

        add 1 to the current number

    end

end

 

前面的category是用来定义一些block的顺序和包围l构什么的。repeat with是属于REPEAT的,而break和continue声明了自己只能直接或者间接方在REPEAT里面Q因此如果你在一个没有@环的地方调用break或者continueQ编译器׃报错了。这是一个花边功能,用来防止手误的?

 

大家可能会注意到一个新东西Q?argument item)。argument的意思指的是Q后面的item是block里面的代码的一个参敎ͼ对于repeat with函数本n他不是一个参数。这通过一个很自然的方法给blockd参数了。如果你用ruby的话得写成q个悲催的样子:

 

repeat_with(1, 10) do |item|

    xxxx

end

 

而用C++写v来就更悲催了Q?

 

repeat_with(1, 10, [](int item)

{

    xxxx

});

 

block的第一个参数sentence deal with (item)是一个引用了block中间的代码的委托。所以你会看C码里面会调用它?

 

好了Q那repeat whileL关键字了吧——不是!后面大家q会知道Q就q?

 

if xxx

    yyy

else if zzz

    www

else if aaa

    bbb

else

    ccc

end

 

也只是你调用了if、else if和else的一pd函数然后让他们串h而已?

 

那Tinymoe到底提供了什么基设施呢?其实只有select-case和递归。用q两个东西,加上内置的数l,图灵完备了。图灵完备就是这么容易啊?

 

多重分派QMultiple DispatchQ?

讲到q里Q我不得不说QTinymoe也可以写c,也可以承,不过他跟传统的语a不一LQ类是没有构造函数、析构函数和其他成员函数的。Tinymoe所有的函数都是全局函数Q但是你可以使用多重分派?挑?cd。这需要第三个例子了(也可以在主页上找刎ͼQ?

 

module geometry

using standard library

 

phrase square root of (number)

    redirect to "Sqrt"

end

 

sentence print (message)

    redirect to "Print"

end

 

type rectangle

    width

    height

end

 

type triangle

    a

    b

    c

end

 

type circle

    radius

end

 

phrase area of (shape)

    raise "This is not a shape."

end

 

phrase area of (shape : rectangle)

    set the result to field width of shape * field height of shape

end

 

phrase area of (shape : triangle)

    set a to field a of shape

    set b to field b of shape

    set c to field c of shape

    set p to (a + b + c) / 2

    set the result to square root of (p * (p - a) * (p - b) * (p - c))

end

 

phrase area of (shape : circle)

    set r to field radius of shape

    set the result to r * r * 3.14

end

 

phrase (a) and (b) are the same shape

    set the result to false

end

 

phrase (a : rectangle) and (b : rectangle) are the same shape

    set the result to true

end

 

phrase (a : triangle) and (b : triangle) are the same shape

    set the result to true

end

 

phrase (a : circle) and (b : circle) are the same shape

    set the result to true

end

 

phrase main

    set shape one to new triangle of (2, 3, 4)

    set shape two to new rectangle of (1, 2)

    if shape one and shape two are the same shape

        print "This world is mad!"

    else

        print "Triangle and rectangle are not the same shape!"

    end

end

 

q个例子E微长了一点点Q不q大家可以很清楚的看到我是如何定义一个类型、创Z们和讉K成员变量的。area of函数可以计算一个^面几何图形的面积Q而且会根据你传给他的不同的几何图形而用不同的公式。当所有的cd判断都失败的时候,׃掉进那个没有Mcd声明的函敎ͼ从而引发一场。嗯Q其实try/catch/finally/raise都是函数来的——TinymoeҎ制流的控制就是如此强大,啊哈哈哈哈。就qreturn都可以自己做Q所以Tinymoe也不提供预定义的return?

 

那phrase (a) and (b) are the same shape怎么办呢Q没问题QTinymoe可以同时指定多个参数的类型。而且Tinymoe的实现具有跟C++虚函CL性质——无Z有多个参数标记了类型,我都可以O(n)跌{C个你需要的函数。这里的n指的是标Ccd的参数的个数Q而不是函数实例的个数Q所以跟C++的情冉|一L——因为this只能有一个,所以就是O(1)。至于Tinymoe到底是怎么实现的,只需要看《如何设计一门语a》第五篇Q?a href="http://www.shnenglu.com/vczh/archive/2013/05/25/200580.html">http://www.shnenglu.com/vczh/archive/2013/05/25/200580.htmlQ就有答案了?

Continuation Passing Style

Z么Tinymoe的控制流都可以自己做呢?因ؓTinymoe的函数都是写成了CPSq种风格的。其实CPS大家都很熟悉Q当你用jquery做动画,用node.js做IO的时候,那些嵌套的一个一个的lambda表达式,有点CPS的味道。不q在q里我们q没有看到嵌套的lambdaQ这是因为Tinymoe提供的语法,让Tinymoe的编译器可以把同一个层ơ的代码Q{成嵌套的lambda那样的代码。这个过E就叫CPS变换。Tinymoe虽然用了很多函数式编E的手段Q但是他q不是一门函数是语言Q只是一门普通的q程式语a。但是这跟C语言不一P因ؓ它连C#的yield return都可以写成函敎ͼq个例子更长了Q大家可以到Tinymoe的主上看。我q里只脓一段代码Q?

 

module enumerable

using standard library

 

symbol yielding return

symbol yielding break

 

type enumerable collection

    body

end

 

type collection enumerator

    current yielding result

    body

    continuation

end

 

略(q里实现了跟enumerable相关的函敎ͼ包括yield returnQ?

 

block (sentence deal with (item)) repeat with (argument item) in (items : enumerable collection)

    set enumerator to new enumerator from items

    repeat

        move enumerator to the next

        deal with current value of enumerator

    end

end

 

sentence print (message)

    redirect to "Print"

end

 

phrase main

    create enumerable to numbers

        repeat with i from 1 to 10

            print "Enumerating " & i

            yield return i

        end

    end

 

    repeat with number in numbers

        if number >= 5

            break

        end

        print "Printing " & number

    end

end

 

什么叫模拟C#的yield return呢?是q惰性计也一h拟!在main函数的第一部分Q我创徏了一个enumerableQiteratorQ,包含1?0十个数字Q而且每生一个数字还会打印出一句话。但是接下来我在循环里面只取?个,打印?个,因此执行l果是

当!

 

CPS风格的函数的威力在于Q每一个函数都可以控制他如何执行函数结束之后写在后面的代码。也是_你可以根据你的需要,q脆选择保护现场Q然后以后再回复。是不是听v来很像lua的coroutine呢?在TinymoeQcoroutine也可以自己做Q?

 

虽然函数最后被转换成了CPS风格的astQ而且试用的生成C#代码的确也是原封不动的输Z出来Q所以运行这个程序耗费了大量的函数调用。但qƈ不意味着Tinymoe的虚拟机也要q么做。大家要CQ一个语a也好Q类库也好,l你的接口的概念Q跟实现的概念,有可能完全不同。yield return写出来的要p点心思,所以《序a》我也不讲这么多了,后箋的文章会详细介绍q方面的知识Q当然了Q还会告诉你怎么实现的?

 

֣

q里我挑选了四个例子来展CTinymoe最重要的一些概c一门语aQ要应用用v来简单,库写h可以发挥惌力,才是有前途的。yield return例子里面的main函数一P用的时候多清爽Q清爽到让你完全忘记yield return实现的时候里面的各种ȝ的细节?

 

所以ؓ什么我要挑选Tinymoe作ؓ实例来科普编译原理呢Q有两个原因。第一个原因是Q想要实现TinymoeQ需要大量的知识。所以既然这个系列想让大家能够看完实C个Tinymoe的低质量原型Q当然会讲很多知识的。第二个原因是,我想通过q个例子向大家将一个道理,是库和应用 、编译器和语法、实现和接口Q完全可以做到隔d杂,只留l最l用L单的部分?span style="font-size:14pt">你看到的复杂的接口,q不意味着他的实现是臃肿的。你看到的简单的接口Q也不意味着他的实现很z?/strong>?

 

Tinymoe目前已经可以输出C#代码来执行了。后面我q会lTinymoe加上静态分析和cd推导。对于这c语a做静态分析和cd推导又很多麻烦,我现在还没有完全搞明白。譬如说q种可以自己控制continuation的函数要怎么~译成状态机才能避免掉大量的函数调用Q就不是一个容易的问题。所以在pd一边做的时候,我还会一边研I这个事情。如果到时候系列把~译部分写完的同Ӟq些问题我也搞明白的话,那我׃让这个系列扩展到包含静态分析和cd推导Ql往下讲?/p>

陈梓?vczh) 2014-01-19 01:21 发表评论
]]>
þþþþ޾ƷӰԺ| þ99Ʒ| ƷþþþþĻ| ŷƷ99þ| þˬˬAVƬ| ƷѾþþþõӰ| 99þѹƷ| Ʒþþþþþ| ĻƷѾþ5151| þŮƵ| 99þۺϹƷ| þ㽶߿ۿ | Ʒ18þþ⺾| 츾þþ| AV12þ| Ʒٸavþ| ھƷþþþӰԺ| þþƷѹۿ| 99þþƷѹۿ| þþɫۺϾɫ99| 99þѹƷػ| þۺ97ɫһһ | ƷþþþӰԺ۲| ŷƷһƷþ| ҹƷþþĸ | vaĻþ| þþƷһ| þþƷĻ23ҳ| þɫۺһ| 99þĻ| һŮȫƾþƬ| ޾ƷƷþ99һ| ߳߳þþ91 | þþƷվ| þþþþþƷͼƬ| þAVӰ| Ʒþۺ123 | Ʒþþþþþþþ | þþ޾Ʒ| þҹ³˿ƬҹƷ| þþƷav鶹С˵|