前兩天可以通過傳入一個(gè)整數(shù)河绽,拿到token和AST了剪芍。今天打算把AST編譯一下返劲,得到一個(gè)函數(shù)玲昧。
還是利用昨天提到的責(zé)任鏈模式,把一個(gè)新的compiler對(duì)象和之前的Lexer以及ASTBuilder對(duì)象結(jié)合起來篮绿,這樣也就終于有了一個(gè)編譯原理提到的三步走流程:
- 詞法分析
- 構(gòu)建抽象語(yǔ)法樹
- 編譯得到目標(biāo)結(jié)果
function Compiler(astBuilder){
this.astBuilder = astBuilder;
}
Compiler.prototype.compile = function (expression) {
this.astBuilder.ast(expression);
//return function 這里需要return一個(gè)function孵延。
};
關(guān)于為什么要返回這個(gè)函數(shù)在前面也寫了很詳細(xì)了,這里我再提醒自己一下:比如我運(yùn)行Compiler.compile('233')亲配,那么我需要返回的函數(shù)是這個(gè)函數(shù):
function (){
return 233;
}
顯然尘应,這個(gè)返回的函數(shù)內(nèi)容是和表達(dá)式相關(guān)的惶凝,那么如何返回一個(gè)動(dòng)態(tài)內(nèi)容的函數(shù)就是我面臨的一個(gè)問題,幸好有angular源碼犬钢,angular的做法是苍鲜,利用Function構(gòu)建方法,先試試這個(gè)構(gòu)建方法看看效果:
describe("Function構(gòu)造方法",function(){
it('試試Function構(gòu)造方法',function () {
var FnA = new Function('return 233;');
var value = FnA();
expect(value).toBe(233);
})
})
上面這個(gè)案例經(jīng)過測(cè)試是通過的玷犹。這也就說明混滔,我想動(dòng)態(tài)生成一個(gè)函數(shù),只要調(diào)用Function構(gòu)造方法并且往里面?zhèn)魅胍恍┳址托辛恕?br> 思路有了就開始干歹颓,首先要寫一個(gè)方法來遍歷AST樹坯屿,反正看到遍歷樹就肯定想到遞歸方法,那就寫個(gè)遞歸方法:
Compiler.prototype.recurse=function(ast){
switch (ast.type) {
case ASTBuilder.Program:
//這里暫時(shí)沒想好
break;
case ASTBuilder.Literal:
return ast.value;
break;
}
}
為上面的代碼做一個(gè)簡(jiǎn)單說明:
-
type==Literal
的節(jié)點(diǎn)肯定是一個(gè)葉節(jié)點(diǎn)巍扛,所以遇到這種節(jié)點(diǎn)就可以直接返回葉節(jié)點(diǎn)的value(因?yàn)長(zhǎng)iteral類型的節(jié)點(diǎn)肯定有一個(gè)value值) - 下一步應(yīng)該建立一個(gè)臨時(shí)數(shù)組领跛,讓每次解析出來的實(shí)際內(nèi)容都放到數(shù)組中,然后把這個(gè)數(shù)組轉(zhuǎn)換成string撤奸,傳入Function構(gòu)造方法吠昭,從而得到一個(gè)函數(shù)。
Compiler.prototype.compile = function (expression) {
this.state = {body:[]};
this.astBuilder.ast(expression);
};
接著完成遞歸方法:
Compiler.prototype.recurse=function(ast){
switch (ast.type) {
case ASTBuilder.Program:
this.state.body.push('return ',this.recurse(ast.body),' ;');
break;
case ASTBuilder.Literal:
return ast.value;
break;
}
}
完成compile方法:
Compiler.prototype.compile = function (expression) {
this.state = {body:[]};
var ast = this.astBuilder.ast(expression);
this.recurse(ast);
};
好了寂呛,現(xiàn)在完成了怎诫,全部代碼是這樣:
function Compiler(astBuilder){
this.astBuilder = astBuilder;
}
Compiler.prototype.compile = function (expression) {
this.state = {body:[]};
var ast = this.astBuilder.ast(expression);
this.recurse(ast);
};
Compiler.prototype.recurse=function(ast){
switch (ast.type) {
case ASTBuilder.Program:
this.state.body.push('return ',this.recurse(ast.body),' ;');
break;
case ASTBuilder.Literal:
return ast.value;
break;
}
}
測(cè)試案例走起來試試:
describe("compiler對(duì)象",function() {
it("可以編譯一個(gè)整數(shù)",function(){
var expression = '233';
var lexer = new Lexer();
var astbuilder = new ASTBuilder(lexer);
var compiler = new Compiler(astbuilder);
var FnA = compiler.compile(expression);
expect(FnA()).toBe(233);
})
})
報(bào)錯(cuò)了,沒通過贷痪,檢查一下,原來是忘了return一個(gè)函數(shù)蹦误。加上劫拢,然后試試:
Compiler.prototype.compile = function (expression) {
this.state = {body:[]};
var ast = this.astBuilder.ast(expression);
this.recurse(ast);
return Function(this.state.body.join(' '));
};
現(xiàn)在測(cè)試通過了:
到這里,就可以得到一個(gè)最初的目標(biāo)了:一個(gè)可以編譯整數(shù)的表達(dá)式編譯器强胰。