@pubid/pubid 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifact/corpus.json.gz +0 -0
- package/dist/flavors/cen_cenelec/grammar.js +10 -4
- package/dist/flavors/cen_cenelec/index.js +20 -1
- package/dist/flavors/cen_cenelec/model.d.ts +1 -0
- package/dist/flavors/cen_cenelec/model.js +4 -1
- package/dist/flavors/evs/grammar.js +53 -7
- package/dist/flavors/evs/index.js +74 -7
- package/dist/flavors/evs/model.d.ts +37 -1
- package/dist/flavors/evs/model.js +173 -11
- package/dist/flavors/ieee/artifact_tree.d.ts +2 -0
- package/dist/flavors/ieee/artifact_tree.js +36 -0
- package/dist/flavors/ieee/grammar.js +99 -19
- package/dist/flavors/ieee/index.js +353 -18
- package/dist/flavors/ieee/model.d.ts +5 -0
- package/dist/flavors/ieee/model.js +132 -20
- package/dist/flavors/ieee/preprocessor.js +28 -6
- package/dist/flavors/nist/grammar.js +18 -9
- package/dist/flavors/nist/index.js +16 -7
- package/dist/flavors/nist/preprocessor.js +9 -1
- package/dist/parg/runtime.d.ts +25 -0
- package/dist/parg/runtime.js +76 -0
- package/dist/parg/suite.d.ts +4 -0
- package/dist/parg/suite.js +14 -0
- package/dist/urn/parsers.js +7 -0
- package/package.json +6 -4
- package/vendor/parg/adobe.json +1 -0
- package/vendor/parg/aiee.json +1 -0
- package/vendor/parg/amca.json +1 -0
- package/vendor/parg/ansi.json +1 -0
- package/vendor/parg/api.json +1 -0
- package/vendor/parg/ashrae.json +1 -0
- package/vendor/parg/asme.json +1 -0
- package/vendor/parg/astm.json +1 -0
- package/vendor/parg/bipm.json +1 -0
- package/vendor/parg/bsi.json +1 -0
- package/vendor/parg/calconnect.json +1 -0
- package/vendor/parg/ccsds.json +1 -0
- package/vendor/parg/cen_cenelec.json +1 -0
- package/vendor/parg/cie.json +1 -0
- package/vendor/parg/csa.json +1 -0
- package/vendor/parg/doi.json +1 -0
- package/vendor/parg/easc.json +1 -0
- package/vendor/parg/ecma.json +1 -0
- package/vendor/parg/etsi.json +1 -0
- package/vendor/parg/evs.json +1 -0
- package/vendor/parg/gb.json +1 -0
- package/vendor/parg/gost.json +1 -0
- package/vendor/parg/iala.json +1 -0
- package/vendor/parg/iana.json +1 -0
- package/vendor/parg/idf.json +1 -0
- package/vendor/parg/iec.json +1 -0
- package/vendor/parg/ieee.json +1 -0
- package/vendor/parg/ietf.json +1 -0
- package/vendor/parg/ire.json +1 -0
- package/vendor/parg/isbn.json +1 -0
- package/vendor/parg/iso.json +1 -0
- package/vendor/parg/itu.json +1 -0
- package/vendor/parg/jcgm.json +1 -0
- package/vendor/parg/jis.json +1 -0
- package/vendor/parg/nesc.json +1 -0
- package/vendor/parg/nist.json +1 -0
- package/vendor/parg/oasis.json +1 -0
- package/vendor/parg/ogc.json +1 -0
- package/vendor/parg/oiml.json +1 -0
- package/vendor/parg/omg.json +1 -0
- package/vendor/parg/parg.json +1 -0
- package/vendor/parg/plateau.json +1 -0
- package/vendor/parg/sae.json +1 -0
- package/vendor/parg/tgpp.json +1 -0
- package/vendor/parg/un.json +1 -0
- package/vendor/parg/w3c.json +1 -0
- package/vendor/parg/xsf.json +1 -0
- package/dist/pg/runtime.d.ts +0 -85
- package/dist/pg/runtime.js +0 -170
- package/dist/pg/suite.d.ts +0 -20
- package/dist/pg/suite.js +0 -90
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"bindings":null,"version":"0.1.0","grammar":"PubidW3c","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Str":{"pattern":"W3C "}},{"Entity":{"atom":16}},{"Entity":{"atom":13}},{"Str":{"pattern":"DNOTE"}},{"Str":{"pattern":"NOTE"}},{"Str":{"pattern":"WD"}},{"Str":{"pattern":"CRD"}},{"Str":{"pattern":"CR"}},{"Str":{"pattern":"REC"}},{"Str":{"pattern":"PER"}},{"Str":{"pattern":"PR"}},{"Str":{"pattern":"SPSD"}},{"Str":{"pattern":"OBSL"}},{"Alternative":{"atoms":[3,4,5,6,7,8,9,10,11,12]}},{"Named":{"name":"type","atom":2}},{"Str":{"pattern":"-"}},{"Sequence":{"atoms":[14,15]}},{"Repetition":{"atom":1,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":22}},{"Entity":{"atom":20}},{"Re":{"pattern":"[\\x01-\\u{10ffff}]"}},{"Repetition":{"atom":19,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"rest","atom":21}},{"Sequence":{"atoms":[0,17,18]}}],"root":23},"bindings":[]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule type_token: branches 4 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule type_token: branches 7 and 8 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"W3C REC-html40-19981224","expect":{}},{"entry":"identifier","kind":"accept","input":"W3C NOTE-datetime-19980827","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}}],"docs":{},"source":"# Port of lib/pubid/w3c/parser.rb: split off the leading maturity token;\n# code/date boundary is builder-side by design. Token order matters for\n# shared prefixes (CRD before CR, PER before PR, DNOTE before NOTE).\n\ngrammar PubidW3c version \"0.1.0\" {\n any_char = %x01-10FFFF\n type_token = \"DNOTE\" / \"NOTE\" / \"WD\" / \"CRD\" / \"CR\" / \"REC\" / \"PER\" / \"PR\" / \"SPSD\" / \"OBSL\"\n type_part = type_token as type \"-\"\n rest = (1*any_char) as rest\n identifier = \"W3C \" [type_part] rest\n entry identifier: identifier\n\n test {\n accept \"W3C REC-html40-19981224\"\n accept \"W3C NOTE-datetime-19980827\"\n reject \"nonsense\"\n }\n}\n","checksum":"sha256:6c6fcb039d92331a683c48e8654064500142c1467d262928107ee9321033f0d7"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"bindings":null,"version":"0.1.0","grammar":"PubidXsf","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Str":{"pattern":"XEP "}},{"Entity":{"atom":10}},{"Entity":{"atom":3}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":2,"min":1,"max":null,"tag":"Repetition"}},{"Entity":{"atom":8}},{"Str":{"pattern":"README"}},{"Str":{"pattern":"xxxx"}},{"Alternative":{"atoms":[6,7]}},{"Alternative":{"atoms":[4,5]}},{"Named":{"name":"number","atom":9}},{"Sequence":{"atoms":[0,1]}}],"root":11},"bindings":[]}},"preprocess":{},"tables":{},"lint":{"order_warnings":[]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"XEP 0001","expect":{}},{"entry":"identifier","kind":"accept","input":"XEP README","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}}],"docs":{},"source":"# Port of lib/pubid/xsf/parser.rb: \"XEP NNNN\" plus the two named\n# documents (README, xxxx) in SPECIAL_NUMBERS.\n grammar PubidXsf version \"0.1.0\" {\n digit = %x30-39\n special_number = \"README\" / \"xxxx\"\n number = (1*digit / special_number) as number\n identifier = \"XEP \" number\n entry identifier: identifier\n\n test {\n accept \"XEP 0001\"\n accept \"XEP README\"\n reject \"nonsense\"\n }\n}\n","checksum":"sha256:ee98d45eed7bfa9222911db413f58244568ed028ac0146d6ee2e7b486c87c1d9"}
|
package/dist/pg/runtime.d.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
type PgArtifactJs = any;
|
|
2
|
-
/** A field requirement from the binding-requirements schema. */
|
|
3
|
-
export interface FieldRequirement {
|
|
4
|
-
type: string;
|
|
5
|
-
card: string;
|
|
6
|
-
preprocess?: string;
|
|
7
|
-
}
|
|
8
|
-
/** The binding-requirements schema record for one entry. */
|
|
9
|
-
export interface EntrySchema {
|
|
10
|
-
root: string;
|
|
11
|
-
fields: Record<string, FieldRequirement>;
|
|
12
|
-
examples: {
|
|
13
|
-
input: string;
|
|
14
|
-
captures: Record<string, unknown>;
|
|
15
|
-
}[];
|
|
16
|
-
}
|
|
17
|
-
export type Schema = Record<string, EntrySchema>;
|
|
18
|
-
/**
|
|
19
|
-
* A loaded, checksum-verified PG artifact with its schema.
|
|
20
|
-
* Construction throws when the checksum does not verify.
|
|
21
|
-
*/
|
|
22
|
-
export declare class PgRuntime {
|
|
23
|
-
readonly artifact: PgArtifactJs;
|
|
24
|
-
readonly schema: Schema;
|
|
25
|
-
readonly renderSpec: Record<string, RenderSegment[]>;
|
|
26
|
-
readonly entry: string;
|
|
27
|
-
readonly defaultEntry: string | null;
|
|
28
|
-
readonly envelope: Record<string, unknown>;
|
|
29
|
-
constructor(artifactJson: string, entry?: string);
|
|
30
|
-
/** Load an artifact JSON file and verify its checksum. */
|
|
31
|
-
static fromFile(path: string, entry?: string): PgRuntime;
|
|
32
|
-
/** The default artifacts directory (the pubid-grammar checkout). */
|
|
33
|
-
static artifactsDir(): string;
|
|
34
|
-
static load(grammar: string, entry?: string): PgRuntime;
|
|
35
|
-
/** Parse with the native wasm engine; returns the parsanol-shape tree. */
|
|
36
|
-
parseShape(input: string): unknown;
|
|
37
|
-
/** Apply the entry's bindings to a parsanol-shape tree. */
|
|
38
|
-
applyBindings(shape: unknown): Record<string, unknown>;
|
|
39
|
-
/** Parse and bind in one step. */
|
|
40
|
-
parseAndBind(input: string): Record<string, unknown> | null;
|
|
41
|
-
/**
|
|
42
|
-
* Schema-driven materialization: the schema (from the artifact, not
|
|
43
|
-
* hand-written model code) declares the fields a data model must
|
|
44
|
-
* implement; the bound captures populate a structurally-typed object.
|
|
45
|
-
* Card `1*`/`*` fields become arrays; `0..1` fields become nullable.
|
|
46
|
-
*/
|
|
47
|
-
materialize(input: string): Materialized | null;
|
|
48
|
-
/** Render the identifier string from a bound map (F6). */
|
|
49
|
-
render(bound: Record<string, unknown>, variant?: string): string;
|
|
50
|
-
/** Parse, bind, and render in one step. */
|
|
51
|
-
renderString(input: string, variant?: string): string | null;
|
|
52
|
-
/** Evaluate a named derive spec against a bound map (F6). */
|
|
53
|
-
derive(name: string, bound: Record<string, unknown>): string;
|
|
54
|
-
/** Parse, bind, and evaluate a derive spec in one step. */
|
|
55
|
-
deriveString(input: string, name: string): string | null;
|
|
56
|
-
/** Run the artifact's embedded tests; empty list means green. */
|
|
57
|
-
runTests(): string[];
|
|
58
|
-
}
|
|
59
|
-
/** F6 v1 derive evaluator: {field} interpolates from the bound map. */
|
|
60
|
-
export declare function deriveTemplate(derive: Record<string, string>, name: string, bound: Record<string, unknown>): string;
|
|
61
|
-
export type RenderSegment = {
|
|
62
|
-
type: "field";
|
|
63
|
-
field: string;
|
|
64
|
-
} | {
|
|
65
|
-
type: "literal";
|
|
66
|
-
text: string;
|
|
67
|
-
} | {
|
|
68
|
-
type: "cond";
|
|
69
|
-
field: string;
|
|
70
|
-
then: RenderSegment[];
|
|
71
|
-
};
|
|
72
|
-
/** F6 v1 generic renderer: field / literal / cond-presence segments. */
|
|
73
|
-
export declare function renderSegments(segments: RenderSegment[] | null, bound: Record<string, unknown>): string;
|
|
74
|
-
/** A schema-materialized identifier: typed fields plus provenance. */
|
|
75
|
-
export interface Materialized {
|
|
76
|
-
entry: string;
|
|
77
|
-
attributes: Record<string, unknown>;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Schema-driven materialization independent of any wasm handle —
|
|
81
|
-
* the same function the model layer will build on (T2 codegen emits the
|
|
82
|
-
* static view of exactly these fields).
|
|
83
|
-
*/
|
|
84
|
-
export declare function materializeFromSchema(schema: EntrySchema, bound: Record<string, unknown>): Materialized;
|
|
85
|
-
export {};
|
package/dist/pg/runtime.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
//! PG artifact runtime for TypeScript (TODO 12 / T1): checksum-verified
|
|
2
|
-
//! artifact load, native wasm parse, bindings application, and
|
|
3
|
-
//! schema-driven materialization. The engine is the vendored
|
|
4
|
-
//! parsanol wasm package (PgArtifactJs, C9) — no grammar code lives in
|
|
5
|
-
//! this repo; the artifact is the contract.
|
|
6
|
-
import { createRequire } from "node:module";
|
|
7
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
9
|
-
import { dirname, join } from "node:path";
|
|
10
|
-
/** Walk up from this module to the package root (which holds package.json). */
|
|
11
|
-
function packageRoot() {
|
|
12
|
-
let dir = dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
for (; !existsSync(join(dir, "package.json")); dir = dirname(dir)) {
|
|
14
|
-
if (dir === dirname(dir))
|
|
15
|
-
throw new Error("package.json not found above pg runtime");
|
|
16
|
-
}
|
|
17
|
-
return dir;
|
|
18
|
-
}
|
|
19
|
-
// The vendored wasm glue is CommonJS and lives outside the compiled tree,
|
|
20
|
-
// so resolve it against the package root at runtime.
|
|
21
|
-
const require = createRequire(import.meta.url);
|
|
22
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
23
|
-
const wasm = require(join(packageRoot(), "vendor", "parsanol-wasm", "parsanol.js"));
|
|
24
|
-
/**
|
|
25
|
-
* A loaded, checksum-verified PG artifact with its schema.
|
|
26
|
-
* Construction throws when the checksum does not verify.
|
|
27
|
-
*/
|
|
28
|
-
export class PgRuntime {
|
|
29
|
-
artifact;
|
|
30
|
-
schema;
|
|
31
|
-
renderSpec;
|
|
32
|
-
entry;
|
|
33
|
-
defaultEntry;
|
|
34
|
-
envelope;
|
|
35
|
-
constructor(artifactJson, entry) {
|
|
36
|
-
this.envelope = JSON.parse(artifactJson);
|
|
37
|
-
this.artifact = new wasm.PgArtifactJs(artifactJson);
|
|
38
|
-
this.schema = JSON.parse(this.artifact.schema());
|
|
39
|
-
this.renderSpec =
|
|
40
|
-
this.envelope["render"] ?? {};
|
|
41
|
-
// The compiler bakes default_entry: JSON key order is not preserved
|
|
42
|
-
// across engines, so the sorted entry list cannot rederive it.
|
|
43
|
-
this.defaultEntry =
|
|
44
|
-
JSON.parse(artifactJson)["default_entry"] ?? null;
|
|
45
|
-
this.entry = entry ?? this.defaultEntry ?? this.artifact.entryNames()[0];
|
|
46
|
-
}
|
|
47
|
-
/** Load an artifact JSON file and verify its checksum. */
|
|
48
|
-
static fromFile(path, entry) {
|
|
49
|
-
return new PgRuntime(readFileSync(path, "utf8"), entry);
|
|
50
|
-
}
|
|
51
|
-
/** The default artifacts directory (the pubid-grammar checkout). */
|
|
52
|
-
static artifactsDir() {
|
|
53
|
-
const env = process.env.PG_ARTIFACT_DIR;
|
|
54
|
-
if (env)
|
|
55
|
-
return env;
|
|
56
|
-
// pubid-grammar is a sibling checkout in the pubid workspace.
|
|
57
|
-
return join(packageRoot(), "..", "pubid-grammar", "artifacts");
|
|
58
|
-
}
|
|
59
|
-
static load(grammar, entry) {
|
|
60
|
-
return PgRuntime.fromFile(join(PgRuntime.artifactsDir(), `${grammar}.json`), entry);
|
|
61
|
-
}
|
|
62
|
-
/** Parse with the native wasm engine; returns the parsanol-shape tree. */
|
|
63
|
-
parseShape(input) {
|
|
64
|
-
return JSON.parse(this.artifact.parseShape(this.entry, input));
|
|
65
|
-
}
|
|
66
|
-
/** Apply the entry's bindings to a parsanol-shape tree. */
|
|
67
|
-
applyBindings(shape) {
|
|
68
|
-
return JSON.parse(this.artifact.applyBindings(this.entry, JSON.stringify(shape)));
|
|
69
|
-
}
|
|
70
|
-
/** Parse and bind in one step. */
|
|
71
|
-
parseAndBind(input) {
|
|
72
|
-
try {
|
|
73
|
-
return JSON.parse(this.artifact.parseAndBind(this.entry, input));
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Schema-driven materialization: the schema (from the artifact, not
|
|
81
|
-
* hand-written model code) declares the fields a data model must
|
|
82
|
-
* implement; the bound captures populate a structurally-typed object.
|
|
83
|
-
* Card `1*`/`*` fields become arrays; `0..1` fields become nullable.
|
|
84
|
-
*/
|
|
85
|
-
materialize(input) {
|
|
86
|
-
const bound = this.parseAndBind(input);
|
|
87
|
-
if (bound === null)
|
|
88
|
-
return null;
|
|
89
|
-
const schema = this.schema[this.entry];
|
|
90
|
-
if (!schema)
|
|
91
|
-
throw new Error(`artifact has no schema for entry ${this.entry}`);
|
|
92
|
-
return materializeFromSchema(schema, bound);
|
|
93
|
-
}
|
|
94
|
-
/** Render the identifier string from a bound map (F6). */
|
|
95
|
-
render(bound, variant = "default") {
|
|
96
|
-
return renderSegments((this.renderSpec[variant] ?? null), bound);
|
|
97
|
-
}
|
|
98
|
-
/** Parse, bind, and render in one step. */
|
|
99
|
-
renderString(input, variant = "default") {
|
|
100
|
-
const bound = this.parseAndBind(input);
|
|
101
|
-
return bound === null ? null : this.render(bound, variant);
|
|
102
|
-
}
|
|
103
|
-
/** Evaluate a named derive spec against a bound map (F6). */
|
|
104
|
-
derive(name, bound) {
|
|
105
|
-
return deriveTemplate((this.envelope["derive"] ?? {}), name, bound);
|
|
106
|
-
}
|
|
107
|
-
/** Parse, bind, and evaluate a derive spec in one step. */
|
|
108
|
-
deriveString(input, name) {
|
|
109
|
-
const bound = this.parseAndBind(input);
|
|
110
|
-
return bound === null ? null : this.derive(name, bound);
|
|
111
|
-
}
|
|
112
|
-
/** Run the artifact's embedded tests; empty list means green. */
|
|
113
|
-
runTests() {
|
|
114
|
-
return JSON.parse(this.artifact.runTests());
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
/** F6 v1 derive evaluator: {field} interpolates from the bound map. */
|
|
118
|
-
export function deriveTemplate(derive, name, bound) {
|
|
119
|
-
const template = derive[name];
|
|
120
|
-
if (template === undefined)
|
|
121
|
-
throw new Error(`derive spec ${name} not declared`);
|
|
122
|
-
return template.replace(/\{(\w+)\}/g, (_, field) => {
|
|
123
|
-
const value = bound[field];
|
|
124
|
-
return value === undefined || value === null ? "" : String(value);
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
/** F6 v1 generic renderer: field / literal / cond-presence segments. */
|
|
128
|
-
export function renderSegments(segments, bound) {
|
|
129
|
-
let out = "";
|
|
130
|
-
for (const segment of segments ?? []) {
|
|
131
|
-
if (segment.type === "field") {
|
|
132
|
-
const value = bound[segment.field];
|
|
133
|
-
out += value === undefined || value === null ? "" : String(value);
|
|
134
|
-
}
|
|
135
|
-
else if (segment.type === "literal") {
|
|
136
|
-
out += segment.text;
|
|
137
|
-
}
|
|
138
|
-
else if (segment.type === "cond") {
|
|
139
|
-
const value = bound[segment.field];
|
|
140
|
-
if (value !== undefined && value !== null)
|
|
141
|
-
out += renderSegments(segment.then, bound);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
return out;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Schema-driven materialization independent of any wasm handle —
|
|
148
|
-
* the same function the model layer will build on (T2 codegen emits the
|
|
149
|
-
* static view of exactly these fields).
|
|
150
|
-
*/
|
|
151
|
-
export function materializeFromSchema(schema, bound) {
|
|
152
|
-
const attributes = {};
|
|
153
|
-
for (const [path, requirements] of Object.entries(schema.fields)) {
|
|
154
|
-
const leaf = path.includes("[]")
|
|
155
|
-
? (path.split("].").pop() ?? path)
|
|
156
|
-
: path;
|
|
157
|
-
const value = bound[leaf];
|
|
158
|
-
if (requirements.card.endsWith("*")) {
|
|
159
|
-
const list = Array.isArray(value) ? value : value === undefined ? [] : [value];
|
|
160
|
-
attributes[leaf] = list;
|
|
161
|
-
}
|
|
162
|
-
else if (requirements.card === "0..1") {
|
|
163
|
-
attributes[leaf] = value === undefined ? null : value;
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
attributes[leaf] = value;
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return { entry: schema.root, attributes };
|
|
170
|
-
}
|
package/dist/pg/suite.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { PgRuntime } from "./runtime.js";
|
|
2
|
-
export type TestKind = "accept" | "reject" | "example";
|
|
3
|
-
export interface SuiteTest {
|
|
4
|
-
kind: TestKind;
|
|
5
|
-
input: string;
|
|
6
|
-
expect?: Record<string, string>;
|
|
7
|
-
}
|
|
8
|
-
export interface Suite {
|
|
9
|
-
name: string;
|
|
10
|
-
entry?: string;
|
|
11
|
-
tests: SuiteTest[];
|
|
12
|
-
}
|
|
13
|
-
/** Parse one *.pgtest file (same grammar as Parsanol::PG::Suite#read_file). */
|
|
14
|
-
export declare function readSuite(text: string, name: string): Suite;
|
|
15
|
-
/** Load every *.pgtest file in a directory. */
|
|
16
|
-
export declare function loadSuites(dir: string): Suite[];
|
|
17
|
-
/** Run a suite against a runtime; returns failure descriptions. */
|
|
18
|
-
export declare function runSuite(runtime: PgRuntime, suite: Suite): string[];
|
|
19
|
-
/** The default suites directory (the pubid-grammar checkout). */
|
|
20
|
-
export declare function suitesDir(): string;
|
package/dist/pg/suite.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
//! T3: external *.pgtest suites run through the wasm runtime. The suite
|
|
2
|
-
//! syntax matches the Ruby loader (Parsanol::PG::Suite): a
|
|
3
|
-
//! `suite <name> [for <entry>] { ... }` header with accept / reject /
|
|
4
|
-
//! example lines. Green means Ruby and TypeScript agree on the contract.
|
|
5
|
-
import { readFileSync, readdirSync } from "node:fs";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
import { PgRuntime } from "./runtime.js";
|
|
8
|
-
const LINE = /^\s*(accept|reject|example)\s+"((?:[^"\\]|\\.)*)"(?:\s*\{([^}]*)\})?\s*$/;
|
|
9
|
-
const HEADER = /suite\s+(\w+)(?:\s+for\s+entry\s+(\w+))?\s*\{/;
|
|
10
|
-
/** Parse one *.pgtest file (same grammar as Parsanol::PG::Suite#read_file). */
|
|
11
|
-
export function readSuite(text, name) {
|
|
12
|
-
const body = text.replace(/^#[^\n]*\n/gm, "");
|
|
13
|
-
const header = HEADER.exec(body);
|
|
14
|
-
if (!header) {
|
|
15
|
-
throw new Error(`${name}: expected 'suite <name> [for <entry>] { ... }'`);
|
|
16
|
-
}
|
|
17
|
-
const tests = [];
|
|
18
|
-
for (const line of body.slice((header.index ?? 0) + header[0].length).split("\n")) {
|
|
19
|
-
if (line.trim() === "}")
|
|
20
|
-
break;
|
|
21
|
-
const match = LINE.exec(line);
|
|
22
|
-
if (!match)
|
|
23
|
-
continue;
|
|
24
|
-
const kind = match[1];
|
|
25
|
-
const rawInput = match[2] ?? "";
|
|
26
|
-
const input = JSON.parse(`"${rawInput}"`);
|
|
27
|
-
const expect = {};
|
|
28
|
-
if (match[3]) {
|
|
29
|
-
for (const pair of match[3].split(",")) {
|
|
30
|
-
const kv = /(\w+)\s*:\s*"((?:[^"\\]|\\.)*)"/.exec(pair);
|
|
31
|
-
const key = kv?.[1];
|
|
32
|
-
const raw = kv?.[2];
|
|
33
|
-
if (key && raw !== undefined)
|
|
34
|
-
expect[key] = JSON.parse(`"${raw}"`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
tests.push(kind === "example" ? { kind, input, expect } : { kind, input });
|
|
38
|
-
}
|
|
39
|
-
const entry = header[2];
|
|
40
|
-
return entry === undefined
|
|
41
|
-
? { name: header[1] ?? "suite", tests }
|
|
42
|
-
: { name: header[1] ?? "suite", entry, tests };
|
|
43
|
-
}
|
|
44
|
-
/** Load every *.pgtest file in a directory. */
|
|
45
|
-
export function loadSuites(dir) {
|
|
46
|
-
return readdirSync(dir)
|
|
47
|
-
.filter((file) => file.endsWith(".pgtest"))
|
|
48
|
-
.map((file) => readSuite(readFileSync(join(dir, file), "utf8"), file.replace(/\.pgtest$/, "")));
|
|
49
|
-
}
|
|
50
|
-
/** Run a suite against a runtime; returns failure descriptions. */
|
|
51
|
-
export function runSuite(runtime, suite) {
|
|
52
|
-
const failures = [];
|
|
53
|
-
const entry = suite.entry ?? runtime.entry;
|
|
54
|
-
for (const test of suite.tests) {
|
|
55
|
-
// The wasm runtime throws on parse failure; that is the reject signal.
|
|
56
|
-
let bound = null;
|
|
57
|
-
try {
|
|
58
|
-
bound = runtime.artifact.parseAndBind(entry, test.input);
|
|
59
|
-
}
|
|
60
|
-
catch {
|
|
61
|
-
bound = null;
|
|
62
|
-
}
|
|
63
|
-
if (test.kind === "reject") {
|
|
64
|
-
if (bound !== null) {
|
|
65
|
-
failures.push(`test ${JSON.stringify(test.input)}: expected the input to be rejected`);
|
|
66
|
-
}
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
if (bound === null) {
|
|
70
|
-
failures.push(`test ${JSON.stringify(test.input)}: expected the input to parse`);
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
if (test.kind === "example") {
|
|
74
|
-
const parsed = JSON.parse(bound);
|
|
75
|
-
for (const [key, value] of Object.entries(test.expect ?? {})) {
|
|
76
|
-
if (parsed[key] !== value) {
|
|
77
|
-
failures.push(`test ${JSON.stringify(test.input)}: expected captures ${key}: ${JSON.stringify(value)}, got ${JSON.stringify(parsed[key])}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return failures;
|
|
83
|
-
}
|
|
84
|
-
/** The default suites directory (the pubid-grammar checkout). */
|
|
85
|
-
export function suitesDir() {
|
|
86
|
-
const env = process.env.PG_SUITES_DIR;
|
|
87
|
-
if (env)
|
|
88
|
-
return env;
|
|
89
|
-
return join(PgRuntime.artifactsDir(), "..", "suites");
|
|
90
|
-
}
|