C编译器剖析_2.2 词法分析

lcc源代码解析之lex.c 有了前面的铺垫,终于可以上正餐了,本节解析编译器的一个重要组件:词法分析器,即lcc的lex.c文件。 lcc出于对效率的考虑,并没有使用类似nfa思想的自动词法解析生成工具来生成,而是采用了手写的lex。 具体代码如下: #include "c.h" #include #include static char rcsid[] = "$Id: lex.c,v 1.1 2002/08/2 阅读详情

2.2  词法分析

    目录ucc\ucl下,与词法分析相关的C文件主要有input.c和lex.c,input.c用于从外存读入预处理后的文件,其主要的函数如图2.2.1所示。在UCC驱动的代码中,已经预定义了宏_UCC,所以第39行的条件成立,函数ReadSourceFile()会使用C标准库的IO函数来读取文件。因为.c文件只是普通的文本文件,其文件大小很少超过1M字节的,所以UCC并没有采取逐行读的行缓存策略,而是一下把整个文件读入内存中,第45行的fopen()用于打开文件,通过第56行的fseek()函数和第57行的ftell()函数,就可以获得文件的大小,然后通过第59行的malloc()开辟了足够大的内存堆空间,第67行通过fread()把整个文件读入内存中。

    

图2.2.1 ReadSourceFile()

     此后,文件lex.c的词法分析器就不再需要去读写文件了,只需要从内存中读取字符即可。头文件ucc\ucl\input.h中的结构体structinput记录了关于输入文件的信息,如图2.2.2所示,其中第15行的base域记录了读入的输入文件在内存中的起始位置,而cursor则反映了当前的读取位置。对一个名为hello.c的C文件而言,UCC编译器见到的实际上经预处理后的文件hello.i。文件hello.i包含了各个头文件及C文件hello.c,为了在错误处理时能准确报告出错位置,引入了图2.2.2第4行的结构体struct coord。其中,coord是coordinate的缩写,代表“坐标”之意,其中包含了出错的原始文件名filename,比如hello.c;出错的行号ppline,这个行号指的是在hello.c中的行号;而line代表的在预处理后的文件hello.i中的行号;出错的列号col。


                                               图2.2.2 struct input

    在ucc\ucl\error.c的Do_Error()函数中,我们会看到如下用于报告出错位置的代码。

void Do_Error(Coord coord, const char *format, ...){

         ……

fprintf(stderr, "(%s,%d):",coord->filename, coord->ppline);

……

}

    在UCC源代码中,我们使用Error()来报错,而不是直接使用Do_Error(),Error的宏定义如下所示。这么做的主要目的是为了方便UCC编译器的调试。

#define     Error         fprintf(stderr, "(%s,%d):",__FILE__, __LINE__) , Do_Error

    通过Error(),我们可以得到如下所示的出错提示,(hello.c,3)告诉我们,程序员编写的hello.c的第3行缺少了一个分号,而我们是在UCC编译器源代码的stmt.c的第24行检测到这个错误的。当然,真正把UCC编译器作为一个产品发布时,” (stmt.c24):”这样的信息是没有必要打印出来的。此时,只要把Error的宏定义改为” #define         Error  Do_Error ”即可。

(stmt.c 24):(hello.c,3):error:Expect ;

     接下来,我们来分析一下词法分析器lex.c中的代码。因为一个字符对应一个字节,一个字节包含了0至255种不同的数值,所以我们可以写一个大大的switch语句,根据当前字符的值来分门别类地进行处理,如下所示。

switch(*cursor){

case  0:           ….     ;        break;

case  1:          ….     ;        break;

…..

case   255:               ….     ;        break;

}

   不过,一般情况下,当遇到一个switch语句中有这么多的case时,往往会采取“打表”的方法来简化。图2.2.3是UCC的词法分析器GetNextToken的主要代码,每调用一次GetNextToken()我们就会获得一个由若干个字符构成的单词,第1165行的SkipWhiteSpace()跳过了注释、空格、制表符、回车和换行等空白符,真正进行词法分析的是第1170行的代码。文件ucc\ucl\token.h中包含了UCC编译器用到的各个token名称。

enum token

{

         TK_BEGIN,

#define TOKEN(k, s) k,

#include "token.h"

#undef  TOKEN

 

};


                                               图2.2.3   GetNextToken()

    我们用CURSOR所指向的当前字符值来作为数组Scanners的下标,字符(*CURSOR)的值正好在0至255之间,而数组Scanners即为一个由函数指针构成的表格。

