svaarala / duktape

Duktape - embeddable Javascript engine with a focus on portability and compact footprint
MIT License
5.97k stars 515 forks source link

Rework regular expression parsing/compiling to be more lenient #74

Open svaarala opened 10 years ago

svaarala commented 10 years ago

Duktape's regular expression parser/compiler conforms strictly to the E5/E5.1 regular expression syntax, and rejects many regular expressions that are leniently allowed by other engines. It's confusing to users that other engines parse several regexp forms that Duktape (technically correctly) rejects as a SyntaxError.

Examples of technically invalid regexps that work with most engines:

Because these regexps are quite widely used, Duktape should probably parse regexps a bit more leniently. This change is non-trivial because lenient regexp parsing needs backtracking which doesn't fit easily into the current regexp parser/compiler code. For instance, when parsing /{foo}/, Duktape will see the left curly brace and think it's parsing a quantifier. When it fails to parse, it currently throws a SyntaxError. Instead, it should rewind and assume { is meant literally, i.e. parse the regexp as /\{foo\}/.

Related issues with notes on various failing regexps:

Tasks:

mitchblank commented 10 years ago

Not directly related, but just wanted to plant an idea in your head.

In my codebase I already have a regex engine available -- namely PCRE with some optimizations layered on top. Modern PCRE even support things like JIT compilation.

I know that pcre and ecmascript regexes aren't identical but pcre is pretty close to being a superset, so if it were possible to bring my own regex engine I probably would.

When I was doing some investigations of mruby, that was one thing I really liked. The language's parser would find the regex, but it would then just hand off the string to a constructor for the ruby Regex class. By putting your own implementation of that class in the global scope it was very easy to seamlessly wire in your own engine.

I don't consider this a high-priority item at all, but if there was an API for me to pass in a few function pointers and slide in my own versions of duk__regexp_match_helper() and duk_regexp_create_instance() (and make the bytecode just emit the raw regex into the bytecode) I would probably do so. Maybe others would find that useful as well, not sure.

svaarala commented 10 years ago

I agree that replacing the regexp engine would be very nice. The built-in engine in Duktape is optimized for compactness which leads to necessary compromises on other features (including performance). I think there's already a Ditz issue for this but it's been low priority.

But now that you mention it, it might make sense to keep Duktape's built-in engine strictly compliant, but make it easy to plug in external regexp engines. These can then be lenient and provide a superset of features when that makes sense.

As for the required hooks, another very straightforward approach is simply to replace the RegExp constructor entirely. RegExp literals need special handling because they're compiled during Ecmascript code compilation and only instantiated during execution. A few hooks would be needed for this.

mitchblank commented 10 years ago

Of course the compiler would still need to lex the regex enough to find its terminating '/' character so it would have to interpret the regex enough to see the backslash escapes etc. It just would skip doing the bytecode generation part.

svaarala commented 10 years ago

Yes, that's what I mean: the compiler would parse the regexp literals (and their flags) but would consult a hook to compile it.

svaarala commented 8 years ago

By the way, ES6 has optional support for literal braces in Section B.1.4:

This doesn't extend to unquoted square brackets etc though.

fatcerberus commented 8 years ago

Nice, so we can say the curly brace support is part of "some features borrowed from Ecmascript E6" ;)

svaarala commented 8 years ago

Yes, and as a technical detail the config option shouldn't say "DUK_USENONSTD..." but "DUK_USEES6...".

mathiasbynens commented 8 years ago

Another example: /]/ — see https://github.com/slevithan/xregexp/pull/141. (Since portability is one of Duktape’s goals you’ll want to support this.)