前面的几天写了一个小编译器,可以编译一个整数表达式,可是这还不够。拿数字来说,还有小数和科学计数法。所以今天我打算把这个功能给加上。
其实加小数这个功能贼容易,按照之前的思路,如果遇到是数字的就进入readNumber流程,在这个流程中,判断每个字符是不是数字,是的话就拼起来,不是就跳出。现在只需要加上小数点就行。只需要修改readNumber方法就可以:
Lexer.prototype.readNumber=function(){
var token;
var number='';
while (this.index<this.text.length) {
var currentChar = this.text.charAt(this.index);
if(this.isNumber(currentChar)||currentChar==="."){
number+=currentChar;
this.index++;
}else{
break;
}
}
token={
text:number,
value:Number(number)
}
this.tokens.push(token)
}
这样写没毛病,测试案例也过去了。写科学计数其实也容易,逻辑理顺了就行,比如这样的:
23e+3
- e的后面只能是加号或者减号或者数字,否则就报错。
- 加号或者减号的后面必须有数字,否则就报错。
先写一个方法,用来判断一个字符是否可以放在E后面:
Lexer.isExOprater=function(char){
return char==="+"||char==="-"||this.isNumber(char);
}
还要写一个方法用来读取当前字符的下一个字符是什么:
Lexer.prototype.peek=function(){
if(this.index<this.text.length-1){
return this.text.charAt(this.index+1);
}else{
return false;
}
}
接下来就是把逻辑理清楚,去完善readNumber流程:
- 当前字符是e,下一个字符是+或者-或者是数字,正常通行。
- 当前字符是e或者+或者-,没有下一个字符,报错。
- 当前字符是e或者+或者-,下一个字符是数字,通行。
- 当前字符是e或者+或者-,下一个字符不是数字,报错。
按照这个逻辑写了代码:
Lexer.prototype.readNumber=function(){
var token;
var number='';
while (this.index<this.text.length) {
var currentChar = this.text.charAt(this.index).toLowerCase();
var nextChar = this.peek();
if(this.isNumber(currentChar)||currentChar==="."){
number+=currentChar;
this.index++;
}else if (currentChar==="e"&&nextChar&&this.isExOprater(nextChar)) {
number+=currentChar;
this.index++;
}else if (this.isExOprater(currentChar)&&nextChar) {
if (this.isNumber(nextChar)) {
number+=currentChar;
this.index++;
}else{
throw "readNumber流程出错:e或者+或者-后面只能是数字"
}
}else if (this.isExOprater(currentChar)&&!nextChar) {
throw "readNumber流程出错:e或者+或者-后面都需要有字符"
}else{
break;
}
}
token={
text:number,
value:Number(number)
}
this.tokens.push(token)
}
这个代码if嵌套了两层,比较不爽,所以重新整一下逻辑:
- 当前字符是e,下一个字符是+或者-或者数字,通过
- 前面是e,下一个是数字,自身是+或者-或者数字,通过。
- 前面是e,自身是+或者-或者数字,后面不是数字或者没有字符,报错。
这个逻辑也没毛病,而且判断比之前少了一个,可以。修改代码:
Lexer.prototype.readNumber=function(){
var token;
var number='';
while (this.index<this.text.length) {
var currentChar = this.text.charAt(this.index).toLowerCase();
var nextChar = this.peek();
var prevChar = this.text.charAt(this.index-1);
if(this.isNumber(currentChar)||currentChar==="."){
number+=currentChar;
this.index++;
}else if (currentChar==="e"&&nextChar&&this.isExOprater(nextChar)) {
number+=currentChar;
this.index++;
}else if (prevChar==='e'&&this.isExOprater(currentChar)&&nextChar&&this.isNumber(nextChar)) {
number+=currentChar;
this.index++;
}else if (prevChar==='e'&&this.isExOprater(currentChar)&&(!nextChar||!this.isNumber(nextChar))) {
throw "readNumber流程出错:不合法的输入"
}else{
break;
}
}
token={
text:number,
value:Number(number)
}
this.tokens.push(token)
}
搞定,测试案例也都通过:
it("可以编译一个以小数点结尾的数",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);
})
it("可以编译格式如2.33e+2这样的科学计数",function() {
var expression = '2.33e+2';
var lexer = new Lexer();
var astbuilder = new ASTBuilder(lexer);
var compiler = new Compiler(astbuilder);
var FnA = compiler.compile(expression);
expect(FnA()).toBe(233);
})
it("可以编译格式如233e-2这样的科学计数",function() {
var expression = '233e-2';
var lexer = new Lexer();
var astbuilder = new ASTBuilder(lexer);
var compiler = new Compiler(astbuilder);
var FnA = compiler.compile(expression);
expect(FnA()).toBe(2.33);
})
it("可以编译格式如233e2这样的科学计数",function() {
var expression = '233e2';
var lexer = new Lexer();
var astbuilder = new ASTBuilder(lexer);
var compiler = new Compiler(astbuilder);
var FnA = compiler.compile(expression);
expect(FnA()).toBe(23300);
})
很烦很烦,今天写代码写的不爽,本以为分分钟就能写完的东西让我写了这么久。两个多小时,烦烦烦。