typedef int (*Scanner)(void);

static Scanner       Scanners[256];

    这个函数指针构成的表格Scanners当然需要经过初始化,在main()函数中,我们调用了SetupLexer()来初始化词法分析器,其主要的工作就是初始化这个表格,如图2.2.4所示。


图2.2.4 SetupLexer()

     该表格共有256项,各个以”Scan”为前缀命名的扫描函数的代码不会太复杂,我们就不一一进行分析,仅以扫描标志符为例,如图2.2.5所示,第778至788行处理以字母L开始的形如L’a’的宽字符和形如L”abc”的宽字符串。第796行需要再判断一下识别出来的标志符会不会是C语言的关键字,如果确实是C程序员声明的标志符(变量名或者函数名),我们就在TokenValue中记录其名称。


图2.2.5 ScanIdentifier()

C语言新手必踩的坑:‘2x‘为啥报错‘invalid suffix‘?手把手教你改对乘法写法 本文解析C语言新手常见的'invalid suffix'编译错误,以'2x'报错为例,详细讲解数学写法与编程语法的差异。通过对比数学符号与C语言运算符,提供系统避免表达式错误的四步校验法,并深入分析编译器解析流程。文章还介绍现代IDE如何帮助预防此类错误,帮助开发者快速掌握正确的乘法写法。 阅读详情

相关推荐

c4编译器源码剖析

c4_source 1. 概述 c4是500行代码实现一个c语言编译器 简单暴力,适合了解基础的编译器原理 2. 主流程 1.建立系统符号表 2.读取源代码 3.一次遍历源代码,同时进行词法分析,语法分析和中间代码生成。 4.执行中间代码 3. 中间代码 3.1. 基础 中间代码 操作 意义 LEA 2 将栈往上的2个位置变量加载到寄存器 加载本地变量 IM

fishmei的博客 6058

Linux Make 命令详解

gcc是编译器 而make不是 make是依赖于Makefile来编译多个源文件的工具 在Makefile里同样是用gcc(或者别的编译器)来编译程序.gcc是编译一个文件,make是编译多个源文件的工程文件的工具。make是一个命令工具,是一个解释makefile中指令的命令工具。make就是一个gcc/g++的调度器,通过读入一个文件(默认文件名为Makefile或者makefile),执行一

发展是曲折的但也是前进的 1992

PHP-Zend引擎剖析之词法分析(一)

前言 闲来研究一下PHP底层的Zend引擎源码,Zend引擎是PHP脚本的虚拟机。 在PHP上层有SAPI接口,负责对各个接入层的抽象,例如PHP在Apache模块里边的实现,Fast-CGI的实现,命令行的实现。在PHP底层便是Zend虚拟机,Zend虚拟机负责解析PHP语法的文件,上层可以在虚拟机中注册函数/变量提供给虚拟机调用,例如从Apache分发过来的HTTP请求经过PHP的

raphealguo 5578

UCC编译器学习笔记1

标准C语言,结构体中的成员是可以指定所占位数的,下面的ST类型表示struct st * typedef struct st { int a:8; int b:24; int c; }* ST

biao2488890051的博客 1820

深入浅出C语言编译器源码剖析

本文还有配套的精品资源,点击获取 简介:编译器是将高级编程语言转换为机器语言的关键工具,本资源提供的“简单编译器”源码用C语言编写,涵盖编译器设计的各个主要阶段,如词法分析、语法分析、语义分析、中间代码生成、代码优化和目标代码生成。通过学习和调试这些源码,开发者能够深入理解编译原理和编程语言的内部工作,提升C语言编程技能,掌握复杂数据结构和算法的应用。建议学习者先了解编译原...

weixin_42451850的博客 1499

C语言子集编译器源码解析:从词法分析到MIPS汇编全链路

编译器是将高级语言转换为机器码的核心系统软件,其工作流程通常划分为词法分析、语法分析、中间代码生成、优化和目标代码生成等阶段。词法分析负责将字符流切分为token,语法分析构建抽象语法树,中间代码生成产生与目标无关的表示,优化提升代码效率,最终生成特定架构的汇编代码。理解这一链路对掌握编译原理和进行课程设计至关重要。本文以一份C语言子集编译器源码为例,详细剖析了从词法分析到MIPS汇编的完整实现,涵盖工程结构、编译步骤、各模块衔接及常见问题排查,为编译原理课设提供了一份可运行的参考。

weixin_33824385的博客 264

linux-内核导入配置 lexer.lex.c错误

