Angular in $parse detailed tutorial

Source: Internet
Author: User
Tags abstract bitwise character set constant constructor lexer mul regular expression

Opening the angular API document, the official only gives a brief explanation "converts angular expression into a function (converts a angular expression into one)", the Heart of the Beast Pentium ———— Why does the function need "2000 lines of code"?





share the first paragraph of the source author's comments








/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *


* Any commits to this file should is reviewed with the security in mind. *


* Changes to this file can potentially create security vulnerabilities. *


* An approval to 2 Core members with history of modifying *


* This file is required. *


*                                                                         *


* Does the somehow allow for arbitrary JavaScript to be executed? *


* Or allows for someone to change the prototype of built-in objects? *


* or gives undesired access to variables likes document or window? *


* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */





In this statement, I only see in the three files in the angular source code: $parse, $sce, $compile.





The general meaning of this statement is that all changes submitted to this file will be examined for security reasons. Modifying this file can create a security vulnerability. At least two angular core member approval is required for this file to be modified. Whether the modification will result in the execution of any JS code, or allow someone to modify the prototype (prototype) that is bound to the object, or give permission to an unreachable variable such as Document,window.





It is true that the author implemented a compiler, is learning the principles of compiling students blessed


By reading the following comments and https://docs.angularjs.org/guide/security, you know that the implementation of $parse uses 2000 of lines of code for security reasons, and compilers are used to compile expressions that need to be parsed. In the process of lexical parsing, regular expressions could have been used, but the author used to build a lexical parser (Lexer), and my guess was for performance reasons. By compiling the principle of learning, we know that lexical parsing requires only 3-type grammar, while the 3-type grammar equivalence present is expressed, so the use of regular representations can identify all the needs of the lexical. The use of regular expressions, although can quickly write a lexical analyzer, but the efficiency is very low, the regular expression execution, needs to iterate repeatedly the string, for is frequently called $parse service (we say it does dirty live dirty) uses the regular expression to implement the lexical analyzer is obviously unsuitable.



Second, the principle of compiling (this part of the angular source analysis, not too big eggs, you can skip)

The principle of compiling is actually a set of theories that computer scientists have summed up in the process of compiling compilers. And the compiler, is a program, its input is a programming language, output is another programming language, equivalent to a translator, for example, can translate English into Chinese. Abstract, is to translate a language into B language program, called the compiler. This poses a problem, whether the language A and B are equally descriptive, that is, whether a and B are equivalent, and whether they can be translated or not.

Say Han
Has the reader ever thought about the question: Why do human languages translate to each other? Anyway, the question I've been thinking about, the recent reading of Chomsky's thoughts and ideals has found an explanation: human beings have a common inner language, which Chomsky calls the I language, which is derived from the intrinsic attributes of man, and the Chinese and English gods and horses we use are merely a form of language, They are the encoding of the I language, whose descriptive power is naturally equivalent to the I language, and the expressive power of these languages is equal. (Chomsky is now still living master, I personally think he can be with Newton, Darwin, Einstein, etc.)

1. General composition of the compiler:

A compiler typically contains these subroutines: code preprocessor, lexical analyzer, parser, semantic analyzer, target code generator, target code optimizer, and so on.
Code Preprocessor: The effect is to kill irrelevant parts of code, including annotations, extra spaces, and to translate precompiled instructions into code.

Lexical Analyzer: Identify valid words according to the vocabulary rules specified in the language, and throw an error message if illegal words stop compiling. For example, in writing the C language, the definition of an identifier is that a letter or underscore can contain letters, numbers, and underlined strings that are identified by the lexical analyzer.
Forget, limited to space, the following omitted ....


2. Grammar classification

1956, Chomsky establishes a description of the formal language. By imposing different restrictions on the production, Chomsky divides the grammar into 4 types
First define a production α→β
0 type grammar (PSG):

Α∈ (VN∪VT) *, with at least one VN

