1 var util = require('./util');
2 var types = require('./types');
3 var sets = require('./sets');
4 var positions = require('./positions');
7 module.exports = function(regexpStr) {
9 start = { type: types.ROOT, stack: []},
11 // Keep track of last clause/group and stack.
17 var repeatErr = function(i) {
18 util.error(regexpStr, 'Nothing to repeat at column ' + (i - 1));
21 // Decode a few escaped characters.
22 var str = util.strToChars(regexpStr);
25 // Iterate through each character in string.
30 // Handle escaped characters, inclues a few sets.
36 last.push(positions.wordBoundary());
40 last.push(positions.nonWordBoundary());
44 last.push(sets.words());
48 last.push(sets.notWords());
52 last.push(sets.ints());
56 last.push(sets.notInts());
60 last.push(sets.whitespace());
64 last.push(sets.notWhitespace());
68 // Check if c is integer.
69 // In which case it's a reference.
71 last.push({ type: types.REFERENCE, value: parseInt(c, 10) });
75 last.push({ type: types.CHAR, value: c.charCodeAt(0) });
84 last.push(positions.begin());
88 last.push(positions.end());
92 // Handle custom sets.
94 // Check if this class is 'anti' i.e. [^abc].
103 // Get all the characters in class.
104 var classTokens = util.tokenizeClass(str.slice(i), regexpStr);
106 // Increase index by length of class.
117 // Class of any character except \n.
119 last.push(sets.anyChar());
123 // Push group onto stack.
134 // If if this is a special kind of group.
139 // Match if followed by.
141 group.followedBy = true;
143 // Match if not followed by.
144 } else if (c === '!') {
145 group.notFollowedBy = true;
147 } else if (c !== ':') {
148 util.error(regexpStr,
149 'Invalid group, character \'' + c +
150 '\' after \'?\' at column ' + (i - 1));
153 group.remember = false;
156 // Insert subgroup into current group stack.
159 // Remember the current group for when the group closes.
160 groupStack.push(lastGroup);
162 // Make this new group the current group.
168 // Pop group out of stack.
170 if (groupStack.length === 0) {
171 util.error(regexpStr, 'Unmatched ) at column ' + (i - 1));
173 lastGroup = groupStack.pop();
175 // Check if this group has a PIPE.
176 // To get back the correct last stack.
177 last = lastGroup.options ?
178 lastGroup.options[lastGroup.options.length - 1] : lastGroup.stack;
182 // Use pipe character to give more choices.
184 // Create array where options are if this is the first PIPE
186 if (!lastGroup.options) {
187 lastGroup.options = [lastGroup.stack];
188 delete lastGroup.stack;
191 // Create a new stack and add to options for rest of clause.
193 lastGroup.options.push(stack);
199 // For every repetition, remove last element from last stack
200 // then insert back a RANGE object.
201 // This design is chosen because there could be more than
202 // one repetition symbols in a regex i.e. `a?+{2,3}`.
204 var rs = /^(\d+)(,(\d+)?)?\}/.exec(str.slice(i)), min, max;
206 if (last.length === 0) {
209 min = parseInt(rs[1], 10);
210 max = rs[2] ? rs[3] ? parseInt(rs[3], 10) : Infinity : min;
214 type: types.REPETITION,
228 if (last.length === 0) {
232 type: types.REPETITION,
240 if (last.length === 0) {
244 type: types.REPETITION,
252 if (last.length === 0) {
256 type: types.REPETITION,
264 // Default is a character that is not `\[](){}?+*^$`.
268 value: c.charCodeAt(0),
274 // Check if any groups have not been closed.
275 if (groupStack.length !== 0) {
276 util.error(regexpStr, 'Unterminated group');
282 module.exports.types = types;