wu@ubuntu:~/linux-5.4.8$ make exynos_defconfig HOSTCC scripts/basic/fixdep HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/confdata.o HOSTCC scripts/kconfig/expr.o LEX scripts/kconfig/lex...

weixin_43568960的博客 7445

小型C编译器源码解析:从词法分析到活性分析

编译器是连接高级语言与机器执行的桥梁,理解其内部机制对后端开发和嵌入式调试尤为重要。从C语言子集裁剪开始,小型编译器通过手写词法分析器识别Token,并采用递归下降法构建抽象语法树;随后生成栈机IR作为中间表示,再经虚拟机或汇编器执行。本文围绕一份最小可用的C编译器源码,梳理了词法分析、运算符优先级、短路求值、函数调用约定等核心实现细节,并结合活性分析展示IR优化的基本思路,帮助读者快速建立编译器后端开发的整体认知,避开常见实现陷阱。

weixin_42692935的博客 271

Java编译全过程解密:从源码到机器码的奇幻之旅

Java的编译过程分为前端编译和后端编译两个阶段,它们各司其职,共同将人类可读的代码转化为机器可执行的指令。它生成的字节码是平台中立的,可以在任何安装了JVM的设备上运行,这也是Java"一次编写,到处运行"的基石。为什么需要两者并存?本文将带你深入探索Java编译的完整过程,理解javac如何将.java文件转换为.class文件,以及JVM如何进一步将字节码优化为高性能的本地机器码。统计循环体执行的次数,当循环执行次数超过阈值时,触发栈上替换(OSR)编译,即在方法执行过程中替换循环体的代码。

2501_94594897的博客 265

SNL语言编译器源码剖析:从词法分析到四元式生成

编译原理中的编译器前端通常由词法分析、语法分析、语义分析与中间代码生成构成:字符流先被扫描为Token,再由递归下降解析成语法树,符号表和作用域链负责语义检查,表达式求值过程同步生成四元式。这条流水线的技术价值在于模块边界清晰、错误可定位、中间结果可验证,适合课程设计、源码阅读和编译器入门实践。围绕SNL语言编译器源码,可以从目录分层、main函数串联、手写DFA、递归下降建树、符号表挂载到四元式回填逐段拆解,并给出编译跑通与常见避坑方法。

weixin_26938645的博客 292

发那科机器人编程软件fanuc roboguide授权补丁_工业机器人离线编程与应用:ROBOGUIDE V8.3版本的工程文件创建...

欢迎关注我们ROBOGUIDE是发那科机器人公司提供的一个离线编程工具,可模拟现实中的机器人和周边设备的布局,通过虚拟TP示教来模拟机器人的运动轨迹,同时可以验证方案的可行性和准确的周期时间。ROBOGUIDE是一款核心应用软件,具体的还包括搬运、弧焊、喷涂等其他模块。以最新的8.3版本为例进行讲解,对于未注册的版本将有30天的免费使用期限。开始菜单击开始菜单中的HandingPRO启动程序,第一...

weixin_39791446的博客 2924

C编译器剖析_2.1 从Makefile走起

2.1  从Makefile走起     目录ucc\ucl包含了UCC编译器的所有源代码,而ucc\driver则是UCC驱动的源代码,我们在第1章时已简要地分析过UCC驱动。从第2章开始,我们要剖析的代码就集中在目录ucc\ucl中,ucl就是UCC编译器的名称。以下命令列出了ucc\ucl目录下的所有文件。 iron@ubuntu:ucl$ ls alloc.c    erro

SheIsC的专栏 1414

AT32串口打印全攻略:从零搭建到printf重定向(GCC版)

本文详细介绍了在GCC编译环境下为AT32系列MCU实现串口打印与printf重定向的完整方案。针对从Keil MDK迁移到GCC工具链时常见的串口无输出问题,文章从环境搭建、USART配置入手,重点剖析了GCC下printf重定向的核心原理——通过重写_write()函数,并提供了经过验证的代码实现、常见问题排查及性能优化建议。

weixin_29163797的博客 264

Nim编译器架构与构建系统剖析

Nim编译器架构与构建系统剖析 【免费下载链接】Nim 项目地址: https://gitcode.com/gh_mirrors/nimro/Nimrod Nim编译器采用...

gitblog_00239的博客 877

电力工程师必看:GOOSE/SV/MMS三协议在智能变电站中的分工与联动实例

