一个礼拜之后我终于从成都回来了, 从今天开始更新会恢复...

一点小的改进

写lex()的时候距离我上一次写已经一个礼拜了, 所以我回顾了一下之前的代码, 发现还是有瑕疵. 比如考虑到一个较短的程序, 短到小于BUFFERSIZE(256), 这时其实我的程序是有错的, 因为此时buffer中的内容有一部分是未定义的... 所以为了防止这种情况我又添加了一个变量, 叫做num, 它代表的是目前buffer中实际有效的字符数.

只有几个地方进行了修改, 很简单 :

Lexer(std::ifstream& ifs):ifs(ifs), EndOfFile(false), idx(0), row(1), column(0){
updateBuffer();
lex();
}
    void updateBuffer(){

        // first read...
if(num == 0 && !EndOfFile){
ifs.read(buffer, BUFFERSIZE);
num = ifs.gcount();
if(ifs.eof()){
EndOfFile = true;
}
return;
}
if(idx <= LIMITSIZE || EndOfFile){
return;
} idx -= LIMITSIZE;
strncpy(buffer, buffer + LIMITSIZE, COPYLENGTH);
ifs.read(buffer + COPYLENGTH, LIMITSIZE);
num = COPYLENGTH + ifs.gcount();
if(ifs.eof()){
EndOfFile = true;
}
}
void Lexer::eatSpace(){
char ch = 0;
//change here!!!
while(idx != num && (ch = buffer[idx++])){
updateBuffer();
switch (ch){
case '\n':{
++row;
column = 0;
break;
}
case ' ':{ }
case '\t':{
++column;
break;
}
default:{
--idx;
return;
}
}
}
}
char getNextChar(){
updateBuffer();
++column;
//change here!!!
if(idx == num){
// error.
}
return buffer[idx++];
}

所以改动的地方都已经使用备注标出, 主要思路在于 :

  1. 增加了updateBuffer初始化buffer功能, 所以关于缓冲中有效字符数量(num), 是否达到文件末尾(EndOfFile)等变量的设置都会在这个函数, 也只会这个函数中进行.
  2. 源代码的结束只出现在eatSpace()中和每一次循环的开头, 所以在getNextChar()中出现只有一种情况, 就是源代码有错.

lex()的设计

最后的任务就是设计这个关键的函数lex(), 我个人想到的最清晰易懂的方式就是通过预读然后调用相应的识别函数的方式来进行词法解析.

我们从简单的几个入手 :

void Lexer::lex() {
eatSpace();
char ch;
while(idx != num) {
ch = getNextChar();
switch (ch) {
case '+':
case '-':
case '*':
case '/': {
if (getNextChar() == '=') {
list.pushBack(Token(Token::OPERATOR, ch + "=", row, column));
} else {
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
}
break;
} case '=':{
if(getNextChar() == ch){
list.pushBack(Token(Token::OPERATOR, ch + ch + "", row, column));
}else{
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
}
break;
} case '&':
case '|':
case '!':
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
break; case ';':
list.pushBack(Token(Token::SEMI, ch + "", row, column));
break; case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
list.pushBack(Token(Token::BRACKET, ch + "", row, column));
break; }
eatSpace();
}
}

然后是字符串解析函数 :

std::string Lexer::stringParse() {
char ch;
std::string temp;
bool escape = false;
while(escape || (ch = getNextChar()) != '"'){
if(escape){
switch (ch){
case 'n':
temp += '\n';
break;
case 't':
temp += '\t';
break;
case '"':
temp += '\"';
break;
case '\\':
temp += '\\';
break;
default:
//error
;
}
escape = false;
continue;
} switch (ch){
case '\\':
escape = true;
continue;
default:
temp += ch;
}
}
return temp;
}

此时的lexer() ...

void Lexer::lex() {
eatSpace();
char ch;
while(idx != num) {
ch = getNextChar();
switch (ch) {
case '+':
case '-':
case '*':
case '/': {
if (getNextChar() == '=') {
list.pushBack(Token(Token::OPERATOR, ch + "=", row, column));
} else {
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
}
break;
} case '=':{
if(getNextChar() == ch){
list.pushBack(Token(Token::OPERATOR, ch + ch + "", row, column));
}else{
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
}
break;
} case '&':
case '|':
case '!':
list.pushBack(Token(Token::OPERATOR, ch + "", row, column));
break; case ';':
list.pushBack(Token(Token::SEMI, ch + "", row, column));
break; case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
list.pushBack(Token(Token::BRACKET, ch + "", row, column));
break; case '0': {
// int or float
std::string temp("0");
if (getNextChar() == '.') {
temp += '.';
while (isDigit(ch = getNextChar())) {
temp += ch;
}
}
backtrace();
list.pushBack(Token(Token::INT, temp, row, column));
break;
} case '"':
list.pushBack(Token(Token::STRING, stringParse(), row, column)); }
eatSpace();
}
}