β∈ (VN∪VT) *
There are no restrictions on the production
For example: a0→a0,a1→b
0 Grammar Description: The
0 type grammar is also called the phrase grammar. A very important theoretical result of

is the ability of the 0 grammar to be equivalent to a Turing machine (Turing), about Turing machines, my other blog, chapter I, the invention of the computer. Or, any type 0 language is recursively enumerable; conversely, a recursive enumerable set must be a 0-type language. Some restrictions are given to the form of type 0 grammars to give the definition of 1,2 and type 3 grammars.
Note
Grammar g is defined as a four-tuple (VN, VT, p,s)
VN: Non-non-terminal set
VT: Non-terminal set
P: Generation collection (rule set)
S: Start symbol (recognition symbol)
Type 1 grammar (context-related grammar context-sensitive):

has |β|>=|α| for any of the generated α→β, except s→ε
produced form description: Α1aα2→α1βα2
(where Alpha 1, Alpha 2, Β∈ (VN∪VT) *,Β≠Ε,A∈VN)
that is: A is allowed to replace with beta only if it appears in the context of α1α2.  The
resulting language is referred to as "contextual language"
such as: 0a0→011000,1a1→101011
Type 2 grammar (CFG):

for any α→β, there are α∈vn,β∈ (VN∪VT) *
The form of a production is described: A→β (A∈VN)
that beta replaces a, regardless of the context in which a is. The
generated language is called "context-free language"
For example: g[s]:s→01 s→0s1
3 Grammar (RG): Also known as normal grammar

Each generation is A→ab or a→a--right linear
A→ba or a→a--left linear
its , A, b∈vn,a∈vt*
The resulting language is called "normal language"
For example: G[s]: s→0a | 0,a→1b | b,b→1 | 0


Three, $parse lexical parser


1. Before talking about the source code, first the search technique for string search.

A problem occurs when matching an operation symbol: for example, when matching an operator in x = y, if a character-by-scan, the intermediate operator in X and Y will be the + and =, and we know that this is actually a notation = =.
Very simple: when we encounter +, we do not assert that it is a "plus budget character", continue to read a character, see whether these two characters can form a new symbol, if you can make up a recognition, Index tab Step two units, if you can not determine the + number is "add Operator", the characters in advance reading does not count.
We refer to this pre processing technology as "pre-search technology".
To generalize, you will get the KMP algorithm.




2. Lexical parser source code:





var Lexer = function (options) {


this.options = options;


};