本文深入解析了智能变电站中GOOSE、SV、MMS三大核心通信协议的分工与协同机制。GOOSE协议作为过程层的“闪电信使”,负责毫秒级传输跳闸命令等事件信息;SV协议则传输精确同步的电流电压采样值流;而MMS协议作为站控层的“管理语言”,实现设备监控与数据管理。三者基于IEC 61850标准,在过程层、间隔层和站控层之间高效联动,共同构建了智能变电站稳定可靠的通信骨架。

jj890的博客 365

FOC实战笔记:RC吸收电路的设计考量与参数调优

本文深入探讨了FOC驱动器中RC吸收电路的设计与调优。针对MOS管开关过程中的电压尖峰问题,详细阐述了RC电路的钳位与能量吸收原理,并提供了从参数计算、实验调试到布局布线的完整实战指南,旨在帮助工程师有效提升系统可靠性与稳定性。

weixin_29219539的博客 273

c4c语言编译器,c4编译器源码剖析

c4_source1. 概述c4是500行代码实现一个c语言编译器简单暴力,适合了解基础的编译器原理1232. 主流程1.建立系统符号表2.读取源代码3.一次遍历源代码,同时进行词法分析,语法分析和中间代码生成。4.执行中间代码123453. 中间代码3.1. 基础中间代码操作意义LEA 2将栈往上的2个位置变量加载到寄存器加载本地变量IMM将代码区位置指定的代码加载到寄存器加载到全局变量JMP ...

weixin_35715335的博客 2137

Flutter 完整学习文档 - 从入门到精通

Widget 就像乐高积木,Flutter 中所有的东西都是 Widget。一个按钮是 Widget,一段文字是 Widget,甚至布局、边距、对齐方式也是 Widget!"万物皆 Widget"

zwjapple的专栏 2316

C编译器剖析PDF文档及UCC162.3源代码下载

C编译器剖析PDF文档及UCC162.3源代码下载 去发现同类优质开源项目:https://gitcode.com/ 此仓库提供了关于C编译器剖析的PDF文档以及经过少量BUG修改后的编译器源代码UCC162.3。该资源旨在帮助开发者和学习者深入理解C编译器的工作原理和实现细节。 资源内容 C编译器剖析PDF文档:详细介绍了C编译器的内部结构、编译过程、以及相关技术。 UCC162.3源代码:经...

gitblog_06738的博客 436

AD7920实战:从咪头到ADC的音频信号采集电路设计全解析(附电路图)

本文详细解析了基于AD7920 ADC芯片的音频信号采集电路设计全流程。从驻极体咪头的驱动、AD8542运放的两级增益与偏置设计,到关键的抗混叠滤波器配置,以及ADC接口的驱动缓冲与PCB布局要点,提供了完整的实战指南与电路图,帮助开发者实现高质量音频信号采集。

red88的博客 802

Linux脚本实战之SSH执行远端脚本

Linux脚本实战之SSH连接主机

jks212454的博客 2134

C编译器剖析PDF文档及UCC162.3源代码下载:深度揭秘编译原理与实践

C编译器剖析PDF文档及UCC162.3源代码下载:深度揭秘编译原理与实践 去发现同类优质开源项目:https://gitcode.com/ 在软件开发领域,理解编译器的内部机制对于提升代码质量和开发效率至关重要。今天,我们将为您介绍一个开源项目——C编译器剖析PDF文档及UCC162.3源代码下载,该项目为广大开发者提供了一个深入学习C编译器工作原理的宝贵资源。 项目介绍 C编译器剖析PDF文档...

gitblog_06795的博客 479

【图像分类】卷积神经网络之VGGNet网络模型实现花卉图像识别(附代码和数据集,PyTorch框架)

卷积神经网络之VGGNet网络模型实现花卉图像识别(附代码和数据集,PyTorch框架)

学无止境、积少成多、厚积薄发 2430

推荐开源项目:纯C编写的C编译器

推荐开源项目:纯C#编写的C编译器 在编程的浩瀚星空中,一款独特而充满挑战性的开源项目映入眼帘——一个完全用C#语言实现的ANSI C编译器。此项目旨在构建一个能够生成x86(32位)汇编代码的编译工具,特别针对Linux环境设计。它以直接利用GCC的汇编器和链接器为目标,为开发者开启了一扇新的大门。 项目简介 位于GitHub上的这个项目——“C Compiler in C#”,不仅是一次语言之...

gitblog_00931的博客 603

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

上一篇: C编译器剖析_2.1 从Makefile走起
下一篇: C编译器剖析_2.3 内存管理
SheIsC
博客等级 码龄12年 224粉丝 64原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值