Rexx: A human-friendly regex library with structured syntax and variable support.
- Human readable.
- Structured syntax.
- Support for variables and comments.
- Easily build regular expression patterns.
# Install rexx using npm npm install rexx # using Yarn yarn add rexx # using pnpm pnpm install rexx
<script src="dist/rexx.js"></script> <!--or via CDN--> <script src="https://www.unpkg.com/rexx"></script>
// CommonJS const rexx = require('rexx') // or ES6 Modules import rexx from 'rexx'
Semantic Versioning (SemVer) follows the pattern major.minor.patch, where major, minor, and patch are non-negative integers.
const regExp = rexx(` digits = { one_or_more { digit } } semVer = { begin optional {'v'} group('major') { digits } '.' group('minor') { digits } '.' group('patch') { digits } end } `) console.log(regExp) // { // digits: /\d+/, // semVer: /^v?(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/, // default: /(?:)/, // } regExp.semVer.test('v1.2.3') // true
A URL contains several components - the protocol, host, path, query, and hash.
http://www.example.com/foo/bar.html?a=b&c=d#hash
const urlRegexp = rexx(` protocol = { optional { one_of{'http', 'https'} } } // http host = { one_or_more { except{'/'} } } // www.example.com path = { loop(0..) {'/', one_or_more { except{'/?#'} } } } // /foo/bar.html query = { optional {'?', one_or_more { except{'#'} } } } // ?a=b&c=d hash = { optional {'#', one_or_more { any } } } // #hash url = { group('protocol'){ protocol } optional{'://'} group('host'){ host } group('path'){ path } group('query'){ query } group('hash'){ hash } } `) console.log(urlRegexp.url) // /(?<protocol>(?:(?:http|https))?)(?:\:\/\/)?(?<host>(?:[^\/])+)(?<path>(?:\/(?:[^\/\?#])+)*)(?<query>(?:\?(?:[^#])+)?)(?<hash>(?:#.+)?)/ urlRegexp.url.test('http://www.example.com/foo/bar.html?a=b&c=d#hash') // true
The password must contain characters from at least 3 of the following 4 rules: upper case, lower case, numbers, non-alphanumeric.
const pwdRegexp = rexx(` lower = { one_of {range {'a', 'z'} } } upper = { one_of {range {'A', 'Z'} } } _any = { loop(0..) { any } } hasLower = { followed_by { _any, lower } } hasUpper = { followed_by { _any, upper } } hasDigit = { followed_by { _any, digit } } hasSymbol = { followed_by { _any, non_word } } password = { begin one_of { {hasLower, hasUpper, hasDigit} {hasLower, hasUpper, hasSymbol} {hasLower, hasDigit, hasSymbol} {hasUpper, hasDigit, hasSymbol} } loop(8..){ any } end } `) console.log(pwdRegexp.password) // /^(?:(?=.*[a-z])(?=.*[A-Z])(?=.*\d)|(?=.*[a-z])(?=.*[A-Z])(?=.*\W)|(?=.*[a-z])(?=.*\d)(?=.*\W)|(?=.*[A-Z])(?=.*\d)(?=.*\W)).{8,}$/
| Rexx | RegExp | Rexx | RegExp | |
|---|---|---|---|---|
| one_of { 'a', 'b', 'c' } | [abc] | word | \w | |
| one_of { 'foo', 'bar' } | foo|bar | non_word | \W | |
| one_of { range {'a', 'z'} } | [a-z] | digit | \d | |
| optional {'a'} / loop(0..1) {'a'} | a? | non_digit | \D | |
| optional {'abc'} | (?:abc)? | whitespace | \s | |
| loop(0..) {'a'} | a* | non_whitespace | \S | |
| one_or_more {'a'} / loop(1..) {'a'} | a+ | boundary | \b | |
| loop(2) {'a'} | a{2} | non_boundary | \B | |
| loop(2..3) {'a'} | a{2,3} | chinese | [\u4e00-\u9fa5] | |
| except {'a'} | [^a] | '\d' | \d | |
| except { range {'a', 'z'} } | [^a-z] | '\uffff' | \uffff | |
| group{'foo'} | (foo) | newline | \n | |
| group('year'){'2024'} | (?<year>2024) | tab | \t | |
| ref {1} | \1 | any | . | |
| ref {year} | \k<year> | begin | ^ | |
| followed_by {'a'} | (?=a) | end | $ | |
| not_followed_by {'a'} | (?!a) | global | g | |
| preceded_by {'a'} | (?<=) | ignore_case | i | |
| not_preceded_by {'a'} | (?<!) | multiline | m | |
| loop (1..) {'a'} lazy | a+? |
Matches exact characters.
Example: 'foo' in Rexx translates to foo in regex.
Syntax: one_of { pattern, pattern, ... }
Matches any one of the listed alternatives. Commas are optional.
Example: one_of { 'foo', 'bar' } translates to foo|bar in regex.
Syntax: range { 'start', 'end' }
Creates a range of characters.
Example: one_of { range { 'a', 'z' } } translates to [a-z].
Syntax: optional { pattern }
Marks the pattern as optional.
Example: optional { 'foo' } translates to foo?.
Syntax: loop(from..to) { pattern } or loop(times) { pattern }
Specifies a pattern to repeat.
Example: loop(1..3) { 'a' } translates to a{1,3}.
Syntax: except { pattern }
Matches any character except the given pattern.
Example: except { 'a' } becomes [^a].
Syntax: group { pattern } or group('name') { pattern }
Capturing group is assigned a name or sequential number.
Example: group { 'abc' } or group('name'){ 'abc' } translates to (abc) or (?<name>abc).
Syntax: ref { 1 } or ref { 'name' }
References a previously matched group.
Example: ref { 1 } or ref { 'name' } translates to \1 or \k<name>.
Syntax: followed_by { 'pattern' }
Asserts that what follows the current position is the specified pattern.
Example: followed_by { 'a' } translates to (?=a).
Syntax: not_followed_by { 'pattern' }
Asserts that what follows the current position is not the specified pattern.
Example: not_followed_by { 'a' } translates to (?!a) .
Syntax: preceded_by { 'pattern' }
Asserts that what precedes the current position is the specified pattern.
Example: preceded_by { 'a' } translates to (?<=a).
Syntax: not_preceded_by { 'pattern' }
Asserts that what precedes the current position is not the specified pattern.
Example: not_preceded_by { 'a' } translates to (?<!a).
Syntax: // This is a comment
Adds a comment to the pattern for explanation.
- To define a variable in a pattern, use
variable_name = { pattern }. - All variables must be declared before they are used.
- Once a variable is declared, you can reference it in your pattern using its name.
- Variable declarations can also include flags that alter the behavior of the regex. There are three kinds of
flags:
ignore_case,global,multiline.
| Packages | Code |
digits = { one_or_more { digit } } semVer = { begin optional {'v'} group('major') { digits } '.' group('minor') { digits } '.' group('patch') { digits } end } // /^v?(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/ |
|
<start>; option of "v"; capture major { some of <digit>; } "."; capture minor { some of <digit>; } "."; capture patch { some of <digit>; } <end>; // /^v?(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/ |
|
import { createRegExp, maybe, oneOrMore, exactly, digit } from 'magic-regexp' const regExp = createRegExp( maybe('v') .and(oneOrMore(digit).groupedAs('major')) .and(exactly('.')) .and(oneOrMore(digit).groupedAs('minor')) .and(exactly('.')) .and(oneOrMore(digit).groupedAs('patch')) .at.lineStart() .at.lineEnd() ) // /^v?(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)$/ | |
const regExp = VerEx() .startOfLine() .maybe('v') .beginCapture() .digit().oneOrMore() .endCapture() .then('.') .beginCapture() .digit().oneOrMore() .endCapture() .then('.') .beginCapture() .digit().oneOrMore() .endCapture() .endOfLine() // /^(?:v)?(\d+)(?:\.)(\d+)(?:\.)(\d+)$/ |
Contributions to this project are welcome.
Clone and fork:
git clone https://github.com/yyytcool/rexx.git
Copyright (c) 2024-present, yyytcool