写程序纯粹是我的一个爱好。我也没有想到自己能找到比较高效支持浏览器三大组件(HTML, CSS和JavaScript)的方法,并将它们编写到一个程序里,协同运行。以前总觉得Javascript肯定是我无法逾越的坎, 很高大上的东西。我相信大家看了我接下来分享的文章后一定会觉得自己花在学习编译原理方面的时间已经很多了。原来没有那么复杂。
我这里说的是系统性地跑起来, 不是支持几个语句什么的。当然要完成整体系统有很多代码要写,而且还有很多细节。Javascript还涉及与DOM的协同工作,可能还需要系统性的调整。
言归正传,我先介绍一下整体思路。
1. 第一步是分步加载js文件或html文件中的Javascript代码。 所谓加载即大家学习编译原理最熟悉的tokenize, 把JS源码变成容易识别且具有语法意义的Token, 并全部放在一个数组里。我现在这些分类还不全面,随着代码的推进需不断完善。这部分和几乎所有的编译原理书上写的基本一致。
enum JSTokenType {
JS_TOKEN_INTEGER,
JS_TOKEN_DOUBLE,
JS_TOKEN_STRING,
JS_TOKEN_NAME,
JS_TOKEN_OPERATOR,
JS_TOKEN_LOGICOP,
JS_TOKEN_ASSIGNMENT,
JS_TOKEN_COUNT
};
struct JSToken {
JSTokenType jsTokenType;
std::string strToken;
};
std::vector<JSToken> vJSTokens;
2. 第二部分是Parsing, 这部分我的方法和编译原理书上讲的差别比较大, 我这里没有AST,我这儿是放在表里, 而且除了函数function外不是将源代码放在表里, 而是把能计算的值都计算出来放在表里。
这一部分是整个程序的关键。 概括起来是这样:我们需要几个表来保存JavaScript的关键词, 如var, document, alert等等, 然后再有全局变量表和局部变量表。程序就可以运行起来了。
具体来讲, 以我以下一段测试代码来举例
<script language="JavaScript">
var x =7;
var y;
y = "Good!";
var z = -5 +2.5-8*2;
document.write('<h2 align="center">欢迎来到javascript世界</h2>');
alert("How are you?");
function myFunction() {
var a = "IT's a new innerHTML.";
{
var b = a;
}
document.getElementById("demo").innerHTML = a;
}
if (!((x+3) >5 && x && x==3)) {
document.write("<p>1235689"+y+"Nice!"); //note
} else if (5<5){}
else {
document.write("<p>12345");
}
myFunction();
document.write("<p>abcdefgijk12345689ABCDEFGHIJKlnop");
/*ttt*/
</script>
这里面主要包含三类代码:
1. 可执行语句,包括alert, document.write, 包括if也是, 函数的调用也是。
2. 变量的定义与赋值。
3. 函数的定义。
其中可执行语句执行过后就结束了, 不用保存起来。 所以要保存到表里的就是变量和function, 变量的值能计算出来的就把值计算出来。实际上function也是变量, 比如 var myFunc = function () { abc = 1; } 就是定义一个名为myFunc的函数,和 funciton myFunc() { abc = 1;} 是等价的。 所以最后就把它们都放在一个表里。
所以上面杂乱的程序里最后保存到变量表里就只有:
x = 7; //整形
y= "good"; //字符串
z = -18.5 ; //double类型
myFunction = () { var a = "IT's a new innerHTML."; { var b = a; } document.getElementById("demo").innerHTML = a; }
是不是很简单;当然myFunction保存的是tokens, 不是以上的字符串。
那接下来我就说说如何保存以上的变量和如何执行这些语句, 其实很简单。
先看看变量表(为了结构清晰我省略了这个类特别重要的构造函数和析构函数)
class JSVarTable {
public:
enum VarType {
JS_VAR_UNDEFINED,
JS_VAR_NULL