Skip to content

Commit 149df13

Browse files
committed
improve path parser (fix #1063)
1 parent 965b61d commit 149df13

File tree

2 files changed

+115
-93
lines changed

2 files changed

+115
-93
lines changed

src/parsers/path.js

Lines changed: 114 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -3,78 +3,100 @@ var Cache = require('../cache')
33
var pathCache = new Cache(1000)
44
var identRE = exports.identRE = /^[$_a-zA-Z]+[\w$]*$/
55

6-
/**
7-
* Path-parsing algorithm scooped from Polymer/observe-js
8-
*/
6+
// actions
7+
var APPEND = 0
8+
var PUSH = 1
9+
10+
// states
11+
var BEFORE_PATH = 0
12+
var IN_PATH = 1
13+
var BEFORE_IDENT = 2
14+
var IN_IDENT = 3
15+
var BEFORE_ELEMENT = 4
16+
var AFTER_ZERO = 5
17+
var IN_INDEX = 6
18+
var IN_SINGLE_QUOTE = 7
19+
var IN_DOUBLE_QUOTE = 8
20+
var IN_SUB_PATH = 9
21+
var AFTER_ELEMENT = 10
22+
var AFTER_PATH = 11
23+
var ERROR = 12
24+
25+
var pathStateMachine = []
26+
27+
pathStateMachine[BEFORE_PATH] = {
28+
'ws': [BEFORE_PATH],
29+
'ident': [IN_IDENT, APPEND],
30+
'[': [BEFORE_ELEMENT],
31+
'eof': [AFTER_PATH]
32+
}
933

10-
var pathStateMachine = {
11-
'beforePath': {
12-
'ws': ['beforePath'],
13-
'ident': ['inIdent', 'append'],
14-
'[': ['beforeElement'],
15-
'eof': ['afterPath']
16-
},
17-
18-
'inPath': {
19-
'ws': ['inPath'],
20-
'.': ['beforeIdent'],
21-
'[': ['beforeElement'],
22-
'eof': ['afterPath']
23-
},
24-
25-
'beforeIdent': {
26-
'ws': ['beforeIdent'],
27-
'ident': ['inIdent', 'append']
28-
},
29-
30-
'inIdent': {
31-
'ident': ['inIdent', 'append'],
32-
'0': ['inIdent', 'append'],
33-
'number': ['inIdent', 'append'],
34-
'ws': ['inPath', 'push'],
35-
'.': ['beforeIdent', 'push'],
36-
'[': ['beforeElement', 'push'],
37-
'eof': ['afterPath', 'push'],
38-
']': ['inPath', 'push']
39-
},
40-
41-
'beforeElement': {
42-
'ws': ['beforeElement'],
43-
'0': ['afterZero', 'append'],
44-
'number': ['inIndex', 'append'],
45-
"'": ['inSingleQuote', 'append', ''],
46-
'"': ['inDoubleQuote', 'append', ''],
47-
'ident': ['inIdent', 'append', '*']
48-
},
49-
50-
'afterZero': {
51-
'ws': ['afterElement', 'push'],
52-
']': ['inPath', 'push']
53-
},
54-
55-
'inIndex': {
56-
'0': ['inIndex', 'append'],
57-
'number': ['inIndex', 'append'],
58-
'ws': ['afterElement'],
59-
']': ['inPath', 'push']
60-
},
61-
62-
'inSingleQuote': {
63-
"'": ['afterElement'],
64-
'eof': 'error',
65-
'else': ['inSingleQuote', 'append']
66-
},
67-
68-
'inDoubleQuote': {
69-
'"': ['afterElement'],
70-
'eof': 'error',
71-
'else': ['inDoubleQuote', 'append']
72-
},
73-
74-
'afterElement': {
75-
'ws': ['afterElement'],
76-
']': ['inPath', 'push']
77-
}
34+
pathStateMachine[IN_PATH] = {
35+
'ws': [IN_PATH],
36+
'.': [BEFORE_IDENT],
37+
'[': [BEFORE_ELEMENT],
38+
'eof': [AFTER_PATH]
39+
}
40+
41+
pathStateMachine[BEFORE_IDENT] = {
42+
'ws': [BEFORE_IDENT],
43+
'ident': [IN_IDENT, APPEND]
44+
}
45+
46+
pathStateMachine[IN_IDENT] = {
47+
'ident': [IN_IDENT, APPEND],
48+
'0': [IN_IDENT, APPEND],
49+
'number': [IN_IDENT, APPEND],
50+
'ws': [IN_PATH, PUSH],
51+
'.': [BEFORE_IDENT, PUSH],
52+
'[': [BEFORE_ELEMENT, PUSH],
53+
'eof': [AFTER_PATH, PUSH]
54+
}
55+
56+
pathStateMachine[BEFORE_ELEMENT] = {
57+
'ws': [BEFORE_ELEMENT],
58+
'0': [AFTER_ZERO, APPEND],
59+
'number': [IN_INDEX, APPEND],
60+
"'": [IN_SINGLE_QUOTE, APPEND, ''],
61+
'"': [IN_DOUBLE_QUOTE, APPEND, ''],
62+
'ident': [IN_SUB_PATH, APPEND, '*']
63+
}
64+
65+
pathStateMachine[AFTER_ZERO] = {
66+
'ws': [AFTER_ELEMENT, PUSH],
67+
']': [IN_PATH, PUSH]
68+
}
69+
70+
pathStateMachine[IN_INDEX] = {
71+
'0': [IN_INDEX, APPEND],
72+
'number': [IN_INDEX, APPEND],
73+
'ws': [AFTER_ELEMENT],
74+
']': [IN_PATH, PUSH]
75+
}
76+
77+
pathStateMachine[IN_SINGLE_QUOTE] = {
78+
"'": [AFTER_ELEMENT],
79+
'eof': ERROR,
80+
'else': [IN_SINGLE_QUOTE, APPEND]
81+
}
82+
83+
pathStateMachine[IN_DOUBLE_QUOTE] = {
84+
'"': [AFTER_ELEMENT],
85+
'eof': ERROR,
86+
'else': [IN_DOUBLE_QUOTE, APPEND]
87+
}
88+
89+
pathStateMachine[IN_SUB_PATH] = {
90+
'ident': [IN_SUB_PATH, APPEND],
91+
'0': [IN_SUB_PATH, APPEND],
92+
'number': [IN_SUB_PATH, APPEND],
93+
'ws': [AFTER_ELEMENT, PUSH],
94+
']': [IN_PATH, PUSH]
95+
}
96+
97+
pathStateMachine[AFTER_ELEMENT] = {
98+
'ws': [AFTER_ELEMENT],
99+
']': [IN_PATH, PUSH]
78100
}
79101

80102
function noop () {}
@@ -144,38 +166,37 @@ function getPathCharType (ch) {
144166
function parsePath (path) {
145167
var keys = []
146168
var index = -1
147-
var mode = 'beforePath'
169+
var mode = BEFORE_PATH
148170
var c, newChar, key, type, transition, action, typeMap
149171

150-
var actions = {
151-
push: function () {
152-
if (key === undefined) {
153-
return
154-
}
155-
keys.push(key)
156-
key = undefined
157-
},
158-
append: function () {
159-
if (key === undefined) {
160-
key = newChar
161-
} else {
162-
key += newChar
163-
}
172+
var actions = []
173+
actions[PUSH] = function () {
174+
if (key === undefined) {
175+
return
176+
}
177+
keys.push(key)
178+
key = undefined
179+
}
180+
actions[APPEND] = function () {
181+
if (key === undefined) {
182+
key = newChar
183+
} else {
184+
key += newChar
164185
}
165186
}
166187

167188
function maybeUnescapeQuote () {
168189
var nextChar = path[index + 1]
169-
if ((mode === 'inSingleQuote' && nextChar === "'") ||
170-
(mode === 'inDoubleQuote' && nextChar === '"')) {
190+
if ((mode === IN_SINGLE_QUOTE && nextChar === "'") ||
191+
(mode === IN_DOUBLE_QUOTE && nextChar === '"')) {
171192
index++
172193
newChar = nextChar
173-
actions.append()
194+
actions[APPEND]()
174195
return true
175196
}
176197
}
177198

178-
while (mode) {
199+
while (mode != null) {
179200
index++
180201
c = path[index]
181202

@@ -185,9 +206,9 @@ function parsePath (path) {
185206

186207
type = getPathCharType(c)
187208
typeMap = pathStateMachine[mode]
188-
transition = typeMap[type] || typeMap['else'] || 'error'
209+
transition = typeMap[type] || typeMap['else'] || ERROR
189210

190-
if (transition === 'error') {
211+
if (transition === ERROR) {
191212
return // parse error
192213
}
193214

@@ -201,7 +222,7 @@ function parsePath (path) {
201222
: newChar
202223
action()
203224

204-
if (mode === 'afterPath') {
225+
if (mode === AFTER_PATH) {
205226
keys.raw = path
206227
return keys
207228
}

test/unit/specs/parsers/path_spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ describe('Path Parser', function () {
6666
assertInvalidPath('foo["bar]')
6767
assertInvalidPath("foo['bar]")
6868
assertInvalidPath('foo[bar + boo]')
69+
assertInvalidPath('a]')
6970
})
7071

7172
it('caching', function () {

0 commit comments

Comments
 (0)