C语言编译器开发之旅(一):词法分析扫描器
2021-06-05 04:11
1301 查看
本节我们先从一个简易的可以识别四则运算和整数值的词法分析扫描器开始。它实现的功能也很简单,就是读取我们给定的文件,并识别出文件中的token将其输出。
这个简易的扫描器支持的词法元素只有五个:
- 四个基本的算术运算符:
+
、-
、*
、/
- 十进制整数
我们需要事先定义好每一个token,使用枚举类型来表示:
//defs.h // Tokens enum { T_PLUS, T_MINUS, T_STAR, T_SLASH, T_INTLIT };
在扫描到token后将其存储在一个如下的结构体中,当标记是
T_INTLIT(即整数文字)时,该
intvalue字段将保存我们扫描的整数值:
//defs.h // Token structure struct token { int token; int intvalue; };
我们现在假定有一个文件,其内部的的代码就是一个四则运算表达式:
2 + 34 * 5 - 8 / 3
我们要实现的是读取他的每一个有效字符并输出,就像这样:
Token intlit, value 2 Token + Token intlit, value 34 Token * Token intlit, value 5 Token - Token intlit, value 8 Token / Token intlit, value 3
我们看到了最终要实现的目标,让我们来一步步分析需要的功能。
- 首先我们需要一个逐字符的读出文件中的内容并返回的函数。当我们在输入流中读的太远时,需要将读取到的字符放回(如上例当读到数字时,因无法直接获取数字是否结束,只能循环读取,当读到第一个非数字字符时则判定该十进制数读取结束,需将该十进制数返回并将读取的非数字字符放回),记录行号的的功能也是在这里实现。
// Get the next character from the input file. static int next(void) { int c; if (Putback) { // Use the character put c = Putback; // back if there is one Putback = 0; return c; } c = fgetc(Infile); // Read from input file if ('\n' == c) Line++; // Increment line count return c; }
- 我们只需要有效字符,所以需要去除空白字符的功能
// Skip past input that we don't need to deal with, // i.e. whitespace, newlines. Return the first // character we do need to deal with. static int skip(void) { int c; c = next(); while (' ' == c || '\t' == c || '\n' == c || '\r' == c || '\f' == c) { c = next(); } return (c); }
- 当读到的是数字的时候,怎么确定数字有多少位呢?所以我们需要一个专门处理数字的函数。
// Return the position of character c // in string s, or -1 if c not found static int chrpos(char *s, int c) { char *p; p = strchr(s, c); return (p ? p - s : -1); } // Scan and return ad8 an integer literal // value from the input file. Store // the value as a string in Text. static int scanint(int c) { int k, val = 0; // Convert each character into an int value while ((k = chrpos("0123456789", c)) >= 0) { val = val * 10 + k; c = next(); } // We hit a non-integer character, put it back. putback(c); return val; }
所以现在我们可以在跳过空格的同时读取字符;如果我们读到一个字符太远,我们也可以放回一个字符。我们现在可以编写我们的第一个词法扫描器:
int scan(struct token *t) { int c; // Skip whitespace c = skip(); // Determine the token based on // the input character switch (c) { case EOF: return (0); case '+': t->token = T_PLUS; break; case '-': t->token = T_MINUS; break; case '*': t->token = T_STAR; break; case '/': t->token = T_SLASH; break; default: // If it's a digit, scan the // literal integer value in if (isdigit(c)) { t->intvalue = scanint(c); t->token = T_INTLIT; break; } printf("Unrecognised character %c on line %d\n", c, Line); exit(1); } // We found a token return (1); }
现在我们可以读取token并将其返回。
main()函数打开一个文件,然后扫描它的令牌:
void main(int argc, char *argv[]) { ... init(); ... Infile = fopen(argv[1], "r"); ... scanfile(); exit(0); }
并
scanfile()在有新token时循环并打印出token的详细信息:
// List of printable tokens char *tokstr[] = { "+", "-", "*", "/", "intlit" }; // Loop scanning in all the tokens in the input file. // Print out details of each token found. static void scanfile() { struct token T; while (scan(&T)) { printf("Token %s", tokstr[T.token]); if (T.token == T_INTLIT) printf(", value %d", T.intvalue); printf("\n"); } }
我们本节的内容就到此为止。下一部分中,我们将构建一个解析器来解释我们输入文件的语法,并计算并打印出每个文件的最终值。
本文Github地址:https://github.com/Shaw9379/acwj/tree/master/01_Scanner
相关文章推荐
- Android 应用开发性能优化完全分析(下)
- VTK修炼之道81:VTK开发基础_vtkObject类深入分析
- 智慧公安大数据分析平台搭建情报指挥决策系统开发
- 阿庆SQL智能查询分析器,使用delphi开发的一个数据库查询分析管理工具.分享给大家
- 软件开发:需求分析的20条法则(zz)
- 谈谈我对软件开发 可行性分析 等9个流程的理解[上]
- 从零开始学习OpenCL开发(二)一个最简单的示例与简单性能分析
- 敏捷开发中的源代码分析(3)
- 软件开发:需求分析的20条法则
- 从零开始开发JVM语言(五)语法分析
- Silverlight企业应用快速开发平台框架设计(三)分析-页面模型
- Android VLC播放器二次开发1——程序结构分析
- arcgis for js开发之路径分析
- Maemo Linux手机平台系列分析:6 Maemo平台开发之D-Bus
- 深入浅出 - Android系统移植与平台开发(十三) - Sensor HAL框架分析之三
- 详谈Linux开发中常见段错误问题的原因及分析
- 关键词生成原创文章及句子的软件!开发原理分析
- EAS 开发中,序时簿上,对某一字段的排序出现混乱的异常分析
- Struts2源码粗略分析一:开发环境
- 微信公众平台开发(二) 微信公众平台示例代码分析