83 Commits

Author SHA1 Message Date
Andrew Gallant
715a807289 syntax: rewrite the regex-syntax crate
This commit represents a ground up rewrite of the regex-syntax crate.
This commit is also an intermediate state. That is, it adds a new
regex-syntax-2 crate without making any serious changes to any other
code. Subsequent commits will cover the integration of the rewrite and
the removal of the old crate.

The rewrite is intended to be the first phase in an effort to overhaul
the entire regex crate. To that end, this rewrite takes steps in that
direction:

* The principle change in the public API is an explicit split between a
  regular expression's abstract syntax (AST) and a high-level
  intermediate representation (HIR) that is easier to analyze. The old
  version of this crate mixes these two concepts, but leaned heavily
  towards an HIR. The AST in the rewrite has a much closer
  correspondence with the concrete syntax than the old `Expr` type does.
  The new HIR embraces its role; all flags are now compiled away
  (including the `i` flag), which will simplify subsequent passes,
  including literal detection and the compiler. ASTs are produced by
  ast::parse and HIR is produced by hir::translate. A top-level parser
  is provided that combines these so that callers can skip straight from
  concrete syntax to HIR.
* Error messages are vastly improved thanks to the span information that
  is now embedded in the AST. In addition to better formatting, error
  messages now also include helpful hints when trying to use features
  that aren't supported (like backreferences and look-around). In
  particular, octal support is now an opt-in option. (Octal support
  will continue to be enabled in regex proper to support backwards
  compatibility, but will be disabled in 1.0.)
* More robust support for Unicode Level 1 as described in UTS#18.
  In particular, we now fully support Unicode character classes
  including set notation (difference, intersection, symmetric
  difference) and correct support for named general categories, scripts,
  script extensions and age. That is, `\p{scx:Hira}` and `p{age:3.0}`
  now work. To make this work, we introduce an internal interval set
  data structure.
* With the exception of literal extraction (which will be overhauled in
  a later phase), all code in the rewrite uses constant stack space,
  even while performing analysis that requires structural induction over
  the AST or HIR. This is done by pushing the call stack onto the heap,
  and is abstracted by the `ast::Visitor` and `hir::Visitor` traits.
  The point of this method is to eliminate stack overflows in the
  general case.
* Empty sub-expressions are now properly supported. Expressions like
  `()`, `|`, `a|` and `b|()+` are now valid syntax.

The principle downsides of these changes are parse time and binary size.
Both seemed to have increased (slower and bigger) by about 1.5x. Parse
time is generally peanuts compared to the compiler, so we mostly don't
care about that. Binary size is mildly unfortunate, and if it becomes a
serious issue, it should be possible to introduce a feature that
disables some level of Unicode support and/or work on compressing the
Unicode tables. Compile times have increased slightly, but are still a
very small fraction of the overall time it takes to compile `regex`.

Fixes #174, Fixes #424
2018-03-07 19:01:24 -05:00
Bobby Holley
608c191518 syntax: decrease binary size
Avoid depending on expression Debug stringifier in release builds.

