??xml version="1.0" encoding="utf-8" standalone="yes"?>青青青国产成人久久111网站,久久国产乱子精品免费女,久久青青国产http://www.shnenglu.com/vczh/zh-cnTue, 06 May 2025 15:54:02 GMTTue, 06 May 2025 15:54:02 GMT602014q终ȝhttp://www.shnenglu.com/vczh/archive/2015/01/09/209442.html陈梓?vczh)陈梓?vczh)Thu, 08 Jan 2015 22:58:00 GMThttp://www.shnenglu.com/vczh/archive/2015/01/09/209442.htmlhttp://www.shnenglu.com/vczh/comments/209442.htmlhttp://www.shnenglu.com/vczh/archive/2015/01/09/209442.html#Feedback14http://www.shnenglu.com/vczh/comments/commentRss/209442.htmlhttp://www.shnenglu.com/vczh/services/trackbacks/209442.html

陈梓?vczh) 2015-01-09 06:58 发表评论
]]>
靠谱的代码和DRYhttp://www.shnenglu.com/vczh/archive/2014/07/15/207658.html陈梓?vczh)陈梓?vczh)Tue, 15 Jul 2014 14:44:00 GMThttp://www.shnenglu.com/vczh/archive/2014/07/15/207658.htmlhttp://www.shnenglu.com/vczh/comments/207658.htmlhttp://www.shnenglu.com/vczh/archive/2014/07/15/207658.html#Feedback9http://www.shnenglu.com/vczh/comments/commentRss/207658.htmlhttp://www.shnenglu.com/vczh/services/trackbacks/207658.html

上次有h来要求我写一文章谈谈什么代码才是好代码Q是谁我已经忘记了,好像是AutoHotkeyq是啥的专栏的作者。撇开那些奇怪的条款不谈Q靠q 代码有一个共同的特点Q就是DRY。DRY是Don't Repeat YourselfQ其实已l被了好多年了,但是几乎所有h都会忘记?/p>

什么是DRYQDon't Repeat YourselfQ?

DRY q不是指你不能复制代码这么简单的。不能repeat的其实是信息Q不是代码。要分析一D代码里面的什么东西时信息Q就跟给物理题做受力分析一Px?都做对其实不太容易。但是一份代码L要不断的修补的,所以在q之前大家要先做好TDDQ也是Test Driven Development。这里我对自q要求是覆盖率要高?5%Q不用什么手D,M95%的代码的输出都要受到验。当有了_多的试做后盄?候,不管你以后发生了什么,譬如说你发现你Repeat了什么东西要改,你才能放心大胆的L。而且从长q的角度来看Q做好TDD可以开发出相同质量的代码的旉~短?0%左右Q这是我自己的经验| ?/p>

什么是信息

信息q个词不太好用语a下定义,不过我可以D个例子。譬如说你要把一个配|文仉面的字符串按照分隔符分解成几个字W串Q你大概׃写出q样的代码:

