果然还是SB了
编译原理的龙书和虎书,各看了两章之后,¥……&……*……@%¥
好吧,既然是码农,就要从基层做起,我尝试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了的更多相关文章
- SB中设置UITextField 无边框,真机上输入汉字聚焦时,文字 下沉
解决方案:sb中一定要设置有边框,然后在代码里设置成无边框 然后正常了. 参考:https://segmentfault.com/q/1010000007244564/a-10200000073481 ...
- SB Admin 2 学习笔记1
需要掌握能够搭建起一个 dashboard 的能力, 因为很少有运维开发团队有专职的前端, bootstrap 也要讲个基本法. SB Admin 2, 一个免费的 bootstrap theme, ...
- 再牛逼的梦想,也抵不住SB似的坚持
说起梦想,哪都是好几年前的事了.自从毕业之后,梦想不知道去哪了.可能一次次的失败,找不到了梦想的方向了吧! 自从毕业去了深圳,为了能够在这个城市安稳下来,白天正常上班晚上在街上摆地摊给人下载音乐和电影 ...
- 解决iphone5,5s有锁版(AU,SB,S版等等)ios7越狱后+86、FT、IM等一切问题
最近无聊,给大家发一个关于完美解决iphone5,5c.5s有锁版本机号码.+86.短信.facetime.imessage等问题.是ios7系统哦!(本人亲测iphone5 SB版 双模卡解锁)相当 ...
- sb 讲解 (!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
代码:(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]] 输出sb. 分段解析: 首先解析s: (! ...
- 流输入练习——寻找Sb.VI codevs 3096
题目描述 Description 已知某开放授权人员名叫Serb,由于经常修改各种数据,因此开发人员们都喊他SB.现在他和许多人一起过飞机安检,排成了一长队列,请问SB.是否在队列中. 输入描述 In ...
- CTF---隐写术入门第一题 SB!SB!SB!
SB!SB!SB!分值:20 来源: 西普学院 难度:中 参与人数:4913人 Get Flag:1541人 答题人数:1577人 解题通过率:98% LSB 解题链接: http://ctf5.sh ...
- 当PsychicBoom_发觉自己是个大SB的时候……
这些题都是没ac调了好久发现是sb错误的题--. 想清楚再写题!!! 2019.4.18 洛谷P5155 [USACO18DEC]Balance Beam 转移方程\((a[l[i]]*(r[i]-i ...
- It is difficult to the point of impossiblity for sb to image a time when ...
对sb而言很难想象一段..的时光.
- SB!SB!SB!
Topic Link http://ctf5.shiyanbar.com/stega/ste.png SB!SB!SB! 其实很简单,可别真的变成 SB! 1)根据链接提示,直接用stegsolve ...
随机推荐
- IE浏览器 json异常
当使用json数据结构时,如果对象数组最后一个元素后面依然跟一个“,”,在非IE浏览器下运行正常,但是,在IE浏览器上,则会报错. 如果使用for循环遍历对象数组时,由于后面多了一个分割符" ...
- linux下设置SSH无密码登陆
SSH配置 主机cloudgis22.edu.cn:192.168.3.21 主机cloudgis33.edu.cn:192.168.2.174 假设需要配置主机A无密码登录主机A,主机B,先确保所有 ...
- js 多物体运动
<!doctype html> <html> <head> <meta charset = "utf-8"> <title&g ...
- 子元素过滤器nth-child解释
jQuery中的子元素过滤器nth-child是指:选取每个父元素下的第index个子元素或者奇偶元素(index从1算起) 这里有几点要注意: 1. index 从1开始算 2. 过滤器filter ...
- DCPcrypt
delphi dcp component TDCP_md5 md5 String; md5 stream;流文件 procedure TForm1.Button1Click(Sender: TObje ...
- Encapsulating Data
[Encapsulating Data] The synthesized methods follow specific naming conventions: The method used to ...
- JPA多对多@manytomany注解配置实例
维护端注解 @ManyToMany (cascade = CascadeType.REFRESH) @JoinTable (//关联表 name = "student_teacher&quo ...
- 那些不被关注但很重要的html标签
1.meta标签: <meta> 元素可提供有关页面的元信息(meta-information),比如针对搜索引擎和更新频度的描述和关键词. <meta> 标签位于文档的头部, ...
- 创建类模式(四):原型(Prototype)
定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象. 原型模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建 ...
- JavaScript一些不常用的写法
如何写JavaScript才能逼格更高呢?怎样才能组织JavaScript才能让别人一眼看出你不简单呢?是否很期待别人在看完你的代码之后感叹一句“原来还可以这样写”呢?下面列出一些在JavaScrip ...