1. 定义词法单元Tag

  首先要将可能出现的词进行分类,可以有不同的分类方式。如多符一类:将所有逗号、分号、括号等都归为一类,或者一符一类,将一个符号归为一类。我这里采用的是一符一类的方式。C代码如下:

    #ifndef TAG_H
#define TAG_H namespace Tag {
//保留字
const int
INT = 1, BOOL = 2, MAIN = 3, IF = 4,
ELSE = 5, FOR = 6, WHILE = 7, FALSE = 8,
BREAK = 9, RETURN = 10, TRUE = 11 ; //运算符
const int
NOT = 20, NE = 21, AUTOMINUS =22, MINUS = 23,
AUTOADD = 24, ADD = 25, OR = 26,
AND = 27, MUTIPLY = 28, DIVIDE = 29, MOD = 30,
EQ = 31, ASSIN = 32, GE = 33, GT = 34,
LE = 35, LS = 36; //分界符
const int
COMMA = 40, SEMICOLON = 41, LLBRACKET = 42,
RLBRACKET = 43, LMBRACKET = 44, RMBRACKET = 45,
LGBRACKET = 46, RGBRACKET = 47; //整数常数
const int NUM = 50; //标识符
const int ID = 60; //错误
const int ERROR = 404; //空
const int EMPTY = 70; } #endif

2. 具体步骤

  • 一个一个字符地扫描测试代码,忽略空白字符,遇到回车时,记录行数加1
  • 要进行区分标识符(即普通变量名字)和保留字
  • 因为将标识符和常数都guiwe各自归为一类,所以要有算法能够识别出一整个常数和完整的标识符
  • 加入适当的非法词检测

3. 设计词法分析类

  设计一个词法分析器,当然要包括如何存储一个词法单元,如何扫描(scan)测试代码等,直接上代码:

myLexer.h

    #ifndef MYLEXER_H
#define MYLEXER_H #include <fstream>
#include <string>
#include <unordered_map>
#include "tag.h" /*
* 主要是定义基本的词法单元类,
* 声明了词法分析类
*/ //存储词法单元
class Word {
public:
Word() = default;
Word(std::string s, int t) : lexeme(s), tag(t) {};
std::string getLexeme() { return lexeme; };
int getTag() { return tag; }
void setTag(int t) { tag = t; }
void setLexeme(std::string s) { lexeme = s; }
private:
std::string lexeme;
int tag;
}; //词法分析器类
class Lexer {
public:
Lexer();
void reserve(Word w);
bool readnext(char c, std::ifstream &in);
Word scan(std::ifstream &in);
int getLine() { return line; }
private:
char peek;
std::unordered_map<std::string, Word> words;
int line;
}; #endif

myLexer.cpp

    #include <iostream>
