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. 十年Java开发程序员回答,自学Java,培训Java的利和弊

    最近有一个朋友在群里面问我:是应该去培训Java还是应该自学Java,我想的说的是我并不是想给他一个去培训或者不去培训,我用自己多年对于行业的了解去分析这个问题,然后让他自己去思考,哪种更加适合他.他 ...

  2. 【BZOJ1009】GT考试(KMP算法,矩阵快速幂,动态规划)

    [BZOJ1009]GT考试(KMP算法,矩阵快速幂,动态规划) 题面 BZOJ 题解 看到这个题目 化简一下题意 长度为\(n\)的,由\(0-9\)组成的字符串中 不含串\(s\)的串的数量有几个 ...

  3. [BZOJ1009] [HNOI2008] GT考试 (KMP & dp & 矩阵乘法)

    Description 阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字. 他的不吉利数学A1A2...Am(0< ...

  4. [BZOJ3110] [Zjoi2013] K大数查询 (树套树)

    Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置 ...

  5. 【MyBatis源码分析】Configuration加载(下篇)

    元素设置 继续MyBatis的Configuration加载源码分析: private void parseConfiguration(XNode root) { try { Properties s ...

  6. 了解wireshark

    Wireshark是很流行的网络分析工具.这个强大的工具可以捕捉网络中的数据,并为用户提供关于网络和上层协议的各种信息.与很多其他网络工具一样,Wireshark也使用pcap network lib ...

  7. 无需安装Oracle Client连接Oracle数据库

    介绍 当我们采用 ODP.NET 检索Oracle 数据库的时候,Oracle客户端是必须安装.假如当时电脑上没有安装Oracle客户端,就不能这么用了,这时候Oracle.ManagedDataAc ...

  8. 34.Ajax

    优先级 如果发送的是[普通数据] jQuery XMLHttpRequest iframe 如果发送的是[文件] iframe jQuery(FormData) XMLHttpRequest(Form ...

  9. oracle 12c 安装指南(各种问题总结)

    下一学期要学习数据库的知识,趁着敲代码之余,安装著名的oracle来放松一下 1.首先从官网下载安装包http://www.oracle.com/technetwork/database/enterp ...

  10. 九度oj题目1002:Grading

    //不是说C语言就是C++的子集么,为毛printf在九度OJ上不能通过编译,abs还不支持参数为整型的abs()重载 //C++比较正确的做法是#include<cmath.h>,cou ...