前文  .NET框架源码解读之MYC编译器 和 MYC编译器源码分析之程序入口 分别讲解了 SSCLI 里示例编译器的架构和程序入口,本文接着分析它的词法分析部分的代码。

词法解析的工作都由Tok类处理,其构造函数接受一个Io对象做文件处理,下面是Tok构造函数的源码:

public Tok(Io ihandle)
{io = ihandle;// 初始化Token(字符归类)字典InitHash();            // initialize the tokens hashtable// 读入文件的第一个字符
    io.ReadChar();// 逐个扫描文件里的字符,获取// 第一个字符归类(Token)
    scan();
}

构造函数中第一个函数调用InitHash的目的是将关键字和操作符解析成更容易识别的字符类型识别号 - Token,这样做的目的是为了便于语法解析器parser处理。例如,对于下面这条C语句:

int foo(int a)

与其让语法解析器去逐个处理单个字符,词法解析器的作用是将去上面一行语句归类成类似下面的格式:

T_INT T_IDENT ‘(‘ T_INT T_IDENT ‘)’

因为T_INT,T_IDENT都是一个整数型常量,而’(‘这样的单个字符也可以当作整数型常量对待,这样语法解析器在分析语法的时候工作会更轻松些。所以在InitHash函数里,其把编程语言里所有的关键字和多字符操作符(如左移赋值操作符 <<=)都设置了类型标识号(Token),在Tok对象的scan()函数扫描源文件时,会逐一在这个字典里查询关键字的标识号:

public void InitHash()
{// 为字符类型识别号对照表 – tokens分配空间tokens = new Hashtable();AddTok(T_LEFT_ASSIGN,    "<<=");     // ... ...AddTok(T_IF,        "if");// ... ...AddTok(T_STATIC,        "static");AddTok(T_INT,        "int");// ... ...
}

而对应的每个标识号(Token)的定义,则可以在Tok.cs源文件的最上面找到:

public const int T_LEFT_ASSIGN    = 10001;
// ... ...
public const int T_IF            = 20001;
// ... ...
public const int T_STATIC        = 30002;
// ... ...
public const int T_INT        = 40003;
// ... ...
public const int T_IDENT         = 50001;
public const int T_DIGITS         = 50002;
public const int T_UNKNOWN        = 99999;
public const int T_EOF         = -1;

字符类型识别号对照表初始化完毕后,语法分析器就可以调用Tok对象的scan函数进行语法处理了,scan函数每次只处理并返回一个字符类型:

public void scan()
{// 跳过注释、换行符、空格等字符
    skipWhite();// 先判断当前读取的字符是不是一个字母// 如果是字母开头的话,要么是关键字,// 要么就是变量名if (Char.IsLetter(io.getNextChar()))// 逐个扫描后面的字符,直到识别出关键字// 或者变量名为止才退出
      LoadName();// 如果当前的字符是 0 - 9的数字else if (Char.IsDigit(io.getNextChar()))// 扫描完后面的数字并归类
      LoadNum();// 如果是操作符,扫描完后面的操作符字符串else if (isOp(io.getNextChar()))LoadOp();// 如果文件已经读取完毕了else if (io.EOF()){// 返回特殊的识别符 T_EOF,表示文件读取完毕value = null;token_id = T_EOF;}else{// 这个字符不是一个合法的字符,归类成T_UNKNOWN// T_UNKNOWN没有被任何语法引用// 如果语法分析器在扫描语法的过程中// 看到这个识别符,很有可能是源码里有语法错误value = new StringBuilder(MyC.MAXSTR);value.Append(io.getNextChar());token_id = T_UNKNOWN;io.ReadChar();}skipWhite();// 条件编译,如果是myc.exe是调试版本,则在命令行里// 打印出当前识别的字符类型,便于myc.exe的开发者排错
#if DEBUGConsole.WriteLine("[tok.scan tok=["+this+"]");
#endif
}

scan函数是Tok对象里最核心的函数,它实际上是完成前面myc语法里这些词法规则(还有隐含的关键字和操作符识别):

letter ::= "A-Za-z";
digit ::= "0-9";name ::= letter { letter | digit };
integer ::= digit { digit };

我们再通过说明LoadName函数来解释词法分析的细节:

void LoadName()
{// 缓存读取到的字符value = new StringBuilder(MyC.MAXSTR);skipWhite();  // 错误验证 - 确保第一个字符是字母if (!Char.IsLetter(io.getNextChar()))throw new ApplicationException("?Expected Name");// 后面跟着的字符只能是数字或者字母while (Char.IsLetterOrDigit(io.getNextChar())){// 缓存字符,以便判断是变量名,还是关键字
    value.Append(io.getNextChar());// 从源文件里读取下一个字符
    io.ReadChar();}// 在字符类型识别表里查询读取到的词组是不是关键字token_id = lookup_id();// 不是关键字的话,那么就是变量名(或函数名)if (token_id <= 0)token_id = T_IDENT;skipWhite();
}