Lexer.prototype = {


Constructor:lexer,





Lex:function (text) {//Convert a string into a glossary


This.text = text;


This.index = 0;


This.tokens = [];





while (This.index < this.text.length) {


var ch = this.text.charAt (this.index);


if (ch = = ' "' | | ch = = "'") {//Identify string


This.readstring (CH);


else if (this.isnumber (ch) | | | ch = = '. ' && This.isnumber (This.peek ())) {//Identify number (including, Integer, floating-point, and scientific notation)


This.readnumber ();


else if (this.isident (CH)) {//identifier


This.readident ();


else if (this.is (CH, ' () {}[].,;:? ') {//Identify boundary symbol


This.tokens.push ({index:this.index, text:ch});


this.index++;


else if (this.iswhitespace (CH)) {//Identify spaces


this.index++;


} else {//operator identification, an operator consisting of 1 to 3 characters, requiring a pre-search


var CH2 = ch + this.peek ()//pre-Search 1 characters


var CH3 = CH2 + This.peek (2);//Pre-Search 2 characters


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); Exception occurred, thrown


}


}


}


Return this.tokens;//returns the stored glossary


},





Is:function (CH, chars) {//Determine if CH is a character in the chars string


return Chars.indexof (CH)!==-1;


},





Peek:function (i) {//back, pre-read I characters


var num = i | | 1;


return (This.index + num < this.text.length)? This.text.charAt (This.index + num): false;


},





Isnumber:function (CH) {//judge whether the character is a number


Return (' 0 ' <= ch && ch <= ' 9 ') && typeof ch = = "string";


},





Iswhitespace:function (CH) {//judge whether the character is a non-printing character


IE treats non-breaking space as \u00a0


return (ch = = ' | | | ch = = ' \ r ' | | | ch = = ' t ' | |


ch = = ' \ n ' | | ch = = ' \v ' | | ch = = ' \u00a0 ');


},





Isident:function (CH) {//Determine whether CH is a character set that belongs to an identifier


Return (' A ' <= ch && ch <= ' Z ' | |


' A ' <= ch && ch <= ' Z ' | |


' _ ' = = CH | | ch = = ' $ ');


},





Isexpoperator:function (CH) {//is the symbol in the Science and technology law (+ or-)


return (ch = = '-' | | | ch = = ' + ' | | this.isnumber (CH));


},





Throwerror:function (Error, start, end) {//Wrong throw


End = End | | This.index;


var colstr = (isdefined (start)


? ' s ' + start + '-' + This.index + ' [' + this.text.substring (start, end) + '] '


: ' + END ';


Throw $parseMinErr (' lexerr ', ' Lexer Error: {0} at Column{1} in expression [{2}]. '


Error, COLSTR, this.text);


},





Readnumber:function () {//read a number


var number = ';


var start = This.index;


while (This.index < this.text.length) {


var ch = lowercase (this.text.charAt (this.index));


if (ch = = '. ' | | this.isnumber (CH)) {


Number = ch;


} else {


var peekch = This.peek ();


if (ch = = ' E ' && this.isexpoperator (peekch)) {


Number = ch;


else if (this.isexpoperator (CH) &&


Peekch && this.isnumber (peekch) &&


Number.charat (number.length-1) = = ' E ') {


Number = ch;


else if (this.isexpoperator (CH) &&


(!peekch | |!this.isnumber (PEEKCH)) &&


Number.charat (number.length-1) = = ' E ') {


This.throwerror (' Invalid exponent ');


} else {


Break


}


}


this.index++;


}


This.tokens.push ({


Index:start,


Text:number,


Constant:true,


Value:number (number)


});


},





Readident:function () {//Read an identifier


var start = This.index;


while (This.index < this.text.length) {


var ch = this.text.charAt (this.index);


if (!) ( This.isident (ch) | | This.isnumber (CH))) {


Break


}


this.index++;


}


This.tokens.push ({


Index:start,


Text:this.text.slice (Start, This.index),


Identifier:true


});


},





Readstring:function (quote) {//Read a string


var start = This.index;


this.index++;


var string = ';


var rawstring = quote;


var escape = false;


while (This.index < this.text.length) {


var ch = this.text.charAt (this.index);


rawstring + = ch;


if (escape) {


if (ch = = ' U ') {


var hex = this.text.substring (This.index + 1, This.index + 5);


if (!hex.match (/[\da-f]{4}/i)) {


This.throwerror (' Invalid Unicode escape [\\u ' + hex + '] ');


}


This.index + 4;


String + string.fromcharcode (parseint (hex, 16));


} else {


var rep = escape[ch];


String = string + (Rep | | ch);


}


Escape = false;


else if (ch = = ' \ ") {


Escape = true;


else if (ch = = quote) {


this.index++;


This.tokens.push ({


Index:start,


Text:rawstring,


Constant:true,


Value:string


});


Return


} else {


string = = ch;


}


this.index++;


}


This.throwerror (' unterminated quote ', start);


}


};





3. The above code corresponds to the finite state machine




4. If a regular expression is used to implement the

1. String:/(["']) \s+\1/
2.number:/([0-9]*.)? [0-9]+ (e[\+-][0-9]+)?/
3. Identifier:/[a-za-z_\$][a-za-z0-9_\$]*/
4. Operator:/\+|-|\*|\/|%| ===|\!==|==|\!=|<|>|<=|>=|&&|\|\| | \!| =|\|/


Four, $parse syntax parser


1. The concept

Ast:abstract Syntax tree, abstract syntax trees
Abstract syntax trees (abstract Syntax tree, AST), as an intermediate representation of programs, are widely used in many fields such as program analysis. The use of abstract syntax tree can easily implement a variety of source program processing tools, such as source program browser, intelligent editor, language translator and so on.


2. $parse AST Implementation:





var AST = function (lexer, options) {


This.lexer = lexer;


this.options = options;


};





The following defines the type of the syntax tree node


Ast. Programs = ' program ';//represent the root node of the application


Ast. expressionstatement = ' expressionstatement '; An expression node


Ast. assignmentexpression = ' assignmentexpression ';//Assignment expression


Ast. conditionalexpression = ' conditionalexpression '/Judge expression


Ast. logicalexpression = ' logicalexpression ';//logical Expression of operation


Ast. binaryexpression = ' binaryexpression ';//binary budget processing expression (bitwise AND, bitwise, OR)? Look at the back, only to know that this understanding is wrong. This represents the size of the relationship. But why do you have to take that name?


Ast.  unaryexpression = ' unaryexpression '; Non-data expression


Ast. callexpression = ' callexpression '; Calling an expression


Ast. memberexpression = ' memberexpression '; Member-Variable expressions


Ast. Identifier = ' Identifier '; Identifier


Ast. Literal = ' Literal '; Text, amount, this means ture,false,null,undefined, etc.


Ast. arrayexpression = ' arrayexpression ';//Data expression


Ast. Property = ' property '; An object property expression


Ast. objectexpression = ' objectexpression ';//Object expression


Ast. thisexpression = ' thisexpression ';//this expression





Internal Use only


Ast. Ngvalueparameter = ' Ngvalueparameter ';





Ast.prototype = {


Ast:function (text) {//Syntax tree entry


This.text = text;


This.tokens = This.lexer.lex (text);//Invoke lexical Analyzer to get a valid glossary





var value = This.program ();//Calling program subroutine to start generating syntax tree





if (this.tokens.length!== 0) {


This.throwerror (' is a unexpected token ', this.tokens[0]);


}





return value;


},





Program:function () {


var body = [];


while (true) {//loop, calling Expressionstatement Read statement


if (this.tokens.length &gt; 0 &amp;&amp;!this.peek ('} ', ') ', '; ', ') ')


Body.push (This.expressionstatement ());


if (!this.expect (';')) {


return {type:ast. Program, BODY:BODY};//returns a syntax tree


}


}


},





Expressionstatement:function () {//Expression node, representing a statement of JS


return {type:ast. Expressionstatement, Expression:this.filterChain ()};//which invokes the filter chain Filterchain. This is the syntax for identifying filters such as: timestamp|date: ' Yyyy-mm-dd '


},





Filterchain:function () {


var left = this.expression ();//call expression, get node of syntax tree


var token;


while ((token = this.expect (' | ')) {//filter may be in a chain-like presence, such as: Xxx|filter1|filter2 ..., so it needs to be recycled.


left = This.filter (left); Call Filter


}


return to left;


},





Expression:function () {


return This.assignment ();


},





Assignment:function () {//Recognition assignment statement


var result = This.ternary ();


if (this.expect (' = ')) {


result = {Type:ast. Assignmentexpression, Left:result, Right:this.assignment (), operator: ' = '};


}


return result;


},





Ternary:function () {//Identify ternary operation: XXX? axxx:bxxx;


var test = This.logicalor ();


var alternate;


var consequent;


if (This.expect ('? ')) {


Alternate = This.expression ();


if (This.consume (': ')) {


consequent = This.expression ();


return {type:ast. Conditionalexpression, Test:test, Alternate:alternate, consequent:consequent};


}


}


return test;


},





Logicalor:function () {//Identify logical Operation "or" (| |)


var left = This.logicaland ();


while (This.expect (' | | ')) {


left = {Type:ast. LogicalExpression, Operator: ' | | ', LEFT:LEFT, Right:this.logicalAND ()};


}


return to left;


},





Logicaland:function () {//Identify logical Operation "and" (&amp;&amp;)


var left = this.equality ();


while (This.expect (' &amp;&amp; ')) {


left = {Type:ast. LogicalExpression, Operator: ' &amp;&amp; ', Left:left, right:this.equality ()};


}


return to left;


},





Equality:function () {//Identify logical operation "etc" (= =)


var left = this.relational ();


var token;


while (token = this.expect (' = = ', '!= ', ' = = = ', '!== ')) {


left = {Type:ast. Binaryexpression, Operator:token.text, Left:left, right:this.relational ()};


}


return to left;


},





Relational:function () {//Recognition of size comparison expressions


var left = this.additive ();


var token;


while ((token = this.expect (' &lt; ', ' &gt; ', ' &lt;= ', ' &gt;= ')) {


left = {Type:ast. Binaryexpression, Operator:token.text, Left:left, right:this.additive ()};//did not understand why to take this name binary, at first thought it was binary operation related


}


return to left;


},





Additive:function () {//Identify add-subtract expression


var left = this.multiplicative ();


var token;


while ((token = this.expect (' + ', '-'))} {


left = {Type:ast. Binaryexpression, Operator:token.text, Left:left, right:this.multiplicative ()};


}


return to left;


},





Multiplicative:function () {//Identify multiplication and division method


var left = This.unary ();


var token;


while ((token = this.expect (' * ', '/', '% ')) {


left = {Type:ast. Binaryexpression, Operator:token.text, Left:left, Right:this.unary ()};


}


return to left;


},





Unary:function () {//handling non-array expressions


var token;


if (token = this.expect (' + ', '-', '! ')) {


return {type:ast. Unaryexpression, Operator:token.text, Prefix:true, Argument:this.unary ()};


} else {


return This.primary ();


}


},





Primary:function () {//Processing JS remote syntax write content


var primary;


if (This.expect (')) {


Primary = This.filterchain ();


This.consume (') ');


else if (this.expect (' [')) {


Primary = This.arraydeclaration ();


else if (This.expect (' {')) {


Primary = This.object ();


else if (This.constants.hasOwnProperty (This.peek (). Text)) {


Primary = Copy (This.constants[this.consume (). text]);


else if (This.peek (). Identifier) {


Primary = This.identifier ();


else if (this.peek (). Constant) {


Primary = This.constant ();


} else {


This.throwerror (' Not a primary expression ', this.peek ());


}





var next;


while (next = This.expect (' (', ' [', ', ')]) {


if (Next.text = = ' (') {


Primary = {Type:ast. Callexpression, Callee:primary, arguments:this.parseArguments ()};


This.consume (') ');


else if (Next.text = = ' [')} {


Primary = {Type:ast. Memberexpression, Object:primary, Property:this.expression (), computed:true};


This.consume ('] ');


else if (Next.text = = '. ') {


Primary = {Type:ast. Memberexpression, Object:primary, Property:this.identifier (), computed:false};


} else {


This.throwerror (' impossible ');


}


}


return primary;


},





Filter:function (baseexpression) {//Process filter syntax


var args = [baseexpression];//puts incoming arguments into args, making the first parameter node


var result = {Type:ast. Callexpression, Callee:this.identifier (), Arguments:args, filter:true};//use filters, which essentially call a function, so the syntax tree node is a " Callexpression "





while (This.expect (': ')) {


Args.push (This.expression ());//Compress other parameter nodes


}





return result;


},





Parsearguments:function () {//Processing parameters


var args = [];


if (This.peektoken (). Text!== ') ') {


do {


Args.push (This.expression ());


while (This.expect (', '));


}


return args;


},





Identifier:function () {//Processing identifier


var token = This.consume ();


if (!token.identifier) {


This.throwerror (' is not a valid identifier ', token);


}


return {type:ast. Identifier, Name:token.text};


},





Constant:function () {//Processing constants


TODO Check, it is a constant


return {type:ast. Literal, Value:this.consume (). Value};


},





Arraydeclaration:function () {//processing array


var elements = [];


if (This.peektoken (). Text!== ']) {


do {


if (This.peek ('])) {


Support trailing commas per ES5.1.


Break


}


Elements.push (This.expression ());


while (This.expect (', '));


}


This.consume ('] ');





return {type:ast. Arrayexpression, elements:elements};


},





Object:function () {//Processing object


var properties = [], property;


if (This.peektoken (). Text!== '} ') {


do {


if (This.peek ('} ')) {


Support trailing commas per ES5.1.


Break


}


property = {Type:ast. property, kind: ' Init '};


if (This.peek (). Constant) {


Property.key = This.constant ();


else if (This.peek (). Identifier) {


Property.key = This.identifier ();


} else {


This.throwerror ("Invalid Key", This.peek ());


}


This.consume (': ');


Property.Value = This.expression ();


Properties.push (property);


while (This.expect (', '));


}


This.consume ('} ');





return {type:ast. Objectexpression, properties:properties};


},





Throwerror:function (msg, token) {


Throw $PARSEMINERR (' syntax ',


' Syntax error:token \ ' {0}\ ' {1} at column {2} of the expression [{3}] starting at [{4}]. '


Token.text, MSG, (Token.index + 1), This.text, this.text.substring (Token.index));


},





Consume:function (E1) {


if (This.tokens.length = = 0) {


Throw $parseMinErr (' Ueoe ', ' unexpected end of expression: {0} ', This.text);


}





var token = this.expect (e1);


if (!token) {


This.throwerror (' is unexpected, expecting [' + e1 + '] ', This.peek ());


}


return token;


},





Peektoken:function () {


if (This.tokens.length = = 0) {


Throw $parseMinErr (' Ueoe ', ' unexpected end of expression: {0} ', This.text);


}


return this.tokens[0];


},





Peek:function (E1, E2, E3, E4) {


Return This.peekahead (0, E1, E2, E3, E4);


},





Peekahead:function (I, E1, E2, E3, E4) {


if (This.tokens.length &gt; i) {


var token = this.tokens[i];


var t = Token.text;


if (t = = = E1 | | = t = = E2 | | t = = = E3 | |


(!e1 &amp;&amp;!e2 &amp;&amp;!e3 &amp;&amp;!e4)) {


return token;


}


}


return false;


},





Expect:function (E1, E2, E3, E4) {//Expected subroutine, if expected, the This.tokens queue head pops up an element returned, otherwise return false


var token = This.peek (e1, E2, E3, E4);


if (token) {


This.tokens.shift ();


return token;


}


return false;


},








/* ' undefined ' isn't a constant, it is a identifier,


* But using it as an identifier isn't supported


*/


Constants: {


' true ': {type:ast. Literal, Value:true},


' false ': {type:ast. Literal, Value:false},


' null ': {type:ast. Literal, Value:null},


' undefined ': {type:ast. Literal, value:undefined},


' This ': {type:ast. Thisexpression}


}


};





3. Data structure of the syntax tree:





As you can see from the code above, the data structure of a node in the syntax tree





{


Type:AST.xxxStatement,//node type


XXX:XXX,//Each node type contains different elements


}





1. Syntax root node


{


Type:ast. Program,


Body:body


}


2. Syntax-Number expression node


{


Type:ast. Expressionstatement,


Expression:this.filterChain ()


}


3. Assignment Statement node


{


Type:ast. Assignmentexpression,


Left:result,


Right:this.assignment (),


Operator: ' = '


}


4. Conditional expression node (three-head budget)


{


Type:ast. Conditionalexpression,


Test:test,


Alternate:alternate,


Consequent:consequent


}


5. Logical Budget Node


{


Type:ast. LogicalExpression,


Operator: ' | | ',


Left:left,


Right:this.logicalAND ()


}


6. Comparison Operation node


{


Type:ast. Binaryexpression,


Operator:token.text,


Left:left,


Right:this.relational ()


}


7. Non-data nodes


{


Type:ast. Unaryexpression,


Operator:token.text,


Prefix:true,


Argument:this.unary ()


}


8. Member Variable node


{


Type:ast. Memberexpression,


Object:primary,


Property:this.expression (),


Computed:true


}


9. Identifier Node


{


Type:ast. Identifier,


Name:token.text


}


10. Constant nodes


{


Type:ast. Literal,


Value:this.consume (). Value


}


11. Data node


{


Type:ast. Arrayexpression,


Elements:elements


}





4. For example





Suppose the angular expression is str = ok.str;str.length&gt;2? ' ABC ': 123|test:look;


So through the above program, the resulting syntax tree will be:


Five, $parse compiler





Two compilers are defined in $parse: Astcompiler and Astinterpreter, their role is to invoke the AST build syntax tree, and then assemble the syntax tree into a JS function.


The difference between the two is


1.ASTCompiler will assemble the syntactic tree chapeau into a function, Astinterpreter is chapeau meaning assembly into a statement, and then call the loop, the main sentence execution;


During the execution of a 2.ASTInterpreter statement, a ensuresafexxx function is added to perform a security check.


3. See how they are all called:





/**


* @constructor


*/


var Parser = function (Lexer, $filter, options) {


This.lexer = lexer;


this. $filter = $filter;


this.options = options;


This.ast = new AST (this.lexer);


This.astcompiler = OPTIONS.CSP? New Astinterpreter (This.ast, $filter)://What is CSP??


New Astcompiler (This.ast, $filter);


};





Parser.prototype = {


Constructor:parser,





Parse:function (text) {


return This.astCompiler.compile (text, this.options.expensiveChecks);


}


};





Vi. $parse Services





Here, will $parse code function code, if the above code understand, the following is very simple, no longer repeat.





function $parse (exp, INTERCEPTORFN, expensivechecks) {


var parsedexpression, onetime, cachekey;





Switch (typeof exp) {


Case ' string ':


Exp = Exp.trim ();


CacheKey = exp;





var cache = (Expensivechecks cacheexpensive:cachedefault);


Parsedexpression = Cache[cachekey];





if (!parsedexpression) {


if (Exp.charat (0) = = ': ' &amp;&amp; exp.charat (1) = = ': ') {


onetime = true;


Exp = exp.substring (2);


}


var parseoptions = expensivechecks? $parseOptionsExpensive: $parseOptions;


var lexer = new Lexer (parseoptions);


var parser = new Parser (lexer, $filter, parseoptions);


Parsedexpression = Parser.parse (exp);


if (parsedexpression.constant) {


parsedexpression.$ $watchDelegate = constantwatchdelegate;


else if (onetime) {


parsedexpression.$ $watchDelegate = parsedexpression.literal?


Onetimeliteralwatchdelegate:onetimewatchdelegate;


else if (parsedexpression.inputs) {


parsedexpression.$ $watchDelegate = inputswatchdelegate;


}


Cache[cachekey] = parsedexpression;


}


Return Addinterceptor (Parsedexpression, INTERCEPTORFN);





Case ' function ':


Return Addinterceptor (exp, INTERCEPTORFN);





Default


return noop;


}


};




talking about the $parse service of Angularjs





First look at the official API for $parse.





$parse





Function: Converts a ANGULARJS expression into a function


Usage


$parse (expression)





Arguments





Expression: Angularjs statements that need to be compiled


Returnsfunc (context, locals)





Context[object]: For the statement you want to parse, this object contains the expression in the statement you want to parse (usually a scope object)


Locals[object]: A local variable about a variable in the context is useful for overriding variable values in the context.


The returned function also has the following three attributes:





Literal[boolean]: Whether the top node of the expression is a JavaScript literal


Constant[boolean]: Whether the expression is all made up of JavaScript constant literal


Assign[func (contexts, local): A value that can be used to modify an expression in a given context





Try to apply this service





T1: In the first example, we first parse a simple expression: (note: The code should be written on jsfiddle so you should pay attention to the introduction of the Angular.js file when you practice)


The code is as follows:


(HTML)





&lt;div ng-app= "MYAPP" &gt;


&lt;div ng-controller= "Mycontroller" &gt;


&lt;h1&gt;{{ParsedValue}}&lt;/h1&gt;


&lt;/div&gt;


&lt;/div&gt;





(JS)





Angular.module ("MyApp", [])


. Controller ("Mycontroller", Function ($scope, $parse) {


var context = {


Name: "Dreamapple"


};


Because the parsed statement contains the expressions we want to parse,


So you want to enclose the irrelevant quotes, and then use the + link


var expression = "' Hello ' + name";


var parsefunc = $parse (expression);


$scope. Parsedvalue = Parsefunc (context);


});





Expression: Is the expression we want to parse


Context: is a contextual environment for parsing expressions (personal understanding)


Parsefunc: is to parse the function returned later


We can also use the console to see the properties of the returned function:





Angular.module ("MyApp", [])


. Controller ("Mycontroller", Function ($scope, $parse) {


var context = {


Name: "Dreamapple"


};


Because the parsed statement contains the expressions we want to parse,


So you want to enclose the irrelevant quotes, and then use the + link


var expression = "' Hello ' + name";


var parsefunc = $parse (expression);





False


Console.log (parsefunc.literal);


False


Console.log (parsefunc.constant);


Undefined


Console.log (parsefunc.assign);


Hello


Console.log (Parsefunc ());


function (self, locals) {


return fn (self, locals, left, right);


// }


Console.log (Parsefunc);





$scope. Parsedvalue = Parsefunc (context);


});





From the console we can know that the returned Parsefunc is a function where its literal and constant properties are false, and Parsefunc () returns a value that is not added to the function's run context, "Hello".


T1-jsfiddle Code








T2: In the second example, we'll use the $parse service again to parse the value in an input box:




(HTML)





&lt;div ng-app= "MYAPP" &gt;


&lt;div ng-controller= "Mycontroller" &gt;


&lt;input type= "text" ng-model= "expression"/&gt;


&lt;div&gt;{{ParsedValue}}&lt;/div&gt;


&lt;/div&gt;


&lt;/div&gt;








(JS)





Angular.module ("MyApp", [])


. Controller ("Mycontroller", Function ($scope, $parse) {


$scope. $watch ("expression", function (NewValue, oldValue, context) {


if (newvalue!== oldValue) {


var parsefunc = $parse (NewValue);


$scope. Parsedvalue = Parsefunc (context);


}


});


});





We use $watch to monitor the input box changes, every time the value of the expression in the input box changes, we will parse it, we can try to enter the input box "1+1", and then see the following display 2.


T2:jsfiddle Code








T3: We will use the $parse service more intensively in the third instance


(HTML)





&lt;div ng-app= "MYAPP" &gt;


&lt;div ng-controller= "Mycontroller" &gt;


&lt;div&gt;{{ParsedValue}}&lt;/div&gt;


&lt;/div&gt;


&lt;/div&gt;








(JS)





Angular.module ("MyApp", [])


. Controller ("Mycontroller", Function ($scope, $parse) {


$scope. Context = {


Add:function (A, b) {return a + B;},


Mul:function (A, b) {return a * b}


}


$scope. Expression = "Mul (A, add (b, C))";


$scope. data = {


A:3,


B:6,


C:9


};


var parsefunc = $parse ($scope. expression);


$scope. Parsedvalue = Parsefunc ($scope. Context, $scope. data);


});





We can see that the result is 45, as we can roughly understand, $parse the service resolves the $scope.expression statement based on the context provided in $scope.context, and then uses $scope.data data to populate the variables in the expression note that if the $ C in Scope.expression is replaced by 4, then the result is 30, so 45 results are obtained.


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.