function (str, callback, additionalData) {
            var root, line, lines, error = null, globalVars, modifyVars, preText = "";

            i = j = currentPos = furthest = 0;

            globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : '';
            modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : '';

            if (globalVars || (additionalData && additionalData.banner)) {
                preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars;
                parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length;
            }

            str = str.replace(/\r\n/g, '\n');
            // Remove potential UTF Byte Order Mark
            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) { 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));
            }

            current = chunks[0];

            // Start with the primary rule.
            // The whole syntax tree is held under a Ruleset node,
            // with the `root` property set to true, so no `{}` are
            // output. The callback is called when the input is parsed.
            try {
                root = new(tree.Ruleset)(null, this.parsers.primary());
                root.root = true;
                root.firstRoot = true;
            } catch (e) {
                return callback(new(LessError)(e, env));
            }

            root.toCSS = (function (evaluate) {
                return function (options, variables) {
                    options = options || {};
                    var evaldRoot,
                        css,
                        evalEnv = new tree.evalEnv(options);

                    //
                    // Allows setting variables with a hash, so:
                    //
                    //   `{ color: new(tree.Color)('#f01') }` will become:
                    //
                    //   new(tree.Rule)('@color',
                    //     new(tree.Value)([
                    //       new(tree.Expression)([
                    //         new(tree.Color)('#f01')
                    //       ])
                    //     ])
                    //   )
                    //
                    if (typeof(variables) === 'object' && !Array.isArray(variables)) {
                        variables = Object.keys(variables).map(function (k) {
                            var value = variables[k];

                            if (! (value instanceof tree.Value)) {
                                if (! (value instanceof tree.Expression)) {
                                    value = new(tree.Expression)([value]);
                                }
                                value = new(tree.Value)([value]);
                            }
                            return new(tree.Rule)('@' + k, value, false, null, 0);
                        });
                        evalEnv.frames = [new(tree.Ruleset)(null, variables)];
                    }

                    try {
                        var preEvalVisitors = [],
                            visitors = [
                                new(tree.joinSelectorVisitor)(),
                                new(tree.processExtendsVisitor)(),
                                new(tree.toCSSVisitor)({compress: Boolean(options.compress)})
                            ], i, root = this;

                        if (options.plugins) {
                            for(i =0; i < options.plugins.length; i++) {
                                if (options.plugins[i].isPreEvalVisitor) {
                                    preEvalVisitors.push(options.plugins[i]);
                                } else {
                                    if (options.plugins[i].isPreVisitor) {
                                        visitors.splice(0, 0, options.plugins[i]);
                                    } else {
                                        visitors.push(options.plugins[i]);
                                    }
                                }
                            }
                        }

                        for(i = 0; i < preEvalVisitors.length; i++) {
                            preEvalVisitors[i].run(root);
                        }

                        evaldRoot = evaluate.call(root, evalEnv);

                        for(i = 0; i < visitors.length; i++) {
                            visitors[i].run(evaldRoot);
                        }

                        if (options.sourceMap) {
                            evaldRoot = new tree.sourceMapOutput(
                                {
                                    contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars,
                                    writeSourceMap: options.writeSourceMap,
                                    rootNode: evaldRoot,
                                    contentsMap: parser.imports.contents,
                                    sourceMapFilename: options.sourceMapFilename,
                                    sourceMapURL: options.sourceMapURL,
                                    outputFilename: options.sourceMapOutputFilename,
                                    sourceMapBasepath: options.sourceMapBasepath,
                                    sourceMapRootpath: options.sourceMapRootpath,
                                    outputSourceFiles: options.outputSourceFiles,
                                    sourceMapGenerator: options.sourceMapGenerator
                                });
                        }

                        css = evaldRoot.toCSS({
                                compress: Boolean(options.compress),
                                dumpLineNumbers: env.dumpLineNumbers,
                                strictUnits: Boolean(options.strictUnits),
                                numPrecision: 8});
                    } catch (e) {
                        throw new(LessError)(e, env);
                    }

                    if (options.cleancss && less.mode === 'node') {
                        var CleanCSS = require('clean-css'),
                            cleancssOptions = options.cleancssOptions || {};

                        if (cleancssOptions.keepSpecialComments === undefined) {
                            cleancssOptions.keepSpecialComments = "*";
                        }
                        cleancssOptions.processImport = false;
                        cleancssOptions.noRebase = true;
                        if (cleancssOptions.noAdvanced === undefined) {
                            cleancssOptions.noAdvanced = true;
                        }

                        return new CleanCSS(cleancssOptions).minify(css);
                    } else if (options.compress) {
                        return css.replace(/(^(\s)+)|((\s)+$)/g, "");
                    } else {
                        return css;
                    }
                };
            })(root.eval);

            // If `i` is smaller than the `input.length - 1`,
            // it means the parser wasn't able to parse the whole
            // string, so we've got a parsing error.
            //
            // We try to extract a \n delimited string,
            // showing the line where the parse error occured.
            // We split it up into two parts (the part which parsed,
            // and the part which didn't), so we can color them differently.
            if (i < input.length - 1) {
                i = furthest;
                var loc = getLocation(i, input);
                lines = input.split('\n');
                line = loc.line + 1;

                error = {
                    type: "Parse",
                    message: "Unrecognised input",
                    index: i,
                    filename: env.currentFileInfo.filename,
                    line: line,
                    column: loc.column,
                    extract: [
                        lines[line - 2],
                        lines[line - 1],
                        lines[line]
                    ]
                };
            }

            var finish = function (e) {
                e = error || e || parser.imports.error;

                if (e) {
                    if (!(e instanceof LessError)) {
                        e = new(LessError)(e, env);
                    }

                    return callback(e);
                }
                else {
                    return callback(null, root);
                }
            };

            if (env.processImports !== false) {
                new tree.importVisitor(this.imports, finish)
                    .run(root);
            } else {
                return finish();
            }
        }
