@@ -2,36 +2,173 @@ import { toNumber, stripQuotes } from '../util/index'
2
2
import Cache from '../cache'
3
3
4
4
const cache = new Cache ( 1000 )
5
- const filterTokenRE = / [ ^ \s ' " ] + | ' [ ^ ' ] * ' | " [ ^ " ] * " / g
6
5
const reservedArgRE = / ^ i n $ | ^ - ? \d + /
7
6
8
7
/**
9
8
* Parser state
10
9
*/
11
10
12
- var str , dir
13
- var c , prev , i , l , lastFilterIndex
14
- var inSingle , inDouble , curly , square , paren
11
+ var str , dir , len
12
+ var index
13
+ var chr
14
+ var state
15
+ var startState = 0
16
+ var filterState = 1
17
+ var filterNameState = 2
18
+ var filterArgState = 3
19
+
20
+ var doubleChr = 0x22
21
+ var singleChr = 0x27
22
+ var pipeChr = 0x7C
23
+ var escapeChr = 0x5C
24
+ var spaceChr = 0x20
25
+
26
+ var expStartChr = { 0x5B : 1 , 0x7B : 1 , 0x28 : 1 }
27
+ var expChrPair = { 0x5B : 0x5D , 0x7B : 0x7D , 0x28 : 0x29 }
28
+
29
+ function peek ( ) {
30
+ return str . charCodeAt ( index + 1 )
31
+ }
32
+
33
+ function next ( ) {
34
+ return str . charCodeAt ( ++ index )
35
+ }
36
+
37
+ function eof ( ) {
38
+ return index >= len
39
+ }
40
+
41
+ function eatSpace ( ) {
42
+ while ( peek ( ) === spaceChr ) {
43
+ next ( )
44
+ }
45
+ }
46
+
47
+ function isStringStart ( chr ) {
48
+ return chr === doubleChr || chr === singleChr
49
+ }
50
+
51
+ function isExpStart ( chr ) {
52
+ return expStartChr [ chr ]
53
+ }
54
+
55
+ function isExpEnd ( start , chr ) {
56
+ return expChrPair [ start ] === chr
57
+ }
58
+
59
+ function parseString ( ) {
60
+ var stringQuote = next ( )
61
+ var chr
62
+ while ( ! eof ( ) ) {
63
+ chr = next ( )
64
+ // escape char
65
+ if ( chr === escapeChr ) {
66
+ next ( )
67
+ } else if ( chr === stringQuote ) {
68
+ break
69
+ }
70
+ }
71
+ }
72
+
73
+ function parseSpecialExp ( chr ) {
74
+ var inExp = 0
75
+ var startChr = chr
76
+
77
+ while ( ! eof ( ) ) {
78
+ chr = peek ( )
79
+ if ( isStringStart ( chr ) ) {
80
+ parseString ( )
81
+ continue
82
+ }
83
+
84
+ if ( startChr === chr ) {
85
+ inExp ++
86
+ }
87
+ if ( isExpEnd ( startChr , chr ) ) {
88
+ inExp --
89
+ }
90
+
91
+ next ( )
92
+
93
+ if ( inExp === 0 ) {
94
+ break
95
+ }
96
+ }
97
+ }
15
98
16
99
/**
17
- * Push a filter to the current directive object
100
+ * syntax:
101
+ * expression | filterName [arg arg [| filterName arg arg]]
18
102
*/
19
103
20
- function pushFilter ( ) {
21
- var exp = str . slice ( lastFilterIndex , i ) . trim ( )
22
- var filter
23
- if ( exp ) {
24
- filter = { }
25
- var tokens = exp . match ( filterTokenRE )
26
- filter . name = tokens [ 0 ]
27
- if ( tokens . length > 1 ) {
28
- filter . args = tokens . slice ( 1 ) . map ( processFilterArg )
104
+ function parseExpression ( ) {
105
+ var start = index
106
+ while ( ! eof ( ) ) {
107
+ chr = peek ( )
108
+ if ( isStringStart ( chr ) ) {
109
+ parseString ( )
110
+ } else if ( isExpStart ( chr ) ) {
111
+ parseSpecialExp ( chr )
112
+ } else if ( chr === pipeChr ) {
113
+ next ( )
114
+ chr = peek ( )
115
+ if ( chr === pipeChr ) {
116
+ next ( )
117
+ } else {
118
+ if ( state === startState || state === filterArgState ) {
119
+ state = filterState
120
+ }
121
+ break
122
+ }
123
+ } else if ( chr === spaceChr && ( state === filterNameState || state === filterArgState ) ) {
124
+ eatSpace ( )
125
+ break
126
+ } else {
127
+ if ( state === filterState ) {
128
+ state = filterNameState
129
+ }
130
+ next ( )
29
131
}
30
132
}
31
- if ( filter ) {
32
- ( dir . filters = dir . filters || [ ] ) . push ( filter )
133
+
134
+ return str . slice ( start + 1 , index ) || null
135
+ }
136
+
137
+ function parseFilterList ( ) {
138
+ var filters = [ ]
139
+ while ( ! eof ( ) ) {
140
+ filters . push ( parseFilter ( ) )
33
141
}
34
- lastFilterIndex = i + 1
142
+ return filters
143
+ }
144
+
145
+ function parseFilter ( ) {
146
+ var filter = { }
147
+ var args
148
+
149
+ state = filterState
150
+ filter . name = parseExpression ( ) . trim ( )
151
+
152
+ state = filterArgState
153
+ args = parseFilterArguments ( )
154
+
155
+ if ( args . length ) {
156
+ filter . args = args
157
+ }
158
+ return filter
159
+ }
160
+
161
+ function parseFilterArguments ( ) {
162
+ var args = [ ]
163
+ while ( ! eof ( ) && state !== filterState ) {
164
+ var arg = parseExpression ( )
165
+ if ( ! arg ) {
166
+ break
167
+ }
168
+ args . push ( processFilterArg ( arg ) )
169
+ }
170
+
171
+ return args
35
172
}
36
173
37
174
/**
@@ -83,51 +220,22 @@ export function parseDirective (s) {
83
220
84
221
// reset parser state
85
222
str = s
86
- inSingle = inDouble = false
87
- curly = square = paren = 0
88
- lastFilterIndex = 0
89
223
dir = { }
224
+ len = str . length
225
+ index = - 1
226
+ chr = ''
227
+ state = startState
90
228
91
- for ( i = 0 , l = str . length ; i < l ; i ++ ) {
92
- prev = c
93
- c = str . charCodeAt ( i )
94
- if ( inSingle ) {
95
- // check single quote
96
- if ( c === 0x27 && prev !== 0x5C ) inSingle = ! inSingle
97
- } else if ( inDouble ) {
98
- // check double quote
99
- if ( c === 0x22 && prev !== 0x5C ) inDouble = ! inDouble
100
- } else if (
101
- c === 0x7C && // pipe
102
- str . charCodeAt ( i + 1 ) !== 0x7C &&
103
- str . charCodeAt ( i - 1 ) !== 0x7C
104
- ) {
105
- if ( dir . expression == null ) {
106
- // first filter, end of expression
107
- lastFilterIndex = i + 1
108
- dir . expression = str . slice ( 0 , i ) . trim ( )
109
- } else {
110
- // already has filter
111
- pushFilter ( )
112
- }
113
- } else {
114
- switch ( c ) {
115
- case 0x22 : inDouble = true ; break // "
116
- case 0x27 : inSingle = true ; break // '
117
- case 0x28 : paren ++ ; break // (
118
- case 0x29 : paren -- ; break // )
119
- case 0x5B : square ++ ; break // [
120
- case 0x5D : square -- ; break // ]
121
- case 0x7B : curly ++ ; break // {
122
- case 0x7D : curly -- ; break // }
123
- }
124
- }
125
- }
229
+ var filters
126
230
127
- if ( dir . expression == null ) {
128
- dir . expression = str . slice ( 0 , i ) . trim ( )
129
- } else if ( lastFilterIndex !== 0 ) {
130
- pushFilter ( )
231
+ if ( str . indexOf ( '|' ) < 0 ) {
232
+ dir . expression = str . trim ( )
233
+ } else {
234
+ dir . expression = parseExpression ( ) . trim ( )
235
+ filters = parseFilterList ( )
236
+ if ( filters . length ) {
237
+ dir . filters = filters
238
+ }
131
239
}
132
240
133
241
cache . put ( s , dir )
0 commit comments