mirror of
https://github.com/less/less.js.git
synced 2026-04-09 03:00:20 -04:00
Merge pull request #2022 from ForbesLindesay/2_0_0-refactor-chunker-and-less-error
2.0.0 refactor chunker and less error
This commit is contained in:
123
lib/less/chunker.js
Normal file
123
lib/less/chunker.js
Normal file
@@ -0,0 +1,123 @@
|
||||
var LessError = require('./less-error.js');
|
||||
|
||||
// Split the input into chunks.
|
||||
module.exports = function (parser, input, env) {
|
||||
var len = input.length, level = 0, parenLevel = 0,
|
||||
lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace,
|
||||
chunks = [], emitFrom = 0,
|
||||
parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched;
|
||||
|
||||
function fail(msg, index) {
|
||||
throw new(LessError)(parser, {
|
||||
index: index || parserCurrentIndex,
|
||||
type: 'Parse',
|
||||
message: msg,
|
||||
filename: env.currentFileInfo.filename
|
||||
}, env);
|
||||
}
|
||||
|
||||
function emitChunk(force) {
|
||||
var len = parserCurrentIndex - emitFrom;
|
||||
if (((len < 512) && !force) || !len) {
|
||||
return;
|
||||
}
|
||||
chunks.push(input.slice(emitFrom, parserCurrentIndex + 1));
|
||||
emitFrom = parserCurrentIndex + 1;
|
||||
}
|
||||
|
||||
for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) {
|
||||
cc = input.charCodeAt(parserCurrentIndex);
|
||||
if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
|
||||
// a-z or whitespace
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (cc) {
|
||||
case 40: // (
|
||||
parenLevel++;
|
||||
lastOpeningParen = parserCurrentIndex;
|
||||
continue;
|
||||
case 41: // )
|
||||
if (--parenLevel < 0) {
|
||||
return fail("missing opening `(`");
|
||||
}
|
||||
continue;
|
||||
case 59: // ;
|
||||
if (!parenLevel) { emitChunk(); }
|
||||
continue;
|
||||
case 123: // {
|
||||
level++;
|
||||
lastOpening = parserCurrentIndex;
|
||||
continue;
|
||||
case 125: // }
|
||||
if (--level < 0) {
|
||||
return fail("missing opening `{`");
|
||||
}
|
||||
if (!level && !parenLevel) { emitChunk(); }
|
||||
continue;
|
||||
case 92: // \
|
||||
if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; }
|
||||
return fail("unescaped `\\`");
|
||||
case 34:
|
||||
case 39:
|
||||
case 96: // ", ' and `
|
||||
matched = 0;
|
||||
currentChunkStartIndex = parserCurrentIndex;
|
||||
for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) {
|
||||
cc2 = input.charCodeAt(parserCurrentIndex);
|
||||
if (cc2 > 96) { continue; }
|
||||
if (cc2 == cc) { matched = 1; break; }
|
||||
if (cc2 == 92) { // \
|
||||
if (parserCurrentIndex == len - 1) {
|
||||
return fail("unescaped `\\`");
|
||||
}
|
||||
parserCurrentIndex++;
|
||||
}
|
||||
}
|
||||
if (matched) { continue; }
|
||||
return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
|
||||
case 47: // /, check for comment
|
||||
if (parenLevel || (parserCurrentIndex == len - 1)) { continue; }
|
||||
cc2 = input.charCodeAt(parserCurrentIndex + 1);
|
||||
if (cc2 == 47) {
|
||||
// //, find lnfeed
|
||||
for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) {
|
||||
cc2 = input.charCodeAt(parserCurrentIndex);
|
||||
if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
|
||||
}
|
||||
} else if (cc2 == 42) {
|
||||
// /*, find */
|
||||
lastMultiComment = currentChunkStartIndex = parserCurrentIndex;
|
||||
for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) {
|
||||
cc2 = input.charCodeAt(parserCurrentIndex);
|
||||
if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; }
|
||||
if (cc2 != 42) { continue; }
|
||||
if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; }
|
||||
}
|
||||
if (parserCurrentIndex == len - 1) {
|
||||
return fail("missing closing `*/`", currentChunkStartIndex);
|
||||
}
|
||||
parserCurrentIndex++;
|
||||
}
|
||||
continue;
|
||||
case 42: // *, check for unmatched */
|
||||
if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) {
|
||||
return fail("unmatched `/*`");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (level !== 0) {
|
||||
if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
|
||||
return fail("missing closing `}` or `*/`", lastOpening);
|
||||
} else {
|
||||
return fail("missing closing `}`", lastOpening);
|
||||
}
|
||||
} else if (parenLevel !== 0) {
|
||||
return fail("missing closing `)`", lastOpeningParen);
|
||||
}
|
||||
|
||||
emitChunk(true);
|
||||
return chunks;
|
||||
};
|
||||
27
lib/less/less-error.js
Normal file
27
lib/less/less-error.js
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
var LessError = module.exports = function LessError(parser, e, env) {
|
||||
var input = parser.getInput(e, env),
|
||||
loc = parser.getLocation(e.index, input),
|
||||
line = loc.line,
|
||||
col = loc.column,
|
||||
callLine = e.call && parser.getLocation(e.call, input).line,
|
||||
lines = input.split('\n');
|
||||
|
||||
this.type = e.type || 'Syntax';
|
||||
this.message = e.message;
|
||||
this.filename = e.filename || env.currentFileInfo.filename;
|
||||
this.index = e.index;
|
||||
this.line = typeof(line) === 'number' ? line + 1 : null;
|
||||
this.callLine = callLine + 1;
|
||||
this.callExtract = lines[callLine];
|
||||
this.stack = e.stack;
|
||||
this.column = col;
|
||||
this.extract = [
|
||||
lines[line - 1],
|
||||
lines[line],
|
||||
lines[line + 1]
|
||||
];
|
||||
};
|
||||
|
||||
LessError.prototype = new Error();
|
||||
LessError.prototype.constructor = LessError;
|
||||
@@ -1,3 +1,6 @@
|
||||
var chunker = require('./chunker.js');
|
||||
var LessError = require('./less-error.js');
|
||||
|
||||
module.exports = function(less, tree, visitor) {
|
||||
//
|
||||
// less.js - parser
|
||||
@@ -320,33 +323,6 @@ var Parser = function Parser(env) {
|
||||
};
|
||||
}
|
||||
|
||||
function LessError(e, env) {
|
||||
var input = getInput(e, env),
|
||||
loc = getLocation(e.index, input),
|
||||
line = loc.line,
|
||||
col = loc.column,
|
||||
callLine = e.call && getLocation(e.call, input).line,
|
||||
lines = input.split('\n');
|
||||
|
||||
this.type = e.type || 'Syntax';
|
||||
this.message = e.message;
|
||||
this.filename = e.filename || env.currentFileInfo.filename;
|
||||
this.index = e.index;
|
||||
this.line = typeof(line) === 'number' ? line + 1 : null;
|
||||
this.callLine = callLine + 1;
|
||||
this.callExtract = lines[callLine];
|
||||
this.stack = e.stack;
|
||||
this.column = col;
|
||||
this.extract = [
|
||||
lines[line - 1],
|
||||
lines[line],
|
||||
lines[line + 1]
|
||||
];
|
||||
}
|
||||
|
||||
LessError.prototype = new Error();
|
||||
LessError.prototype.constructor = LessError;
|
||||
|
||||
this.env = env = env || {};
|
||||
|
||||
//
|
||||
@@ -379,130 +355,10 @@ var Parser = function Parser(env) {
|
||||
input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars;
|
||||
parser.imports.contents[env.currentFileInfo.filename] = str;
|
||||
|
||||
// Split the input into chunks.
|
||||
chunks = (function (input) {
|
||||
var len = input.length, level = 0, parenLevel = 0,
|
||||
lastOpening, lastOpeningParen, lastMultiComment, lastMultiCommentEndBrace,
|
||||
chunks = [], emitFrom = 0,
|
||||
parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched;
|
||||
|
||||
function fail(msg, index) {
|
||||
error = new(LessError)({
|
||||
index: index || parserCurrentIndex,
|
||||
type: 'Parse',
|
||||
message: msg,
|
||||
filename: env.currentFileInfo.filename
|
||||
}, env);
|
||||
}
|
||||
|
||||
function emitChunk(force) {
|
||||
var len = parserCurrentIndex - emitFrom;
|
||||
if (((len < 512) && !force) || !len) {
|
||||
return;
|
||||
}
|
||||
chunks.push(input.slice(emitFrom, parserCurrentIndex + 1));
|
||||
emitFrom = parserCurrentIndex + 1;
|
||||
}
|
||||
|
||||
for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) {
|
||||
cc = input.charCodeAt(parserCurrentIndex);
|
||||
if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
|
||||
// a-z or whitespace
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (cc) {
|
||||
case 40: // (
|
||||
parenLevel++;
|
||||
lastOpeningParen = parserCurrentIndex;
|
||||
continue;
|
||||
case 41: // )
|
||||
if (--parenLevel < 0) {
|
||||
return fail("missing opening `(`");
|
||||
}
|
||||
continue;
|
||||
case 59: // ;
|
||||
if (!parenLevel) { emitChunk(); }
|
||||
continue;
|
||||
case 123: // {
|
||||
level++;
|
||||
lastOpening = parserCurrentIndex;
|
||||
continue;
|
||||
case 125: // }
|
||||
if (--level < 0) {
|
||||
return fail("missing opening `{`");
|
||||
}
|
||||
if (!level && !parenLevel) { emitChunk(); }
|
||||
continue;
|
||||
case 92: // \
|
||||
if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; }
|
||||
return fail("unescaped `\\`");
|
||||
case 34:
|
||||
case 39:
|
||||
case 96: // ", ' and `
|
||||
matched = 0;
|
||||
currentChunkStartIndex = parserCurrentIndex;
|
||||
for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) {
|
||||
cc2 = input.charCodeAt(parserCurrentIndex);
|
||||
if (cc2 > 96) { continue; }
|
||||
if (cc2 == cc) { matched = 1; break; }
|
||||
if (cc2 == 92) { // \
|
||||
if (parserCurrentIndex == len - 1) {
|
||||
return fail("unescaped `\\`");
|
||||
}
|
||||
parserCurrentIndex++;
|
||||
}
|
||||
}
|
||||
if (matched) { continue; }
|
||||
return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
|
||||
case 47: // /, check for comment
|
||||
if (parenLevel || (parserCurrentIndex == len - 1)) { continue; }
|
||||
cc2 = input.charCodeAt(parserCurrentIndex + 1);
|
||||
if (cc2 == 47) {
|
||||
// //, find lnfeed
|
||||
for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) {
|
||||
cc2 = input.charCodeAt(parserCurrentIndex);
|
||||
if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
|
||||
}
|
||||
} else if (cc2 == 42) {
|
||||
// /*, find */
|
||||
lastMultiComment = currentChunkStartIndex = parserCurrentIndex;
|
||||
for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) {
|
||||
cc2 = input.charCodeAt(parserCurrentIndex);
|
||||
if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; }
|
||||
if (cc2 != 42) { continue; }
|
||||
if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; }
|
||||
}
|
||||
if (parserCurrentIndex == len - 1) {
|
||||
return fail("missing closing `*/`", currentChunkStartIndex);
|
||||
}
|
||||
parserCurrentIndex++;
|
||||
}
|
||||
continue;
|
||||
case 42: // *, check for unmatched */
|
||||
if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) {
|
||||
return fail("unmatched `/*`");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (level !== 0) {
|
||||
if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
|
||||
return fail("missing closing `}` or `*/`", lastOpening);
|
||||
} else {
|
||||
return fail("missing closing `}`", lastOpening);
|
||||
}
|
||||
} else if (parenLevel !== 0) {
|
||||
return fail("missing closing `)`", lastOpeningParen);
|
||||
}
|
||||
|
||||
emitChunk(true);
|
||||
return chunks;
|
||||
})(str);
|
||||
|
||||
if (error) {
|
||||
return callback(new(LessError)(error, env));
|
||||
try {
|
||||
chunks = chunker(parser, str, env);
|
||||
} catch (ex) {
|
||||
return callback(new LessError(parser, ex, env));
|
||||
}
|
||||
|
||||
current = chunks[0];
|
||||
@@ -516,7 +372,7 @@ var Parser = function Parser(env) {
|
||||
root.root = true;
|
||||
root.firstRoot = true;
|
||||
} catch (e) {
|
||||
return callback(new(LessError)(e, env));
|
||||
return callback(new LessError(parser, e, env));
|
||||
}
|
||||
|
||||
root.toCSS = (function (evaluate) {
|
||||
@@ -609,7 +465,7 @@ var Parser = function Parser(env) {
|
||||
strictUnits: Boolean(options.strictUnits),
|
||||
numPrecision: 8});
|
||||
} catch (e) {
|
||||
throw new(LessError)(e, env);
|
||||
throw new LessError(parser, e, env);
|
||||
}
|
||||
|
||||
var CleanCSS = less.environment.getCleanCSS();
|
||||
@@ -668,7 +524,7 @@ var Parser = function Parser(env) {
|
||||
|
||||
if (e) {
|
||||
if (!(e instanceof LessError)) {
|
||||
e = new(LessError)(e, env);
|
||||
e = new LessError(parser, e, env);
|
||||
}
|
||||
|
||||
return callback(e);
|
||||
@@ -2053,6 +1909,10 @@ var Parser = function Parser(env) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
parser.getInput = getInput;
|
||||
parser.getLocation = getLocation;
|
||||
|
||||
return parser;
|
||||
};
|
||||
Parser.serializeVars = function(vars) {
|
||||
|
||||
Reference in New Issue
Block a user