zoukankan      html  css  js  c++  java
  • 解释器漫谈

    最近连续做了三个语言解析器,一个是SQL的,另外两个是我自己设计的语法格式,我分别把它们叫做XGML和GORL。
     
    XGML是继承自XML的一种语法格式。本来我是希望直接使用.Net自带的XML读写类的,但是发现XML对符号的限制实在太多,我需要大量地在元素名、属性名、值中写入语法符号(如尖括号,方括号,大括号等),经过一番试验,发现直接使用XML完全没有可能,于是我决定自己动手来实现一个XML的读写类。
     
    既然自己实现了XML的读写器,我后来觉得干脆进一步扩展其功能,比如我允许每个元素定义自己的特性(Attribute),并且为每种结构定义了语法含义,而且在语法形式上也有所修改,最重要的是它们能够直接映射成为C#对象,这样一来,它就形成了一种类似但不同于XML的新的东西,我于是称其为XGML。
     
    在XGML的设计之初,为了方便起见,我为其定义了两种语法形式,一种是基于XML格式的主体语法,另一种是类似于JSON的嵌入式语法,用于辅助XGML的对象映射功能,这种嵌入式语法我称其为InlineXGML。但是在试用了一段时间以后,我发现短小精悍的InlineXGML在使用过程中要远远比冗长的XGML来得方便,我开始认真考虑用InlineXGML完全替代XGML的可能性。
     
    经过一番考量,我觉得这完全是可行的,于是我重新设计了InlineXGML的语法规范,使其在功能上能够完全替代XGML,而且还包括一些XGML无法实现的功能,例如我非常喜欢的默认属性、集合构造器等,这样一来,再称其为InlineXGML显然是不合时宜了,于是我为它起了个新的名字,叫做Goodbaby Object Rendering Language(GORL)。
     
    GORL的设计目的非常明确,就是呈现一个对象,这与XGML有所不同,XGML虽然也能够呈现对象,但是由于XML本质上的松散结构,XGML实际上还包含许多非对象呈现的因素,这也导致它很难进一步为对象呈现作优化。而GORL则完全是用于呈现一个对象,虽然它继承了一部分XML的概念,但是完全不受XML的束缚。
     
    例如,定义一个C#类型Apple,它有一个string型的属性Name,Dictionary<int,string>型的属性Standard,用GORL表示为:
    {Apple Name=apple1 Standard=[(1,'good morning'),(2,hello)]}
    或者可以把Name设定为默认属性,这样去定义另一个对象:
    {Apple apple2} ,

    如果只想表示一个值类型的数组(这种需求一般不存在):

    {int[]=[3,4,5]},

    可以为类或属性定义特性:

    {[MyAttribute]Apple apple3}
     
    对于XGML或GORL来说,解析其语法只是实现了第一步,第二步是执行部分,需要把上述GORL代码转换成一个C#对象,在实现机制上,GORL的性能是非常好的,首先我将所有的值类型、以及常用类型、用户自定义类型都加入字典,当分析一个类型的时候,首先去查字典,一般来说,99%以上的概率会在字典中命中,如果是罕见类型,未能在字典中查到,则搜索当前程序集,然后是mscorlib,再然后其它程序集,一旦在任何一个程序集中找到类型,则将此类型加入字典,所以无论什么类型,连续使用时,GORL解释器的返回性能都是极高的。
     
    在对象值的解析上,我为常见类型进行了单独的优化设计,所以绝大多数情况下,使用GORL序列化/反序列化的性能要远远高于其它任何一种类似技术。
     
    这样说我是有依据的。在实现GORL之前,我曾经犹豫过很长一段时间是否考虑使用JSON,虽然使用JSON会受到各种约束,但是毕竟会比较省事,这期间我看了JSON在.net中的实现代码,真的是非常复杂,后来考虑到GORL也可以把JSON集成进来作为GORL的一部分功能,我决定不能把自己绑在JSON上,使自己以后处处被动,还是要实现GORL。经过一天半的努力,在今天凌晨0点之前,终于完成了GORL的绝大部分设计功能,还有一小部分功能暂时用不上,以后有心情再做,而总代码只有不足1300行!
     
    当然GORL的顺利实现是建立在SQL解析器的基础上的。
     
    语法解析部分应该说是编译中最复杂的部分,在做SQL解析器多年之前,我已经实现过一个简单的批处理解释器,自觉对解析源代码还是比较有经验的,加之我一向对自己处理复杂程序的能力信心十足,所以我一开始打算一遍完成语法分析。
     
    “遍”是编译原理中很基本的一个概念,在正统的流程中,解析一种语言会经过很多遍代码扫描,但是如果能够在一遍中完成词法和语法的解析,效率显然会更高一些。由于SQL是一种相对来说比较简单的语言,所以我觉得一遍完成是可能的。
     
    但是,事实是很残酷的,在我经过两天焦头烂额的编程之后,我决定在自己吐血之前放弃这个想法。SQL虽然比较简单,但是它太灵活多变了,子查询和表达式的任意嵌套让我一度怀疑自己是否真的有可能写出这个解析器。经历了一次失败以后,我端正了心态,不能幻想着一遍实现这种有点荒唐的想法,必须严格按流程来,先实现词法分析器,生成中间表,再去分层、逐步实现语法分析。
     
    新的程序很快就出来了,我只用了一天就完成了词法分析器,当我在词法分析器工作的基础上进行语法分析时,那叫一个安逸!回想起此前一踏糊涂的工作场景,此刻我才真正体会到了前人的智慧是不容随意挑战的,“大跃进”是不能随便搞的…… 建立在词法分析基础上的语法分析流程非常清晰,虽然编程量不小,但是写程序的过程 中还是很轻松的,完全不需要绞尽脑汁地应对千头万绪的上下文以至于整个程序有失控的风险。 全部工作完成用了一周半的时间。
     
    至此,我觉得实现一个真正的编译器也完全有思路了。相对于语法极为松散的sql,我觉得实现一个C语言的解析器说不定会更加轻松一些。至于解析以后的生成目标代码部分,事实上,根据对象结构生成某种代码是非常容易的,较为复杂的是如何进行代码优化,如果不考虑优化这回事,只要生成相应的汇编就算是一个编译器了。
     
    有了SQL的基础,再去解析XGML或GORL就变得非常简单了,虽然在实现GORL之前我一度犹豫过,但是现在我已经非常庆幸自己当时没有去偷这个懒,GORL的解析/执行都是如此简洁,它的性能和扩展性都远非JSON可比,更何况它还像XGML一样支持特性?

    ---------------------------------------------

    作者:夏狼哉
    博客:http://www.cnblogs.com/Moosdau

    如需引用,敬请保留作者信息,谢谢

  • 相关阅读:
    String/StringBuffer
    二维数组的打印,查找等
    二叉树的各种遍历
    本地安装部署ActiveCollab
    为什么我们不使用JIRA
    本地安装部署禅道
    本地安装部署Jira
    拖拉插件 drag drop
    C++二维数组 取地址 复制给 二维指针
    解决:CentOS下的 error while loading shared libraries: libmysqlclient.so.16: cannot open shared object file: No such file or dir
  • 原文地址:https://www.cnblogs.com/Moosdau/p/2407237.html
Copyright © 2011-2022 走看看