上面基本上就是词法分析的关键代码了,不过在说明的时候,我特意跳过了构造函数的 io.ReadChar()这个函数,这个函数从字面意义上看是读取一个字符,但实际上从源文件一个字符一个字符的读取效率实在是太低了,因此一般都是从源文件里读取一大段字符并缓存在内存里,提高效率:

// Io.cs – ReadChar函数public void ReadChar()
{// 判断是不是读到文件末尾了if (_eof)            // if already eof, nothing to do herereturn;// 如果缓存还没有实例化,或者缓存里的字符// 已经处理完毕了,创建一个新的缓存// 对于老的缓存数组,丢给垃圾回收机制处理if (ibuf == null || ibufidx >= MyC.MAXBUF){ibuf = new char[MyC.MAXBUF];_eof = false;// 从源文件里读取一大块内容到缓存里ibufread = rfile.Read(ibuf, 0, MyC.MAXBUF);ibufidx = 0;if (buf == null)buf = new StringBuilder(MyC.MAXSTR);}// 从缓存里读取下一个字符look = ibuf[ibufidx++];// 判断这次读取时,是否已经到源文件末尾了if (ibufread < MyC.MAXBUF && ibufidx > ibufread)_eof = true;/** track the read characters*/// 保存当前读取的字符,以便在生成IL源文件的时候// 可以把C源码跟生成的IL源码对应起来
  buf.Append(look);// 如果碰到换行,更新行号,行号在报告语法错误// 的时候会用到,告知具体语法出错的行号便于// 程序员找到错误if (look == '\n')bufline++;
}

在Io.ReadChar函数里,会保存读取的C源码,当要生成IL源文件的时候,这个信息用来保存C语句跟IL语句的对应关系,如用下面的命令编译myc里自带的测试源码文件:

效果如下图:

转载于:https://www.cnblogs.com/vowei/p/4337922.html