#include <cctype>
#include <sstream>
#include "myLexer.h" void Lexer::reserve(Word w) {
words.insert({w.getLexeme(), w});
} Lexer::Lexer() {
//存入保留字,为了区分标识符
reserve( Word("int", Tag::INT) );
reserve( Word("bool", Tag::BOOL) );
reserve( Word("main", Tag::MAIN) );
reserve( Word("if", Tag::IF) );
reserve( Word("else", Tag::ELSE) );
reserve( Word("for", Tag::FOR) );
reserve( Word("while", Tag::WHILE) );
reserve( Word("break", Tag::BREAK) );
reserve( Word("return", Tag::RETURN) );
reserve( Word("true", Tag::TRUE) );
reserve( Word("false", Tag::FALSE) ); peek = ' ';
line = 1; } //方便处理像>=,++等这些两个字符连在一起的运算符
bool Lexer::readnext(char c, std::ifstream &in) {
in >> peek;
if( peek != c)
return false;
peek = ' ';
return true;
} Word Lexer::scan(std::ifstream &in) {
//跳过空白符
while(!in.eof()) {
if(peek == ' ' || peek == '\t') {
in >> peek;
continue;
}
else if(peek == '\n')
++line;
else
break;
in >> peek;
} //处理分界符、运算符等
switch(peek) {
case '!':
if(readnext('=', in))
return Word("!=", Tag::NE);
else
return Word("!", Tag::NOT);
case '-':
if(readnext('-', in))
return Word("--", Tag::AUTOMINUS);
else
return Word("-", Tag::MINUS);
case '+':
if(readnext('+', in))
return Word("++", Tag::AUTOADD);
else
return Word("+", Tag::ADD);
case '|':
if(readnext('|', in))
return Word("||", Tag::OR);
else
return Word("error", Tag::ERROR);
case '&':
if(readnext('&', in))
return Word("&&", Tag::AND);
else
return Word("error", Tag::ERROR);
case '*':
in >> peek;
return Word("*", Tag::MUTIPLY);
case '/':
in >> peek;
return Word("/", Tag::DIVIDE);
case '%':
in >> peek;
return Word("%", Tag::MOD);
case '=':
if(readnext('=', in))
return Word("==", Tag::EQ);
else
return Word("=", Tag::ASSIN);
case '>':
if(readnext('=', in))
return Word(">=", Tag::GE);
else
return Word(">", Tag::GT);
case '<':
if(readnext('=', in))
return Word("<=", Tag::LE);
else
return Word("<", Tag::LS);
case ',':
in >> peek;
return Word(",", Tag::COMMA);
case ';':
in >> peek;
return Word(";", Tag::SEMICOLON);
case '(':
in >> peek;
return Word("(", Tag::LLBRACKET);
case ')':
in >> peek;
return Word(")", Tag::RLBRACKET);
case '[':
in >> peek;
return Word("[", Tag::LMBRACKET);
case ']':
in >> peek;
return Word("]", Tag::RMBRACKET);
case '{':
in >> peek;
return Word("{", Tag::LGBRACKET);
case '}':
in >> peek;
return Word("}", Tag::RGBRACKET);
} //处理常数
if(isdigit(peek)) {
int v = 0;
do {
v = 10*v + peek - 48;
in >> peek;
} while(isdigit(peek));
if(peek != '.')
return Word(std::to_string(v), Tag::NUM);
} //处理标识符
if(isalpha(peek)) {
std::ostringstream b;
do {
b << peek;
in >> peek;
} while(isalnum(peek) || peek == '_'); std::string tmp = b.str(); //判断是否为保留字
if(words.find(tmp) != words.end())
return words[tmp];
else
return Word(tmp, Tag::ID);
}
if(peek != ' ' && peek != '\t' && peek != '\n')
return Word("error", Tag::ERROR);
return Word("empty", Tag::EMPTY);
}

  设计完成后,自己写一个Main函数,在while循环中调用scan函数,每次打印出Word内容,就能够得到