The code weighs over a kilobyte, which doesn't seem worth it for an unreachable.
2018-01-26 17:31:45 -05:00
Wim
9aec6ecad2 license: include license files in sub-crates
Fixes #342
2018-01-17 16:09:35 -05:00
Andrew Gallant
fa5cf6b86e
regex-syntax-0.4.2 2017-12-30 15:38:50 -05:00
Andrew Gallant
65c4f8ee1f docs: link to docs.rs 2017-12-30 15:37:41 -05:00
Andrew Gallant
2f1e5b0e10 deps: setup workspace
There are a few sub-crates in this repository, so sharing a target
directory makes sense.
2017-12-30 15:37:41 -05:00
Andrew Gallant
55223a5269 deps: update quickcheck and rand to latest versions
These are dev dependencies, so we don't need to worry about the minimum
Rust version supported.
2017-12-30 15:37:41 -05:00
Josh Stone
f2ced64455 bump quickcheck to 0.5
Signed-off-by: Igor Gnatenko <i.gnatenko.brain@gmail.com>
2017-11-30 23:06:35 +01:00
Prearo, Andrea
ba5a3df264 Remove sub-expressions for InvalidClassEscape 2017-09-27 09:14:48 -07:00
Prearo, Andrea
d756cd6e90 Fix unused variable warning 2017-09-27 09:14:45 -07:00
Prearo, Andrea
c8471c4041 Remove sub-expressions from parsing error 2017-09-27 09:14:40 -07:00
Josh Stone
a852378de0 regex-syntax: update to Unicode 10.0 2017-09-07 10:36:51 -07:00
David Fifield
83762bdde2 Remove stray newline from fmt::Display for MissingBase10.
Introduced in 45ad9237.
2017-05-31 18:17:03 -07:00
Andrew Gallant
28174bac90
release 0.2.2 2017-05-21 11:43:46 -04:00
Robin Stocker
bb233ecb5d Support nested character classes and intersection with &&
This implements parts of UTS#18 RL1.3, namely:

* Nested character classes, e.g.: `[a[b-c]]`
* Intersections in classes, e.g.: `[\w&&\p{Greek}]`

They can be combined to do things like `[\w&&[^a]]` to get all word
characters except `a`.

