We have a very good regular lexical grammar that approximates the way
JavaScript interpreters tokenize JavaScript source, but we rely on our
source tree rendering to make sure that a JavaScript interpreter will
always come to the same conclusion as to where regular expression
literals and division operators start and end.

SES might benefit from a cheap reliable transformation that
(1) takes a string of SES source
(2) produces a string of SES source where a standards compliant
   JavaScript interpreter will either throw a ParseError or come to the
   same conclusion as an approximate regular lexical grammar

I think the below is such a transformation.

function removeSlashAmbiguity(tokens /* []string */) {
  var outputTokens = [];
  for (var i = 0, n = tokens.length; i < n; ++i) {
    var token = tokens[i];
    if (isRegularExpressionToken(token)) {
      outputTokens.push("/./", ".", "constructor", "(", token, ")");
    } else if (token === "/" || token === "/=") {
      outputTokens.push(token.replace(/^\//, "*"), "1", "*");
    } else {
      outputTokens.push(token);
    }
  }
  return outputTokens;
}


To break it down:

(1) a regular expression /foo/i is transformed to
    /./.constructor(/foo/i)

    If the "/" at the start of /foo/i were a division operator, then
    the transformed code would fail to parse because of the dot in
    /./.

    This is valid because the dot operator and the function call
    operator bind very tightly.

     It is semantics preserving because /./.constructor is a reliably
     path (in SES) to RegExp and RegExp(/./) returns its input
     unchanged.

     It also does not conflict with semicolon insertion -- there is no
     infix operator on the left (or ParseError would have been thrown)
     that might attach to the previous line.

(2) A use of the division operator (a / b) is transformed to (a * 1 / b)

     If our lexing was wrong and the slash was the start of a regular
     expression, then replacing the slash with an asterisk makes the
     content obviously a numeric operation, so our interpretation is
     maintained.

     This is semantics preserving because both * and / coerce both
     sides to numbers, and have the same precedence, and 1 is
     multiplicative identity.

(3) A use of the /= operator (a /= b) is transformed to (a *= 1 / b)

    Same argument as 2.

    This is semantics preserving because the left-hand side is
    unchanged and is evaluated only once, and same argument as 2.

Reply via email to