经过许久的折腾,hello world 是出来了。
目前语言自身的类库还未准备好,所以直接调用了.net库来实现 基本控制台输出功能。
为什么开发一个语言
很多人问我这个语言有什么优势,我想了下,优势就是它是自己的语言,可以DIY。
语言只是个工具,我不指望有多少人可以用它。但我的目标是
做一个既能像C#一样方便、又能像C++一样跨平台、不可反编译的语言。
开发一个语言到底有多难
编译原理是开发语言首先会想到的知识,而事实上,编译原理只是语言开发的一个很小的阶段。
很多人说自己懂编译原理,其实也就是了解了LR之类的高科技词语。这些人整天就知道做个解析器
解析一些代码,事实上他们的工作只限于解析,解析只是为了炫耀他们会编译原理。
真正开发一门语言,技术上需要至少需要包含这些东西:
1. 编译器(或解释器)
2. 运行库
一个脚本语言解释器由这些部分组成:
词法解析器 -> 语法解析器 -> 解释执行器
为了提高执行效率,解释器改进为以下部分组成:
词法解析器 -> 语法解析器 ->即时编译器(JIT)-> 代码优化器 -> 解释执行器
一个编程语言编译器由这些部分组成:
词法解析器 -> 语法解析器 -> 语义分析器 -> 代码优化器 -> 代码生成器
运行库是语言能执行的必要条件。比如 js 来说,内置对象就是运行库。对于 C++ 来说 <stdlib.h> 就是运行库。
如果需要开发一个包含 100 种语法的且真实能用的编程语言,那么需要的时间是:
词法解析器 1 + 语法解析器 10 + 语义分析器 100 + 代码优化器 + 30 + 代码生成器 100 + 运行库 1000 。
如果有人突然有兴趣想做个语言玩玩,然后花了11天时间终于折腾个语法解析器出来,对不起,你还需要 1230 天来完成整个语言。
所以,开发语言不仅是技术难题,更是时间难题。
开发语言会碰到的一些问题
我已经开发完语言的大部分内容,总结下几个难题,以及解决思路。当然,对于脚本语言,碰到的问题会少很多。
但是,我觉得像JavaScript, Python 都做的非常好了,真的我们也不需要新的脚本语言。
1. 语言如何调用操作系统的 API 实现文件读写操作?
2. 如何检测代码内部的死循环?用过 C# 的人都知道,下面的代码会得到一个警告:
void Main(){ while(true) { } return; // 警告: 检测到无法访问的代码 }
3. 如何支持泛型,支持GC,支持闭包,支持很多很多的流行特性?
也许这些问题你可以不解决,但试问,这些功能都没有的语言还值的用么。
脚本语言实现原理
我想更多的人应该期望给自己做个脚本语言玩玩,或者可以给工作提高很多效率。
这些脚本语言可以不需要自己的运行库,它可以转为现成的语言执行。
这样的语言开发起来其实不难,现在演示如何开发一个可以翻译为 JavaScript 的语言。
一、做语法解析器
自己写解析器其实很累,所以找一个现成语法生成器,用法可以看它的文档。语法解析器的目标是将语言代码字符串转换为语法树。
因为有太多教程介绍语法解析器做法,这里不再重点说明。
二、做代码转换器
语法树是代码解析后的一个数据结构,通过递归遍历语法树,可以知道代码中有什么,然后进行相应的转换操作。
var input = [ { type: 'if', condition: { type: 'int', value: '1' }, then: [ { type: 'funcCall', value: 'alert' } ] } ]; var output = []; function visitNode(node) { if(node.type == 'if') { return visitIf(node); } if(node.type == 'funcCall') { return visitFuncNode(node); } if(node.type == 'int') { return visitInt(node); } } function visitIf(node) { visitNode(node.condition); visitNode(node.then); visitNode(node.elseNode); } function visitFuncCall(node) { output.push(node.value); output.push('('); visitFuncArguments(node.arguments); output.push(')'); } function visitInt(node) { output.push(node.value); }