Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2.0.0 refactor chunker and less error #2022

Merged
merged 5 commits into from
Jun 8, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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