Fixes #341
2017-05-21 21:30:15 +10:00
Robin Stocker
6f32d2f683 Fix panics with whitespace in extended mode by being more strict
Instead of ignoring space in all the bump/peek methods (as proposed in
pull request #349), have an explicit `ignore_space` method that can be
used in places where space/comments should be allowed.

This makes parsing a bit stricter than before as well.
2017-04-07 19:06:43 +10:00
Andrew Gallant
d894c631cb Fix a bug in literal extraction.
When doing literal extraction, a non-empty concatenation should always be
cut when a `^` (for prefixes) or a `$` (for suffixes) is seen. If a counted
repetition is used, e.g., `${2}`, then the cut detection fails. We add in
a special case to handle it.

Fixes #321
2017-02-17 21:39:29 -05:00
Robin Stocker
de011eaa09 Fix typo in comment (UTC -> UTS) 2017-02-16 15:19:08 +11:00
Andrew Gallant
c7bc06f8d4 Reorganize CI testing.
Writing all of the testing scripts inside the .travis.yml file was
becoming painful, and parts of it were wrong by allowing for some
commands to fail without failing the entire build.

This also fixes the Github token (again).
2017-01-02 16:50:48 -05:00
Andrew Gallant
ac3ab6d21b Bump versions everywhere and update CHANGELOG.
Fixes #296, Fixes #307
2016-12-31 17:01:54 -05:00
Andrew Gallant
f094d15678 Update github links. 2016-12-31 16:49:30 -05:00
Andrew Gallant
dd120a963a Require escaping of [, &, - and ~ in classes.
The escaping of &, - and ~ is only required when the characters are
repeated adjacently, which should be quite rare. Escaping of [ is always
required, unless it appear in the second position of a range.

These rules enable us to add character class sets as described in
UTS#18 RL1.3 in a backward compatible way.
2016-12-30 01:06:18 -05:00
Andrew Gallant
bc06024c7f Make ASCII classes consistent with other engines.
For example, the regex `[:upper:]` used to correspond to the `upper`
ASCII character class, but it now corresponds to the character class
containing the characters `:upper:`.

Forms like `[[:upper:][:blank:]]` are still accepted.

Fixes #175
2016-12-30 01:06:18 -05:00
Aaronepower
8ee9262124 Changed the name of quote to escape. 2016-12-30 01:06:18 -05:00
Andrew Gallant
524ba8e6ad Add a test case for fix #304. 2016-12-28 19:38:50 -05:00
Scott Steele
b96e5cb899 Verify character class still non-empty after converting to byte class
For `[^\x00-\xff]`, while it is still treated as a full Unicode
character class, it is not empty. For instance `≥` would still be
matched.

However, when `CharClass::to_byte_class` is called on it (as is done
when using `regex::bytes::Regex::new` rather than `regex::Regex::new`),
it _is_ now empty, since it excludes all possible bytes.

This commit adds a test asserting that `regex::bytes::Regex::new`
returns `Err` for this case (in accordance with
https://github.com/rust-lang-nursery/regex/issues/106) and adds an
`is_empty` check to the result of calling `CharClass::to_byte_class`,
which allows the test to pass.
2016-12-07 21:20:08 -05:00
Andrew Gallant
54ae5b6b42 remove codegen-units 2016-12-06 17:35:44 -05:00
Matt DeBoard
45ad9237b6 Fix typo in error messages. 2016-11-17 21:25:59 -06:00
Andrew Gallant
4ff10d58db regex-syntax-0.3.9 2016-10-27 18:01:32 -04:00
Andrew Gallant
0471c74929 Fixes a bug introduced by the fix in 630049.
This is another variant of incorrectly producing an unambiguous set of
literals.

Fixes #291.
2016-10-27 17:36:12 -04:00
Andrew Gallant
06f952b9ac regex-syntax-0.3.8 2016-10-26 18:31:33 -04:00
Andrew Gallant
220c076b7a Add another regression test. 2016-10-26 18:31:04 -04:00
Andrew Gallant
6300490803 Fix a bug in uambiguous prefixes.
Specifically, given the strings ABCX, CDAX and BCX, it was reporting
the unambiguous set as A, BCX and CDAX, which is wrong since A is a
substring of CDAX.

unambiguous_prefixes is now quite a bit of a mess, but so is the rest
of the literal extraction code. The only thing it has going for it is
a massive test suite.

Fixes #289
2016-10-26 17:27:23 -04:00
Andrew Gallant
3cfef1e79d regex-syntax-0.3.7 2016-10-10 21:54:14 -04:00
Andrew Gallant
53e7ed5ff5 regex-syntax-0.3.6 2016-10-10 21:53:40 -04:00
Andrew Gallant
96d90f8b5b tag version in regex-syntax/Cargo.toml 2016-10-10 21:53:06 -04:00
Andrew Gallant
e5b4063815 Fix bug in expression printing round trip.
In particular, if a range started or ended with `-`, then the pretty
printer would naively print `-` as if it were a normal character. For
example, `[--/]` corresponds to the class `[-/]` since the first `-` is
treated as a literal `-` and not part of any range.

There are a couple ways to fix this. For now, we fix the pretty printer
so that it never prints a `-` as part of a range.

This bug was found through ripgrep:
https://github.com/BurntSushi/ripgrep/issues/156
2016-10-10 21:53:06 -04:00
aweinstock314
54048b6df3 Fix invariant in comment
`(end >= end)` is trivial, `(end >= start)` is likely intended.
2016-09-13 18:04:08 -04:00
Andrew Gallant
dc92a0bd4e Add a way to trim suffixes from literal sets. 2016-09-11 16:01:05 -04:00
Andrew Gallant
1488bb8ea3 regex-syntax 0.3.5 2016-09-04 09:31:39 -04:00
Andrew Gallant
e3449685f2 Add 'u' flag to error message. 2016-09-04 09:28:44 -04:00
Andrew Gallant
225f8e190d Disable literal optimizations for partially anchored regexes.
The specific problem here is that our literal search doesn't know about
anchors, so it will try to search all of the detected literals in a regex.
In a regex like `a|^b`, the literal `b` should only be searched for at the
beginning of the haystack and in no other place.

The right way to fix this is probably to make the literal detector smarter,
but the literal detector is already too complex. Instead, this commit
detects whether a regex is partially anchored (that is, when the regex has
at least one matchable sub-expression that is anchored), and if so,
disables the literal engine.

Note that this doesn't disable all literal optimizations, just the
optimization that opts out of regex engines entirely. Both the DFA and the
NFA will still use literal prefixes to search. Namely, if it searches and
finds a literal that needs to be anchored but isn't in the haystack, then
the regex engine rules it out as a false positive.

Fixes #268.
2016-08-04 20:17:59 -04:00
Andrew Gallant
98b9da3b52 regex-syntax 0.3.4 2016-07-10 00:45:41 -04:00
Andrew Gallant
84a2bf5d73 Match (?-u:\B) correctly in the NFA engines when valid UTF-8 is required.
This commit fixes a bug where matching (?-u:\B) (that is, "not an ASCII
word boundary") in the NFA engines could produce match positions at invalid
UTF-8 sequence boundaries. The specific problem is that determining whether
(?-u:\B) matches or not relies on knowing whether we must report matches
only at UTF-8 boundaries, and this wasn't actually being taken into
account. (Instead, we prefer to enforce this invariant in the compiler, so
that the matching engines mostly don't have to care about it.) But of
course, the zero-width assertions are kind of a special case all around,
so we need to handle ASCII word boundaries differently depending on
whether we require valid UTF-8.

This bug was noticed because the DFA actually handles this correctly (by
encoding ASCII word boundaries into the state machine itself, which in turn
guarantees the valid UTF-8 invariant) while the NFAs don't, leading to an
inconsistency.

Fix #241.
2016-07-09 22:45:11 -04:00
Andrew Gallant
81297f09cf Disallow Unicode literals in character classes when Unicode is disabled.
When Unicode mode is disabled, we also disable the use of Unicode literals
in the regular expression, since it can lead to unintuitive behavior. In
this case, Unicode literals in character classes were not disallowed, and
subsequent code filtered them out, which resulted in an empty character
class. The compiler assumes that empty character classes are not allowed,
and so this causes an assert to trigger.

Fixes #250.
2016-07-09 19:07:24 -04:00
Andrew Gallant
9062f38eff Disallow empty character class ranges.
The compiler in particular assumes that it never gets an empty character
class. The current parser is pretty paranoid about rejecting empty classes,
but a few tricky cases made it through. In particular, one can write
`[^\d\D]` to correspond to "match nothing." This commit now looks for
empty classes explicitly, and if one is found, returns an error.

Interestingly, other regex engines allow this particular idiosyncrasy and
interpret it as "never match." Even more interesting, expressions like
`a{0}` are also allowed (including by this regex library) and are
interpreted as "always match the empty string." Both seem semantically the
same. In any case, we forbid empty character classes, primarily because
that seems like the sensible thing to do but secondarily because it's the
conservative choice.

It seems plausible that such a construct could be occasionally useful if
one were machine generating regexes, because it could be used to indicate
"never match." If we do want to support that use case, we'll need to add
a new opcode to the regex matching engines. One can still achieve
that today using something like `(a|[^a])`.

Fixes #257, where using such a form caused an assert to trip in the
compiler. A new, more explicit assert has been added.
2016-07-09 16:52:58 -04:00
Andrew Gallant
ed4f046bb1 regex-syntax 0.3.3 2016-06-16 07:21:00 -04:00
Andrew Gallant
e84a22485c Fix bug where unclosed paren was allowed.
When popping the parse stack at the end of parsing, we weren't actually
checking that the stack was empty. If the stack isn't empty, then that
indicates an unclosed parenthesis.

Fixes #239.
2016-05-23 10:00:09 -04:00
Andrew Gallant
f4e9e15e58 regex-syntax 0.3.2 2016-05-20 06:36:10 -04:00
Andrew Gallant
f9af58c4ca Fixes #234.
It turns out that we weren't compute suffix literals correctly in all
cases. In particular, the bytes from a Unicode character were being
reversed.
2016-05-19 17:47:44 -04:00