博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

自己动手开发语言.笔记@2014-1-13

Posted on 2014-01-13 21:53  xuld  阅读(770)  评论(0编辑  收藏  举报

经过许久的折腾,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);
}