mirror of
https://github.com/less/less.js.git
synced 2026-04-09 03:00:20 -04:00
cleanup, smaller chunks, speed improvements
This commit is contained in:
@@ -10,22 +10,16 @@ if (typeof(window) === 'undefined') {
|
||||
//
|
||||
// less.js - parser
|
||||
//
|
||||
// A relatively straight-forward recursive-descent parser.
|
||||
// A relatively straight-forward predictive parser.
|
||||
// There is no tokenization/lexing stage, the input is parsed
|
||||
// in one sweep.
|
||||
//
|
||||
// To make the parser fast enough to run in the browser, several
|
||||
// optimization had to be made:
|
||||
//
|
||||
// - Instead of the more commonly used technique of slicing the
|
||||
// input string on every match, we use global regexps (/g),
|
||||
// and move the `lastIndex` pointer on match, foregoing `slice()`
|
||||
// completely. This gives us a 3x speed-up.
|
||||
//
|
||||
// - Matching on a huge input is often cause of slowdowns,
|
||||
// especially with the /g flag. The solution to that is to
|
||||
// chunkify the input: we split it by /\n\n/, just to be on
|
||||
// the safe side. The chunks are stored in the `chunks` var,
|
||||
// - Matching and slicing on a huge input is often cause of slowdowns.
|
||||
// The solution is to chunkify the input into smaller strings.
|
||||
// The chunks are stored in the `chunks` var,
|
||||
// `j` holds the current chunk index, and `current` holds
|
||||
// the index of the current chunk in relation to `input`.
|
||||
// This gives us an almost 4x speed-up.
|
||||
@@ -50,9 +44,8 @@ less.Parser = function Parser(env) {
|
||||
var input, // LeSS input string
|
||||
i, // current index in `input`
|
||||
j, // current chunk
|
||||
temp,
|
||||
memo,
|
||||
chunk,
|
||||
temp, // temporarily holds a chunk's state, for backtracking
|
||||
memo, // temporarily holds `i`, when backtracking
|
||||
furthest, // furthest index the parser has gone to
|
||||
chunks, // chunkified input
|
||||
current, // index of current chunk, in `input`
|
||||
@@ -87,19 +80,12 @@ less.Parser = function Parser(env) {
|
||||
}
|
||||
};
|
||||
|
||||
function save(){
|
||||
temp = chunk;
|
||||
memo = i;
|
||||
current = i;
|
||||
}
|
||||
function restore() {
|
||||
chunks[j] = chunk = temp;
|
||||
i = memo;
|
||||
current = i;
|
||||
}
|
||||
function save() { temp = chunks[j], memo = i, current = i }
|
||||
function restore() { chunks[j] = temp, i = memo, current = i }
|
||||
|
||||
function sync() {
|
||||
if (i > current) {
|
||||
chunks[j] = chunk = chunk.slice(i - current);
|
||||
chunks[j] = chunks[j].slice(i - current);
|
||||
current = i;
|
||||
}
|
||||
}
|
||||
@@ -136,11 +122,11 @@ less.Parser = function Parser(env) {
|
||||
} else {
|
||||
sync ();
|
||||
|
||||
match = tok.exec(chunk);
|
||||
|
||||
if (match) { // 3.
|
||||
if (match = tok.exec(chunks[j])) { // 3.
|
||||
length = match[0].length;
|
||||
} else { return }
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// The match is confirmed, add the match length to `i`,
|
||||
@@ -149,8 +135,7 @@ less.Parser = function Parser(env) {
|
||||
// grammar is mostly white-space insensitive.
|
||||
//
|
||||
if (match) {
|
||||
i += length;
|
||||
var mem = i;
|
||||
mem = i += length;
|
||||
endIndex = i + chunk.length - length;
|
||||
|
||||
while (i < endIndex) {
|
||||
@@ -158,10 +143,10 @@ less.Parser = function Parser(env) {
|
||||
if (! (c === 32 || c === 10 || c === 9)) { break }
|
||||
i++;
|
||||
}
|
||||
chunks[j] = chunk = chunk.slice(length + (i - mem));
|
||||
chunks[j] = chunks[j].slice(length + (i - mem));
|
||||
current = i;
|
||||
|
||||
if (chunk.length === 0 && j < chunks.length - 1) { chunk = chunks[++j] }
|
||||
if (chunks[j].length === 0 && j < chunks.length - 1) { j++ }
|
||||
|
||||
if(typeof(match) === 'string') {
|
||||
return match;
|
||||
@@ -177,7 +162,7 @@ less.Parser = function Parser(env) {
|
||||
if (typeof(tok) === 'string') {
|
||||
return input.charAt(i) === tok;
|
||||
} else {
|
||||
if (tok.test(chunk)) {
|
||||
if (tok.test(chunks[j])) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -185,14 +170,6 @@ less.Parser = function Parser(env) {
|
||||
}
|
||||
}
|
||||
|
||||
function exec(tok) {
|
||||
var match;
|
||||
|
||||
if ((match = tok.exec(chunks[j]))) {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
this.env = env || {};
|
||||
|
||||
// The optimization level dictates the thoroughness of the parser,
|
||||
@@ -223,13 +200,10 @@ less.Parser = function Parser(env) {
|
||||
// removing comments (see rationale above),
|
||||
// depending on the level of optimization.
|
||||
if (that.optimization > 0) {
|
||||
input = input.replace(/\/\*(?:[^*]|\*+[^\/*])*\*+\//g, function (comment) {
|
||||
return that.optimization > 1 ? '' : comment.replace(/\n(\s*\n)+/g, '\n');
|
||||
});
|
||||
if (that.optimization > 1) {
|
||||
input = input.replace(/\/\*(?:[^*]|\*+[^\/*])*\*+\//g, '');
|
||||
chunks = (function (chunks) {
|
||||
var level = 0,
|
||||
j = 0,
|
||||
var j = 0,
|
||||
skip = /[^"'\{\}]+/g,
|
||||
match,
|
||||
chunk,
|
||||
@@ -249,15 +223,10 @@ less.Parser = function Parser(env) {
|
||||
c = input.charAt(i);
|
||||
|
||||
if (c === '}' && !inString) {
|
||||
level --;
|
||||
chunk.push(c);
|
||||
if (level === 0) {
|
||||
chunks[++j] = [];
|
||||
}
|
||||
chunks[++j] = [];
|
||||
} else {
|
||||
if (c === '{' && !inString) {
|
||||
level ++;
|
||||
} else if (c === '"' || c === "'") {
|
||||
if (c === '"' || c === "'") {
|
||||
inString = inString === c ? false : c;
|
||||
}
|
||||
chunk.push(c);
|
||||
@@ -272,7 +241,6 @@ less.Parser = function Parser(env) {
|
||||
chunks = [input];
|
||||
}
|
||||
inputLength = input.length;
|
||||
chunk = chunks[0];
|
||||
|
||||
// Start with the primary rule.
|
||||
// The whole syntax tree is held under a Ruleset node,
|
||||
@@ -819,7 +787,7 @@ less.Parser = function Parser(env) {
|
||||
var selectors = [], s, rules, match;
|
||||
save();
|
||||
|
||||
if (match = exec(/^([.#: \w-]+)[\s\n]*\{/)) {
|
||||
if (match = /^([.#: \w-]+)[\s\n]*\{/.exec(chunks[j])) {
|
||||
i += match[0].length - 1;
|
||||
selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])];
|
||||
} else {
|
||||
@@ -845,7 +813,7 @@ less.Parser = function Parser(env) {
|
||||
if (c === '.' || c === '#' || c === '&') { return }
|
||||
|
||||
if (name = $(this.variable) || $(this.property)) {
|
||||
if ((name.charAt(0) != '@') && (match = exec(/^([^@+\/*(;{}-]*);/))) {
|
||||
if ((name.charAt(0) != '@') && (match = /^([^@+\/*(;{}-]*);/.exec(chunks[j]))) {
|
||||
i += match[0].length - 1;
|
||||
value = new(tree.Anonymous)(match[1]);
|
||||
} else if (name === "font") {
|
||||
|
||||
Reference in New Issue
Block a user