slice parsing

This commit is contained in:
cloudhead 2010-06-25 03:03:37 -04:00
parent 89a5a03ca0
commit 843aeffa41

View File

@ -50,6 +50,9 @@ less.Parser = function Parser(env) {
var input, // LeSS input string
i, // current index in `input`
j, // current chunk
temp,
memo,
chunk,
furthest, // furthest index the parser has gone to
chunks, // chunkified input
current, // index of current chunk, in `input`
@ -84,11 +87,27 @@ less.Parser = function Parser(env) {
}
};
function save(){
temp = chunk;
memo = i;
current = i;
}
function restore() {
chunks[j] = chunk = temp;
i = memo;
current = i;
}
function sync() {
if (i > current) {
chunks[j] = chunk = chunk.slice(i - current);
current = i;
}
}
//
// Parse from a token, regexp or string, and move forward if match
//
function $(tok) {
var match, args, length, c, index, endIndex;
var match, args, length, c, index, endIndex, k;
//
// Non-terminal
@ -104,6 +123,7 @@ less.Parser = function Parser(env) {
} else if (typeof(tok) === 'string') {
match = input.charAt(i) === tok ? tok : null;
length = 1;
sync ();
// 1. We move to the next chunk, if necessary.
// 2. Set the `lastIndex` to be relative
@ -114,14 +134,11 @@ less.Parser = function Parser(env) {
// index, which we stored in [2].
//
} else {
if (i >= current + chunks[j].length &&
j < chunks.length - 1) { // 1.
current += chunks[j++].length;
}
tok.lastIndex = index = i - current; // 2.
match = tok.exec(chunks[j]);
sync ();
if (match && (match.index === index)) { // 3.
match = tok.exec(chunk);
if (match) { // 3.
length = match[0].length;
} else { return }
}
@ -133,13 +150,18 @@ less.Parser = function Parser(env) {
//
if (match) {
i += length;
endIndex = current + chunks[j].length;
var mem = i;
endIndex = i + chunk.length - length;
while (i <= endIndex) {
while (i < endIndex) {
c = input.charCodeAt(i);
if (! (c === 32 || c === 10 || c === 9)) { break }
i++;
}
chunks[j] = chunk = chunk.slice(length + (i - mem));
current = i;
if (chunk.length === 0 && j < chunks.length - 1) { chunk = chunks[++j] }
if(typeof(match) === 'string') {
return match;
@ -152,21 +174,25 @@ less.Parser = function Parser(env) {
// Same as $(), but don't change the state of the parser,
// just return the match.
function peek(tok) {
var match, index;
if (typeof(tok) === 'string') {
return input.charAt(i) === tok;
} else {
index = i - current;
tok.lastIndex = index;
if ((match = tok.exec(chunks[j])) &&
(match.index === index)) {
return match;
if (tok.test(chunk)) {
return true;
} else {
return false;
}
}
}
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,
@ -246,6 +272,7 @@ 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,
@ -387,7 +414,7 @@ less.Parser = function Parser(env) {
while (node = $(this.mixin.definition) || $(this.rule) || $(this.ruleset) ||
$(this.mixin.call) || $(this.comment) ||
$(/[\n\s]+/g) || $(this.directive)) {
$(/^[\n\s]+/) || $(this.directive)) {
root.push(node);
}
return root;
@ -402,8 +429,8 @@ less.Parser = function Parser(env) {
if (input.charAt(i) !== '/') return;
if (input.charAt(i + 1) === '/') {
return new(tree.Comment)($(/\/\/.*/g), true);
} else if (comment = $(/\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/g)) {
return new(tree.Comment)($(/^\/\/.*/), true);
} else if (comment = $(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/)) {
return new(tree.Comment)(comment);
}
},
@ -421,7 +448,7 @@ less.Parser = function Parser(env) {
var str;
if (input.charAt(i) !== '"' && input.charAt(i) !== "'") return;
if (str = $(/"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/g)) {
if (str = $(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/)) {
return new(tree.Quoted)(str[0], str[1] || str[2]);
}
},
@ -433,7 +460,7 @@ less.Parser = function Parser(env) {
//
keyword: function () {
var k;
if (k = $(/[A-Za-z-]+/g)) { return new(tree.Keyword)(k) }
if (k = $(/^[A-Za-z-]+/)) { return new(tree.Keyword)(k) }
},
//
@ -449,7 +476,7 @@ less.Parser = function Parser(env) {
call: function () {
var name, args;
if (! (name = $(/([\w-]+|%)\(/g))) return;
if (! (name = $(/^([\w-]+|%)\(/))) return;
if (name[1].toLowerCase() === 'alpha') { return $(this.alpha) }
@ -484,8 +511,8 @@ less.Parser = function Parser(env) {
url: function () {
var value;
if (input.charAt(i) !== 'u' || !$(/url\(/g)) return;
value = $(this.entities.quoted) || $(/[-\w%@$\/.&=:;#+?]+/g);
if (input.charAt(i) !== 'u' || !$(/^url\(/)) return;
value = $(this.entities.quoted) || $(/^[-\w%@$\/.&=:;#+?]+/);
if (! $(')')) throw new(Error)("missing closing ) for url()");
return new(tree.URL)(value.value ? value : new(tree.Anonymous)(value));
@ -502,7 +529,7 @@ less.Parser = function Parser(env) {
variable: function () {
var name, index = i;
if (input.charAt(i) === '@' && (name = $(/@[\w-]+/g))) {
if (input.charAt(i) === '@' && (name = $(/^@[\w-]+/))) {
return new(tree.Variable)(name, index);
}
},
@ -517,7 +544,7 @@ less.Parser = function Parser(env) {
color: function () {
var rgb;
if (input.charAt(i) === '#' && (rgb = $(/#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/g))) {
if (input.charAt(i) === '#' && (rgb = $(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/))) {
return new(tree.Color)(rgb[1]);
}
},
@ -531,7 +558,7 @@ less.Parser = function Parser(env) {
var value, c = input.charCodeAt(i);
if ((c > 57 || c < 45) || c === 47) return;
if (value = $(/(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm)?/g)) {
if (value = $(/^(-?\d*\.?\d+)(px|%|em|pc|ex|in|deg|s|ms|pt|cm|mm)?/)) {
return new(tree.Dimension)(value[1], value[2]);
}
}
@ -545,7 +572,7 @@ less.Parser = function Parser(env) {
variable: function () {
var name;
if (input.charAt(i) === '@' && (name = $(/(@[\w-]+)\s*:/g))) { return name[1] }
if (input.charAt(i) === '@' && (name = $(/^(@[\w-]+)\s*:/))) { return name[1] }
},
//
@ -558,7 +585,7 @@ less.Parser = function Parser(env) {
shorthand: function () {
var a, b;
if (! peek(/[@\w.-]+\/[@\w.-]+/g)) return;
if (! peek(/^[@\w.-]+\/[@\w.-]+/)) return;
if ((a = $(this.entity)) && $('/') && (b = $(this.entity))) {
return new(tree.Shorthand)(a, b);
@ -585,7 +612,7 @@ less.Parser = function Parser(env) {
if (s !== '.' && s !== '#') { return }
while (e = $(/[#.][\w-]+/g)) {
while (e = $(/^[#.][\w-]+/)) {
elements.push(new(tree.Element)(c, e));
c = $('>');
}
@ -618,12 +645,13 @@ less.Parser = function Parser(env) {
definition: function () {
var name, params = [], match, ruleset, param, value;
if (input.charAt(i) !== '.' || peek(/[^{]*(;|})/g)) return;
if (input.charAt(i) !== '.' || peek(/^[^{]*(;|})/)) return;
if (match = $(/([#.][\w-]+)\s*\(/g)) {
if (match = $(/^([#.][\w-]+)\s*\(/)) {
name = match[1];
while (param = $(/@[\w-]+/g) || $(this.entities.literal)
while (param = $(/^@[\w-]+/) || $(this.entities.literal)
|| $(this.entities.keyword)) {
// Variable
if (param[0] === '@') {
@ -678,8 +706,8 @@ less.Parser = function Parser(env) {
alpha: function () {
var value;
if (! $(/opacity=/gi)) return;
if (value = $(/\d+/g) || $(this.entities.variable)) {
if (! $(/^opacity=/i)) return;
if (value = $(/^\d+/) || $(this.entities.variable)) {
if (! $(')')) throw new(Error)("missing closing ) for alpha()");
return new(tree.Alpha)(value);
}
@ -701,7 +729,7 @@ less.Parser = function Parser(env) {
var e, t;
c = $(this.combinator);
e = $(/[.#:]?[\w-]+/g) || $('*') || $(this.attribute) || $(/\([^)@]+\)/g);
e = $(/^[.#:]?[\w-]+/) || $('*') || $(this.attribute) || $(/^\([^)@]+\)/);
if (e) { return new(tree.Element)(c, e) }
},
@ -753,16 +781,16 @@ less.Parser = function Parser(env) {
if (elements.length > 0) { return new(tree.Selector)(elements) }
},
tag: function () {
return $(/[a-zA-Z][a-zA-Z-]*[0-9]?/g) || $('*');
return $(/^[a-zA-Z][a-zA-Z-]*[0-9]?/) || $('*');
},
attribute: function () {
var attr = '', key, val, op;
if (! $('[')) return;
if (key = $(/[a-z-]+/g) || $(this.entities.quoted)) {
if ((op = $(/[|~*$^]?=/g)) &&
(val = $(this.entities.quoted) || $(/[\w-]+/g))) {
if (key = $(/^[a-z-]+/) || $(this.entities.quoted)) {
if ((op = $(/^[|~*$^]?=/)) &&
(val = $(this.entities.quoted) || $(/^[\w-]+/))) {
attr = [key, op, val.toCSS ? val.toCSS() : val].join('');
} else { attr = key }
}
@ -788,9 +816,10 @@ less.Parser = function Parser(env) {
// div, .class, body > p {...}
//
ruleset: function () {
var selectors = [], s, rules, match, memo = i;
var selectors = [], s, rules, match;
save();
if (match = peek(/([.#: \w-]+)[\s\n]*\{/g)) {
if (match = exec(/^([.#: \w-]+)[\s\n]*\{/)) {
i += match[0].length - 1;
selectors = [new(tree.Selector)([new(tree.Element)(null, match[1])])];
} else {
@ -806,17 +835,17 @@ less.Parser = function Parser(env) {
} else {
// Backtrack
furthest = i;
i = memo;
restore();
}
},
rule: function () {
var value, c = input.charAt(i);
var memo = i;
save();
if (c === '.' || c === '#' || c === '&') { return }
if (name = $(this.variable) || $(this.property)) {
if ((name.charAt(0) != '@') && (match = peek(/([^@+\/*(;{}-]*);/g))) {
if ((name.charAt(0) != '@') && (match = exec(/^([^@+\/*(;{}-]*);/))) {
i += match[0].length - 1;
value = new(tree.Anonymous)(match[1]);
} else if (name === "font") {
@ -829,7 +858,7 @@ less.Parser = function Parser(env) {
return new(tree.Rule)(name, value, memo);
} else {
furthest = i;
i = memo;
restore();
}
}
},
@ -846,7 +875,7 @@ less.Parser = function Parser(env) {
//
"import": function () {
var path;
if ($(/@import\s+/g) &&
if ($(/^@import\s+/) &&
(path = $(this.entities.quoted) || $(this.entities.url)) &&
$(';')) {
return new(tree.Import)(path, imports);
@ -865,12 +894,12 @@ less.Parser = function Parser(env) {
if (value = $(this['import'])) {
return value;
} else if (name = $(/@media|@page/g)) {
types = $(/[^{]+/g).trim();
} else if (name = $(/^@media|@page/)) {
types = $(/^[^{]+/).trim();
if (rules = $(this.block)) {
return new(tree.Directive)(name + " " + types, rules);
}
} else if (name = $(/@[-a-z]+/g)) {
} else if (name = $(/^@[-a-z]+/)) {
if (name === '@font-face') {
if (rules = $(this.block)) {
return new(tree.Directive)(name, rules);
@ -920,7 +949,7 @@ less.Parser = function Parser(env) {
},
important: function () {
if (input.charAt(i) === '!') {
return $(/!\s*important/g);
return $(/^!\s*important/);
}
},
sub: function () {
@ -942,7 +971,7 @@ less.Parser = function Parser(env) {
addition: function () {
var m, a, op, operation;
if (m = $(this.multiplication)) {
while ((op = $(/[-+]\s+/g) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) &&
while ((op = $(/^[-+]\s+/) || (input.charAt(i - 1) != ' ' && ($('+') || $('-')))) &&
(a = $(this.multiplication))) {
operation = new(tree.Operation)(op, [operation || m, a]);
}
@ -979,7 +1008,7 @@ less.Parser = function Parser(env) {
property: function () {
var name;
if (name = $(/(\*?-?[-a-z_0-9]+)\s*:/g)) {
if (name = $(/^(\*?-?[-a-z_0-9]+)\s*:/)) {
return name[1];
}
}