feat: publish Regex Tools 0.2.0

This commit is contained in:
2026-07-27 01:06:57 +02:00
parent 873b9b218d
commit 4951c966d4
180 changed files with 35344 additions and 503 deletions

View File

@@ -0,0 +1,110 @@
import { createHash } from "node:crypto";
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { describe, expect, it } from "vitest";
import {
generatePcre2C,
type Pcre2CGenerationRequest,
} from "../src/regex/codegen/pcre2-c";
function request(operation: "match" | "replace"): Pcre2CGenerationRequest {
return {
flavour: "pcre2",
flavourVersion: "PCRE2 10.47 8-bit WebAssembly",
operation,
pattern: String.raw`(?<word>\p{L}+)-(\d+)`,
flags: ["g"],
options: {
matchLimit: 1_000_000,
depthLimit: 1_000,
heapLimitKib: 32_768,
},
subject: "x Grüße-42 y alpha-7",
scanAll: true,
...(operation === "replace" ? { replacement: "$2:$<word>" } : {}),
maximumMatches: 100,
maximumCaptureRows: 1_000,
maximumOutputBytes: 4_096,
};
}
function sha256(value: string): string {
return createHash("sha256").update(value).digest("hex");
}
const exactToolchainPrefix = process.env.PCRE2_CODEGEN_PREFIX;
const compileWithExactPcre2 = exactToolchainPrefix ? it : it.skip;
describe("PCRE2 C code-generation golden", () => {
it("retains a reviewed deterministic source identity", () => {
expect(sha256(generatePcre2C(request("match")).source)).toBe(
"22b91d578a449b0ed5c30e6eadad30f19a2bcb26ac9db355c8ec7a69328675da",
);
expect(sha256(generatePcre2C(request("replace")).source)).toBe(
"ab90a0d9831ea7b7145aa0072ac352b42baf9a795319d81f531b0f341b04a636",
);
});
compileWithExactPcre2(
"compiles and executes against the explicitly supplied PCRE2 10.47 C toolchain",
async () => {
if (!exactToolchainPrefix) return;
const header = path.join(exactToolchainPrefix, "include", "pcre2.h");
const library =
process.env.PCRE2_CODEGEN_LIBRARY ??
path.join(exactToolchainPrefix, "lib64", "libpcre2-8.a");
await expect(readFile(header)).resolves.toBeInstanceOf(Buffer);
await expect(readFile(library)).resolves.toBeInstanceOf(Buffer);
const directory = await mkdtemp(
path.join(tmpdir(), "regex-tools-codegen-"),
);
try {
for (const operation of ["match", "replace"] as const) {
const generated = generatePcre2C(request(operation));
const source = path.join(directory, generated.fileName);
const executable = path.join(directory, `pcre2-${operation}`);
await writeFile(source, generated.source);
const compiled = spawnSync(
process.env.CC ?? "cc",
[
"-std=c17",
"-Wall",
"-Wextra",
"-Werror",
`-I${path.join(exactToolchainPrefix, "include")}`,
source,
library,
"-o",
executable,
],
{ encoding: "utf8" },
);
expect(
`${compiled.stdout}${compiled.stderr}`,
`${operation} fixture did not compile`,
).toBe("");
expect(compiled.status).toBe(0);
const executed = spawnSync(executable, [], { encoding: "utf8" });
expect(
executed.status,
`${operation} fixture failed: ${executed.stdout}${executed.stderr}`,
).toBe(0);
if (operation === "match") {
expect(executed.stdout).toContain("match 1: UTF-8 bytes 2..12");
expect(executed.stdout).toContain("group 1 / word");
expect(executed.stdout).toContain(
"completed: 2 match(es), 4 capture row(s)",
);
} else {
expect(executed.stdout).toBe("x 42:Grüße y 7:alpha");
expect(executed.stderr).toContain("completed: 2 substitution(s)");
}
}
} finally {
await rm(directory, { recursive: true, force: true });
}
},
);
});