function (str, callback, additionalData) {
            var root, line, lines, error = null, globalVars, modifyVars, preText = "";

            i = j = currentPos = furthest = 0;

            globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : '';
            modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : '';

            if (globalVars || (additionalData && additionalData.banner)) {
                preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars;
                parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length;
            }

            str = str.replace(/\r\n/g, '\n');
            // Remove potential UTF Byte Order Mark
            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) { 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));
            }

            current = chunks[0];

            // Start with the primary rule.
            // The whole syntax tree is held under a Ruleset node,
            // with the `root` property set to true, so no `{}` are
            // output. The callback is called when the input is parsed.
            try {
                root = new(tree.Ruleset)(null, this.parsers.primary());
                root.root = true;
                root.firstRoot = true;
            } catch (e) {
                return callback(new(LessError)(e, env));
            }

            root.toCSS = (function (evaluate) {
                return function (options, variables) {
                    options = options || {};
                    var evaldRoot,
                        css,
                        evalEnv = new tree.evalEnv(options);

                    //
                    // Allows setting variables with a hash, so:
                    //
                    //   `{ color: new(tree.Color)('#f01') }` will become:
                    //
                    //   new(tree.Rule)('@color',
                    //     new(tree.Value)([
                    //       new(tree.Expression)([
                    //         new(tree.Color)('#f01')
                    //       ])
                    //     ])
                    //   )
                    //
                    if (typeof(variables) === 'object' && !Array.isArray(variables)) {
                        variables = Object.keys(variables).map(function (k) {
                            var value = variables[k];

                            if (! (value instanceof tree.Value)) {
                                if (! (value instanceof tree.Expression)) {
                                    value = new(tree.Expression)([value]);
                                }
                                value = new(tree.Value)([value]);
                            }
                            return new(tree.Rule)('@' + k, value, false, null, 0);
                        });
                        evalEnv.frames = [new(tree.Ruleset)(null, variables)];
                    }

                    try {
                        var preEvalVisitors = [],
                            visitors = [
                                new(tree.joinSelectorVisitor)(),
                                new(tree.processExtendsVisitor)(),
                                new(tree.toCSSVisitor)({compress: Boolean(options.compress)})
                            ], i, root = this;

                        if (options.plugins) {
                            for(i =0; i < options.plugins.length; i++) {
                                if (options.plugins[i].isPreEvalVisitor) {
                                    preEvalVisitors.push(options.plugins[i]);
                                } else {
                                    if (options.plugins[i].isPreVisitor) {
                                        visitors.splice(0, 0, options.plugins[i]);
                                    } else {
                                        visitors.push(options.plugins[i]);
                                    }
                                }
                            }
                        }

                        for(i = 0; i < preEvalVisitors.length; i++) {
                            preEvalVisitors[i].run(root);
                        }

                        evaldRoot = evaluate.call(root, evalEnv);

                        for(i = 0; i < visitors.length; i++) {
                            visitors[i].run(evaldRoot);
                        }

                        if (options.sourceMap) {
                            evaldRoot = new tree.sourceMapOutput(
                                {
                                    contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars,
                                    writeSourceMap: options.writeSourceMap,
                                    rootNode: evaldRoot,
                                    contentsMap: parser.imports.contents,
                                    sourceMapFilename: options.sourceMapFilename,
                                    sourceMapURL: options.sourceMapURL,
                                    outputFilename: options.sourceMapOutputFilename,
                                    sourceMapBasepath: options.sourceMapBasepath,
                                    sourceMapRootpath: options.sourceMapRootpath,
                                    outputSourceFiles: options.outputSourceFiles,
                                    sourceMapGenerator: options.sourceMapGenerator
                                });
                        }

                        css = evaldRoot.toCSS({
                                compress: Boolean(options.compress),
                                dumpLineNumbers: env.dumpLineNumbers,
                                strictUnits: Boolean(options.strictUnits),
                                numPrecision: 8});
                    } catch (e) {
                        throw new(LessError)(e, env);
                    }

                    if (options.cleancss && less.mode === 'node') {
                        var CleanCSS = require('clean-css'),
                            cleancssOptions = options.cleancssOptions || {};

                        if (cleancssOptions.keepSpecialComments === undefined) {
                            cleancssOptions.keepSpecialComments = "*";
                        }
                        cleancssOptions.processImport = false;
                        cleancssOptions.noRebase = true;
                        if (cleancssOptions.noAdvanced === undefined) {
                            cleancssOptions.noAdvanced = true;
                        }

                        return new CleanCSS(cleancssOptions).minify(css);
                    } else if (options.compress) {
                        return css.replace(/(^(\s)+)|((\s)+$)/g, "");
                    } else {
                        return css;
                    }
                };
            })(root.eval);

            // If `i` is smaller than the `input.length - 1`,
            // it means the parser wasn't able to parse the whole
            // string, so we've got a parsing error.
            //
            // We try to extract a \n delimited string,
            // showing the line where the parse error occured.
            // We split it up into two parts (the part which parsed,
            // and the part which didn't), so we can color them differently.
            if (i < input.length - 1) {
                i = furthest;
                var loc = getLocation(i, input);
                lines = input.split('\n');
                line = loc.line + 1;

                error = {
                    type: "Parse",
                    message: "Unrecognised input",
                    index: i,
                    filename: env.currentFileInfo.filename,
                    line: line,
                    column: loc.column,
                    extract: [
                        lines[line - 2],
                        lines[line - 1],
                        lines[line]
                    ]
                };
            }

            var finish = function (e) {
                e = error || e || parser.imports.error;

                if (e) {
                    if (!(e instanceof LessError)) {
                        e = new(LessError)(e, env);
                    }

                    return callback(e);
                }
                else {
                    return callback(null, root);
                }
            };

            if (env.processImports !== false) {
                new tree.importVisitor(this.imports, finish)
                    .run(root);
            } else {
                return finish();
            }
        }
function ( str , callback , additionalData ) { var root , line , lines , error = null , globalVars , modifyVars , preText = "" ; i = j = currentPos = furthest = 0 ; globalVars = ( additionalData && additionalData . globalVars ) ? less . Parser . serializeVars ( additionalData . globalVars ) + '\n' : '' ; modifyVars = ( additionalData && additionalData . modifyVars ) ? '\n' + less . Parser . serializeVars ( additionalData . modifyVars ) : '' ; if ( globalVars || ( additionalData && additionalData . banner ) ) { preText = ( ( additionalData && additionalData . banner ) ? additionalData . banner : "" ) + globalVars ; parser . imports . contentsIgnoredChars [ env . currentFileInfo . filename ] = preText . length ; } str = str . replace ( / \r\n / g , '\n' ) ; // Remove potential UTF Byte Order Mark 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 ) { 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 ) ) ; } current = chunks [ 0 ] ; // Start with the primary rule. // The whole syntax tree is held under a Ruleset node, // with the `root` property set to true, so no `{}` are // output. The callback is called when the input is parsed. try { root = new ( tree . Ruleset ) ( null , this . parsers . primary ( ) ) ; root . root = true ; root . firstRoot = true ; } catch ( e ) { return callback ( new ( LessError ) ( e , env ) ) ; } root . toCSS = ( function ( evaluate ) { return function ( options , variables ) { options = options || { } ; var evaldRoot , css , evalEnv = new tree . evalEnv ( options ) ; // // Allows setting variables with a hash, so: // //   `{ color: new(tree.Color)('#f01') }` will become: // //   new(tree.Rule)('@color', //     new(tree.Value)([ //       new(tree.Expression)([ //         new(tree.Color)('#f01') //       ]) //     ]) //   ) // if ( typeof ( variables ) === 'object' && ! Array . isArray ( variables ) ) { variables = Object . keys ( variables ) . map ( function ( k ) { var value = variables [ k ] ; if ( ! ( value instanceof tree . Value ) ) { if ( ! ( value instanceof tree . Expression ) ) { value = new ( tree . Expression ) ( [ value ] ) ; } value = new ( tree . Value ) ( [ value ] ) ; } return new ( tree . Rule ) ( '@' + k , value , false , null , 0 ) ; } ) ; evalEnv . frames = [ new ( tree . Ruleset ) ( null , variables ) ] ; } try { var preEvalVisitors = [ ] , visitors = [ new ( tree . joinSelectorVisitor ) ( ) , new ( tree . processExtendsVisitor ) ( ) , new ( tree . toCSSVisitor ) ( { compress : Boolean ( options . compress ) } ) ] , i , root = this ; if ( options . plugins ) { for ( i = 0 ; i < options . plugins . length ; i ++ ) { if ( options . plugins [ i ] . isPreEvalVisitor ) { preEvalVisitors . push ( options . plugins [ i ] ) ; } else { if ( options . plugins [ i ] . isPreVisitor ) { visitors . splice ( 0 , 0 , options . plugins [ i ] ) ; } else { visitors . push ( options . plugins [ i ] ) ; } } } } for ( i = 0 ; i < preEvalVisitors . length ; i ++ ) { preEvalVisitors [ i ] . run ( root ) ; } evaldRoot = evaluate . call ( root , evalEnv ) ; for ( i = 0 ; i < visitors . length ; i ++ ) { visitors [ i ] . run ( evaldRoot ) ; } if ( options . sourceMap ) { evaldRoot = new tree . sourceMapOutput ( { contentsIgnoredCharsMap : parser . imports . contentsIgnoredChars , writeSourceMap : options . writeSourceMap , rootNode : evaldRoot , contentsMap : parser . imports . contents , sourceMapFilename : options . sourceMapFilename , sourceMapURL : options . sourceMapURL , outputFilename : options . sourceMapOutputFilename , sourceMapBasepath : options . sourceMapBasepath , sourceMapRootpath : options . sourceMapRootpath , outputSourceFiles : options . outputSourceFiles , sourceMapGenerator : options . sourceMapGenerator } ) ; } css = evaldRoot . toCSS ( { compress : Boolean ( options . compress ) , dumpLineNumbers : env . dumpLineNumbers , strictUnits : Boolean ( options . strictUnits ) , numPrecision : 8 } ) ; } catch ( e ) { throw new ( LessError ) ( e , env ) ; } if ( options . cleancss && less . mode === 'node' ) { var CleanCSS = require ( 'clean-css' ) , cleancssOptions = options . cleancssOptions || { } ; if ( cleancssOptions . keepSpecialComments === undefined ) { cleancssOptions . keepSpecialComments = "*" ; } cleancssOptions . processImport = false ; cleancssOptions . noRebase = true ; if ( cleancssOptions . noAdvanced === undefined ) { cleancssOptions . noAdvanced = true ; } return new CleanCSS ( cleancssOptions ) . minify ( css ) ; } else if ( options . compress ) { return css . replace ( / (^(\s)+)|((\s)+$) / g , "" ) ; } else { return css ; } } ; } ) ( root . eval ) ; // If `i` is smaller than the `input.length - 1`, // it means the parser wasn't able to parse the whole // string, so we've got a parsing error. // // We try to extract a \n delimited string, // showing the line where the parse error occured. // We split it up into two parts (the part which parsed, // and the part which didn't), so we can color them differently. if ( i < input . length - 1 ) { i = furthest ; var loc = getLocation ( i , input ) ; lines = input . split ( '\n' ) ; line = loc . line + 1 ; error = { type : "Parse" , message : "Unrecognised input" , index : i , filename : env . currentFileInfo . filename , line : line , column : loc . column , extract : [ lines [ line - 2 ] , lines [ line - 1 ] , lines [ line ] ] } ; } var finish = function ( e ) { e = error || e || parser . imports . error ; if ( e ) { if ( ! ( e instanceof LessError ) ) { e = new ( LessError ) ( e , env ) ; } return callback ( e ) ; } else { return callback ( null , root ) ; } } ; if ( env . processImports !== false ) { new tree . importVisitor ( this . imports , finish ) . run ( root ) ; } else { return finish ( ) ; } }
Parse an input string into an abstract syntax tree, @param str A string containing 'less' markup @param callback call `callback` when done. @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply
Parse an input string into an abstract syntax tree
Parse a string into chunks of less. js