简单的C语言编译器--词法分析器的更多相关文章

  1. 简单的C语言编译器--概述

      在学习了编译原理的相关知识后,逐渐的掌握一个编译器的结构.作用和实现方法.同时,希望自己在不断的努力下写出一个简单的C语言编译器. 实现步骤 词法分析器:将C语言测试代码分解成一个一个的词法单元: ...

  2. 简单的C语言编译器--语义制导翻译

      语法分析是最难写的,而这部分确实最伤脑的.大量的语义动作分析差点把我逼疯.   简而言之,这部分的作用就是在每次归约之后,都进行一些语义动作,最终让我们得到测试程序的三地址码,即中间代码. 1. ...

  3. 简单的C语言编译器--语法分析器

      语法分析算是最难的一部分了.总而言之,语法分析就是先设计一系列语法,然后再用设计好的语法去归约词法分析中的结果.最后将归约过程打印出来,或者生成抽象语法树. 1. 设计文法 以下是我的文法(引入的 ...

  4. 02.从0实现一个JVM语言之词法分析器-Lexer-03月02日更新

    从0实现JVM语言之词法分析器-Lexer 本次有较大幅度更新, 老读者如果对前面的一些bug, 错误有疑问可以复盘或者留言. 源码github仓库, 如果这个系列文章对你有帮助, 希望获得你的一个s ...

  5. 第一个C语言编译器是怎样编写的?

    首先向C语言之父Dennis MacAlistair Ritchie致敬! 当今几乎所有的实用的编译器/解释器(以下统称编译器)都是用C语言编写的,有一些语言比如Clojure,Jython等是基于J ...

  6. 【转】自己动手写SC语言编译器

    自序 编译原理与技术的一整套理论在整个计算机科学领域占有相当重要的地位,学习它对程序设计人员有很大的帮助.我们考究历史会发现那些人人称颂的程序设 计大师都是编译领域的高手,像写出BASIC语言的BIL ...

  7. 在线C语言编译器/解释器

    在线C语言编译器/解释器 本文介绍两个C语言在线解释器/编译器,这些工具可以提高代码片段检测方便的工作效率,并可以保证这些代码的正确性,而且还可以和别人一起编辑/分享之间的代码,这样可以共同分析代码并 ...

  8. 一个简单的C语言程序(详解)

    C Primer Plus之一个简单的C语言程序(详解) #include <stdio.h> int main(void) //一个简单的 C程序 { int num; //定义一个名为 ...

  9. C语言编译器和IDE的选择

    什么是编译器: CPU只认识几百个二进制形式的指令,C语言对CPU而言简直就是天书.C语言是用固定的词汇与格式组织起来,简单直观,程序员容易识别和理解. 这时候就需要一个工具,将C语言代码转换成CPU ...

随机推荐

  1. 洛谷U19464 山村游历(Wander)(LCT,Splay)

    洛谷题目传送门 LCT维护子树信息常见套路详见我的总结 闲话 题目摘自WC模拟试题(by Philipsweng),原题目名Wander,"山村游历"是自己搞出来的中文名. 数据自 ...

  2. 【BZOJ1212】L语言(AC自动机)

    [BZOJ1212]L语言(AC自动机) 题面 BZOJ 题解 很自然的,既然要匹配单词,那就全部都丢到\(AC\)自动机里面去 现在想想怎么匹配 先是\(AC\)自动机正常的匹配 如果此时这个位置能 ...

  3. 【BZOJ4571】美味(主席树)

    [BZOJ4571]美味(主席树) 题面 Description 一家餐厅有 n 道菜,编号 1...n ,大家对第 i 道菜的评价值为 ai(1≤i≤n).有 m 位顾客,第 i 位顾客的期 望值为 ...

  4. LCT总结

    LCT总结 类比树剖,树剖是通过静态地把一棵树剖成若干条链然后用一种支持区间操作的数据结构维护(比如线段树.树状数组),而LCT是动态地去处理这个问题. 大家都知道树剖用线段树维护,而LCT用\(sp ...

  5. Linux文件目录权限对比

    读取权限(r) 文件只有r权限: 具有读取\阅读文件内容权限1.只能使用查看类命令 cat.head.tail.less.more2.不能复制,也就是不能使用cp命令3.不能移动,不能使用mv命令移动 ...

  6. webuploader 上传文件 生成链接下载文件

    最近 在asp.net MVC 项目 需要实现一个Excel和 图片上传功能.之前有使用过SWFUpload 做过上传图片功能,在本次实现过程中,有人推荐WebUploader 上传组件,因此采用we ...

  7. Django+xadmin打造在线教育平台(二)

    三.xadmin后台管理 3.1.xadmin的安装 django2.0的安装(源码安装方式): https://github.com/sshwsfc/xadmin/tree/django2 把zip ...

  8. IPFS家族(一)

    IPFS这个项目其实很大,并不像大家想象的是一个东西,IPFS是由很多模块组成,每一个模块现在都已经独立成项目了,并且有自己的主页.让我们来简单看一下IPFS家族成员. 协议实验室的主页:https: ...

  9. selenium 基础(一)

    selenium安装 pip install selenium selenium操作浏览器原理 早期selenium 1.0 用的selenium RC, 后来selenum2集合了selenium1 ...

  10. 微信小程序开发之--”emplate模板“的应用

    第一步:定义模板 ①:新建一个template文件夹用来管理项目中所有的模板(本文案例定义文件夹名为:temp): ②.新建一个wxml文件来定义模板(本文案例定义文件夹名为:temp.wxml): ...