Regular expressions for MoonBit.
let year = @moonregex.search("\\d{4}", "copyright 2026").unwrap()
year.content() // "2026"
year.start() // 10
@moonregex.sub("\\s+", " ", "a b c") // "a b c"| Python (the default) | re as Python 3.15 documents it, plus everything the regex package adds |
| Ecma262 | ECMA-262 §22.2, the grammar JSON Schema and OpenAPI patterns are written in |
| Lexmatch | the grammar the core library's @string.Regex and the lexer speak |
moon add moonbitstack/moonregex| Python | moonregex | where the match must be |
|---|---|---|
| re.search | search | anywhere, leftmost first |
| re.match | prefix | at the position the scan starts |
| re.fullmatch | full | the whole region |
| re.sub with a function | sub_with | — |
| re.subn with a function | subn_with | — |
let p = @moonregex.compile("[a-z]+")
p.search(" hello ") // Some(_) — at 2
p.prefix(" hello ") // None — the scan starts at 0
p.full("hello") // Some(_)| Function | Returns |
|---|---|
| compile(pattern, flags~, dialect~, lists?, partial~) | Pattern |
| search / prefix / full | Match? |
| findall | Array[String?] |
| finditer | Array[Match] |
| split | Array[String?] |
| sub / subn / subf / subfn | String or (String, Int) |
| sub_with / subn_with | String or (String, Int) |
| escape(string, dialect~, special_only~, literal_spaces~) | String |
@moonregex.findall("(\\w+)=(\\d+)", "a=1") // raises Ambiguous
@moonregex.findall("(\\w+)=(\\d+)", "a=1", group=Num(n=1)) // ["a"]| Dialect | escape("a.b") | rule |
|---|---|---|
| Python | a\.b | re.escape: the characters that can mean something take a backslash |
| Ecma262 | \x61\.b | RegExp.escape: punctuation is spelled in hex, and a leading alphanumeric too |
| Lexmatch | a\.b | the @string.Regex metacharacters, with \u{…} for the invisible ones |
// Python: named group, `\A`, backreference — the default.
@moonregex.findall("(?P<w>\\w+)\\s+\\1", "go go no")
// ECMA-262: what a JSON Schema `pattern` means.
@moonregex.compile("^[a-z]+$", dialect=@moonregex.ecma262)
// lexmatch: a pattern already written for the core library.
@moonregex.compile("[[:digit:]]+", dialect=@moonregex.lexmatch_dialect)let year = @moonregex.Pattern::string("\\d{4}").capture("year")
let dash = @moonregex.Pattern::string("-")
let month = @moonregex.Pattern::string("\\d{2}").capture("month")
let date = year + dash + month
let m = date.prefix("2026-10").unwrap()
m.group("month") // Some("10")
// `lower` hands the pattern to the core engine when it fits.
match date.lower() {
Some(core) => core.execute("2026-10") is Some(_) // true
None => false
}| Not implemented | Why |
|---|---|
| re.LOCALE | MoonBit has no locale; re itself only offers it for bytes and advises against it |
| re.DEBUG | the library does not print; Pattern::dump returns the tree as text instead |
| re.purge | there is no global pattern cache to clear |
| ECMA's g, y, d | they are ways of calling, not properties of a pattern: g is findall/finditer/count=0, y is prefix(pos=), d is what span has always answered |
| Match.detach_string, Match.regs | strings are immutable here, and spans covers regs |
| Case | Here | The reference |
|---|---|---|
| match | not offered; prefix is the same thing | re.match, soft-deprecated in 3.15 |
| Runaway patterns | Exceeded after ten million steps | no limit |
| \N without a name | any character but a newline | re refuses it; regex reads a literal N under V0 |
| \v, \V | the vertical tab under V0, the vertical-space class under V1 | re always means the tab |
| \e, \Q…\E, \K | accepted | re refuses them |
| (?<name>…) | accepted in the Python dialect too | re wants (?P<name>…) |
| [[:alpha:]] | a class escape in every dialect | re reads it as a set of characters |
| Refusal offsets | where reading stopped | Python's convention differs per error |
| A group repeated over an empty tail | keeps the captures of the empty turn ((a*)* leaves group 1 at the end) | ECMA-262 clears them |
| Astral characters | offsets count code points | ECMA-262 counts UTF-16 code units |
| Property names | Unicode loose matching in every dialect | ECMA-262 is case- and space-sensitive |
| A name used twice | allowed in the ECMA dialect anywhere | re refuses it; ECMA-262 only across alternatives |
| v-mode nested classes | [[a-z]--[aeiou]] is not read as a class expression | ECMA-262 reads them |
pub(all) enum Group {
Whole
Num(n~ : Int)
Name(n~ : String)
}pub(all) struct Match {
// private fields
}pub(all) struct Pattern {
// private fields
}pub struct Scanner {
// private fields
}fn escape(pattern : StringView, dialect? : Dialect, special_only? : Bool, literal_spaces? : Bool) -> Stringlet version_info : (Int, Int, Int)