前文.NET框架源码解读之MYC编译器讲了MyC编译器的架构,整个编译器是用C#语言写的,上图列出了MyC编译器编译一个C源文件的过程,编译主路径如下:

  1. 首先是入口Main函数用来解析命令行参数,读取源文件,并开始编译过程。Main函数在MyC.cs文件,而IO.cs文件主要保存读取源码文件的相关操作。下表是Main函数的源码(批注用注释的方式显示),IO.cs文件用单独的一个小节说明:
  1. public static void Main()
  2. {
  3. try
  4. {
  5.  // 看源码注释,代码是99年写的,也就是说.NET当年正在开发中
  6. // 可能那个时候虚拟机都没有做好向Main函数传递命令行参数的开发
  7. // 用了下面这个奇葩方法获取程序的命令行参数
  8. String[] args = Environment.GetCommandLineArgs();
  9. // 初始化读取源文件的IO对象,该对象负责将源文件以字节流的方式
  10. // 输出给下一个对象 – 词法分析
  11. Io prog = new Io(args);
  12. // 词法分析对象,该对象的工作是过滤掉源码中不必要的字符,比如空格
  13. // 注释之类的,并且把源码中的字符归类 – Tokenize,以便语法分析器
  14. // 更方便的解析语法
  15. Tok tok = new Tok(prog);
  16. // 语法分析对象,解析完毕后即是代码生成阶段,但一般语法分析过程
  17. // 都只会生成语法树,这样的设计可以对接多种结果文件输出手段。比如
  18. // 说,本例中生成可执行文件的exe.cs和生成IL源码的asm.cs都是通过
  19. // 遍历语法树,使用不同的输出策略生成结果文件的
  20. Parse p = new Parse(prog, tok);
  21. // 采用自顶向下的方式进行语法解析
  22. p.program();
  23. // 编译工作已经完成,关闭打开的源文件句柄等资源
  24. prog.Finish();
  25. }
  26. catch (Exception e)
  27. {
  28. // 编译过程中有任何错误,即中断处理,打印错误消息并退出程序
  29. Console.WriteLine("Compiler aborting: " + e.ToString());
  30. }
  31. }

  

  1. MyC的语法很简单,因此编译过程是很干净的词法分析、语法分析、代码生成和结果输出的过程。其中词法分析代码在tok.cs,语法分析代码在parse.cs中,Emit.cs处理代码生成,而Asm.cs和Exe.cs分别根据命令行参数的设置,来生成最终的可执行文件并选择是否输出IL源码。 IO.cs - IO处理 将源文件读取进内存,并采用流式处理的代码都放在IO这个类里面,IO的构造函数解析命令行参数,并打开源文件,等待Tok.cs里面代码的指令将源文件的字符一个个读进内存并处理,下面是它的构造函数的源码:
  1. public Io(String[] a)
  2. {
  3. int i;
  4.  
  5. args = a;
  6. // 解析命令行参数,并根据参数打开内部的控制开关,详情请看下面对ParseArgs
  7. // 函数的源码解读
  8. ParseArgs();
  9.  
  10. // 打开要编译的源文件
  11. ifile = new FileStream(ifilename, FileMode.Open,
  12. FileAccess.Read, FileShare.Read, 8192);
  13. // 如果源文件不存在,报错退出
  14. if (ifile == null)
  15. {
  16. Abort("Could not open file '"+ifilename+"'\n");
  17. }
  18. // 采用流式处理方式读取源文件
  19. rfile = new StreamReader(ifile); // open up a stream for reading
  20.  
  21. // 根据源文件的名称设定结果输出文件的文件名
  22. i = ifilename.LastIndexOf('.');
  23. if (i < 0)
  24. Abort("Bad filename '"+ifilename+"'");
  25. int j = ifilename.LastIndexOf('\\');
  26. if (j < 0)
  27. j = 0;
  28. else
  29. j++;
  30.  
  31. classname = ifilename.Substring(j,i-j);
  32.  
  33. // 根据命令行参数决定是生成.exe、.dll等可执行文件,还是输出包含
  34. // IL源码的.lst文件
  35. if (genexe)
  36. ofilename = classname+".exe";
  37. if (gendll)
  38. ofilename = classname+".dll";
  39. if (genlist)
  40. {
  41. // 如果是要输出IL源码,因为原来的可执行文件也要输出,需要创建一个新的文件
  42. lst_ofilename = classname+".lst";
  43. lst_ofile = new FileStream(lst_ofilename, FileMode.Create,
  44. FileAccess.Write, FileShare.Write, 8192);
  45. if (lst_ofile == null)
  46. Abort("Could not open file '"+ofilename+"'\n");
  47. lst_wfile = new StreamWriter(lst_ofile);
  48. }
  49. }

  

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

  1. void ParseArgs()
  2. {
  3. int i = 1;
  4.  
  5. // 程序至少需要两个参数,否则就输出帮助文字并退出
  6. if (args.Length < 2)
  7. {
  8. Abort("myc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");
  9. }
  10.  
  11. // 逐个遍历命令行参数
  12. while (true)
  13. {
  14. if (args[i][0] != '/')
  15. break;
  16. // 处理 /? 这个参数,即输出帮助文本
  17. if (args[i].Equals("/?"))
  18. {
  19. Console.WriteLine("Compiler options:\n myc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");
  20. Environment.Exit(1);
  21. }
  22. // 如果有 /debug 参数,则打开内部的 gendebug 开关,这个开关在代码生成的过程
  23. // 中会用到
  24. if (args[i].Equals("/debug"))
  25. {
  26. gendebug = true;
  27. i++;
  28. continue;
  29. }
  30. // ... ... 跳过类似的代码
  31. // 如果有 /outdir 参数,则获取命令行中指定的目录路径
  32. if (args[i].Length > 8 && args[i].Substring(0,8).Equals("/outdir:"))
  33. {
  34. genpath = args[i].Substring(8);
  35. i++;
  36. continue;
  37. }
  38. // 前面那么多的if相当于switch … case … default 块里面的 case 处理路径
  39. // 下面这段代码即是 default 处理路径 – 如果命令行参数符合前面的if条件
  40. // 都会执行里面的 continue 子句跳出循环,能执行到这里,说明参数
  41. // 是无法识别的参数,因此报告错误并退出执行
  42. Abort("Unmatched switch = '"+args[i]+"'\nArguments are:\nmyc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");
  43. }
  44.  
  45. // 如果前面的循环执行完毕,还有参数列表未处理,说明输入了不支持的参数
  46. if (args.Length-i != 1)
  47. {
  48. Abort("myc [/debug] [/nodebug] [/list] [/dll] [/exe] [/outdir:path] filename.myc\n");
  49. }
  50.  
  51. // 最后一个参数是要编译的源文件路径
  52. ifilename = args[args.Length-1]; // filename is last
  53. }

  

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

