-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdirective.js
159 lines (146 loc) · 3.4 KB
/
directive.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
var _ = require('../util')
var Cache = require('../cache')
var cache = new Cache(1000)
var argRE = /^[^\{\?]+$|^'[^']*'$|^"[^"]*"$/
var filterTokenRE = /[^\s'"]+|'[^']+'|"[^"]+"/g
/**
* Parser state
*/
var str
var c, i, l
var inSingle
var inDouble
var curly
var square
var paren
var begin
var argIndex
var dirs
var dir
var lastFilterIndex
var arg
/**
* Push a directive object into the result Array
*/
function pushDir () {
dir.raw = str.slice(begin, i).trim()
if (dir.expression === undefined) {
dir.expression = str.slice(argIndex, i).trim()
} else if (lastFilterIndex !== begin) {
pushFilter()
}
if (i === 0 || dir.expression) {
dirs.push(dir)
}
}
/**
* Push a filter to the current directive object
*/
function pushFilter () {
var exp = str.slice(lastFilterIndex, i).trim()
var filter
if (exp) {
filter = {}
var tokens = exp.match(filterTokenRE)
filter.name = tokens[0]
filter.args = tokens.length > 1 ? tokens.slice(1) : null
}
if (filter) {
(dir.filters = dir.filters || []).push(filter)
}
lastFilterIndex = i + 1
}
/**
* Parse a directive string into an Array of AST-like
* objects representing directives.
*
* Example:
*
* "click: a = a + 1 | uppercase" will yield:
* {
* arg: 'click',
* expression: 'a = a + 1',
* filters: [
* { name: 'uppercase', args: null }
* ]
* }
*
* @param {String} str
* @return {Array<Object>}
*/
exports.parse = function (s) {
var hit = cache.get(s)
if (hit) {
return hit
}
// reset parser state
str = s
inSingle = inDouble = false
curly = square = paren = begin = argIndex = 0
lastFilterIndex = 0
dirs = []
dir = {}
arg = null
for (i = 0, l = str.length; i < l; i++) {
c = str.charCodeAt(i)
if (inSingle) {
// check single quote
if (c === 0x27) inSingle = !inSingle
} else if (inDouble) {
// check double quote
if (c === 0x22) inDouble = !inDouble
} else if (
c === 0x2C && // comma
!paren && !curly && !square
) {
// reached the end of a directive
pushDir()
// reset & skip the comma
dir = {}
begin = argIndex = lastFilterIndex = i + 1
} else if (
c === 0x3A && // colon
!dir.expression &&
!dir.arg
) {
// argument
arg = str.slice(begin, i).trim()
// test for valid argument here
// since we may have caught stuff like first half of
// an object literal or a ternary expression.
if (argRE.test(arg)) {
argIndex = i + 1
dir.arg = _.stripQuotes(arg) || arg
}
} else if (
c === 0x7C && // pipe
str.charCodeAt(i + 1) !== 0x7C &&
str.charCodeAt(i - 1) !== 0x7C
) {
if (dir.expression === undefined) {
// first filter, end of expression
lastFilterIndex = i + 1
dir.expression = str.slice(argIndex, i).trim()
} else {
// already has filter
pushFilter()
}
} else {
switch (c) {
case 0x22: inDouble = true; break // "
case 0x27: inSingle = true; break // '
case 0x28: paren++; break // (
case 0x29: paren--; break // )
case 0x5B: square++; break // [
case 0x5D: square--; break // ]
case 0x7B: curly++; break // {
case 0x7D: curly--; break // }
}
}
}
if (i === 0 || begin !== i) {
pushDir()
}
cache.put(s, dirs)
return dirs
}