reni

A regular expression engine compatible with Oniguruma

Pure Nim score 15/100 · tests present · no docs generated

Summary

Latest Version 0.1.0
License MIT
CI Status Failing
Downloads 0
Last Indexed 2026-09-04 07:26

Tags

Authors

  • fox0430

Installation

nimble install reni
choosenim install reni
git clone https://github.com/fox0430/reni

OS Compatibility

Platform Linux macOS Windows FreeBSD OpenBSD NetBSD Android iOS WASM Embedded
reni - - - - - - -

Dependencies

Package Version Optional
nim >= 2.0.0 No
unicodedb >= 0.13.0 No

Source

Repository https://github.com/fox0430/reni
Homepage https://github.com/fox0430/reni
Registry Source nimble_official

README

reni

A regular expression engine compatible with Oniguruma

A pure Nim regex engine that replicates the syntax and semantics of Oniguruma.

This project aims to implement a tmLanguage parser.

Features

  • Capture groups (numbered and named)
  • Backreferences and named backreferences with recursion-level support
  • Lookaround assertions (lookahead, lookbehind, negative variants)
  • Atomic groups (?>...)
  • Conditionals (?(cond)yes|no)
  • Subexpression calls \g<name>, \g<n>
  • Absent operator (?~...)
  • POSIX character classes, Unicode properties \p{...}
  • Greedy, lazy, and possessive quantifiers
  • Grapheme cluster mode (?y{g}), word mode (?y{w})
  • Flags: (?i), (?m), (?x), (?W), (?D), (?S), (?P), (?I), (?L)
  • ReDoS protection via step limit

Requirements

  • Nim >= 2.0.0

Usage

Search

import pkg/reni

let m = search("hello world", re("(\\w+)\\s(\\w+)"))
assert m.found
assert m.boundaries[0] == 0 .. 11  # full match
assert m.boundaries[1] == 0 .. 5   # group 1
assert m.boundaries[2] == 6 .. 11  # group 2

Named captures

import std/options

let r = re("(?<user>\\w+)@(?<host>\\w+)")
let m = search("user@host", r)
assert m.found
assert captureText(m, "user", "user@host", r) == some("user")
assert captureText(m, "host", "user@host", r) == some("host")

Match at position

let m = matchAt("abcabc", re("abc"), pos = 3)
assert m.found
assert m.boundaries[0] == 3 .. 6

Find all

import std/sequtils

let matches = toSeq(findAll("ab12cd34", re("\\d+")))
assert matches.len == 2

Replace

# Template replacement ($0, $1, ${name})
assert replace("2025-04-05", re("(\\d+)-(\\d+)-(\\d+)"), "$2/$3/$1") == "04/05/2025"

# Callback replacement
let result = replace("hello", re("\\w+"), proc(m: Match, s: string): string =
  let b = m.boundaries[0]
  s[b.a].toUpperAscii & s[b.a + 1 ..< b.b]
)
assert result == "Hello"

Split

assert split("a,b,,c", re(",")) == @["a", "b", "", "c"]

# Capture groups are included in results (like Python re.split)
assert split("a1b2c", re("(\\d)")) == @["a", "1", "b", "2", "c"]

Backward search

let m = searchBackward("abcabc", re("abc"))
assert m.found
assert m.boundaries[0] == 3 .. 6

Step limit (ReDoS protection)

# Limit matching steps to prevent catastrophic backtracking.
# Raises RegexLimitError when the step count exceeds stepLimit.
try:
  let m = search("aaaaaaaaaaab", re("(a+)+$"), stepLimit = 10000)
  doAssert m.found
except RegexLimitError:
  echo "step limit exceeded"

Internal API notice

Regex.ast, the Node type, and NodeKind are exposed by the library but they are internal implementation details. They are re-exported so that tests inside this repository can inspect parsed trees. User code should not depend on them, and they may be removed or restricted in a future release. Use captureText, captureSpan, captureIndex, captureCount, namedCaptures, and pattern instead.

Documentation

https://fox0430.github.io/reni/reni.html

License

MIT