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.