Skip to content

feat: allow custom regexp engine via RegexpCompiler option #72

@satisataka

Description

@satisataka

Is your feature request related to a problem? Please describe.

The doc.go already acknowledges that Go's regexp package differs from ECMA 262 (no back-references, no lookahead/lookbehind). This is documented under "Deviations from the specification" with a link to the regexp2 comparison table.

Currently, there is no way to plug in an alternative regexp engine. Users working with schemas that use PCRE-specific features (e.g., (?=...) lookahead in pattern or patternProperties) cannot validate them correctly.

Describe the solution you'd like

Add a RegexpCompiler option to ResolveOptions that allows users to provide a custom regexp compiler. The default behavior remains unchanged (Go's regexp.Compile).

The proposed API:

// A Regexp is a compiled regular expression.
type Regexp interface {
    MatchString(s string) bool
}

// A RegexpCompiler compiles a regular expression pattern string into a Regexp.
type RegexpCompiler func(pattern string) (Regexp, error)

type ResolveOptions struct {
    // ... existing fields ...
    
    // RegexpCompiler compiles regular expression patterns used in "pattern" and
    // "patternProperties" keywords. If nil, Go's regexp.Compile is used.
    RegexpCompiler RegexpCompiler
}

Usage with regexp2 (note: regexp2.MatchString returns (bool, error), so an adapter is needed):

import "github.com/dlclark/regexp2"

type regexp2Adapter struct {
    re *regexp2.Regexp
}

func (r regexp2Adapter) MatchString(s string) bool {
    ok, err := r.re.MatchString(s)
    return err == nil && ok
}

compiler := func(pattern string) (jsonschema.Regexp, error) {
    re, err := regexp2.Compile(pattern, regexp2.ECMAScript)
    if err != nil {
        return nil, err
    }
    return regexp2Adapter{re: re}, nil
}

resolved, err := schema.Resolve(&jsonschema.ResolveOptions{
    RegexpCompiler: compiler,
})

Implementation details

  • Minimal interface: Regexp requires only MatchString(string) bool, matching the existing validation path and avoiding changes to runtime validation semantics
  • Internal resolvedInfo stores Regexp interface instead of *regexp.Regexp
  • validate.go should require little or no change, since validation already only needs MatchString semantics
  • doc.go "Deviations" section updated to mention the escape hatch
  • Full test coverage: custom compiler for pattern, patternProperties, and error propagation
  • Fully backwards-compatible — nil RegexpCompiler defaults to regexp.Compile

PR: #73

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions