Skip to content

Commit

Permalink
Merge pull request #2022 from ForbesLindesay/2_0_0-refactor-chunker-a…
Browse files Browse the repository at this point in the history
…nd-less-error

2.0.0 refactor chunker and less error
  • Loading branch information
lukeapage committed Jun 8, 2014
2 parents 1d629bf + 01a5d7c commit dcdc76f
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 154 deletions.
123 changes: 123 additions & 0 deletions lib/less/chunker.js
Original file line number Diff line number Diff line change
@@ -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 changes: 27 additions & 0 deletions lib/less/less-error.js
Original file line number Diff line number Diff line change
@@ -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;
168 changes: 14 additions & 154 deletions lib/less/parser.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
var chunker = require('./chunker.js');
var LessError = require('./less-error.js');

module.exports = function(less, tree, visitor) {
//
// less.js - parser
Expand Down Expand Up @@ -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 || {};

//
Expand Down Expand Up @@ -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];
Expand All @@ -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) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -2053,6 +1909,10 @@ var Parser = function Parser(env) {
}
}
};

parser.getInput = getInput;
parser.getLocation = getLocation;

return parser;
};
Parser.serializeVars = function(vars) {
Expand Down

0 comments on commit dcdc76f

Please sign in to comment.