lexer的构造函数

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

private:
static const size_t BUFFERSIZE = 256;
static const size_t LIMITSIZE = 250;
static const size_t COPYLENGTH = BUFFERSIZE - LIMITSIZE; std::ifstream& ifs;
char buffer[BUFFERSIZE];
size_t idx; size_t row;
size_t column;
public:
Lexer(std::ifstream& ifs):ifs(ifs), idx(0), row(1), column(0){
ifs.read(buffer, BUFFERSIZE);
lex();
}

然后这里有一点要注意的是, 我设计了一个buffer专门用来缓存读取的char, 这么做的原因有如下几个 :

  1. 考虑到读取过程可能会使用前瞰来进行准确判断, 使用缓存buffer可以让我们方便地获取上一个读取的字符.
  2. 一次读取一个字符效率太低, 一次多个读取有助于提高效率.

这里的row和column用来记录当前分析到的位置, 在编译器发现用户的源代码错误的时候可以给用户一个相对准确的定位. 所以昨天的Token也需要做相应的修改.

// part of the Token class, not complete!
private:
Token_Type type;
union Value{
long l;
double d;
std::string s;
}; Value value;
size_t row;
size_t column; public:
Token(Token_Type type, const std::string string, size_t row, size_t column): type(type), row(row), column(column) {
switch (type){
case INT:{
value.l = parseInt(string);
break;
}
case FLOAT:{
value.d = parseFloat(string);
break;
}

然后我们再来看下面几个工具函数.

    void fill(){
if(idx <= LIMITSIZE){
return;
}
idx -= LIMITSIZE;
strncpy(buffer, buffer + LIMITSIZE, COPYLENGTH);
ifs.read(buffer + COPYLENGTH, LIMITSIZE);
} char getNextChar(){
fill();
return buffer[idx++];
} void eatSpace(){
char ch = 0;
while(ch = buffer[idx++]){
fill();
switch (ch){
case '\n':{
++row;
column = 0;
break;
}
case ' ':{ }
case '\t':{
++column;
break;
}
default:{
--idx;
return;
}
}
}
}

第一个函数的作用是当缓存中的字符串几乎要分析完毕的时候, 将最后几个字符移动到开头并重新刷新缓存, 这里我这是的刷新界限是250. 后面两个很简单, 就不解释了.

在进行词法分析之前, 我准备测试一下, 粗略地一下前面的代码, 我个人认为在代码规模相对较小的时候确保代码的正确性和可靠性是一种珍惜生命的方式...

void Lexer::lex() {
eatSpace();
std::cout << getNextChar() << std::endl;
std::cout << row << std::endl;
std::cout << column << std::endl; }
int main() {
std::ifstream ifs("/Users/zhangzhimin/x.txt");
if(ifs){
Lexer x(ifs);
}
}

很幸运的是, 报错了...

In file included from /Users/zhangzhimin/ClionProjects/Fred/Font/Lexer/Token.cpp:1:
/Users/zhangzhimin/ClionProjects/Fred/Font/Lexer/Token.h:31:5: error: call to implicitly-deleted default constructor of 'Token::Value'
Token(Token_Type type, const std::string string, size_t row, size_t column): type(type), row(row), column(column) {
^
/Users/zhangzhimin/ClionProjects/Fred/Font/Lexer/Token.h:23:21: note: default constructor of 'Value' is implicitly deleted because variant field 's' has a non-trivial default constructor
std::string s;
^
/Users/zhangzhimin/ClionProjects/Fred/Font/Lexer/Token.h:31:5: error: attempt to use a deleted function
Token(Token_Type type, const std::string string, size_t row, size_t column): type(type), row(row), column(column) {
^
/Users/zhangzhimin/ClionProjects/Fred/Font/Lexer/Token.h:23:21: note: destructor of 'Value' is implicitly deleted because variant field 's' has a non-trivial destructor
std::string s;
^
2 errors generated.

log看着一大堆, 其实都在说一个错误, 就是我在Token中使用了一种联合体的方式, 也就是说将字符串放在union里面会导致编译器无法为联合体自动合成构造函数和析构函数, 关于这一块我也是第一次接触到, so上搜索了一下 :

Unrestricted unions[edit]

In C++03, there are restrictions on what types of objects can be members of a union. For example, unions cannot contain any objects that define a non-trivial constructor or destructor. C++11 lifts some of these restrictions.[3]

If a union member has a non trivial special member function, the compiler will not generate the equivalent member function for the union and it must be manually defined.

大概就是说, 在C++03的时候, 联合体如果有任何成员的构造函数或者析构函数是显式的, 那么就不行. 而在C++11中放宽了要求, 如果出现上述情况也可以, 但是对于那个显式出现的函数, 编译器将不再帮助你自动生成对应在联合体中的函数, 你必须自己定义它.

所以这个地方的错误根源在于标准库中的string有它自己的构造函数而且析构函数 这里稍微有点过于隐晦了, 所以我选择的改动方式是 : 对于所有的Token都只留下string, 将string解析为long或double等到词法分析的时候再执行. 这样可以避开这种比较隐晦的问题, 因为说实话, 这一块我也不是很了解.

改动之后的Token代码如下.

#ifndef FRED_TOKEN_H
#define FRED_TOKEN_H #include <string> class Token{
public:
enum Token_Type{
INT, // 0|([1-9][0-9]*)
FLOAT, // (0|([1-9][0-9]*)\.?[0-9]+)
STRING,
IDENTIFIER, // [a-zA-Z_][0-9a-zA-Z_]*
KEYWORD,
OPERATOR, // + - * / += -= *= /= = == ! - && ||
BRACKET, // () {} []
}; private:
Token_Type type;
std::string value;
size_t row;
size_t column; public:
Token(Token_Type type, const std::string string, size_t row, size_t column): type(type), value(string), row(row), column(column) {
if(type == IDENTIFIER && isKeyword(string)){
this->type = KEYWORD;
}
} Token(const Token&) = delete;
Token(const Token&&) = delete;
Token& operator=(const Token& rhs) = delete;
Token& operator=(const Token&& rhs) = delete;
virtual ~Token() = default; Token_Type getType() const { return type; } const std::string& getValue() const { return value; } private:
long parseHelper(const std::string&, size_t&);
long parseInt(const std::string&);
double parseFloat(const std::string&);
bool isKeyword(const std::string&);
}; #endif //FRED_TOKEN_H

再次运行, 发现已经可以了, 但是我突然想到了另外一个问题. 我们每次读取下一个字符的时候都会尝试着跟新我们的缓存区, 但是如果文件中内容读取完毕之后是不是应该考虑不再跟新缓存区呢? 对此我做了两个改变 :

  1. 添加了一个flagEndOfFile, 并在更新缓存的fill()函数中进行了相应的改变.
  2. 将fill()改名为updateBuffer()因为之前这个名字是在是太不贴切了...

所以经过一系列的更改, 目前源代码如下 :

#ifndef FRED_LEXER_H
#define FRED_LEXER_H #include <fstream> class Lexer{
private:
static const size_t BUFFERSIZE = 256;
static const size_t LIMITSIZE = 250;
static const size_t COPYLENGTH = BUFFERSIZE - LIMITSIZE; std::ifstream& ifs;
bool EndOfFile;
char buffer[BUFFERSIZE];
size_t idx; size_t row;
size_t column;
public:
Lexer(std::ifstream& ifs):ifs(ifs), EndOfFile(false), idx(0), row(1), column(0){
ifs.read(buffer, BUFFERSIZE);
lex();
} void lex(); private:
void updateBuffer(){
if(idx <= LIMITSIZE || EndOfFile){
return;
}
idx -= LIMITSIZE;
strncpy(buffer, buffer + LIMITSIZE, COPYLENGTH);
ifs.read(buffer + COPYLENGTH, LIMITSIZE);
if(ifs.eof()){
EndOfFile = true;
}
} char getNextChar(){
updateBuffer();
++column;
return buffer[idx++];
} void eatSpace();
}; #endif //FRED_LEXER_H

Lexer的设计--上(3)的更多相关文章

  1. 设计上如何避免EMC问题

    最近经常被问到EMC相关的问题,比如怎么设计才能避免EMC的问题,我想经常关注高速先生的同鞋们有机会肯定也会问到这个问题.首先这是一个系统 性的问题,不是那么好回答,尤其是对于聚焦在高速信号这个领域而 ...

  2. C#进阶系列——MEF实现设计上的“松耦合”(二)

    前言:前篇 C#进阶系列——MEF实现设计上的“松耦合”(一) 介绍了下MEF的基础用法,让我们对MEF有了一个抽象的认识.当然MEF的用法可能不限于此,比如MEF的目录服务.目录筛选.重组部件等高级 ...

  3. C#进阶系列——MEF实现设计上的“松耦合”(四):构造函数注入

    前言:今天十一长假的第一天,本因出去走走,奈何博主最大的乐趣是假期坐在电脑前看各处堵车,顺便写写博客,有点收获也是好的.关于MEF的知识,之前已经分享过三篇,为什么有今天这篇?是因为昨天分享领域服务的 ...

  4. Type-C设计上的防护

    Type C设计上各家芯片公司都提供了很多方案,但在防护方面很多留给了客户自己选择,这方面我可以重点聊聊,说起防护,无非就是过压过流防护. 过压防护,Type C的信号线有很多,都需要做静电防护,US ...

  5. MEF实现设计上的“松耦合”

    C#进阶系列——MEF实现设计上的“松耦合”(二)   前言:前篇 C#进阶系列——MEF实现设计上的“松耦合”(一) 介绍了下MEF的基础用法,让我们对MEF有了一个抽象的认识.当然MEF的用法可能 ...

  6. 面试挂在了 LRU 缓存算法设计上

    好吧,有人可能觉得我标题党了,但我想告诉你们的是,前阵子面试确实挂在了 RLU 缓存算法的设计上了.当时做题的时候,自己想的太多了,感觉设计一个 LRU(Least recently used) 缓存 ...

  7. JavaScript 与 Java 是两种完全不同的语言,无论在概念还是设计上。

    JavaScript 与 Java 是两种完全不同的语言,无论在概念还是设计上. Java(由 Sun 发明)是更复杂的编程语言. ECMA-262 是 JavaScript 标准的官方名称. Jav ...

  8. 全面提价2499元起小米6发布:四曲陶瓷机身+骁龙835+变焦双摄(小米在设计上也多次获得红点最佳、iF金奖等72项工业设计大奖)

    集微网  4月19日报道 今日,小米公司在北京召开正式推出了新一代旗舰手机“小米手机6”.在试玩过真机后,第一感觉就是这款手机做工与颜值相比此前小米手机提升巨大:有四曲面玻璃或陶瓷机身.不锈钢高亮边框 ...

  9. java架构-一些设计上的基本常识

    最近给团队新人讲了一些设计上的常识,可能会对其它的新人也有些帮助, 把暂时想到的几条,先记在这里. 1.API与SPI分离 框架或组件通常有两类客户,一个是使用者,一个是扩展者. API(Applic ...

随机推荐

  1. mycat schema.xml 配置文件详解

    <?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> &l ...

  2. php数组全排列,元素所有组合

    <?php $source = array('pll','我','爱','你','嘿'); sort($source); //保证初始数组是有序的 $last = count($source) ...

  3. 【codeforces 742A】Arpa’s hard exam and Mehrdad’s naive cheat

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  4. Windows Phone 8.1 控件

    如果你已经开始了 Windows Phone 8.1 的学习,就会发现许多在 8.0 下的控件在 8.1 中都发生了变化,以下就谈谈几个 8.1 下的新控件以及与 8.0 控件的改变. 1. Text ...

  5. ios开发总结,日常开发:ios开发功能收集,经验分享等等(不断更新中。。。)

    github资料学习和下载地址:https://github.com/niexiaobo/MyDailyDevelopmentNotes ios 学习模块 ios APP 日志管理的重要性: 一个功能 ...

  6. uml精粹——11.活动图(及整个读书笔记分享)

    11.活动图activity diagram   活动图是描写叙述过程化逻辑procedural logic.业务过程business process和工作流work flow的技术. 他和流程图fl ...

  7. 前端切图:手机端自适应布局demo

    手机端自适应布局demo原型如下: 图片发自简书App 要求如下:适应各种机型源码如下: <!DOCTYPE html > <html> <head> <me ...

  8. 三星语音AI助理背后的华人身影—73岁科技人三度创业成功(孙子兵法:道、天、地、将、法)

    我绝对不当老二,也不当老大,我要当霸主!”说这句话的是富迪科技董事长黄炎松.他还把“独霸”当作公司愿景宣言,大剌剌的放在美国总公司进门最显眼的墙上.   集微网消息,据台湾商业周刊报道,黄炎松,是台湾 ...

  9. html5中input的type类型有哪些(总结)

    html5中input的type类型有哪些(总结) 一.总结 一句话总结:时间.颜色.(邮件.电话.url).(数字.数字范围).搜索search 二.html5中input的type类型 值 描述 ...

  10. Java设计模式菜鸟系列(二十二)中介者模式建模与实现

    转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/40027109 中介者模式(Mediator):主要用来减少类与类之间的耦合的,由于假设类与 ...