123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- exports = (module.exports = parse);
- exports.parse = parse;
- function parse(src, state, options) {
- options = options || {};
- state = state || exports.defaultState();
- var start = options.start || 0;
- var end = options.end || src.length;
- var index = start;
- while (index < end) {
- if (state.roundDepth < 0 || state.curlyDepth < 0 || state.squareDepth < 0) {
- throw new SyntaxError('Mismatched Bracket: ' + src[index - 1]);
- }
- exports.parseChar(src[index++], state);
- }
- return state;
- }
-
- exports.parseMax = parseMax;
- function parseMax(src, options) {
- options = options || {};
- var start = options.start || 0;
- var index = start;
- var state = exports.defaultState();
- while (state.roundDepth >= 0 && state.curlyDepth >= 0 && state.squareDepth >= 0) {
- if (index >= src.length) {
- throw new Error('The end of the string was reached with no closing bracket found.');
- }
- exports.parseChar(src[index++], state);
- }
- var end = index - 1;
- return {
- start: start,
- end: end,
- src: src.substring(start, end)
- };
- }
-
- exports.parseUntil = parseUntil;
- function parseUntil(src, delimiter, options) {
- options = options || {};
- var includeLineComment = options.includeLineComment || false;
- var start = options.start || 0;
- var index = start;
- var state = exports.defaultState();
- while (state.isString() || state.regexp || state.blockComment ||
- (!includeLineComment && state.lineComment) || !startsWith(src, delimiter, index)) {
- exports.parseChar(src[index++], state);
- }
- var end = index;
- return {
- start: start,
- end: end,
- src: src.substring(start, end)
- };
- }
-
-
- exports.parseChar = parseChar;
- function parseChar(character, state) {
- if (character.length !== 1) throw new Error('Character must be a string of length 1');
- state = state || exports.defaultState();
- var wasComment = state.blockComment || state.lineComment;
- var lastChar = state.history ? state.history[0] : '';
- if (state.lineComment) {
- if (character === '\n') {
- state.lineComment = false;
- }
- } else if (state.blockComment) {
- if (state.lastChar === '*' && character === '/') {
- state.blockComment = false;
- }
- } else if (state.singleQuote) {
- if (character === '\'' && !state.escaped) {
- state.singleQuote = false;
- } else if (character === '\\' && !state.escaped) {
- state.escaped = true;
- } else {
- state.escaped = false;
- }
- } else if (state.doubleQuote) {
- if (character === '"' && !state.escaped) {
- state.doubleQuote = false;
- } else if (character === '\\' && !state.escaped) {
- state.escaped = true;
- } else {
- state.escaped = false;
- }
- } else if (state.regexp) {
- if (character === '/' && !state.escaped) {
- state.regexp = false;
- } else if (character === '\\' && !state.escaped) {
- state.escaped = true;
- } else {
- state.escaped = false;
- }
- } else if (lastChar === '/' && character === '/') {
- state.history = state.history.substr(1);
- state.lineComment = true;
- } else if (lastChar === '/' && character === '*') {
- state.history = state.history.substr(1);
- state.blockComment = true;
- } else if (character === '/' && isRegexp(state.history)) {
- state.regexp = true;
- } else if (character === '\'') {
- state.singleQuote = true;
- } else if (character === '"') {
- state.doubleQuote = true;
- } else if (character === '(') {
- state.roundDepth++;
- } else if (character === ')') {
- state.roundDepth--;
- } else if (character === '{') {
- state.curlyDepth++;
- } else if (character === '}') {
- state.curlyDepth--;
- } else if (character === '[') {
- state.squareDepth++;
- } else if (character === ']') {
- state.squareDepth--;
- }
- if (!state.blockComment && !state.lineComment && !wasComment) state.history = character + state.history;
- return state;
- }
-
- exports.defaultState = function () { return new State() };
- function State() {
- this.lineComment = false;
- this.blockComment = false;
-
- this.singleQuote = false;
- this.doubleQuote = false;
- this.regexp = false;
- this.escaped = false;
-
- this.roundDepth = 0;
- this.curlyDepth = 0;
- this.squareDepth = 0;
-
- this.history = ''
- }
- State.prototype.isString = function () {
- return this.singleQuote || this.doubleQuote;
- }
- State.prototype.isComment = function () {
- return this.lineComment || this.blockComment;
- }
- State.prototype.isNesting = function () {
- return this.isString() || this.isComment() || this.regexp || this.roundDepth > 0 || this.curlyDepth > 0 || this.squareDepth > 0
- }
-
- function startsWith(str, start, i) {
- return str.substr(i || 0, start.length) === start;
- }
-
- exports.isPunctuator = isPunctuator
- function isPunctuator(c) {
- var code = c.charCodeAt(0)
-
- switch (code) {
- case 46: // . dot
- case 40: // ( open bracket
- case 41: // ) close bracket
- case 59: // ; semicolon
- case 44: // , comma
- case 123: // { open curly brace
- case 125: // } close curly brace
- case 91: // [
- case 93: // ]
- case 58: // :
- case 63: // ?
- case 126: // ~
- case 37: // %
- case 38: // &
- case 42: // *:
- case 43: // +
- case 45: // -
- case 47: // /
- case 60: // <
- case 62: // >
- case 94: // ^
- case 124: // |
- case 33: // !
- case 61: // =
- return true;
- default:
- return false;
- }
- }
- exports.isKeyword = isKeyword
- function isKeyword(id) {
- return (id === 'if') || (id === 'in') || (id === 'do') || (id === 'var') || (id === 'for') || (id === 'new') ||
- (id === 'try') || (id === 'let') || (id === 'this') || (id === 'else') || (id === 'case') ||
- (id === 'void') || (id === 'with') || (id === 'enum') || (id === 'while') || (id === 'break') || (id === 'catch') ||
- (id === 'throw') || (id === 'const') || (id === 'yield') || (id === 'class') || (id === 'super') ||
- (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch') || (id === 'export') ||
- (id === 'import') || (id === 'default') || (id === 'finally') || (id === 'extends') || (id === 'function') ||
- (id === 'continue') || (id === 'debugger') || (id === 'package') || (id === 'private') || (id === 'interface') ||
- (id === 'instanceof') || (id === 'implements') || (id === 'protected') || (id === 'public') || (id === 'static') ||
- (id === 'yield') || (id === 'let');
- }
-
- function isRegexp(history) {
- //could be start of regexp or divide sign
-
- history = history.replace(/^\s*/, '');
-
- //unless its an `if`, `while`, `for` or `with` it's a divide, so we assume it's a divide
- if (history[0] === ')') return false;
- //unless it's a function expression, it's a regexp, so we assume it's a regexp
- if (history[0] === '}') return true;
- //any punctuation means it's a regexp
- if (isPunctuator(history[0])) return true;
- //if the last thing was a keyword then it must be a regexp (e.g. `typeof /foo/`)
- if (/^\w+\b/.test(history) && isKeyword(/^\w+\b/.exec(history)[0].split('').reverse().join(''))) return true;
-
- return false;
- }
|