今天打算完成编译字符串的功能。
目标效果(测试案例):
var expression = " 'hello' ";
var lexer = new Lexer();
var astbuilder = new ASTBuilder(lexer);
var compiler = new Compiler(astbuilder);
var FnA = compiler.compile(expression);
expect(FnA()).toBe('hello');
基本思路是,测法分析阶段,如果检测到单引号或者双引号,则进入readString流程,就像readNumber流程一样。
Lexer.prototype.lex=function(expression){
this.tokens=[];
this.text = expression;
this.index = 0;
while (this.index<this.text.length) {
var currentChar = this.text.charAt(this.index);
if(this.isNumber(currentChar)){
this.readNumber();
}else if (currentChar==="'"||currentChar==="\"") {
this.readString(currentChar);
}else{
throw "现在只支持数字,不支持别的字符"
}
}
return this.tokens;
}
在这里面,调用readString方法的时候传入当前的字符,也就是单引号或者双引号。原因是这样的:
如果是双引号开始的字符串,一定要用双引号封闭;单引号也是同理。所以readString方法是这样的:
Lexer.prototype.readString=function(quote){
var string='';
this.index++;
while(this.index<this.text.length){
var char = this.text.charAt(this.index);
if(char==quote){
this.tokens.push({
text:string,
value:string
})
this.index++;
return ;
}else{
string+=char;
}
this.index++;
}
throw "字符串解析流程出错";
}
在这里我写代码的时候有个小问题,注意readString方法里面的循环,循环体中,进入if(char==quote)
分支之后,把一个token推入tokens数组,然后this.index++
这里没问题,问题是下面的那个return
,我之前写的是break
然后就每次都会抛出异常字符串解析流程出错
。原因是:
break语句是跳出当前循环,所以跳出循环以后接着往下走,遇到了throw语句。
return 语句是跳出当前函数/方法。直接跳出了readString流程,回到lex方法。
虽然这是一个很常识性的错误,可是这再次提醒我:写程序要注意逻辑思维,想好了再写!
试着想一下现在运行测试案例会出现什么问题?假设我们编译的是123
这个数字,编译后生成的函数会是这样:
function (){
return 123 ;
}
那么我们编译'hello'
这个字符串呢?生成的函数会是这样:
function (){
return hello ;
}
这个函数运行的时候肯定会报错说hello is undefined
。
所以我们想要的编译出来的函数应该是这样的:
function (){
return 'hello' ;//hello被引号包裹着
}
想做出这个改动表面复杂,其实很简单,因为String产生的token其实也是Literal类型,换句话说,Literal类型的token也就是数字和字符两种东西。说的不清楚,还是写个代码。
数字产生的token和string产生的token分别是这样的:
{
text:'123',
value:123
}
{
text:'hello',
value:'hello'
}
ASTBuilder根据token的类型给他们加上type属性,然后插入AST中。接着Compiler根据AST树的type来执行相应的流程。那么思路就是,在Compiler阶段,遇到type是Literal的节点,有可能是数字也有可能是字符串,要做一下更妥善的处理,之前只是处理的数字没有处理字符串,现在给加上:
Compiler.prototype.recurse=function(ast){
switch (ast.type) {
case ASTBuilder.Program:
this.state.body.push('return ',this.recurse(ast.body),' ;');
break;
case ASTBuilder.Literal:
if(webframe.isString(ast.value)){
return "\'"+ast.value+"\'";
}else{
return ast.value;
}
break;
}
}
在这里面调用了一个webframe.isString方法来判断一个变量是不是字符串,这个方法是自己手写的,也是一个很简单的方法:
webframe.isString=function(userinput){
return String(userinput)===userinput;
}
现在测试案例就可以跑通了:
image.png感觉怎么这么简单?果然我忘了,还有转义字符串的功能没写。我想想怎么写,明天再弄。
网友评论