diff --git a/9-regular-expressions/01-regexp-introduction/article.md b/9-regular-expressions/01-regexp-introduction/article.md index 59775d3c0..a35d19a7b 100644 --- a/9-regular-expressions/01-regexp-introduction/article.md +++ b/9-regular-expressions/01-regexp-introduction/article.md @@ -1,59 +1,65 @@ # Patterns and flags -Regular expressions is a powerful way of searching and replacing inside a string. +Regular expressions are patterns that provide a powerful way to search and replace in text. -In JavaScript regular expressions are implemented using objects of a built-in `RegExp` class and integrated with strings. +In JavaScript, they are available via the [RegExp](mdn:js/RegExp) object, as well as being integrated in methods of strings. -Please note that regular expressions vary between programming languages. In this tutorial we concentrate on JavaScript. Of course there's a lot in common, but they are a somewhat different in Perl, Ruby, PHP etc. - -## Regular expressions +## Regular Expressions A regular expression (also "regexp", or just "reg") consists of a *pattern* and optional *flags*. -There are two syntaxes to create a regular expression object. +There are two syntaxes that can be used to create a regular expression object. -The long syntax: +The "long" syntax: ```js regexp = new RegExp("pattern", "flags"); ``` -...And the short one, using slashes `"/"`: +And the "short" one, using slashes `"/"`: ```js regexp = /pattern/; // no flags regexp = /pattern/gmi; // with flags g,m and i (to be covered soon) ``` -Slashes `"/"` tell JavaScript that we are creating a regular expression. They play the same role as quotes for strings. +Slashes `pattern:/.../` tell JavaScript that we are creating a regular expression. They play the same role as quotes for strings. -## Usage +In both cases `regexp` becomes an instance of the built-in `RegExp` class. -To search inside a string, we can use method [search](mdn:js/String/search). +The main difference between these two syntaxes is that pattern using slashes `/.../` does not allow for expressions to be inserted (like string template literals with `${...}`). They are fully static. -Here's an example: +Slashes are used when we know the regular expression at the code writing time -- and that's the most common situation. While `new RegExp`, is more often used when we need to create a regexp "on the fly" from a dynamically generated string. For instance: -```js run -let str = "I love JavaScript!"; // will search here +```js +let tag = prompt("What tag do you want to find?", "h2"); -let regexp = /love/; -alert( str.search(regexp) ); // 2 +let regexp = new RegExp(`<${tag}>`); // same as /
$`|inserts a part of the string before the match|
+|`$'`|inserts a part of the string after the match|
+|`$n`|if `n` is a 1-2 digit number, then it inserts the contents of n-th parentheses, more about it in the chapter `.** + +1. The simplest one: `pattern:/<[a-z]+>/i` + + ```js run + alert( "
... ".match(/<[a-z]+>/gi) ); // + ``` + + The regexp looks for character `pattern:'<'` followed by one or more Latin letters, and then `pattern:'>'`. + +2. Improved: `pattern:/<[a-z][a-z0-9]*>/i` + + According to the standard, HTML tag name may have a digit at any position except the first one, like `...';
+let regexp = //g;
+
+// Wrong match!
+alert( str.match(regexp) ); // ...
+```
+
+Now it fails. The match includes not just a link, but also a lot of text after it, including ` `. Not what we want.
+
+Here's the picture of the match aligned with the text:
+
+```html
+
+...
+```
+
+So, we need the pattern to look for ``, but both greedy and lazy variants have problems.
+
+The correct variant can be: `pattern:href="[^"]*"`. It will take all characters inside the `href` attribute till the nearest quote, just what we need.
+
+A working example:
+
+```js run
+let str1 = '...... ...';
+let str2 = '...... ...';
+let regexp = //g;
+
+// Works!
+alert( str1.match(regexp) ); // null, no matches, that's correct
+alert( str2.match(regexp) ); // ,
+```
+
+## Summary
+
+Quantifiers have two modes of work:
+
+Greedy
+: By default the regular expression engine tries to repeat the quantifier as many times as possible. For instance, `pattern:\d+` consumes all possible digits. When it becomes impossible to consume more (no more digits or string end), then it continues to match the rest of the pattern. If there's no match then it decreases the number of repetitions (backtracks) and tries again.
+
+Lazy
+: Enabled by the question mark `pattern:?` after the quantifier. The regexp engine tries to match the rest of the pattern before each repetition of the quantifier.
+
+As we've seen, the lazy mode is not a "panacea" from the greedy search. An alternative is a "fine-tuned" greedy search, with exclusions, as in the pattern `pattern:"[^"]+"`.
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy1.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy1.svg
new file mode 100644
index 000000000..2eaf636cd
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy2.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy2.svg
new file mode 100644
index 000000000..0489875a6
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy3.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy3.svg
new file mode 100644
index 000000000..f5175e5c3
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy4.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy4.svg
new file mode 100644
index 000000000..61b37fb9c
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy4.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy5.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy5.svg
new file mode 100644
index 000000000..a0c5f1fb8
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy5.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy6.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy6.svg
new file mode 100644
index 000000000..c7cc7537c
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy6.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy3.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy3.svg
new file mode 100644
index 000000000..77d5d1562
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy4.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy4.svg
new file mode 100644
index 000000000..6c9cc29cf
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy4.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy5.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy5.svg
new file mode 100644
index 000000000..68c77d27d
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy5.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy6.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy6.svg
new file mode 100644
index 000000000..2ee64f5b8
--- /dev/null
+++ b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy6.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/9-regular-expressions/11-regexp-groups/01-test-mac/solution.md b/9-regular-expressions/11-regexp-groups/01-test-mac/solution.md
new file mode 100644
index 000000000..26f7888f7
--- /dev/null
+++ b/9-regular-expressions/11-regexp-groups/01-test-mac/solution.md
@@ -0,0 +1,21 @@
+A two-digit hex number is `pattern:[0-9a-f]{2}` (assuming the flag `pattern:i` is set).
+
+We need that number `NN`, and then `:NN` repeated 5 times (more numbers);
+
+The regexp is: `pattern:[0-9a-f]{2}(:[0-9a-f]{2}){5}`
+
+Now let's show that the match should capture all the text: start at the beginning and end at the end. That's done by wrapping the pattern in `pattern:^...$`.
+
+Finally:
+
+```js run
+let regexp = /^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}$/i;
+
+alert( regexp.test('01:32:54:67:89:AB') ); // true
+
+alert( regexp.test('0132546789AB') ); // false (no colons)
+
+alert( regexp.test('01:32:54:67:89') ); // false (5 numbers, need 6)
+
+alert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ in the end)
+```
diff --git a/9-regular-expressions/11-regexp-groups/01-test-mac/task.md b/9-regular-expressions/11-regexp-groups/01-test-mac/task.md
new file mode 100644
index 000000000..029a4803a
--- /dev/null
+++ b/9-regular-expressions/11-regexp-groups/01-test-mac/task.md
@@ -0,0 +1,20 @@
+# Check MAC-address
+
+[MAC-address](https://en.wikipedia.org/wiki/MAC_address) of a network interface consists of 6 two-digit hex numbers separated by a colon.
+
+For instance: `subject:'01:32:54:67:89:AB'`.
+
+Write a regexp that checks whether a string is MAC-address.
+
+Usage:
+```js
+let regexp = /your regexp/;
+
+alert( regexp.test('01:32:54:67:89:AB') ); // true
+
+alert( regexp.test('0132546789AB') ); // false (no colons)
+
+alert( regexp.test('01:32:54:67:89') ); // false (5 numbers, must be 6)
+
+alert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ ad the end)
+```
diff --git a/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md b/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md
new file mode 100644
index 000000000..0806dc4fd
--- /dev/null
+++ b/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md
@@ -0,0 +1,27 @@
+A regexp to search 3-digit color `#abc`: `pattern:/#[a-f0-9]{3}/i`.
+
+We can add exactly 3 more optional hex digits. We don't need more or less. The color has either 3 or 6 digits.
+
+Let's use the quantifier `pattern:{1,2}` for that: we'll have `pattern:/#([a-f0-9]{3}){1,2}/i`.
+
+Here the pattern `pattern:[a-f0-9]{3}` is enclosed in parentheses to apply the quantifier `pattern:{1,2}`.
+
+In action:
+
+```js run
+let regexp = /#([a-f0-9]{3}){1,2}/gi;
+
+let str = "color: #3f3; background-color: #AA00ef; and: #abcd";
+
+alert( str.match(regexp) ); // #3f3 #AA00ef #abc
+```
+
+There's a minor problem here: the pattern found `match:#abc` in `subject:#abcd`. To prevent that we can add `pattern:\b` to the end:
+
+```js run
+let regexp = /#([a-f0-9]{3}){1,2}\b/gi;
+
+let str = "color: #3f3; background-color: #AA00ef; and: #abcd";
+
+alert( str.match(regexp) ); // #3f3 #AA00ef
+```
diff --git a/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md b/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md
new file mode 100644
index 000000000..09108484a
--- /dev/null
+++ b/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md
@@ -0,0 +1,14 @@
+# Find color in the format #abc or #abcdef
+
+Write a RegExp that matches colors in the format `#abc` or `#abcdef`. That is: `#` followed by 3 or 6 hexadecimal digits.
+
+Usage example:
+```js
+let regexp = /your regexp/g;
+
+let str = "color: #3f3; background-color: #AA00ef; and: #abcd";
+
+alert( str.match(regexp) ); // #3f3 #AA00ef
+```
+
+P.S. This should be exactly 3 or 6 hex digits. Values with 4 digits, such as `#abcd`, should not match.
diff --git a/9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/solution.md b/9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/solution.md
new file mode 100644
index 000000000..c4349f9a0
--- /dev/null
+++ b/9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/solution.md
@@ -0,0 +1,11 @@
+A positive number with an optional decimal part is (per previous task): `pattern:\d+(\.\d+)?`.
+
+Let's add the optional `pattern:-` in the beginning:
+
+```js run
+let regexp = /-?\d+(\.\d+)?/g;
+
+let str = "-1.5 0 2 -123.4.";
+
+alert( str.match(regexp) ); // -1.5, 0, 2, -123.4
+```
diff --git a/9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/task.md b/9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/task.md
new file mode 100644
index 000000000..4f5a73fff
--- /dev/null
+++ b/9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/task.md
@@ -0,0 +1,13 @@
+# Find all numbers
+
+Write a regexp that looks for all decimal numbers including integer ones, with the floating point and negative ones.
+
+An example of use:
+
+```js
+let regexp = /your regexp/g;
+
+let str = "-1.5 0 2 -123.4.";
+
+alert( str.match(regexp) ); // -1.5, 0, 2, -123.4
+```
diff --git a/9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md b/9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md
new file mode 100644
index 000000000..130c57be3
--- /dev/null
+++ b/9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md
@@ -0,0 +1,56 @@
+A regexp for a number is: `pattern:-?\d+(\.\d+)?`. We created it in previous tasks.
+
+An operator is `pattern:[-+*/]`. The hyphen `pattern:-` goes first in the square brackets, because in the middle it would mean a character range, while we just want a character `-`.
+
+The slash `/` should be escaped inside a JavaScript regexp `pattern:/.../`, we'll do that later.
+
+We need a number, an operator, and then another number. And optional spaces between them.
+
+The full regular expression: `pattern:-?\d+(\.\d+)?\s*[-+*/]\s*-?\d+(\.\d+)?`.
+
+It has 3 parts, with `pattern:\s*` between them:
+1. `pattern:-?\d+(\.\d+)?` - the first number,
+1. `pattern:[-+*/]` - the operator,
+1. `pattern:-?\d+(\.\d+)?` - the second number.
+
+To make each of these parts a separate element of the result array, let's enclose them in parentheses: `pattern:(-?\d+(\.\d+)?)\s*([-+*/])\s*(-?\d+(\.\d+)?)`.
+
+In action:
+
+```js run
+let regexp = /(-?\d+(\.\d+)?)\s*([-+*\/])\s*(-?\d+(\.\d+)?)/;
+
+alert( "1.2 + 12".match(regexp) );
+```
+
+The result includes:
+
+- `result[0] == "1.2 + 12"` (full match)
+- `result[1] == "1.2"` (first group `(-?\d+(\.\d+)?)` -- the first number, including the decimal part)
+- `result[2] == ".2"` (second group`(\.\d+)?` -- the first decimal part)
+- `result[3] == "+"` (third group `([-+*\/])` -- the operator)
+- `result[4] == "12"` (forth group `(-?\d+(\.\d+)?)` -- the second number)
+- `result[5] == undefined` (fifth group `(\.\d+)?` -- the last decimal part is absent, so it's undefined)
+
+We only want the numbers and the operator, without the full match or the decimal parts, so let's "clean" the result a bit.
+
+The full match (the arrays first item) can be removed by shifting the array `result.shift()`.
+
+Groups that contain decimal parts (number 2 and 4) `pattern:(.\d+)` can be excluded by adding `pattern:?:` to the beginning: `pattern:(?:\.\d+)?`.
+
+The final solution:
+
+```js run
+function parse(expr) {
+ let regexp = /(-?\d+(?:\.\d+)?)\s*([-+*\/])\s*(-?\d+(?:\.\d+)?)/;
+
+ let result = expr.match(regexp);
+
+ if (!result) return [];
+ result.shift();
+
+ return result;
+}
+
+alert( parse("-1.23 * 3.45") ); // -1.23, *, 3.45
+```
diff --git a/9-regular-expressions/11-regexp-groups/04-parse-expression/task.md b/9-regular-expressions/11-regexp-groups/04-parse-expression/task.md
new file mode 100644
index 000000000..8b54d4683
--- /dev/null
+++ b/9-regular-expressions/11-regexp-groups/04-parse-expression/task.md
@@ -0,0 +1,28 @@
+# Parse an expression
+
+An arithmetical expression consists of 2 numbers and an operator between them, for instance:
+
+- `1 + 2`
+- `1.2 * 3.4`
+- `-3 / -6`
+- `-2 - 2`
+
+The operator is one of: `"+"`, `"-"`, `"*"` or `"/"`.
+
+There may be extra spaces at the beginning, at the end or between the parts.
+
+Create a function `parse(expr)` that takes an expression and returns an array of 3 items:
+
+1. The first number.
+2. The operator.
+3. The second number.
+
+For example:
+
+```js
+let [a, op, b] = parse("1.2 * 3.4");
+
+alert(a); // 1.2
+alert(op); // *
+alert(b); // 3.4
+```
diff --git a/9-regular-expressions/11-regexp-groups/article.md b/9-regular-expressions/11-regexp-groups/article.md
new file mode 100644
index 000000000..e559fd87c
--- /dev/null
+++ b/9-regular-expressions/11-regexp-groups/article.md
@@ -0,0 +1,364 @@
+# Capturing groups
+
+A part of a pattern can be enclosed in parentheses `pattern:(...)`. This is called a "capturing group".
+
+That has two effects:
+
+1. It allows to get a part of the match as a separate item in the result array.
+2. If we put a quantifier after the parentheses, it applies to the parentheses as a whole.
+
+## Examples
+
+Let's see how parentheses work in examples.
+
+### Example: gogogo
+
+Without parentheses, the pattern `pattern:go+` means `subject:g` character, followed by `subject:o` repeated one or more times. For instance, `match:goooo` or `match:gooooooooo`.
+
+Parentheses group characters together, so `pattern:(go)+` means `match:go`, `match:gogo`, `match:gogogo` and so on.
+
+```js run
+alert( 'Gogogo now!'.match(/(go)+/ig) ); // "Gogogo"
+```
+
+### Example: domain
+
+Let's make something more complex -- a regular expression to search for a website domain.
+
+For example:
+
+```
+mail.com
+users.mail.com
+smith.users.mail.com
+```
+
+As we can see, a domain consists of repeated words, a dot after each one except the last one.
+
+In regular expressions that's `pattern:(\w+\.)+\w+`:
+
+```js run
+let regexp = /(\w+\.)+\w+/g;
+
+alert( "site.com my.site.com".match(regexp) ); // site.com,my.site.com
+```
+
+The search works, but the pattern can't match a domain with a hyphen, e.g. `my-site.com`, because the hyphen does not belong to class `pattern:\w`.
+
+We can fix it by replacing `pattern:\w` with `pattern:[\w-]` in every word except the last one: `pattern:([\w-]+\.)+\w+`.
+
+### Example: email
+
+The previous example can be extended. We can create a regular expression for emails based on it.
+
+The email format is: `name@domain`. Any word can be the name, hyphens and dots are allowed. In regular expressions that's `pattern:[-.\w]+`.
+
+The pattern:
+
+```js run
+let regexp = /[-.\w]+@([\w-]+\.)+[\w-]+/g;
+
+alert("my@mail.com @ his@site.com.uk".match(regexp)); // my@mail.com, his@site.com.uk
+```
+
+That regexp is not perfect, but mostly works and helps to fix accidental mistypes. The only truly reliable check for an email can only be done by sending a letter.
+
+## Parentheses contents in the match
+
+Parentheses are numbered from left to right. The search engine memorizes the content matched by each of them and allows to get it in the result.
+
+The method `str.match(regexp)`, if `regexp` has no flag `g`, looks for the first match and returns it as an array:
+
+1. At index `0`: the full match.
+2. At index `1`: the contents of the first parentheses.
+3. At index `2`: the contents of the second parentheses.
+4. ...and so on...
+
+For instance, we'd like to find HTML tags `pattern:<.*?>`, and process them. It would be convenient to have tag content (what's inside the angles), in a separate variable.
+
+Let's wrap the inner content into parentheses, like this: `pattern:<(.*?)>`.
+
+Now we'll get both the tag as a whole `match:` and its contents `match:h1` in the resulting array:
+
+```js run
+let str = '
Hello, world!
';
+
+let tag = str.match(/<(.*?)>/);
+
+alert( tag[0] ); //
+alert( tag[1] ); // h1
+```
+
+### Nested groups
+
+Parentheses can be nested. In this case the numbering also goes from left to right.
+
+For instance, when searching a tag in `subject:` we may be interested in:
+
+1. The tag content as a whole: `match:span class="my"`.
+2. The tag name: `match:span`.
+3. The tag attributes: `match:class="my"`.
+
+Let's add parentheses for them: `pattern:<(([a-z]+)\s*([^>]*))>`.
+
+Here's how they are numbered (left to right, by the opening paren):
+
+
+
+In action:
+
+```js run
+let str = '';
+
+let regexp = /<(([a-z]+)\s*([^>]*))>/;
+
+let result = str.match(regexp);
+alert(result[0]); //
+alert(result[1]); // span class="my"
+alert(result[2]); // span
+alert(result[3]); // class="my"
+```
+
+The zero index of `result` always holds the full match.
+
+Then groups, numbered from left to right by an opening paren. The first group is returned as `result[1]`. Here it encloses the whole tag content.
+
+Then in `result[2]` goes the group from the second opening paren `pattern:([a-z]+)` - tag name, then in `result[3]` the tag: `pattern:([^>]*)`.
+
+The contents of every group in the string:
+
+
+
+### Optional groups
+
+Even if a group is optional and doesn't exist in the match (e.g. has the quantifier `pattern:(...)?`), the corresponding `result` array item is present and equals `undefined`.
+
+For instance, let's consider the regexp `pattern:a(z)?(c)?`. It looks for `"a"` optionally followed by `"z"` optionally followed by `"c"`.
+
+If we run it on the string with a single letter `subject:a`, then the result is:
+
+```js run
+let match = 'a'.match(/a(z)?(c)?/);
+
+alert( match.length ); // 3
+alert( match[0] ); // a (whole match)
+alert( match[1] ); // undefined
+alert( match[2] ); // undefined
+```
+
+The array has the length of `3`, but all groups are empty.
+
+And here's a more complex match for the string `subject:ac`:
+
+```js run
+let match = 'ac'.match(/a(z)?(c)?/)
+
+alert( match.length ); // 3
+alert( match[0] ); // ac (whole match)
+alert( match[1] ); // undefined, because there's nothing for (z)?
+alert( match[2] ); // c
+```
+
+The array length is permanent: `3`. But there's nothing for the group `pattern:(z)?`, so the result is `["ac", undefined, "c"]`.
+
+## Searching for all matches with groups: matchAll
+
+```warn header="`matchAll` is a new method, polyfill may be needed"
+The method `matchAll` is not supported in old browsers.
+
+A polyfill may be required, such as
';
+
+let tags = str.match(/<(.*?)>/g);
+
+alert( tags ); //
,
+```
+
+The result is an array of matches, but without details about each of them. But in practice we usually need contents of capturing groups in the result.
+
+To get them, we should search using the method `str.matchAll(regexp)`.
+
+It was added to JavaScript language long after `match`, as its "new and improved version".
+
+Just like `match`, it looks for matches, but there are 3 differences:
+
+1. It returns not an array, but an iterable object.
+2. When the flag `pattern:g` is present, it returns every match as an array with groups.
+3. If there are no matches, it returns not `null`, but an empty iterable object.
+
+For instance:
+
+```js run
+let results = '
'.matchAll(/<(.*?)>/gi);
+
+// results - is not an array, but an iterable object
+alert(results); // [object RegExp String Iterator]
+
+alert(results[0]); // undefined (*)
+
+results = Array.from(results); // let's turn it into array
+
+alert(results[0]); //
,h1 (1st tag)
+alert(results[1]); //
,h2 (2nd tag)
+```
+
+As we can see, the first difference is very important, as demonstrated in the line `(*)`. We can't get the match as `results[0]`, because that object isn't pseudoarray. We can turn it into a real `Array` using `Array.from`. There are more details about pseudoarrays and iterables in the article
'.matchAll(/<(.*?)>/gi);
+
+for(let result of results) {
+ alert(result);
+ // first alert:
,h1
+ // second:
,h2
+}
+```
+
+...Or using destructuring:
+
+```js
+let [tag1, tag2] = '
'.matchAll(/<(.*?)>/gi);
+```
+
+Every match, returned by `matchAll`, has the same format as returned by `match` without flag `pattern:g`: it's an array with additional properties `index` (match index in the string) and `input` (source string):
+
+```js run
+let results = '
'.matchAll(/<(.*?)>/gi);
+
+let [tag1, tag2] = results;
+
+alert( tag1[0] ); //
+alert( tag1[1] ); // h1
+alert( tag1.index ); // 0
+alert( tag1.input ); //
+```
+
+```smart header="Why is a result of `matchAll` an iterable object, not an array?"
+Why is the method designed like that? The reason is simple - for the optimization.
+
+The call to `matchAll` does not perform the search. Instead, it returns an iterable object, without the results initially. The search is performed each time we iterate over it, e.g. in the loop.
+
+So, there will be found as many results as needed, not more.
+
+E.g. there are potentially 100 matches in the text, but in a `for..of` loop we found 5 of them, then decided it's enough and make a `break`. Then the engine won't spend time finding other 95 matches.
+```
+
+## Named groups
+
+Remembering groups by their numbers is hard. For simple patterns it's doable, but for more complex ones counting parentheses is inconvenient. We have a much better option: give names to parentheses.
+
+That's done by putting `pattern:?
`, so the backreference is `pattern:\k
`:
+
+```js run
+let str = `He said: "She's the one!".`;
+
+*!*
+let regexp = /(?
['"])(.*?)\k
/g;
+*/!*
+
+alert( str.match(regexp) ); // "She's the one!"
+```
diff --git a/9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md b/9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md
new file mode 100644
index 000000000..e33f9cf2f
--- /dev/null
+++ b/9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md
@@ -0,0 +1,33 @@
+
+The first idea can be to list the languages with `|` in-between.
+
+But that doesn't work right:
+
+```js run
+let regexp = /Java|JavaScript|PHP|C|C\+\+/g;
+
+let str = "Java, JavaScript, PHP, C, C++";
+
+alert( str.match(regexp) ); // Java,Java,PHP,C,C
+```
+
+The regular expression engine looks for alternations one-by-one. That is: first it checks if we have `match:Java`, otherwise -- looks for `match:JavaScript` and so on.
+
+As a result, `match:JavaScript` can never be found, just because `match:Java` is checked first.
+
+The same with `match:C` and `match:C++`.
+
+There are two solutions for that problem:
+
+1. Change the order to check the longer match first: `pattern:JavaScript|Java|C\+\+|C|PHP`.
+2. Merge variants with the same start: `pattern:Java(Script)?|C(\+\+)?|PHP`.
+
+In action:
+
+```js run
+let regexp = /Java(Script)?|C(\+\+)?|PHP/g;
+
+let str = "Java, JavaScript, PHP, C, C++";
+
+alert( str.match(regexp) ); // Java,JavaScript,PHP,C,C++
+```
diff --git a/9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md b/9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md
new file mode 100644
index 000000000..e0f7af95c
--- /dev/null
+++ b/9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md
@@ -0,0 +1,11 @@
+# Find programming languages
+
+There are many programming languages, for instance Java, JavaScript, PHP, C, C++.
+
+Create a regexp that finds them in the string `subject:Java JavaScript PHP C++ C`:
+
+```js
+let regexp = /your regexp/g;
+
+alert("Java JavaScript PHP C++ C".match(regexp)); // Java JavaScript PHP C++ C
+```
diff --git a/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md b/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md
new file mode 100644
index 000000000..9b3fa1877
--- /dev/null
+++ b/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md
@@ -0,0 +1,23 @@
+
+Opening tag is `pattern:\[(b|url|quote)\]`.
+
+Then to find everything till the closing tag -- let's use the pattern `pattern:.*?` with flag `pattern:s` to match any character including the newline and then add a backreference to the closing tag.
+
+The full pattern: `pattern:\[(b|url|quote)\].*?\[/\1\]`.
+
+In action:
+
+```js run
+let regexp = /\[(b|url|quote)\].*?\[\/\1\]/gs;
+
+let str = `
+ [b]hello![/b]
+ [quote]
+ [url]http://google.com[/url]
+ [/quote]
+`;
+
+alert( str.match(regexp) ); // [b]hello![/b],[quote][url]http://google.com[/url][/quote]
+```
+
+Please note that besides escaping `pattern:[` and `pattern:]`, we had to escape a slash for the closing tag `pattern:[\/\1]`, because normally the slash closes the pattern.
diff --git a/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md b/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md
new file mode 100644
index 000000000..72d715afd
--- /dev/null
+++ b/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md
@@ -0,0 +1,48 @@
+# Find bbtag pairs
+
+A "bb-tag" looks like `[tag]...[/tag]`, where `tag` is one of: `b`, `url` or `quote`.
+
+For instance:
+```
+[b]text[/b]
+[url]http://google.com[/url]
+```
+
+BB-tags can be nested. But a tag can't be nested into itself, for instance:
+
+```
+Normal:
+[url] [b]http://google.com[/b] [/url]
+[quote] [b]text[/b] [/quote]
+
+Can't happen:
+[b][b]text[/b][/b]
+```
+
+Tags can contain line breaks, that's normal:
+
+```
+[quote]
+ [b]text[/b]
+[/quote]
+```
+
+Create a regexp to find all BB-tags with their contents.
+
+For instance:
+
+```js
+let regexp = /your regexp/flags;
+
+let str = "..[url]http://google.com[/url]..";
+alert( str.match(regexp) ); // [url]http://google.com[/url]
+```
+
+If tags are nested, then we need the outer tag (if we want we can continue the search in its content):
+
+```js
+let regexp = /your regexp/flags;
+
+let str = "..[url][b]http://google.com[/b][/url]..";
+alert( str.match(regexp) ); // [url][b]http://google.com[/b][/url]
+```
diff --git a/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md b/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md
new file mode 100644
index 000000000..5a007aee0
--- /dev/null
+++ b/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md
@@ -0,0 +1,17 @@
+The solution: `pattern:/"(\\.|[^"\\])*"/g`.
+
+Step by step:
+
+- First we look for an opening quote `pattern:"`
+- Then if we have a backslash `pattern:\\` (we technically have to double it in the pattern, because it is a special character, so that's a single backslash in fact), then any character is fine after it (a dot).
+- Otherwise we take any character except a quote (that would mean the end of the string) and a backslash (to prevent lonely backslashes, the backslash is only used with some other symbol after it): `pattern:[^"\\]`
+- ...And so on till the closing quote.
+
+In action:
+
+```js run
+let regexp = /"(\\.|[^"\\])*"/g;
+let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';
+
+alert( str.match(regexp) ); // "test me","Say \"Hello\"!","\\ \""
+```
diff --git a/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md b/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md
new file mode 100644
index 000000000..ad41d91b1
--- /dev/null
+++ b/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md
@@ -0,0 +1,32 @@
+# Find quoted strings
+
+Create a regexp to find strings in double quotes `subject:"..."`.
+
+The strings should support escaping, the same way as JavaScript strings do. For instance, quotes can be inserted as `subject:\"` a newline as `subject:\n`, and the slash itself as `subject:\\`.
+
+```js
+let str = "Just like \"here\".";
+```
+
+Please note, in particular, that an escaped quote `subject:\"` does not end a string.
+
+So we should search from one quote to the other ignoring escaped quotes on the way.
+
+That's the essential part of the task, otherwise it would be trivial.
+
+Examples of strings to match:
+```js
+.. *!*"test me"*/!* ..
+.. *!*"Say \"Hello\"!"*/!* ... (escaped quotes inside)
+.. *!*"\\"*/!* .. (double slash inside)
+.. *!*"\\ \""*/!* .. (double slash and an escaped quote inside)
+```
+
+In JavaScript we need to double the slashes to pass them right into the string, like this:
+
+```js run
+let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. ';
+
+// the in-memory string
+alert(str); // .. "test me" .. "Say \"Hello\"!" .. "\\ \"" ..
+```
diff --git a/9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md b/9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md
new file mode 100644
index 000000000..5d4ba8d96
--- /dev/null
+++ b/9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md
@@ -0,0 +1,16 @@
+
+The pattern start is obvious: `pattern: