编译原理的龙书和虎书,各看了两章之后,¥……&……*……@%¥

好吧,既然是码农,就要从基层做起,我尝试handwritten一下C或者C的极小子集的one-pass编译器先,等有了深切的体会再去研究那些高深的形式理论也不迟。于是,我花了几天搞了简单的词法分析,还费了一包子力,凭我那捉急的智商,凑出来一个像是语法树的东西,能解析四则运算表达式。书上说手写编译器用递归下降法最合适,我想了半天也不知道咋递归下降。。刚才看了看书上的简化手写语法分析器,一共才100行不到,我只好望书兴叹了,唉,底子完全不够硬啊,差得远啊。

写了几天hardcode的一点心得:

终于有点明白为什么书上要我们把文法的BNF产生式都列出来,然后再相应的为每个产生式写实现函数了,这其实是在算法解耦。 比如我们可以测试发现,下面的递归下降语法器是不支持unary operator的,比如3*-2。如果想加入这个运算规则,我们只需在文法中新加入一条规则,然后修缮文法,最后编码:在递归下降的层次中加入一个unary()函数,下层是factor(),上层是term(),完成。如此便可以通过加新的函数来扩展支持逻辑运算,比较运算。而如果像我那样hardcode的话。。。。。。。扩展的时候翔的感觉一下子就出来了。

而且考虑到类似这样的语法特性:

a = b = c = 1;

利用递归下降法也能完美简洁的解决啊!

为什么要编译原理?因为我手写的玩意已经越来越意大利面条了,每扩充一个feature真是牵一发而动全身,自己都完全不能确定有没有给其他地方引入bug,也许最后能编译一般的C代码文件了,但是我TM都不知道为什么它能运作,当要移植到其它平台或者想复用来做个其它语言的编译器时,只能傻眼了.这正是所谓的"靠人品编程".此乃码农的标志性特征:先把功能赶完,bug可不敢说没有有的话大不了加班修修补补呗...大神们做最基础的编译器时可不敢怀着这种大无畏精神...虽然我的意大利面条杯具了,但是不错的是认识到了编译原理的重要性..

贴上代码:

标准的递归下降语法器:

 #include <stdio.h>
#include <stdlib.h> char token; /*全局标志变量*/ /*递归调用的函数原型*/
int exp( void );
int term( void );
int factor( void ); void error( void ) /*报告出错信息的函数*/
{
fprintf( stderr, "错误\n");
exit( );
} void match( char expectedToken ) /*对当前的标志进行匹配*/
{
if( token == expectedToken ) token = getchar(); /*匹配成功,获取下一个标志*/
else error(); /*匹配不成功,报告错误*/
}
void Message(void)
{
printf("================================================================\n");
printf("* 递归实现的四则运算表达式求值程序 *\n");
printf("****************************************************************\n");
printf("使用方法:请从键盘上直接输入表达式,以回车键结束.如45*(12-2)[回车]\n");
printf("*****************************************************************\n\n");
}
main()
{
int result; /*运算的结果*/
Message();
printf(" >> 请输入表达式: ");
token = getchar(); /*载入第一个符号*/ result = exp(); /*进行计算*/
if( token == '\n' ) /* 是否一行结束 */
printf( " >> 表达式的计算结果为 : %d\n", result );
else error(); /* 出现了例外的字符 */
puts("\n\n 请按任意键退出 ...\n");
getch();
return ;
} int exp( void )
{
int temp = term(); /*计算比加减运算优先级别高的部分*/
while(( token == '+' ) || ( token == '-' ))
switch( token ) {
case '+': match('+'); /*加法*/
temp += term();
break;
case '-': match('-');
temp -= term(); /*减法*/
break;
}
return temp;
} int term( void )
{
int div; /*除数*/
int temp = factor(); /*计算比乘除运算优先级别高的部分*/
while(( token == '*' ) || ( token == '/' ))
switch( token ) {
case '*': match('*'); /*乘法*/
temp *= factor();
break;
case '/': match('/'); /*除法*/
div = factor();
if( div == ) /*需要判断除数是否为0*/
{
fprintf( stderr, "除数为0.\n" );
exit();
}
temp /= div;
break;
}
return temp;
} int factor( void )
{
int temp;
if( token == '(' ) /*带有括号的运算*/
{
match( '(' );
temp = exp();
match(')');
}
else if ( isdigit( token )) /*实际的数字*/
{
ungetc( token, stdin ); /*将读入的字符退还给输入流*/
scanf( "%d", &temp ); /*读出数字*/
token = getchar(); /*读出当前的标志*/
}
else error(); /*不是括号也不是数字*/
return temp;
}

我那翔一般的代码:

 #include "StdAfx.h"