// name;parent;description
void ReadConfig(const wchar_t* config)
{
    auto p 
= wcschr(config, L';');                            // 1
    if(!p) throw ArgumentException(L"Illegal config string"); // 2
    DoName(wstring(config, p));                               // 3
    auto q = wcschr(p + 1, L';');                             // 4
    if(!q) throw ArgumentException(L"Illegal config string"); // 5
    DoParent(wstring(p + 1, q);                               // 6
    auto r = wcschr(q + 1, L';');                             // 7
    if(r) throw ArgumentException(L"Illegal config string");  // 8
    DoDescription(q + 1);                                     // 9
}

q段短短的代码重复了多少信息Q?/p>

  • 分隔W用的是分号Q???Q?/li>
  • W二/三个片段的第一个字W位于第一/二个分号的后面(4???Q?/li>
  • 格式查(2??Q?
  • 异常内容Q???Q?br />

除了DRY以外q有一个问题,是处理description的方法跟name和parent不一P因ؓ他后面再也没有分号了?/p>

那这D代码要怎么改呢Q有些h可能会想刎ͼ那把重复的代码抽取出一个函数就好了Q?/p>

wstring Parse(const wchar_t& config, bool end)
{
    auto next 
= wcschr(config, L';');
    ArgumentException up(L
"Illegal config string");
    
if (next)
    {
        
if (end) throw up;
        wstring result(config, next);
        config 
= next + 1;
        
return result;
    }
    
else
    {
        
if (!end) throw up;
        wstring result(config);
        config 
+= result.size();
        
return result;
    }
}

// name;parent;description
void ReadConfig(const wchar_t* config)
{
    DoName(Parse(config, 
false));
    DoParent(Parse(config, 
false));
    DoDescription(Parse(config, 
true));
}

是不是看hq很别扭Q好像把代码修改了之后只把事情搞得更׃Q而且qconfig对了我们也会创徏那个up变量Q就仅仅是ؓ了不 重复代码。而且q䆾代码q散发出了一些不好的味道Q因为对于Name、Parent和Description的处理方法还是不能统一QParse里面针对 end变量的处理看h也是很重复,但实际上q是无法在这栯计的前提下消除的。所以这个代码也是不好的Q充光只是比第一份代码强一点点?br />

?际上Q代码之所以要写的好,之所以不能repeat东西Q是因ؓ产品狗L要改需求,不改代码你就要死Q改代码你就要加班,所以ؓ了减修改代码的痛苦Q?我们不能repeatM信息。D个例子,有一天品狗_要把分隔W从分号ҎI格Q一下子p改两个地方了。description后面要加tagQ?q样你处理description的方法又要改了因Z是以I格l尾不是0l尾?/p>

因此针对q个片段Q我们需要把它改成这P

vector<wstring> SplitString(const wchar_t* config, wchar_t delimiter)
{
    vector
<wstring> fragments;
    
while(auto next = wcschr(config, delimiter))
    {
        fragments.push_back(wstring(config, next));
        config 
= next + 1;
    }
    fragments.push_back(wstring(config));
    
return fragments; // C++11是好!
}

void ReadConfig(const wchar_t* config)
{
    auto fragments 
= SplitString(config, L';');
    
if(fragments.size() != 3)
    {
        
throw ArgumentException(L"Illegal config string");
    }
    DoName(fragments[
0]);
    DoParent(fragments[
1]);
    DoDescription(fragments[
2]);
}

我们可以发现Q分PL';'Q在q里只出C一ơ,异常内容也只出现了一ơ,而且处理name、parent?description的代码也没有什么区别了Q检查错误也更简单了。你在这里还l你的Library增加了一个SplitString函数Q说不定在以 后什么地方就用上了,比Parseq种专门的函数要强很多倍?br />

大家可以发现Q在q里重复的东西ƈ不仅仅是复制了代码,而是׃你把 同一个信息散播在了代码的各个部分D了有很多相近的代码也散播在各个地方,而且q不是那么好通过抽成函数的方法来解决。因为在q种情况下,q你把重复 的代码抽成了Parse函数Q你把函数调用了几次实际上也{于重复了信息。因此正的Ҏ是把做事情的方法变一下,写成SplitString。这?SplitString函数q不是通过把重复的代码单的抽取成函数而做出来的?u>L重复的信息会让你的代码的l构发生本质的变?/strong>?/p>

q个问题其实也有很多变体Q?/p>

  • 不能有Magic Number。L';'出现了很多遍Q其实就是个Magic Number。所以我们要l他个名字,譬如说delimiter?/li>
  • 不要复制代码。这个应该不用我讲了?
  • 解耦要做成正交的。SplitString虽然不是直接冲着读config来写的,但是它反映了一个在其它地方也会遇到的常见的问题。如果用Parse的那个版本,昄只是看v来解决了问题而已Qƈ没有l你带来M额外的效益?/li>

信息一旦被你repeat了,你的代码׃不同E度的出现各U腐烂或者破H,上面那三条其实只是我能想到的比较常见的表现Ş式。这件事情也告诉我们Q当高手告诉你什么什么不能做的时候,得想一惌后的原因Q不然跟徏q信有什么区别?/p>

陈梓?vczh) 2014-07-15 22:44 发表评论
]]>
要学车暂停更新几个星?http://www.shnenglu.com/vczh/archive/2014/04/30/206776.html陈梓?vczh)陈梓?vczh)Wed, 30 Apr 2014 07:33:00 GMThttp://www.shnenglu.com/vczh/archive/2014/04/30/206776.htmlhttp://www.shnenglu.com/vczh/comments/206776.htmlhttp://www.shnenglu.com/vczh/archive/2014/04/30/206776.html#Feedback5http://www.shnenglu.com/vczh/comments/commentRss/206776.htmlhttp://www.shnenglu.com/vczh/services/trackbacks/206776.html

陈梓?vczh) 2014-04-30 15:33 发表评论
]]>
跟vczh看实例学~译原理——三QTinymoe与无歧义语法分析http://www.shnenglu.com/vczh/archive/2014/03/23/206298.html陈梓?vczh)陈梓?vczh)Sun, 23 Mar 2014 08:51:00 GMThttp://www.shnenglu.com/vczh/archive/2014/03/23/206298.htmlhttp://www.shnenglu.com/vczh/comments/206298.htmlhttp://www.shnenglu.com/vczh/archive/2014/03/23/206298.html#Feedback2http://www.shnenglu.com/vczh/comments/commentRss/206298.htmlhttp://www.shnenglu.com/vczh/services/trackbacks/206298.html文章中引用的代码均来?a >https://github.com/vczh/tinymoe?

 

看了前面的三文章,大家应该基本对Tinymoe的代码有一个初步的感觉了。在正确分析"print sum from 1 to 100"之前Q我们首先得分析"phrase sum from (lower bound) to (upper bound)"q样的声明。Tinymoe的函数声明又很多关于block和sentence的配|,不过q里q不打算所有细节,我会重Ҏ在如何写一个针Ҏ歧义语法的递归下降语法分析器上。所以我们这里不会涉及sentence和block的什么category和cps的配|?

 

虽然"print sum from 1 to 100"无法用无歧义的语法分析的Ҏ做出来,但是我们可以借用?phrase sum from (lower bound) to (upper bound)"的语法分析的l果Q动态构造能够分?print sum from 1 to 100"的语法分析器。这U说法看h好像很高大上Q但是其实ƈ没有什么特别难的技巧。关?构?的问题我在下一文章《跟vczh看实例学~译原理——三QTinymoe与有歧义语法分析》详l介l?

 

在我之前的博客里我曾l写q?a href="http://www.shnenglu.com/vczh/archive/2008/06/15/53373.html">如何手写语法分析?/a>》,q篇文章讲了一些简单的写递归下降语法分析器的规则Q尽很多h来信说这文章帮他们解决了很多问题,但实际上l节q不够丰富,用来对编E语a做语法分析的话,q是会觉得复杂性太高。这文章也同时作ؓ?a href="http://www.shnenglu.com/vczh/archive/2008/06/15/53373.html">如何手写语法分析?/a>》的补充。好了,我们开始进入无歧义语法分析的主题吧?

 

我们需要的W一个函数是用来读tokenq判断其内容是不是我们希望看到的东西。这个函数比较特别,所以单独拿出来讌Ӏ在词法分析里面我们已经把文件分行,每一行一个CodeToken的列表。但是由于一个函数声明独占一行,因此在这里我们只需要对每一行进行分析。我们判断这一行是否以cps、category、symbol、type、phrase、sentence或block开_如果是那Tinymoep一定是一个声明,否则是普通的代码。所以这里假设我们找C一行代码以上面的这些token作ؓ开_于是我们pq入语法分析的环节。作为整个分析器的基Q我们需要一个ConsumeToken的函敎ͼ

 

 

作ؓ一个纯_的C++11的项目,我们应该使用STL的P代器。其实在写语法分析器的时候,Zq代器的代码也比Z"token在数l里的下?的代码要单得多。这个函数所做的内容是这LQ它查看it指向的那个tokenQ如果token的类型跟tokenType描述的一P他就it++然后q回trueQ否则就是用content和ownerToken来生一个错误信息加入errors列表里,然后q回false。当Ӟ如果传进ȝ参数it本nq于endQ那自然要生一个错误。自Ӟ函数体也十分单:

 