MYC编译器源码分析之程序入口的更多相关文章

  1. MYC编译器源码之词法分析

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

  2. MongoDB源码分析——mongod程序源码入口分析

    Edit 说明:第一次写笔记,之前都是看别人写的,觉得很简单,开始写了之后才发现真的很难,不知道该怎么分析,这篇文章也参考了很多前辈对MongoDB源码的分析,也有一些自己的理解,后续将会继续分析其他 ...

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

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

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

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

  5. MongoDB源码分析——mongo主程序入口分析

    Edit   源码版本为MongoDB 2.6分支 mongo主程序入口分析 mongo是MongoDB提供的一个执行JavaScript脚本的客户端工具,可以用来和服务端交互,2.6版本的Mongo ...

  6. Django源码分析之程序执行入口分析

    一般我们开启一个django项目,最简单的方法是进入project 目录,这时目录结构是这样的 然后我们执行python manage.py runserver,程序就开始执行了. 那django是如 ...

  7. yii2 源码分析1从入口开始

    我是在 backend 一步步打印的 很多地方我也是很模糊 .后来发现一位大神的文章(http://www.yiichina.com/tutorial/773) 参考文章自己动手开始写的 至于后来的 ...

  8. Django源码分析之执行入口

    魔法门 一般我们启动django,最简单的方法是进入project 目录,这时目录结构是这样的 然后我们执行python manage.py runserver,程序就开始执行了. 那django是如 ...

  9. Spring基础系列-AOP源码分析

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...

随机推荐

  1. nohup 后台运行命令

    在Linux上部署zipkin,在SSH客户端执行java -jar zipkin-server-1.21.0-exec.jar,启动成功,在关闭SSH客户端后,运行的程序也同时终止了,怎样才能保证在 ...

  2. Codeforces Round #474-E(树形dp)

    一.题目链接 http://codeforces.com/contest/960/problem/B 二.题意 给定一棵$N$个节点的树,每个节点的权值$V$.定义树中两点$u_1$和$u_m$的权值 ...

  3. 2017CCSP总结——失败(铜)

    这次比赛,算是铩羽而归.尽管是第一次出去打比赛,在经验方面略显不足,但是,归根到底,我这次比赛打的很失败.包括我们学校去的,打的也不好,可以说是全体翻车.真的很对不起带我们去的老师.>_< ...

  4. undo表空间居高不下和enq: US - contention

    这几天遇到一个错误,我也不知道算不算错误吧,因为没有报错,只是在那突然的短短2分钟内表的操作突然降低了,导致了该软件重新启动.查看alert日志没有报错,而是在ASH里找到了TOP SQL框有一个这样 ...

  5. Linux 性能分析的前 60 秒

    编译自:http://techblog.netflix.com/2015/11/linux-performance-analysis-in-60s.html作者: Brendan Gregg转载自:h ...

  6. 将Hive统计分析结果导入到MySQL数据库表中(一)——Sqoop导入方式

    https://blog.csdn.net/niityzu/article/details/45190787 交通流的数据分析,需求是对于海量的城市交通数据,需要使用MapReduce清洗后导入到HB ...

  7. SpringBoot2.0实现静态资源版本控制

    写在最前面 犹记毕业第一年时,公司每次发布完成后,都会在一个群里通知[版本更新,各部门清理缓存,有问题及时反馈]之类的话.归根结底就是资源缓存的问题,浏览器会将请求到的静态资源,如JS.CSS等文件缓 ...

  8. Django学习---jsonp跨域请求

    jsonp跨域请求 我们通过ajax进行跨域请求的时候,请求发送过去,但是在接受返回数据的时候浏览器会进行拦截. 这是由于浏览器存在同源策略机制,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源 ...

  9. 好记性不如烂笔头--linux学习笔记8关于nginx的动静分离

    动静分离逻辑梳理 就是给nginx配置访问规则,不同后缀的文件访问不同的目录 worker_processes 1; events { worker_connections 1024; } http ...

  10. 「小程序JAVA实战」小程序的分享和下载功能(69)

    转自:https://idig8.com/2018/09/25/xiaochengxujavashizhanxiaochengxudefenxianghexiazaigongneng68/ 在小程序上 ...