语言实现的几种方式
语言是一种规范,要想使用它,需要将其实体化,用编译原理的话讲,可以大致分为编译器和解释器,而根据之前说的,如果把目标代码的解释过程看做解析过程,比如x86机器指令load到cpu时,根据不同的指令进行不同的运算,以及字节码的分派等,这个界限是模糊的
因此,这个事情可以这么讲,假设我们已经有一台机器M,它可以接收A语言并完成计算工作,则对于高级语言B来说,我们只要将其转换成A语言即可。根据实际情况,大致有这么三种
M是个真实机器,A是机器语言,这种方案原因可能有: 一,没有其他方案可选,比如早期的高级语言,只能直接编译成机器码执行 二,为了效率,比如大部分C编译器的实现,或java的jit之类的 三,打包发布方便,比如发布java的class文件,需要目标机器有环境,直接打包可以让目标系统更轻量级 这种方案的缺点是很明显的,首先机器代码就是比较复杂的东西,而且不安全,开发成本太高了
M是虚拟机,A是虚拟机支持的字节码或类似语言,采用这种方案实现的语言非常多,比如java,python,ruby等等,因为虚拟机实现语言执行,有很多优点,跨平台,执行过程可控,也方便实现一些特性,比如协程机制等。这种方案的缺点在于,虚拟机解释执行速度是比较慢的,需要依赖jit或代码静态分析上大量的优化,而jit又存在上述方案的问题
A同样是一个高级语言或已存在规范的其他语言的目标低级语言,M是支持A的一整套编译-运行环境,对于语言B,我们只需要将其编译成A,即可运行。这种实现最大的优势是性价比高,且AB可以无缝结合,不过一般好像很少见到,例如scala就是这样一种,scala的代码直接编译成java的字节码,还有Cython,可以把python(含pyrex语法)转换成等价的C代码。我最早听到这方面的是暴雪公司的一个采访里面的,他们的技术人员透露,游戏开发就是用类似脚本的语言写好,然后转成C,做进一步优化,这样可以兼顾开发和运行效率。这种方案的问题在于,要想简单稳定地实现,AB两种语言最好比较相似,比如java和C++,如果给C++加点料,增加了GC等机制的话,那么可以很方便地将java转为C++,但是,这样一来并没有解决开发成本的问题,如果C++库够用的话,甚至还不如直接写C++,因为java一样啰嗦;反过来,如果两种语言差别比较大,那就会存在一些转换上的难点,比如一个函数式编程转一个命令式编程,这还是相对好解决的(Haskell直接转机器码,机器码是命令式的),但反过来一个命令式的转函数式的,可能不是那么好分析。还有语法和设计的差异,比如说,一个java代码转C++,可以把所有引用改成指针,因为本身就是这么实现的,但是,如果把C++转java,那么别名引用,结构体赋值,指针运算,模板甚至指针类型强转这些东西就得特殊处理了,效果可能大打折扣
可以看到,以上三种方式的区别就在于程序的运行平台(硬件,自研和借用),如果自己造语言,第一种成本显然太高了,第二种的话,做出来效率可能不高,还有垃圾收集等麻烦事,达不到要求,所以,我最后还是选用第三种方式,执行载体使用java,也就是设计一种语言,将其转换为等价的java代码,然后利用jdk和jre来执行,达到效果。具体的语言设计,以最熟的python作为原本
顺便说下,第三种方案在其他地方也用的很多,比如协议打包,可能会这样写:
*(unsigned int *)buf = htonl(a);
buf += sizeof(unsigned int);
......
这样做效率高,但是维护性太差,如果写通用的打包接口,效率又可能低,所以用工具分析协议格式配置文件,直接生成代码是两全的做法了