Regex Tester — Go source
Test regular expressions against any text with all flags (g/i/m/s/u/y). See every match, its index and capture groups, plus the constructed pattern. 100% in-browser.
This is the Go implementation — the same logic the interactive tool runs, in a shareable, citable form.
// Regular expression tester: match a pattern against input text and report
// each match with its capture groups and byte index.
//
// Language: Go
// CosmoDev polyglot showcase port of the `regex-tester` tool.
// Ported from src/tools/RegexTester.tsx — display source, part of CosmoDev's
// polyglot tool pages.
//
// Compiles the pattern and walks every match via the stdlib regexp package,
// surfacing the full match, its index, and capture groups. JS flags map to
// RE2 inline groups: i→(?i), m→(?m), s→(?s); Go's regexp is Unicode-aware by
// default, so u is a no-op. g selects all-matches vs first-match (capped at
// 2000, mirroring the JS source). Parity caveats: Go uses RE2, so patterns
// using backreferences or lookahead/lookbehind will not compile; the sticky
// y flag has no RE2 equivalent. Indices are byte offsets (== JS char indices
// for ASCII input); RE2 advances past empty matches itself, so the explicit
// zero-width guard from the JS source is unnecessary here.
package main
import (
"fmt"
"regexp"
"strings"
)
// RegexMatch is one match: full text, its starting index, and the capture
// groups (group 0 excluded; "" for a group that did not participate).
type RegexMatch struct {
Match string
Index int
Groups []string
}
// RegexResult holds the matches, or — when the pattern is invalid — an error.
type RegexResult struct {
Matches []RegexMatch
Error string
}
// flagPrefix turns the JS flag letters Go understands into an RE2 inline group
// such as "(?ims)". Unknown letters (g, u, y) are handled elsewhere or no-op.
func flagPrefix(flags string) string {
var b strings.Builder
b.WriteString("(?")
for _, f := range flags {
switch f {
case 'i', 'm', 's':
b.WriteRune(f)
}
}
b.WriteString(")")
if body := b.String(); body != "(?)" {
return body
}
return ""
}
// RunRegex compiles pattern+flags against testStr and returns every match.
// With the g flag all matches are returned (≤2000); without it, only the first.
func RunRegex(pattern, flags, testStr string) RegexResult {
if pattern == "" {
return RegexResult{}
}
re, err := regexp.Compile(flagPrefix(flags) + pattern)
if err != nil {
return RegexResult{Error: err.Error()}
}
var locs [][]int
if strings.ContainsRune(flags, 'g') {
locs = re.FindAllStringSubmatchIndex(testStr, 2000)
} else if loc := re.FindStringSubmatchIndex(testStr); loc != nil {
locs = [][]int{loc}
}
out := make([]RegexMatch, 0, len(locs))
for _, loc := range locs {
m := RegexMatch{Match: testStr[loc[0]:loc[1]], Index: loc[0], Groups: []string{}}
for i := 2; i+1 < len(loc); i += 2 {
if loc[i] >= 0 { // -1 means the group did not participate
m.Groups = append(m.Groups, testStr[loc[i]:loc[i+1]])
} else {
m.Groups = append(m.Groups, "")
}
}
out = append(out, m)
}
return RegexResult{Matches: out}
}
// FormatMatches renders a result as the tool's UI does:
// `N. "match" @ index → [g1, g2]`, or `(none)` when empty.
func FormatMatches(r RegexResult) string {
if r.Error != "" {
return r.Error
}
if len(r.Matches) == 0 {
return "(none)"
}
lines := make([]string, 0, len(r.Matches))
for i, m := range r.Matches {
line := fmt.Sprintf("%d. %q @ %d", i+1, m.Match, m.Index)
if len(m.Groups) > 0 {
line += " → [" + strings.Join(m.Groups, ", ") + "]"
}
lines = append(lines, line)
}
return strings.Join(lines, "\n")
}
func main() {
res := RunRegex(`\b(\w+)@(\w+)\.(\w+)\b`, "gi", "mail@x.com nope bob@test.org")
fmt.Println(FormatMatches(res))
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →