0.0.2 • Published 3 months ago

rexx v0.0.2

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
3 months ago

🦖 Rexx

Rexx: A human-friendly regex library with structured syntax and variable support.

Introduction

  • Human readable.
  • Structured syntax.
  • Support for variables and comments.
  • Easily build regular expression patterns.

Installation

# Install rexx using npm
npm install rexx

# using Yarn
yarn add rexx

# using pnpm 
pnpm install rexx

Usage

Browser

<script src="dist/rexx.js"></script>
<!--or via CDN-->
<script src="https://www.jsdelivr.com/package/npm/rexx"></script>

Node.js

// CommonJS
const Rexx = require('rexx')
// or ES6 Modules
import Rexx from 'rexx'

Examples

Semantic Versioning

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

URL

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

Password validation

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,}$/

Cheat sheet

RexxRegExpRexxRegExp
one_of { 'a', 'b', 'c' }abcword\w
one_of { 'foo', 'bar' }foo|barnon_word\W
one_of { range {'a', 'z'} }a-zdigit\d
optional {'a'} / loop(0..) {'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}\1any.
ref {year}\k\<year>begin^
followed_by {'a'}(?=a)end$
not_followed_by {'a'}(?!a)globalg
preceded_by {'a'}(?<=)ignore_casei
not_preceded_by {'a'}(?<!)multilinem
loop (1..) {'a'} lazya+?

Syntax

' ... '

Matches exact characters.

Example: 'foo' in Rexx translates to foo in regex.

one_of

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.

range

Syntax: range { 'start', 'end' }

Creates a range of characters.

Example: one_of { range { 'a', 'z' } } translates to [a-z].

optional

Syntax: optional { pattern }

Marks the pattern as optional.

Example: optional { 'foo' } translates to foo?.

loop

Syntax: loop(from..to) { pattern } or loop(times) { pattern }

Specifies a pattern to repeat.

Example: loop(1..3) { 'a' } translates to a{1,3}.

except

Syntax: except { pattern }

Matches any character except the given pattern.

Example: except { 'a' } becomes [^a].

group

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).

ref

Syntax: ref { 1 } or ref { 'name' }

References a previously matched group.

Example: ref { 1 } or ref { 'name' } translates to \1 or \k<name>.

followed_by

Syntax: followed_by { 'pattern' }

Asserts that what follows the current position is the specified pattern.

Example: followed_by { 'a' } translates to (?=a).

not_followed_by

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) .

preceded_by

Syntax: preceded_by { 'pattern' }

Asserts that what precedes the current position is the specified pattern.

Example: preceded_by { 'a' } translates to (?<=a).

not_preceded_by

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).

Comments

Syntax: // This is a comment

Adds a comment to the pattern for explanation.

Variable

  • 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.

Similar packages

Rexx

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+)$/

melody

<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+)$/

megic-regexp

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+)$/

JSVerbalExpressions

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+)$/

Contributing

Contributions to this project are welcome.

Clone and fork:

git clone https://github.com/yyytcool/rexx.git

License

Apache License.

Copyright (c) 2024-present, yyyt

0.0.2

3 months ago

0.0.1

3 months ago