前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >angularjs源码笔记(5.1)--parse

angularjs源码笔记(5.1)--parse

作者头像
alexqdjay
发布2018-05-11 13:57:36
6710
发布2018-05-11 13:57:36
举报
文章被收录于专栏:alexqdjayalexqdjay

简介

ng提供一个 $parse 服务用于解析与scope值相关的字符串表达式,如:

代码语言:javascript
复制
scope = {
  a: 1,
  b: 2
};

function fn ($parse) {
  var resFn = $parse('a + b + 1');
  resFn(scope); // == 4
}

可以将字符串表达式中的变量映射到scope的变量上执行运算。

$parse 的功能就是编译器,将传入的字符串表达式通过词法、语法分析,最后编译成跟 scope 及 locals 相关联的代码进行执行。

所以,本文主要就 $parse  的工作原理进行解析,而非代码的细节。

主结构

既然 $parse 是个service,那么就有其对应的 provider.$get, 由其内代码所知,涉及到的对象有 ParserLexer(词法分析器)AST(语法分析器)ASTCompile(编译器)

代码语言:javascript
复制
Parser.parse -> astCompiler.compile -> ast.ast -> lexer.lex
                                              |-> ast.program

各个方法的返回:

  1. astCompiler.compile:返回一个function,供调用执行
  2. ast.ast::返回一个语法解析树
  3. lexer.lex: 返回一个词法分割数组

下面按主结构对源码进行分析

源码分析

1. lexer 词法分析

游标进行逐个字符扫描,遇到不一样的字符做不一样的处理,如遇到 ' 或 " 表示字符串即开始读取字符串,一直到对应的闭合符号 ' 或者 ",还有如遇到数字或者. 开头就表示接下去是数字进行读取数字操作。

代码语言:javascript
复制
while (this.index < this.text.length) {
  var ch = this.text.charAt(this.index);
  // 读取字符串
  if (ch === '"' || ch === '\'') {
    this.readString(ch);
  } 
  // 读取数字包含小数0.22及2e10这样的
  else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
    this.readNumber();
  } 
  // 读取标识符如变量等
  else if (this.isIdentifierStart(this.peekMultichar())) {
    this.readIdent();
  } 
  // 读取(){}[]等符号
  else if (this.is(ch, '(){}[].,;:?')) {
    this.tokens.push({index: this.index, text: ch});
    this.index++;
  } 
  // 出去空白字符
  else if (this.isWhitespace(ch)) {
    this.index++;
  }
  // 读取操作符+-*/ >= === !==等
  else {
    var ch2 = ch + this.peek();
    var ch3 = ch2 + this.peek(2);
    var op1 = OPERATORS[ch];
    var op2 = OPERATORS[ch2];
    var op3 = OPERATORS[ch3];
    if (op1 || op2 || op3) {
      var token = op3 ? ch3 : (op2 ? ch2 : ch);
      this.tokens.push({index: this.index, text: token, operator: true});
      this.index += token.length;
    } else {
      this.throwError('Unexpected next character ', this.index, this.index + 1);
    }
  }
}

所有的读取操作如 readNumber 最终都会生成一个形如下面的对象,放入tokens 数组中

代码语言:javascript
复制
{
  index: start,
  text: number,
  constant: true,
  value: Number(number)
}

例如: 

代码语言:javascript
复制
str = obj.aa + '11';
aa = str.length > 2? 'abc':123

分解成 tokens (有些字段省略如index) :

代码语言:javascript
复制
[
  {identifier: true, text:'str'},
  {operator: true, text: '='},
  {identifier: true, text: 'obj'},
  {text: '.'},
  {identifier: true, text: 'aa'},
  {operator: true, text: '+'},
  {constant: true, text: '11',value: '11'},
  {text: ';'},
  {identifier: true, text:'aa'},
  {operator: true, text: '='},
  {identifier: true, text:'str'},
  {text: '.'},
  {identifier: true, text:'length'},
  {operator: true, text: '>'},
  {constant: true, text: '2', value: 2},
  {operator: true, text: '?'},
  {constant: true, text: 'abc', value: 'abc'},
  {operator: true, text: ':'},
  {constant: true, text: '123', value: 123}
]