MYC编译器源码之词法分析相关推荐

  1. MYC编译器源码分析之程序入口

    前文.NET框架源码解读之MYC编译器讲了MyC编译器的架构,整个编译器是用C#语言写的,上图列出了MyC编译器编译一个C源文件的过程,编译主路径如下: 首先是入口Main函数用来解析命令行参数,读取 ...

  2. MYC编译器源码之语法分析

    MyC编译器采用自顶向下的方法进行语法解析,这种语法解析方式,一般是从最左边的Token开始,然后自顶向下看哪一条语法规则可能包含这个Token,如果包含这个Token,则自左向右根据这条语法规则逐一 ...

  3. MYC编译器源码之代码生成

    前面讲过语法的解析之后,代码生成方面就简单很多了.虽然myc是一个简单的示例编译器,但是它还是在解析的过程中生成了一个小的语法树,这个语法树将会用在生成exe可执行文件和il源码的过程中. 编译器在解 ...

  4. 华为鸿蒙系统学习笔记4-方舟编译器源码下载及安装

    2019华为全球开发者大会将在8月9日-11日在华为松山湖基地召开.本次开发者大会邀请了1500位合作伙伴.5000名全球开发者,将是华为历来规模最大的一次会议.在这次大会上,华为方舟编译器也是关注的 ...

  5. 尝试自动批量翻译方舟编译器源码中的标识符

    在对方舟编译器源码中的近百个标识符/字符串常量进行手工汉化后, 尝试用批量替换+字典的方式对源码标识符进行自动翻译, 目标是自动翻译后达到与手工相同的效果. 字典来源于之前的手动提交. 批量替换之前基 ...

  6. 各个编程语言编译器源码收集

    心血来潮在 Github 收集了各个主流编程语言的编译器源码,下面列出了各个编译器文件链接以及实现语言(可能会有错误). GCC 系列 官网 官方仓库 Github镜像 The GNU Compile ...

  7. 编译方舟编译器源码教程

    前言:本博客主要是对华为开源平台的官方编译文档,做进一步的讲解,以及解决在编译时可能会到的问题.现在,先把编译成功的流程分享出来,后续再对各个工具和术语,以及如何使用编译出来的编译器,做进一步的讲解. ...

  8. 方舟编译器编写鸿蒙软件,华为鸿蒙系统学习笔记4-方舟编译器源码下载及安装...

    2019华为全球开发者大会将在8月9日-11日在华为松山湖基地召开.本次开发者大会邀请了1500位合作伙伴.5000名全球开发者,将是华为历来规模最大的一次会议.在这次大会上,华为方舟编译器也是关注的 ...

  9. java编译器源码分析之语法分析器

    token流到抽象语法树的过程是语法分析. 前面认识到token流,这部分将介绍抽象语法树(AST). 那么什么是抽象语法树(AST)?AST长啥样?我们的token流是如何转变成AST的?下面围绕这 ...

最新文章

  1. excel 怎么让数字不用科学计数法
  2. js修改display_Vue.js从零开始——过渡 / 动画效果(2)
  3. iphone静音键失灵_你知道iPhone手机中哪些一般人不知道的功能?
  4. 脚本命令配置mysql_MySQL 自动化部署脚本
  5. 第十五篇 Python之文件处理
  6. PPT优秀模板|7个技巧,让你的设计呈现更加完美
  7. Powershell: powershell 获取本机IP地址
  8. 程序代码移植和烧录需要注意什么_牙齿矫正需要注意什么?
  9. CH24C 逃不掉的路
  10. 大数据如何学习 cda认证_大数据学习之学习要求
  11. linux命令学习之---- file
  12. 如何获取qq群成员的资料信息(爬虫)
  13. java 1.5 jdk_jdk1.5安装及配置
  14. HDLBITS 练习 卡诺图化简部分
  15. c语言的if语句案例,c语言if语句(c语言if语句例子)
  16. PAT甲级准备方法(附2021年PAT甲级秋季考试题解)
  17. java web 播放flv,实现网页中播放FLV文件的源代码
  18. Mac OS X任务管理器
  19. 《算法笔记》3.6小节——入门模拟->字符串处理 问题 B: 首字母大写
  20. 旅行社旅游APP开发维护经验

热门文章

  1. Linux 包管理基础:apt、yum、dnf 和 pkg
  2. 使用deepstream对自己模型进行加速推理以及与ROS通信
  3. 四大游戏编程网站,边玩边学Python
  4. 新blog开张...
  5. Xshell 6、Xftp 6、Xmanager 6 免费版下载
  6. 《乔布斯传》英文原著重点词汇笔记(三)【 chapter one】
  7. 计算机四级网络技术题库套,全国计算机等级考试四级计算机网络第6套试题(3)...
  8. google地图静态下载和js调用
  9. 讲座录播|Wook-Shin Han教授:Combining Sampling and Synopses with Worst-Case Optimal Runtime and Quality Gua
  10. 集合(Set、Collections、Map、集合嵌套)