@spyglassmc/mcdoc 0.1.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.
@@ -0,0 +1,161 @@
1
+ import type { FullResourceLocation, ProcessorContext, SymbolPath } from '@spyglassmc/core';
2
+ import type { EnumKind } from '../node';
3
+ export interface Attribute {
4
+ name: string;
5
+ value: AttributeValue;
6
+ }
7
+ export declare type AttributeValue = string | {
8
+ [key: string | number]: AttributeValue;
9
+ };
10
+ export declare type NumericRange = [number | undefined, number | undefined];
11
+ interface StaticIndex {
12
+ kind: 'static';
13
+ value: string | {
14
+ keyword: 'fallback' | 'none' | 'unknown';
15
+ };
16
+ }
17
+ interface DynamicIndex {
18
+ kind: 'dynamic';
19
+ accessor: (string | {
20
+ keyword: 'key' | 'parent';
21
+ })[];
22
+ }
23
+ export declare type Index = StaticIndex | DynamicIndex;
24
+ export declare type ParallelIndices = [Index, ...Index[]];
25
+ export interface DispatcherData {
26
+ registry: FullResourceLocation | undefined;
27
+ index: ParallelIndices;
28
+ }
29
+ interface TypeBase {
30
+ kind: string;
31
+ attributes?: Attribute[];
32
+ indices?: ParallelIndices[];
33
+ }
34
+ export interface DispatcherType extends TypeBase, DispatcherData {
35
+ kind: 'dispatcher';
36
+ }
37
+ export interface StructType extends TypeBase {
38
+ kind: 'struct';
39
+ fields: ({
40
+ kind: 'field';
41
+ key: string;
42
+ type: McdocType;
43
+ symbol: SymbolPath;
44
+ } | {
45
+ kind: 'spread';
46
+ type: McdocType;
47
+ })[];
48
+ }
49
+ export interface EnumType extends TypeBase {
50
+ kind: 'enum';
51
+ enumKind: EnumKind;
52
+ values: {
53
+ identifier: string;
54
+ value: string | number | bigint;
55
+ }[];
56
+ }
57
+ export interface ReferenceType extends TypeBase {
58
+ kind: 'reference';
59
+ symbol?: SymbolPath;
60
+ index?: Index;
61
+ }
62
+ export interface UnionType<T extends McdocType = McdocType> extends TypeBase {
63
+ kind: 'union';
64
+ members: T[];
65
+ }
66
+ export declare const EmptyUnion: UnionType<never> & NoIndices;
67
+ export declare function createEmptyUnion(attributes?: Attribute[]): UnionType<never> & NoIndices;
68
+ interface KeywordType extends TypeBase {
69
+ kind: 'any' | 'boolean';
70
+ }
71
+ interface StringType extends TypeBase {
72
+ kind: 'string';
73
+ lengthRange?: NumericRange;
74
+ }
75
+ declare type LiteralValue = {
76
+ kind: 'boolean';
77
+ value: boolean;
78
+ } | {
79
+ kind: 'string';
80
+ value: string;
81
+ } | {
82
+ kind: 'number';
83
+ value: number;
84
+ suffix: 'b' | 's' | 'L' | 'f' | 'd' | undefined;
85
+ };
86
+ interface LiteralType extends TypeBase {
87
+ kind: 'literal';
88
+ value: LiteralValue;
89
+ }
90
+ interface NumericType extends TypeBase {
91
+ kind: 'byte' | 'short' | 'int' | 'long' | 'float' | 'double';
92
+ valueRange?: NumericRange;
93
+ }
94
+ interface PrimitiveArrayType extends TypeBase {
95
+ kind: 'byte_array' | 'int_array' | 'long_array';
96
+ valueRange?: NumericRange;
97
+ lengthRange?: NumericRange;
98
+ }
99
+ export interface ListType extends TypeBase {
100
+ kind: 'list';
101
+ item: McdocType;
102
+ lengthRange?: NumericRange;
103
+ }
104
+ export interface TupleType extends TypeBase {
105
+ kind: 'tuple';
106
+ items: McdocType[];
107
+ }
108
+ export declare type McdocType = DispatcherType | EnumType | KeywordType | ListType | LiteralType | NumericType | PrimitiveArrayType | ReferenceType | StringType | StructType | TupleType | UnionType;
109
+ export declare namespace McdocType {
110
+ function toString(type: McdocType | undefined): string;
111
+ }
112
+ /**
113
+ * A type that doesn't include a dispatcher type.
114
+ */
115
+ export declare type DispatchedType = Exclude<McdocType, DispatcherType | UnionType> | UnionType<DispatchedType>;
116
+ /**
117
+ * A type that doesn't include a reference type.
118
+ */
119
+ export declare type DereferencedType = Exclude<McdocType, ReferenceType | UnionType> | UnionType<DereferencedType>;
120
+ /**
121
+ * A type that doesn't include a dispatcher type or a reference type.
122
+ */
123
+ export declare type TangibleType = Exclude<McdocType, DispatcherType | ReferenceType | UnionType> | UnionType<TangibleType>;
124
+ /**
125
+ * A type that is {@link TangibleType} and doesn't have any indices.
126
+ */
127
+ export declare type ResolvedType = (Exclude<McdocType, DispatcherType | ReferenceType | UnionType> | UnionType<ResolvedType>) & NoIndices;
128
+ declare type NoIndices = {
129
+ indices?: undefined;
130
+ };
131
+ export interface FlatStructType extends TypeBase {
132
+ kind: 'flat_struct';
133
+ fields: Record<string, {
134
+ type: McdocType;
135
+ symbol: SymbolPath;
136
+ }>;
137
+ }
138
+ export declare const flattenUnionType: (union: UnionType) => UnionType;
139
+ export declare const unionTypes: (a: McdocType, b: McdocType) => McdocType;
140
+ export declare const simplifyUnionType: (union: UnionType) => McdocType;
141
+ export declare const simplifyListType: (list: ListType) => ListType;
142
+ export declare const simplifyType: (data: McdocType) => McdocType;
143
+ export declare const checkAssignability: ({ source, target }: {
144
+ source: McdocType | undefined;
145
+ target: McdocType | undefined;
146
+ }) => {
147
+ isAssignable: boolean;
148
+ errorMessage?: string;
149
+ };
150
+ /**
151
+ * https://spyglassmc.com/user/mcdoc/#p-RuntimeValue
152
+ */
153
+ export interface RuntimeValue {
154
+ asString(): string | undefined;
155
+ getKeyOnParent(): RuntimeValue | undefined;
156
+ getParent(): RuntimeValue | undefined;
157
+ getValue(key: string): RuntimeValue | undefined;
158
+ }
159
+ export declare function resolveType(inputType: McdocType, ctx: ProcessorContext, value: RuntimeValue | undefined): ResolvedType;
160
+ export {};
161
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,398 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveType = exports.checkAssignability = exports.simplifyType = exports.simplifyListType = exports.simplifyUnionType = exports.unionTypes = exports.flattenUnionType = exports.McdocType = exports.createEmptyUnion = exports.EmptyUnion = void 0;
4
+ const core_1 = require("@spyglassmc/core");
5
+ const locales_1 = require("@spyglassmc/locales");
6
+ exports.EmptyUnion = Object.freeze({ kind: 'union', members: [] });
7
+ function createEmptyUnion(attributes) {
8
+ return {
9
+ ...exports.EmptyUnion,
10
+ attributes,
11
+ };
12
+ }
13
+ exports.createEmptyUnion = createEmptyUnion;
14
+ var McdocType;
15
+ (function (McdocType) {
16
+ function toString(type) {
17
+ const rangeToString = (range) => {
18
+ if (!range) {
19
+ return '';
20
+ }
21
+ const [min, max] = range;
22
+ return min === max ? ` @ ${min}` : ` @ ${min ?? ''}..${max ?? ''}`;
23
+ };
24
+ const indicesToString = (indices) => {
25
+ const strings = [];
26
+ for (const index of core_1.Arrayable.toArray(indices)) {
27
+ if (index === undefined) {
28
+ strings.push('()');
29
+ }
30
+ else {
31
+ strings.push(index.kind === 'static'
32
+ ? `[${index.value}]`
33
+ : `[[${index.accessor.map(v => typeof v === 'string' ? v : v.keyword).join('.')}]]`);
34
+ }
35
+ }
36
+ return `[${strings.join(', ')}]`;
37
+ };
38
+ if (type === undefined) {
39
+ return '<unknown>';
40
+ }
41
+ switch (type.kind) {
42
+ case 'any':
43
+ case 'boolean':
44
+ return type.kind;
45
+ case 'byte':
46
+ return `byte${rangeToString(type.valueRange)}`;
47
+ case 'byte_array':
48
+ return `byte${rangeToString(type.valueRange)}[]${rangeToString(type.lengthRange)}`;
49
+ case 'dispatcher':
50
+ return `${type.registry ?? 'spyglass:unknown'}[${indicesToString(type.index)}]`;
51
+ case 'double':
52
+ return `double${rangeToString(type.valueRange)}`;
53
+ case 'enum':
54
+ return '<anonymous_enum>';
55
+ case 'float':
56
+ return `float${rangeToString(type.valueRange)}`;
57
+ case 'int':
58
+ return `int${rangeToString(type.valueRange)}`;
59
+ case 'int_array':
60
+ return `int${rangeToString(type.valueRange)}[]${rangeToString(type.lengthRange)}`;
61
+ case 'list':
62
+ return `[${toString(type.item)}]${rangeToString(type.lengthRange)}`;
63
+ case 'literal':
64
+ return `${type.value}`;
65
+ case 'long':
66
+ return `long${rangeToString(type.valueRange)}`;
67
+ case 'long_array':
68
+ return `long${rangeToString(type.valueRange)}[]${rangeToString(type.lengthRange)}`;
69
+ case 'reference':
70
+ return type.symbol?.path.join('::') ?? '<unknown_reference>';
71
+ case 'short':
72
+ return `short${rangeToString(type.valueRange)}`;
73
+ case 'string':
74
+ return `string${rangeToString(type.lengthRange)}`;
75
+ case 'struct':
76
+ return '<anonymous_compound>';
77
+ case 'tuple':
78
+ return `[${type.items.map(v => toString(v)).join(',')}${type.items.length === 1 ? ',' : ''}]`;
79
+ case 'union':
80
+ return `(${type.members.map(toString).join(' | ')})`;
81
+ }
82
+ }
83
+ McdocType.toString = toString;
84
+ })(McdocType = exports.McdocType || (exports.McdocType = {}));
85
+ var CheckResult;
86
+ (function (CheckResult) {
87
+ CheckResult[CheckResult["Nah"] = 0] = "Nah";
88
+ CheckResult[CheckResult["Assignable"] = 1] = "Assignable";
89
+ CheckResult[CheckResult["StrictlyAssignable"] = 3] = "StrictlyAssignable";
90
+ })(CheckResult || (CheckResult = {}));
91
+ const areRangesMatch = (s, t) => {
92
+ if (!t) {
93
+ return true;
94
+ }
95
+ if (!s) {
96
+ return false;
97
+ }
98
+ const [sMin, sMax] = s;
99
+ const [tMin, tMax] = t;
100
+ return (tMin === undefined || (sMin !== undefined && sMin >= tMin)) &&
101
+ (tMax === undefined || (sMax !== undefined && sMax <= tMax));
102
+ };
103
+ const flattenUnionType = (union) => {
104
+ const set = new Set();
105
+ const add = (data) => {
106
+ for (const existingMember of set) {
107
+ if ((check(data, existingMember) & CheckResult.StrictlyAssignable) === CheckResult.StrictlyAssignable) {
108
+ return;
109
+ }
110
+ if ((check(existingMember, data) & CheckResult.StrictlyAssignable) === CheckResult.StrictlyAssignable) {
111
+ set.delete(existingMember);
112
+ }
113
+ }
114
+ set.add(data);
115
+ };
116
+ for (const member of union.members) {
117
+ if (member.kind === 'union') {
118
+ (0, exports.flattenUnionType)(member).members.forEach(add);
119
+ }
120
+ else {
121
+ add(member);
122
+ }
123
+ }
124
+ return {
125
+ kind: 'union',
126
+ members: [...set],
127
+ };
128
+ };
129
+ exports.flattenUnionType = flattenUnionType;
130
+ const unionTypes = (a, b) => {
131
+ if ((check(a, b) & CheckResult.StrictlyAssignable) === CheckResult.StrictlyAssignable) {
132
+ return b;
133
+ }
134
+ if ((check(b, a) & CheckResult.StrictlyAssignable) === CheckResult.StrictlyAssignable) {
135
+ return a;
136
+ }
137
+ const ans = {
138
+ kind: 'union',
139
+ members: [
140
+ ...a.kind === 'union' ? a.members : [a],
141
+ ...b.kind === 'union' ? b.members : [b],
142
+ ],
143
+ };
144
+ return ans;
145
+ };
146
+ exports.unionTypes = unionTypes;
147
+ const simplifyUnionType = (union) => {
148
+ union = (0, exports.flattenUnionType)(union);
149
+ if (union.members.length === 1) {
150
+ return union.members[0];
151
+ }
152
+ return union;
153
+ };
154
+ exports.simplifyUnionType = simplifyUnionType;
155
+ const simplifyListType = (list) => ({
156
+ kind: 'list',
157
+ item: (0, exports.simplifyType)(list.item),
158
+ ...list.lengthRange ? { lengthRange: [...list.lengthRange] } : {},
159
+ });
160
+ exports.simplifyListType = simplifyListType;
161
+ const simplifyType = (data) => {
162
+ if (data.kind === 'union') {
163
+ data = (0, exports.simplifyUnionType)(data);
164
+ }
165
+ else if (data.kind === 'list') {
166
+ data = (0, exports.simplifyListType)(data);
167
+ }
168
+ return data;
169
+ };
170
+ exports.simplifyType = simplifyType;
171
+ const check = (s, t, errors = []) => {
172
+ const strictlyAssignableIfTrue = (value) => value ? CheckResult.StrictlyAssignable : CheckResult.Nah;
173
+ const assignableIfTrue = (value) => value ? CheckResult.Assignable : CheckResult.Nah;
174
+ let ans;
175
+ s = (0, exports.simplifyType)(s);
176
+ t = (0, exports.simplifyType)(t);
177
+ if (s.kind === 'any' || s.kind === 'reference' || t.kind === 'reference') {
178
+ // Reference types are treated as any for now.
179
+ ans = CheckResult.Assignable;
180
+ }
181
+ else if (t.kind === 'any') {
182
+ ans = CheckResult.StrictlyAssignable;
183
+ }
184
+ else if (s.kind === 'union') {
185
+ ans = assignableIfTrue(s.members.every(v => check(v, t, errors)));
186
+ }
187
+ else if (t.kind === 'union') {
188
+ ans = assignableIfTrue(t.members.some(v => check(s, v)));
189
+ }
190
+ else if (s.kind === 'boolean') {
191
+ ans = strictlyAssignableIfTrue(t.kind === 'boolean' || t.kind === 'byte');
192
+ }
193
+ else if (s.kind === 'byte') {
194
+ if (t.kind === 'boolean') {
195
+ ans = check(s, { kind: 'byte', valueRange: [0, 1] }, errors);
196
+ }
197
+ else if (t.kind === 'byte') {
198
+ ans = strictlyAssignableIfTrue(areRangesMatch(s.valueRange, t.valueRange));
199
+ }
200
+ else if (t.kind === 'enum') {
201
+ ans = assignableIfTrue(!t.enumKind || t.enumKind === 'byte');
202
+ }
203
+ else {
204
+ ans = CheckResult.Nah;
205
+ }
206
+ }
207
+ else if (s.kind === 'byte_array' || s.kind === 'int_array' || s.kind === 'long_array') {
208
+ ans = strictlyAssignableIfTrue(t.kind === s.kind && areRangesMatch(s.lengthRange, t.lengthRange) && areRangesMatch(s.valueRange, t.valueRange));
209
+ }
210
+ else if (s.kind === 'struct' || s.kind === 'dispatcher') {
211
+ ans = assignableIfTrue(t.kind === 'struct' || t.kind === 'dispatcher');
212
+ }
213
+ else if (s.kind === 'enum') {
214
+ ans = assignableIfTrue((t.kind === 'byte' || t.kind === 'float' || t.kind === 'double' || t.kind === 'int' || t.kind === 'long' || t.kind === 'short' || t.kind === 'string') && (!s.enumKind || s.enumKind === t.kind));
215
+ }
216
+ else if (s.kind === 'float' || s.kind === 'double' || s.kind === 'int' || s.kind === 'long' || s.kind === 'short') {
217
+ if (t.kind === s.kind) {
218
+ ans = strictlyAssignableIfTrue(areRangesMatch(s.valueRange, t.valueRange));
219
+ }
220
+ else if (t.kind === 'enum') {
221
+ ans = assignableIfTrue(!t.enumKind || t.enumKind === s.kind);
222
+ }
223
+ else {
224
+ ans = CheckResult.Nah;
225
+ }
226
+ }
227
+ else if (s.kind === 'list') {
228
+ if (t.kind === 'list' && areRangesMatch(s.lengthRange, t.lengthRange)) {
229
+ ans = check(s.item, t.item, errors);
230
+ }
231
+ else {
232
+ ans = CheckResult.Nah;
233
+ }
234
+ }
235
+ else if (s.kind === 'string') {
236
+ if (t.kind === 'string') {
237
+ ans = CheckResult.StrictlyAssignable;
238
+ }
239
+ else {
240
+ ans = assignableIfTrue(t.kind === 'enum' && (!t.enumKind || t.enumKind === 'string'));
241
+ }
242
+ }
243
+ else {
244
+ ans = CheckResult.Nah;
245
+ }
246
+ if (!ans) {
247
+ errors.push((0, locales_1.localize)('mcdoc.checker.type-not-assignable', (0, locales_1.localeQuote)(McdocType.toString(s)), (0, locales_1.localeQuote)(McdocType.toString(t))));
248
+ }
249
+ return ans;
250
+ };
251
+ const checkAssignability = ({ source, target }) => {
252
+ if (source === undefined || target === undefined) {
253
+ return { isAssignable: true };
254
+ }
255
+ const errors = [];
256
+ check(source, target, errors);
257
+ return {
258
+ isAssignable: errors.length === 0,
259
+ ...errors.length ? { errorMessage: errors.reverse().map((m, i) => `${' '.repeat(i)}${m}`).join('\n') } : {},
260
+ };
261
+ };
262
+ exports.checkAssignability = checkAssignability;
263
+ function resolveType(inputType, ctx, value) {
264
+ const type = getTangibleType(inputType, ctx, value);
265
+ let ans = (() => {
266
+ if (type.kind === 'union') {
267
+ return {
268
+ kind: 'union',
269
+ members: type.members.map(t => resolveType(t, ctx, value)),
270
+ attributes: type.attributes,
271
+ };
272
+ }
273
+ else {
274
+ return {
275
+ ...type,
276
+ indices: undefined,
277
+ };
278
+ }
279
+ })();
280
+ for (const parallelIndices of type.indices ?? []) {
281
+ ans = navigateParallelIndices(ans, parallelIndices, ctx, value);
282
+ }
283
+ return ans;
284
+ }
285
+ exports.resolveType = resolveType;
286
+ function dispatchType(type, ctx) {
287
+ throw '// TODO';
288
+ }
289
+ function dereferenceType(type, ctx) {
290
+ throw '// TODO';
291
+ }
292
+ function getTangibleType(type, ctx, value) {
293
+ let ans;
294
+ if (type.kind === 'dispatcher') {
295
+ const dispatchedType = dispatchType(type, ctx);
296
+ return getTangibleType(dispatchedType, ctx, value);
297
+ }
298
+ else if (type.kind === 'reference') {
299
+ const dereferencedType = dereferenceType(type, ctx);
300
+ return getTangibleType(dereferencedType, ctx, value);
301
+ }
302
+ else if (type.kind === 'union') {
303
+ ans = mapUnion(type, t => getTangibleType(t, ctx, value));
304
+ }
305
+ else {
306
+ ans = type;
307
+ }
308
+ return ans;
309
+ }
310
+ function navigateParallelIndices(type, indices, ctx, value) {
311
+ if (indices.length === 1) {
312
+ return navigateIndex(type, indices[0], ctx, value);
313
+ }
314
+ else {
315
+ return {
316
+ kind: 'union',
317
+ members: indices.map(i => navigateIndex(type, i, ctx, value)),
318
+ attributes: type.attributes,
319
+ };
320
+ }
321
+ }
322
+ function navigateIndex(type, index, ctx, value) {
323
+ if (type.kind === 'struct') {
324
+ const key = index.kind === 'static'
325
+ ? typeof index.value === 'string' ? index.value : undefined // Special static indices have no meaning on structs.
326
+ : resolveDynamicIndex(index, value);
327
+ if (key === undefined) {
328
+ return createEmptyUnion(type.attributes);
329
+ }
330
+ const flatStruct = flattenStruct(type, ctx, value);
331
+ return resolveType(flatStruct.fields[key].type, ctx, value);
332
+ }
333
+ else if (type.kind === 'union') {
334
+ return mapUnion(type, t => navigateIndex(t, index, ctx, value));
335
+ }
336
+ else {
337
+ return createEmptyUnion(type.attributes);
338
+ }
339
+ }
340
+ function resolveDynamicIndex(index, value) {
341
+ for (const key of index.accessor) {
342
+ if (value === undefined) {
343
+ break;
344
+ }
345
+ if (typeof key === 'string') {
346
+ value = value.getValue(key);
347
+ }
348
+ else if (key.keyword === 'key') {
349
+ value = value.getKeyOnParent();
350
+ }
351
+ else if (key.keyword === 'parent') {
352
+ value = value.getParent();
353
+ }
354
+ }
355
+ return value?.asString();
356
+ }
357
+ function mapUnion(type, mapper) {
358
+ const ans = {
359
+ kind: 'union',
360
+ members: type.members.map(mapper),
361
+ attributes: type.attributes,
362
+ indices: type.indices,
363
+ };
364
+ return ans;
365
+ }
366
+ function flattenStruct(type, ctx, value) {
367
+ const ans = {
368
+ kind: 'flat_struct',
369
+ fields: Object.create(null),
370
+ attributes: type.attributes,
371
+ indices: type.indices,
372
+ };
373
+ for (const field of type.fields) {
374
+ if (field.kind === 'spread') {
375
+ const target = resolveType(field.type, ctx, value);
376
+ addAttributes(ans, ...target.attributes ?? []);
377
+ if (target.kind === 'struct') {
378
+ const flatTarget = flattenStruct(target, ctx, value);
379
+ for (const [key, value] of Object.entries(flatTarget)) {
380
+ ans.fields[key] = value;
381
+ }
382
+ }
383
+ }
384
+ else {
385
+ ans.fields[field.key] = { type: field.type, symbol: field.symbol };
386
+ }
387
+ }
388
+ return ans;
389
+ }
390
+ function addAttributes(type, ...attributes) {
391
+ for (const attr of attributes) {
392
+ type.attributes ?? (type.attributes = []);
393
+ if (!type.attributes.some(a => a.name === attr.name)) {
394
+ type.attributes.push(attr);
395
+ }
396
+ }
397
+ }
398
+ //# sourceMappingURL=index.js.map
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@spyglassmc/mcdoc",
3
+ "version": "0.1.0",
4
+ "main": "lib/index.js",
5
+ "types": "lib/index.d.ts",
6
+ "author": "SPGoding",
7
+ "license": "MIT",
8
+ "directories": {
9
+ "test": "test/"
10
+ },
11
+ "scripts": {
12
+ "release": "npm publish",
13
+ "release:dry": "npm publish --dry-run"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "https://github.com/SpyglassMC/Spyglass.git"
21
+ },
22
+ "homepage": "https://spyglassmc.com",
23
+ "bugs": {
24
+ "url": "https://github.com/SpyglassMC/Spyglass/issues"
25
+ },
26
+ "dependencies": {
27
+ "@spyglassmc/core": "0.1.2",
28
+ "@spyglassmc/locales": "0.1.2"
29
+ }
30
+ }