├── .travis.yml
├── .gitattributes
├── test
├── index.html
└── tests.js
├── bower.json
├── .gitignore
├── .npmignore
├── package.json
├── LICENSE
├── re-build.min.js
├── doc
└── reference.md
├── re-build.min.map
├── readme.md
└── re-build.js
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "6.3"
4 | - "5.12"
5 | - "4.4"
6 | - "0.12"
7 | - "0.10"
8 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RE-Build tests
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "re-build",
3 | "main": "re-build.js",
4 | "homepage": "https://github.com/MaxArt2501/re-build",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/MaxArt2501/re-build.git"
8 | },
9 | "authors": [
10 | "Massimo Artizzu "
11 | ],
12 | "description": "Building regular expressions with natural language",
13 | "moduleType": [
14 | "amd",
15 | "globals",
16 | "node"
17 | ],
18 | "keywords": [
19 | "regex",
20 | "regular-expression",
21 | "regexp",
22 | "composition",
23 | "builder"
24 | ],
25 | "license": "MIT",
26 | "ignore": [
27 | "**/.*",
28 | "node_modules",
29 | "bower_components",
30 | "test",
31 | "tests"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
3 | # Windows image file caches
4 | Thumbs.db
5 | ehthumbs.db
6 |
7 | # Folder config file
8 | Desktop.ini
9 |
10 | # Recycle Bin used on file shares
11 | $RECYCLE.BIN/
12 |
13 | # Windows Installer files
14 | *.cab
15 | *.msi
16 | *.msm
17 | *.msp
18 |
19 | # Windows shortcuts
20 | *.lnk
21 |
22 | # =========================
23 | # Operating System Files
24 | # =========================
25 |
26 | # OSX
27 | # =========================
28 |
29 | .DS_Store
30 | .AppleDouble
31 | .LSOverride
32 |
33 | # Thumbnails
34 | ._*
35 |
36 | # Files that might appear on external disk
37 | .Spotlight-V100
38 | .Trashes
39 |
40 | # Directories potentially created on remote AFP share
41 | .AppleDB
42 | .AppleDesktop
43 | Network Trash Folder
44 | Temporary Items
45 | .apdisk
46 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .gitignore
2 | .gitattributes
3 | *.min.*
4 | bower.json
5 |
6 | # Windows image file caches
7 | Thumbs.db
8 | ehthumbs.db
9 |
10 | # Folder config file
11 | Desktop.ini
12 |
13 | # Recycle Bin used on file shares
14 | $RECYCLE.BIN/
15 |
16 | # Windows Installer files
17 | *.cab
18 | *.msi
19 | *.msm
20 | *.msp
21 |
22 | # Windows shortcuts
23 | *.lnk
24 |
25 | # =========================
26 | # Operating System Files
27 | # =========================
28 |
29 | # OSX
30 | # =========================
31 |
32 | .DS_Store
33 | .AppleDouble
34 | .LSOverride
35 |
36 | # Thumbnails
37 | ._*
38 |
39 | # Files that might appear on external disk
40 | .Spotlight-V100
41 | .Trashes
42 |
43 | # Directories potentially created on remote AFP share
44 | .AppleDB
45 | .AppleDesktop
46 | Network Trash Folder
47 | Temporary Items
48 | .apdisk
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "re-build",
3 | "version": "1.0.0",
4 | "description": "Building regular expressions with natural language",
5 | "main": "re-build.js",
6 | "directories": {
7 | "test": "test"
8 | },
9 | "scripts": {
10 | "test": "node_modules/.bin/mocha"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "https://github.com/MaxArt2501/re-build.git"
15 | },
16 | "keywords": [
17 | "regex",
18 | "regular-expression",
19 | "regexp",
20 | "composition",
21 | "builder"
22 | ],
23 | "author": {
24 | "name": "Massimo Artizzu",
25 | "email": "maxart.x@gmail.com"
26 | },
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/MaxArt2501/re-build/issues"
30 | },
31 | "homepage": "https://github.com/MaxArt2501/re-build",
32 | "devDependencies": {
33 | "mocha": "^2.2.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015-2017 Massimo Artizzu (MaxArt2501)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/re-build.min.js:
--------------------------------------------------------------------------------
1 | !function(n,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():n.RE=t()}(this,function(){"use strict";function n(n){return{not:function(){return f(l(v(c(this),{negate:!0}),this.source),n)}}}function t(n,t){if("number"==typeof t.min||"number"==typeof t.max){var e,r="number"==typeof t.min?t.min:0,i="number"==typeof t.max?t.max:1/0;e=r===i?1===r?"":"{"+r+"}":0===r?1===i?"?":i===1/0?"*":"{,"+i+"}":1===r?i===1/0?"+":"{1,"+i+"}":"{"+r+","+(i===1/0?"":i)+"}",e&&((n.length>2||2===n.length&&"\\"!==n[0])&&u(n)&&(n="(?:"+n+")"),n+=e+(t.lazy?"?":""))}return n}function e(n){var t={};for(var e in n)t[e]={value:n[e],writable:!1,configurable:!1};return t}function r(n,t){return y(n,e(t))}function i(n,t,e){return t.source=e||"",r(n,t)}function o(n){for(var t,e="",r=0;r=55296&&e<=56319){var r=n.charCodeAt(t+1);r>=56320&&r<=57343&&(e=65536+(e-55296<<10)+(r-56320))}return e},O={digit:["\\d","\\D"],alphaNumeric:["\\w","\\W"],whiteSpace:["\\s","\\S"],wordBoundary:["\\b","\\B",b+m],anyChar:[".","",m],tab:["\\t"],vTab:["\\v"],cReturn:["\\r"],newLine:["\\n"],formFeed:["\\f"],null:["\\0"],slash:["\\/"],backslash:["\\\\"],theStart:["^","",b+m],theEnd:["$","",b+m],ascii:[function(){for(var n="",t=0,e=0;t255)throw new RangeError("Invalid character code");n+="\\x"+("0"+r.toString(16)).slice(-2)}return n}],codePoint:[function(){for(var n="",t=this.unicode,e=0,r=0;e65535?2:1,r1114111)throw new RangeError("Invalid code point "+i);i>65535&&!t&&(i-=65536,n+="\\u"+(55296+(i>>10)).toString(16),i=56320+(1023&i)),n+="\\u"+(i>65535?"{"+i.toString(16)+"}":("000"+i.toString(16)).slice(-4))}return n}],control:[function(n){if(!/^[a-zA-Z]$/.test(n))throw new RangeError("Invalid control code");return"\\c"+n.toUpperCase()}],group:[function(){var n=j(arguments);return"(?:"!==n.slice(0,3)&&(n="(?:"+n+")"),n},0,m],capture:[function(){var n=j(arguments);return"(?:"===n.slice(0,3)?n="("+n.slice(3):"("!==n.charAt(0)&&(n="("+n+")"),n},0,m],reference:[function(n){if("number"!=typeof n||n!==n|0||n<0)throw new RangeError("Invalid back reference number");return"\\"+n},0,m]},E={withFlags:function(){return function(n){var t={};return"string"==typeof n?t={global:~n.indexOf("g"),ignoreCase:~n.indexOf("i"),multiline:~n.indexOf("m"),unicode:~n.indexOf("u"),sticky:~n.indexOf("y")}:"object"==typeof n&&n.forEach(function(n){t[n]=this[n]},n),f(r({},t),[C])}}};p.forEach(function(n){E[this[n]]=function(){var t={};return p.forEach(function(e){t[e]=e===n||this[e]},this),f(r({},t),[E,C])}},{global:"globally",ignoreCase:"anyCase",multiline:"fullText",unicode:"withUnicode",sticky:"stickily"});var C={matching:function(){return f(i(function(){return f(l(a(this),j(arguments)),[A])},a(this)),[I,M,n([z,M])])}},R={between:function(){return function(e,r){if(null!=e&&(isNaN(e)||Math.floor(e)!==+e||+e<0)||null!=r&&(isNaN(r)||Math.floor(r)!==+r||+r<0))throw new RangeError("Non-negative integer expected");if(null==e&&null==r)throw new RangeError("Range expected");var o=this,u=this.source,s=v(c(this),{min:e,max:r});return f(i(function(){return f(l(a(o),u+t(j(arguments),s)),[A])},s,u),[P,n([$])])}},exactly:function(){return function(n){return this.between(n,n)}},atLeast:function(){return function(n){return this.between(n,this.max)}},atMost:function(){return function(n){return this.between(this.min,n)}},anyAmountOf:function(){return this.between(0,1/0)},noneOrOne:function(){return this.between(0,1)},oneOrMore:function(){return this.between(1,1/0)}},k={lazily:function(){return f(l(v(c(this),{lazy:!0}),this.source),[R])}},A={then:function(){var t=a(this),e=this.source;return f(i(function(){return f(l(t,e+j(arguments)),[A])},t,e),[I,n([z])])},or:function(){var t=a(this),e=this.source+"|";return f(i(function(){return f(l(t,e+j(arguments)),[A])},t,e),[I,M,n([z,M])])}},I={},z={},S={},N={},P={},$={};d.keys(O).forEach(function(n){var e=O[n];"string"==typeof e[0]?(I[n]=function(){var n=this.source+t(this.negate&&e[1]||e[0],this);return f(l(a(this),n),[A])},e[1]&&(z[n]=I[n])):I[n]=function(){return function(){var n=this.source+t(e[0].apply(this,arguments),this);return f(l(a(this),n),[A])}},e[2]&b||(P[n]=I[n],e[1]&&($[n]=I[n])),e[2]&m||("string"==typeof e[0]?(S[n]=function(){var n=this.source,t=n.lastIndexOf("]");return f(l(a(this),n.slice(0,t)+(this.negate&&e[1]||e[0])+n.slice(t)),[A,F])},e[1]&&(N[n]=S[n])):S[n]=function(){return function(){var n=this.source,t=n.lastIndexOf("]");return f(l(a(this),n.slice(0,t)+e[0].apply(this,arguments)+n.slice(t)),[A,F])}})}),I.oneOf=z.oneOf=P.oneOf=$.oneOf=function(){var n=this,e=this.source;return f(i(function(){return f(l(a(n),e+t((n.negate?"[^":"[")+B(arguments)+"]",n)),[F,A])},c(this),e+t(this.negate?"[^]":"[]",this)),[S])},v(I,R,k),S.backspace=function(){var n=this.source,t=n.lastIndexOf("]");return f(l(a(this),n.slice(0,t)+"\\b"+n.slice(t)),[A,F])},S.range=function(){function n(n){if("string"==typeof n&&1===n.length)return B(n);if(h(n)&&(n=n.source,1===n.length||/^\\(?:[0btnvfr\/\\]|x[\da-fA-F]{2}|u[\da-fA-F]{4}|c[a-zA-Z])$/.test(n)))return n;throw new RangeError("Incorrect character range")}return function(t,e){t=n(t),e=n(e);var r=this.source,i=r.lastIndexOf("]");return f(l(a(this),r.slice(0,i)+t+"-"+e+r.slice(i)),[A,F])}},v(S,n([N]));var F={and:function(){var n=a(this),t=this.source;return f(i(function(){var e=t.lastIndexOf("]");return f(l(n,t.slice(0,e)+B(arguments)+t.slice(e)),[F,A])},n,t),[S])}},M={followedBy:function(){return function(){var n=t(j(arguments),this),e=this.negate?"(?!":"(?=";return n.slice(0,3)!==e&&(n=e+n+")"),f(l(a(this),(this.source||"")+n),[A])}}};v(A,M);var j=o.bind(/[\^\$\/\.\*\+\?\|\(\)\[\]\{\}\\]/g),B=o.bind(/[\^\/\[\]\\-]/g),L={valueOf:function(){return this.regex},toString:function(){return"/"+this.source+"/"+this.flags},test:function(n){return this.regex.test(n)},exec:function(n){return this.regex.exec(n)},replace:function(n,t){return n.replace(this.regex,t)},split:function(n){return n.split(this.regex)},search:function(n){return n.search(this.regex)}};return L.toRegExp=L.valueOf,f(i(g,{global:!1,ignoreCase:!1,multiline:!1,unicode:!1,sticky:!1}),[I,E,C]),g});
2 | //# sourceMappingURL=re-build.min.map
3 |
--------------------------------------------------------------------------------
/doc/reference.md:
--------------------------------------------------------------------------------
1 | RE-Build reference
2 | ==================
3 |
4 | # `RegExp` builders
5 |
6 | The object obtained from building a regular expressions *builders*. Builders are augmented with members and methods to build the regex further, but they're basically immutable objects as every call to extend the builder returns a *new* builder instance.
7 |
8 | ## Properties
9 |
10 | All the following properties are read-only.
11 |
12 | Type | Name | Description
13 | -------:|--------------|-------------
14 | string | `regex` | The regular expression defined by the builder. It's compiled the first time the property is requested, then cached
15 | string | `source` | The source of the underlying regular expression. Used to compile it
16 | string | `flags` | A string comprising the regex' flags. It may include one or more of the letters `"g"`, `"m"`, `"i"`, `"u"` or `"y"`
17 | boolean | `global` | The regex' `global` flag
18 | boolean | `ignoreCase` | The regex' `ignoreCase` flag
19 | boolean | `multiline` | The regex' `multiline` flag
20 | boolean | `unicode` | The regex' `unicode` flag
21 | boolean | `sticky` | The regex' `sticky` flag
22 |
23 | ## Methods
24 |
25 | Returns | Name | Description
26 | --------:|------------------|-------------------------
27 | `RegExp` | `toRegExp()` | Basically, returns the `regex` property
28 | `RegExp` | `valueOf()` | See above
29 | string | `toString()` | Returns a string representation
30 | boolean | `test(string)` | Uses the underlying regex to test a string. Short for `.regex.test(...)`
31 | array | `exec(string)` | Executes the underlying regex on a string. Short for `.regex.exec(...)`
32 | string | `replace(string, string/function)` | Uses the underlying regex to perform a regex-based replacement. Short for `string.replace(regex, ...)`
33 | array | `split(string)` | Uses the underlying regex to perform a regex-based string split. Short for `string.split(regex)`
34 | number | `search(string)` | Uses the underlying regex to perform a string search. Short for `string.search(regex)`
35 |
36 | # Building a regex
37 |
38 | Regex building begins from the he `RE` object returned by the module. You can obtain a *builder* every time you use "words" like `digit`, `then` and such. Some of these words act like functions (like `atLeast` and `codePoint`), some like properties (like `digit` and `theEnd`), some work as both.
39 |
40 | In this last case, if the word is not used as a function, additional words are expected to obtain a builder:
41 |
42 | ```js
43 | var foo = RE.matching.digit.then.alphaNumeric;
44 | ```
45 |
46 | Many words that can (or must) be used as functions accept a variable number of arguments, that can be either strings, or regular expressions, or builders, which are all appended to the source. Strings are backslash-escaped, while in the other cases the `source` property is then added *unescaped*:
47 |
48 | ```js
49 | var amount = RE.oneOrMore.digit.then(".").then.digit.then.digit,
50 | currency = /[$€£]/;
51 |
52 | var builder = RE.matching.theStart
53 | .then("Total: ", amount, currency)
54 | .then.theEnd;
55 | ```
56 |
57 | Other words that work as functions only usually accept other types of arguments.
58 |
59 | ## Flags
60 |
61 | The flags of a builder (and its underlying regular expression) can be set using words starting from the `RE` object. After one of these words, another flag word or `matching` must follow, with the exception of `withFlags` that must be followed by `matching` only.
62 |
63 | * **`globally`**
64 |
65 | Set the `global` flag on.
66 |
67 | * **`anyCase`**
68 |
69 | Set the `ignoreCase` flag on.
70 |
71 | * **`fullText`**
72 |
73 | Set the `multiline` flag on.
74 |
75 | * **`stickily`**
76 |
77 | Set the `sticky` flag on.
78 |
79 | * **`withUnicode`**
80 |
81 | Set the `unicode` flag on.
82 |
83 | * **`withFlags(flags)`**
84 |
85 | Set multiple flags. `flags` is expected to be a string containing letters in the set `"g"`, `"m"`, `"i"` and `"y"`.
86 |
87 | ## Conjunctions
88 |
89 | Conjunctions append additional blocks to the current source. They can follow any open or set block.
90 |
91 | * **`then`**
92 |
93 | Appends a block to the current source.
94 |
95 | * **`or`**
96 |
97 | Adds an alternative block (prefixed by the pipe `|` character in regular expressions).
98 |
99 | ## Open and set blocks
100 |
101 | These words can be used in both "open" sequences or inside character sets. They can be used after conjunction words, or a quantifier, or the `matching` word, or the `RE` object itself, or the `and` word joining blocks in character sets.
102 |
103 | * **`digit` / `not.digit`**
104 |
105 | A digit character (`\d`) or its negation (`\D`).
106 |
107 | * **`alphaNumeric` / `not.alphaNumeric`**
108 |
109 | An alphanumeric character plus the undescore (`\w`) or its negation (`\W`).
110 |
111 | * **`whiteSpace` / `not.whisteSpace`**
112 |
113 | A whitespace (`\s`) or its negation (`\W`).
114 | * **`cReturn`** `\r`
115 | * **`newLine`** `\n`
116 | * **`tab`** `\t`
117 | * **`vTab`** `\v`
118 | * **`formFeed`** `\f`
119 | * **`null`** `\0`
120 | * **`slash`** `\/`
121 | * **`backslash`** `\\`
122 | * **`ascii(code)`**
123 |
124 | An ASCII escape sequence (`\xhh`). `code` must be an integer between 0 and 255. It it then converted as two hexadecimal digits in the sequence.
125 |
126 | * **`codePoint(code, ...)`**
127 |
128 | An Unicode escape sequence (`\uhhhh`, or `\u{hhhhh}` with the `unicode` flag set and with a code not from the [Basic Multilingual Plane](https://en.wikipedia.org/wiki/Plane_(Unicode))). `code` must be an integer between 0 and 1114111 (`0x10ffff`) or a `RangeError` will be thrown; or it can be a string, whose code points will be converted in the corresponding Unicode escape sequence. Keep in mind that code points from astral planes, when the `unicode` flag is *not* set, are encoded in the corresponding surrogate code point pairs (e.g.: `"🍰"` will become `"\ud83c\udf70"`): *it is your duty* to wrap the pairs in a group if needed or, when it's not possible (for example, in a character range) using an adequate regex structure.
129 |
130 | * **`control(letter)`**
131 |
132 | A control sequence (`\cx`). `letter` must be a string of a single letter. It is then converted to uppercase in the sequence.
133 |
134 | ## Open-only blocks
135 |
136 | These words can be used in open block sequences only (which means, not inside character sets). They can be used after conjunction words, or a quantifier, or the `matching` word, or the `RE` object itself.
137 |
138 | * **`anyChar`**
139 |
140 | The universal character (`.`).
141 |
142 | * **`theStart` / `theEnd`**
143 |
144 | The string-start and string-end boundaries (`^` and `$`, respectively).
145 |
146 | * **`wordBoundary` / `not.wordBoundary`**
147 |
148 | A word boundary (`\b`) or its negation (`\B`).
149 |
150 | * **`oneOf` / `not.oneOf`**
151 |
152 | Appends a character set (`[...]` or `[^...]`, respectively). See the paragraph about [character sets](#character-sets).
153 |
154 | * **`group(...)`**
155 |
156 | Non-capturing group - `(?:...)`. Used as functions only. Arguments can be strings, regexes or builders.
157 |
158 | * **`capture(...)`**
159 |
160 | Capturing group - `(...)`. Used as functions only. Arguments can be strings, regexes or builders.
161 |
162 | * **`reference(number)`**
163 |
164 | Group backreference (`\number`). `number` should be a positive integer.
165 |
166 | ## Character sets
167 |
168 | Character sets are introduced by the `oneOf` word, and may include one or more blocks separated by the `and` word (e.g.: `RE.oneOf.digit.and("abcdef")`).
169 |
170 | These words can be used in character sets only:
171 |
172 | * **`range(start, end)`**
173 |
174 | Adds a character interval into the character set (`[...start-end...]`). `start` and `end` are supposed to be strings of single characters defining the boundaries of the character range; or they can be builders that define one single character, or character class usable in character ranges (which include: `ascii`, `unicode`, `control`, `newLine`, `cReturn`, `tab`, `vTab`, `formFeed`, `null`).
175 |
176 | * **`backspace`**
177 |
178 | The backspace character, `\b` (U+0008). Not to be confused with the word boundary, which can be used as an "open" block only.
179 |
180 | ## Quantifiers
181 |
182 | Quantifiers can follow conjunction words, or the `matching` word, or the `RE` object itself, and can precede any "open" block, with the exception of `wordBoundary`, `not.wordBoundary`, `theStart` and `theEnd`.
183 |
184 | They can be prefixed by `lazily` to define a lazy quantifier, instead of a greedy one.
185 |
186 | Quantifiers can be used as functions, and accept strings, regexes or builders as arguments. A convenient group wrap will be used if necessary:
187 |
188 | ```js
189 | var foo = RE.oneOrMore("a"); // /a+/
190 | var bar = RE.oneOrMore("abc"); // /(?:abc)+/
191 | ```
192 |
193 | * **`anyAmountOf`** `*`
194 | * **`oneOrMore`** `+`
195 | * **`noneOrOne`** `?`
196 | * **`atLeast(n)`**
197 |
198 | `n` must be a non-negative integer. If `n` is 0, a `*` is produced; if `n` is 1, then `+` is produced; else, the quantifier is `{n,}`.
199 |
200 | * **`atMost(n)`**
201 |
202 | `n` must be a non-negative integer. If `n` is 1, then `?` is produced; else, the quantifier is `{,n}`.
203 |
204 | * **`exactly(n)`**
205 |
206 | `n` must be a non-negative integer. If `n` is 1, then no quantifier is defined; else, the quantifier is `{n}`.
207 |
208 | * **`between(n, m)`**
209 |
210 | `n` and `m` must be non-negative integers. If the the values are adequate, the produced quantifier can be one of the above; otherwise, the quantifier is `{n,m}`.
211 |
212 |
213 | ## Look-aheads
214 |
215 | * **`followedBy(...)` / `not.followedBy(...)`**
216 |
217 | Appends a look-ahead (`(?=...)` or `(?!...)`, respectively). Used as functions only. Arguments can be strings, regexes or builders.
218 |
219 | Can follow any open block, or the `matching` word, or the `RE` object itself, or the `or` conjunction.
220 |
--------------------------------------------------------------------------------
/re-build.min.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["re-build.js"],"names":["root","factory","define","amd","exports","module","RE","this","negator","bundles","not","buildBuilder","createBuilder","extend","getSettings","negate","source","wrapSource","settings","min","max","quantifier","Infinity","length","hasManyBlocks","lazy","getConstMap","consts","map","name","value","writable","configurable","setConsts","dest","defineProps","initFunc","fnc","reparser","blocks","block","i","replace","RegExp","isBuilder","len","search","match","re","count","lastIndex","exec","index","object","props","settingList","sets","getFlags","flags","bundle","prop","defs","enumerable","get","getPropDefs","regex","global","ignoreCase","multiline","unicode","sticky","O","create","proto","isPrototypeOf","parseArgs","arguments","thenable","Object","assign","defineProperties","concat","NOQUANTIFY","NOSETS","getCodePointAt","codePointAt","string","code","charCodeAt","surr","names","digit","alphaNumeric","whiteSpace","wordBoundary","anyChar","tab","vTab","cReturn","newLine","formFeed","null","slash","backslash","theStart","theEnd","ascii","j","arg","RangeError","toString","slice","codePoint","control","letter","test","toUpperCase","group","capture","charAt","reference","number","flagger","withFlags","indexOf","forEach","f","matcher","flag","matching","openable","lookAheads","negable","quantifiers","between","isNaN","Math","floor","that","quantifiable","qntnegable","exactly","quantity","atLeast","atMost","anyAmountOf","noneOrOne","oneOrMore","lazinator","lazily","then","or","settable","setnegable","keys","def","apply","lastBracket","lastIndexOf","andCharSet","oneOf","parseSets","backspace","range","checkBoundary","bnd","start","end","and","followedBy","seq","bind","valueOf","subs","split","toRegExp"],"mappings":"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"}
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | RE-Build
2 | ========
3 |
4 | Build regular expressions with natural language.
5 |
6 | ## Introduction
7 |
8 | Have you ever dealt with complex regular expressions like the following one?
9 |
10 | ```js
11 | var ipMatch = /(?:(?:1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.){3}(?:1\d\d|2[0-4]\d|25[0-5]|[1-9]\d|\d)\b/;
12 | ```
13 |
14 | Using a meaningful variable name can help, writing comments helps even more, but what's always hard to understand is what the regular expression actually *does*: They're left as some sort of magic trick that it's never updated because their syntax is so obscure that even the authors themselves hardly fell like facing them again. Debugging a regular expression often means rewriting it from scratch.
15 |
16 | RE-Build's aim is to change that, converting the process of creating a regular expression to combining nice natural language expressions. The above regex would be composed as
17 |
18 | ```js
19 | var ipNumber = RE.group(
20 | RE ("1").then.digit.then.digit
21 | .or ("2").then.oneOf.range("0", "4").then.digit
22 | .or ("25").then.oneOf.range("0", "5")
23 | .or .oneOf.range("1", "9").then.digit
24 | .or .digit
25 | ),
26 |
27 | ipMatch = RE.matching.exactly(3).group( ipNumber.then(".") )
28 | .then(ipNumber).then.wordBoundary.regex;
29 | ```
30 |
31 | This approach is definitely more verbose, but also much clearer and less error prone.
32 |
33 | Another module for the same purpose is [VerbalExpressions](https://github.com/VerbalExpressions/JSVerbalExpressions), but it doesn't allow to build just *any* regular expression. RE-Build aims to fill that gap too.
34 |
35 | Remember, as a general rule, that RE-Build does *not* care if your environment doesn't support certain `RegExp` features (for example, the `sticky` flag or extended Unicode escaping sequences), as the corresponding source code will be generated anyway. Of course, you'll get an error trying to get a `RegExp` object out of it.
36 |
37 | ## Installation
38 |
39 | Via `npm`:
40 |
41 | ```bash
42 | npm install re-build
43 | ```
44 |
45 | Via `bower`:
46 |
47 | ```bash
48 | bower install re-build
49 | ```
50 |
51 | The package can be loaded as a CommonJS module (node.js, io.js), as an AMD module (RequireJS, ...) or as a standalone script:
52 |
53 | ```html
54 |
55 | ```
56 |
57 | ## Usage
58 |
59 | For a detailed documentation, check the [reference sheet](doc/reference.md). Keep in mind that RE-Build is a tool to help building, understanding and debugging regular expressions, and does *not* prevent one to create incorrect results.
60 |
61 | ### Basics
62 |
63 | The *core* point is the `RE` object (or whatever variable name you assigned to it), together with the `matching` method:
64 |
65 | ```js
66 | var RE = require("re-build");
67 | var builder = RE.matching("xyz");
68 | ```
69 |
70 | The output is *not*, however, a regular expression, but a regular expression *builder* that can be extended, or used as an extension for other builders. To get the corresponding regular expression, use the `regex` property or the `toRegExp()/valueOf()` methods.
71 |
72 | ```js
73 | var start = RE.matching.theStart.then(builder).toRegExp(); // /^xyz/
74 |
75 | var foo = RE.matching(builder).then.oneOrMore.digit.regex; // /xyz\d+/
76 | ```
77 |
78 | As you can see, you can put additional matching blocks using the `then` word, which is also a function that can take arguments as blocks to add too. The arguments can be strings (which are backslash-escaped), regular expressions or RE-Build'ers, whose `source` property is added to the builder *unescaped*.
79 |
80 | The `or` word has a similar meaning, but adds an alternative block to the source:
81 |
82 | ```js
83 | var hex = RE.matching.digit
84 | .or.oneOf.range("A", "F")
85 | .regex; // /\d|[A-F]/
86 | ```
87 |
88 | ### Regex builders are immutable
89 |
90 | Regular expression builders are immutable objects, meaning that when extending a builder we get a new builder instance:
91 |
92 | ```js
93 | var bld1 = RE.matching.digit;
94 | var bld2 = bld1.or.oneOf.range("A", "F");
95 | bld1 === bld2; // => false
96 | ```
97 |
98 | ### Special classes, aliases and escaping
99 |
100 | RE-Build uses specific names to address common regex character classes:
101 |
102 | Name | Result | Notes
103 | ---------------|--------------|--------------
104 | `digit` | `\d` | from `0` to `9`
105 | `alphaNumeric` | `\w` | digits, uppercase and lowercase letters and the underscore
106 | `whiteSpace` | `\s` | white space characters
107 | `wordBoundary` | `\b` |
108 | `anyChar` | `.` | universal matcher
109 | `theStart` | `^` |
110 | `theEnd` | `$` |
111 | `cReturn` | `\r` | carriage return
112 | `newLine` | `\n` |
113 | `tab` | `\t` |
114 | `vTab` | `\v` | vertical tab
115 | `formFeed` | `\f` |
116 | `null` | `\0` |
117 | `slash` | `\/` |
118 | `backslash` | `\\` |
119 | `backspace` | `\b` | can be used in character sets `[...]` *only*
120 |
121 | The first four names can be negated prefixing them with `not` to get the complementary meaning:
122 |
123 | * `not.digit` for `\D`;
124 | * `not.alphaNumeric` for `\W`;
125 | * `not.whiteSpace` for `\S`;
126 | * `not.wordBoundary` for `\B`.
127 |
128 | Single characters can be defined by escape sequences:
129 |
130 | Function | Result | Meaning
131 | ---------------|----------|-----------
132 | `ascii(n)` | `\xhh` | ASCII character corrisponding to `n`
133 | `codePoint(n)` | `\uhhhh` / `\u{hhhhhh}` | Unicode character corrisponding to `n`
134 | `control(a)` | `\ca` | Control sequence corrisponding to the letter `a`
135 |
136 | With the exception of `wordBoundary`, `theStart` and `theEnd`, all of the previous words can be used inside character sets (see after).
137 |
138 | ### Flags
139 |
140 | You can set the flags of the regex prefixing `matching` with one or more of the flagging options:
141 |
142 | * `globally` for a global regex;
143 | * `anyCase` for a case-insensitive regex;
144 | * `fullText` for a "multiline" regex (i.e., the dot '`.`' matches new line characters too);
145 | * `withUnicode` for a regex with extended Unicode support;
146 | * `stickily` for a "sticky" regex.
147 |
148 | Alternatively, you can set the flags with the `withFlags` method of the `RE` object.
149 |
150 | ```js
151 | // The following regexes are equivalent: /[a-f]/gi
152 | var foo = RE.globally.anyCase.matching.oneOf.range("a", "f").regex;
153 | var bar = RE.withFlags("gi").matching.oneOf.range("a", "f").regex;
154 | ```
155 |
156 | You can't change a regex builder's flags, as builders are immutable, but you can create a copy of a builder with different flags:
157 |
158 | ```js
159 | var foo = RE.matching.oneOrMore.alphaNumeric; // /\w+/
160 | var bar = RE.globally.matching(foo); // /\w+/g
161 | ```
162 |
163 | If you don't need flags set, as a shortened version you can remove the `matching` word:
164 |
165 | ```js
166 | // These are equivalent:
167 | RE.matching("abc").then.digit;
168 | RE("abc").then.digit;
169 | ```
170 |
171 | This becomes useful when defining the content of groups, character sets or look-aheads.
172 |
173 | ### Grouping
174 |
175 | Use the `group` word to define a non-capturing group, and `capture` for a capturing group:
176 |
177 | ```js
178 | var amount = RE.matching("$").then.capture(
179 | RE.oneOrMore.digit
180 | .then.noneOrOne.group(".", RE.oneOrMore.digit)
181 | ).regex;
182 | // /\$(\d+(?:\.\d+)?)/
183 | ```
184 |
185 | The `group` and `capture` words are function, and the resulting groups will embrace everything passed as arguments. Just like for `then` and `or`, arguments can be strings, regular expression or other RE-Build'ers.
186 |
187 | Backrefences for capturing groups are obtained using the `reference` function, passing the reference number:
188 |
189 | ```js
190 | var quote = RE.matching.capture( RE.oneOf("'\"") )
191 | .then.anyAmountOf.alphaNumeric
192 | .then.reference(1);
193 | // /(['"])\w*\1/
194 | ```
195 |
196 | ### Character sets
197 |
198 | Character sets (`[...]`) are introduced by the word `oneOf`. Several characters can be included separated by the word `and`. Additionally, one can include a character interval, using the function `range` and giving the initial and final character of the interval.
199 |
200 | Exclusive character sets can be obtained prefixing `oneOf` with the word `not`.
201 |
202 | ```js
203 | var hexColor = RE.matching("#").then.exactly(6)
204 | .oneOf.digit.and.range("a", "f").and.range("A", "F");
205 | // /#[\da-fA-F]{6}/
206 |
207 | var hours = RE.oneOf("01").then.digit.or("2").then.oneOf.range("0", "3");
208 | // /[01]\d|2[0-3]/
209 |
210 | var quote = RE.matching('"').then.oneOrMore.not.oneOf('"').then('"');
211 | // /"[^"]+"/
212 | ```
213 |
214 | ### Quantifiers
215 |
216 | Quantifiers can be defined prefixing the quantified block by one of these constructs:
217 |
218 | Construct | Result
219 | ----------------|---------
220 | `anyAmountOf` | `*`
221 | `oneOrMore` | `+`
222 | `noneOrOne` | `?`
223 | `atLeast(n)` | `{n,}`
224 | `atMost(n)` | `{,n}`
225 | `exactly(n)` | `{n}`
226 | `between(n, m)` | `{n,m}`
227 |
228 | Quantification is smart enough to translate constructs in their most compact form (e.g., `.atLeast(1)` becomes `+`, `.between(0, 1)` becomes `?` and so on).
229 |
230 | Lazy quantifiers can be obtained prefixing the word `lazily` prior to the quantifier.
231 |
232 | ```js
233 | var number = RE.oneOrMore.digit; // /\d+/
234 |
235 | var hexnumber = RE.exactly(2).oneOf.digit.and.range("a", "f");
236 | // /[\da-f]{2}/
237 |
238 | var macAddress = RE.anyCase.matching(hexnumber).then.exactly(5).group(
239 | RE("-").then(hexnumber)
240 | );
241 | // /[\da-f]{2}(?:-[\da-f]{2}){5}/i
242 |
243 | var quoteAlt = RE.matching.capture(RE.oneOf("'\""))
244 | .then.lazily.anyAmountOf.anyChar
245 | .then.reference(1);
246 | // /(['"]).*?\1/
247 | ```
248 |
249 | ### Look-aheads
250 |
251 | Look-aheads are introduced by the function `followedBy` (eventually prefixed by `not` for negative look-aheads).
252 |
253 | ```js
254 | var euro = RE.matching.oneOrMore.digit.followedBy("€");
255 | // /\d+(?=€)/
256 |
257 | var foo = RE("a").or.not.followedBy("b").then("c");
258 | // /a|(?!b)c/
259 | ```
260 |
261 | ## Compatibilty
262 |
263 | * Internet Explorer 9+
264 | * Firefox 4+
265 | * Safari 5+
266 | * Chrome
267 | * Opera 11.60+
268 | * node.js
269 |
270 | Basically, every Javascript environment that supports [`Object.defineProperties`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperties) should be fine.
271 |
272 | ## Tests
273 |
274 | The unit tests are built on top of [mocha](http://mochajs.org/). Once the package is installed, run `npm install` from the package's root directory in order to locally install mocha, then `npm run test` to execute the tests. Open [index.html](test/index.html) with a browser to perform the tests on the client side.
275 |
276 | If mocha is installed globally, served side tests can be run with just the command `mocha` from the package's root directory.
277 |
278 | ## To do
279 |
280 | * More natural language alternatives
281 | * Plurals, articles
282 | * CLI tool to translate regexes to and from RE-Build's syntax
283 | * More examples
284 | * Consider IE8 support
285 |
286 | ## License
287 |
288 | MIT @ Massimo Artizzu 2015-2016. See [LICENSE](LICENSE).
289 |
--------------------------------------------------------------------------------
/test/tests.js:
--------------------------------------------------------------------------------
1 | (function(root, tests) {
2 | if (typeof define === "function" && define.amd)
3 | define(["re-build"], tests);
4 | else if (typeof exports === "object")
5 | tests(require("../re-build.js"));
6 | else tests(root.RE);
7 | })(this, function(RE) {
8 | "use strict";
9 |
10 | function assert(value, expected) {
11 | if (value !== expected)
12 | throw new Error("Expected " + expected + ", computed " + value);
13 | }
14 |
15 | function assertBuilder(builder, expsource, expflags) {
16 | assertSource(builder, expsource);
17 | assertFlags(builder, expflags);
18 | }
19 |
20 | function assertSource(builder, expected) {
21 | var source = builder.regex.source;
22 | if (source !== expected)
23 | throw new Error("Expected source /" + expected + "/, computed /" + source + "/");
24 | }
25 |
26 | function assertFlags(builder, expected) {
27 | var restring = builder.regex.toString(),
28 | flags = restring.substring(restring.lastIndexOf("/") + 1);
29 | if (flags !== expected)
30 | throw new Error("Expected flags \"" + expected + "\", computed \"" + flags + "\"");
31 | }
32 |
33 | describe("RE-Build'ers", function() {
34 | it("Character sequences", function() {
35 | assertSource(RE.matching("abc"), "abc");
36 | assertSource(RE.matching("a", /b/, RE("c")), "abc");
37 | assertSource(RE.matching("a[b]"), "a\\[b\\]");
38 | assertSource(RE.matching("f(x) = {4.5}^\\3"), "f\\(x\\) = \\{4\\.5\\}\\^\\\\3");
39 | });
40 |
41 | it("Flags", function() {
42 | assertFlags(RE.matching("abc"), "");
43 | assertFlags(RE.globally.matching("abc"), "g");
44 | assertFlags(RE.anyCase.matching("abc"), "i");
45 | assertFlags(RE.fullText.matching("abc"), "m");
46 | if ("unicode" in /a/)
47 | assertFlags(RE.withUnicode.matching("abc"), "u");
48 | if ("sticky" in /a/)
49 | assertFlags(RE.stickily.matching("abc"), "y");
50 | assertFlags(RE.globally.anyCase.fullText.matching("abc"), "gim");
51 | assertFlags(RE.withFlags("img").matching("abc"), "gim");
52 |
53 | // Cloning a builder with different flags
54 | var builder = RE.matching.oneOrMore.digit,
55 | other = RE.globally.matching(builder);
56 | assertBuilder(other, builder.source, "g");
57 | });
58 |
59 | it("Character classes and aliases", function() {
60 | assertSource(RE.matching.digit, "\\d");
61 | assertSource(RE.matching.alphaNumeric, "\\w");
62 | assertSource(RE.matching.whiteSpace, "\\s");
63 | assertSource(RE.matching.wordBoundary, "\\b");
64 | assertSource(RE.matching.cReturn, "\\r");
65 | assertSource(RE.matching.newLine, "\\n");
66 | assertSource(RE.matching.tab, "\\t");
67 | assertSource(RE.matching.vTab, "\\v");
68 | assertSource(RE.matching.formFeed, "\\f");
69 | assertSource(RE.matching.slash, "\\/");
70 | assertSource(RE.matching.backslash, "\\\\");
71 | assertSource(RE.matching.anyChar, ".");
72 | });
73 |
74 | it("Negated character classes", function() {
75 | assertSource(RE.matching.not.digit, "\\D");
76 | assertSource(RE.matching.not.alphaNumeric, "\\W");
77 | assertSource(RE.matching.not.whiteSpace, "\\S");
78 | assertSource(RE.matching.not.wordBoundary, "\\B");
79 | });
80 |
81 | it("Character escaping", function() {
82 | assertSource(RE.matching.control("M"), "\\cM");
83 | assertSource(RE.matching.ascii(160), "\\xa0");
84 | assertSource(RE.matching.ascii("ABC"), "\\x41\\x42\\x43");
85 | assertSource(RE.matching.codePoint(0x2661), "\\u2661");
86 | assertSource(RE.matching.codePoint("♡"), "\\u2661");
87 | assertSource(RE.matching.codePoint(0x1f370), "\\ud83c\\udf70");
88 | assertSource(RE.matching.codePoint("I♡🍰"), "\\u0049\\u2661\\ud83c\\udf70");
89 | if ("unicode" in /a/) {
90 | assertSource(RE.withUnicode.matching.codePoint(0x1f370), "\\u{1f370}");
91 | assertSource(RE.withUnicode.matching.codePoint("I♡🍰"), "\\u0049\\u2661\\u{1f370}");
92 | }
93 |
94 | try {
95 | RE.matching.control("1");
96 | throw new Error("Expected RangeError");
97 | } catch (e) {
98 | assert(e.name, "RangeError");
99 | }
100 | try {
101 | RE.matching.ascii("♡");
102 | throw new Error("Expected RangeError");
103 | } catch (e) {
104 | assert(e.name, "RangeError");
105 | }
106 | try {
107 | RE.matching.codePoint(0x200000);
108 | throw new Error("Expected RangeError");
109 | } catch (e) {
110 | assert(e.name, "RangeError");
111 | }
112 | });
113 |
114 | it("Concatenation of sequences and blocks", function() {
115 | assertSource(RE.matching("abc").then("de"), "abcde");
116 | assertSource(RE.matching("abc").then.digit, "abc\\d");
117 | assertSource(RE.matching("abc").then.not.digit, "abc\\D");
118 | assertSource(RE.matching.digit.then.digit, "\\d\\d");
119 | assertSource(RE.matching("ab").then("cd").then("ef"), "abcdef");
120 | assert("backspace" in RE.matching, false);
121 | });
122 |
123 | it("Character sets", function() {
124 | assertSource(RE.matching.oneOf("abc"), "[abc]");
125 | assertSource(RE.matching.oneOf("a-z"), "[a\\-z]");
126 | assertSource(RE.matching.oneOf("^[]"), "[\\^\\[\\]]");
127 | assertSource(RE.matching.oneOf("abc").and("de"), "[abcde]");
128 | assertSource(RE.matching.oneOf.digit.and.whiteSpace, "[\\d\\s]");
129 | assertSource(RE.matching.oneOf.digit, "[\\d]");
130 | assertSource(RE.matching.oneOf.ascii(240).and.codePoint(0xca0), "[\\xf0\\u0ca0]");
131 | assertSource(RE.matching.oneOf.backspace.and.newLine.and("abc"), "[\\b\\nabc]");
132 | assertSource(RE.matching.not.oneOf("abc"), "[^abc]");
133 | assertSource(RE.matching.not.oneOf.not.digit, "[^\\D]");
134 | assertSource(RE.matching.oneOf("abc").then.digit, "[abc]\\d");
135 | assertSource(RE.matching.oneOf.not.digit.then.digit, "[\\D]\\d");
136 | assert("anyChar" in RE.matching.oneOf, false);
137 | assert("wordBoundary" in RE.matching.oneOf, false);
138 | assert("theStart" in RE.matching.oneOf, false);
139 | });
140 |
141 | it("Character set ranges", function() {
142 | assertSource(RE.matching.oneOf.range("a", "z"), "[a-z]");
143 | assertSource(RE.matching.oneOf.range("a", "z").and.range("0", "9"), "[a-z0-9]");
144 | assertSource(RE.matching.oneOf.range(RE.ascii(128), RE.ascii(255)), "[\\x80-\\xff]");
145 | assertSource(RE.matching.oneOf.range("z", RE.codePoint(0x2001)), "[z-\\u2001]");
146 | assertSource(RE.matching.oneOf.range(RE.null, RE.control("M")), "[\\0-\\cM]");
147 | assertSource(RE.matching.oneOf.range(RE.tab, RE.cReturn), "[\\t-\\r]");
148 | assertSource(RE.matching.oneOf.range(RE.newLine, RE.vTab), "[\\n-\\v]");
149 | assertSource(RE.matching.oneOf.range(RE.slash, RE.backslash), "[\\/-\\\\]");
150 | });
151 |
152 | it("String boundaries", function() {
153 | assertSource(RE.matching.theStart.then.digit, "^\\d");
154 | assertSource(RE.matching("abc").then.theEnd, "abc$");
155 | });
156 |
157 | it("Capturing and non-capturing groups", function() {
158 | assertSource(RE.matching.group("abc"), "(?:abc)");
159 | assertSource(RE.matching.group(RE.digit), "(?:\\d)");
160 | assertSource(RE.matching.group("a", /b/, RE("c")), "(?:abc)");
161 | assertSource(RE.matching.capture("abc"), "(abc)");
162 | assertSource(RE.matching.capture(RE.digit), "(\\d)");
163 | assertSource(RE.matching.capture("a", /b/, RE("c")), "(abc)");
164 | });
165 |
166 | it("Backreferences", function() {
167 | assertSource(RE.matching.capture(RE.oneOrMore.digit).then(" - ").then.reference(1).then(" = 0"), "(\\d+) - \\1 = 0");
168 | assertSource(RE.matching.capture(RE.oneOf("'\"")).then.oneOrMore.alphaNumeric.then.reference(1), "(['\"])\\w+\\1");
169 | try {
170 | RE.matching.reference(-1);
171 | throw new Error("Expected RangeError");
172 | } catch (e) {
173 | assert(e.name, "RangeError");
174 | }
175 | });
176 |
177 | it("Greedy quantifiers", function() {
178 | assertSource(RE.matching.noneOrOne("a"), "a?");
179 | assertSource(RE.matching.anyAmountOf("a"), "a*");
180 | assertSource(RE.matching.oneOrMore("a"), "a+");
181 | assertSource(RE.matching.atLeast(0)("a"), "a*");
182 | assertSource(RE.matching.atLeast(1)("a"), "a+");
183 | assertSource(RE.matching.atLeast(2)("a"), "a{2,}");
184 | assertSource(RE.matching.atMost(0)("a"), "a{0}");
185 | assertSource(RE.matching.atMost(1)("a"), "a?");
186 | assertSource(RE.matching.atMost(2)("a"), "a{,2}");
187 | assertSource(RE.matching.exactly(1)("a"), "a");
188 | assertSource(RE.matching.exactly(4)("a"), "a{4}");
189 | assertSource(RE.matching.between(0)("a"), "a*");
190 | assertSource(RE.matching.between(1)("a"), "a+");
191 | assertSource(RE.matching.between(0, 1)("a"), "a?");
192 | assertSource(RE.matching.between(null, 1)("a"), "a?");
193 | assertSource(RE.matching.between(2, 4)("a"), "a{2,4}");
194 | assertSource(RE.matching.between(1, 1)("a"), "a");
195 | assertSource(RE.matching.between(3, 3)("a"), "a{3}");
196 | assertSource(RE.matching.between(3)("a"), "a{3,}");
197 | assertSource(RE.matching.between(null, 3)("a"), "a{,3}");
198 |
199 | assertSource(RE.matching.oneOrMore("abc"), "(?:abc)+");
200 | assertSource(RE.matching.oneOrMore.digit, "\\d+");
201 | assertSource(RE.matching.oneOrMore.oneOf("abc"), "[abc]+");
202 | assertSource(RE.matching.oneOrMore.oneOf.range("a", "z"), "[a-z]+");
203 | assertSource(RE.matching.oneOrMore.oneOf.range("a", "z").and.digit, "[a-z\\d]+");
204 | assertSource(RE.matching.oneOrMore.group("abc"), "(?:abc)+");
205 | assertSource(RE.matching.oneOrMore.capture("abc"), "(abc)+");
206 | assertSource(RE.matching.oneOrMore.capture("a)(b"), "(a\\)\\(b)+");
207 | assertSource(RE.matching.oneOrMore(/(ab)(cd)/), "(?:(ab)(cd))+");
208 | assertSource(RE.matching.oneOrMore(/(ab(cd))/), "(ab(cd))+");
209 |
210 | try {
211 | RE.matching.exactly(1.5)("a");
212 | throw new Error("Expected RangeError");
213 | } catch (e) {
214 | assert(e.name, "RangeError");
215 | }
216 | try {
217 | RE.matching.exactly(-1)("a");
218 | throw new Error("Expected RangeError");
219 | } catch (e) {
220 | assert(e.name, "RangeError");
221 | }
222 | try {
223 | RE.matching.between()("a");
224 | throw new Error("Expected RangeError");
225 | } catch (e) {
226 | assert(e.name, "RangeError");
227 | }
228 |
229 | assert("digit" in RE.matching.exactly(1), true);
230 | assert("slash" in RE.matching.exactly(1), true);
231 | assert("oneOf" in RE.matching.exactly(1), true);
232 | assert("ascii" in RE.matching.exactly(1), true);
233 | assert("group" in RE.matching.exactly(1), true);
234 | assert("wordBoundary" in RE.matching.exactly(1), false);
235 | assert("theStart" in RE.matching.exactly(1), false);
236 | });
237 |
238 | it("Lazy quantifiers", function() {
239 | assertSource(RE.matching.lazily.noneOrOne("a"), "a??");
240 | assertSource(RE.matching.lazily.anyAmountOf("a"), "a*?");
241 | assertSource(RE.matching.lazily.oneOrMore("a"), "a+?");
242 | assertSource(RE.matching.lazily.atLeast(0)("a"), "a*?");
243 | assertSource(RE.matching.lazily.atLeast(1)("a"), "a+?");
244 | assertSource(RE.matching.lazily.atLeast(2)("a"), "a{2,}?");
245 | assertSource(RE.matching.lazily.atMost(1)("a"), "a??");
246 | assertSource(RE.matching.lazily.atMost(2)("a"), "a{,2}?");
247 | assertSource(RE.matching.lazily.exactly(4)("a"), "a{4}?");
248 | assertSource(RE.matching.lazily.between(2, 4)("a"), "a{2,4}?");
249 |
250 | assertSource(RE.matching.lazily.oneOrMore("abc"), "(?:abc)+?");
251 | assertSource(RE.matching.lazily.oneOrMore.digit, "\\d+?");
252 | assertSource(RE.matching.lazily.oneOrMore.oneOf("abc"), "[abc]+?");
253 | assertSource(RE.matching.lazily.oneOrMore.oneOf.range("a", "z"), "[a-z]+?");
254 | assertSource(RE.matching.lazily.oneOrMore.oneOf.range("a", "z").and.digit, "[a-z\\d]+?");
255 | assertSource(RE.matching.lazily.oneOrMore.group("abc"), "(?:abc)+?");
256 | assertSource(RE.matching.lazily.oneOrMore.capture("abc"), "(abc)+?");
257 | assertSource(RE.matching.lazily.oneOrMore.capture("a)(b"), "(a\\)\\(b)+?");
258 | assertSource(RE.matching.lazily.oneOrMore(/(ab)(cd)/), "(?:(ab)(cd))+?");
259 | assertSource(RE.matching.lazily.oneOrMore(/(ab(cd))/), "(ab(cd))+?");
260 | });
261 |
262 | it("Look-aheads", function() {
263 | assertSource(RE.matching.followedBy("abc"), "(?=abc)");
264 | assertSource(RE.matching.not.followedBy("abc"), "(?!abc)");
265 | assertSource(RE.matching("0").or.not.followedBy("b").then.alphaNumeric, "0|(?!b)\\w");
266 | assertSource(RE.matching.oneOrMore.alphaNumeric.followedBy(","), "\\w+(?=,)");
267 | });
268 |
269 | it("Complex examples", function() {
270 | // Matching time, format hh:mm:ss
271 | assertSource(
272 | RE.matching.theStart.then.group(
273 | RE.oneOf("01").then.digit
274 | .or("2").then.oneOf.range("0", "3")
275 | ).then.exactly(2).group(
276 | RE(":").then.oneOf.range("0", "5").then.digit
277 | ).then.theEnd,
278 | "^(?:[01]\\d|2[0-3])(?::[0-5]\\d){2}$"
279 | );
280 |
281 | // Matching HTML/XML attributes (sort of)
282 | assertBuilder(
283 | RE.globally.anyCase.matching.whiteSpace
284 | .then.oneOf.range("a", "z").then.anyAmountOf.alphaNumeric
285 | .then.anyAmountOf.whiteSpace.then("=").then.anyAmountOf.whiteSpace
286 | .then.capture(RE.oneOf("'\""))
287 | .then.lazily.anyAmountOf.anyChar.then.reference(1),
288 | "\\s[a-z]\\w*\\s*=\\s*(['\"]).*?\\1", "gi"
289 | );
290 |
291 | // Matching CSS colors
292 | var spaces = RE.anyAmountOf(" "),
293 | comma = RE(spaces).then(",").then(spaces),
294 | upTo255 = RE.group(RE("25").then.oneOf.range("0", "5")
295 | .or("2").then.oneOf.range("0", "4").then.digit
296 | .or("1").then.digit.then.digit
297 | .or.oneOf.range("1", "9").then.noneOrOne.digit
298 | .or("0")),
299 | upTo360 = RE.group(RE("360")
300 | .or("3").then.oneOf.range("0", "5").then.digit
301 | .or.oneOf("12").then.digit.then.digit
302 | .or.oneOf.range("1", "9").then.noneOrOne.digit
303 | .or("0")),
304 | percentage = RE.group(RE("100")
305 | .or.oneOf.range("1", "9").then.noneOrOne.digit
306 | .or("0")).then("%"),
307 | opacity = RE.group(RE.noneOrOne("0").then(".").then.oneOrMore.digit
308 | .or.oneOf("01"));
309 | assertBuilder(
310 | RE.anyCase.matching // #xxxxxx or #xxx
311 | .not.wordBoundary.then("#")
312 | .then.between(1, 2).group(
313 | RE.exactly(3)(RE.oneOf.digit.and.range("a", "f"))
314 | ).then.wordBoundary
315 | .or // rgb(x, y, z)
316 | .wordBoundary.then("rgb(").then(spaces).then.exactly(2).group(
317 | RE(upTo255).then(comma)
318 | ).then(upTo255).then(spaces).then(")")
319 | .or // rgba(x, y, z, k)
320 | .wordBoundary.then("rgba(").then(spaces).then.exactly(3).group(
321 | RE(upTo255).then(comma)
322 | ).then(opacity).then(spaces).then(")")
323 | .or // hsl(x, y, z)
324 | .wordBoundary.then("hsl(").then(spaces).then(upTo360).then(comma)
325 | .then(percentage).then(comma).then(percentage)
326 | .then(spaces).then(")")
327 | .or // hsla(x, y, z, k)
328 | .wordBoundary.then("hsla(").then(spaces).then(upTo360).then(comma)
329 | .then(percentage).then(comma).then(percentage).then(comma)
330 | .then(opacity).then(spaces).then(")"),
331 | "\\B#(?:[\\da-f]{3}){1,2}\\b|\\brgb\\( *(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d?|0) *, *){2}(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d?|0) *\\)|\\brgba\\( *(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]\\d?|0) *, *){3}(?:0?\\.\\d+|[01]) *\\)|\\bhsl\\( *(?:360|3[0-5]\\d|[12]\\d\\d|[1-9]\\d?|0) *, *(?:100|[1-9]\\d?|0)% *, *(?:100|[1-9]\\d?|0)% *\\)|\\bhsla\\( *(?:360|3[0-5]\\d|[12]\\d\\d|[1-9]\\d?|0) *, *(?:100|[1-9]\\d?|0)% *, *(?:100|[1-9]\\d?|0)% *, *(?:0?\\.\\d+|[01]) *\\)", "i"
332 | );
333 | });
334 | });
335 |
336 | describe("Builder prototype", function() {
337 | it("test", function() {
338 | var builder = RE.matching.oneOrMore.digit;
339 | assert(builder.test("We're living in " + new Date().getFullYear()), true);
340 | assert(builder.test("Hello, world!"), false);
341 | });
342 |
343 | it("exec", function() {
344 | var builder = RE.matching.oneOrMore.digit,
345 | result = builder.exec("The answer is 42.");
346 | assert(result instanceof Array, true);
347 | assert(result[0].length, 2);
348 | assert(result.index, 14);
349 | });
350 |
351 | it("replace", function() {
352 | var reverseDate = RE.matching.capture(RE.exactly(4).digit).then("-")
353 | .then.capture(RE.exactly(2).digit).then("-")
354 | .then.capture(RE.exactly(2).digit);
355 | assert(reverseDate.replace("2015-04-20", "$3/$2/$1"), "20/04/2015");
356 |
357 | var capital = RE.globally.matching.wordBoundary.then.oneOrMore.alphaNumeric;
358 | assert(capital.replace("hello, world!", function(m) {
359 | return m.charAt(0).toUpperCase() + m.substring(1);
360 | }), "Hello, World!");
361 | });
362 |
363 | it("split", function() {
364 | var space = RE.oneOrMore.whiteSpace;
365 | assert(space.split("Lorem ipsum dolor sit amet").length, 5);
366 | assert(space.split("RE-Build").length, 1);
367 | });
368 |
369 | it("search", function() {
370 | var builder = RE.matching.oneOrMore.digit;
371 | assert(builder.search("The answer is 42."), 14);
372 | assert(builder.search("Hello, world!"), -1);
373 | });
374 | });
375 |
376 | });
377 |
--------------------------------------------------------------------------------
/re-build.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * RE-Build - v1.0.0
3 | * by Massimo Artizzu (MaxArt2501)
4 | *
5 | * https://github.com/MaxArt2501/re-build
6 | *
7 | * Licensed under the MIT License
8 | * See LICENSE for details
9 | */
10 |
11 | (function (root, factory) {
12 | if (typeof define === "function" && define.amd) {
13 | // AMD. Register as an anonymous module.
14 | define([], factory);
15 | } else if (typeof exports === "object") {
16 | // Node. Does not work with strict CommonJS, but
17 | // only CommonJS-like environments that support module.exports,
18 | // like Node.
19 | module.exports = factory();
20 | } else {
21 | // Browser globals (root is window)
22 | root.RE = factory();
23 | }
24 | })(this, function() {
25 | "use strict";
26 |
27 | var O = Object;
28 | var extend = O.assign || function(dest) {
29 | for (var i = 1, source, prop; i < arguments.length;) {
30 | source = arguments[i++];
31 | if (source)
32 | for (var prop in source)
33 | dest[prop] = source[prop];
34 | }
35 |
36 | return dest;
37 | };
38 | var defineProps = O.defineProperties;
39 |
40 | var flags = [ "global", "ignoreCase", "multiline", "unicode", "sticky" ],
41 | settingList = flags.concat([ "min", "max", "lazy", "negate" ]);
42 |
43 | var /** @const */ NOQUANTIFY = 1,
44 | /** @const */ NOSETS = 2;
45 |
46 | var getCodePointAt = "".codePointAt ? function(string, index) {
47 | return string.codePointAt(index);
48 | } : function(string, index) {
49 | var code = string.charCodeAt(index);
50 | if (code >= 0xd800 && code <= 0xdbff) {
51 | var surr = string.charCodeAt(index + 1);
52 | if (surr >= 0xdc00 && surr <= 0xdfff)
53 | code = 0x10000 + ((code - 0xd800) << 10) + (surr - 0xdc00);
54 | }
55 |
56 | return code;
57 | };
58 |
59 | var names = {
60 | digit: ["\\d", "\\D"],
61 | alphaNumeric: [ "\\w", "\\W"],
62 | whiteSpace: ["\\s", "\\S"],
63 | wordBoundary: ["\\b", "\\B", NOQUANTIFY + NOSETS],
64 | anyChar: [".", "", NOSETS],
65 |
66 | tab: ["\\t"],
67 | vTab: ["\\v"],
68 | cReturn: ["\\r"],
69 | newLine: ["\\n"],
70 | formFeed: ["\\f"],
71 | null: ["\\0"],
72 | slash: ["\\/"],
73 | backslash: ["\\\\"],
74 |
75 | theStart: ["^", "", NOQUANTIFY + NOSETS],
76 | theEnd: ["$", "", NOQUANTIFY + NOSETS],
77 |
78 | ascii: [function() {
79 | var source = "";
80 | for (var i = 0, j = 0; i < arguments.length; i++) {
81 | var arg = arguments[i], code;
82 | if (typeof arg === "string") {
83 | code = arg.charCodeAt(j++);
84 | if (j < arg.length) i--;
85 | else j = 0;
86 | } else code = arg|0;
87 | if (code < 0 || code > 255)
88 | throw new RangeError("Invalid character code");
89 |
90 | source += "\\x" + ("0" + code.toString(16)).slice(-2);
91 | }
92 |
93 | return source;
94 | }],
95 | codePoint: [function() {
96 | var source = "",
97 | unicode = this.unicode;
98 |
99 | for (var i = 0, j = 0; i < arguments.length; i++) {
100 | var arg = arguments[i], code;
101 | if (typeof arg === "string") {
102 | code = unicode ? getCodePointAt(arg, j) : arg.charCodeAt(j);
103 | j += code > 0xffff ? 2 : 1;
104 | if (j < arg.length) i--;
105 | else j = 0;
106 | } else code = arg|0;
107 |
108 | if (code < 0 || code > 0x10ffff)
109 | throw new RangeError("Invalid code point " + code);
110 | if (code > 0xffff && !unicode) {
111 | // Computing surrogate code points
112 | code -= 0x10000;
113 | // First surrogate is immediately added to the source
114 | source += "\\u" + (0xd800 + (code >> 10)).toString(16);
115 | code = 0xdc00 + (code & 0x3ff);
116 | }
117 |
118 | source += "\\u" + (code > 0xffff ? "{" + code.toString(16) + "}" : ("000" + code.toString(16)).slice(-4));
119 | }
120 |
121 | return source;
122 | }],
123 | control: [function(letter) {
124 | if (!/^[a-zA-Z]$/.test(letter))
125 | throw new RangeError("Invalid control code");
126 |
127 | return "\\c" + letter.toUpperCase();
128 | }],
129 |
130 | group: [function() {
131 | var source = parseArgs(arguments);
132 | if (source.slice(0, 3) !== "(?:")
133 | source = "(?:" + source + ")";
134 |
135 | return source;
136 | }, 0, NOSETS],
137 | capture: [function() {
138 | var source = parseArgs(arguments);
139 | if (source.slice(0, 3) === "(?:")
140 | source = "(" + source.slice(3);
141 | else if (source.charAt(0) !== "(")
142 | source = "(" + source + ")";
143 |
144 | return source;
145 | }, 0, NOSETS],
146 | reference: [function(number) {
147 | if (typeof number !== "number" || number !== number | 0 || number < 0)
148 | throw new RangeError("Invalid back reference number");
149 |
150 | return "\\" + number;
151 | }, 0, NOSETS]
152 | };
153 |
154 | var flagger = {
155 | withFlags: function() {
156 | return function(flags) {
157 | var consts = {};
158 | if (typeof flags === "string")
159 | consts = {
160 | global: ~flags.indexOf("g"),
161 | ignoreCase: ~flags.indexOf("i"),
162 | multiline: ~flags.indexOf("m"),
163 | unicode: ~flags.indexOf("u"),
164 | sticky: ~flags.indexOf("y")
165 | }
166 | else if (typeof flags === "object")
167 | flags.forEach(function(f) { consts[f] = this[f]; }, flags);
168 |
169 | return buildBuilder(setConsts({}, consts), [ matcher ]);
170 | };
171 | }
172 | };
173 | flags.forEach(function(flag) {
174 | flagger[this[flag]] = function() {
175 | var consts = {};
176 | flags.forEach(function(f) { consts[f] = f === flag || this[f]; }, this);
177 |
178 | return buildBuilder(setConsts({}, consts), [ flagger, matcher ]);
179 | };
180 | }, {
181 | global: "globally",
182 | ignoreCase: "anyCase",
183 | multiline: "fullText",
184 | unicode: "withUnicode",
185 | sticky: "stickily"
186 | });
187 |
188 | var matcher = {
189 | matching: function() {
190 | return buildBuilder(initFunc(function() {
191 | return buildBuilder(createBuilder(getFlags(this), parseArgs(arguments)), [ thenable ]);
192 | }, getFlags(this)), [ openable, lookAheads, negator([ negable, lookAheads ]) ]);
193 | }
194 | };
195 |
196 | var quantifiers = {
197 | between: function() {
198 | return function(min, max) {
199 | if (min != null && (isNaN(min) || Math.floor(min) !== +min || +min < 0)
200 | || max != null && (isNaN(max) || Math.floor(max) !== +max || +max < 0))
201 | throw new RangeError("Non-negative integer expected");
202 |
203 | if (min == null && max == null)
204 | throw new RangeError("Range expected");
205 |
206 | var that = this,
207 | source = this.source,
208 | settings = extend(getSettings(this), { min: min, max: max });
209 |
210 | return buildBuilder(initFunc(function() {
211 | return buildBuilder(createBuilder(getFlags(that),
212 | source + wrapSource(parseArgs(arguments), settings)), [ thenable ]);
213 | }, settings, source), [ quantifiable, negator([ qntnegable ]) ]);
214 | };
215 | },
216 | exactly: function() {
217 | return function(quantity) {
218 | return this.between(quantity, quantity);
219 | };
220 | },
221 | atLeast: function() {
222 | return function(quantity) {
223 | return this.between(quantity, this.max);
224 | };
225 | },
226 | atMost: function() {
227 | return function(quantity) {
228 | return this.between(this.min, quantity);
229 | };
230 | },
231 | anyAmountOf: function() {
232 | return this.between(0, Infinity);
233 | },
234 | noneOrOne: function() {
235 | return this.between(0, 1);
236 | },
237 | oneOrMore: function() {
238 | return this.between(1, Infinity);
239 | }
240 | };
241 |
242 | var lazinator = {
243 | lazily: function() {
244 | return buildBuilder(createBuilder(extend(getSettings(this), { lazy: true }), this.source), [ quantifiers ]);
245 | }
246 | };
247 |
248 | var thenable = {
249 | then: function() {
250 | var settings = getFlags(this),
251 | source = this.source;
252 |
253 | return buildBuilder(initFunc(function() {
254 | return buildBuilder(createBuilder(settings,
255 | source + parseArgs(arguments)), [ thenable ]);
256 | }, settings, source), [ openable, negator([ negable ]) ]);
257 | },
258 | or: function() {
259 | var settings = getFlags(this),
260 | source = this.source + "|";
261 |
262 | return buildBuilder(initFunc(function() {
263 | return buildBuilder(createBuilder(settings,
264 | source + parseArgs(arguments)), [ thenable ]);
265 | }, settings, source), [ openable, lookAheads, negator([ negable, lookAheads ]) ]);
266 | }
267 | };
268 |
269 | var openable = {}, negable = {},
270 | settable = {}, setnegable = {},
271 | quantifiable = {}, qntnegable = {};
272 |
273 | O.keys(names).forEach(function(name) {
274 | var def = names[name];
275 |
276 | if (typeof def[0] === "string") {
277 | openable[name] = function() {
278 | var source = this.source + wrapSource(this.negate && def[1] || def[0], this);
279 | return buildBuilder(createBuilder(getFlags(this), source), [ thenable ]);
280 | };
281 | if (def[1]) negable[name] = openable[name];
282 | } else
283 | openable[name] = function() {
284 | return function() {
285 | var source = this.source + wrapSource(def[0].apply(this, arguments), this);
286 | return buildBuilder(createBuilder(getFlags(this), source), [ thenable ]);
287 | }
288 | };
289 | if (!(def[2] & NOQUANTIFY)) {
290 | quantifiable[name] = openable[name];
291 | if (def[1]) qntnegable[name] = openable[name];
292 | }
293 |
294 | if (!(def[2] & NOSETS)) {
295 | if (typeof def[0] === "string") {
296 | settable[name] = function() {
297 | var source = this.source,
298 | lastBracket = source.lastIndexOf("]");
299 | return buildBuilder(createBuilder(getFlags(this), source.slice(0, lastBracket)
300 | + (this.negate && def[1] || def[0]) + source.slice(lastBracket)), [ thenable, andCharSet ]);
301 | };
302 | if (def[1]) setnegable[name] = settable[name];
303 | } else
304 | settable[name] = function() {
305 | return function() {
306 | var source = this.source,
307 | lastBracket = source.lastIndexOf("]");
308 | return buildBuilder(createBuilder(getFlags(this), source.slice(0, lastBracket)
309 | + def[0].apply(this, arguments) + source.slice(lastBracket)), [ thenable, andCharSet ]);
310 | };
311 | };
312 | }
313 | });
314 | openable.oneOf = negable.oneOf = quantifiable.oneOf = qntnegable.oneOf = function() {
315 | var that = this, source = this.source;
316 |
317 | return buildBuilder(initFunc(function() {
318 | return buildBuilder(createBuilder(getFlags(that), source
319 | + wrapSource((that.negate ? "[^" : "[") + parseSets(arguments) + "]", that)), [ andCharSet, thenable ]);
320 | }, getSettings(this), source + wrapSource(this.negate ? "[^]" : "[]", this)), [ settable ]);
321 | };
322 | extend(openable, quantifiers, lazinator);
323 |
324 | settable.backspace = function() {
325 | var source = this.source,
326 | lastBracket = source.lastIndexOf("]");
327 | return buildBuilder(createBuilder(getFlags(this), source.slice(0, lastBracket)
328 | + "\\b" + source.slice(lastBracket)), [ thenable, andCharSet ]);
329 | };
330 | settable.range = function() {
331 | function checkBoundary(bnd) {
332 | if (typeof bnd === "string" && bnd.length === 1)
333 | return parseSets(bnd);
334 |
335 | if (isBuilder(bnd)) {
336 | bnd = bnd.source;
337 | if (bnd.length === 1 || /^\\(?:[0btnvfr\/\\]|x[\da-fA-F]{2}|u[\da-fA-F]{4}|c[a-zA-Z])$/.test(bnd))
338 | return bnd;
339 | }
340 |
341 | throw new RangeError("Incorrect character range");
342 | }
343 | return function(start, end) {
344 | start = checkBoundary(start);
345 | end = checkBoundary(end);
346 |
347 | var source = this.source,
348 | lastBracket = source.lastIndexOf("]");
349 | return buildBuilder(createBuilder(getFlags(this), source.slice(0, lastBracket)
350 | + start + "-" + end + source.slice(lastBracket)),
351 | [ thenable, andCharSet ]);
352 | };
353 | };
354 | extend(settable, negator([ setnegable ]));
355 |
356 | var andCharSet = {
357 | and: function() {
358 | var flags = getFlags(this), source = this.source;
359 |
360 | return buildBuilder(initFunc(function() {
361 | var lastBracket = source.lastIndexOf("]");
362 | return buildBuilder(createBuilder(flags, source.slice(0, lastBracket)
363 | + parseSets(arguments) + source.slice(lastBracket)), [ andCharSet, thenable ]);
364 | }, flags, source), [ settable ]);
365 | }
366 | };
367 |
368 | var lookAheads = {
369 | followedBy: function() {
370 | return function() {
371 | var source = wrapSource(parseArgs(arguments), this),
372 | seq = this.negate ? "(?!" : "(?=";
373 | if (source.slice(0, 3) !== seq)
374 | source = seq + source + ")";
375 |
376 | return buildBuilder(createBuilder(getFlags(this), (this.source || "") + source), [ thenable ]);
377 | };
378 | }
379 | };
380 | extend(thenable, lookAheads);
381 |
382 | function negator(bundles) {
383 | return { not: function() {
384 | return buildBuilder(createBuilder(extend(getSettings(this), { negate: true }), this.source), bundles);
385 | } };
386 | }
387 |
388 | /**
389 | * Adds the eventual quantifier to a chunk of regex source, conveniently
390 | * wrapping it in a non-capturing group if it contains more than a block.
391 | * @param {string} source
392 | * @param {Object} settings Quantifying settings (min, max and lazy).
393 | * @returns {string} Quantified source
394 | */
395 | function wrapSource(source, settings) {
396 | if (typeof settings.min === "number" || typeof settings.max === "number") {
397 | var quantifier,
398 | min = typeof settings.min === "number" ? settings.min : 0,
399 | max = typeof settings.max === "number" ? settings.max : Infinity;
400 |
401 | if (min === max)
402 | quantifier = min === 1 ? "" : "{" + min + "}";
403 | else if (min === 0)
404 | quantifier = max === 1 ? "?"
405 | : max === Infinity ? "*"
406 | : "{," + max + "}";
407 | else if (min === 1)
408 | quantifier = max === Infinity ? "+" : "{1," + max + "}";
409 | else quantifier = "{" + min + "," + (max === Infinity ? "" : max) + "}";
410 |
411 | if (quantifier) {
412 | if ((source.length > 2 || source.length === 2 && source[0] !== "\\") && hasManyBlocks(source))
413 | source = "(?:" + source + ")";
414 | source += quantifier + (settings.lazy ? "?" : "");
415 | }
416 | }
417 |
418 | return source;
419 | }
420 |
421 | function getConstMap(consts) {
422 | var map = {};
423 | for (var name in consts)
424 | map[name] = { value: consts[name], writable: false, configurable: false };
425 |
426 | return map;
427 | }
428 | function setConsts(dest, consts) {
429 | return defineProps(dest, getConstMap(consts));
430 | }
431 |
432 | function initFunc(fnc, consts, source) {
433 | consts.source = source || "";
434 | return setConsts(fnc, consts);
435 | }
436 |
437 | /**
438 | */
439 | function reparser(blocks) {
440 | var source = "", i = 0, block;
441 | while (i < blocks.length) {
442 | block = blocks[i++];
443 | if (typeof block === "string")
444 | source += block.replace(this, "\\$&");
445 | else if (block instanceof RegExp || isBuilder(block))
446 | source += block.source;
447 | }
448 | return source;
449 | }
450 | var parseArgs = reparser.bind(/[\^\$\/\.\*\+\?\|\(\)\[\]\{\}\\]/g),
451 | parseSets = reparser.bind(/[\^\/\[\]\\-]/g);
452 |
453 | function hasManyBlocks(source) {
454 | var len = source.length;
455 | if (len < 2 || len === 2 && (source[0] === "\\" || source === "[]" || source === "()")) return false;
456 |
457 | if (source[0] === "[" && source[len - 1] === "]")
458 | return source.search(/[^\\]\]/) < len - 2;
459 |
460 | if (source[0] === "(" && source[len - 1] === ")") {
461 | var re = /[\(\)]/g, count = 1, match;
462 | re.lastIndex = 1;
463 | while (match = re.exec(source)) {
464 | if (source[match.index - 1] === "\\") continue;
465 | if (match[0] === ")") {
466 | if (!--count)
467 | return match.index < len - 1;
468 | } else count++;
469 | }
470 | }
471 |
472 | return true;
473 | }
474 |
475 | function getSettings(object, props) {
476 | if (!props) props = settingList;
477 | for (var i = 0, sets = {}; i < props.length; i++)
478 | sets[props[i]] = object[props[i]];
479 |
480 | return sets;
481 | }
482 | function getFlags(object) { return getSettings(object, flags); }
483 |
484 | /**
485 | * RegExpBuilder factory function
486 | */
487 | function buildBuilder(dest, bundles) {
488 | var i = 0, bundle, prop, defs = {};
489 | while (i < bundles.length) {
490 | bundle = bundles[i++];
491 | for (prop in bundle) {
492 | defs[prop] = { configurable: false, enumerable: true };
493 | if (typeof bundle[prop] === "function") {
494 | defs[prop].get = bundle[prop];
495 | } else {
496 | defs[prop].value = bundle[prop];
497 | defs[prop].writable = false;
498 | }
499 | }
500 | }
501 |
502 | return defineProps(dest, defs);
503 | }
504 |
505 | var proto = {
506 | valueOf: function() { return this.regex; },
507 | toString: function() { return "/" + this.source + "/" + this.flags; },
508 | test: function(string) { return this.regex.test(string); },
509 | exec: function(string) { return this.regex.exec(string); },
510 | replace: function(string, subs) { return string.replace(this.regex, subs); },
511 | split: function(string) { return string.split(this.regex); },
512 | search: function(string) { return string.search(this.regex); }
513 | };
514 | proto.toRegExp = proto.valueOf;
515 |
516 | function getPropDefs(settings, source) {
517 | if (typeof source !== "string") source = "";
518 |
519 | var flags = (settings.global ? "g" : "")
520 | + (settings.ignoreCase ? "i" : "")
521 | + (settings.multiline ? "m" : "")
522 | + (settings.unicode ? "u" : "")
523 | + (settings.sticky ? "y" : "");
524 |
525 | var defs = getConstMap({
526 | global: settings.global,
527 | ignoreCase: settings.ignoreCase,
528 | multiline: settings.multiline,
529 | sticky: settings.sticky,
530 | negate: settings.negate,
531 | lazy: settings.lazy,
532 | min: settings.min,
533 | max: settings.max,
534 | source: source,
535 | flags: flags
536 | });
537 | var regex;
538 | defs.regex = {
539 | get: function() {
540 | return regex || (regex = new RegExp(source, flags));
541 | },
542 | configurable: false
543 | };
544 |
545 | return defs;
546 | }
547 |
548 | function createBuilder(settings, source) {
549 | var defs = getPropDefs(settings, source);
550 | defs.regex.configurable = false;
551 |
552 | return O.create(proto, defs);
553 | };
554 | function isBuilder(object) {
555 | return proto.isPrototypeOf(object);
556 | }
557 |
558 | function RE() {
559 | return buildBuilder(createBuilder(getFlags(RE), parseArgs(arguments)), [ thenable ]);
560 | }
561 |
562 | buildBuilder(initFunc(RE,
563 | { global: false, ignoreCase: false, multiline: false, unicode: false, sticky: false }),
564 | [ openable, flagger, matcher ]);
565 |
566 | return RE;
567 | });
568 |
--------------------------------------------------------------------------------