Compiler Lab1- 自制词法分析器
由于编译原理课的Lab1为自制词法分析器,所以笔者用C++实现了一个极简的C语言词法分析器,用于分析C语言源代码。它可以处理关键字、标识符、整数、实数、浮点数的科学计数法表示、运算符、分隔符、字符串字面量、字符字面量、注释和预处理指令。请注意,此版本的词法分析器不是很完善,但它应该能够处理大多数简单的C语言源代码。
用户输入输入文件名和输出文件名,然后检查这些文件是否可以正确打开。然后,我们从输入文件中读取内容,对其进行词法分析,并将结果写入输出文件中。最后,我们通知用户词法分析已完成,并提示用户查看输出文件以获取结果。
mylexer.cpp文件
词法分析器核心文件
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
#include <unordered_set>using namespace std;enum class TokenType
{Keyword,Identifier,Integer,Real,Operator,Separator,StringLiteral,CharLiteral,Comment,Preprocessor,Unknown
};struct Token
{TokenType type;string value;
};bool isKeyword(const string &value)
{static const unordered_set<string> keywords = {"auto", "break", "case", "char", "const", "continue", "default", "do","double", "else", "enum", "extern", "float", "for", "goto", "if", "int","long", "register", "return", "short", "signed", "sizeof", "static","struct", "switch", "typedef", "union", "unsigned", "void", "volatile", "while"};return keywords.find(value) != keywords.end();
}bool isOperator(char c)
{static const unordered_set<char> operators = {'+', '-', '*', '/', '%', '>', '<', '=', '&', '|', '!', '~', '^', '?', ':'};return operators.find(c) != operators.end();
}bool isSeparator(char c)
{static const unordered_set<char> separators = {'(', ')', '[', ']', '{', '}', ',', ';', '.', '#'};return separators.find(c) != separators.end();
}vector<Token> lex(const string &input)
{vector<Token> tokens;string buffer;auto flushBuffer = [&](){if (!buffer.empty()){if (isKeyword(buffer)){tokens.push_back({TokenType::Keyword, buffer});}else{tokens.push_back({TokenType::Identifier, buffer});}buffer.clear();}};size_t i = 0;while (i < input.length()){char c = input[i];if (isalpha(c) || c == '_'){buffer.push_back(c);i++;}else{flushBuffer();if (isdigit(c)){string number;number.push_back(c);i++;while (i < input.length() && (isdigit(input[i]) || input[i] == '.' || tolower(input[i]) == 'e')){number.push_back(input[i]);if (tolower(input[i]) == 'e' && i + 1 < input.length() && (input[i + 1] == '+' || input[i + 1] == '-')){number.push_back(input[++i]);}i++;}while (i < input.length() && (tolower(input[i]) == 'u' || tolower(input[i]) == 'l')){number.push_back(input[i]);i++;}tokens.push_back({number.find('.') != string::npos || number.find('e') != string::npos || number.find('E') != string::npos ? TokenType::Real : TokenType::Integer, number});}else if (isOperator(c)){if (c == '/' && i + 1 < input.length()){if (input[i + 1] == '/'){i += 2;string comment;while (i < input.length() && input[i] != '\n'){comment.push_back(input[i]);i++;}tokens.push_back({TokenType::Comment, comment});}else if (input[i + 1] == '*'){i += 2;string comment;while (i + 1 < input.length() && !(input[i] == '*' && input[i + 1] == '/')){comment.push_back(input[i]);i++;}if (i + 1 < input.length()){// comment.push_back(input[i]);i += 2;}tokens.push_back({TokenType::Comment, comment});// cout << "here " << endl;}}else{tokens.push_back({TokenType::Operator, string(1, c)});i++;}}else if (isSeparator(c)){if (c == '#'){string preprocessor;i++;while (i < input.length() && (isalnum(input[i]) || input[i] == '_')){preprocessor.push_back(input[i]);i++;}tokens.push_back({TokenType::Preprocessor, preprocessor});}else{tokens.push_back({TokenType::Separator, string(1, c)});i++;}}else if (c == '\"'){string str_literal;i++;while (i < input.length() && input[i] != '\"'){if (input[i] == '\\' && i + 1 < input.length()){str_literal.push_back(input[i]);i++;}str_literal.push_back(input[i]);i++;}i++;tokens.push_back({TokenType::StringLiteral, str_literal});}else if (c == '\''){string char_literal;i++;if (i < input.length()){if (input[i] == '\\' && i + 1 < input.length()){char_literal.push_back(input[i]);i++;}char_literal.push_back(input[i]);i++;}i++;tokens.push_back({TokenType::CharLiteral, char_literal});}else{i++;}}}flushBuffer();return tokens;
}int main()
{string input_filename;string output_filename;cout << "Enter the input file name: ";cin >> input_filename;cout << "Enter the output file name: ";cin >> output_filename;ifstream infile(input_filename);ofstream outfile(output_filename);if (!infile){cerr << "Error opening the input file!" << endl;return 1;}if (!outfile){cerr << "Error opening the output file!" << endl;return 1;}string input((istreambuf_iterator<char>(infile)), istreambuf_iterator<char>());auto tokens = lex(input);for (const auto &token : tokens){// outfile << "Token type: " << static_cast<int>(token.type) << ", value: " << token.value << endl;outfile << "Token type: ";switch (token.type){case TokenType::Keyword:outfile << "Keyword";break;case TokenType::Identifier:outfile << "Identifier";break;case TokenType::Integer:outfile << "Integer";break;case TokenType::Real:outfile << "Real";break;case TokenType::Operator:outfile << "Operator";break;case TokenType::Separator:outfile << "Separator";break;case TokenType::StringLiteral:outfile << "StringLiteral";break;case TokenType::CharLiteral:outfile << "CharLiteral";break;case TokenType::Comment:outfile << "Comment";break;case TokenType::Preprocessor:outfile << "Preprocessor";break;case TokenType::Unknown:outfile << "Unknown";break;}outfile << ", Value: " << token.value << endl;}cout << "Lexical analysis complete." << endl;return 0;
}
input.c文件
用于词法分析器的输入文件
#include <stdio.h>
#define N 6int main()
{// Single-Line Commentsint a = 0;double b = 1.5;long c = 100L;char d = 'd';char s[6] = "hello";/*Multiline commentMultiline comment*/if (a > 0){printf("%s", s);}else{c = a + N;}return 0;
}

output.txt文件
词法分析器的输出结果
Token type: Preprocessor, Value: include
Token type: Operator, Value: <
Token type: Identifier, Value: stdio
Token type: Separator, Value: .
Token type: Identifier, Value: h
Token type: Operator, Value: >
Token type: Preprocessor, Value: define
Token type: Identifier, Value: N
Token type: Integer, Value: 6
Token type: Keyword, Value: int
Token type: Identifier, Value: main
Token type: Separator, Value: (
Token type: Separator, Value: )
Token type: Separator, Value: {
Token type: Comment, Value: Single-Line Comments
Token type: Keyword, Value: int
Token type: Identifier, Value: a
Token type: Operator, Value: =
Token type: Integer, Value: 0
Token type: Separator, Value: ;
Token type: Keyword, Value: double
Token type: Identifier, Value: b
Token type: Operator, Value: =
Token type: Real, Value: 1.5
Token type: Separator, Value: ;
Token type: Keyword, Value: long
Token type: Identifier, Value: c
Token type: Operator, Value: =
Token type: Integer, Value: 100L
Token type: Separator, Value: ;
Token type: Keyword, Value: char
Token type: Identifier, Value: d
Token type: Operator, Value: =
Token type: CharLiteral, Value: d
Token type: Separator, Value: ;
Token type: Keyword, Value: char
Token type: Identifier, Value: s
Token type: Separator, Value: [
Token type: Integer, Value: 6
Token type: Separator, Value: ]
Token type: Operator, Value: =
Token type: StringLiteral, Value: hello
Token type: Separator, Value: ;
Token type: Comment, Value: Multiline commentMultiline commentToken type: Keyword, Value: if
Token type: Separator, Value: (
Token type: Identifier, Value: a
Token type: Operator, Value: >
Token type: Integer, Value: 0
Token type: Separator, Value: )
Token type: Separator, Value: {
Token type: Identifier, Value: printf
Token type: Separator, Value: (
Token type: StringLiteral, Value: %s
Token type: Separator, Value: ,
Token type: Identifier, Value: s
Token type: Separator, Value: )
Token type: Separator, Value: ;
Token type: Separator, Value: }
Token type: Keyword, Value: else
Token type: Separator, Value: {
Token type: Identifier, Value: c
Token type: Operator, Value: =
Token type: Identifier, Value: a
Token type: Operator, Value: +
Token type: Identifier, Value: N
Token type: Separator, Value: ;
Token type: Separator, Value: }
Token type: Keyword, Value: return
Token type: Integer, Value: 0
Token type: Separator, Value: ;
Token type: Separator, Value: }
注:在mylexer.cpp中,笔者定义了一个名为flushBuffer的Lambda函数,它将buffer中的内容添加到tokens向量,并清空buffer。
下面来详细解释一下这个Lambda函数:
auto flushBuffer:我们使用auto关键字来定义一个名为flushBuffer的变量,它将存储我们的Lambda表达式。auto关键字告诉编译器根据Lambda表达式的类型自动推导flushBuffer的类型。
[&]():这是Lambda表达式的开头部分,方括号[]内表示Lambda函数的捕获说明符。在这个例子中,我们使用&表示按引用捕获所有外部变量。这意味着在Lambda函数内部,我们可以访问并修改外部作用域中的变量,例如buffer和tokens。括号()表示Lambda函数没有参数。
{}:这是Lambda函数的主体,大括号{}内包含了函数的实现。在这个例子中,我们检查buffer是否为空,如果不为空,我们将buffer中的内容添加到tokens向量,并清空buffer。
C++中的lambda表达式是一种创建匿名函数对象的便捷方式。自C++11起,lambda表达式成为了C++的一部分。它们通常用于定义简短的函数,可以直接在需要使用它们的地方定义。Lambda表达式的语法如下:
[capture](parameters) -> return_type { function_body }
- capture:捕获列表,用于捕获来自定义lambda的作用域内的变量。捕获列表可以按值或按引用捕获变量。
- parameters:函数参数列表,与常规函数参数列表类似。
- return_type:返回类型(可选)。如果省略此部分,编译器会自动推导返回类型(通常为void或单个 return 语句的类型)。
- function_body:函数体,包含实现所需功能的代码。
只看上面的概念还是太抽象了,我们举个简单的例子,来直观地感受一下Lambda表达式
#include <iostream>
#include <vector>
#include <algorithm>using namespace std;int main() {vector<int> numbers = {1, 2, 3, 4, 5};int factor = 3;// vector数组中每个元素都乘以factorfor_each(numbers.begin(), numbers.end(), [factor](int& number) {number *= factor;});// 打印修改过的number数组for (const auto& number : numbers) {cout << number << " ";}return 0;
}
输出结果为:
3 6 9 12 15
相关文章:
Compiler Lab1- 自制词法分析器
由于编译原理课的Lab1为自制词法分析器,所以笔者用C实现了一个极简的C语言词法分析器,用于分析C语言源代码。它可以处理关键字、标识符、整数、实数、浮点数的科学计数法表示、运算符、分隔符、字符串字面量、字符字面量、注释和预处理指令。请注意&…...
构建API的战斗——与来自Kong的Marco Palladino的问答
Kong是一个开源的API网关,可用于管理、安全性和监视微服务和API的所有流量。以下是Kong官方网站的介绍: Kong是一个云原生、快速、可扩展的分布式微服务抽象层(也称为API网关、API中枢、API发布器或API服务的网关)。 Kong即可充当…...
华为OD机试 - 对称美学(Python)
题目描述 对称就是最大的美学,现有一道关于对称字符串的美学。已知: 第1个字符串:R 第2个字符串:BR 第3个字符串:RBBR 第4个字符串:BRRBRBBR 第5个字符串:RBBRBRRBBRRBRBBR 相信你已经发现规律了,没错!就是第 i 个字符串 = 第 i - 1 号字符串取反 + 第 i - 1 号字符…...
argparse.ArgumentParser
文章目录 argparse.Namespace() Python参数解析工具argparse.ArgumentParser()和实例详解 创建解析器 parserargparse.ArgumentParser() 添加参数 parser.add_argument(name or flags…[, action][, nargs][, const][, default][, type][, choices][, required][, help][, meta…...
大数据Doris(五):FE 扩缩容
文章目录 FE 扩缩容 一、通过MySQL客户端连接Doris 二、FE Follower扩缩容 1、准备 FE 安装包...
react相关概念
真实DOM和虚拟DOM区别 react关于虚拟DOM和真实DOM 虚拟DOM比较“轻”,真实DOM比较“重”,因为虚拟DOM是React在用,无需真实DOM上那么多属性 虚拟DOM最终一定会转为真实DOM放入页面 JSX JSX: 全称JavsScript XML 是react定义的一种类似于XM…...
计算机的硬件系统的组成
微型计算机是指一种体积小、功能强大的计算机系统,通常用于个人或小型企业的日常办公、娱乐等需求。微型计算机的硬件系统主要由以下几个部分组成: 一、中央处理器(CPU) 中央处理器,简称CPU(Central Proc…...
Python基础-列表元组
列表元组 列表元组的操作符 len在列表元组中的使用 len函数可以计算除数字类型之外,其他所有数据类型的长度 列表(元组)之间的累加与乘法 两个列表相加可以使用同一个列表多次累加可以使用* in和not in在列表(元组)中的用法 in用于判断某个成员(元素)是否在该数据结构中…...
【校招VIP】拿到offer就躺平?转正前需要知道的这些事儿...
现在春招基本上结束了,拿到offer的同学就觉得可以直接躺平了。 但是拿到offer只是我们取经路上九九八十一难的第一关,后面还有很多的关卡等着考验我们。 近些年来在实习期间或者试用期间,无法转正的例子比比皆是,令人心动的offe…...
考研拓展:汇编基础
一.说明 本篇博客是基于考研之计算机组成原理中的程序机器级代码表示进行学习的,并不是从汇编语言这一门单独的课程来学习的,涉及的汇编语言知识多是帮助你学习考研之计算机组成原理中对应的考点。 二.相关寄存器 1.相关寄存器 X86处理器中有8个32位…...
10 【Sass语法介绍-继承】
1.前言 在我们编写样式的时候,很多情况下我们几个不同的类会有相同的样式代码,同时这几个类又有其自己的样式代码,这使我们就可以通过 Sass 提供的继承 extend 来实现。本节内容我们将讲解 Sass 继承的语法以及继承的多重延伸等等࿰…...
魔兽worldserver.conf 服务端配置文件说明
魔兽worldserver.conf 服务端配置文件说明 我是艾西,今天把很多小伙伴需要的魔兽worldserver.conf 服务端配置文件说明分享给大家,大家可以自己研究参考下 worldserver.conf 这个文件是服务端的配置文件,可以在这里做很多个性化修改 注意&a…...
关于电信设备进网许可制度若干改革举措的通告
Q:3月1日后,不再实行进网许可管理的11种电信设备是否还需要继续申请和使用标志? A:3月1日起,对不再实行进网许可管理的11种电信设备停止核发进网许可标志,已申请的标志可在证书有效期内继续使用。 Q&#…...
TuGraph 开源数据库体验
TuGraph 开源数据库体验 文章目录 TuGraph 开源数据库体验1. 简单介绍2. 可视化界面体验:查询界面:数据建模:数据导入: 3. 体验心得: 1. 简单介绍 TuGraph 是蚂蚁集团自主研发的大规模图计算系统,提供图数…...
【C++】18.哈希
1.unordered_set和unordered_map 使用与set和map的用法一样 #include <iostream> #include <unordered_map> #include <unordered_set> #include <map> #include <set> #include <string> #include <vector> #include <time.h&…...
C# 利用TabControl控件制作多窗口切换
TabControl控件切换时触发的事件 选项卡切换触发的是TabControl控件的SelectedIndexChanged事件。 当TabControl控件的任何一个TabPage被点击或选择,即发生SelectedIndexChanged事件事件。 代码如下: private void tabControl1_SelectedIndexChanged(o…...
论文阅读《PIDNet: A Real-time Semantic Segmentation Network Inspired by PID》
论文地址:https://arxiv.org/pdf/2206.02066.pdf 源码地址:https://github.com/XuJiacong/PIDNet 概述 针对双分支模型在语义分割任务上直接融合高分辨率的细节信息与低频的上下文信息过程中细节特征会被上下文信息掩盖的问题,提出了一种新的…...
SOA与中间件、基础件的发展
应运而生的SOA 美国著名的IT市场研究和顾问咨询公司Gartner预测:到2006年,采用面向服务的企业级应用将占全球销售出的所有商业应用产品的80 以上到2008年,SOA将成为绝对主流的软件工程实践方法。近几年全球各大IT巨头纷纷推出自己的面向服务的应用平…...
渗透测试 | 目录扫描
0x00 免责声明 本文仅限于学习讨论与技术知识的分享,不得违反当地国家的法律法规。对于传播、利用文章中提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,本文作者不为此承担任何责任,一旦造成后果请自行承担…...
基于Springboot的班级综合测评管理系统的设计与实现
摘要 随着互联网技术的高速发展,人们生活的各方面都受到互联网技术的影响。现在人们可以通过互联网技术就能实现不出家门就可以通过网络进行系统管理,交易等,而且过程简单、快捷。同样的,在人们的工作生活中,也就需要…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...
DingDing机器人群消息推送
文章目录 1 新建机器人2 API文档说明3 代码编写 1 新建机器人 点击群设置 下滑到群管理的机器人,点击进入 添加机器人 选择自定义Webhook服务 点击添加 设置安全设置,详见说明文档 成功后,记录Webhook 2 API文档说明 点击设置说明 查看自…...
省略号和可变参数模板
本文主要介绍如何展开可变参数的参数包 1.C语言的va_list展开可变参数 #include <iostream> #include <cstdarg>void printNumbers(int count, ...) {// 声明va_list类型的变量va_list args;// 使用va_start将可变参数写入变量argsva_start(args, count);for (in…...
PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