那对于标识符和数字怎么办呢Q明gh肯定一眼就看出来,q是l检查符L的,譬如左括受右括号、冒号和关键字等。在声明里面我们是不需要太复杂的东西的Q因此我们还需要两外一个函数来输入标识W。Tinymoe事实上有两个针对标识W的语法分析函数Q第一个是d标识W,W二个不仅要d标识W还要判断是否到了行末否则报错:

 

在这里我需要强调一个重点,在写语法分析器的时候,函数的各式一定要整齐划一。Tinymoe的语法分析函数有两个格式Q分别是针对parse一行的一个部分,和parse一个文件的一些行的。ParseToEnd和ParseToFarest属于parse一行的一个部分的函数。这U函数的格式如下Q?

  1. q回g定是语法树的一个节炏V在q里我们以share_ptr<SymbolName>作ؓ标识W的节点。一个标识符可以含有多个标识WtokenQ譬如说the Chinese people、the real Tinymoe programmer什么的。因此我们可以简单地推测SymbolName里面有一个vector<CodeToken>。这个定义可以在TinymoeLexicalAnalyzer.h的最后一部分扑ֈ?
  2. 前两个参数分别是iterator&和指向末iterator。末N常?从这里开始我们不希望q个parser函数来读"Q当然这通常意味着行末。我们把"末尾"q个概念抽取出来Q在某些情况下可以得到极大的好处?
  3. 最后一个token一定是vector<CodeError>&Q用来存放过E中遇到的错误?

 

除了函数格式以外Q我们还需要所有的函数都遵循某些前|条件和后置条g。在语法分析里,如果你试囑ֈ析一个结构但是不q出C错误Q这个时候,你有可能可以q回一个语法树的节点,你也有可能什么都q回不了。于是这里就有两U情况:

  1. 你可以返回,那就证明虽然输入的代码有错误Q但是你成功的进行了错误恢复——实际上是_你觉得你自己可以猜测q䆾代码的作者实际是要表达什么意思——于是你要移动第一个iteratorQ让他指向你W一个还没读到的tokenQ以便后l的语法分析q程l箋q行下去?
  2. 你不能返回,那就证明你恢复不了,因此W一个iterator不能动。因个函数有可能只是Z试一下当前的输入是否满一个什么结构。既然他不是Q而且你恢复不出来——也是你猜作者本来也不打在q里写某个结构——因此iterator不能动,表示你什么都没有诅R?

 

当你Ҏq样的格式写了很多语法分析函C后,你会发现你可以很Ҏ用简单结构的语法分析函数Q拼凑出一个复杂的语法分析函数。但是由于Tinymoe的声明ƈ没有一个编E语a那么复杂Q所以这U嵌套结构出现的ơ数q不多,于是我们q里先蟩q关于嵌套的讨论Q等到后面具体分?函数指针cd的参?的时候自然会涉及到?

 

说了q么多,我觉得也应该上ParseToEnd和ParseToFarest的代码了。首先是ParseToEndQ?

 

我们很快可以发玎ͼ其实语法分析器里面绝大多数篇q的代码都是关于错误处理的,真正处理正确代码的部分其实很。ParseToEnd做的事情不多Q他是从it开始一直读到end的位|,把所有不是标识符的token都扔掉,然后把所有遇到的标识Wtoken都连h作ؓ一个完整的标识W。也是_ParseToEnd遇到cM"the real 100 Tinymoe programmer"的时候,他会q回"the real Tinymoe programmer"Q然后在"100"的地Ҏ一个错误?

 

ParseToFarest的逻辑差不多:

 

只是当这个函数遇?the real 100 Tinymoe programmer"的时候,会返?the real"Q然后把光标Ud?100"Q但是没有报错?

 

看了q几个基本的函数之后Q我们可以进入正题了。做语法分析器,当然q是从文法开始。跟上一文章一P我们来尝试直接构造一下文法。但是语法分析器跟词法分析器的文法的区别是,词法分析其的文法可以 "定义函数"?调用函数"?

 

首先Q我们来看symbol的文法:

SymbolName ::= <identifier> { <identifier> }

Symbol ::= "symbol" SymbolName

 

其次Q就是type的声明。type是多行的Q不q我们这里只兛_开头的一P

Type ::= "type" SymbolName [ ":" SymbolName ]

 

在这里,中括号代表可有可无,大括号代表重?ơ或以上。现在让我们来看函数的声明。函数的生命略ؓ复杂Q?

 

Function ::= ("phrase" | "sentence" | "block") { SymbolName | "(" Argument ")" } [ ":" SymbolName ]

Argument ::= ["list" | "expression" | "argument" | "assignable"] SymbolName

Argument ::= SymbolName

Argument ::= Function

 

Declaration ::= Symbol | Type | Function

 

在这里我们看到Function递归了自己,q是因ؓ函数的参数可以是另一个函数。ؓ了让q个参数调用h更加漂亮一点,你可以把参数写成函数的Ş式,譬如_

pharse (the number) is odd : odd numbers

    return the number % 2 == 1

end

 

print all (phrase (the number) is wanted) in (numbers)

    repeat with the number in all numbers

        if the number is wanted

            print the number

        end

    end

end

 