#include "SyntaxTree.h"
#include "Compiler.h" eTokenType SyntaxTree::lastTokenType = eTokenType_Invalid; int SyntaxTreeOpNode::Evaluate()
{
if (!strcmp(op, "+"))
{
return lchild->Evaluate() + rchild->Evaluate();
}
else if (!strcmp(op, "-"))
{
return lchild->Evaluate() - rchild->Evaluate();
}
else if (!strcmp(op, "*"))
{
return lchild->Evaluate() * rchild->Evaluate();
}
else if (!strcmp(op, "/"))
{
return lchild->Evaluate() / rchild->Evaluate();
}
else
{
//TODO: refactoring for no ugly if-else
assert();
return ;
}
} bool SyntaxTreeOpNode::IsThisOpPriorityHigher( const char* s )
{
return cl.opPriorityMap[op] >= cl.opPriorityMap[s];
} int SyntaxTreeLeafNode::Evaluate()
{
return value;
} int SyntaxTreeRootNode::Evaluate()
{
assert(rchild && "WTF!? This is not my tree!");
return rchild->Evaluate();
} SyntaxTree::SyntaxTree()
:root(nullptr)
{
} SyntaxTree::~SyntaxTree()
{
ResetTree();
} bool SyntaxTree::ConstructTree(int len)
{
const char* expstart = cl.sentence + cl.sentence_curpos;
assert(expstart[] != );
char op[MAX_IDENT_LEN];
eTokenType type;
SyntaxTreeNode* curNode = root;
std::vector<int> vecNums;
std::vector<SyntaxTreeOpNode*> vecFlatNodes; //1.首先构建运算符的树结构
while (cl.sentence_curpos < len)
{
type = cl.ScanWord(op);
if (type == eTokenType_Operator)
{
auto iter = cl.opPriorityMap.find(op);
assert(iter != cl.opPriorityMap.end() && "Invalid op!");
SyntaxTreeOpNode* node = new SyntaxTreeOpNode;
strcpy_s(node->op, ARRAYSIZE(node->op), op); //unary op process
bool bUnary = op[]== && (op[]=='+' || op[]=='-');
if (lastTokenType==eTokenType_Operator || lastTokenType==eTokenType_LBracket && bUnary)
{
vecNums.push_back();
curNode->rchild = node;
node->parent = curNode;
}
else if (curNode->IsThisOpPriorityHigher(op))
{
assert(node->parent == nullptr);
curNode->parent = node;
node->lchild = curNode;
//降低root高度
root->rchild = node;
node->parent = root;
}
else
{
curNode->rchild = node;
node->parent = curNode;
}
curNode = node;
vecFlatNodes.push_back(node);
}
else if (type == eTokenType_ConstantNumber)
{
vecNums.push_back(atoi(op));
}
else if (type == eTokenType_LBracket)
{
int substr_len = len - ;
//must find a matching r-bracket!
bool bFind = false;
while (substr_len >= cl.sentence_curpos)
{
if(cl.sentence[substr_len] == ')')
{
bFind = true;
break;
}
--substr_len;
}
if (bFind)
{
//对于括号,我们利用递归来求值...
SyntaxTree tmpTree;
tmpTree.ResetTree();
if(tmpTree.ConstructTree(substr_len))
vecNums.push_back(tmpTree.Evaluate());
else
return false; assert(cl.sentence[cl.sentence_curpos] == ')' && "Can't be true!...");
++cl.sentence_curpos;
}
else
{
LOG_ERR(eErr_NotMatchBracket);
return false;
}
type = eTokenType_ConstantNumber;
}
else
{
LOG_ERR(eErr_InvalidExpression);
return false;
}
lastTokenType = type;
} //2.然后为每个运算符插入叶节点
if (root->rchild == nullptr)
{
LOG_ERR(eErr_InvalidExpression);
return false;
} size_t leaf = , totalLeaf = vecNums.size();
for (size_t i=; i<vecFlatNodes.size(); ++i)
{
SyntaxTreeOpNode* node = vecFlatNodes[i];
if (!node->lchild)
{
if (leaf < totalLeaf)
{
SyntaxTreeLeafNode* leafNode = new SyntaxTreeLeafNode;
leafNode->value = vecNums[leaf];
node->lchild = leafNode;
leafNode->parent = node;
++leaf;
}
else
{
LOG_ERR(eErr_InvalidExpression);
return false;
}
}
if (!node->rchild)
{
if (leaf < totalLeaf)
{
SyntaxTreeLeafNode* leafNode = new SyntaxTreeLeafNode;
leafNode->value = vecNums[leaf];
node->rchild = leafNode;
leafNode->parent = node;
++leaf;
}
else
{
LOG_ERR(eErr_InvalidExpression);
return false;
}
}
} return true;
} void SyntaxTree::ResetTree()
{
SAFE_DELETE(root);
root = new SyntaxTreeRootNode;
} int SyntaxTree::Evaluate()
{
return root->Evaluate();
}

最后,无意中看到了这哥们摆弄的玩意,貌似也比我好不到哪去。。。。。。哇哈哈哈,可悲的咱码农啊

http://blog.csdn.net/zhouzxi/article/details/7897301

