New input chunker in parser.js. #1615

This commit is contained in:
fredburger
2013-10-30 08:11:22 +01:00
parent 1a33bc69f8
commit 52dc714927
2 changed files with 125 additions and 85 deletions

View File

@@ -359,96 +359,136 @@ less.Parser = function Parser(env) {
parser.imports.contents[env.currentFileInfo.filename] = str;
// Split the input into chunks.
chunks = (function (chunks, input) {
var j = 0,
skip = /(?:@\{[\w-]+\}|[^"'`\{\}\/\(\)\\])+/g,
comment = /\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,
string = /"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'|`((?:[^`]|\\.)*)`/g,
level = 0,
match,
chunk = chunks[0],
inputLen = input.length,
inParam;
chunks = (function (input) {
var checkParenLevel = false; // todo: enable?
for (var i = 0, c, cc; i < inputLen;) {
skip.lastIndex = i;
if (match = skip.exec(input)) {
if (match.index === i) {
i += match[0].length;
chunk.push(match[0]);
}
}
c = input.charAt(i);
comment.lastIndex = string.lastIndex = i;
var len = input.length, level = 0, parenLevel = 0,
lastOpening, lastClosing, lastMultiComment, lastMultiCommentEndBrace,
chunks = [], emitFrom = 0,
k, ko, cc, cc2, matched;
if (match = string.exec(input)) {
if (match.index === i) {
i += match[0].length;
chunk.push(match[0]);
continue;
}
}
if (!inParam && c === '/') {
cc = input.charAt(i + 1);
if (cc === '/' || cc === '*') {
if (match = comment.exec(input)) {
if (match.index === i) {
i += match[0].length;
chunk.push(match[0]);
continue;
}
}
}
}
switch (c) {
case '{':
if (!inParam) {
level++;
chunk.push(c);
break;
}
/* falls through */
case '}':
if (!inParam) {
level--;
chunk.push(c);
chunks[++j] = chunk = [];
break;
}
/* falls through */
case '(':
if (!inParam) {
inParam = true;
chunk.push(c);
break;
}
/* falls through */
case ')':
if (inParam) {
inParam = false;
chunk.push(c);
break;
}
/* falls through */
default:
chunk.push(c);
}
i++;
}
if (level !== 0) {
function fail(msg, index) {
index = index || k;
error = new(LessError)({
index: i-1,
index: index || k,
type: 'Parse',
message: (level > 0) ? "missing closing `}`" : "missing opening `{`",
message: msg,
filename: env.currentFileInfo.filename
}, env);
}
return chunks.map(function (c) { return c.join(''); });
})([[]], str);
function emitChunk(force) {
var len = k - emitFrom;
if (((len < 512) && !force) || !len) {
return;
}
chunks.push(input.slice(emitFrom, k + 1));
emitFrom = k + 1;
}
for (k = 0; k < len; k++) {
cc = input.charCodeAt(k);
if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
// a-z or whitespace
continue;
}
switch (cc) {
case 40: // (
parenLevel++; continue;
case 41: // )
parenLevel--; continue;
case 59: // ;
if (!parenLevel) { emitChunk(); }
continue;
case 123: // {
level++; lastOpening = k; continue;
case 125: // }
level--; lastClosing = k;
if (!level) { emitChunk(); }
continue;
case 92: // \
if (k < len - 1) { k++; continue; }
fail("unescaped '\\'");
break;
case 34:
case 39:
case 96: // ", ' and `
matched = 0;
ko = k;
for (k = k + 1; k < len; k++) {
cc2 = input.charCodeAt(k);
if (cc2 > 96) { continue; }
if (cc2 == cc) { matched = 1; break; }
if (cc2 == 92) { // \
if (k == len - 1) {
fail("unescaped '\\'");
break;
}
k++;
}
}
if (matched) { continue; }
if (!error) {
fail("unmatched '" + String.fromCharCode(cc) + "'", ko);
}
break;
case 47: // /, check for comment
if (parenLevel || (k == len - 1)) { continue; }
cc2 = input.charCodeAt(k + 1);
if (cc2 == 47) {
// //, find lnfeed
for (k = k + 2; k < len; k++) {
cc2 = input.charCodeAt(k);
if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
}
} else if (cc2 == 42) {
// /*, find */
lastMultiComment = ko = k;
for (k = k + 2; k < len - 1; k++) {
cc2 = input.charCodeAt(k);
if (cc2 == 125) { lastMultiCommentEndBrace = k; }
if (cc2 != 42) { continue; }
if (input.charCodeAt(k + 1) == 47) { break; }
}
if (k == len - 1) {
fail("missing closing `*/`", ko);
}
}
continue;
case 42: // *, check for unmatched */
if ((k < len - 1) && (input.charCodeAt(k + 1) == 47)) {
fail("unmatched `/*`");
break;
}
continue;
default:
continue;
}
// only reaches here on error
break;
}
if (!error) {
if (level !== 0) {
if (level > 0) {
if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
fail("missing closing `}` or `*/`", lastOpening);
} else {
fail("missing closing `}`", lastOpening);
}
} else {
fail("missing opening `{`", lastClosing);
}
} else if (checkParenLevel && (parenLevel !== 0)) {
fail((parenLevel > 0) ? "missing closing `)`" : "missing opening `(`");
}
}
emitChunk(true);
return chunks;
})(str);
current = chunks[0];
@@ -938,7 +978,7 @@ less.Parser = function Parser(env) {
option = option && option[1];
extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
} while($char(","));

View File

@@ -1,3 +1,3 @@
ParseError: missing closing `}` in {path}parse-error-missing-bracket.less on line 3, column 1:
ParseError: missing closing `}` in {path}parse-error-missing-bracket.less on line 1, column 6:
1 body {
2 background-color: #fff;
3