print main

    print all odd numbers in array of (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

end

 

我们l?(the number) is odd"q个判断一个数字是不是奇数的函敎ͼ起了一个别名叫"odd numbers"Q这个别名不能被调用Q但是他{h于一个只ȝ变量保存着奇数函数的函数指针。于是我们就可以把它传递给"print all (? in (?"q个函数的第一个参C。第一个参数声明成函数Q所以我们可以在print函数内直接调用这个参数指向的odd numbers函数?

 

事实上Tinymoe的SymbolName是可以包含关键字的,但是我ؓ了不让它写的太长Q于是我q单的写成了上面的那条式子。那Argument是否可以包含关键字呢Q答案当然是可以的,只是当它以list、expression、argument、assignable、phrase、sentence、block开始的时候,我们认ؓ他有额外的意义?

 

现在一个Tinymoe的声明的W一行都由Declaration来定义。当我们识别Z个正的Declaration之后Q我们就可以Ҏ分析的结果来对后面的行进行分析。譬如说symbol后面没有东西Q于是就q么完了。type后面都是成员函数Q所以我们一直找?end"为止。函数的函数体就更复杂了Q所以我们会直接跛_下一个看h像Declaration的东襎쀔—也是以symbol、type、phrase、sentence、block、cps、category开始的行。这些步骤都很简单,所以问题的重点是Q?strong>如何ҎDeclaration的文法来处理输入的字W串?

 

Z让文法可以真正的q行Q我们需要把它做成状态机。根据之前的描述Q这个状态及仍然需要有"定义函数"?执行函数"的能力。我们可以先假装他们是正则表辑ּQ然后把整个状态机d来。这个时候,"函数"本n我们把它看成是一个跟标识W无关的输入Q然后就可以得到下面的状态机Q?

 

 

q样我们的状态机暂时完成了。但是现在还不能直接把它转换成代码,因ؓ当我们遇C个输入,而我们可以选择调用函数Q而且可以用的函数q不止一个的时候,那应该怎么办呢Q答案就是要查我们的文法是不是有歧义?

 

文法的歧义是一个很有意思的问题。在我们真的实践一个编译器的时候,我们会遇CU歧义:

  1. 文法本n是有歧义的Q譬如说C++著名的A* B;的问题。当A是一个变量的时候,q是一个乘法表辑ּ。当A是一个类型的时候,q是一个变量声明语句。如果我们在语法分析的时候不知道A到底指向的是什么东西,那我们根本无从得知这一句话到底要取什么意思,于是要么q回错误Q要么两个结果一赯回。这是问法本n固有的歧义?
  2. 文法本n没有歧义Q但是在分析的过E中却无法在走每一步的时候都能够出唯一?下一个状?。譬如说C#著名的A<B>C;问题。当A是一个变量的时候,q个语句是不成立的,因ؓC#的一个语句的根节点不能是一个操作符Q这里是">"Q。当A是一个类型的时候,q是一个变量声明语句。从l果来看Q这q没有歧义,但是当我们读完A<B>的时候仍然不知道q个语句的结构到底是取哪一U。实际上B作ؓcd参数Q他也可以有B<C>q样的结构,因此q个B可以是Q意长的。也是说我们只有在">"l束之后再多d个字W才能得到正的判断。譬如说C?(1)"Q那我们知道A应该是一个模板函数。如果C是一个名字,A多半应该是一个类型。因此我们在做语法分析的时候,只能两种情况一赯虑Qƈ行处理。最后如果哪一个情况分析不下去了,q单的扔掉Q剩下的是我们所需要的?
  3. 文法本n没有歧义Q而且分析的过E中只要你每一步都往后多看最多N个tokenQ酒可以出唯一?下一个状?到底是什么。这个想必大安很熟悉,因ؓq个N是LookAhead。所谓的LL(1)、SLR(1)、LR(1)、LALR(1)什么的Q这?其实是N=1的情cN当然不一定是1Q他也可以是一个很大的数字Q譬如说8。一个文法的LookAhead是多,q是文法本n固有的属性。一个LR(2)的状态机你非要在语法分析的时候只LookAhead一个tokenQ那也会遇到W二U歧义的情况。如果C语言没有typedef的话Q那他就是一个带有LookAhead的没有歧义的语言了?

 

看一眼我们刚才写出来的文法,明显是LookAhead=0的情况,而且q左递归都没有,写v来肯定很Ҏ。那接下来我们要做的是l?函数"first set。一个函数的first setQ顾名思义是Q他的第一个token都可以是什么。SymbolName、Symbol、Type、Function都不用看了,因ؓ他们的文法第一个输入都是tokenQ那是他们的first set。最后就剩下Argument。Argument的第一个token除了list、expression、argument和assignable以外Q还有Function。因此Argument的first set是q些token加上Function的first set。如果文法有左递归的话Q也可以用类似的Ҏ做,只要我们在函数A->B->C->?>A的时候,知道A正在计算于是q回I集可以了。当Ӟ只有左递归才会遇到q种情况?

 

然后我们查一下每一个状态,可以发现QQ何一个状态出ȝ所有边Q他接受的token或者函数的first set都是没有交集的。譬如Argument?状态,W一条边接受的token、第二条Ҏ受的SymbolName的first setQ和W三条边接受的Function的first setQ是没有交集的,所以我们就可以断定Q这个文法一定没有歧义。按照上ơ状态机C码的写法Q我们可以机械的写出代码了。写代码的时候,我们把每一个文法的函数Q都写成一个C++的函数。每C个状态的时候,我们看一下当前的token是什么,然后再决定走哪条辏V如果选中的边是token边,那我们就跌一个token。如果选中的边是函数边Q那我们不蟩qtokenQ{而调用那个函敎ͼ让函数自己去跳token。?a href="http://www.shnenglu.com/vczh/archive/2008/06/15/53373.html">如何手写语法分析?/a>》用的也是一LҎQ如果对q个q程不清楚的Q可以再看一遍这个文章?

 

于是我们C定义语法树的时候了。幸q的是,我们可以直接从文法上看到语法树的形状Q然后稍微做一点微调就可以了。我们把每一个函数都看成一个类Q然后用下面的规则Q?

  1. 对于A、A B、A B C{的情况Q我们{换成class里面有若q个成员变量?
  2. 对于A | B的情况,我们把它做成l承QA的语法树和B的语法树l承自一个基c,然后q个基类的指针就攑֜class里面做成员变量?
  3. 对于{ A }Q或者A { A }的情况,那个成员变量是一个vector?
  4. 对于[ A ]的情况,我们当A看,区别只是Q这个成员变量可以填nullptr?

 

对于每一个函敎ͼ要不要用shared_ptr来装则见仁见智。于是我们可以直接通过上面的文法得到我们所需要的语法树:

 

首先是SymbolNameQ?

 

其次是SymbolQ?

 

然后是TypeQ?

 

接下来是ArgumentQ?

 

最后是FunctionQ?

 

大家可以看到Q在Argument那里Q同时出ȝ三条边就l成了三个子c,都承自FunctionFragment。图中红色的部分是Tinymoe源代码里在上q的文法里出现的那部分。至于ؓ什么还有多出来的部分,其实是因里的文法是ؓ了叙q方便简化过的。至于Tinymoe关于函数声明的所有语法可以分别看下面的四个github的wiki pageQ?

https://github.com/vczh/tinymoe/wiki/Phrases,-Sentences-and-Blocks

https://github.com/vczh/tinymoe/wiki/Manipulating-Functions

https://github.com/vczh/tinymoe/wiki/Category

https://github.com/vczh/tinymoe/wiki/State-and-Continuation

 

在本章的末尾Q我向大家展示Tinymoe关于函数声明的那一个Parse函数。文章已l把所有关键的知识炚w讲了Q具体怎么做大家可以上https://github.com/vczh/tinymoe 阅读源代码来学习?

 

首先是我们的函数_

 

回想一下我们之前讲到的关于语法分析函数的格式:

  1. q回g定是语法树的一个节炏V在q里我们以share_ptr<SymbolName>作ؓ标识W的节点。一个标识符可以含有多个标识WtokenQ譬如说the Chinese people、the real Tinymoe programmer什么的。因此我们可以简单地推测SymbolName里面有一个vector<CodeToken>。这个定义可以在TinymoeLexicalAnalyzer.h的最后一部分扑ֈ?
  2. 前两个参数分别是iterator&和指向末iterator。末N常?从这里开始我们不希望q个parser函数来读"Q当然这通常意味着行末。我们把"末尾"q个概念抽取出来Q在某些情况下可以得到极大的好处?
  3. 最后一个token一定是vector<CodeError>&Q用来存放过E中遇到的错误?

 

我们可以清楚地看到这个函数满上文提出来的三个要求。剩下来的参数有两个Q第一个是declQ如果不为空那代表调用函数的人已l帮你吧语法树给new出来了,你应该直接用它。领一个参数ownerToken则是Z产生语法错误使用的。然后我们看代码Q?

 

W一步,我们判断输入是否为空Q然后根据需要报错:

 

W二步,ҎW一个token来确定函数的形式是phrase、sentenceq是blockQƈ记录在成员变量type里:

 

W三步是一个@环,我们Ҏ当前的tokenQ还C记得之前说过Q要先看一下token是什么,然后再决定走哪条边?Q来军_我们接下来要分析的,是ArgumentFragment的两个子c(分别是VariableArgumentFragment和FunctionArgumentFragmentQ,q是普通的函数名的一部分Q还是说函数已经l束了,遇到了一个冒P因此开始分析别名:

 

最后就不脓了,是检查格式是否满义的一些代码,譬如说block的函数名必须由参数开始啦Q或者phrase的参C能是argument和assignable{?

 

q篇文章到此结束了Q希望大家在看了q片文章之后Q配合wiki关于语法的全面描qͼ已经知道如何对Tinymoe的声明部分进行语法分析。紧接着是下一文章——Tinymoe与带歧义语法分析了,会让大家明白Q想对诸?print sum from 1 to 100"q样的代码做语法分析Q也不需要多复杂的手D就可以做出来?/p>

陈梓?vczh) 2014-03-23 16:51 发表评论
]]>
跟vczh看实例学~译原理——二Q实现Tinymoe的词法分?/title><link>http://www.shnenglu.com/vczh/archive/2014/03/02/206014.html</link><dc:creator>陈梓?vczh)</dc:creator><author>陈梓?vczh)</author><pubDate>Sun, 02 Mar 2014 15:44:00 GMT</pubDate><guid>http://www.shnenglu.com/vczh/archive/2014/03/02/206014.html</guid><wfw:comment>http://www.shnenglu.com/vczh/comments/206014.html</wfw:comment><comments>http://www.shnenglu.com/vczh/archive/2014/03/02/206014.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.shnenglu.com/vczh/comments/commentRss/206014.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/vczh/services/trackbacks/206014.html</trackback:ping><description><![CDATA[<p>文章中引用的代码均来?a >https://github.com/vczh/tinymoe</a>? </p><p>  </p><p>实现Tinymoe的第一步自然是一个词法分析器。词法分析其所作的事情很简单,是把一份代码分割成若干个tokenQ记录下他们所在文件的位置Q以及丢掉不必要的信息。但是Tinymoe是一个按行分割的语言Q自然token列表也就是二l的Q第一l是行,W二l是每一行的token。在l箋讲词法分析器之前Q先看看Tinymoe包含多少tokenQ? </p><ul><li><div style="text-align: justify"><span style="font-size:10pt">W号Q?????amp;????、\??lt;?gt;?lt;=?gt;=??lt;> </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">关键字:module、using、phrase、sentence、block、symbol、type、cps、category、expression、argument、assignable、list、end、and、or、not </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">数字Q?23?56.789 </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">字符Ԍ"abc\r\ndef" </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">标识W:tinymoe </span></div></li><li><div style="text-align: justify"><span style="font-size:10pt">注释Q?- this is a comment </span></div></li></ul><p>  </p><p>Tinymoe对于token有一个特D的规定Q就是字W串和注释都是单行的。因此如果一个字W串在没有结束之前就遇到了换行,那么q种写法定义Z遇到了一个没有右双引L字符Ԍ需要报个错Q然后下一行就不是q个字符串的内容了? </p><p>  </p><p>一个词法分析器所需要做的事情,是把一个字W串分解成描q此法的数据l构。既然上面已l说到Tinymoe的token列表是二l的Q因此数据结构肯定会体现q个观点。Tinymoe的词法分析器代码可以在这里找刎ͼ<a >https://github.com/vczh/tinymoe/blob/master/Development/Source/Compiler/TinymoeLexicalAnalyzer.h</a>? </p><p>  </p><p>首先是tokenQ? </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh1.png" alt=""/> </p><p>CodeTokenType是一个枚丄型,标记一个token的类型。这个类型比较细化,每一个关键字有自qcdQ每一个符号也有自qcdQ剩下的按种cL分。我们可以看到token需要记录的最关键的东西只有三个:cd、内容和代码位置。在token记录代码位置是十分重要的Q正地记录代码位置可以让你能够报告带位|的错误、从语法树的节点q原成代码位|、甚臛_调试的时候可以把指o也换成位|? </p><p>  </p><p>q里需要提到的是,string_t是一个typedefQ具体的声明可以在这里看刎ͼ<a >https://github.com/vczh/tinymoe/blob/master/Development/Source/TinymoeSTL.h</a>。Tinymoe是完全由标准的C++11和STL写成的,但是Z适应不同情况的需要,Tinymoe分ؓ依赖code page的版本和Unicode版本。如果编译Tinymoe代码的时候声明了全局的宏UNICODE_TINYMOE的话Q那Tinymoe所有的字符处理用wchar_tQ否则用char。char的类型和Tinymoe~译器在q行的时候操作系l当前的code page是绑定的。所以这里会有类似string_t啊、ifstream_t啊、char_t{类型,会在不同的编译选项的媄响下指向不同的STLcd或者原生的C++cd。github上的VC++2013工程使用的是wchar_t的版本,所以string_t是std::wstring? </p><p>  </p><p>Tinymoe的词法分析器除了token的类型以外,肯定q需要定义整个文件结构在词法分析后的l果Q? </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh2.png" alt=""/> </p><p>q个数据l构体现?Tinymoe的token列表是二l的"的这个观炏V一个文件会被词法分析器处理成一个shared_ptr<CodeFIle>对象QCodeFile::lines记录了所有非I的行,CodeLine::tokens记录了该行的所有token? </p><p>  </p><p>现在让我们来看词法分析的具体q程。关于如何从正则表达式构造词法分析器可以在这里(<a href="http://www.shnenglu.com/vczh/archive/2008/05/22/50763.html">http://www.shnenglu.com/vczh/archive/2008/05/22/50763.html</a>Q看刎ͼ不过我们今天要讲一讲如何h肉构造词法分析器。方法其实是一LQ首先h肉构造状态机Q然后把用状态机分析输入的字W串的代码抄q来可以了。但是很有Z解耦得那么开Q因样写很容易看不懂Q其ơ有可能会遇C些极端情冉|你无法用Ua的正则表辑ּ来分词的Q譬如说C++的raw string literalQR"tinymoe(q里的字W串没有转义)tinymoe"。一个用【R"<一些字W?gt;(】开始的字符串只能由?<同样的字W?gt;"】来l束Q要利分析q种情况Q只能通过在状态机里面做hack才能解决。这是Z么我们h肉构造词法分析器的时候,会把状态和动作都؜在一起写Q因样便于处理特D情c? </p><p>  </p><p>不过q好的是QTinymoeq没有这U情况发生。所以我们可以直接从状态机入手。ؓ了简单v见,我在下面的状态机中去掉所有不??的符受首先,我们需要一个v始状态和一个结束状态: </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh3.png" alt=""/> </p><p>  </p><p>首先我们d整数和标识符q去Q? </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh4.png" alt=""/> </p><p>  </p><p>其次是加减和点Q? </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh5.png" alt=""/> </p><p>  </p><p>最后把字符串和注释补全Q? </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh6.png" alt=""/> </p><p>  </p><p>q样状态机已l完成了。读q编译原理的可能会问Qؓ什么终l状态都是由虚线而不是带有输入的实现指向的?因ؓ虚线在这里有Ҏ的意义,也就是说它不能移动输入的字符串的指针Q而且他还要负责添加一个token。当状态蟩到End之后Q那他就会变成StartQ所?strong>实际上Start和End是同一个状?/strong>。这个状态机也不是输入什么字W都能蟩转到下一个状态的。所?strong>当你发现输入的字W让你无路可走的时候,你就是遇C一个词法错?/strong>? </p><p>  </p><p>q样我们的设计就完成了Q接下来是如何用C++来实现它了。ؓ了让代码更容易阅读,我们应该lStart?-9q么多状态v名字Q做法如下: </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh7.png" alt=""/> </p><p>在这里类似状?q样的状态被我省略掉了,因ؓq个状态唯一的出路就是虚U,所以蟩到这个状态意味着你要立刻执行虚线Q也是说你不需要做"跛_q个状?q个动作。因此它不需要有一个名字? </p><p>  </p><p>然后你只要按照下面的做法译q个状态机可以了Q? </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh8.png" alt=""/> </p><p>  </p><p>只要写到q里Q那么我们就初步完成了词法分析器了。其实Q何系l的主要功能都是相对Ҏ实现的,往往是次要的功能才需要花费大量的_֊来完成,而且q很Ҏ出错。在q里"ơ要的功?是——记录token的行列号Q还有维护CodeFile::lines避免I的出玎ͼ </p><p>  </p><p>管我已l做q了那么多次词法分析器,但是我仍然无法一气呵成写对,仍然会出一些bug。面对编译器q种U计程序,debug的最好方法就是写单元试。不q对于不熟悉单元试的h来讲可能很难一下子惛_要做什么测试,在这里我可以把我lTinymoe谢的一部分单元试在这里脓一下? </p><p>  </p><p>W一个,当然是传说中?Hello, world!"试了: </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh9.png" alt=""/> </p><p>  </p><p>TEST_CASE和TEST_ASSERTQ这里暂时没有直接用到TEST_ASSERTQ是我ؓ了开发Tinymoe随手撸的一个宏Q可以在Tinymoe的代码里看到。ؓ了检查我们有没有_心大意Q我们有必要查词法分析器的Q何一个输出的数据Q譬如每一行有多少tokenQ譬如每一个token的行号列好内容和cd。我Z让这些枯燥的试用例Ҏ看懂Q在q个文gQ?a ? </p><p>  </p><p>W二个测试用例针对的是整数和点的输出和报错上,重点在于查每一个token的列h不是正确的计了出来Q? </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh10.png" alt=""/> </p><p>  </p><p>W三个测试用例的重点主要?W号和—注释的分析Q? </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh11.png" alt=""/> </p><p>  </p><p>W四个测试用例则是测试字W串的escaping和在面对换行的时候是否正的处理Q之前提到字W串不能换行Q遇C个突然的换行会被看成缺双引号Q: </p><p><img src="http://www.shnenglu.com/images/cppblog_com/vczh/030214_1544_vczh12.png" alt=""/> </p><p>  </p><p>鉴于词法分析本来内容也不多,所以这文章也不会太长。相信有前途的E序员也会在q里得到一些编译原理以外的知识。下一文章将会描qTinymoe的函数头的语法分析部分,会描述一个编译器的不带歧义的语法分析是如何h肉出来的。敬h待?/p><img src ="http://www.shnenglu.com/vczh/aggbug/206014.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-03-02 23:44 <a href="http://www.shnenglu.com/vczh/archive/2014/03/02/206014.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>随手怺一个可以写递归lambda的Y函数http://www.shnenglu.com/vczh/archive/2014/03/01/205998.html陈梓?vczh)陈梓?vczh)Fri, 28 Feb 2014 16:34:00 GMThttp://www.shnenglu.com/vczh/archive/2014/03/01/205998.htmlhttp://www.shnenglu.com/vczh/comments/205998.htmlhttp://www.shnenglu.com/vczh/archive/2014/03/01/205998.html#Feedback3http://www.shnenglu.com/vczh/comments/commentRss/205998.htmlhttp://www.shnenglu.com/vczh/services/trackbacks/205998.html最q学习C++11的variadic template argumentQ终于可以摆qfpmacro模板来复制一大堆代码的做法了Q好开心。这个例子的main函数用lambda写了一个斐波那契数列的递归计算函数。跟以往不同的是Q在Y函数的帮助下Q这个lambda表达是可以成功看到自己,然后递归调用。当然这仍然需要用普通的C++递归来实玎ͼq不是?calculus那个高大上的Y Combinator?

 

#include <functional>

#include <memory>

#include <iostream>

#include <string>

 

using namespace std;

 

template<typename TResult, typename ...TArgs>

class YBuilder

{

private:

    function<TResult(function<TResult(TArgs...)>, TArgs...)> partialLambda;

 

public:

    YBuilder(function<TResult(function<TResult(TArgs...)>, TArgs...)> _partialLambda)

        :partialLambda(_partialLambda)

    {

    }

 

    TResult operator()(TArgs ...args)const

    {

        return partialLambda(

            [this](TArgs ...args)

            {

                return this->operator()(args...);

            }, args...);

    }

};

 

template<typename TMethod>

struct PartialLambdaTypeRetriver

{

    typedef void FunctionType;

    typedef void LambdaType;

    typedef void YBuilderType;

};

 

template<typename TClass, typename TResult, typename ...TArgs>

struct PartialLambdaTypeRetriver<TResult(__thiscall TClass::*)(function<TResult(TArgs...)>, TArgs...)>

{

    typedef TResult FunctionType(TArgs...);

    typedef TResult LambdaType(function<TResult(TArgs...)>, TArgs...);

    typedef YBuilder<TResult, TArgs...> YBuilderType;

};

 

template<typename TClass, typename TResult, typename ...TArgs>

struct PartialLambdaTypeRetriver<TResult(__thiscall TClass::*)(function<TResult(TArgs...)>, TArgs...)const>

{

    typedef TResult FunctionType(TArgs...);

    typedef TResult LambdaType(function<TResult(TArgs...)>, TArgs...);

    typedef YBuilder<TResult, TArgs...> YBuilderType;

};

 

template<typename TLambda>

function<typename PartialLambdaTypeRetriver<decltype(&TLambda::operator())>::FunctionType> Y(TLambda partialLambda)

{

    return typename PartialLambdaTypeRetriver<decltype(&TLambda::operator())>::YBuilderType(partialLambda);

}

 

int _tmain(int argc, _TCHAR* argv[])

{

    auto fib = Y([](function<int(int)> self, int index)

    {

        return index<2

            ?1

            :self(index-1)+self(index-2);

    });

 

    for (int i = 0; i < 10; i++)

    {

        cout << fib(i) << " ";

    }

    cout << endl;

}



陈梓?vczh) 2014-03-01 00:34 发表评论
]]>
跟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>11</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 发表评论
]]>
2013q终ȝhttp://www.shnenglu.com/vczh/archive/2014/01/04/205169.html陈梓?vczh)陈梓?vczh)Sat, 04 Jan 2014 13:52:00 GMThttp://www.shnenglu.com/vczh/archive/2014/01/04/205169.htmlhttp://www.shnenglu.com/vczh/comments/205169.htmlhttp://www.shnenglu.com/vczh/archive/2014/01/04/205169.html#Feedback9http://www.shnenglu.com/vczh/comments/commentRss/205169.htmlhttp://www.shnenglu.com/vczh/services/trackbacks/205169.html2013q我干了两件事情。第一件是gaclibQ第二g?a >tinymoe?

 