果然还是SB了的更多相关文章

  1. SB中设置UITextField 无边框,真机上输入汉字聚焦时,文字 下沉

    解决方案:sb中一定要设置有边框,然后在代码里设置成无边框 然后正常了. 参考:https://segmentfault.com/q/1010000007244564/a-10200000073481 ...

  2. SB Admin 2 学习笔记1

    需要掌握能够搭建起一个 dashboard 的能力, 因为很少有运维开发团队有专职的前端, bootstrap 也要讲个基本法. SB Admin 2, 一个免费的 bootstrap theme, ...

  3. 再牛逼的梦想,也抵不住SB似的坚持

    说起梦想,哪都是好几年前的事了.自从毕业之后,梦想不知道去哪了.可能一次次的失败,找不到了梦想的方向了吧! 自从毕业去了深圳,为了能够在这个城市安稳下来,白天正常上班晚上在街上摆地摊给人下载音乐和电影 ...

  4. 解决iphone5,5s有锁版(AU,SB,S版等等)ios7越狱后+86、FT、IM等一切问题

    最近无聊,给大家发一个关于完美解决iphone5,5c.5s有锁版本机号码.+86.短信.facetime.imessage等问题.是ios7系统哦!(本人亲测iphone5 SB版 双模卡解锁)相当 ...

  5. sb 讲解 (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]

    代码:(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]] 输出sb. 分段解析: 首先解析s: (! ...

  6. 流输入练习——寻找Sb.VI codevs 3096

    题目描述 Description 已知某开放授权人员名叫Serb,由于经常修改各种数据,因此开发人员们都喊他SB.现在他和许多人一起过飞机安检,排成了一长队列,请问SB.是否在队列中. 输入描述 In ...

  7. CTF---隐写术入门第一题 SB!SB!SB!

    SB!SB!SB!分值:20 来源: 西普学院 难度:中 参与人数:4913人 Get Flag:1541人 答题人数:1577人 解题通过率:98% LSB 解题链接: http://ctf5.sh ...

  8. 当PsychicBoom_发觉自己是个大SB的时候……

    这些题都是没ac调了好久发现是sb错误的题--. 想清楚再写题!!! 2019.4.18 洛谷P5155 [USACO18DEC]Balance Beam 转移方程\((a[l[i]]*(r[i]-i ...

  9. It is difficult to the point of impossiblity for sb to image a time when ...

    对sb而言很难想象一段..的时光.

  10. SB!SB!SB!

    Topic Link http://ctf5.shiyanbar.com/stega/ste.png SB!SB!SB! 其实很简单,可别真的变成 SB! 1)根据链接提示,直接用stegsolve ...

随机推荐

  1. effective c++:尽量替换define,确保对象使用前初始化

    #define ASPECT_RATIO 1.653 名为ASPECT_RATIO的值在预编译阶段被替换成1.653,如果在这个常量上出现编译错误,我们可能会困惑1.653的值是什么意思,于是将因为跟 ...

  2. Android Activity学习笔记(一)

    Activity为Android应用的四大组件之一,提供界面来与用户完成交互等操作.其中Activity的生命周期的知识这里做个笔记. Activity的生命周期由以下几个部分组成: 1.onCrea ...

  3. 20160512关于mac安装caffe的记录

    记得2015年在mac系统上安装过一次caffe,非常顺利,但是最近群里许多同学反映mac安装caffe出现了各种问题,同时我也在帮助别人安装caffe的时候也遇到了一些坑,不再像以前这么顺利了.估计 ...

  4. cocos2d-html5在cocos2d-x里面打包编译

    main.cpp打开USE_WIN32_CONSOLE输出 #include "main.h" #include "AppDelegate.h" #includ ...

  5. tmux的使用方法和个性化配置

    介绍 tmux是一个优秀的终端复用软件,即使非正常掉线,也能保证当前的任务运行,这一点对于远程SSH访问特别有用,网络不好的情况下仍然能保证工作现场不丢失!此外,tmux完全使用键盘控制窗口,实现窗口 ...

  6. C++11无限制的unions

    [C++11无限制的unions] 在标准 C++ 中,并非任意的类型都能做为 union 的成员.比方说,带有 non-trivial 构造函数的类型就不能是 union 的成员.在新的标准里,移除 ...

  7. oracle学习 十一 包+复合类型+自定义异常(持续更新)

    在这里讲一下包的概念, 二话不说上个例子 包头: create or replace package pck_test is procedure proc_report_salary(name nva ...

  8. MVC的System.Web.Mvc.ViewPage小结

    Inherits="System.Web.Mvc.ViewPage<dynamic>这一句最好是自己手动修改,如果是维护用户数据,用户对象名是User,改成Inherits=&q ...

  9. 功能强大支持64位操作系统的转Flash软件(doc转swf):Print2Flash

    Print2Flash是一个虚拟打印机类的文档转换软件,因此只要是可打印的文档,都可以轻松转换为Flash文件,即SWF动画,特别是用于转换PDF.Word.Excel.PowerPoint等文档为S ...

  10. TT付款方式、前TT和后TT、LC信用证+TT付款方式

    TT付款方式是以外汇现金方式结算,由您的客户将款项汇至贵公司指定的外汇银行账号内,可以要求货到后一定期限内汇款. .T/T属于商业信用,也就是说付款的最终决定权在于客户.T/T分预付,即期和远期.现在 ...