Compiler Principles Study 1: Removing Comments from Code
Preface I've started learning compiler principles~ For all compiler principles exercises, as usual, I'll still implement them in my favorite C# language, running on the .NetCore platform~ All the code for this series has already been uploaded to github, project homepage: https://github.com/Deali-Axy/CompilerConstructionLearning This Problem Delete // comments and /…/ comments from source programs written in high-level programming languages such as C or C++, keep the deleted
目录13 个章节
Preface
I've started learning compiler principles~ For all compiler principles exercises, as usual, I'll still implement them in my favorite C# language, running on the .NetCore platform~ All the code for this series has already been uploaded to github, project homepage:
This Problem
Delete // comments and /…/ comments from source programs written in high-level programming languages such as C or C++, and retain the source program after deletion. It is required to save it in file form.
Approach Analysis
The main function of the program is to eliminate comments from already written source programs. In source programs, comments have two forms: one is single-line comments, denoted by “//”, and the other is multi-line comments, denoted by “/…/”. For these two forms, the program uses if..else.. statements to judge and handle them accordingly. There may also be another situation here: the above two kinds of comment symbols may appear inside quotation marks, and comment symbols that appear inside quotation marks do not have commenting functionality, so comment symbols that appear inside quotation marks should not be eliminated. Therefore, the program written this time will analyze three cases.
First case, single-line comments:
if (ch != temp)
{
// 这里就是单行注释
ofile.put(ch);
ch = ifile.get();
}
or
if (ch != temp)
{
/* 这里就是单行注释 */
ofile.put(ch);
ch = ifile.get();
}
Second case, block comments:
if (ifile.fail() || ofile.fail())
{
cerr << "open file fail\n";
return EXIT_FAILURE;
/*返回值EXIT_FAILURE(在cstdlib库中定义),用于向操作系统报*
告打开文件失败*/
}
Third case, trailing comments:
ifile.close(); // 关闭文件
ofile.close();
cout << "/////*////ret/rtr////";
system("pause");
return 0;
There's also a key point to note
You can see this line
cout << "/////*////ret/rtr////";
This string of code wrapped in double quotes contains many slashes, so we need to avoid identifying these slashes as comments. The method I used here is to first replace the strings that contain comment symbols before processing comments, and then add them back after the comments have been deleted.
Implementation Code
The comments are written in great detail, so together with the approach analysis above, I won't continue analyzing the code~
var sReader = new StreamReader(filePath);
var newSource = "";
var inBlock = false;
var replaceFlag = false;
var tempLine = ""; // 用于保存被替换的特殊行代码
while (!sReader.EndOfStream)
{
var line = sReader.ReadLine();
if (line.Length == 0) continue; // 去除空行
var quotationPattern = "^(.*?)\".*//.*\"";
var quotationResult = Regex.Match(line, quotationPattern);
if (quotationResult.Success)
{
System.Console.WriteLine("替换特殊代码,双引号中包裹注释斜杠");
tempLine = quotationResult.Groups[0].Value;
replaceFlag = true;
line = Regex.Replace(line, quotationPattern, REPLACEMENT);
}
// 单行注释
if (line.Trim().StartsWith(@"//"))
continue;
if (line.Trim().StartsWith(@"/*") && line.EndsWith(@"*/"))
continue;
// 注释块
if (Regex.Match(line.Trim(), @"^/\*").Success)
inBlock = true;
if (Regex.Match(line.Trim(), @"\*/$").Success)
{
inBlock = false;
continue;
}
// 行后注释
// 使用非贪婪模式(.+?)匹配第一个//
var pattern = @"^(.*?)//(.*)";
// var pattern = @"[^(.*?)//(.*)]|[^(.*?)/\*(.*)\*/]";
var result = Regex.Match(line, pattern);
if (result.Success)
{
System.Console.WriteLine("发现行后注释:{0}", result.Groups[2]);
line = result.Groups[1].Value;
}
// 还原被替换的代码
if (replaceFlag)
{
System.Console.WriteLine("还原特殊代码");
line = line.Replace(REPLACEMENT, tempLine);
replaceFlag = false;
}
if (inBlock) continue;
newSource += line + Environment.NewLine;
}
var outputPath = "output/exp1.src";
System.Console.WriteLine("去除注释完成,创建新文件。");
using (var sWriter = new StreamWriter(outputPath))
{
sWriter.Write(newSource);
}
System.Console.WriteLine("操作完成!文件路径:{0}", outputPath);
Result Testing
Source file
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
using namespace std;
int main()
{
cout << '/';
ifstream ifile; //建立文件流对象
ofstream ofile;
ifile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.cpp"); //打开F盘根目录下的fileIn.txt文件
ofile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.obj");
if (ifile.fail() || ofile.fail())
{ //测试打开操作是否成功
cerr << "open file fail\n";
return EXIT_FAILURE;
/*返回值EXIT_FAILURE(在cstdlib库中定义),用于向操作系统报*
告打开文件失败*/
}
char ch;
ch = ifile.get(); //进行读写操作
while (!ifile.eof())
{
if (ch == 34)
{ //双引号中若出现“//”,双引号中的字符不消除
char temp = ch; //第一个双引号
ofile.put(ch);
ch = ifile.get();
while (!ifile.eof())
{
if (ch != temp)
{ //寻找下一个双引号
ofile.put(ch);
ch = ifile.get();
}
else
{
ofile.put(ch);
break;
}
}
ch = ifile.get();
continue; //双引号情况结束,重新新一轮判断
}
if (ch == 47)
{ //出现第一个斜杠
char temp2 = ch;
ch = ifile.get();
if (ch == 47)
{ //单行注释情况
ch = ifile.get();
while (!(ch == '\n'))
ch = ifile.get();
}
else if (ch == '*')
{ //多行注释情况
while (1)
{
ch = ifile.get();
while (!(ch == '*'))
ch = ifile.get();
ch = ifile.get();
if (ch == 47)
break;
}
ch = ifile.get();
}
else
{
ofile.put(temp2); //temp2保存第一个斜杠,当上述两种情况都没有时,将此斜杠输出
}
//ch = ifile.get();
}
//cout << ch << endl;
ofile.put(ch); //将字符写入文件流对象中
ch = ifile.get(); //从输入文件对象流中读取一个字符
}
ifile.close(); //关闭文件
ofile.close();
cout << "/////*////ret/rtr////";
system("pause");
return 0;
}
Result after processing
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdlib>
using namespace std;
int main()
{
cout << '/';
ifstream ifile;
ofstream ofile;
ifile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.cpp");
ofile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.obj");
if (ifile.fail() || ofile.fail())
{
cerr << "open file fail\n";
return EXIT_FAILURE;
}
char ch;
ch = ifile.get();
while (!ifile.eof())
{
if (ch == 34)
{
char temp = ch;
ofile.put(ch);
ch = ifile.get();
while (!ifile.eof())
{
if (ch != temp)
{
ofile.put(ch);
ch = ifile.get();
}
else
{
ofile.put(ch);
break;
}
}
ch = ifile.get();
continue;
}
if (ch == 47)
{
char temp2 = ch;
ch = ifile.get();
if (ch == 47)
{
ch = ifile.get();
while (!(ch == '\n'))
ch = ifile.get();
}
else if (ch == '*')
{
while (1)
{
ch = ifile.get();
while (!(ch == '*'))
ch = ifile.get();
ch = ifile.get();
if (ch == 47)
break;
}
ch = ifile.get();
}
else
{
ofile.put(temp2);
}
}
ofile.put(ch);
ch = ifile.get();
}
ifile.close();
ofile.close();
cout << "/////*////ret/rtr////";
system("pause");
return 0;
}
Complete Code
https://github.com/Deali-Axy/CompilerConstructionLearning/blob/master/code/exp/exp1/Exp1.cs
References:
- Regular expression metacharacters: https://www.runoob.com/regexp/regexp-metachar.html
- Removing comments in JavaScript: https://segmentfault.com/a/1190000015611632
Welcome to Discuss
For questions, please leave a message on the WeChat official account backend; I will reply to every message~
- WeChat official account: 画星星高手
- Coding livestream: https://live.bilibili.com/11883038
- Zhihu: https://www.zhihu.com/people/dealiaxy
- Column: https://zhuanlan.zhihu.com/deali
_EOF_
Compiler Principles Study 1: Removing Comments from Code
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
软件工程课程思维导图
结构如下 软件工程 什么是软件工程 方法 系统的 规范的 可量化的 过程 软件开发 运行 维护 构成的主要要素 人 项目 过程 方法 工具 软件制品 影响软件工程进步的动力 硬件能力 软件技术 社会需求 科学技术水平 软件的8个质量要素 正确性 软件满足需求规约的程度 完成用户目标的程度 可用性 学习、使用成本 操作软件 输入数据 解释软件输出结果 可靠性 完成预期功能概率 成功运行概率 有效性
2022-06-17 · 计科基础 · 约 1 分钟
奇怪的操作系统相关习题~4-21
操作系统作业 操作系统在计算机系统分层中处于哪个层次?它与上下层是怎样衔接的? 计算机系统的分层: 第1级是微程序机器层,这是一个实在的硬件层,它由机器硬件直接执行微指令。 第2级是传统机器语言,它也是一个实际的机器层,它由微程序解释机器指令系统。 第3级是操作系统层,它由操作系统程序实现。操作系统程序由机器指令和广义指令组成,这些广义指令是为了扩展机器功能而设置的,它是由操作系统定义和解释的软件
2022-06-17 · 计科基础 · 约 1 分钟
设计模式之代理模式浅析
代理模式 在客户端与实体之间建立一个代理对象(proxy),客户端对实体进行操作全部委派给代理对象, 隐藏实体的具体实现细节。 Proxy还可以与业务代码分离,部署到另外的服务器。业务代码中通过RPC来委派任务。 代理模式与父类和接口的异同 相同点:代理模式的作用和父类以及接口和组合的作用类似,都是为了聚合共用部分,减少公共部分的代码 不同点: 相比起父类,他们的语境不同,父类要表达的含义是 is
2022-06-17 · 计科基础 · 约 1 分钟
操作系统学习三:进程调度与死锁-以及银行家算法避免死锁--NetCore实现
前言 这是操作系统学习的第三篇啦,关于进程调度有很多内容,操作系统在调度进程的时候最容易遇到的问题就是死锁了,银行家算法是一个典型的避免死锁算法。 死锁的概念 先来了解一下死锁的基本概念:一组竞争系统资源或相互通信的进程相互的“永久”阻塞。若无外力作用,这组进程将永远不能继续执行。 看下面两幅图片,左边是可能产生死锁的状态,四辆汽车(进程)要竞争同一个资源(通过路口),如果系统调度不当,就会陷入死
2021-12-18 · 计科基础 · 约 1 分钟
讨论
共 0 条加载评论中…