Gaclibl于做到安全的支持C++的反、从XML加蝲H口和控件了。现在在实现的东西则是一个给gaclib用的workflow脚本,用来写一些简单的view的逻辑、定义viewmodel接口Q还有跟WPF差不多的data binding?

 

Tinymoe是我大二的时候就设计出来的东西,无奈以前对计机的理论基了解的太,以至于没法实玎ͼ直到现在才能做出来。ȝ来说tinymoe是一个模仿英语语法的严肃的编E语a——也是说它是不ZNLP的,语法是严格的Q写错一个单词也会编译不q。因此所有的函数都要写成短语Q包括控制流语句也是。所以我想了一惻I能不能让分支、@环、异常处理和异步处理{等其他语言的内|的功能在我q里都变成库Q这当然是可以的Q只要做全文的cps变换Q然后要求这些控制流函数也写成cps的风格就可以了?

 

目前的第一个想法是Q等搞好了之后先生成javascript或者C#的代码,不太惛_自己的VM了,然后出一个系列文章叫做《看实例跟大牛学~译原理》,׃q个tinymoe作ؓ例子Q来把《如何设计一门语a》gl下去,啊哈哈哈哈哈?

 

写博客是一件很隄事情。我大三开始经营这个cppblog/cnblogs的博客的时候,一天都可以写一,基本上是在记录我学到的东西和我造的轮子。现在都比较懒了Q觉得整天说自己在开发什么也没意思了Q于是想写一些别的,竟然不知如何下手Q于是就Z各种没填完的pd?

 

我觉得我学编E这13q来也是学到了不东西的Q除了纯_的api和语a的知识以外,很多Ҏ论都l我起到了十分重要的作用。一开始是面向对象Q然后是数据l构法Q然后是面向斚w~程Q然后是函数式编E,后来q接触了各种跟函数式~程有关的概念,譬如说reactive programming啊,actor啊,异步啊,continuation{等。脑子里充满了各U各LҎ论和模型之后Q现在无论写什么程序,几乎都可以拿q些东西往上套Q然后做Z个维护也很容易(前提是有q些知识Q,代码也很z的E序了?

 

工作的这四年半里Q让我学习到了文档和自动化测试的重要性,于是利用q几q我把文档和试的能力也ȝ的差不多了。现在我觉得Q技术的话工作应付v来是񔽎单,但是自己Ҏ术的热情q是促我不断的研究下去?014q应该研I的技能就是嘴炮了?/p>

陈梓?vczh) 2014-01-04 21:52 发表评论
]]>
如何设计一门语aQ十二)——设计可扩展的类?/title><link>http://www.shnenglu.com/vczh/archive/2013/11/10/204189.html</link><dc:creator>陈梓?vczh)</dc:creator><author>陈梓?vczh)</author><pubDate>Sun, 10 Nov 2013 09:06:00 GMT</pubDate><guid>http://www.shnenglu.com/vczh/archive/2013/11/10/204189.html</guid><wfw:comment>http://www.shnenglu.com/vczh/comments/204189.html</wfw:comment><comments>http://www.shnenglu.com/vczh/archive/2013/11/10/204189.html#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://www.shnenglu.com/vczh/comments/commentRss/204189.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/vczh/services/trackbacks/204189.html</trackback:ping><description><![CDATA[     摘要: 在思考怎么写这一文章的时候,我又惛_了以前讨论正交概늚事情。如果一个系l被设计成正交的Q他的功能扩展v来也可以很容易的保持质量q是没错的,但是对于每一个单独给他扩展功能的个体来说Q这个系l一炚w不好用。所以我觉得现在的语a被设计成q样也是有那么点道理的。就是设计Java的那谁,他也不是傻|那ؓ什么Java会被设计成这P我觉得这跟他刚开始想让金字塔的底层程序员也可以顺利用Java是有关系...  <a href='http://www.shnenglu.com/vczh/archive/2013/11/10/204189.html'>阅读全文</a><img src ="http://www.shnenglu.com/vczh/aggbug/204189.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> 2013-11-10 17:06 <a href="http://www.shnenglu.com/vczh/archive/2013/11/10/204189.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://www.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.id19.cn" target="_blank">þþƷž޾Ʒ</a>| <a href="http://www.yhkim.cn" target="_blank">þù</a>| <a href="http://www.gnkk.net.cn" target="_blank">þ޹˾Ʒ</a>| <a href="http://www.hhabg.com.cn" target="_blank">þ99Ʒþ</a>| <a href="http://www.shjinhuashiye.cn" target="_blank">99þ99ֻѷѾƷ</a>| <a href="http://www.rrthhz.cn" target="_blank">þþƷ</a>| <a href="http://www.qsstudio.cn" target="_blank">˾þô߽AVɫɫ </a>| <a href="http://www.xecwf.cn" target="_blank">91ɫۺϾþ</a>| <a href="http://www.chuidu.cn" target="_blank">AVþþƷɫ</a>| <a href="http://www.xiaohaa.cn" target="_blank">þþþþAVר</a>| <a href="http://www.lxbike.cn" target="_blank">ҹƷþþþþþ</a>| <a href="http://www.30ww.cn" target="_blank">þۺϸþúݺ97ɫ</a>| <a href="http://www.baojingqi88.org.cn" target="_blank">ݹ˾þ91</a>| <a href="http://www.s88w.cn" target="_blank">2021¾þþӾƷ </a>| <a href="http://www.gzfbn.cn" target="_blank">VۺVŷþ</a>| <a href="http://www.daodaoo.cn" target="_blank">þҹӰԺѹۿ</a>| <a href="http://www.dgdike.cn" target="_blank">޹þþþƷС˵</a>| <a href="http://www.ptrjmfv.cn" target="_blank">ҹƷþþþþþþ</a>| <a href="http://www.84993.com.cn" target="_blank">2021þþƷ99Ʒ </a>| <a href="http://www.ohdou.cn" target="_blank">ŷƷһƷþ</a>| <a href="http://www.opsdc8.cn" target="_blank">94þù׾Ʒ</a>| <a href="http://www.impark.cn" target="_blank">þۺϾþۺ</a>| <a href="http://www.hyattzhuzhou.cn" target="_blank">þþƷ99Ӱ </a>| <a href="http://www.583762743.cn" target="_blank">2021ھþþƷ</a>| <a href="http://www.lihd.cn" target="_blank">ŷɫ۾þþƷ</a>| <a href="http://www.ejtc.cn" target="_blank">Ʒþ</a>| <a href="http://www.ab91.cn" target="_blank">ŷպĻþþò</a>| <a href="http://www.qlswxflaw.cn" target="_blank">պþþþþ</a>| <a href="http://www.tengfangwang.cn" target="_blank">99999þþþþ</a>| <a href="http://www.todouba.cn" target="_blank">91þþƷֱ</a>| <a href="http://www.wuow.cn" target="_blank">þþøƵ</a>| <a href="http://www.javareport.cn" target="_blank">ھƷþþþӰԺձ</a>| <a href="http://www.freejavbt.cn" target="_blank">91Ʒɫ۾þ</a>| <a href="http://www.verywin.cn" target="_blank">Ʒպþ</a>| <a href="http://www.kanglue.cn" target="_blank">ݺɫþþۺϲ</a>| <a href="http://www.bethesdagroup.cn" target="_blank">þþƷѹۿ97</a>| <a href="http://www.cqmh.com.cn" target="_blank">þþƷ99Ʒ </a>| <a href="http://www.zhe9tao.cn" target="_blank">ɫۺϾþ</a>| <a href="http://www.0546bbs.cn" target="_blank">þ91ƷۺϹҳ</a>| <a href="http://www.jh942.cn" target="_blank">ݺۺϾþۺ88 </a>| <a href="http://www.loveindg.cn" target="_blank">þƵ1</a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>