美文网首页自制前端框架Web前端之路让前端飞
自制前端框架 Day5.编译普通字符串

自制前端框架 Day5.编译普通字符串

作者: 蚊子爸爸 | 来源:发表于2017-05-13 22:22 被阅读43次

    今天打算完成编译字符串的功能。
    目标效果(测试案例):

        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

    感觉怎么这么简单?果然我忘了,还有转义字符串的功能没写。我想想怎么写,明天再弄。

    相关文章

      网友评论

        本文标题:自制前端框架 Day5.编译普通字符串

        本文链接:https://www.haomeiwen.com/subject/zpaxxxtx.html