2. AST 语法分析

对词法分析返回的 tokens 进行语法分析,解析出如下结构的数据,可以嵌套,或者说是一种树结构:

代码语言:javascript
复制
{type: AST.xxx, xxx:xxx, yyy: {type: AST.xxx, xxx:xxx}}

type表示该字段的类型

代码语言:javascript
复制
AST.Program = 'Program'; // root节点
AST.ExpressionStatement = 'ExpressionStatement'; // 表达式节点 
AST.AssignmentExpression = 'AssignmentExpression'; // 赋值表达式:f=12+22
AST.ConditionalExpression = 'ConditionalExpression'; // 判断表达式:
AST.LogicalExpression = 'LogicalExpression';  // 逻辑表达式
AST.BinaryExpression = 'BinaryExpression'; // 二元表达式:+-*/等
AST.UnaryExpression = 'UnaryExpression'; // 一元表达式: !a
AST.CallExpression = 'CallExpression';  // 调用表达式:fn()
AST.MemberExpression = 'MemberExpression'; // 成员变量:obj.prop1
AST.Identifier = 'Identifier';  // 标识符:变量等
AST.Literal = 'Literal'; // ture,false,null,undefined 常量
AST.ArrayExpression = 'ArrayExpression'; // 数组
AST.Property = 'Property'; // 对象属性
AST.ObjectExpression = 'ObjectExpression'; //对象表达式:{a:11, b:12}
AST.ThisExpression = 'ThisExpression'; // this表达式: this.ff
AST.LocalsExpression = 'LocalsExpression'; // ??

根据运算符的优先级,将tokens进行翻译,使用上面的例子,翻译成如下object:

代码语言:javascript
复制
{
  AST.Program,
  body: [{
    type: AST.ExpressionStatement,
    expression: {
      type: AST.AssignmentExpression,
      left: {type: AST.Identifier, name: 'str'},
      operator: '=',
      right: {
        type: AST.BinaryExpression, 
        operator: '+',
        left: {
          type: AST.MemberExpression, 
          object: {
            type: AST.Identifier,
            name: 'obj'
          }, 
          property: {
            type: AST.Identifier,
            name: 'aa'
          }, 
          computed: false
        },
        right: {type: AST.Literal, value: '11'}
      }
    }
  },{
    type: AST.ExpressionStatement,
    expression: {
      type: AST.AssignmentExpression,
      left: {type: AST.Identifier, name: 'aa'},
      operator: '=',
      right: {
        type: AST.ConditionalExpression,
        test: {
          type: AST.BinaryExpression,
          operator: '>',
          left: {
            type: AST.MemberExpression, 
            object: {
              type: AST.Identifier,
              name: 'str'
            }, 
            property: {
              type: AST.Identifier,
              name: 'length'
            }, 
            computed: false
          },
          right: {type: AST.Literal, value: 2}
        },
        alternate: {type: AST.Literal, value: 'abc'}, 
        consequent: {type: AST.Literal, value: 123}
      }
    }
  }]
}

展开如图就是一棵树。

3. AST编译

接下去做的就是就ast树编译成目标代码,完成这项任务的function是 recurse 。

recurse 是个递归调用的方法,根据不一样的ast对象做不一样的字符串拼接处理,最简单的如 Literal 的处理,就是直接将常量返回出来或者赋值给变量然后将变量返回出来。

简单来说,如:

代码语言:javascript
复制
parse('123');

// 转化为
function () {
  return 123;
}

// 或
function () {
  var v0 = 123;
  return v0;
}


parse('ab.c=123');

// 转化为
function (s) {
  s.ab.c = 123;
}

本篇对于词法及语法分析解析到这,不再作过多的解读,代码层面也基本都是围绕编译原理的基本知识展开,所以对ng的整体的理念关联不大,所以不一一解释,对于目标代码的编译细节、插值表达式及watch的字符串解析下篇再详细介绍。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 主结构
  • 源码分析
    • 1. lexer 词法分析
      • 2. AST 语法分析
        • 3. AST编译
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档