angularjs源码笔记(5.1)--parse

前端之家收集整理的这篇文章主要介绍了angularjs源码笔记(5.1)--parse前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

简介

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

@H_502_8@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(编译器)

@H_502_8@Parser.parse -> astCompiler.compile -> ast.ast -> lexer.lex |-> ast.program

各个方法的返回:

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

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

源码分析

1. lexer 词法分析

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

@H_502_8@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 数组中

@H_502_8@{ index: start,text: number,constant: true,value: Number(number) }

例如:

@H_502_8@str = obj.aa + '11'; aa = str.length > 2? 'abc':123

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

@H_502_8@[ {identifier: true,text:'str'},{operator: true,text: '='},{identifier: true,text: 'obj'},{text: '.'},text: 'aa'},text: '+'},{constant: true,text: '11',value: '11'},{text: ';'},text:'aa'},text:'length'},text: '>'},text: '2',value: 2},text: '?'},text: 'abc',value: 'abc'},text: ':'},text: '123',value: 123} ]

2. AST 语法分析

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

@H_502_8@{type: AST.xxx,xxx:xxx,yyy: {type: AST.xxx,xxx:xxx}}

type表示该字段的类型

@H_502_8@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:

@H_502_8@{ 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,name: 'aa'},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 },value: 2} },alternate: {type: AST.Literal,consequent: {type: AST.Literal,value: 123} } } }] }

展开如图就是一棵树。

3. AST编译

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

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

简单来说,如:

@H_502_8@parse('123'); // 转化为 function () { return 123; } // 或 function () { var v0 = 123; return v0; } parse('ab.c=123'); // 转化为 function (s) { s.ab.c = 123; }

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

猜你在找的Angularjs相关文章