由于int和float有两种情况, 所以这里分开设计, 先讨论了比较简单的0开头的情况.

然后把剩下的一些在switch的default中补齐.

void Lexer::lex() {
eatSpace();
char ch;
while(idx != num) {
ch = getNextChar();
switch (ch) {
case '+':
case '-':
case '*':
case '/': {
if (getNextChar() == '=') {
list.pushBack(Token(Token::OPERATOR, ch + std::string("="), row, column));
} else {
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + std::string(""), row, column));
}
break;
} case '=':{
if(getNextChar() == ch){
list.pushBack(Token(Token::OPERATOR, std::string("") + ch + ch , row, column));
}else{
backtrace();
list.pushBack(Token(Token::OPERATOR, ch + std::string(""), row, column));
}
break;
} case '&':
case '|':
case '!':
list.pushBack(Token(Token::OPERATOR, ch + std::string(""), row, column));
break; case ';':
list.pushBack(Token(Token::SEMI, ch + std::string(""), row, column));
break; case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
list.pushBack(Token(Token::BRACKET, ch + std::string(""), row, column));
break; case '0': {
// int or float
bool isFloat = false;
std::string temp("0");
if (getNextChar() == '.') {
isFloat = true;
temp += '.';
while (isDigit(ch = getNextChar())) {
temp += ch;
}
}
backtrace();
isFloat ? list.pushBack(Token(Token::FLOAT, temp, row, column))
: list.pushBack(Token(Token::INT, temp, row, column));
break;
} case '"':
list.pushBack(Token(Token::STRING, stringParse(), row, column)); default:{
if(isDigit(ch) && ch != 0){
bool isFloat = false;
std::string temp;
temp += ch;
while (isDigit(ch = getNextChar())) {
temp += ch;
}
if (getNextChar() == '.') {
isFloat = true;
temp += '.';
while (isDigit(ch = getNextChar())) {
temp += ch;
}
}
backtrace();
isFloat ? list.pushBack(Token(Token::FLOAT, temp, row, column))
: list.pushBack(Token(Token::INT, temp, row, column));
} else if(isID(ch) && !isDigit(ch)){
std::string temp;
temp += ch;
while (isID(ch = getNextChar())) {
temp += ch;
}
backtrace();
list.pushBack(Token(Token::IDENTIFIER, temp, row, column));
} else{
//error
}
} }
eatSpace();
}
}

然后在进行了简单的测试 :

main.cpp :

#include <iostream>
#include <fstream>
#include "Font/Lexer/Lexer.h" int main() {
std::ifstream ifstream("/Users/zhangzhimin/x.txt");
Lexer lexer(ifstream);
lexer.print();
return 0;
}

x.txt

int main(){
int x = 3;
x += 3.5;
a123b = 4.3333;
"\n1\t2\\\"";
> *= +++ <
return 0;
}

结果如下

int
main
(
)
{
int
x
=
3
x
+=
3
5
a123b
=
4
3333 1 2\"
;
*=
+
+
+
return
0
;
}

除了没有加入报错, 其他的都ok了, 我目前还不太了解C++中的异常机制, 毕竟才学了不到一个月, 其他的以后再说吧, 反正词法分析就告一段落了...

