简单的C语言编译器--词法分析器
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语言编译器--词法分析器的更多相关文章
- 简单的C语言编译器--概述
在学习了编译原理的相关知识后,逐渐的掌握一个编译器的结构.作用和实现方法.同时,希望自己在不断的努力下写出一个简单的C语言编译器. 实现步骤 词法分析器:将C语言测试代码分解成一个一个的词法单元: ...
- 简单的C语言编译器--语义制导翻译
语法分析是最难写的,而这部分确实最伤脑的.大量的语义动作分析差点把我逼疯. 简而言之,这部分的作用就是在每次归约之后,都进行一些语义动作,最终让我们得到测试程序的三地址码,即中间代码. 1. ...
- 简单的C语言编译器--语法分析器
语法分析算是最难的一部分了.总而言之,语法分析就是先设计一系列语法,然后再用设计好的语法去归约词法分析中的结果.最后将归约过程打印出来,或者生成抽象语法树. 1. 设计文法 以下是我的文法(引入的 ...
- 02.从0实现一个JVM语言之词法分析器-Lexer-03月02日更新
从0实现JVM语言之词法分析器-Lexer 本次有较大幅度更新, 老读者如果对前面的一些bug, 错误有疑问可以复盘或者留言. 源码github仓库, 如果这个系列文章对你有帮助, 希望获得你的一个s ...
- 第一个C语言编译器是怎样编写的?
首先向C语言之父Dennis MacAlistair Ritchie致敬! 当今几乎所有的实用的编译器/解释器(以下统称编译器)都是用C语言编写的,有一些语言比如Clojure,Jython等是基于J ...
- 【转】自己动手写SC语言编译器
自序 编译原理与技术的一整套理论在整个计算机科学领域占有相当重要的地位,学习它对程序设计人员有很大的帮助.我们考究历史会发现那些人人称颂的程序设 计大师都是编译领域的高手,像写出BASIC语言的BIL ...
- 在线C语言编译器/解释器
在线C语言编译器/解释器 本文介绍两个C语言在线解释器/编译器,这些工具可以提高代码片段检测方便的工作效率,并可以保证这些代码的正确性,而且还可以和别人一起编辑/分享之间的代码,这样可以共同分析代码并 ...
- 一个简单的C语言程序(详解)
C Primer Plus之一个简单的C语言程序(详解) #include <stdio.h> int main(void) //一个简单的 C程序 { int num; //定义一个名为 ...
- C语言编译器和IDE的选择
什么是编译器: CPU只认识几百个二进制形式的指令,C语言对CPU而言简直就是天书.C语言是用固定的词汇与格式组织起来,简单直观,程序员容易识别和理解. 这时候就需要一个工具,将C语言代码转换成CPU ...
随机推荐
- Git Compare with base,比较大文件时,长时间等待,无法加载
问题 当使用Git比较一个大文件(几十兆数量级)版本见差异时,会一直等待加载,且内存消耗很大,导致其他进程很难执行.任务管理器中,可以看到此时的TortoiseGitMerge吃掉3G左右的内存. 原 ...
- python 生成器和迭代器有这篇就够了
本节主要记录一下列表生成式,生成器和迭代器的知识点 列表生成器 首先举个例子 现在有个需求,看列表 [0,1,2,3,4,5,6,7,8,9],要求你把列表里面的每个值加1,你怎么实现呢? 方法一(简 ...
- 如何在Java应用中提交Spark任务?
最近看到有几个Github友关注了Streaming的监控工程--Teddy,所以思来想去还是优化下代码,不能让别人看笑话,是不.于是就想改在一下之前最丑陋的一个地方--任务提交 本博客内容基于Spa ...
- 网页加载进度的实现--JavaScript基础
总结了一些网页加载进度的实现方式…… 1.定时器实现加载进度 <!DOCTYPE html><html lang="en"><head> < ...
- 【BZOJ1010】【HNOI2008】玩具装箱(斜率优化,动态规划)
[BZOJ1010][HNOI2008]玩具装箱 题面 题目描述 P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆,再放到一 ...
- 【SHOI2012】魔法树(树链剖分,线段树)
[SHOI2012]魔法树 题面 BZOJ上找不到这道题目 只有洛谷上有.. 所以粘贴洛谷的题面 题解 树链剖分之后直接维护线段树就可以了 树链剖分良心模板题 #include<iostream ...
- [SCOI2010]连续攻击游戏 匈牙利算法
觉得题目水的离开 不会匈牙利的请离开 不知道二分图的请离开 不屑的大佬请离开 ……. 感谢您贡献的访问量 ————————————华丽的分割线———————————— 扯淡完了,先重温一下题目 [SC ...
- (2)Deep Learning之线性单元和梯度下降
往期回顾 在上一篇文章中,我们已经学会了编写一个简单的感知器,并用它来实现一个线性分类器.你应该还记得用来训练感知器的『感知器规则』.然而,我们并没有关心这个规则是怎么得到的.本文通过介绍另外一种『感 ...
- iOS工具——Xcode9无证书真机调试
入坑iOS开发这么久,一直都是在模拟器上运行,公司的项目也都有公司的开发者账号进行真机调试.但是很多时候在网上download一些demo想在真机上运行看一下效果的时候都没法成行,今天抽空好好研究和学 ...
- 【Spring源码分析】.properties文件读取及占位符${...}替换源码解析
前言 我们在开发中常遇到一种场景,Bean里面有一些参数是比较固定的,这种时候通常会采用配置的方式,将这些参数配置在.properties文件中,然后在Bean实例化的时候通过Spring将这些.pr ...