@rpgm-tools/neo-angband-content 0.11.0
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/LICENSE.md +43 -0
- package/README.md +65 -0
- package/dist/compile.d.ts +10 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +42 -0
- package/dist/compile.js.map +1 -0
- package/dist/data-exactness.reader.d.ts +105 -0
- package/dist/data-exactness.reader.d.ts.map +1 -0
- package/dist/data-exactness.reader.js +547 -0
- package/dist/data-exactness.reader.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +70 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +365 -0
- package/dist/parser.js.map +1 -0
- package/dist/records.d.ts +101 -0
- package/dist/records.d.ts.map +1 -0
- package/dist/records.js +244 -0
- package/dist/records.js.map +1 -0
- package/dist/specs/generate.d.ts +12 -0
- package/dist/specs/generate.d.ts.map +1 -0
- package/dist/specs/generate.js +57 -0
- package/dist/specs/generate.js.map +1 -0
- package/dist/specs/index.d.ts +9 -0
- package/dist/specs/index.d.ts.map +1 -0
- package/dist/specs/index.js +60 -0
- package/dist/specs/index.js.map +1 -0
- package/dist/specs/init.d.ts +23 -0
- package/dist/specs/init.d.ts.map +1 -0
- package/dist/specs/init.js +305 -0
- package/dist/specs/init.js.map +1 -0
- package/dist/specs/misc.d.ts +33 -0
- package/dist/specs/misc.d.ts.map +1 -0
- package/dist/specs/misc.js +146 -0
- package/dist/specs/misc.js.map +1 -0
- package/dist/specs/mon-init.d.ts +22 -0
- package/dist/specs/mon-init.d.ts.map +1 -0
- package/dist/specs/mon-init.js +157 -0
- package/dist/specs/mon-init.js.map +1 -0
- package/dist/specs/obj-init.d.ts +19 -0
- package/dist/specs/obj-init.d.ts.map +1 -0
- package/dist/specs/obj-init.js +219 -0
- package/dist/specs/obj-init.js.map +1 -0
- package/dist/specs/ui-entry.d.ts +22 -0
- package/dist/specs/ui-entry.d.ts.map +1 -0
- package/dist/specs/ui-entry.js +69 -0
- package/dist/specs/ui-entry.js.map +1 -0
- package/dist/specs/visuals.d.ts +26 -0
- package/dist/specs/visuals.d.ts.map +1 -0
- package/dist/specs/visuals.js +35 -0
- package/dist/specs/visuals.js.map +1 -0
- package/pack/activation.json +3340 -0
- package/pack/artifact.json +5146 -0
- package/pack/blow_effects.json +370 -0
- package/pack/blow_methods.json +286 -0
- package/pack/body.json +59 -0
- package/pack/brand.json +100 -0
- package/pack/chest_trap.json +117 -0
- package/pack/class.json +5501 -0
- package/pack/constants.json +470 -0
- package/pack/curse.json +580 -0
- package/pack/dungeon_profile.json +836 -0
- package/pack/ego_item.json +2900 -0
- package/pack/flavor.json +1541 -0
- package/pack/hints.json +303 -0
- package/pack/history.json +1656 -0
- package/pack/manifest.json +52 -0
- package/pack/monster.json +29392 -0
- package/pack/monster_base.json +688 -0
- package/pack/monster_spell.json +2735 -0
- package/pack/names.json +1204 -0
- package/pack/object.json +11877 -0
- package/pack/object_base.json +364 -0
- package/pack/object_property.json +2034 -0
- package/pack/p_race.json +447 -0
- package/pack/pain.json +150 -0
- package/pack/pit.json +617 -0
- package/pack/player_property.json +519 -0
- package/pack/player_timed.json +1321 -0
- package/pack/projection.json +664 -0
- package/pack/quest.json +18 -0
- package/pack/realm.json +34 -0
- package/pack/room_template.json +9571 -0
- package/pack/shape.json +263 -0
- package/pack/slay.json +116 -0
- package/pack/store.json +675 -0
- package/pack/summon.json +143 -0
- package/pack/terrain.json +511 -0
- package/pack/trap.json +1483 -0
- package/pack/ui_entry.json +382 -0
- package/pack/ui_entry_base.json +60 -0
- package/pack/ui_entry_renderer.json +43 -0
- package/pack/ui_knowledge.json +302 -0
- package/pack/vault.json +4355 -0
- package/pack/visuals.json +680 -0
- package/pack/world.json +1030 -0
- package/package.json +67 -0
- package/src/compile.ts +55 -0
- package/src/data-exactness.reader.ts +559 -0
- package/src/index.ts +31 -0
- package/src/parser.ts +406 -0
- package/src/records.ts +341 -0
- package/src/specs/generate.ts +61 -0
- package/src/specs/index.ts +103 -0
- package/src/specs/init.ts +322 -0
- package/src/specs/misc.ts +153 -0
- package/src/specs/mon-init.ts +165 -0
- package/src/specs/obj-init.ts +230 -0
- package/src/specs/ui-entry.ts +74 -0
- package/src/specs/visuals.ts +37 -0
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic parser for Angband's gamedata record format.
|
|
3
|
+
*
|
|
4
|
+
* Faithful port of the line-level semantics of reference/src/parser.c
|
|
5
|
+
* (Angband 4.2.6):
|
|
6
|
+
*
|
|
7
|
+
* - A line is `directive:field1:field2:...`. Leading whitespace is skipped;
|
|
8
|
+
* blank lines and lines starting with `#` are ignored.
|
|
9
|
+
* - Each directive is registered with a format string like upstream
|
|
10
|
+
* parser_reg(), e.g. "name str name" or "effect sym eff ?sym type
|
|
11
|
+
* ?int radius ?int other". A `?` prefix marks an optional field; a
|
|
12
|
+
* mandatory field may not follow an optional one, and no field may
|
|
13
|
+
* follow a `str` field.
|
|
14
|
+
* - int/uint/sym/rand fields are tokenized on `:` with strtok() semantics
|
|
15
|
+
* (consecutive colons collapse). `str` consumes the rest of the line,
|
|
16
|
+
* colons included. `char` consumes exactly one character (which may be
|
|
17
|
+
* a space or a colon) and then expects `:` or end of line.
|
|
18
|
+
* - int uses strtol(_, _, 0) semantics (hex/octal prefixes accepted,
|
|
19
|
+
* trailing garbage ignored); uint additionally rejects a leading `-`.
|
|
20
|
+
* - rand fields are validated against upstream parse_random() but the RAW
|
|
21
|
+
* dice string (e.g. "1+2d3M4") is preserved; it is never evaluated.
|
|
22
|
+
*/
|
|
23
|
+
export type FieldType = "int" | "uint" | "sym" | "str" | "char" | "rand";
|
|
24
|
+
export interface FieldSpec {
|
|
25
|
+
readonly type: FieldType;
|
|
26
|
+
readonly name: string;
|
|
27
|
+
readonly optional: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface DirectiveSignature {
|
|
30
|
+
readonly directive: string;
|
|
31
|
+
readonly fields: readonly FieldSpec[];
|
|
32
|
+
}
|
|
33
|
+
export type ParseErrorCode = "MISSING_FIELD" | "UNDEFINED_DIRECTIVE" | "FIELD_TOO_LONG" | "NOT_NUMBER" | "NOT_RANDOM" | "INVALID_SPEC";
|
|
34
|
+
/** Parse failure; `code` mirrors the upstream PARSE_ERROR_* it corresponds to. */
|
|
35
|
+
export declare class ParseError extends Error {
|
|
36
|
+
readonly code: ParseErrorCode;
|
|
37
|
+
constructor(code: ParseErrorCode, message: string);
|
|
38
|
+
}
|
|
39
|
+
/** A successfully parsed line: the directive plus its named field values. */
|
|
40
|
+
export interface ParsedLine {
|
|
41
|
+
readonly directive: string;
|
|
42
|
+
/** Field name -> value, in signature order. Absent optionals are omitted. */
|
|
43
|
+
readonly values: Readonly<Record<string, string | number>>;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse a registration format string exactly like upstream parse_specs():
|
|
47
|
+
* `fmt ::= directive [type name]*` where `type` may carry a `?` prefix.
|
|
48
|
+
*/
|
|
49
|
+
export declare function parseSignature(fmt: string): DirectiveSignature;
|
|
50
|
+
/**
|
|
51
|
+
* Validate a random-value (dice) string. Faithful port of parse_random()
|
|
52
|
+
* in reference/src/parser.c; only validity is reported, the raw string is
|
|
53
|
+
* what gets stored.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isValidRandom(s: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Parse one line against a set of registered directives.
|
|
58
|
+
*
|
|
59
|
+
* Returns null for blank lines and comments. Throws ParseError for
|
|
60
|
+
* undefined directives, missing mandatory fields, malformed numbers and
|
|
61
|
+
* malformed dice strings, mirroring parser_parse().
|
|
62
|
+
*
|
|
63
|
+
* parser_new (parser.c:99) has no counterpart: upstream allocates a mutable
|
|
64
|
+
* `struct parser` that parser_reg fills with hooks and parser_setpriv threads
|
|
65
|
+
* state through. Here the directive table is the caller's own `lookup` and
|
|
66
|
+
* parsing is this pure function, so there is nothing to allocate or free
|
|
67
|
+
* (parser_destroy likewise).
|
|
68
|
+
*/
|
|
69
|
+
export declare function parseLine(line: string, lookup: (directive: string) => DirectiveSignature | undefined): ParsedLine | null;
|
|
70
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;CACvC;AAED,MAAM,MAAM,cAAc,GACtB,eAAe,GACf,qBAAqB,GACrB,gBAAgB,GAChB,YAAY,GACZ,YAAY,GACZ,cAAc,CAAC;AAEnB,kFAAkF;AAClF,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;gBAElB,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM;CAKlD;AAED,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6EAA6E;IAC7E,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;CAC5D;AAQD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAiC9D;AAiGD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CA0DhD;AAiED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,GAC5D,UAAU,GAAG,IAAI,CA2DnB"}
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic parser for Angband's gamedata record format.
|
|
3
|
+
*
|
|
4
|
+
* Faithful port of the line-level semantics of reference/src/parser.c
|
|
5
|
+
* (Angband 4.2.6):
|
|
6
|
+
*
|
|
7
|
+
* - A line is `directive:field1:field2:...`. Leading whitespace is skipped;
|
|
8
|
+
* blank lines and lines starting with `#` are ignored.
|
|
9
|
+
* - Each directive is registered with a format string like upstream
|
|
10
|
+
* parser_reg(), e.g. "name str name" or "effect sym eff ?sym type
|
|
11
|
+
* ?int radius ?int other". A `?` prefix marks an optional field; a
|
|
12
|
+
* mandatory field may not follow an optional one, and no field may
|
|
13
|
+
* follow a `str` field.
|
|
14
|
+
* - int/uint/sym/rand fields are tokenized on `:` with strtok() semantics
|
|
15
|
+
* (consecutive colons collapse). `str` consumes the rest of the line,
|
|
16
|
+
* colons included. `char` consumes exactly one character (which may be
|
|
17
|
+
* a space or a colon) and then expects `:` or end of line.
|
|
18
|
+
* - int uses strtol(_, _, 0) semantics (hex/octal prefixes accepted,
|
|
19
|
+
* trailing garbage ignored); uint additionally rejects a leading `-`.
|
|
20
|
+
* - rand fields are validated against upstream parse_random() but the RAW
|
|
21
|
+
* dice string (e.g. "1+2d3M4") is preserved; it is never evaluated.
|
|
22
|
+
*/
|
|
23
|
+
/** Parse failure; `code` mirrors the upstream PARSE_ERROR_* it corresponds to. */
|
|
24
|
+
export class ParseError extends Error {
|
|
25
|
+
code;
|
|
26
|
+
constructor(code, message) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = "ParseError";
|
|
29
|
+
this.code = code;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const FIELD_TYPES = ["int", "uint", "sym", "str", "char", "rand"];
|
|
33
|
+
function isFieldType(s) {
|
|
34
|
+
return FIELD_TYPES.includes(s);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parse a registration format string exactly like upstream parse_specs():
|
|
38
|
+
* `fmt ::= directive [type name]*` where `type` may carry a `?` prefix.
|
|
39
|
+
*/
|
|
40
|
+
export function parseSignature(fmt) {
|
|
41
|
+
const tokens = fmt.split(" ").filter((t) => t.length > 0);
|
|
42
|
+
const directive = tokens.shift();
|
|
43
|
+
if (directive === undefined) {
|
|
44
|
+
throw new ParseError("INVALID_SPEC", `empty format string`);
|
|
45
|
+
}
|
|
46
|
+
const fields = [];
|
|
47
|
+
while (tokens.length > 0) {
|
|
48
|
+
const rawType = tokens.shift();
|
|
49
|
+
if (rawType === undefined) {
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
const name = tokens.shift();
|
|
53
|
+
if (name === undefined) {
|
|
54
|
+
throw new ParseError("INVALID_SPEC", `type without name in "${fmt}"`);
|
|
55
|
+
}
|
|
56
|
+
const optional = rawType.startsWith("?");
|
|
57
|
+
const typeName = optional ? rawType.slice(1) : rawType;
|
|
58
|
+
if (!isFieldType(typeName)) {
|
|
59
|
+
throw new ParseError("INVALID_SPEC", `unknown field type "${rawType}" in "${fmt}"`);
|
|
60
|
+
}
|
|
61
|
+
const prev = fields[fields.length - 1];
|
|
62
|
+
if (prev !== undefined) {
|
|
63
|
+
if (!optional && prev.optional) {
|
|
64
|
+
throw new ParseError("INVALID_SPEC", `mandatory field after optional in "${fmt}"`);
|
|
65
|
+
}
|
|
66
|
+
if (prev.type === "str") {
|
|
67
|
+
throw new ParseError("INVALID_SPEC", `field after str field in "${fmt}"`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
fields.push({ type: typeName, name, optional });
|
|
71
|
+
}
|
|
72
|
+
return { directive, fields };
|
|
73
|
+
}
|
|
74
|
+
/** C isspace() for the default locale. */
|
|
75
|
+
function isCSpace(code) {
|
|
76
|
+
return (code === 0x20 || // space
|
|
77
|
+
code === 0x09 || // \t
|
|
78
|
+
code === 0x0a || // \n
|
|
79
|
+
code === 0x0b || // \v
|
|
80
|
+
code === 0x0c || // \f
|
|
81
|
+
code === 0x0d // \r
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
const INT_MAX = 2147483647;
|
|
85
|
+
function digitValue(ch, base) {
|
|
86
|
+
let v;
|
|
87
|
+
const code = ch.charCodeAt(0);
|
|
88
|
+
if (code >= 0x30 && code <= 0x39) {
|
|
89
|
+
v = code - 0x30;
|
|
90
|
+
}
|
|
91
|
+
else if (code >= 0x41 && code <= 0x5a) {
|
|
92
|
+
v = code - 0x41 + 10;
|
|
93
|
+
}
|
|
94
|
+
else if (code >= 0x61 && code <= 0x7a) {
|
|
95
|
+
v = code - 0x61 + 10;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
return v < base ? v : null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* C strtol/strtoul-style number scan. With `base0` the base is inferred
|
|
104
|
+
* from a 0x/0 prefix as strtol(_, _, 0) does; otherwise base 10.
|
|
105
|
+
* Returns null when no digits were consumed (endptr == nptr in C).
|
|
106
|
+
*/
|
|
107
|
+
function scanNumber(s, start, base0) {
|
|
108
|
+
let i = start;
|
|
109
|
+
while (i < s.length && isCSpace(s.charCodeAt(i))) {
|
|
110
|
+
i++;
|
|
111
|
+
}
|
|
112
|
+
let negative = false;
|
|
113
|
+
const sign = s[i];
|
|
114
|
+
if (sign === "+" || sign === "-") {
|
|
115
|
+
negative = sign === "-";
|
|
116
|
+
i++;
|
|
117
|
+
}
|
|
118
|
+
let base = 10;
|
|
119
|
+
if (base0 && s[i] === "0") {
|
|
120
|
+
const next = s[i + 1];
|
|
121
|
+
const afterPrefix = s[i + 2];
|
|
122
|
+
if ((next === "x" || next === "X") &&
|
|
123
|
+
afterPrefix !== undefined &&
|
|
124
|
+
digitValue(afterPrefix, 16) !== null) {
|
|
125
|
+
base = 16;
|
|
126
|
+
i += 2;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
base = 8;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const digitsStart = i;
|
|
133
|
+
let value = 0;
|
|
134
|
+
while (i < s.length) {
|
|
135
|
+
const ch = s[i];
|
|
136
|
+
if (ch === undefined) {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
const d = digitValue(ch, base);
|
|
140
|
+
if (d === null) {
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
value = value * base + d;
|
|
144
|
+
i++;
|
|
145
|
+
}
|
|
146
|
+
if (i === digitsStart) {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
return { value: negative ? -value : value, end: i, negative };
|
|
150
|
+
}
|
|
151
|
+
function containsOnlySpaces(s, from) {
|
|
152
|
+
for (let i = from; i < s.length; i++) {
|
|
153
|
+
if (!isCSpace(s.charCodeAt(i))) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validate a random-value (dice) string. Faithful port of parse_random()
|
|
161
|
+
* in reference/src/parser.c; only validity is reported, the raw string is
|
|
162
|
+
* what gets stored.
|
|
163
|
+
*/
|
|
164
|
+
export function isValidRandom(s) {
|
|
165
|
+
let pos = 0;
|
|
166
|
+
let i = 0;
|
|
167
|
+
let minI = 1;
|
|
168
|
+
if (s[0] === "-") {
|
|
169
|
+
pos++;
|
|
170
|
+
}
|
|
171
|
+
for (;;) {
|
|
172
|
+
const c = s[pos];
|
|
173
|
+
if (c === "d") {
|
|
174
|
+
if (i > 2) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
if (i < 2) {
|
|
178
|
+
/* 'd' with no preceding number implies one die. */
|
|
179
|
+
i = 2;
|
|
180
|
+
}
|
|
181
|
+
minI = 3;
|
|
182
|
+
pos++;
|
|
183
|
+
}
|
|
184
|
+
else if (c === "M") {
|
|
185
|
+
if (i === 2) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
i = 3;
|
|
189
|
+
minI = 4;
|
|
190
|
+
pos++;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
const scan = scanNumber(s, pos, false);
|
|
194
|
+
if (scan === null) {
|
|
195
|
+
/* Trailing garbage or not enough values are not accepted. */
|
|
196
|
+
if (!containsOnlySpaces(s, pos) || i < minI) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
if (scan.value > INT_MAX || (scan.negative && scan.value !== 0) || s[pos] === "+") {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
pos = scan.end;
|
|
205
|
+
if (i === 0) {
|
|
206
|
+
if (s[pos] === "d") {
|
|
207
|
+
i = 1;
|
|
208
|
+
}
|
|
209
|
+
else if (s[pos] === "+") {
|
|
210
|
+
pos++;
|
|
211
|
+
minI = 3;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
if (!containsOnlySpaces(s, scan.end)) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else if (i === 4) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
i++;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Cursor over one line, replicating the strtok() usage in parser_parse().
|
|
230
|
+
*/
|
|
231
|
+
class Tokenizer {
|
|
232
|
+
s;
|
|
233
|
+
pos = 0;
|
|
234
|
+
constructor(s) {
|
|
235
|
+
this.s = s;
|
|
236
|
+
}
|
|
237
|
+
/** strtok(_, ":"): skip leading colons, take up to the next colon. */
|
|
238
|
+
nextToken() {
|
|
239
|
+
while (this.pos < this.s.length && this.s[this.pos] === ":") {
|
|
240
|
+
this.pos++;
|
|
241
|
+
}
|
|
242
|
+
if (this.pos >= this.s.length) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
const start = this.pos;
|
|
246
|
+
while (this.pos < this.s.length && this.s[this.pos] !== ":") {
|
|
247
|
+
this.pos++;
|
|
248
|
+
}
|
|
249
|
+
const tok = this.s.slice(start, this.pos);
|
|
250
|
+
if (this.pos < this.s.length) {
|
|
251
|
+
this.pos++; /* consume the delimiter, as strtok() does */
|
|
252
|
+
}
|
|
253
|
+
return tok;
|
|
254
|
+
}
|
|
255
|
+
/** strtok(_, ""): the untouched rest of the line, or null at the end. */
|
|
256
|
+
rest() {
|
|
257
|
+
if (this.pos >= this.s.length) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
const tok = this.s.slice(this.pos);
|
|
261
|
+
this.pos = this.s.length;
|
|
262
|
+
return tok;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* A char field: exactly one character (possibly a space or a colon),
|
|
266
|
+
* then a `:` separator or end of line. Anything else is FIELD_TOO_LONG.
|
|
267
|
+
*/
|
|
268
|
+
takeChar(fieldName) {
|
|
269
|
+
if (this.pos >= this.s.length) {
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
const cp = this.s.codePointAt(this.pos);
|
|
273
|
+
/* pos is in bounds, so a code point exists. */
|
|
274
|
+
const ch = String.fromCodePoint(cp ?? 0);
|
|
275
|
+
this.pos += ch.length;
|
|
276
|
+
if (this.pos < this.s.length) {
|
|
277
|
+
if (this.s[this.pos] === ":") {
|
|
278
|
+
this.pos++;
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
throw new ParseError("FIELD_TOO_LONG", fieldName);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return ch;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Parse one line against a set of registered directives.
|
|
289
|
+
*
|
|
290
|
+
* Returns null for blank lines and comments. Throws ParseError for
|
|
291
|
+
* undefined directives, missing mandatory fields, malformed numbers and
|
|
292
|
+
* malformed dice strings, mirroring parser_parse().
|
|
293
|
+
*
|
|
294
|
+
* parser_new (parser.c:99) has no counterpart: upstream allocates a mutable
|
|
295
|
+
* `struct parser` that parser_reg fills with hooks and parser_setpriv threads
|
|
296
|
+
* state through. Here the directive table is the caller's own `lookup` and
|
|
297
|
+
* parsing is this pure function, so there is nothing to allocate or free
|
|
298
|
+
* (parser_destroy likewise).
|
|
299
|
+
*/
|
|
300
|
+
export function parseLine(line, lookup) {
|
|
301
|
+
let start = 0;
|
|
302
|
+
while (start < line.length && isCSpace(line.charCodeAt(start))) {
|
|
303
|
+
start++;
|
|
304
|
+
}
|
|
305
|
+
if (start >= line.length || line[start] === "#") {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
const t = new Tokenizer(line.slice(start));
|
|
309
|
+
const directiveTok = t.nextToken();
|
|
310
|
+
if (directiveTok === null) {
|
|
311
|
+
throw new ParseError("MISSING_FIELD", "missing directive");
|
|
312
|
+
}
|
|
313
|
+
const sig = lookup(directiveTok);
|
|
314
|
+
if (sig === undefined) {
|
|
315
|
+
throw new ParseError("UNDEFINED_DIRECTIVE", directiveTok);
|
|
316
|
+
}
|
|
317
|
+
const values = {};
|
|
318
|
+
for (const field of sig.fields) {
|
|
319
|
+
let tok;
|
|
320
|
+
if (field.type === "char") {
|
|
321
|
+
tok = t.takeChar(field.name);
|
|
322
|
+
}
|
|
323
|
+
else if (field.type === "str") {
|
|
324
|
+
tok = t.rest();
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
tok = t.nextToken();
|
|
328
|
+
}
|
|
329
|
+
if (tok === null) {
|
|
330
|
+
if (!field.optional) {
|
|
331
|
+
throw new ParseError("MISSING_FIELD", field.name);
|
|
332
|
+
}
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
if (field.type === "int") {
|
|
336
|
+
const scan = scanNumber(tok, 0, true);
|
|
337
|
+
if (scan === null) {
|
|
338
|
+
throw new ParseError("NOT_NUMBER", field.name);
|
|
339
|
+
}
|
|
340
|
+
values[field.name] = scan.value;
|
|
341
|
+
}
|
|
342
|
+
else if (field.type === "uint") {
|
|
343
|
+
if (tok[0] === "-") {
|
|
344
|
+
throw new ParseError("NOT_NUMBER", field.name);
|
|
345
|
+
}
|
|
346
|
+
const scan = scanNumber(tok, 0, true);
|
|
347
|
+
if (scan === null || scan.negative) {
|
|
348
|
+
throw new ParseError("NOT_NUMBER", field.name);
|
|
349
|
+
}
|
|
350
|
+
values[field.name] = scan.value;
|
|
351
|
+
}
|
|
352
|
+
else if (field.type === "rand") {
|
|
353
|
+
if (!isValidRandom(tok)) {
|
|
354
|
+
throw new ParseError("NOT_RANDOM", field.name);
|
|
355
|
+
}
|
|
356
|
+
values[field.name] = tok;
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
/* sym, str, char: stored verbatim */
|
|
360
|
+
values[field.name] = tok;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return { directive: sig.directive, values };
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Record assembly: turns a parsed gamedata file into JSON records.
|
|
3
|
+
*
|
|
4
|
+
* A FileSpec mirrors one upstream init_parse_* registration set. Records
|
|
5
|
+
* begin at the record-start directive; other directives attach to the
|
|
6
|
+
* current record. Directives marked `repeat` accumulate into arrays (in
|
|
7
|
+
* file order). Directives marked `childOf` attach to the most recent
|
|
8
|
+
* instance of one of their parent directives, replicating the upstream
|
|
9
|
+
* "walk to the last effect/book/spell/level" parsing idiom; when no parent
|
|
10
|
+
* instance exists yet in the record they attach to the record itself
|
|
11
|
+
* (upstream treats that as "human, not parser error") unless the directive
|
|
12
|
+
* is marked `requireParent`, where upstream instead reports
|
|
13
|
+
* PARSE_ERROR_MISSING_RECORD_HEADER. A repeat of a non-`repeat` directive is
|
|
14
|
+
* otherwise silently overwrite the previous value, matching the usual
|
|
15
|
+
* upstream handler behavior. Only explicitly marked directives reject a
|
|
16
|
+
* repeat with PARSE_ERROR_REPEATED_DIRECTIVE.
|
|
17
|
+
*
|
|
18
|
+
* Output key order is spec order (the upstream registration order), not
|
|
19
|
+
* encounter order, so records diff cleanly against the .txt sources.
|
|
20
|
+
*/
|
|
21
|
+
export interface DirectiveDef {
|
|
22
|
+
/** The exact upstream parser_reg() format string. */
|
|
23
|
+
readonly fmt: string;
|
|
24
|
+
/** Accumulate repeated occurrences into an array. */
|
|
25
|
+
readonly repeat?: boolean;
|
|
26
|
+
/** Attach to the most recent instance of one of these directives. */
|
|
27
|
+
readonly childOf?: readonly string[];
|
|
28
|
+
/** Preserve encounter order for a cross-directive repeated list. */
|
|
29
|
+
readonly orderKey?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Reject a repeated occurrence. This models the two upstream handlers
|
|
32
|
+
* that explicitly test already-initialized state: parse_feat_name
|
|
33
|
+
* (init.c:2059-2060) and parse_class_magic (init.c:3714-3716).
|
|
34
|
+
*/
|
|
35
|
+
readonly rejectRepeated?: (prior: JsonValue) => boolean;
|
|
36
|
+
/**
|
|
37
|
+
* This directive is an ERROR when none of its `childOf` parents has been
|
|
38
|
+
* seen yet, rather than attaching to the enclosing record. Upstream is
|
|
39
|
+
* split on this: class.txt, object.txt and trap.txt treat an orphan
|
|
40
|
+
* effect detail as "human, not parser, error" and return
|
|
41
|
+
* PARSE_ERROR_NONE, but player-timed.c's four effect-* handlers return
|
|
42
|
+
* PARSE_ERROR_MISSING_RECORD_HEADER when `ps->e` is NULL
|
|
43
|
+
* (player-timed.c L473-560, asserted by ptimed.c test_missing_effect0).
|
|
44
|
+
*/
|
|
45
|
+
readonly requireParent?: boolean;
|
|
46
|
+
/** Error returned when no `childOf` parent has been seen. */
|
|
47
|
+
readonly orphanError?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Handler-specific state validation that is expressible from already
|
|
50
|
+
* assembled records. This deliberately cannot see runtime registries.
|
|
51
|
+
*/
|
|
52
|
+
readonly validate?: (context: DirectiveValidationContext) => string | undefined;
|
|
53
|
+
}
|
|
54
|
+
export interface DirectiveValidationContext {
|
|
55
|
+
readonly record: JsonObject;
|
|
56
|
+
readonly target: JsonObject;
|
|
57
|
+
readonly values: Readonly<Record<string, string | number>>;
|
|
58
|
+
}
|
|
59
|
+
export interface FileSpec {
|
|
60
|
+
/** Gamedata file stem, e.g. "monster" for lib/gamedata/monster.txt. */
|
|
61
|
+
readonly name: string;
|
|
62
|
+
/** Upstream C sources that register this file's parser. */
|
|
63
|
+
readonly upstream: readonly string[];
|
|
64
|
+
/** Directive that begins a record; null for singleton files (constants). */
|
|
65
|
+
readonly recordStart: string | null;
|
|
66
|
+
/** Directives collected before the first record (e.g. object_base defaults). */
|
|
67
|
+
readonly header?: readonly string[];
|
|
68
|
+
readonly directives: readonly DirectiveDef[];
|
|
69
|
+
}
|
|
70
|
+
export type JsonPrimitive = string | number | boolean;
|
|
71
|
+
export type JsonValue = JsonPrimitive | JsonValue[] | {
|
|
72
|
+
[key: string]: JsonValue;
|
|
73
|
+
};
|
|
74
|
+
export type JsonObject = {
|
|
75
|
+
[key: string]: JsonValue;
|
|
76
|
+
};
|
|
77
|
+
export interface CompiledFile {
|
|
78
|
+
file: string;
|
|
79
|
+
source: string;
|
|
80
|
+
header?: JsonObject;
|
|
81
|
+
records: JsonObject[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Compile the text of one gamedata file into JSON records.
|
|
85
|
+
* Throws on any line the upstream parser would reject. Non-repeating
|
|
86
|
+
* directives overwrite their previous value unless their spec explicitly
|
|
87
|
+
* models an upstream repeated-directive error.
|
|
88
|
+
*
|
|
89
|
+
* This is the port's `parse_file` (datafile.c:87) plus the `fp->run` half of
|
|
90
|
+
* `run_parser` (datafile.c:45): one line-at-a-time loop over the file feeding
|
|
91
|
+
* `parseLine` (upstream `parser_parse`). Two deliberate differences, both
|
|
92
|
+
* unobservable on shipped 4.2.6 data (W1-CAVE-SAVE-DATA.md, datafile section):
|
|
93
|
+
* - Upstream tries ANGBAND_DIR_USER/<name>.txt before ANGBAND_DIR_GAMEDATA
|
|
94
|
+
* (datafile.c:96-105); user data-file overriding is the mod substrate's job
|
|
95
|
+
* here, and the compiler reads only reference/lib/gamedata.
|
|
96
|
+
* - Upstream logs up to get_parser_error_limit() parse errors and returns the
|
|
97
|
+
* FIRST (datafile.c:113-141); this throws on the first. `parse_file_quit_
|
|
98
|
+
* not_found` (datafile.c:71) is the readFileSync throw in compile.ts.
|
|
99
|
+
*/
|
|
100
|
+
export declare function compileGamedata(text: string, spec: FileSpec): CompiledFile;
|
|
101
|
+
//# sourceMappingURL=records.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../src/records.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAKH,MAAM,WAAW,YAAY;IAC3B,qDAAqD;IACrD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,oEAAoE;IACpE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC;IACxD;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,6DAA6D;IAC7D,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,0BAA0B,KAAK,MAAM,GAAG,SAAS,CAAC;CACjF;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,QAAQ;IACvB,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,SAAS,YAAY,EAAE,CAAC;CAC9C;AAED,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACtD,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,SAAS,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AACnF,MAAM,MAAM,UAAU,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEtD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AAsGD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,YAAY,CAwI1E"}
|