自制前段框架Day12.Function Call

作者: 蚊子爸爸 | 来源:发表于2017-05-29 15:21 被阅读50次

    端午的第一天我毛都没干,除了睡觉就是晃悠,还花了两三个小时在玩游戏。唯独看了看spring和mybatis,感觉也没什么可看的。今天睡觉睡到11点。现在我咬牙决定写会代码。
    之前写到可以读取某个对象的属性了。今天打算做一下函数调用,假设对象的某个属性是一个funciton,应该可以调用这个function。这个功能也是核心功能,假设

    $scope.hello=function(){
      console.log('hello')
    }
    

    那么在html中,应该是这样调用的:

    <button ng-click="hello()">打印</button>
    

    事实上测试案例就是这样:

    it('Function Call', function() {
        var fn = parse('getName()');
        expect(fn({getName:function(){return 'wangji'}})).toBe('wangji');
      });
    

    先想一下思路,对于这个字符串,产生的token是什么?应该是:getName,(,)这三个。先去补完一下这个词法分析。

     else if (this.is('{}[],:.()')) {//之前不能解析括号,现在加上
          this.tokens.push({
            text: currentChar,
            identifier: true
          })
          this.index++;
    

    在AST阶段如果遇到括号,就应该产生一个Call类型的节点。

    else if (next.text === "(") {
          primary = {type: ASTBuilder.CallExpression, callee: primary};
          this.consume(')');
        } else {
    

    这个节点有一个属性是callee,因为既然是函数调用,肯定需要一个上下文。上下文就是左边的那个对象(左值和右值)。
    现在可以脑子里想一下会生成一个什么样的AST树。

    image.png

    进入编译阶段的时候,先写一下针对ASTBuilder.CallExpression类型节点的写法。
    下面这个思路已经很熟悉了,写过很多遍了,编译从AST的program开始,生成一个return xxxxx ; 的语句。然后进入AST.body节点,这个节点遇到的是CallExpression节点,需要return的是scope.callee()这个字符串。callee可以继续用recurse方法遍历。

     case ASTBuilder.CallExpression:
          var callee = this.recurse(ast.callee);
          return callee+"&&"+callee+"()";
    

    跑测试的时候发现不对头,结果通过跟踪发现少了一部分。

    var next;
      while ((next = this.expect('.', '[','('))) {//刚才忘了加左括号
        if (next.text === "[") {
          primary = {
            type: ASTBuilder.MemberExpression,
            object: primary,
            property: this.primary(),
            computed: true
          }
          this.consume(']');
        } else if (next.text === "(") {
          primary = { type: ASTBuilder.CallExpression, callee: primary };
          this.consume(')');
        } else {
    
    成功

    现在空函数是有了。下一步是解析函数的参数。在创建CallExpression节点的时候,应该还有一个属性是参数。
    假设一下解析这个add(100,200)
    自己先想一下生成的tokens应该是什么。然后在AST构建的时候:

    primary = { type: ASTBuilder.CallExpression, callee: primary ,arguments:解析参数方法,this.parseArguments()};
    

    parseArguments方法,再次提醒:primary方法用于把token转为节点

    STBuilder.prototype.parseArguments = function () {
      var args = [];
      if (!this.peek(')')) {
        do {
          args.push(this.primary())
        } while (this.expect(','));
      }
      return args;
    }
    

    在编译的过程里,只需要把参数也编译出来就行了。思路很简单,建立一个arguments数组,然后把相应的节点都编译一下推入数组,最后加入到生成的函数代码中。

    case ASTBuilder.CallExpression:
          var callee = this.recurse(ast.callee);
          var arguments=[];
          for(var i=0;i<ast.arguments.length;i++){
            arguments.push(this.recurse(ast.arguments[i]));
          }
          return callee + "&&" + callee + "("+arguments.join(",")+")";
    

    最开始的时候觉得函数参数很麻烦,现在想一下思路,

    $scope.add = function (a,b) {
        return a+b;
    }
    

    实际上生成的时候需要生成add(100,200)就可以了,不需要关注内在逻辑。现在跑一下试试。

    image.png

    成功。

    相关文章

      网友评论

      本文标题:自制前段框架Day12.Function Call

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