MYC编译器源码分析

整个编译器是用C#语言写的,上图列出了MyC编译器编译一个C源文件的过程,编译主路径如下:

首先是入口Main函数用来解析命令行参数,读取源文件,并开始编译过程。Main函数在MyC.cs文件,而IO.cs文件主要保存读取源码文件的相关操作。下表是Main函数的源码(批注用注释的方式显示),IO.cs文件用单独的一个小节说明:

public static void Main()

{

try

{

// 看源码注释,代码是99年写的,也就是说.NET当年正在开发中

// 可能那个时候虚拟机都没有做好向Main函数传递命令行参数的开发

// 用了下面这个奇葩方法获取程序的命令行参数

String[] args = Environment.GetCommandLineArgs();

// 初始化读取源文件的IO对象,该对象负责将源文件以字节流的方式

// 输出给下一个对象 – 词法分析

Io prog = new Io(args);

// 词法分析对象,该对象的工作是过滤掉源码中不必要的字符,比如空格

// 注释之类的,并且把源码中的字符归类 – Tokenize,以便语法分析器

// 更方便的解析语法

Tok tok = new Tok(prog);

// 语法分析对象,解析完毕后即是代码生成阶段,但一般语法分析过程

// 都只会生成语法树,这样的设计可以对接多种结果文件输出手段。比如

// 说,本例中生成可执行文件的exe.cs和生成IL源码的asm.cs都是通过

// 遍历语法树,使用不同的输出策略生成结果文件的

Parse p = new Parse(prog, tok);

// 采用自顶向下的方式进行语法解析

p.program();

// 编译工作已经完成,关闭打开的源文件句柄等资源

prog.Finish();

}

catch (Exception e)

{

// 编译过程中有任何错误,即中断处理,打印错误消息并退出程序

Console.WriteLine("Compiler aborting: " + e.ToString());

}

}

MyC的语法很简单,因此编译过程是很干净的词法分析、语法分析、代码生成和结果输出的过程。其中词法分析代码在tok.cs,语法分析代码在parse.cs中,Emit.cs处理代码生成,而Asm.cs和Exe.cs分别根据命令行参数的设置,来生成最终的可执行文件并选择是否输出IL源码。 IO.cs - IO处理 将源文件读取进内存,并采用流式处理的代码都放在IO这个类里面,IO的构造函数解析命令行参数,并打开源文件,等待Tok.cs里面代码的指令将源文件的字符一个个读进内存并处理,下面是它的构造函数的源码:

public Io(String[] a)

{

int i;

args = a;

// 解析命令行参数,并根据参数打开内部的控制开关,详情请看下面对ParseArgs

// 函数的源码解读

ParseArgs();

// 打开要编译的源文件

ifile = new FileStream(ifilename, FileMode.Open,

FileAccess.Read, FileShare.Read, 8192);

// 如果源文件不存在,报错退出

if (ifile == null)

{

Abort("Could not open file '"+ifilename+"'\n");

}

// 采用流式处理方式读取源文件

rfile = new StreamReader(ifile); // open up a stream for reading

// 根据源文件的名称设定结果输出文件的文件名

i = ifilename.LastIndexOf('.');

if (i < 0)

Abort("Bad filename '"+ifilename+"'");

int j = ifilename.LastIndexOf('\\');

if (j < 0)

j = 0;

else

j++;

classname = ifilename.Substring(j,i-j);

// 根据命令行参数决定是生成.exe、.dll等可执行文件,还是输出包含

// IL源码的.lst文件

if (genexe)

ofilename = classname+".exe";

if (gendll)

ofilename = classname+".dll";

if (genlist)

{

// 如果是要输出IL源码,因为原来的可执行文件也要输出,需要创建一个新的文件

lst_ofilename = classname+".lst";

lst_ofile = new FileStream(lst_ofilename, FileMode.Create,

FileAccess.Write, FileShare.Write, 8192);

if (lst_ofile == null)

Abort("Could not open file '"+ofilename+"'\n");

lst_wfile = new StreamWriter(lst_ofile);

}

}

编译器是在IO类里处理命令行参数的,参数解析实际上是一些字符串处理的活,本文解释下关键代码:

void ParseArgs()

{

int i = 1;

// 程序至少需要两个参数,否则就输出帮助文字并退出

if (args.Length < 2)

{

Abort("myc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");

}

// 逐个遍历命令行参数

while (true)

{

if (args[i][0] != '/')

break;

// 处理 /? 这个参数,即输出帮助文本

if (args[i].Equals("/?"))

{

Console.WriteLine("Compiler options:\n  myc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");

Environment.Exit(1);

}

// 如果有 /debug 参数,则打开内部的 gendebug 开关,这个开关在代码生成的过程

// 中会用到

if (args[i].Equals("/debug"))

{

gendebug = true;

i++;

continue;

}

// ... ... 跳过类似的代码

// 如果有 /outdir 参数,则获取命令行中指定的目录路径

if (args[i].Length > 8 && args[i].Substring(0,8).Equals("/outdir:"))

{

genpath = args[i].Substring(8);

i++;

continue;

}

// 前面那么多的if相当于switch … case … default 块里面的 case 处理路径

// 下面这段代码即是 default 处理路径 – 如果命令行参数符合前面的if条件

// 都会执行里面的 continue 子句跳出循环,能执行到这里,说明参数

// 是无法识别的参数,因此报告错误并退出执行

Abort("Unmatched switch = '"+args[i]+"'\nArguments are:\nmyc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");

}

// 如果前面的循环执行完毕,还有参数列表未处理,说明输入了不支持的参数

if (args.Length-i != 1)

{

Abort("myc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");

}

// 最后一个参数是要编译的源文件路径

ifilename = args[args.Length-1]; // filename is last

}

IO类中大部分函数都是为Tok.cs服务的,因此其它函数在解释词法分析的时候说明

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,552评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,666评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,519评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,180评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,205评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,344评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,781评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,449评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,635评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,467评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,515评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,217评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,775评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,851评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,084评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,637评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,204评论 2 341

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,570评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,325评论 25 707
  • Ubuntu的发音 Ubuntu,源于非洲祖鲁人和科萨人的语言,发作 oo-boon-too 的音。了解发音是有意...
    萤火虫de梦阅读 99,100评论 9 467
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,546评论 18 399
  • 今天早晨,一睁眼就习惯性的拿起手机点开微信行动营的群,看晨读素材,没有,嗯,没有……嗯?没有?!哦……想起来了,结...
    兜兜有糖902阅读 212评论 0 0