`
梁利锋
  • 浏览: 81370 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

D Parser 之前:写一个简单的虚拟机

阅读更多

  最近写了一点儿 D 程序,除了感觉标准库太差之外,没有一个好的 IDE 也是一个很头疼的事,特别是没有智能提示,每次调用一个函数什么的,都要查文档或者直接看源代码,实在是太费劲了。

 

  所以决定自己尝试写一个支持智能提示的 D 的 IDE。因为 SharpDelelop 比较小,而且它对 C# 的支持也做到了智能提示、窗体编辑器等等,所以决定用它作为主框架,除了智能提示,也许还能加入 DFL 的窗体编辑之类的功能(Entice 做的窗体编辑已经不错了,只是没有事件支持)。目前,已经完成了语法加亮,代码折叠(目前和 notepad++ 一样只是通过大括号匹配来做的),下一步,就是智能提示了,而智能提示就牵涉到语法分析。

 

  找了几个分析器生成器,试用之后,觉得 Grammatica 还不错,生成的代码比较清晰,调试起来也比较方便。照它的例子写了一个四则运算的分析器,还不错。

 

  看了一下 D 的语法详细列表,那也不是一般的复杂。所以,决定先写一个简单的语言的分析器、编译器和虚拟机练练手。今天先把虚拟机做了出来。

 

  这种语言的语法非常简单,姑且称之为 Z 语言吧(还没有细化):

 

声明语句: int x;        // 只支持 int
赋值语句: x = 1;
条件语句: if(x > 1) { ... } else { ... }
跳转语句: goto lable
标签语句: :lable
输出语句: write(x);        // 只支持 int
注释语句: // ... <eol>

 

  而虚拟机部分,参照 x86 asm,定义如下:

寄存器:        EAX, EBX, ESP, EIP        // EAX,EBX操作数,ESP堆栈指针,EIP指令指针
内存:          200000B,0B~99999B为堆栈,100000B~199999B为程序
指令:
      为EAX赋值:                       set eax, 1                // 01 01 00 00 00
        将当前堆栈地址变量复制到eax:     mov eax, *esp             // 02
        为EAX赋值:                       set ebx, 1                // 03 01 00 00 00
        将当前堆栈地址变量复制到ebx:     mov ebx, *esp             // 04
        为当前堆栈地址变量赋值eax:       mov *esp, eax             // 05
        为当前堆栈地址变量赋值ebx:       mov *esp, ebx             // 06
        esp 加运算:                      add esp, 1                // 07 01 00 00 00
        eax 加运算:                      add eax, ebx              // 08
        eax大于ebx?结果放eax:           gt                        // 11
        eax大于等于ebx?结果放eax:       gteq                      // 12
        eax等于ebx?结果放eax:           eq                        // 13
        eax bool not:                    not                       // 14
        eax 为非 0 跳转(相对):         if eax jmp {sp}           // 21 {sp}
        无条件跳转(相对):              jmb {sp}                  // 22 {sp}
        输出 eax:                        out                       // 31
        结束:                            over                      // ff

  

   另外,虚拟机需要能显示寄存器值,显示当前堆栈顶值,显示输出。支持单步执行。

 

  再写一段小程序,用来验证虚拟机的运行情况,因为只支持 int,所以计算 1 到 100 的和是一个比较合适的小代码段, C 的代码如下:

int n = 0;
for(int i=1; i<=100; i++)
{
    n += i;
}
write(n);

  Z 语言不支持 for 循环,所以,相应的 Z 代码大体如下:

int n = 0;
int i = 1;
:next
if(i > 100) { goto end; }
n += i;
i++;
goto next;
:end
write(n);

  

  而根据上面定义的指令集,其相应的汇编代码如下:

 

// esp n, esp+4 i;
// int n = 0;
set eax, 0                               // 01 00 00 00 00
mov *esp, eax                            // 05
// int i = 1;
add esp, 4                               // 07 04 00 00 00
set eax, 1                               // 01 01 00 00 00
mov *esp, eax                            // 05
// :next
// if(i > 100) { goto end; }
mov eax, *esp                            // 02
set ebx, 100                             // 03 64 00 00 00
gt                                       // 11
if eax jmb <end>                         // 21 1B 00 00 00
// n += i;
mov ebx *esp                             // 04
add esp, -4                              // 07 FC FF FF FF
 mov eax *esp                             // 02
add eax, ebx                             // 08
mov *esp, eax                            // 05
add esp, 4                               // 07 04 00 00 00
// i++;
mov eax, *esp                            // 02
set ebx, 1                               // 03 01 00 00 00
add eax, ebx                             // 08
mov *esp, eax                            // 05
// goto next;
jmb <next>                               // 22 D9 FF FF FF
// :end
// write(n);
add esp, -4                              // 07 FC FF FF FF
mov eax, *esp                            // 02
out                                      // 31
over                                     // FF


  虚拟机的代码不算复杂,VM 类拥有 eax, ebx, esp, eip 等属性,然后有一个函数 Step 提供执行一条指令的功能,在 Step 中,使用一个 switch 来处理不同的指令。之后,运行程序,把上面的汇编代码的字节序列写入 add.bin 文件中,用虚拟机加载,运行,得到结果:5050。

 

  在把 Z 转换到汇编的过程中,发现写编译器的话,对于寄存器的使用,是一个很需要考虑的问题,而对于 D 智能提示,只需要分析器就够了,似乎写编译器有一些超出了,不过,既然都写了,就试着先把这个完成吧。

 

  下面是源代码和运行截图:

  • ZLan.zip (45.8 KB)
  • 描述: ZVM 源代码
  • 下载次数: 56
  • 描述: 运行截图
  • 大小: 12.7 KB
16
3
分享到:
评论
26 楼 梁利锋 2008-01-11  
Z语言部分完成了:
D Parser 之前(二):汇编编译器 http://llf.iteye.com/blog/153818
D Parser 之前(三):Z 语言编译器 http://llf.iteye.com/blog/155151
25 楼 oldrev 2008-01-10  
引用
Copyright © 2003-2006 The University of Wroclaw.


貌似是波兰的吧,好像还有 MS Research 的资助
BSD的license,有鉴于麒麟OS,希望它可别真成了中国大学的“研究项目”
24 楼 梁利锋 2008-01-10  
刚想起来,VB 的类型转换不是使用 as 关键字的...
23 楼 梁利锋 2008-01-10  
我简单看了一下 Nemerle 的 tutorials,倒是没感觉符号用的太多。
  • 既然使用“:”作为类型声明符,那么用“:>”来做类型转换似乎也顺理成章。当然,像 VB 那样都用 as 关键字也不错。
  • 用“[]”声明数组,用“_”声明不需要使用的变量,在很多语言里,已经是约定俗成了。
  • 用 match 和“|”来代替 switch case 也是很多函数式编程语言选择的方式,比如 F#。
  • 在编写宏的时候需要用“<[]>”和“$”,也还说的过去,一来不需要操作 AST 了,二来毕竟并不是经常需要编写宏。


Nemerle 是大学的研究项目么?我倒没有注意,什么时候中国的大学研究项目是这种的,就好了。
22 楼 oldrev 2008-01-09  
Nemerle的缺点是作为大学的研究项目对于实际使用来说太过激进了一些,而且目前开发也不活跃。
我个人感觉 Nermerle 的符号用得太多,可读性不强。
21 楼 梁利锋 2008-01-09  
刚去 Nemerle 的网站看了一下,宏的使用确实比 Boo 强大一些,也容易使用一些。Boo 的宏除了语法上的一些限制之外,还需要直接操作 AST,难度确实比较大。
20 楼 oldrev 2008-01-09  
开源的引擎没法跟商业的比,游戏和其他软件不一样,不是单单靠程序员就能完成的。目前最好的开源引擎 Ogre 跟商业的比也差了很多。
玩玩 Mogre 应该不错,C++/CLI的包装,既有C++的效率,又有 .Net 的遍历性。
19 楼 oldrev 2008-01-09  
Boo 的那个宏目前只能模拟关键字的效果,还不能扩展语法的吧,现在只有 Nemerle 的可以
18 楼 梁利锋 2008-01-09  
另外,除了运行时反射,Boo 的语法构造宏,也可以提供类似 D 里的 Mixin 的效果。也就是可以实现编译时的语法扩展。
17 楼 梁利锋 2008-01-09  
Boo 确实比较像 Python,包括很多被宣传为动态语言特性的东西,Boo 都实现了。但是 Boo 是静态语言,编译出的程序集我反编译过,跟 C# 编译出的程序集基本没什么不同。而 IronPython 编译出的程序集就很杂乱,如果要 C# 反过来调用 IronPython 的话,可能就会比较麻烦。另外,Boo 也提供一个解释器,可以解释运行,这给调试带来了非常大的优势 —— 真正的“编辑并继续”。

jmonkey 我记得去看过,截图不错,我下载了一个示例(或者开源的游戏,记不清了),测试后的印象大概如下:(不敢保证是 jmonkey 的,以前下载过挺多这种东西的)
  1 是 3D 的
  2 WSAD 可以以第一人称视角移动
  3 贴图严重混乱

XNA Studio 我也玩过一下,比直接使用 DirectX 是简单一些,不过要形成一个可以称之为引擎的东西,所要做的东西还是很多。而且微软自己也说,XNA 是给业余作者和爱好者使用的。

至少现在来说,很难想象类似半条命2、孤岛惊魂、虚幻3、Quake3、魔兽世界或者使命召唤4之类的游戏会采用 Java/.Net 来开发。
16 楼 oldrev 2008-01-09  
游戏现在 .net/java 之流都开始有机会了,尤其是新成立的公司,你看看 jmonkey 网站,竟然有不少的用户。
15 楼 oldrev 2008-01-09  
说起Boo,我手头的项目就是用的 MonoRail,Boo 可以说是我每天都接触的语言了(虽然才刚入门)感觉也就比 Python 看着舒服那么一点,可选的 end,尤其是没有丑恶的 __init__ 之类
14 楼 梁利锋 2008-01-08  
我总是觉的,D 最好的出路是游戏开发和嵌入式设备开发。这两个方面,对于速度有一定的要求,而对于程序是否能被反编译,也更敏感一些,另外,对于是否有好的 IDE、第三方库之类的要求也比通用程序低。虽然 J2me、Java card、.Net CF、.Net Micro 也能用于嵌入式开发,不过,在那种资源受限的条件下,我基本上认定,他们是被解释执行,而不是JIT编译执行的。因此,D 在速度上的优势可能会更大一点儿。而且,这两个方面,一般也都会有一些 C 语言的积累,D 可以和 C 无缝集成,也是一个不错的特性。
13 楼 梁利锋 2008-01-08  
呵呵,反编译问题只是让人不太舒服罢了,确实也没什么可看的。其实,就算微软要把 Windows 的源代码给我,我还嫌占磁盘空间呢。

当然,.Net 和 Java 做服务器端很多,做客户端软件很少,一是启动比较慢,二是需要客户安装运行库。

但是即使做客户端,目前可能还是 Delphi 比较多,D 的优势也不明显。

以前找过一些 Java 和 .Net 的本机编译器,一个 Hello World 也能编译出7、8M,其实把大多数 Framework 都打包进去了,做大软件可能还好。不过总给人一种还不如让用户安装运行库的感觉。Mono 我是很久没 Follow 了,听说现在 WinForm 的程序也能运行的八八九九,可能还是不错的吧。

要说 .Net 语言,其实基本上就是 C#。vb.net 也处在一个尴尬的地位,很少有人用。其实,早期的时候,如果 BOO 语言(BOO 是静态语言,却包含很多动态语言特性,比如 Method Missing)能被微软收购的话,倒是能填上这个缺,可惜现在有了 IronPython 和 IronRuby,应该不可能了。(微软并没有收购 BOO 的意向,只不过是我的希望而已)
12 楼 oldrev 2008-01-08  
另外,.Net 的本机编译也不是什么太难的事情,用 Mono 的 --aot 选性就成,支持 x86和x86-64
11 楼 oldrev 2008-01-08  
.net/java 的反编译根本不是什么问题,不就是些mis么,还真没什么值得研究的(其实我也是做 mis 的.....)

apaged 确实是我没说清楚,这是一个很方便的解析器生成器,等于是结合了flex和yacc,但是实际上还是玩具水平。
10 楼 梁利锋 2008-01-08  
另外,请大家不要误会,写这个虚拟机不是为了把 D 编译成这个虚拟机的代码。只是以前没有写过 Parser Compiler,所以先从简单的入手而已。这个虚拟机非常简单,从构想到实现也只花了几个小时。并不是为了它有什么实际用途。
9 楼 梁利锋 2008-01-08  
@oldrev
和C++/CLI相比,也许是D比较适合吧,不过,我从.Net刚出就用.Net做开发,也差不多6、7年了,在工作中从来没有用过C++/CLI,也不觉得有这种需求。工作中确实见过一个别人用C++/CLI做的程序,因为结构太差,可读性也差,而我要维护它,所以完全被我改成C#了。

另外,C++/CLI 似乎在编译成 IL 的时候还是做了一些限制,并非所有的 C++ 特性都可以被编译成 IL。C++/CLI 因为可以混合 IL 和本机代码,所以基本上只是为了起粘合剂的作用。

比如 union 之类的结构,D 也支持,虽然我没有仔细研究过 IL,但是应该是不支持的。而如果只使用它的“单根继承,abstract/interface”之类的特性,和 C# 相比有什么不同?

dsource.org 上的 parser 项目,我下载了,代码我简单看了,确实没看懂。那两个 eclipse 插件,我都装过,试过,智能提示的功能都属于玩具级别。

另外,http://apaged.mainia.de/ 的那个,是用 D 写的 Parser 生成器,不是解析 D 的 Parser。http://seatd.mainia.de/ 那个编辑器,我也下载试了,和上面说的 eclipse 插件一样,还属于玩具级别。

其实我也是因为最近准备换工作,所以有一些时间,心血来潮想写这样一个 IDE,至于最后能不能完成,现在还不敢打保票。我写这个虚拟机、编译器,是因为它简单,而以前,我也没有写过这种程序。写了这些程序后,就能更好地理解怎么才能写出 D Parser。而如果要把 D 编译成 IL,则除了 D Parser 之外,还要写 D Compiler,那比单纯的 D Parser 就更难了,或者也可以说,只有在写出 D Parser 之后,才有可能考虑 D Compiler 的问题。另外,和 DMD 兼容性也是一个问题。

IL 我在用反射写一些程序的时候用到过,确实比 x86 汇编要直观,有了 D Parser 之后,把 D 的子集(不支持 C 接口和 C 结构,汇编,内联之类的功能等等)编译成 IL 还是可能的,另外,SharpDevelop 支持使用各种语言的 Parser 转换成中间结构,就可以提供每两种语言之间的代码的转换。所以应该可以支持 CSharp <=> D 之间的双向转换吧。当然,这种转换不见得能转换出无错的代码。

我之所以对 D 感兴趣,其实什么 C 的无缝集成,内联汇编,内置的单元测试,契约编程等等都不是重点。对我来说,本机编译才是。除了可能的速度上的提升(是否速度更快要看具体场景,另外大多数时候速度并不不是很重要),对于很多公司来说,Java 和 .Net 可以被反编译这件事,一直都是一个不大不小的问题。

关于 D,我觉得,它要抢夺 Java 或者 .Net 的阵营,基本上不可能。而作为本机编译的高级语言,做游戏开发,嵌入式设备开发是两个可能的途径。游戏开发方面,dsource 上已经有一些库,不过完成度好像都不高。而嵌入式设备开发,我去 GDC 的主页看过,他们说还没有支持,说是其它方面都没有问题,只是需要针对每一种 CPU 写垃圾回收器是现在的障碍。对于嵌入式设备的 D 编译器,也只有寄希望与 GDC 了……
8 楼 oldrev 2008-01-08  
D是单根继承,又有 abstract/interface 这些东东,总比 C++/CLI 适合 .Net 吧

dsource.org 上已经有很多个parser项目了,也有不下两个的 eclipse 插件
7 楼 梁利锋 2008-01-08  
@oldrev
D 的 Parser 见过几个,不过不太会用 

做虚拟机只是为了练手,“为 DMD 做一个生成 .Net IL 的后端”可要难多了,而且,老实说,把 D 编译成 .Net IL 意义不大。

相关推荐

    ua-parser-1.3.0

    ua-parser-1.3.0

    ua-parser-1.3.0.jar.rar

    ua-parser-1.3.0.jar,现在maven中http://maven.twttr.com/ua_parser/ua-parser/1.3.0/ua-parser-1.3.0.pom下载不下来。

    ua-parser-1.3.0.jar

    下载 ua-parser-master cd /app/ua-parser-master/java vi pom.xml &lt;version&gt;1.3.0&lt;/version&gt; 原来是&lt;version&gt;1.3.1-SNAPSHOT mvn package -DskipTests mvn install:install-file -Dfile="/app/ua-parser-master/...

    SGML::Parser::OpenSP-开源

    SGML::Parser::OpenSP 是一个Perl模块,它提供了一个用C++和XS编写的本地Perl接口,用于访问OpenSP(Open Source Parser)的SGML(Standard Generalized Markup Language)和XML(eXtensible Markup Language)解析...

    javaparservisited.pdf

    JavaParser 不仅仅是一个简单的语法解析器,它是一个完整的工具链,包括词法分析、语法分析、抽象语法树(AST)的构建以及对AST的遍历和修改。它支持Java语言的最新版本,可以处理现代Java代码的各种复杂特性。 这...

    javaparser-visited:《 JavaParser》一书的代码示例

    JavaParser:访问 该项目包含上书籍的代码示例 标题 JavaParser:已访问 作者 尼古拉斯·史密斯(Nicholas Smith),丹尼·范·布鲁根(Danny van Bruggen)和费德里科·托马塞蒂(Federico Tomassetti) 前导文字 ...

    视频编辑MP4 Parser代码与jar包

    《视频编辑MP4 Parser代码与jar包》 ...总的来说,MP4 Parser是一个强大的工具,对于需要处理MP4文件的开发者来说,它是不可或缺的资源。通过深入理解和实践提供的示例代码,你将能够自如地应对各种视频编辑任务。

    pull-parser-2.jar 工具類

    `pull-parser-2.jar`工具类库就是这样一个专门针对XML和JSON解析的利器,它为开发者提供了便捷、高效的解析方式,极大地优化了数据处理的流程。 XML(Extensible Markup Language)和JSON(JavaScript Object ...

    XMLParser iphone

    你可以创建一个`Item`类,然后在`parser(_:didStartElement:namespaceURI:qualifiedName:attributes:)`中开始创建新的`Item`实例,在`parser(_:foundCharacters:)`中收集属性值,并在`parser(_:didEndElement:...

    mp4parser工具箱中isoparser和muxer的jar包

    mp4parser中的视频处理方法需要依靠该jar包才能运行,该jar包是基于mp4parser-mp4parser-project-1.9.27打包的。使用方法可以参考我的CSDN博文,地址是http://blog.csdn.net/u014691453/article/details/53256605

    Perl-Parser-Combinators:Perl中的Parsec样式解析器组合器库

    Parser :: Combinators是一个解析器构件块(“ parser combinators”)库,其灵感来自Haskell( )中的Parsec解析器组合库。 。 这个想法是,您不是通过指定语法(例如yacc / lex或Parse :: RecDescent)来构建解析...

    IOS开发 XmlParser Demo

    本示例"IOS开发 XmlParser Demo"提供了一个简单的教程,帮助开发者理解如何在Xcode 4.2环境下集成并使用XMLParser。 首先,让我们了解XMLParser的基本工作原理。XMLParser遵循SAX(Simple API for XML)解析模式,...

    arg_parser 源码

    `arg_parser`是一个用于解析命令行参数的C++库,同时也支持C语言接口。这个库在软件开发中扮演着重要角色,特别是在需要通过命令行接口与用户交互的工具或服务中。下面我们将深入探讨`arg_parser`的核心概念、功能、...

    Parser_Generator.rar

    Parser_Generator是一个在Windows操作系统环境下使用的工具,主要用于生成解析器(Parser)和生成器(Generator)。解析器是计算机科学中的一个重要组件,它处理输入源代码,将其转化为抽象语法树(AST),这是理解...

    conch-parser:用于解析以Shell编程语言编写的程序的库

    快速开始首先,将其添加到您的Cargo.toml : [ dependencies ]conch-parser = " 0.1.0 " 接下来,您可以开始: use conch_parser :: lexer :: Lexer;use conch_parser :: parse :: DefaultParser;fn main () { // ...

    Simple-PHP-Code-Parser::red_heart:简单PHP代码解析器| php代码中的简单数据结构

    :red_heart: 简单PHP代码解析器 您可以简单地扫描字符串,文件或完整目录,并且可以从php代码中看到简单的数据结构。 类( PHPClass ) 属性( PHPProperties ) 常量( PHPConst ) 方法( PHPMethod ) 接口...

Global site tag (gtag.js) - Google Analytics