// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0
// The formats that name a place inside a document or a value of a fixed shape:
// the two JSON Pointers (RFC 6901 and the relative form), the UUID (RFC 4122)
// and the regular expression (ECMA-262).
///|
/// A JSON Pointer: the empty string, which points at the whole document, or a
/// run of `/`-prefixed tokens in which `~` may only appear as `~0` or `~1`.
///
/// Everything else is a token character, including the control characters a
/// URI could never carry: a pointer is not a URI, and a pointer that arrived
/// inside one is percent-coded before it gets here.
fn is_json_pointer(text : String) -> Bool {
let view = text[:]
if view.length() == 0 {
return true
}
guard view[0] == '/' else { return false }
let mut i = 0
while i < view.length() {
if view[i] == '~' {
guard i + 1 < view.length() else { return false }
guard view[i + 1] == '0' || view[i + 1] == '1' else { return false }
i = i + 2
continue
}
i = i + 1
}
true
}
///|
/// A relative JSON Pointer: a non-negative integer, then either `#` — the
/// whole document above this one — or a JSON Pointer.
///
/// No leading zero, because `01/a` is a number nobody writes.
fn is_relative_json_pointer(text : String) -> Bool {
let view = text[:]
let mut i = 0
while i < view.length() && digit_at(view, i) >= 0 {
i = i + 1
}
guard i > 0 else { return false }
guard i == 1 || view[0] != '0' else { return false }
if i == view.length() {
return true
}
if view[i] == '#' {
// `#` is the whole reference and takes nothing after it.
return i + 1 == view.length()
}
is_json_pointer(view[i:].to_owned())
}
///|
/// A UUID in its canonical textual form: five groups of hexadecimal digits,
/// 8-4-4-4-12, joined by hyphens. Either case, and no braces and no `urn:`
/// — those are other formats' business.
fn is_uuid(text : String) -> Bool {
guard text.length() == 36 else { return false }
for index in [8, 13, 18, 23] {
guard text[index] == '-' else { return false }
}
for i = 0; i < 36; i = i + 1 {
if i == 8 || i == 13 || i == 18 || i == 23 {
continue
}
guard hex_value(text[i].to_int()) >= 0 else { return false }
}
true
}
///|
/// An ECMA-262 regular expression, which is the dialect `pattern` is read in
/// (§6.3.3): one reading of a pattern, so that a schema cannot mean one thing
/// to `pattern` and another to `format: regex`.
fn is_regex(text : String) -> Bool {
let _ = @moonregex.compile(
text,
flags=@moonregex.unicode,
dialect=@moonregex.ecma262,
) catch {
_ => return false
}
groups_are_ecma(text)
}
///|
/// Whether every `(?` in a pattern introduces a group form ECMA-262 has.
///
/// The grammar has five — `(?:`, `(?=`, `(?!`, `(?<=`, `(?`. A reader that accepts more than those accepts a pattern
/// written for another dialect and then matches it differently, which is the one
/// failure a `pattern` must not have.
fn groups_are_ecma(text : String) -> Bool {
let points : Array[Int32] = []
for c in text {
points.push(c.to_int())
}
let mut i = 0
let mut in_class = false
while i < points.length() {
let code = points[i]
if code == 0x5C {
// An escape, including `\(`, which is not a group.
i = i + 2
continue
}
if in_class {
if code == 0x5D {
in_class = false
}
i = i + 1
continue
}
if code == 0x5B {
in_class = true
i = i + 1
continue
}
if code == 0x28 && i + 1 < points.length() && points[i + 1] == 0x3F {
guard i + 2 < points.length() else { return false }
let next = points[i + 2]
let assertion = next == 0x3A || next == 0x3D || next == 0x21
if !assertion {
guard next == 0x3C && i + 3 < points.length() else { return false }
// `(?<=` and `(?` names a capture.
let after = points[i + 3]
if after != 0x3D && after != 0x21 {
guard is_name_start(after) else { return false }
}
}
}
i = i + 1
}
true
}
///|
fn is_name_start(code : Int32) -> Bool {
(code >= 0x41 && code <= 0x5A) ||
(code >= 0x61 && code <= 0x7A) ||
code == 0x5F ||
code == 0x24
}