// http://www.w3.org/TR/CSS21/grammar.html
// https://github.com/visionmedia/css-parse/pull/49#issuecomment-30088027
var commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g;
export function parse(css, options) {
    options = options || {};
    /**
     * Positional.
     */
    var lineno = 1;
    var column = 1;
    /**
     * Update lineno and column based on `str`.
     */
    function updatePosition(str) {
        var lines = str.match(/\n/g);
        if (lines)
            lineno += lines.length;
        var i = str.lastIndexOf('\n');
        column = ~i ? str.length - i : column + str.length;
    }
    /**
     * Mark position and patch `node.position`.
     */
    function position() {
        var start = { line: lineno, column: column };
        return function (node) {
            node.position = new Position(start);
            whitespace();
            return node;
        };
    }
    /**
     * Store position information for a node
     */
    function Position(start) {
        this.start = start;
        this.end = { line: lineno, column: column };
        this.source = options.source;
    }
    /**
     * Non-enumerable source string
     */
    Position.prototype.content = css;
    /**
     * Error `msg`.
     */
    var errorsList = [];
    function error(msg) {
        var err = new Error(options.source + ':' + lineno + ':' + column + ': ' + msg);
        err.reason = msg;
        err.filename = options.source;
        err.line = lineno;
        err.column = column;
        err.source = css;
        if (options.silent) {
            errorsList.push(err);
        }
        else {
            throw err;
        }
    }
    /**
     * Parse stylesheet.
     */
    function stylesheet() {
        var rulesList = rules();
        return {
            type: 'stylesheet',
            stylesheet: {
                rules: rulesList,
                parsingErrors: errorsList,
            },
        };
    }
    /**
     * Opening brace.
     */
    function open() {
        return match(/^{\s*/);
    }
    /**
     * Closing brace.
     */
    function close() {
        return match(/^}/);
    }
    /**
     * Parse ruleset.
     */
    function rules() {
        var node;
        var rules = [];
        whitespace();
        comments(rules);
        while (css.length && css.charAt(0) != '}' && (node = atrule() || rule())) {
            if (node !== false) {
                rules.push(node);
                comments(rules);
            }
        }
        return rules;
    }
    /**
     * Match `re` and return captures.
     */
    function match(re) {
        var m = re.exec(css);
        if (!m)
            return;
        var str = m[0];
        updatePosition(str);
        css = css.slice(str.length);
        return m;
    }
    /**
     * Parse whitespace.
     */
    function whitespace() {
        match(/^\s*/);
    }
    /**
     * Parse comments;
     */
    function comments(rules) {
        var c;
        rules = rules || [];
        while ((c = comment())) {
            if (c !== false) {
                rules.push(c);
            }
        }
        return rules;
    }
    /**
     * Parse comment.
     */
    function comment() {
        var pos = position();
        if ('/' != css.charAt(0) || '*' != css.charAt(1))
            return;
        var i = 2;
        while ('' != css.charAt(i) && ('*' != css.charAt(i) || '/' != css.charAt(i + 1)))
            ++i;
        i += 2;
        if ('' === css.charAt(i - 1)) {
            return error('End of comment missing');
        }
        var str = css.slice(2, i - 2);
        column += 2;
        updatePosition(str);
        css = css.slice(i);
        column += 2;
        return pos({
            type: 'comment',
            comment: str,
        });
    }
    /**
     * Parse selector.
     */
    function selector() {
        var m = match(/^([^{]+)/);
        if (!m)
            return;
        /* @fix Remove all comments from selectors
         * http://ostermiller.org/findcomment.html */
        return trim(m[0])
            .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '')
            .replace(/"(?:\\"|[^"])*"|'(?:\\'|[^'])*'/g, function (m) {
            return m.replace(/,/g, '\u200C');
        })
            .split(/\s*(?![^(]*\)),\s*/)
            .map(function (s) {
            return s.replace(/\u200C/g, ',');
        });
    }
    /**
     * Parse declaration.
     */
    function declaration() {
        var pos = position();
        // prop
        var prop = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/);
        if (!prop)
            return;
        prop = trim(prop[0]);
        // :
        if (!match(/^:\s*/))
            return error("property missing ':'");
        // val
        var val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/);
        var ret = pos({
            type: 'declaration',
            property: prop.replace(commentre, ''),
            value: val ? trim(val[0]).replace(commentre, '') : '',
        });
        // ;
        match(/^[;\s]*/);
        return ret;
    }
    /**
     * Parse declarations.
     */
    function declarations() {
        var decls = [];
        if (!open())
            return error("missing '{'");
        comments(decls);
        // declarations
        var decl;
        while ((decl = declaration())) {
            if (decl !== false) {
                decls.push(decl);
                comments(decls);
            }
        }
        if (!close())
            return error("missing '}'");
        return decls;
    }
    /**
     * Parse keyframe.
     */
    function keyframe() {
        var m;
        var vals = [];
        var pos = position();
        while ((m = match(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/))) {
            vals.push(m[1]);
            match(/^,\s*/);
        }
        if (!vals.length)
            return;
        return pos({
            type: 'keyframe',
            values: vals,
            declarations: declarations(),
        });
    }
    /**
     * Parse keyframes.
     */
    function atkeyframes() {
        var pos = position();
        var m = match(/^@([-\w]+)?keyframes\s*/);
        if (!m)
            return;
        var vendor = m[1];
        // identifier
        var m = match(/^([-\w]+)\s*/);
        if (!m)
            return error('@keyframes missing name');
        var name = m[1];
        if (!open())
            return error("@keyframes missing '{'");
        var frame;
        var frames = comments();
        while ((frame = keyframe())) {
            frames.push(frame);
            frames = frames.concat(comments());
        }
        if (!close())
            return error("@keyframes missing '}'");
        return pos({
            type: 'keyframes',
            name: name,
            vendor: vendor,
            keyframes: frames,
        });
    }
    /**
     * Parse supports.
     */
    function atsupports() {
        var pos = position();
        var m = match(/^@supports *([^{]+)/);
        if (!m)
            return;
        var supports = trim(m[1]);
        if (!open())
            return error("@supports missing '{'");
        var style = comments().concat(rules());
        if (!close())
            return error("@supports missing '}'");
        return pos({
            type: 'supports',
            supports: supports,
            rules: style,
        });
    }
    /**
     * Parse host.
     */
    function athost() {
        var pos = position();
        var m = match(/^@host\s*/);
        if (!m)
            return;
        if (!open())
            return error("@host missing '{'");
        var style = comments().concat(rules());
        if (!close())
            return error("@host missing '}'");
        return pos({
            type: 'host',
            rules: style,
        });
    }
    /**
     * Parse media.
     */
    function atmedia() {
        var pos = position();
        var m = match(/^@media *([^{]+)/);
        if (!m)
            return;
        var media = trim(m[1]);
        if (!open())
            return error("@media missing '{'");
        var style = comments().concat(rules());
        if (!close())
            return error("@media missing '}'");
        return pos({
            type: 'media',
            media: media,
            rules: style,
        });
    }
    /**
     * Parse custom-media.
     */
    function atcustommedia() {
        var pos = position();
        var m = match(/^@custom-media\s+(--[^\s]+)\s*([^{;]+);/);
        if (!m)
            return;
        return pos({
            type: 'custom-media',
            name: trim(m[1]),
            media: trim(m[2]),
        });
    }
    /**
     * Parse paged media.
     */
    function atpage() {
        var pos = position();
        var m = match(/^@page */);
        if (!m)
            return;
        var sel = selector() || [];
        if (!open())
            return error("@page missing '{'");
        var decls = comments();
        // declarations
        var decl;
        while ((decl = declaration())) {
            decls.push(decl);
            decls = decls.concat(comments());
        }
        if (!close())
            return error("@page missing '}'");
        return pos({
            type: 'page',
            selectors: sel,
            declarations: decls,
        });
    }
    /**
     * Parse document.
     */
    function atdocument() {
        var pos = position();
        var m = match(/^@([-\w]+)?document *([^{]+)/);
        if (!m)
            return;
        var vendor = trim(m[1]);
        var doc = trim(m[2]);
        if (!open())
            return error("@document missing '{'");
        var style = comments().concat(rules());
        if (!close())
            return error("@document missing '}'");
        return pos({
            type: 'document',
            document: doc,
            vendor: vendor,
            rules: style,
        });
    }
    /**
     * Parse font-face.
     */
    function atfontface() {
        var pos = position();
        var m = match(/^@font-face\s*/);
        if (!m)
            return;
        if (!open())
            return error("@font-face missing '{'");
        var decls = comments();
        // declarations
        var decl;
        while ((decl = declaration())) {
            decls.push(decl);
            decls = decls.concat(comments());
        }
        if (!close())
            return error("@font-face missing '}'");
        return pos({
            type: 'font-face',
            declarations: decls,
        });
    }
    /**
     * Parse import
     */
    var atimport = _compileAtrule('import');
    /**
     * Parse charset
     */
    var atcharset = _compileAtrule('charset');
    /**
     * Parse namespace
     */
    var atnamespace = _compileAtrule('namespace');
    /**
     * Parse non-block at-rules
     */
    function _compileAtrule(name) {
        var re = new RegExp('^@' + name + '\\s*([^;]+);');
        return function () {
            var pos = position();
            var m = match(re);
            if (!m)
                return;
            var ret = { type: name };
            ret[name] = m[1].trim();
            return pos(ret);
        };
    }
    /**
     * Parse at rule.
     */
    function atrule() {
        if (css[0] != '@')
            return;
        return atkeyframes() || atmedia() || atcustommedia() || atsupports() || atimport() || atcharset() || atnamespace() || atdocument() || atpage() || athost() || atfontface();
    }
    /**
     * Parse rule.
     */
    function rule() {
        var pos = position();
        var sel = selector();
        if (!sel)
            return error('selector missing');
        comments();
        return pos({
            type: 'rule',
            selectors: sel,
            declarations: declarations(),
        });
    }
    return addParent(stylesheet());
}
/**
 * Trim `str`.
 */
function trim(str) {
    return str ? str.replace(/^\s+|\s+$/g, '') : '';
}
/**
 * Adds non-enumerable parent node reference to each node.
 */
function addParent(obj, parent) {
    var isNode = obj && typeof obj.type === 'string';
    var childParent = isNode ? obj : parent;
    for (var k in obj) {
        var value = obj[k];
        if (Array.isArray(value)) {
            value.forEach(function (v) {
                addParent(v, childParent);
            });
        }
        else if (value && typeof value === 'object') {
            addParent(value, childParent);
        }
    }
    if (isNode) {
        Object.defineProperty(obj, 'parent', {
            configurable: true,
            writable: true,
            enumerable: false,
            value: parent || null,
        });
    }
    return obj;
}
//# sourceMappingURL=index.js.map