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

好吧,既然是码农,就要从基层做起,我尝试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. 实现带有getMin的栈

    题目 实现一个特殊的栈,在实现栈的基础上,再实现返回栈中最小的元素的操作. 要求 pop.push.getMin的时间复杂度是O(1) 可以使用现成的栈类型 思路 如下图所示,在栈结构中,每次pop的 ...

  2. EasyUI DataGrid 窗口大小自适用--------------未测试

    EasyUI 新版本里添加了 fit 属性,不需要老版本的那么复杂,重新load DataGrid.但是昨天用的时间发现只有一个DataGrid的时候用fit:true 很好使,但是如果有其它元素,如 ...

  3. 【转】eclipse.ini内存设置

    -vmargs -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M 这里有几个问题:1. 各个参数的含义什么?2. 为什么有的机器我将-Xm ...

  4. [翻译]创建ASP.NET WebApi RESTful 服务(7)

    实现资源分页 本章我们将介绍几种不同的结果集分页方式,实现手工分页,然后将Response通过两个不同的方式进行格式化(通过Response的Envelop元数据或header). 大家都知道一次查询 ...

  5. xml velocity模板

    . <?xml version="1.0" encoding="GBK"?> <PACKET type="REQUEST" ...

  6. Chocolatey的安装与使用

    @(编程) 前言 在 Linux 下,大家喜欢用 apt-get 来安装应用程序,如今在 windows 下,大家可以使用 Chocolatey 来快速下载搭建一个开发环境. Chocolatey 的 ...

  7. 什么是IntelAMT

    IntelAMT 全称为INTEL主动管理技术,该技术允许IT经理们远程管理和修复联网的计算机系统,而且实施过程是对于服务对象完全透明的,从而节省了用户的时间和计 算机维护成本.释放出来的iAMT构架 ...

  8. Spring4.0+Hibernate4.0+Struts2.3整合包括增删改查案例,解决整合中出现的异常

    源码下载:http://download.csdn.net/detail/cmcc_1234/7034775 ======================Application.xml======== ...

  9. C#扫描仪编程、条形码识别编程资料

    扫描仪编程资料:http://www.cnblogs.com/wubh/archive/2011/11/07/2239178.html 图片条形码识别资料:http://www.codeproject ...

  10. DIV 布局 左中右

    <style type="text/css">body{ margin:0; padding:0;}.Header{ height:100px; background: ...