Lexer的设计--下(5)的更多相关文章

  1. Lexer的设计--上(3)

    lexer的构造函数 有了上一节Token做铺垫, 可以开始设计lexer, 首先应该想到的是, 源代码是以文件流的格式传到编译器中的, 所以作为编译器的前段的第一个阶段, lexer必须负责处理输入 ...

  2. 客户端热更新框架之UI热更框架设计(下)

    上一篇笔者介绍了关于什么是热更新,为什么需要热更新的技术文章.本篇就专门针对UI框架的热更新功能实现部分展开讨论,讨论的重点是热更新如何与UI框架进行结合? 现在笔者把设计“UI热更新框架”的整体设计 ...

  3. Java进阶专题(十八) 系统缓存架构设计 (下)

    前言 上章节介绍了Redis相关知识,了解了Redis的高可用,高性能的原因.很多人认为提到缓存,就局限于Redis,其实缓存的应用不仅仅在于Redis的使用,比如还有Nginx缓存,缓存队列等等.这 ...

  4. div+css 设计下拉

    css样式 <style type="text/css"> <!-- /* www.divcss5.com CSS下拉菜单实例 */ * { margin:; p ...

  5. Lexer的设计--中(4)

    设计一个小型的内存池以及链表 上一节撸到万事俱备只欠真正的lex, 但是lex的作用是将源代码转化为Token流, 用什么保存Token? 这就涉及到我们要接触的第一个数据结构-链表, 虽然标准库中很 ...

  6. NoSql数据库使用半年后在设计上面的一些心得

    NoSql数据库这个概念听闻许久了,也陆续看到很多公司和产品都在使用,优缺点似乎都被分析的清清楚楚.但我心里一直存有一个疑惑,它的出现究竟是为了解决什么问题? 这个疑惑非常大,为此我看了很多分析文章, ...

  7. 企业SOA架构设计理论

    SOA简介 SOA(Service-Oriented Architecture,面向服务架构)是一种将信息系统模块化为服务的架构风格.拥有了服务之后,我们就可以迅速地将这些服务按不同方式重新组合,从而 ...

  8. ASP.NET SignalR 高可用设计

    在 One ASP.NET 的架构图中,微软将 WebAPI 和 SignalR 归类到 Services 类型与 MVC.Web Forms 同列为一等公民,未来的 ASP.NET 5 尽管还在be ...

  9. 安卓app设计规范整理和Android APP设计篇(转)

    随着安卓智能手机不停的更新换代.安卓手机系统越来越完美,屏幕尺寸也越来越大啦!比如最近小米的miui 6的发布和魅族手机系统的更新等等. 以小米MIUI6的安卓手机来说,MIUI6进行了全新设计,坚持 ...

随机推荐

  1. zookeeper无法启动&quot;Unable to load database on disk&quot;

    自己的虚拟机集群.一次强制关机后,发现slave2的zookeeper起不来了 http://blog.csdn.net/ashic/article/details/47088299 下午5点29:5 ...

  2. 【转】request和response的页面跳转

    跳转:request.getRequestDispatcher("p3.jsp").forward(request,response);这种方法称为转发,地址栏上的URL不会改变: ...

  3. 【前端统计图】echarts实现简单柱状图

    图片.png <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...

  4. linux使用.rpm包安装mysql

    一:下载mysql的.rpm安装包 点击链接查看下载教程:点击打开链接 二:创建目录,上传文件 创建mysql目录:# mkdir mysql 进入目录:# cd mysql 将下载好的MySQL-s ...

  5. [React Router v4] Use the React Router v4 Link Component for Navigation Between Routes

    If you’ve created several Routes within your application, you will also want to be able to navigate ...

  6. 怎么实现登录之后跳转到登录之前的页面?SpringMVC+Freemarker

    项目中,想实现一个功能. 直接访问某个需要登录的url,比如/addArticle,可能会跳转到登录页面login.html. 登录成功之后,自动跳转到/addArticle这个登录前的页面,继续登录 ...

  7. 递归(c++)(转)

    1.什么是递归函数(recursive function) 递归函数即自调用函数,在函数体内部直接或间接地自己调用自己,即函数的嵌套调用是函数本身. 例如,下面的程序为求n!: long fact(i ...

  8. markdown模式的一些语法

    Markdown是一种可以使用普通文本编辑器编写的标记语言,通过简单的标记语法,它可以使普通文本内容具有一定的格式. 简书支持程序员写技术博客的模式,Markdown具有一系列衍生版本,用于扩展Mar ...

  9. Web自动化测试(全网最给力自动化教程)

    http://www.cnblogs.com/zidonghua/p/7430083.html python+selenium自动化软件测试(第2章):WebDriver API 欢迎您来阅读和练手! ...

  10. js判断两个时间段是否有交集

    //判断两个时间是否有交集 function isDateIntersection(start1, end1, start2, end2) { var startdate1 = new Date(st ...