Regexp vs Parser - Phase 3

JavaScript performance comparison

Test case created by Jamie Hill

Info

More tweaks.

Preparation code

 
<script>
Benchmark.prototype.setup = function() {
    var Rule,
        openRe = /((([\*\@\#\.\w\d])([\s\*\@\#\.\w\d\-\,\>\:\=\"\~\^\$\(\)\+]*))\{)\s*/,
        closeRe = /\s*\}/;
   
    Rule = (function() {
      function Rule(source, selector, start, declarationStart, end, declarationEnd) {
        this.source = source;
        this.selector = selector;
        this.start = start;
        this.declarationStart = declarationStart;
        this.end = end || null;
        this.declarationEnd = declarationEnd || null;
        this.nested = [];
      }
   
      return Rule;
    }());
};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Real Parser
function parser(css) {
  var stack = [], rules = [], context = rules, state = 'before-selector',
      buffer = 0, index = 0, rule, char, start;

  this.cssText = css;
  this.rules = rules;

  while (char = css.charAt(index++)) {          
    switch(char) {
    case ' ': case '\t': case '\r': case '\n': case '\f':
      if (state === 'selector') { buffer++; }
      break;
    case '{':
      if (state !== 'selector') { break; }
      start = index - buffer - 1;
      rule = new Rule(css, css.slice(start, index - 1).trim(), start, index);
      context.push(rule);
      stack.push(context);
      context = rule.nested;
      state = 'before-selector'
      buffer = 0;
      break;
    case ';':
      if (state !== 'selector') { break; }
      state = 'before-selector';
      buffer = 0;
      break;
    case '}':
      if (state !== 'before-selector') { break; }
      context = stack.pop();
      rule = context[context.length - 1];
      if (rule) {
        rule.end = index;
        rule.declarationEnd = index - 1;
      } else {
        console.log('Closing unopened rule');
      }
      state = "before-selector";
      buffer = 0;
      break;
    default:
      if (state === 'before-selector') { state = 'selector'; }
      buffer++;
      break;
    }
  }
 
  // Pop remaining
  while (stack.length) {
    context = stack.pop();
    rule = context[context.length - 1];
    rule.end = rule.declarationEnd = css.length;
    console.log("Unclosed rule: '" + rule.selector + "'");
  }

  return rules;
}

var result = parser("h1 {\n  color: red;\n  }\n\n  .some-class {\n  color: #ff0;\n  background: #f00;\n\n  .nested {\n  color: blue;\n\n  .another {\n    background: pink;\n  }\n  }\n  }");
pending…
Regex Parser
function parser(css) {
  var length = css.length, stack = [], context, nested, match, open, close, rule, rules;
 
  this.cssText = css;
  this.rules = rules = context = [];

  while (css) {
    open = css.indexOf('{');
    close = css.indexOf('}');
 
    if (open >= 0 && (close == -1 || open < close) && (match = css.match(openRe))) {
      css = css.slice(open + 1);
      rule = new Rule(this.cssText, match[2].trim(),
                      length - css.length - match[1].length, length - css.length);
      context.push(rule);
      stack.push(context);
      context = rule.nested;
    } else if (close >= 0 && (open == -1 || close < open) && (match = css.match(closeRe))) {
      css = css.slice(close + match.length);
      context = stack.pop();
      rule = context[context.length - 1];
      if (rule) {
        rule.end = length - css.length;
        rule.declarationEnd = rule.end - match.length;
      } else {
        console.log('Closing unopened rule');
      }
    } else {
      css = null;
    }
  }

  // Pop remaining
  while (stack.length) {
    context = stack.pop();
    rule = context[context.length - 1];
    rule.end = rule.declarationEnd = length;
    console.log("Unclosed rule: '" + rule.selector + "'");
  }

  return rules;
}

var result = parser("h1 {\n  color: red;\n  }\n\n  .some-class {\n  color: #ff0;\n  background: #f00;\n\n  .nested {\n  color: blue;\n\n  .another {\n    background: pink;\n  }\n  }\n  }");
pending…

You can edit these tests or add even more tests to this page by appending /edit to the URL.

Compare results of other browsers

0 comments

Add a comment