@k67/kaitai-struct-ts 0.6.0 → 0.7.1

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/dist/cli.js ADDED
@@ -0,0 +1,2776 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // src/cli.ts
5
+ var import_fs = require("fs");
6
+ var import_path = require("path");
7
+ var import_util = require("util");
8
+
9
+ // src/utils/errors.ts
10
+ var KaitaiError = class _KaitaiError extends Error {
11
+ constructor(message, position) {
12
+ super(message);
13
+ this.position = position;
14
+ this.name = "KaitaiError";
15
+ Object.setPrototypeOf(this, _KaitaiError.prototype);
16
+ }
17
+ };
18
+ var ValidationError = class _ValidationError extends KaitaiError {
19
+ constructor(message, position) {
20
+ super(message, position);
21
+ this.name = "ValidationError";
22
+ Object.setPrototypeOf(this, _ValidationError.prototype);
23
+ }
24
+ };
25
+ var ParseError = class _ParseError extends KaitaiError {
26
+ constructor(message, position) {
27
+ super(message, position);
28
+ this.name = "ParseError";
29
+ Object.setPrototypeOf(this, _ParseError.prototype);
30
+ }
31
+ };
32
+ var EOFError = class _EOFError extends KaitaiError {
33
+ constructor(message = "Unexpected end of stream", position) {
34
+ super(message, position);
35
+ this.name = "EOFError";
36
+ Object.setPrototypeOf(this, _EOFError.prototype);
37
+ }
38
+ };
39
+ var NotImplementedError = class _NotImplementedError extends KaitaiError {
40
+ constructor(feature) {
41
+ super(`Feature not yet implemented: ${feature}`);
42
+ this.name = "NotImplementedError";
43
+ Object.setPrototypeOf(this, _NotImplementedError.prototype);
44
+ }
45
+ };
46
+
47
+ // src/utils/encoding.ts
48
+ function decodeString(bytes, encoding) {
49
+ const normalizedEncoding = encoding.toLowerCase().replace(/[-_]/g, "");
50
+ switch (normalizedEncoding) {
51
+ case "utf8":
52
+ case "utf-8":
53
+ return decodeUtf8(bytes);
54
+ case "ascii":
55
+ case "usascii":
56
+ return decodeAscii(bytes);
57
+ case "utf16":
58
+ case "utf16le":
59
+ case "utf-16le":
60
+ return decodeUtf16Le(bytes);
61
+ case "utf16be":
62
+ case "utf-16be":
63
+ return decodeUtf16Be(bytes);
64
+ case "latin1":
65
+ case "iso88591":
66
+ case "iso-8859-1":
67
+ return decodeLatin1(bytes);
68
+ default:
69
+ if (typeof TextDecoder !== "undefined") {
70
+ try {
71
+ return new TextDecoder(encoding).decode(bytes);
72
+ } catch {
73
+ throw new Error(`Unsupported encoding: ${encoding}`);
74
+ }
75
+ }
76
+ throw new Error(`Unsupported encoding: ${encoding}`);
77
+ }
78
+ }
79
+ function decodeUtf8(bytes) {
80
+ if (typeof TextDecoder !== "undefined") {
81
+ return new TextDecoder("utf-8").decode(bytes);
82
+ }
83
+ let result = "";
84
+ let i = 0;
85
+ while (i < bytes.length) {
86
+ const byte1 = bytes[i++];
87
+ if (byte1 < 128) {
88
+ result += String.fromCharCode(byte1);
89
+ } else if (byte1 < 224) {
90
+ const byte2 = bytes[i++];
91
+ result += String.fromCharCode((byte1 & 31) << 6 | byte2 & 63);
92
+ } else if (byte1 < 240) {
93
+ const byte2 = bytes[i++];
94
+ const byte3 = bytes[i++];
95
+ result += String.fromCharCode(
96
+ (byte1 & 15) << 12 | (byte2 & 63) << 6 | byte3 & 63
97
+ );
98
+ } else {
99
+ const byte2 = bytes[i++];
100
+ const byte3 = bytes[i++];
101
+ const byte4 = bytes[i++];
102
+ let codePoint = (byte1 & 7) << 18 | (byte2 & 63) << 12 | (byte3 & 63) << 6 | byte4 & 63;
103
+ codePoint -= 65536;
104
+ result += String.fromCharCode(
105
+ 55296 + (codePoint >> 10),
106
+ 56320 + (codePoint & 1023)
107
+ );
108
+ }
109
+ }
110
+ return result;
111
+ }
112
+ function decodeAscii(bytes) {
113
+ let result = "";
114
+ for (let i = 0; i < bytes.length; i++) {
115
+ result += String.fromCharCode(bytes[i] & 127);
116
+ }
117
+ return result;
118
+ }
119
+ function decodeLatin1(bytes) {
120
+ let result = "";
121
+ for (let i = 0; i < bytes.length; i++) {
122
+ result += String.fromCharCode(bytes[i]);
123
+ }
124
+ return result;
125
+ }
126
+ function decodeUtf16Le(bytes) {
127
+ if (typeof TextDecoder !== "undefined") {
128
+ return new TextDecoder("utf-16le").decode(bytes);
129
+ }
130
+ let result = "";
131
+ for (let i = 0; i < bytes.length; i += 2) {
132
+ const charCode = bytes[i] | bytes[i + 1] << 8;
133
+ result += String.fromCharCode(charCode);
134
+ }
135
+ return result;
136
+ }
137
+ function decodeUtf16Be(bytes) {
138
+ if (typeof TextDecoder !== "undefined") {
139
+ return new TextDecoder("utf-16be").decode(bytes);
140
+ }
141
+ let result = "";
142
+ for (let i = 0; i < bytes.length; i += 2) {
143
+ const charCode = bytes[i] << 8 | bytes[i + 1];
144
+ result += String.fromCharCode(charCode);
145
+ }
146
+ return result;
147
+ }
148
+
149
+ // src/stream/KaitaiStream.ts
150
+ var KaitaiStream = class _KaitaiStream {
151
+ /**
152
+ * Create a new KaitaiStream from a buffer
153
+ * @param buffer - ArrayBuffer or Uint8Array containing the binary data
154
+ */
155
+ constructor(buffer) {
156
+ this._pos = 0;
157
+ this._bits = 0;
158
+ this._bitsLeft = 0;
159
+ if (buffer instanceof ArrayBuffer) {
160
+ this.buffer = new Uint8Array(buffer);
161
+ this.view = new DataView(buffer);
162
+ } else {
163
+ this.buffer = buffer;
164
+ this.view = new DataView(
165
+ buffer.buffer,
166
+ buffer.byteOffset,
167
+ buffer.byteLength
168
+ );
169
+ }
170
+ }
171
+ /**
172
+ * Current position in the stream
173
+ */
174
+ get pos() {
175
+ return this._pos;
176
+ }
177
+ set pos(value) {
178
+ this._pos = value;
179
+ this._bitsLeft = 0;
180
+ }
181
+ /**
182
+ * Total size of the stream in bytes
183
+ */
184
+ get size() {
185
+ return this.buffer.length;
186
+ }
187
+ /**
188
+ * Check if we've reached the end of the stream
189
+ */
190
+ isEof() {
191
+ return this._pos >= this.buffer.length;
192
+ }
193
+ /**
194
+ * Seek to a specific position in the stream
195
+ * @param pos - Position to seek to
196
+ */
197
+ seek(pos) {
198
+ if (pos < 0 || pos > this.buffer.length) {
199
+ throw new Error(`Invalid seek position: ${pos}`);
200
+ }
201
+ this.pos = pos;
202
+ }
203
+ /**
204
+ * Ensure we have enough bytes available
205
+ * @param count - Number of bytes needed
206
+ */
207
+ ensureBytes(count) {
208
+ if (this._pos + count > this.buffer.length) {
209
+ throw new EOFError(
210
+ `Requested ${count} bytes at position ${this._pos}, but only ${this.buffer.length - this._pos} bytes available`,
211
+ this._pos
212
+ );
213
+ }
214
+ }
215
+ // ==================== Unsigned Integers ====================
216
+ /**
217
+ * Read 1-byte unsigned integer (0 to 255)
218
+ */
219
+ readU1() {
220
+ this.ensureBytes(1);
221
+ return this.buffer[this._pos++];
222
+ }
223
+ /**
224
+ * Read 2-byte unsigned integer, little-endian
225
+ */
226
+ readU2le() {
227
+ this.ensureBytes(2);
228
+ const value = this.view.getUint16(this._pos, true);
229
+ this._pos += 2;
230
+ return value;
231
+ }
232
+ /**
233
+ * Read 2-byte unsigned integer, big-endian
234
+ */
235
+ readU2be() {
236
+ this.ensureBytes(2);
237
+ const value = this.view.getUint16(this._pos, false);
238
+ this._pos += 2;
239
+ return value;
240
+ }
241
+ /**
242
+ * Read 4-byte unsigned integer, little-endian
243
+ */
244
+ readU4le() {
245
+ this.ensureBytes(4);
246
+ const value = this.view.getUint32(this._pos, true);
247
+ this._pos += 4;
248
+ return value;
249
+ }
250
+ /**
251
+ * Read 4-byte unsigned integer, big-endian
252
+ */
253
+ readU4be() {
254
+ this.ensureBytes(4);
255
+ const value = this.view.getUint32(this._pos, false);
256
+ this._pos += 4;
257
+ return value;
258
+ }
259
+ /**
260
+ * Read 8-byte unsigned integer, little-endian
261
+ * Returns BigInt for values > Number.MAX_SAFE_INTEGER
262
+ */
263
+ readU8le() {
264
+ this.ensureBytes(8);
265
+ const value = this.view.getBigUint64(this._pos, true);
266
+ this._pos += 8;
267
+ return value;
268
+ }
269
+ /**
270
+ * Read 8-byte unsigned integer, big-endian
271
+ * Returns BigInt for values > Number.MAX_SAFE_INTEGER
272
+ */
273
+ readU8be() {
274
+ this.ensureBytes(8);
275
+ const value = this.view.getBigUint64(this._pos, false);
276
+ this._pos += 8;
277
+ return value;
278
+ }
279
+ // ==================== Signed Integers ====================
280
+ /**
281
+ * Read 1-byte signed integer (-128 to 127)
282
+ */
283
+ readS1() {
284
+ this.ensureBytes(1);
285
+ return this.view.getInt8(this._pos++);
286
+ }
287
+ /**
288
+ * Read 2-byte signed integer, little-endian
289
+ */
290
+ readS2le() {
291
+ this.ensureBytes(2);
292
+ const value = this.view.getInt16(this._pos, true);
293
+ this._pos += 2;
294
+ return value;
295
+ }
296
+ /**
297
+ * Read 2-byte signed integer, big-endian
298
+ */
299
+ readS2be() {
300
+ this.ensureBytes(2);
301
+ const value = this.view.getInt16(this._pos, false);
302
+ this._pos += 2;
303
+ return value;
304
+ }
305
+ /**
306
+ * Read 4-byte signed integer, little-endian
307
+ */
308
+ readS4le() {
309
+ this.ensureBytes(4);
310
+ const value = this.view.getInt32(this._pos, true);
311
+ this._pos += 4;
312
+ return value;
313
+ }
314
+ /**
315
+ * Read 4-byte signed integer, big-endian
316
+ */
317
+ readS4be() {
318
+ this.ensureBytes(4);
319
+ const value = this.view.getInt32(this._pos, false);
320
+ this._pos += 4;
321
+ return value;
322
+ }
323
+ /**
324
+ * Read 8-byte signed integer, little-endian
325
+ * Returns BigInt for values outside Number.MAX_SAFE_INTEGER range
326
+ */
327
+ readS8le() {
328
+ this.ensureBytes(8);
329
+ const value = this.view.getBigInt64(this._pos, true);
330
+ this._pos += 8;
331
+ return value;
332
+ }
333
+ /**
334
+ * Read 8-byte signed integer, big-endian
335
+ * Returns BigInt for values outside Number.MAX_SAFE_INTEGER range
336
+ */
337
+ readS8be() {
338
+ this.ensureBytes(8);
339
+ const value = this.view.getBigInt64(this._pos, false);
340
+ this._pos += 8;
341
+ return value;
342
+ }
343
+ // ==================== Floating Point ====================
344
+ /**
345
+ * Read 4-byte IEEE 754 single-precision float, little-endian
346
+ */
347
+ readF4le() {
348
+ this.ensureBytes(4);
349
+ const value = this.view.getFloat32(this._pos, true);
350
+ this._pos += 4;
351
+ return value;
352
+ }
353
+ /**
354
+ * Read 4-byte IEEE 754 single-precision float, big-endian
355
+ */
356
+ readF4be() {
357
+ this.ensureBytes(4);
358
+ const value = this.view.getFloat32(this._pos, false);
359
+ this._pos += 4;
360
+ return value;
361
+ }
362
+ /**
363
+ * Read 8-byte IEEE 754 double-precision float, little-endian
364
+ */
365
+ readF8le() {
366
+ this.ensureBytes(8);
367
+ const value = this.view.getFloat64(this._pos, true);
368
+ this._pos += 8;
369
+ return value;
370
+ }
371
+ /**
372
+ * Read 8-byte IEEE 754 double-precision float, big-endian
373
+ */
374
+ readF8be() {
375
+ this.ensureBytes(8);
376
+ const value = this.view.getFloat64(this._pos, false);
377
+ this._pos += 8;
378
+ return value;
379
+ }
380
+ // ==================== Byte Arrays ====================
381
+ /**
382
+ * Read a fixed number of bytes
383
+ * @param length - Number of bytes to read
384
+ */
385
+ readBytes(length) {
386
+ this.ensureBytes(length);
387
+ const bytes = this.buffer.slice(this._pos, this._pos + length);
388
+ this._pos += length;
389
+ return bytes;
390
+ }
391
+ /**
392
+ * Read all remaining bytes until end of stream
393
+ */
394
+ readBytesFull() {
395
+ const bytes = this.buffer.slice(this._pos);
396
+ this._pos = this.buffer.length;
397
+ return bytes;
398
+ }
399
+ /**
400
+ * Read bytes until a terminator byte is found
401
+ * @param term - Terminator byte value
402
+ * @param include - Include terminator in result
403
+ * @param consume - Consume terminator from stream
404
+ * @param eosError - Throw error if EOS reached before terminator
405
+ */
406
+ readBytesterm(term, include = false, consume = true, eosError = true) {
407
+ const start = this._pos;
408
+ let end = start;
409
+ while (end < this.buffer.length && this.buffer[end] !== term) {
410
+ end++;
411
+ }
412
+ const foundTerm = end < this.buffer.length;
413
+ if (!foundTerm && eosError) {
414
+ throw new EOFError(
415
+ `Terminator byte ${term} not found before end of stream`,
416
+ this._pos
417
+ );
418
+ }
419
+ const includeEnd = include && foundTerm ? end + 1 : end;
420
+ const bytes = this.buffer.slice(start, includeEnd);
421
+ if (foundTerm && consume) {
422
+ this._pos = end + 1;
423
+ } else {
424
+ this._pos = end;
425
+ }
426
+ return bytes;
427
+ }
428
+ // ==================== Strings ====================
429
+ /**
430
+ * Read a fixed-length string
431
+ * @param length - Number of bytes to read
432
+ * @param encoding - Character encoding (default: UTF-8)
433
+ */
434
+ readStr(length, encoding = "UTF-8") {
435
+ const bytes = this.readBytes(length);
436
+ return decodeString(bytes, encoding);
437
+ }
438
+ /**
439
+ * Read a null-terminated string
440
+ * @param encoding - Character encoding (default: UTF-8)
441
+ * @param term - Terminator byte (default: 0)
442
+ * @param include - Include terminator in result
443
+ * @param consume - Consume terminator from stream
444
+ * @param eosError - Throw error if EOS reached before terminator
445
+ */
446
+ readStrz(encoding = "UTF-8", term = 0, include = false, consume = true, eosError = true) {
447
+ const bytes = this.readBytesterm(term, include, consume, eosError);
448
+ return decodeString(bytes, encoding);
449
+ }
450
+ // ==================== Bit-level Reading ====================
451
+ /**
452
+ * Align bit reading to byte boundary
453
+ */
454
+ alignToByte() {
455
+ this._bitsLeft = 0;
456
+ }
457
+ /**
458
+ * Read specified number of bits as unsigned integer (big-endian)
459
+ * @param n - Number of bits to read (1-64)
460
+ */
461
+ readBitsIntBe(n) {
462
+ if (n < 1 || n > 64) {
463
+ throw new Error(`Invalid bit count: ${n}. Must be between 1 and 64`);
464
+ }
465
+ let result = 0n;
466
+ for (let bitsNeeded = n; bitsNeeded > 0; ) {
467
+ if (this._bitsLeft === 0) {
468
+ this._bits = this.readU1();
469
+ this._bitsLeft = 8;
470
+ }
471
+ const bitsToRead = Math.min(bitsNeeded, this._bitsLeft);
472
+ const mask = (1 << bitsToRead) - 1;
473
+ const shift = this._bitsLeft - bitsToRead;
474
+ result = result << BigInt(bitsToRead) | BigInt(this._bits >> shift & mask);
475
+ this._bitsLeft -= bitsToRead;
476
+ bitsNeeded -= bitsToRead;
477
+ }
478
+ return result;
479
+ }
480
+ /**
481
+ * Read specified number of bits as unsigned integer (little-endian)
482
+ * @param n - Number of bits to read (1-64)
483
+ */
484
+ readBitsIntLe(n) {
485
+ if (n < 1 || n > 64) {
486
+ throw new Error(`Invalid bit count: ${n}. Must be between 1 and 64`);
487
+ }
488
+ let result = 0n;
489
+ let bitPos = 0;
490
+ for (let bitsNeeded = n; bitsNeeded > 0; ) {
491
+ if (this._bitsLeft === 0) {
492
+ this._bits = this.readU1();
493
+ this._bitsLeft = 8;
494
+ }
495
+ const bitsToRead = Math.min(bitsNeeded, this._bitsLeft);
496
+ const mask = (1 << bitsToRead) - 1;
497
+ result |= BigInt(this._bits & mask) << BigInt(bitPos);
498
+ this._bits >>= bitsToRead;
499
+ this._bitsLeft -= bitsToRead;
500
+ bitsNeeded -= bitsToRead;
501
+ bitPos += bitsToRead;
502
+ }
503
+ return result;
504
+ }
505
+ // ==================== Utility Methods ====================
506
+ /**
507
+ * Get the underlying buffer
508
+ */
509
+ getBuffer() {
510
+ return this.buffer;
511
+ }
512
+ /**
513
+ * Create a substream from current position with specified size
514
+ * @param size - Size of the substream in bytes
515
+ */
516
+ substream(size) {
517
+ this.ensureBytes(size);
518
+ const subBuffer = this.buffer.slice(this._pos, this._pos + size);
519
+ this._pos += size;
520
+ return new _KaitaiStream(subBuffer);
521
+ }
522
+ };
523
+
524
+ // src/parser/schema.ts
525
+ var BUILTIN_TYPES = [
526
+ // Unsigned integers
527
+ "u1",
528
+ "u2",
529
+ "u2le",
530
+ "u2be",
531
+ "u4",
532
+ "u4le",
533
+ "u4be",
534
+ "u8",
535
+ "u8le",
536
+ "u8be",
537
+ // Signed integers
538
+ "s1",
539
+ "s2",
540
+ "s2le",
541
+ "s2be",
542
+ "s4",
543
+ "s4le",
544
+ "s4be",
545
+ "s8",
546
+ "s8le",
547
+ "s8be",
548
+ // Floating point
549
+ "f4",
550
+ "f4le",
551
+ "f4be",
552
+ "f8",
553
+ "f8le",
554
+ "f8be",
555
+ // String
556
+ "str",
557
+ "strz"
558
+ ];
559
+ function isBuiltinType(type) {
560
+ return BUILTIN_TYPES.includes(type);
561
+ }
562
+ function getTypeEndianness(type) {
563
+ if (type.endsWith("le")) return "le";
564
+ if (type.endsWith("be")) return "be";
565
+ return void 0;
566
+ }
567
+ function getBaseType(type) {
568
+ if (type.endsWith("le") || type.endsWith("be")) {
569
+ return type.slice(0, -2);
570
+ }
571
+ return type;
572
+ }
573
+ function isIntegerType(type) {
574
+ const base = getBaseType(type);
575
+ return /^[us][1248]$/.test(base);
576
+ }
577
+ function isFloatType(type) {
578
+ const base = getBaseType(type);
579
+ return /^f[48]$/.test(base);
580
+ }
581
+ function isStringType(type) {
582
+ return type === "str" || type === "strz";
583
+ }
584
+
585
+ // src/parser/KsyParser.ts
586
+ var import_yaml = require("yaml");
587
+ var KsyParser = class {
588
+ /**
589
+ * Parse a .ksy YAML string into a typed schema object.
590
+ *
591
+ * @param yaml - YAML string containing the .ksy definition
592
+ * @param options - Parsing options
593
+ * @returns Parsed and validated schema
594
+ * @throws {ParseError} If YAML parsing fails
595
+ * @throws {ValidationError} If schema validation fails
596
+ */
597
+ parse(yaml, options = {}) {
598
+ const { validate = true, strict = false } = options;
599
+ let parsed;
600
+ try {
601
+ parsed = (0, import_yaml.parse)(yaml);
602
+ } catch (error) {
603
+ throw new ParseError(
604
+ `Failed to parse YAML: ${error instanceof Error ? error.message : String(error)}`
605
+ );
606
+ }
607
+ if (typeof parsed !== "object" || parsed === null) {
608
+ throw new ParseError("KSY file must contain an object");
609
+ }
610
+ const schema = parsed;
611
+ if (validate) {
612
+ const result = this.validate(schema, { strict });
613
+ if (!result.valid) {
614
+ const errorMessages = result.errors.map((e) => e.message).join("; ");
615
+ throw new ValidationError(
616
+ `Schema validation failed: ${errorMessages}`
617
+ );
618
+ }
619
+ if (result.warnings.length > 0 && !strict) {
620
+ console.warn(
621
+ "Schema validation warnings:",
622
+ result.warnings.map((w) => w.message)
623
+ );
624
+ }
625
+ }
626
+ return schema;
627
+ }
628
+ /**
629
+ * Validate a schema object.
630
+ *
631
+ * @param schema - Schema to validate
632
+ * @param options - Validation options
633
+ * @returns Validation result with errors and warnings
634
+ */
635
+ validate(schema, options = {}) {
636
+ const { strict = false, isNested = false } = options;
637
+ const errors = [];
638
+ const warnings = [];
639
+ if (!schema.meta && !isNested) {
640
+ errors.push({
641
+ message: 'Missing required "meta" section',
642
+ path: [],
643
+ code: "MISSING_META"
644
+ });
645
+ } else if (schema.meta) {
646
+ if (!schema.meta.id) {
647
+ errors.push({
648
+ message: 'Missing required "meta.id" field',
649
+ path: ["meta"],
650
+ code: "MISSING_META_ID"
651
+ });
652
+ } else if (typeof schema.meta.id !== "string") {
653
+ errors.push({
654
+ message: '"meta.id" must be a string',
655
+ path: ["meta", "id"],
656
+ code: "INVALID_META_ID_TYPE"
657
+ });
658
+ } else if (!/^[a-z][a-z0-9_]*$/.test(schema.meta.id)) {
659
+ warnings.push({
660
+ message: '"meta.id" should follow snake_case naming convention',
661
+ path: ["meta", "id"],
662
+ code: "META_ID_NAMING"
663
+ });
664
+ }
665
+ if (schema.meta.endian) {
666
+ if (typeof schema.meta.endian === "string" && schema.meta.endian !== "le" && schema.meta.endian !== "be") {
667
+ errors.push({
668
+ message: '"meta.endian" must be "le" or "be"',
669
+ path: ["meta", "endian"],
670
+ code: "INVALID_ENDIAN"
671
+ });
672
+ }
673
+ }
674
+ }
675
+ if (schema.seq) {
676
+ if (!Array.isArray(schema.seq)) {
677
+ errors.push({
678
+ message: '"seq" must be an array',
679
+ path: ["seq"],
680
+ code: "INVALID_SEQ_TYPE"
681
+ });
682
+ } else {
683
+ schema.seq.forEach((attr, index) => {
684
+ this.validateAttribute(
685
+ attr,
686
+ ["seq", String(index)],
687
+ errors,
688
+ warnings,
689
+ strict
690
+ );
691
+ });
692
+ }
693
+ }
694
+ if (schema.instances) {
695
+ if (typeof schema.instances !== "object") {
696
+ errors.push({
697
+ message: '"instances" must be an object',
698
+ path: ["instances"],
699
+ code: "INVALID_INSTANCES_TYPE"
700
+ });
701
+ } else {
702
+ Object.entries(schema.instances).forEach(([key, instance]) => {
703
+ this.validateAttribute(
704
+ instance,
705
+ ["instances", key],
706
+ errors,
707
+ warnings,
708
+ strict
709
+ );
710
+ });
711
+ }
712
+ }
713
+ if (schema.types) {
714
+ if (typeof schema.types !== "object") {
715
+ errors.push({
716
+ message: '"types" must be an object',
717
+ path: ["types"],
718
+ code: "INVALID_TYPES_TYPE"
719
+ });
720
+ } else {
721
+ Object.entries(schema.types).forEach(([key, type]) => {
722
+ const typeResult = this.validate(type, { ...options, isNested: true });
723
+ errors.push(
724
+ ...typeResult.errors.map((e) => ({
725
+ ...e,
726
+ path: ["types", key, ...e.path]
727
+ }))
728
+ );
729
+ warnings.push(
730
+ ...typeResult.warnings.map((w) => ({
731
+ ...w,
732
+ path: ["types", key, ...w.path]
733
+ }))
734
+ );
735
+ });
736
+ }
737
+ }
738
+ if (schema.enums) {
739
+ if (typeof schema.enums !== "object") {
740
+ errors.push({
741
+ message: '"enums" must be an object',
742
+ path: ["enums"],
743
+ code: "INVALID_ENUMS_TYPE"
744
+ });
745
+ }
746
+ }
747
+ return {
748
+ valid: errors.length === 0 && (strict ? warnings.length === 0 : true),
749
+ errors,
750
+ warnings
751
+ };
752
+ }
753
+ /**
754
+ * Validate an attribute specification.
755
+ *
756
+ * @param attr - Attribute to validate
757
+ * @param path - Path to this attribute in the schema
758
+ * @param errors - Array to collect errors
759
+ * @param warnings - Array to collect warnings
760
+ * @param strict - Whether to be strict about warnings
761
+ * @private
762
+ */
763
+ validateAttribute(attr, path, errors, warnings, _strict) {
764
+ if (attr.id && typeof attr.id === "string") {
765
+ if (!/^[a-z][a-z0-9_]*$/.test(attr.id)) {
766
+ warnings.push({
767
+ message: `Field "${attr.id}" should follow snake_case naming convention`,
768
+ path: [...path, "id"],
769
+ code: "FIELD_ID_NAMING"
770
+ });
771
+ }
772
+ }
773
+ if (attr.repeat) {
774
+ if (attr.repeat !== "expr" && attr.repeat !== "eos" && attr.repeat !== "until") {
775
+ errors.push({
776
+ message: '"repeat" must be "expr", "eos", or "until"',
777
+ path: [...path, "repeat"],
778
+ code: "INVALID_REPEAT"
779
+ });
780
+ }
781
+ if (attr.repeat === "expr" && !attr["repeat-expr"]) {
782
+ errors.push({
783
+ message: '"repeat-expr" is required when repeat is "expr"',
784
+ path: [...path, "repeat-expr"],
785
+ code: "MISSING_REPEAT_EXPR"
786
+ });
787
+ }
788
+ if (attr.repeat === "until" && !attr["repeat-until"]) {
789
+ errors.push({
790
+ message: '"repeat-until" is required when repeat is "until"',
791
+ path: [...path, "repeat-until"],
792
+ code: "MISSING_REPEAT_UNTIL"
793
+ });
794
+ }
795
+ }
796
+ if (attr["size-eos"] && attr.size) {
797
+ warnings.push({
798
+ message: '"size-eos" and "size" are mutually exclusive',
799
+ path: [...path],
800
+ code: "SIZE_EOS_WITH_SIZE"
801
+ });
802
+ }
803
+ if (attr.contents) {
804
+ if (!Array.isArray(attr.contents) && typeof attr.contents !== "string") {
805
+ errors.push({
806
+ message: '"contents" must be an array or string',
807
+ path: [...path, "contents"],
808
+ code: "INVALID_CONTENTS_TYPE"
809
+ });
810
+ }
811
+ }
812
+ }
813
+ /**
814
+ * Parse multiple .ksy files and resolve imports.
815
+ *
816
+ * @param mainYaml - Main .ksy file content
817
+ * @param imports - Map of import names to their YAML content
818
+ * @param options - Parsing options
819
+ * @returns Parsed schema with resolved imports
820
+ */
821
+ parseWithImports(mainYaml, _imports, options = {}) {
822
+ const mainSchema = this.parse(mainYaml, options);
823
+ return mainSchema;
824
+ }
825
+ };
826
+
827
+ // src/interpreter/Context.ts
828
+ var Context = class _Context {
829
+ /**
830
+ * Create a new execution context.
831
+ *
832
+ * @param _io - Binary stream being read
833
+ * @param _root - Root object of the parse tree
834
+ * @param _parent - Parent object (optional)
835
+ * @param enums - Enum definitions from schema (optional)
836
+ */
837
+ constructor(_io, _root = null, _parent = null, enums) {
838
+ this._io = _io;
839
+ this._root = _root;
840
+ /** Stack of parent objects */
841
+ this.parentStack = [];
842
+ /** Current object being parsed */
843
+ this._current = {};
844
+ /** Enum definitions from schema */
845
+ this._enums = {};
846
+ if (_parent !== null) {
847
+ this.parentStack.push(_parent);
848
+ }
849
+ if (enums) {
850
+ this._enums = enums;
851
+ }
852
+ }
853
+ /**
854
+ * Get the current I/O stream.
855
+ * Accessible in expressions as `_io`.
856
+ *
857
+ * @returns Current stream
858
+ */
859
+ get io() {
860
+ return this._io;
861
+ }
862
+ /**
863
+ * Get the root object.
864
+ * Accessible in expressions as `_root`.
865
+ *
866
+ * @returns Root object
867
+ */
868
+ get root() {
869
+ return this._root;
870
+ }
871
+ /**
872
+ * Get the parent object.
873
+ * Accessible in expressions as `_parent`.
874
+ *
875
+ * @returns Parent object or null if at root
876
+ */
877
+ get parent() {
878
+ return this.parentStack.length > 0 ? this.parentStack[this.parentStack.length - 1] : null;
879
+ }
880
+ /**
881
+ * Get the current object being parsed.
882
+ * Used to access fields defined earlier in the sequence.
883
+ *
884
+ * @returns Current object
885
+ */
886
+ get current() {
887
+ return this._current;
888
+ }
889
+ /**
890
+ * Set the current object.
891
+ *
892
+ * @param obj - Object to set as current
893
+ */
894
+ set current(obj) {
895
+ this._current = obj;
896
+ }
897
+ /**
898
+ * Push a new parent onto the stack.
899
+ * Used when entering a nested type.
900
+ *
901
+ * @param parent - Parent object to push
902
+ */
903
+ pushParent(parent) {
904
+ this.parentStack.push(parent);
905
+ }
906
+ /**
907
+ * Pop the current parent from the stack.
908
+ * Used when exiting a nested type.
909
+ *
910
+ * @returns The popped parent object
911
+ */
912
+ popParent() {
913
+ return this.parentStack.pop();
914
+ }
915
+ /**
916
+ * Get a value from the context by path.
917
+ * Supports special names: _io, _root, _parent, _index.
918
+ *
919
+ * @param name - Name or path to resolve
920
+ * @returns Resolved value
921
+ */
922
+ resolve(name) {
923
+ switch (name) {
924
+ case "_io":
925
+ return this._io;
926
+ case "_root":
927
+ return this._root;
928
+ case "_parent":
929
+ return this.parent;
930
+ case "_index":
931
+ return this._current["_index"];
932
+ default:
933
+ if (name in this._current) {
934
+ return this._current[name];
935
+ }
936
+ return void 0;
937
+ }
938
+ }
939
+ /**
940
+ * Set a value in the current object.
941
+ *
942
+ * @param name - Field name
943
+ * @param value - Value to set
944
+ */
945
+ set(name, value) {
946
+ this._current[name] = value;
947
+ }
948
+ /**
949
+ * Get enum value by name.
950
+ * Used for enum access in expressions (EnumName::value).
951
+ *
952
+ * @param enumName - Name of the enum
953
+ * @param valueName - Name of the enum value
954
+ * @returns Enum value (number) or undefined
955
+ */
956
+ getEnumValue(enumName, valueName) {
957
+ const enumDef = this._enums[enumName];
958
+ if (!enumDef) {
959
+ return void 0;
960
+ }
961
+ for (const [key, value] of Object.entries(enumDef)) {
962
+ if (value === valueName) {
963
+ const numKey = Number(key);
964
+ return isNaN(numKey) ? key : numKey;
965
+ }
966
+ }
967
+ return void 0;
968
+ }
969
+ /**
970
+ * Check if an enum exists.
971
+ *
972
+ * @param enumName - Name of the enum
973
+ * @returns True if enum exists
974
+ */
975
+ hasEnum(enumName) {
976
+ return enumName in this._enums;
977
+ }
978
+ /**
979
+ * Create a child context for nested parsing.
980
+ * The current object becomes the parent in the child context.
981
+ *
982
+ * @param stream - Stream for the child context (defaults to current stream)
983
+ * @returns New child context
984
+ */
985
+ createChild(stream) {
986
+ const childContext = new _Context(
987
+ stream || this._io,
988
+ this._root || this._current,
989
+ this._current,
990
+ this._enums
991
+ );
992
+ return childContext;
993
+ }
994
+ /**
995
+ * Clone this context.
996
+ * Creates a shallow copy with the same stream, root, and parent.
997
+ *
998
+ * @returns Cloned context
999
+ */
1000
+ clone() {
1001
+ const cloned = new _Context(this._io, this._root, this.parent, this._enums);
1002
+ cloned._current = { ...this._current };
1003
+ cloned.parentStack = [...this.parentStack];
1004
+ return cloned;
1005
+ }
1006
+ };
1007
+
1008
+ // src/expression/Token.ts
1009
+ function createToken(type, value = null, position = 0) {
1010
+ return { type, value, position };
1011
+ }
1012
+
1013
+ // src/expression/Lexer.ts
1014
+ var Lexer = class {
1015
+ /**
1016
+ * Create a new lexer.
1017
+ *
1018
+ * @param input - Expression string to tokenize
1019
+ */
1020
+ constructor(input) {
1021
+ this.position = 0;
1022
+ this.current = null;
1023
+ this.input = input;
1024
+ this.current = input.length > 0 ? input[0] : null;
1025
+ }
1026
+ /**
1027
+ * Tokenize the entire input string.
1028
+ *
1029
+ * @returns Array of tokens
1030
+ * @throws {ParseError} If invalid syntax is encountered
1031
+ */
1032
+ tokenize() {
1033
+ const tokens = [];
1034
+ while (this.current !== null) {
1035
+ if (this.isWhitespace(this.current)) {
1036
+ this.skipWhitespace();
1037
+ continue;
1038
+ }
1039
+ if (this.isDigit(this.current)) {
1040
+ tokens.push(this.readNumber());
1041
+ continue;
1042
+ }
1043
+ if (this.isIdentifierStart(this.current)) {
1044
+ tokens.push(this.readIdentifierOrKeyword());
1045
+ continue;
1046
+ }
1047
+ if (this.current === '"' || this.current === "'") {
1048
+ tokens.push(this.readString());
1049
+ continue;
1050
+ }
1051
+ const token = this.readOperator();
1052
+ if (token) {
1053
+ tokens.push(token);
1054
+ continue;
1055
+ }
1056
+ throw new ParseError(
1057
+ `Unexpected character: ${this.current}`,
1058
+ this.position
1059
+ );
1060
+ }
1061
+ tokens.push(createToken("EOF" /* EOF */, null, this.position));
1062
+ return tokens;
1063
+ }
1064
+ /**
1065
+ * Advance to the next character.
1066
+ * @private
1067
+ */
1068
+ advance() {
1069
+ this.position++;
1070
+ this.current = this.position < this.input.length ? this.input[this.position] : null;
1071
+ }
1072
+ /**
1073
+ * Peek at the next character without advancing.
1074
+ * @private
1075
+ */
1076
+ peek(offset = 1) {
1077
+ const pos = this.position + offset;
1078
+ return pos < this.input.length ? this.input[pos] : null;
1079
+ }
1080
+ /**
1081
+ * Check if character is whitespace.
1082
+ * @private
1083
+ */
1084
+ isWhitespace(char) {
1085
+ return /\s/.test(char);
1086
+ }
1087
+ /**
1088
+ * Check if character is a digit.
1089
+ * @private
1090
+ */
1091
+ isDigit(char) {
1092
+ return /[0-9]/.test(char);
1093
+ }
1094
+ /**
1095
+ * Check if character can start an identifier.
1096
+ * @private
1097
+ */
1098
+ isIdentifierStart(char) {
1099
+ return /[a-zA-Z_]/.test(char);
1100
+ }
1101
+ /**
1102
+ * Check if character can be part of an identifier.
1103
+ * @private
1104
+ */
1105
+ isIdentifierPart(char) {
1106
+ return /[a-zA-Z0-9_]/.test(char);
1107
+ }
1108
+ /**
1109
+ * Skip whitespace characters.
1110
+ * @private
1111
+ */
1112
+ skipWhitespace() {
1113
+ while (this.current !== null && this.isWhitespace(this.current)) {
1114
+ this.advance();
1115
+ }
1116
+ }
1117
+ /**
1118
+ * Read a number token.
1119
+ * @private
1120
+ */
1121
+ readNumber() {
1122
+ const start = this.position;
1123
+ let value = "";
1124
+ if (this.current === "0" && this.peek() === "x") {
1125
+ value += this.current;
1126
+ this.advance();
1127
+ value += this.current;
1128
+ this.advance();
1129
+ while (this.current !== null && /[0-9a-fA-F]/.test(this.current)) {
1130
+ value += this.current;
1131
+ this.advance();
1132
+ }
1133
+ return createToken("NUMBER" /* NUMBER */, parseInt(value, 16), start);
1134
+ }
1135
+ while (this.current !== null && this.isDigit(this.current)) {
1136
+ value += this.current;
1137
+ this.advance();
1138
+ }
1139
+ if (this.current === "." && this.peek() && this.isDigit(this.peek())) {
1140
+ value += this.current;
1141
+ this.advance();
1142
+ while (this.current !== null && this.isDigit(this.current)) {
1143
+ value += this.current;
1144
+ this.advance();
1145
+ }
1146
+ return createToken("NUMBER" /* NUMBER */, parseFloat(value), start);
1147
+ }
1148
+ return createToken("NUMBER" /* NUMBER */, parseInt(value, 10), start);
1149
+ }
1150
+ /**
1151
+ * Read an identifier or keyword token.
1152
+ * @private
1153
+ */
1154
+ readIdentifierOrKeyword() {
1155
+ const start = this.position;
1156
+ let value = "";
1157
+ while (this.current !== null && this.isIdentifierPart(this.current)) {
1158
+ value += this.current;
1159
+ this.advance();
1160
+ }
1161
+ switch (value) {
1162
+ case "true":
1163
+ return createToken("BOOLEAN" /* BOOLEAN */, true, start);
1164
+ case "false":
1165
+ return createToken("BOOLEAN" /* BOOLEAN */, false, start);
1166
+ case "and":
1167
+ return createToken("AND" /* AND */, value, start);
1168
+ case "or":
1169
+ return createToken("OR" /* OR */, value, start);
1170
+ case "not":
1171
+ return createToken("NOT" /* NOT */, value, start);
1172
+ default:
1173
+ return createToken("IDENTIFIER" /* IDENTIFIER */, value, start);
1174
+ }
1175
+ }
1176
+ /**
1177
+ * Read a string token.
1178
+ * @private
1179
+ */
1180
+ readString() {
1181
+ const start = this.position;
1182
+ const quote = this.current;
1183
+ let value = "";
1184
+ this.advance();
1185
+ while (this.current !== null && this.current !== quote) {
1186
+ if (this.current === "\\") {
1187
+ this.advance();
1188
+ if (this.current === null) {
1189
+ throw new ParseError("Unterminated string", start);
1190
+ }
1191
+ const ch = this.current;
1192
+ if (ch === "n") {
1193
+ value += "\n";
1194
+ } else if (ch === "t") {
1195
+ value += " ";
1196
+ } else if (ch === "r") {
1197
+ value += "\r";
1198
+ } else if (ch === "\\") {
1199
+ value += "\\";
1200
+ } else if (ch === '"') {
1201
+ value += '"';
1202
+ } else if (ch === "'") {
1203
+ value += "'";
1204
+ } else {
1205
+ value += ch;
1206
+ }
1207
+ } else {
1208
+ value += this.current;
1209
+ }
1210
+ this.advance();
1211
+ }
1212
+ if (this.current === null) {
1213
+ throw new ParseError("Unterminated string", start);
1214
+ }
1215
+ this.advance();
1216
+ return createToken("STRING" /* STRING */, value, start);
1217
+ }
1218
+ /**
1219
+ * Read an operator or punctuation token.
1220
+ * @private
1221
+ */
1222
+ readOperator() {
1223
+ const start = this.position;
1224
+ const char = this.current;
1225
+ switch (char) {
1226
+ case "+":
1227
+ this.advance();
1228
+ return createToken("PLUS" /* PLUS */, char, start);
1229
+ case "-":
1230
+ this.advance();
1231
+ return createToken("MINUS" /* MINUS */, char, start);
1232
+ case "*":
1233
+ this.advance();
1234
+ return createToken("STAR" /* STAR */, char, start);
1235
+ case "/":
1236
+ this.advance();
1237
+ return createToken("SLASH" /* SLASH */, char, start);
1238
+ case "%":
1239
+ this.advance();
1240
+ return createToken("PERCENT" /* PERCENT */, char, start);
1241
+ case "<":
1242
+ this.advance();
1243
+ if (this.current === "=") {
1244
+ this.advance();
1245
+ return createToken("LE" /* LE */, "<=", start);
1246
+ } else if (this.current === "<") {
1247
+ this.advance();
1248
+ return createToken("LSHIFT" /* LSHIFT */, "<<", start);
1249
+ }
1250
+ return createToken("LT" /* LT */, "<", start);
1251
+ case ">":
1252
+ this.advance();
1253
+ if (this.current === "=") {
1254
+ this.advance();
1255
+ return createToken("GE" /* GE */, ">=", start);
1256
+ } else if (this.current === ">") {
1257
+ this.advance();
1258
+ return createToken("RSHIFT" /* RSHIFT */, ">>", start);
1259
+ }
1260
+ return createToken("GT" /* GT */, ">", start);
1261
+ case "=":
1262
+ this.advance();
1263
+ if (this.current === "=") {
1264
+ this.advance();
1265
+ return createToken("EQ" /* EQ */, "==", start);
1266
+ }
1267
+ throw new ParseError("Expected == for equality", start);
1268
+ case "!":
1269
+ this.advance();
1270
+ if (this.current === "=") {
1271
+ this.advance();
1272
+ return createToken("NE" /* NE */, "!=", start);
1273
+ }
1274
+ throw new ParseError("Expected != for inequality", start);
1275
+ case "&":
1276
+ this.advance();
1277
+ return createToken("AMPERSAND" /* AMPERSAND */, char, start);
1278
+ case "|":
1279
+ this.advance();
1280
+ return createToken("PIPE" /* PIPE */, char, start);
1281
+ case "^":
1282
+ this.advance();
1283
+ return createToken("CARET" /* CARET */, char, start);
1284
+ case "?":
1285
+ this.advance();
1286
+ return createToken("QUESTION" /* QUESTION */, char, start);
1287
+ case ":":
1288
+ this.advance();
1289
+ if (this.current === ":") {
1290
+ this.advance();
1291
+ return createToken("DOUBLE_COLON" /* DOUBLE_COLON */, "::", start);
1292
+ }
1293
+ return createToken("COLON" /* COLON */, char, start);
1294
+ case "(":
1295
+ this.advance();
1296
+ return createToken("LPAREN" /* LPAREN */, char, start);
1297
+ case ")":
1298
+ this.advance();
1299
+ return createToken("RPAREN" /* RPAREN */, char, start);
1300
+ case "[":
1301
+ this.advance();
1302
+ return createToken("LBRACKET" /* LBRACKET */, char, start);
1303
+ case "]":
1304
+ this.advance();
1305
+ return createToken("RBRACKET" /* RBRACKET */, char, start);
1306
+ case ".":
1307
+ this.advance();
1308
+ return createToken("DOT" /* DOT */, char, start);
1309
+ case ",":
1310
+ this.advance();
1311
+ return createToken("COMMA" /* COMMA */, char, start);
1312
+ default:
1313
+ return null;
1314
+ }
1315
+ }
1316
+ };
1317
+
1318
+ // src/expression/AST.ts
1319
+ function createLiteral(value) {
1320
+ return { kind: "Literal", value };
1321
+ }
1322
+ function createIdentifier(name) {
1323
+ return { kind: "Identifier", name };
1324
+ }
1325
+ function createBinaryOp(operator, left, right) {
1326
+ return { kind: "BinaryOp", operator, left, right };
1327
+ }
1328
+ function createUnaryOp(operator, operand) {
1329
+ return { kind: "UnaryOp", operator, operand };
1330
+ }
1331
+ function createTernary(condition, ifTrue, ifFalse) {
1332
+ return { kind: "Ternary", condition, ifTrue, ifFalse };
1333
+ }
1334
+ function createMemberAccess(object, property) {
1335
+ return { kind: "MemberAccess", object, property };
1336
+ }
1337
+ function createIndexAccess(object, index) {
1338
+ return { kind: "IndexAccess", object, index };
1339
+ }
1340
+ function createMethodCall(object, method, args) {
1341
+ return { kind: "MethodCall", object, method, args };
1342
+ }
1343
+ function createEnumAccess(enumName, value) {
1344
+ return { kind: "EnumAccess", enumName, value };
1345
+ }
1346
+
1347
+ // src/expression/Parser.ts
1348
+ var ExpressionParser = class {
1349
+ /**
1350
+ * Create a new expression parser.
1351
+ *
1352
+ * @param tokens - Array of tokens to parse
1353
+ */
1354
+ constructor(tokens) {
1355
+ this.position = 0;
1356
+ this.tokens = tokens;
1357
+ }
1358
+ /**
1359
+ * Parse the tokens into an AST.
1360
+ *
1361
+ * @returns Root AST node
1362
+ * @throws {ParseError} If invalid syntax is encountered
1363
+ */
1364
+ parse() {
1365
+ const expr = this.parseTernary();
1366
+ if (!this.isAtEnd()) {
1367
+ throw new ParseError(
1368
+ `Unexpected token: ${this.current().type}`,
1369
+ this.current().position
1370
+ );
1371
+ }
1372
+ return expr;
1373
+ }
1374
+ /**
1375
+ * Get the current token.
1376
+ * @private
1377
+ */
1378
+ current() {
1379
+ return this.tokens[this.position];
1380
+ }
1381
+ /**
1382
+ * Check if we're at the end of tokens.
1383
+ * @private
1384
+ */
1385
+ isAtEnd() {
1386
+ return this.current().type === "EOF" /* EOF */;
1387
+ }
1388
+ /**
1389
+ * Advance to the next token.
1390
+ * @private
1391
+ */
1392
+ advance() {
1393
+ if (!this.isAtEnd()) {
1394
+ this.position++;
1395
+ }
1396
+ return this.tokens[this.position - 1];
1397
+ }
1398
+ /**
1399
+ * Check if current token matches any of the given types.
1400
+ * @private
1401
+ */
1402
+ match(...types) {
1403
+ for (const type of types) {
1404
+ if (this.current().type === type) {
1405
+ this.advance();
1406
+ return true;
1407
+ }
1408
+ }
1409
+ return false;
1410
+ }
1411
+ /**
1412
+ * Expect a specific token type and advance.
1413
+ * @private
1414
+ */
1415
+ expect(type, message) {
1416
+ if (this.current().type !== type) {
1417
+ throw new ParseError(message, this.current().position);
1418
+ }
1419
+ return this.advance();
1420
+ }
1421
+ /**
1422
+ * Parse ternary conditional (lowest precedence).
1423
+ * condition ? ifTrue : ifFalse
1424
+ * @private
1425
+ */
1426
+ parseTernary() {
1427
+ let expr = this.parseLogicalOr();
1428
+ if (this.match("QUESTION" /* QUESTION */)) {
1429
+ const ifTrue = this.parseTernary();
1430
+ this.expect("COLON" /* COLON */, "Expected : in ternary expression");
1431
+ const ifFalse = this.parseTernary();
1432
+ return createTernary(expr, ifTrue, ifFalse);
1433
+ }
1434
+ return expr;
1435
+ }
1436
+ /**
1437
+ * Parse logical OR.
1438
+ * @private
1439
+ */
1440
+ parseLogicalOr() {
1441
+ let left = this.parseLogicalAnd();
1442
+ while (this.match("OR" /* OR */)) {
1443
+ const operator = "or";
1444
+ const right = this.parseLogicalAnd();
1445
+ left = createBinaryOp(operator, left, right);
1446
+ }
1447
+ return left;
1448
+ }
1449
+ /**
1450
+ * Parse logical AND.
1451
+ * @private
1452
+ */
1453
+ parseLogicalAnd() {
1454
+ let left = this.parseBitwiseOr();
1455
+ while (this.match("AND" /* AND */)) {
1456
+ const operator = "and";
1457
+ const right = this.parseBitwiseOr();
1458
+ left = createBinaryOp(operator, left, right);
1459
+ }
1460
+ return left;
1461
+ }
1462
+ /**
1463
+ * Parse bitwise OR.
1464
+ * @private
1465
+ */
1466
+ parseBitwiseOr() {
1467
+ let left = this.parseBitwiseXor();
1468
+ while (this.match("PIPE" /* PIPE */)) {
1469
+ const operator = "|";
1470
+ const right = this.parseBitwiseXor();
1471
+ left = createBinaryOp(operator, left, right);
1472
+ }
1473
+ return left;
1474
+ }
1475
+ /**
1476
+ * Parse bitwise XOR.
1477
+ * @private
1478
+ */
1479
+ parseBitwiseXor() {
1480
+ let left = this.parseBitwiseAnd();
1481
+ while (this.match("CARET" /* CARET */)) {
1482
+ const operator = "^";
1483
+ const right = this.parseBitwiseAnd();
1484
+ left = createBinaryOp(operator, left, right);
1485
+ }
1486
+ return left;
1487
+ }
1488
+ /**
1489
+ * Parse bitwise AND.
1490
+ * @private
1491
+ */
1492
+ parseBitwiseAnd() {
1493
+ let left = this.parseEquality();
1494
+ while (this.match("AMPERSAND" /* AMPERSAND */)) {
1495
+ const operator = "&";
1496
+ const right = this.parseEquality();
1497
+ left = createBinaryOp(operator, left, right);
1498
+ }
1499
+ return left;
1500
+ }
1501
+ /**
1502
+ * Parse equality operators (==, !=).
1503
+ * @private
1504
+ */
1505
+ parseEquality() {
1506
+ let left = this.parseRelational();
1507
+ while (this.match("EQ" /* EQ */, "NE" /* NE */)) {
1508
+ const operator = this.tokens[this.position - 1].value;
1509
+ const right = this.parseRelational();
1510
+ left = createBinaryOp(operator, left, right);
1511
+ }
1512
+ return left;
1513
+ }
1514
+ /**
1515
+ * Parse relational operators (<, <=, >, >=).
1516
+ * @private
1517
+ */
1518
+ parseRelational() {
1519
+ let left = this.parseBitShift();
1520
+ while (this.match("LT" /* LT */, "LE" /* LE */, "GT" /* GT */, "GE" /* GE */)) {
1521
+ const operator = this.tokens[this.position - 1].value;
1522
+ const right = this.parseBitShift();
1523
+ left = createBinaryOp(operator, left, right);
1524
+ }
1525
+ return left;
1526
+ }
1527
+ /**
1528
+ * Parse bit shift operators (<<, >>).
1529
+ * @private
1530
+ */
1531
+ parseBitShift() {
1532
+ let left = this.parseAdditive();
1533
+ while (this.match("LSHIFT" /* LSHIFT */, "RSHIFT" /* RSHIFT */)) {
1534
+ const operator = this.tokens[this.position - 1].value;
1535
+ const right = this.parseAdditive();
1536
+ left = createBinaryOp(operator, left, right);
1537
+ }
1538
+ return left;
1539
+ }
1540
+ /**
1541
+ * Parse additive operators (+, -).
1542
+ * @private
1543
+ */
1544
+ parseAdditive() {
1545
+ let left = this.parseMultiplicative();
1546
+ while (this.match("PLUS" /* PLUS */, "MINUS" /* MINUS */)) {
1547
+ const operator = this.tokens[this.position - 1].value;
1548
+ const right = this.parseMultiplicative();
1549
+ left = createBinaryOp(operator, left, right);
1550
+ }
1551
+ return left;
1552
+ }
1553
+ /**
1554
+ * Parse multiplicative operators (*, /, %).
1555
+ * @private
1556
+ */
1557
+ parseMultiplicative() {
1558
+ let left = this.parseUnary();
1559
+ while (this.match("STAR" /* STAR */, "SLASH" /* SLASH */, "PERCENT" /* PERCENT */)) {
1560
+ const operator = this.tokens[this.position - 1].value;
1561
+ const right = this.parseUnary();
1562
+ left = createBinaryOp(operator, left, right);
1563
+ }
1564
+ return left;
1565
+ }
1566
+ /**
1567
+ * Parse unary operators (-, not).
1568
+ * @private
1569
+ */
1570
+ parseUnary() {
1571
+ if (this.match("MINUS" /* MINUS */, "NOT" /* NOT */)) {
1572
+ const operator = this.tokens[this.position - 1].value;
1573
+ const operand = this.parseUnary();
1574
+ return createUnaryOp(operator, operand);
1575
+ }
1576
+ return this.parsePostfix();
1577
+ }
1578
+ /**
1579
+ * Parse postfix operators (., [], ()).
1580
+ * @private
1581
+ */
1582
+ parsePostfix() {
1583
+ let expr = this.parsePrimary();
1584
+ while (true) {
1585
+ if (this.match("DOT" /* DOT */)) {
1586
+ const property = this.expect(
1587
+ "IDENTIFIER" /* IDENTIFIER */,
1588
+ "Expected property name after ."
1589
+ );
1590
+ expr = createMemberAccess(expr, property.value);
1591
+ } else if (this.match("LBRACKET" /* LBRACKET */)) {
1592
+ const index = this.parseTernary();
1593
+ this.expect("RBRACKET" /* RBRACKET */, "Expected ] after array index");
1594
+ expr = createIndexAccess(expr, index);
1595
+ } else if (this.current().type === "LPAREN" /* LPAREN */) {
1596
+ if (expr.kind === "MemberAccess") {
1597
+ this.advance();
1598
+ const args = [];
1599
+ if (this.current().type !== "RPAREN" /* RPAREN */) {
1600
+ args.push(this.parseTernary());
1601
+ while (this.match("COMMA" /* COMMA */)) {
1602
+ args.push(this.parseTernary());
1603
+ }
1604
+ }
1605
+ this.expect("RPAREN" /* RPAREN */, "Expected ) after arguments");
1606
+ const memberExpr = expr;
1607
+ expr = createMethodCall(memberExpr.object, memberExpr.property, args);
1608
+ } else {
1609
+ break;
1610
+ }
1611
+ } else {
1612
+ break;
1613
+ }
1614
+ }
1615
+ return expr;
1616
+ }
1617
+ /**
1618
+ * Parse primary expressions (literals, identifiers, grouping).
1619
+ * @private
1620
+ */
1621
+ parsePrimary() {
1622
+ if (this.match("NUMBER" /* NUMBER */, "STRING" /* STRING */, "BOOLEAN" /* BOOLEAN */)) {
1623
+ const token = this.tokens[this.position - 1];
1624
+ return createLiteral(token.value);
1625
+ }
1626
+ if (this.match("IDENTIFIER" /* IDENTIFIER */)) {
1627
+ const name = this.tokens[this.position - 1].value;
1628
+ if (this.match("DOUBLE_COLON" /* DOUBLE_COLON */)) {
1629
+ const value = this.expect(
1630
+ "IDENTIFIER" /* IDENTIFIER */,
1631
+ "Expected enum value after ::"
1632
+ );
1633
+ return createEnumAccess(name, value.value);
1634
+ }
1635
+ return createIdentifier(name);
1636
+ }
1637
+ if (this.match("LPAREN" /* LPAREN */)) {
1638
+ const expr = this.parseTernary();
1639
+ this.expect("RPAREN" /* RPAREN */, "Expected ) after expression");
1640
+ return expr;
1641
+ }
1642
+ throw new ParseError(
1643
+ `Unexpected token: ${this.current().type}`,
1644
+ this.current().position
1645
+ );
1646
+ }
1647
+ };
1648
+
1649
+ // src/expression/Evaluator.ts
1650
+ var Evaluator = class {
1651
+ /**
1652
+ * Evaluate an AST node in the given context.
1653
+ *
1654
+ * @param node - AST node to evaluate
1655
+ * @param context - Execution context
1656
+ * @returns Evaluated value
1657
+ * @throws {ParseError} If evaluation fails
1658
+ */
1659
+ evaluate(node, context) {
1660
+ const n = node;
1661
+ switch (node.kind) {
1662
+ case "Literal":
1663
+ return n.value;
1664
+ case "Identifier":
1665
+ return this.evaluateIdentifier(n.name, context);
1666
+ case "BinaryOp":
1667
+ return this.evaluateBinaryOp(n.operator, n.left, n.right, context);
1668
+ case "UnaryOp":
1669
+ return this.evaluateUnaryOp(n.operator, n.operand, context);
1670
+ case "Ternary":
1671
+ return this.evaluateTernary(n.condition, n.ifTrue, n.ifFalse, context);
1672
+ case "MemberAccess":
1673
+ return this.evaluateMemberAccess(n.object, n.property, context);
1674
+ case "IndexAccess":
1675
+ return this.evaluateIndexAccess(n.object, n.index, context);
1676
+ case "MethodCall":
1677
+ return this.evaluateMethodCall(n.object, n.method, n.args, context);
1678
+ case "EnumAccess":
1679
+ return this.evaluateEnumAccess(n.enumName, n.value, context);
1680
+ default:
1681
+ throw new ParseError(`Unknown AST node kind: ${node.kind}`);
1682
+ }
1683
+ }
1684
+ /**
1685
+ * Evaluate an identifier.
1686
+ * @private
1687
+ */
1688
+ evaluateIdentifier(name, context) {
1689
+ return context.resolve(name);
1690
+ }
1691
+ /**
1692
+ * Evaluate a binary operation.
1693
+ * @private
1694
+ */
1695
+ evaluateBinaryOp(operator, left, right, context) {
1696
+ const leftVal = this.evaluate(left, context);
1697
+ const rightVal = this.evaluate(right, context);
1698
+ switch (operator) {
1699
+ // Arithmetic
1700
+ case "+":
1701
+ return this.add(leftVal, rightVal);
1702
+ case "-":
1703
+ return this.toNumber(leftVal) - this.toNumber(rightVal);
1704
+ case "*":
1705
+ return this.toNumber(leftVal) * this.toNumber(rightVal);
1706
+ case "/":
1707
+ return this.toNumber(leftVal) / this.toNumber(rightVal);
1708
+ case "%":
1709
+ return this.modulo(this.toNumber(leftVal), this.toNumber(rightVal));
1710
+ // Comparison
1711
+ case "<":
1712
+ return this.compare(leftVal, rightVal) < 0;
1713
+ case "<=":
1714
+ return this.compare(leftVal, rightVal) <= 0;
1715
+ case ">":
1716
+ return this.compare(leftVal, rightVal) > 0;
1717
+ case ">=":
1718
+ return this.compare(leftVal, rightVal) >= 0;
1719
+ case "==":
1720
+ return this.equals(leftVal, rightVal);
1721
+ case "!=":
1722
+ return !this.equals(leftVal, rightVal);
1723
+ // Bitwise
1724
+ case "<<":
1725
+ return this.toInt(leftVal) << this.toInt(rightVal);
1726
+ case ">>":
1727
+ return this.toInt(leftVal) >> this.toInt(rightVal);
1728
+ case "&":
1729
+ return this.toInt(leftVal) & this.toInt(rightVal);
1730
+ case "|":
1731
+ return this.toInt(leftVal) | this.toInt(rightVal);
1732
+ case "^":
1733
+ return this.toInt(leftVal) ^ this.toInt(rightVal);
1734
+ // Logical
1735
+ case "and":
1736
+ return this.toBoolean(leftVal) && this.toBoolean(rightVal);
1737
+ case "or":
1738
+ return this.toBoolean(leftVal) || this.toBoolean(rightVal);
1739
+ default:
1740
+ throw new ParseError(`Unknown binary operator: ${operator}`);
1741
+ }
1742
+ }
1743
+ /**
1744
+ * Evaluate a unary operation.
1745
+ * @private
1746
+ */
1747
+ evaluateUnaryOp(operator, operand, context) {
1748
+ const value = this.evaluate(operand, context);
1749
+ switch (operator) {
1750
+ case "-":
1751
+ return -this.toNumber(value);
1752
+ case "not":
1753
+ return !this.toBoolean(value);
1754
+ default:
1755
+ throw new ParseError(`Unknown unary operator: ${operator}`);
1756
+ }
1757
+ }
1758
+ /**
1759
+ * Evaluate a ternary conditional.
1760
+ * @private
1761
+ */
1762
+ evaluateTernary(condition, ifTrue, ifFalse, context) {
1763
+ const condValue = this.evaluate(condition, context);
1764
+ return this.toBoolean(condValue) ? this.evaluate(ifTrue, context) : this.evaluate(ifFalse, context);
1765
+ }
1766
+ /**
1767
+ * Evaluate member access (object.property).
1768
+ * @private
1769
+ */
1770
+ evaluateMemberAccess(object, property, context) {
1771
+ const obj = this.evaluate(object, context);
1772
+ if (obj === null || obj === void 0) {
1773
+ throw new ParseError(
1774
+ `Cannot access property ${property} of null/undefined`
1775
+ );
1776
+ }
1777
+ if (typeof obj === "object") {
1778
+ return obj[property];
1779
+ }
1780
+ throw new ParseError(`Cannot access property ${property} of non-object`);
1781
+ }
1782
+ /**
1783
+ * Evaluate index access (array[index]).
1784
+ * @private
1785
+ */
1786
+ evaluateIndexAccess(object, index, context) {
1787
+ const obj = this.evaluate(object, context);
1788
+ const idx = this.evaluate(index, context);
1789
+ if (Array.isArray(obj)) {
1790
+ const numIdx = this.toInt(idx);
1791
+ return obj[numIdx];
1792
+ }
1793
+ if (obj instanceof Uint8Array) {
1794
+ const numIdx = this.toInt(idx);
1795
+ return obj[numIdx];
1796
+ }
1797
+ throw new ParseError("Index access requires an array");
1798
+ }
1799
+ /**
1800
+ * Evaluate method call (object.method()).
1801
+ * @private
1802
+ */
1803
+ evaluateMethodCall(object, method, _args, context) {
1804
+ const obj = this.evaluate(object, context);
1805
+ if (method === "length" || method === "size") {
1806
+ if (Array.isArray(obj)) return obj.length;
1807
+ if (obj instanceof Uint8Array) return obj.length;
1808
+ if (typeof obj === "string") return obj.length;
1809
+ throw new ParseError(`Object does not have a ${method} property`);
1810
+ }
1811
+ if (method === "to_i") {
1812
+ return this.toInt(obj);
1813
+ }
1814
+ if (method === "to_s") {
1815
+ return String(obj);
1816
+ }
1817
+ throw new ParseError(`Unknown method: ${method}`);
1818
+ }
1819
+ /**
1820
+ * Evaluate enum access (EnumName::value).
1821
+ * @private
1822
+ */
1823
+ evaluateEnumAccess(enumName, valueName, context) {
1824
+ const value = context.getEnumValue(enumName, valueName);
1825
+ if (value === void 0) {
1826
+ throw new ParseError(`Enum value "${enumName}::${valueName}" not found`);
1827
+ }
1828
+ return value;
1829
+ }
1830
+ /**
1831
+ * Helper: Add two values (handles strings and numbers).
1832
+ * @private
1833
+ */
1834
+ add(left, right) {
1835
+ if (typeof left === "string" || typeof right === "string") {
1836
+ return String(left) + String(right);
1837
+ }
1838
+ return this.toNumber(left) + this.toNumber(right);
1839
+ }
1840
+ /**
1841
+ * Helper: Modulo operation (Kaitai-style, not remainder).
1842
+ * @private
1843
+ */
1844
+ modulo(a, b) {
1845
+ const result = a % b;
1846
+ return result < 0 ? result + b : result;
1847
+ }
1848
+ /**
1849
+ * Helper: Compare two values.
1850
+ * @private
1851
+ */
1852
+ compare(left, right) {
1853
+ if (typeof left === "string" && typeof right === "string") {
1854
+ return left < right ? -1 : left > right ? 1 : 0;
1855
+ }
1856
+ const leftNum = this.toNumber(left);
1857
+ const rightNum = this.toNumber(right);
1858
+ return leftNum < rightNum ? -1 : leftNum > rightNum ? 1 : 0;
1859
+ }
1860
+ /**
1861
+ * Helper: Check equality.
1862
+ * @private
1863
+ */
1864
+ equals(left, right) {
1865
+ if (typeof left === "bigint" || typeof right === "bigint") {
1866
+ return BigInt(left) === BigInt(right);
1867
+ }
1868
+ return left === right;
1869
+ }
1870
+ /**
1871
+ * Helper: Convert to number.
1872
+ * @private
1873
+ */
1874
+ toNumber(value) {
1875
+ if (typeof value === "number") return value;
1876
+ if (typeof value === "bigint") return Number(value);
1877
+ if (typeof value === "boolean") return value ? 1 : 0;
1878
+ if (typeof value === "string") return parseFloat(value);
1879
+ throw new ParseError(`Cannot convert ${typeof value} to number`);
1880
+ }
1881
+ /**
1882
+ * Helper: Convert to integer.
1883
+ * @private
1884
+ */
1885
+ toInt(value) {
1886
+ return Math.floor(this.toNumber(value));
1887
+ }
1888
+ /**
1889
+ * Helper: Convert to boolean.
1890
+ * @private
1891
+ */
1892
+ toBoolean(value) {
1893
+ if (typeof value === "boolean") return value;
1894
+ if (typeof value === "number") return value !== 0;
1895
+ if (typeof value === "bigint") return value !== 0n;
1896
+ if (typeof value === "string") return value.length > 0;
1897
+ if (value === null || value === void 0) return false;
1898
+ return true;
1899
+ }
1900
+ };
1901
+
1902
+ // src/expression/index.ts
1903
+ function evaluateExpression(expression, context) {
1904
+ const lexer = new Lexer(expression);
1905
+ const tokens = lexer.tokenize();
1906
+ const parser = new ExpressionParser(tokens);
1907
+ const ast = parser.parse();
1908
+ const evaluator = new Evaluator();
1909
+ return evaluator.evaluate(ast, context);
1910
+ }
1911
+
1912
+ // src/interpreter/TypeInterpreter.ts
1913
+ var TypeInterpreter = class _TypeInterpreter {
1914
+ /**
1915
+ * Create a new type interpreter.
1916
+ *
1917
+ * @param schema - Kaitai Struct schema to interpret
1918
+ * @param parentMeta - Parent schema's meta (for nested types)
1919
+ */
1920
+ constructor(schema, parentMeta) {
1921
+ this.schema = schema;
1922
+ this.parentMeta = parentMeta;
1923
+ if (!schema.meta && !parentMeta) {
1924
+ throw new ParseError("Schema must have meta section");
1925
+ }
1926
+ if (schema.meta && !schema.meta.id && !parentMeta) {
1927
+ throw new ParseError("Root schema must have meta.id");
1928
+ }
1929
+ }
1930
+ /**
1931
+ * Parse binary data according to the schema.
1932
+ *
1933
+ * @param stream - Binary stream to parse
1934
+ * @param parent - Parent object (for nested types)
1935
+ * @param typeArgs - Arguments for parametric types
1936
+ * @returns Parsed object
1937
+ */
1938
+ parse(stream, parent, typeArgs) {
1939
+ const result = {};
1940
+ const context = new Context(stream, result, parent, this.schema.enums);
1941
+ context.current = result;
1942
+ if (typeArgs && this.schema.params) {
1943
+ for (let i = 0; i < this.schema.params.length && i < typeArgs.length; i++) {
1944
+ const param = this.schema.params[i];
1945
+ const argValue = typeArgs[i];
1946
+ const evaluatedArg = typeof argValue === "string" ? this.evaluateValue(argValue, context) : argValue;
1947
+ context.set(param.id, evaluatedArg);
1948
+ }
1949
+ }
1950
+ if (this.schema.seq) {
1951
+ for (const attr of this.schema.seq) {
1952
+ const value = this.parseAttribute(attr, context);
1953
+ if (attr.id) {
1954
+ result[attr.id] = value;
1955
+ }
1956
+ }
1957
+ }
1958
+ if (this.schema.instances) {
1959
+ this.setupInstances(result, stream, context);
1960
+ }
1961
+ return result;
1962
+ }
1963
+ /**
1964
+ * Set up lazy-evaluated instance getters.
1965
+ * Instances are computed on first access and cached.
1966
+ *
1967
+ * @param result - Result object to add getters to
1968
+ * @param stream - Stream for parsing
1969
+ * @param context - Execution context
1970
+ * @private
1971
+ */
1972
+ setupInstances(result, stream, context) {
1973
+ if (!this.schema.instances) return;
1974
+ for (const [name, instance] of Object.entries(this.schema.instances)) {
1975
+ let cached = void 0;
1976
+ let evaluated = false;
1977
+ Object.defineProperty(result, name, {
1978
+ get: () => {
1979
+ if (!evaluated) {
1980
+ cached = this.parseInstance(
1981
+ instance,
1982
+ stream,
1983
+ context
1984
+ );
1985
+ evaluated = true;
1986
+ }
1987
+ return cached;
1988
+ },
1989
+ enumerable: true,
1990
+ configurable: true
1991
+ });
1992
+ }
1993
+ }
1994
+ /**
1995
+ * Parse an instance (lazy-evaluated field).
1996
+ *
1997
+ * @param instance - Instance specification
1998
+ * @param stream - Stream to read from
1999
+ * @param context - Execution context
2000
+ * @returns Parsed or calculated value
2001
+ * @private
2002
+ */
2003
+ parseInstance(instance, stream, context) {
2004
+ if ("value" in instance) {
2005
+ return this.evaluateValue(
2006
+ instance.value,
2007
+ context
2008
+ );
2009
+ }
2010
+ const savedPos = stream.pos;
2011
+ try {
2012
+ if (instance.pos !== void 0) {
2013
+ const pos = this.evaluateValue(
2014
+ instance.pos,
2015
+ context
2016
+ );
2017
+ if (typeof pos === "number") {
2018
+ stream.seek(pos);
2019
+ } else if (typeof pos === "bigint") {
2020
+ stream.seek(Number(pos));
2021
+ } else {
2022
+ throw new ParseError(
2023
+ `pos must evaluate to a number, got ${typeof pos}`
2024
+ );
2025
+ }
2026
+ }
2027
+ const value = this.parseAttribute(instance, context);
2028
+ return value;
2029
+ } finally {
2030
+ if (instance.pos !== void 0) {
2031
+ stream.seek(savedPos);
2032
+ }
2033
+ }
2034
+ }
2035
+ /**
2036
+ * Parse a single attribute according to its specification.
2037
+ *
2038
+ * @param attr - Attribute specification
2039
+ * @param context - Execution context
2040
+ * @returns Parsed value
2041
+ * @private
2042
+ */
2043
+ parseAttribute(attr, context) {
2044
+ const stream = context.io;
2045
+ if (attr.if) {
2046
+ const condition = this.evaluateValue(attr.if, context);
2047
+ if (!condition) {
2048
+ return void 0;
2049
+ }
2050
+ }
2051
+ if (attr.pos !== void 0) {
2052
+ const pos = this.evaluateValue(attr.pos, context);
2053
+ if (typeof pos === "number") {
2054
+ stream.seek(pos);
2055
+ } else if (typeof pos === "bigint") {
2056
+ stream.seek(Number(pos));
2057
+ } else {
2058
+ throw new ParseError(`pos must evaluate to a number, got ${typeof pos}`);
2059
+ }
2060
+ }
2061
+ if (attr.io) {
2062
+ throw new NotImplementedError("Custom I/O streams");
2063
+ }
2064
+ if (attr.repeat) {
2065
+ return this.parseRepeated(attr, context);
2066
+ }
2067
+ if (attr.contents) {
2068
+ return this.parseContents(attr, context);
2069
+ }
2070
+ const value = this.parseValue(attr, context);
2071
+ return value;
2072
+ }
2073
+ /**
2074
+ * Parse a repeated attribute.
2075
+ *
2076
+ * @param attr - Attribute specification with repeat
2077
+ * @param context - Execution context
2078
+ * @returns Array of parsed values
2079
+ * @private
2080
+ */
2081
+ parseRepeated(attr, context) {
2082
+ const stream = context.io;
2083
+ const result = [];
2084
+ switch (attr.repeat) {
2085
+ case "expr": {
2086
+ const countValue = this.evaluateValue(attr["repeat-expr"], context);
2087
+ const count = typeof countValue === "number" ? countValue : typeof countValue === "bigint" ? Number(countValue) : 0;
2088
+ if (count < 0) {
2089
+ throw new ParseError(`repeat-expr must be non-negative, got ${count}`);
2090
+ }
2091
+ for (let i = 0; i < count; i++) {
2092
+ context.set("_index", i);
2093
+ const value = this.parseAttribute(
2094
+ { ...attr, repeat: void 0, "repeat-expr": void 0 },
2095
+ context
2096
+ );
2097
+ result.push(value);
2098
+ }
2099
+ break;
2100
+ }
2101
+ case "eos": {
2102
+ while (!stream.isEof()) {
2103
+ context.set("_index", result.length);
2104
+ result.push(this.parseValue(attr, context));
2105
+ }
2106
+ break;
2107
+ }
2108
+ case "until": {
2109
+ if (!attr["repeat-until"]) {
2110
+ throw new ParseError("repeat-until expression is required");
2111
+ }
2112
+ let index = 0;
2113
+ while (true) {
2114
+ context.set("_index", index);
2115
+ const value = this.parseAttribute(
2116
+ { ...attr, repeat: void 0, "repeat-until": void 0 },
2117
+ context
2118
+ );
2119
+ result.push(value);
2120
+ context.set("_", value);
2121
+ const condition = this.evaluateValue(attr["repeat-until"], context);
2122
+ if (condition) {
2123
+ break;
2124
+ }
2125
+ if (stream.isEof()) {
2126
+ break;
2127
+ }
2128
+ index++;
2129
+ }
2130
+ break;
2131
+ }
2132
+ default:
2133
+ throw new ParseError(`Unknown repeat type: ${attr.repeat}`);
2134
+ }
2135
+ return result;
2136
+ }
2137
+ /**
2138
+ * Parse and validate contents.
2139
+ *
2140
+ * @param attr - Attribute specification with contents
2141
+ * @param context - Execution context
2142
+ * @returns The validated contents
2143
+ * @private
2144
+ */
2145
+ parseContents(attr, context) {
2146
+ const stream = context.io;
2147
+ const expected = attr.contents;
2148
+ if (Array.isArray(expected)) {
2149
+ const bytes = stream.readBytes(expected.length);
2150
+ for (let i = 0; i < expected.length; i++) {
2151
+ if (bytes[i] !== expected[i]) {
2152
+ throw new ValidationError(
2153
+ `Contents mismatch at byte ${i}: expected ${expected[i]}, got ${bytes[i]}`,
2154
+ stream.pos - expected.length + i
2155
+ );
2156
+ }
2157
+ }
2158
+ return bytes;
2159
+ } else {
2160
+ const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
2161
+ const str = stream.readStr(expected.length, encoding);
2162
+ if (str !== expected) {
2163
+ throw new ValidationError(
2164
+ `Contents mismatch: expected "${expected}", got "${str}"`,
2165
+ stream.pos - expected.length
2166
+ );
2167
+ }
2168
+ return str;
2169
+ }
2170
+ }
2171
+ /**
2172
+ * Parse a single value according to its type.
2173
+ *
2174
+ * @param attr - Attribute specification
2175
+ * @param context - Execution context
2176
+ * @returns Parsed value
2177
+ * @private
2178
+ */
2179
+ parseValue(attr, context) {
2180
+ const stream = context.io;
2181
+ const type = attr.type;
2182
+ if (attr.size !== void 0) {
2183
+ const sizeValue = this.evaluateValue(attr.size, context);
2184
+ const size = typeof sizeValue === "number" ? sizeValue : typeof sizeValue === "bigint" ? Number(sizeValue) : 0;
2185
+ if (size < 0) {
2186
+ throw new ParseError(`size must be non-negative, got ${size}`);
2187
+ }
2188
+ if (type === "str" || !type) {
2189
+ const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
2190
+ let data;
2191
+ if (type === "str") {
2192
+ data = stream.readBytes(size);
2193
+ if (attr.process) {
2194
+ data = this.applyProcessing(data, attr.process);
2195
+ }
2196
+ return new TextDecoder(encoding).decode(data);
2197
+ } else {
2198
+ data = stream.readBytes(size);
2199
+ if (attr.process) {
2200
+ data = this.applyProcessing(data, attr.process);
2201
+ }
2202
+ return data;
2203
+ }
2204
+ } else {
2205
+ let data = stream.readBytes(size);
2206
+ if (attr.process) {
2207
+ data = this.applyProcessing(data, attr.process);
2208
+ }
2209
+ const substream = new KaitaiStream(data);
2210
+ return this.parseType(type, substream, context, attr["type-args"]);
2211
+ }
2212
+ }
2213
+ if (attr["size-eos"]) {
2214
+ if (type === "str") {
2215
+ const encoding = attr.encoding || this.schema.meta.encoding || "UTF-8";
2216
+ const bytes = stream.readBytesFull();
2217
+ return new TextDecoder(encoding).decode(bytes);
2218
+ } else {
2219
+ return stream.readBytesFull();
2220
+ }
2221
+ }
2222
+ if (!type) {
2223
+ throw new ParseError("Attribute must have either type, size, or contents");
2224
+ }
2225
+ return this.parseType(type, stream, context, attr["type-args"]);
2226
+ }
2227
+ /**
2228
+ * Parse a value of a specific type.
2229
+ *
2230
+ * @param type - Type name or switch specification
2231
+ * @param stream - Stream to read from
2232
+ * @param context - Execution context
2233
+ * @param typeArgs - Arguments for parametric types
2234
+ * @returns Parsed value
2235
+ * @private
2236
+ */
2237
+ parseType(type, stream, context, typeArgs) {
2238
+ if (typeof type === "object") {
2239
+ return this.parseSwitchType(
2240
+ type,
2241
+ stream,
2242
+ context
2243
+ );
2244
+ }
2245
+ if (isBuiltinType(type)) {
2246
+ return this.parseBuiltinType(type, stream, context);
2247
+ }
2248
+ if (this.schema.types && type in this.schema.types) {
2249
+ const typeSchema = this.schema.types[type];
2250
+ const meta = this.schema.meta || this.parentMeta;
2251
+ if (this.schema.enums && !typeSchema.enums) {
2252
+ typeSchema.enums = this.schema.enums;
2253
+ }
2254
+ if (this.schema.types && !typeSchema.types) {
2255
+ typeSchema.types = this.schema.types;
2256
+ }
2257
+ const interpreter = new _TypeInterpreter(typeSchema, meta);
2258
+ return interpreter.parse(stream, context.current, typeArgs);
2259
+ }
2260
+ throw new ParseError(`Unknown type: ${type}`);
2261
+ }
2262
+ /**
2263
+ * Parse a switch type (type selection based on expression).
2264
+ *
2265
+ * @param switchType - Switch type specification
2266
+ * @param stream - Stream to read from
2267
+ * @param context - Execution context
2268
+ * @returns Parsed value
2269
+ * @private
2270
+ */
2271
+ parseSwitchType(switchType, stream, context) {
2272
+ const switchOn = switchType["switch-on"];
2273
+ const cases = switchType["cases"];
2274
+ const defaultType = switchType["default"];
2275
+ if (!switchOn || typeof switchOn !== "string") {
2276
+ throw new ParseError("switch-on expression is required for switch types");
2277
+ }
2278
+ if (!cases) {
2279
+ throw new ParseError("cases are required for switch types");
2280
+ }
2281
+ const switchValue = this.evaluateValue(switchOn, context);
2282
+ const switchKey = String(switchValue);
2283
+ let selectedType = cases[switchKey];
2284
+ if (selectedType === void 0 && defaultType) {
2285
+ selectedType = defaultType;
2286
+ }
2287
+ if (selectedType === void 0) {
2288
+ throw new ParseError(
2289
+ `No matching case for switch value "${switchKey}" and no default type specified`
2290
+ );
2291
+ }
2292
+ return this.parseType(selectedType, stream, context);
2293
+ }
2294
+ /**
2295
+ * Parse a built-in type.
2296
+ *
2297
+ * @param type - Built-in type name
2298
+ * @param stream - Stream to read from
2299
+ * @param context - Execution context
2300
+ * @returns Parsed value
2301
+ * @private
2302
+ */
2303
+ parseBuiltinType(type, stream, _context) {
2304
+ const base = getBaseType(type);
2305
+ const typeEndian = getTypeEndianness(type);
2306
+ const meta = this.schema.meta || this.parentMeta;
2307
+ const metaEndian = meta?.endian;
2308
+ const endian = typeEndian || (typeof metaEndian === "string" ? metaEndian : "le");
2309
+ if (isIntegerType(type)) {
2310
+ return this.readInteger(base, endian, stream);
2311
+ }
2312
+ if (isFloatType(type)) {
2313
+ return this.readFloat(base, endian, stream);
2314
+ }
2315
+ if (isStringType(type)) {
2316
+ throw new ParseError("String types require size, size-eos, or terminator");
2317
+ }
2318
+ throw new ParseError(`Unknown built-in type: ${type}`);
2319
+ }
2320
+ /**
2321
+ * Read an integer value.
2322
+ *
2323
+ * @param type - Integer type (u1, u2, u4, u8, s1, s2, s4, s8)
2324
+ * @param endian - Endianness
2325
+ * @param stream - Stream to read from
2326
+ * @returns Integer value
2327
+ * @private
2328
+ */
2329
+ readInteger(type, endian, stream) {
2330
+ switch (type) {
2331
+ case "u1":
2332
+ return stream.readU1();
2333
+ case "u2":
2334
+ return endian === "le" ? stream.readU2le() : stream.readU2be();
2335
+ case "u4":
2336
+ return endian === "le" ? stream.readU4le() : stream.readU4be();
2337
+ case "u8":
2338
+ return endian === "le" ? stream.readU8le() : stream.readU8be();
2339
+ case "s1":
2340
+ return stream.readS1();
2341
+ case "s2":
2342
+ return endian === "le" ? stream.readS2le() : stream.readS2be();
2343
+ case "s4":
2344
+ return endian === "le" ? stream.readS4le() : stream.readS4be();
2345
+ case "s8":
2346
+ return endian === "le" ? stream.readS8le() : stream.readS8be();
2347
+ default:
2348
+ throw new ParseError(`Unknown integer type: ${type}`);
2349
+ }
2350
+ }
2351
+ /**
2352
+ * Read a floating point value.
2353
+ *
2354
+ * @param type - Float type (f4, f8)
2355
+ * @param endian - Endianness
2356
+ * @param stream - Stream to read from
2357
+ * @returns Float value
2358
+ * @private
2359
+ */
2360
+ readFloat(type, endian, stream) {
2361
+ switch (type) {
2362
+ case "f4":
2363
+ return endian === "le" ? stream.readF4le() : stream.readF4be();
2364
+ case "f8":
2365
+ return endian === "le" ? stream.readF8le() : stream.readF8be();
2366
+ default:
2367
+ throw new ParseError(`Unknown float type: ${type}`);
2368
+ }
2369
+ }
2370
+ /**
2371
+ * Apply processing transformation to data.
2372
+ * Supports basic transformations like zlib decompression.
2373
+ *
2374
+ * @param data - Data to process
2375
+ * @param process - Processing specification
2376
+ * @returns Processed data
2377
+ * @private
2378
+ */
2379
+ applyProcessing(data, process2) {
2380
+ const processType = typeof process2 === "string" ? process2 : process2.algorithm;
2381
+ if (processType) {
2382
+ throw new NotImplementedError(
2383
+ `Processing type "${processType}" is not yet implemented. Supported in future versions with zlib, encryption, etc.`
2384
+ );
2385
+ }
2386
+ return data;
2387
+ }
2388
+ /**
2389
+ * Evaluate a value that can be an expression or literal.
2390
+ *
2391
+ * @param value - Value to evaluate (expression string, number, or boolean)
2392
+ * @param context - Execution context
2393
+ * @returns Evaluated result
2394
+ * @private
2395
+ */
2396
+ evaluateValue(value, context) {
2397
+ if (value === void 0) {
2398
+ return void 0;
2399
+ }
2400
+ if (typeof value === "number" || typeof value === "boolean") {
2401
+ return value;
2402
+ }
2403
+ if (typeof value === "string") {
2404
+ try {
2405
+ return evaluateExpression(value, context);
2406
+ } catch (error) {
2407
+ throw new ParseError(
2408
+ `Failed to evaluate expression "${value}": ${error instanceof Error ? error.message : String(error)}`
2409
+ );
2410
+ }
2411
+ }
2412
+ return value;
2413
+ }
2414
+ };
2415
+
2416
+ // src/index.ts
2417
+ function parse(ksyYaml, buffer, options = {}) {
2418
+ const { validate = true, strict = false } = options;
2419
+ const parser = new KsyParser();
2420
+ const schema = parser.parse(ksyYaml, { validate, strict });
2421
+ const stream = new KaitaiStream(buffer);
2422
+ const interpreter = new TypeInterpreter(schema);
2423
+ return interpreter.parse(stream);
2424
+ }
2425
+
2426
+ // src/cli.ts
2427
+ var VERSION = "0.7.0";
2428
+ var HELP_TEXT = `
2429
+ kaitai - Parse binary files using Kaitai Struct definitions
2430
+
2431
+ USAGE:
2432
+ kaitai <ksy-file> <binary-file> [options]
2433
+ pnpx kaitai <ksy-file> <binary-file> [options]
2434
+
2435
+ ARGUMENTS:
2436
+ <ksy-file> Path to .ksy definition file (YAML format)
2437
+ <binary-file> Path to binary file to parse
2438
+
2439
+ OPTIONS:
2440
+ -o, --output <file> Write output to file instead of stdout
2441
+ -p, --pretty Pretty-print JSON output (default: true for stdout)
2442
+ -f, --format <format> Output format: json or yaml (default: json)
2443
+ --field <path> Extract specific field (dot notation: e.g., "header.version")
2444
+ --no-validate Skip schema validation
2445
+ --strict Treat schema warnings as errors
2446
+ -q, --quiet Suppress non-error output
2447
+ -h, --help Show this help message
2448
+ -v, --version Show version number
2449
+
2450
+ EXAMPLES:
2451
+ # Parse a binary file and output JSON
2452
+ kaitai format.ksy data.bin
2453
+
2454
+ # Parse and save to file
2455
+ kaitai format.ksy data.bin -o output.json
2456
+
2457
+ # Parse with pretty printing disabled
2458
+ kaitai format.ksy data.bin --no-pretty
2459
+
2460
+ # Extract specific field
2461
+ kaitai format.ksy data.bin --field header.magic
2462
+
2463
+ # Strict validation
2464
+ kaitai format.ksy data.bin --strict
2465
+
2466
+ # Output as YAML
2467
+ kaitai format.ksy data.bin --format yaml
2468
+
2469
+ EXIT CODES:
2470
+ 0 Success
2471
+ 1 General error (file not found, parse error, etc.)
2472
+ 2 Invalid arguments or usage
2473
+ 3 Schema validation error
2474
+
2475
+ For more information, visit: https://github.com/fabianopinto/kaitai-struct-ts
2476
+ `;
2477
+ function showHelp() {
2478
+ console.log(HELP_TEXT);
2479
+ }
2480
+ function showVersion() {
2481
+ console.log(`kaitai v${VERSION}`);
2482
+ }
2483
+ function parseCliArgs() {
2484
+ try {
2485
+ const { values, positionals } = (0, import_util.parseArgs)({
2486
+ args: process.argv.slice(2),
2487
+ options: {
2488
+ output: { type: "string", short: "o" },
2489
+ pretty: { type: "boolean", short: "p", default: void 0 },
2490
+ "no-pretty": { type: "boolean", default: false },
2491
+ format: { type: "string", short: "f", default: "json" },
2492
+ field: { type: "string" },
2493
+ validate: { type: "boolean", default: true },
2494
+ "no-validate": { type: "boolean", default: false },
2495
+ strict: { type: "boolean", default: false },
2496
+ quiet: { type: "boolean", short: "q", default: false },
2497
+ help: { type: "boolean", short: "h", default: false },
2498
+ version: { type: "boolean", short: "v", default: false }
2499
+ },
2500
+ allowPositionals: true
2501
+ });
2502
+ const pretty = values.pretty !== void 0 ? values.pretty : values["no-pretty"] ? false : void 0;
2503
+ const validate = values["no-validate"] ? false : values.validate;
2504
+ const options = {
2505
+ output: values.output,
2506
+ pretty,
2507
+ format: values.format || "json",
2508
+ field: values.field,
2509
+ validate,
2510
+ strict: values.strict,
2511
+ quiet: values.quiet,
2512
+ help: values.help,
2513
+ version: values.version
2514
+ };
2515
+ return { options, positional: positionals };
2516
+ } catch (error) {
2517
+ if (error instanceof Error) {
2518
+ console.error(`Error parsing arguments: ${error.message}`);
2519
+ }
2520
+ process.exit(2);
2521
+ }
2522
+ }
2523
+ function readFile(filePath, description) {
2524
+ const resolvedPath = (0, import_path.resolve)(filePath);
2525
+ if (!(0, import_fs.existsSync)(resolvedPath)) {
2526
+ console.error(`Error: ${description} not found: ${filePath}`);
2527
+ process.exit(1);
2528
+ }
2529
+ try {
2530
+ return (0, import_fs.readFileSync)(resolvedPath);
2531
+ } catch (error) {
2532
+ console.error(
2533
+ `Error reading ${description}: ${error instanceof Error ? error.message : String(error)}`
2534
+ );
2535
+ process.exit(1);
2536
+ }
2537
+ }
2538
+ function extractField(obj, path) {
2539
+ const parts = path.split(".");
2540
+ let current = obj;
2541
+ for (const part of parts) {
2542
+ if (current === null || current === void 0 || typeof current !== "object") {
2543
+ throw new Error(`Cannot access property '${part}' of ${typeof current}`);
2544
+ }
2545
+ current = current[part];
2546
+ }
2547
+ return current;
2548
+ }
2549
+ function formatOutput(data, format, pretty) {
2550
+ if (format === "yaml") {
2551
+ return JSON.stringify(data, null, 2).replace(/^{$/gm, "").replace(/^}$/gm, "").replace(/^\s*"([^"]+)":\s*/gm, "$1: ").replace(/,$/gm, "");
2552
+ }
2553
+ return pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
2554
+ }
2555
+ function main() {
2556
+ const { options, positional } = parseCliArgs();
2557
+ if (options.help) {
2558
+ showHelp();
2559
+ process.exit(0);
2560
+ }
2561
+ if (options.version) {
2562
+ showVersion();
2563
+ process.exit(0);
2564
+ }
2565
+ if (positional.length < 2) {
2566
+ console.error("Error: Missing required arguments");
2567
+ console.error("Usage: kaitai <ksy-file> <binary-file> [options]");
2568
+ console.error('Run "kaitai --help" for more information');
2569
+ process.exit(2);
2570
+ }
2571
+ if (positional.length > 2) {
2572
+ console.error("Error: Too many arguments");
2573
+ console.error("Usage: kaitai <ksy-file> <binary-file> [options]");
2574
+ process.exit(2);
2575
+ }
2576
+ const [ksyFile, binaryFile] = positional;
2577
+ if (options.format && !["json", "yaml"].includes(options.format)) {
2578
+ console.error(
2579
+ `Error: Invalid format '${options.format}'. Must be 'json' or 'yaml'`
2580
+ );
2581
+ process.exit(2);
2582
+ }
2583
+ if (!options.quiet) {
2584
+ console.error(`Reading KSY definition: ${ksyFile}`);
2585
+ console.error(`Reading binary file: ${binaryFile}`);
2586
+ }
2587
+ const ksyContent = readFile(ksyFile, "KSY definition file").toString("utf-8");
2588
+ const binaryData = readFile(binaryFile, "Binary file");
2589
+ const parseOptions = {
2590
+ validate: options.validate,
2591
+ strict: options.strict
2592
+ };
2593
+ if (!options.quiet) {
2594
+ console.error("Parsing...");
2595
+ }
2596
+ let result;
2597
+ try {
2598
+ result = parse(ksyContent, binaryData, parseOptions);
2599
+ } catch (error) {
2600
+ console.error(
2601
+ `Parse error: ${error instanceof Error ? error.message : String(error)}`
2602
+ );
2603
+ if (error instanceof Error && error.name === "ValidationError") {
2604
+ process.exit(3);
2605
+ }
2606
+ process.exit(1);
2607
+ }
2608
+ let output = result;
2609
+ if (options.field) {
2610
+ try {
2611
+ output = extractField(result, options.field);
2612
+ if (!options.quiet) {
2613
+ console.error(`Extracted field: ${options.field}`);
2614
+ }
2615
+ } catch (error) {
2616
+ console.error(
2617
+ `Error extracting field '${options.field}': ${error instanceof Error ? error.message : String(error)}`
2618
+ );
2619
+ process.exit(1);
2620
+ }
2621
+ }
2622
+ const shouldPretty = options.pretty !== void 0 ? options.pretty : !options.output;
2623
+ const formatted = formatOutput(output, options.format || "json", shouldPretty);
2624
+ if (options.output) {
2625
+ try {
2626
+ (0, import_fs.writeFileSync)(options.output, formatted + "\n", "utf-8");
2627
+ if (!options.quiet) {
2628
+ console.error(`Output written to: ${options.output}`);
2629
+ }
2630
+ } catch (error) {
2631
+ console.error(
2632
+ `Error writing output file: ${error instanceof Error ? error.message : String(error)}`
2633
+ );
2634
+ process.exit(1);
2635
+ }
2636
+ } else {
2637
+ console.log(formatted);
2638
+ }
2639
+ if (!options.quiet) {
2640
+ console.error("Done!");
2641
+ }
2642
+ }
2643
+ main();
2644
+ /**
2645
+ * @fileoverview Custom error classes for Kaitai Struct parsing and validation
2646
+ * @module utils/errors
2647
+ * @author Fabiano Pinto
2648
+ * @license MIT
2649
+ */
2650
+ /**
2651
+ * @fileoverview String encoding and decoding utilities for binary data
2652
+ * @module utils/encoding
2653
+ * @author Fabiano Pinto
2654
+ * @license MIT
2655
+ */
2656
+ /**
2657
+ * @fileoverview Binary stream reader for Kaitai Struct
2658
+ * @module stream/KaitaiStream
2659
+ * @author Fabiano Pinto
2660
+ * @license MIT
2661
+ */
2662
+ /**
2663
+ * @fileoverview Binary stream reading functionality
2664
+ * @module stream
2665
+ * @author Fabiano Pinto
2666
+ * @license MIT
2667
+ */
2668
+ /**
2669
+ * @fileoverview Type definitions for Kaitai Struct YAML schema (.ksy files)
2670
+ * @module parser/schema
2671
+ * @author Fabiano Pinto
2672
+ * @license MIT
2673
+ */
2674
+ /**
2675
+ * @fileoverview Parser for Kaitai Struct YAML (.ksy) files
2676
+ * @module parser/KsyParser
2677
+ * @author Fabiano Pinto
2678
+ * @license MIT
2679
+ */
2680
+ /**
2681
+ * @fileoverview Parser module for Kaitai Struct YAML files
2682
+ * @module parser
2683
+ * @author Fabiano Pinto
2684
+ * @license MIT
2685
+ */
2686
+ /**
2687
+ * @fileoverview Execution context for Kaitai Struct parsing
2688
+ * @module interpreter/Context
2689
+ * @author Fabiano Pinto
2690
+ * @license MIT
2691
+ */
2692
+ /**
2693
+ * @fileoverview Token types for Kaitai Struct expression language
2694
+ * @module expression/Token
2695
+ * @author Fabiano Pinto
2696
+ * @license MIT
2697
+ */
2698
+ /**
2699
+ * @fileoverview Lexer for Kaitai Struct expression language
2700
+ * @module expression/Lexer
2701
+ * @author Fabiano Pinto
2702
+ * @license MIT
2703
+ */
2704
+ /**
2705
+ * @fileoverview Abstract Syntax Tree nodes for expression language
2706
+ * @module expression/AST
2707
+ * @author Fabiano Pinto
2708
+ * @license MIT
2709
+ */
2710
+ /**
2711
+ * @fileoverview Parser for Kaitai Struct expression language
2712
+ * @module expression/Parser
2713
+ * @author Fabiano Pinto
2714
+ * @license MIT
2715
+ */
2716
+ /**
2717
+ * @fileoverview Evaluator for Kaitai Struct expression AST
2718
+ * @module expression/Evaluator
2719
+ * @author Fabiano Pinto
2720
+ * @license MIT
2721
+ */
2722
+ /**
2723
+ * @fileoverview Expression evaluation module
2724
+ * @module expression
2725
+ * @author Fabiano Pinto
2726
+ * @license MIT
2727
+ */
2728
+ /**
2729
+ * @fileoverview Type interpreter for executing Kaitai Struct schemas
2730
+ * @module interpreter/TypeInterpreter
2731
+ * @author Fabiano Pinto
2732
+ * @license MIT
2733
+ */
2734
+ /**
2735
+ * @fileoverview Interpreter module for executing Kaitai Struct schemas
2736
+ * @module interpreter
2737
+ * @author Fabiano Pinto
2738
+ * @license MIT
2739
+ */
2740
+ /**
2741
+ * @fileoverview Main entry point for kaitai-struct-ts library
2742
+ * @module kaitai-struct-ts
2743
+ * @author Fabiano Pinto
2744
+ * @license MIT
2745
+ * @version 0.2.0
2746
+ *
2747
+ * @description
2748
+ * A runtime interpreter for Kaitai Struct binary format definitions in TypeScript.
2749
+ * Parse any binary data format by providing a .ksy (Kaitai Struct YAML) definition file.
2750
+ *
2751
+ * @example
2752
+ * ```typescript
2753
+ * import { parse } from 'kaitai-struct-ts'
2754
+ *
2755
+ * const ksyDefinition = `
2756
+ * meta:
2757
+ * id: my_format
2758
+ * endian: le
2759
+ * seq:
2760
+ * - id: magic
2761
+ * contents: [0x4D, 0x5A]
2762
+ * - id: version
2763
+ * type: u2
2764
+ * `
2765
+ *
2766
+ * const buffer = new Uint8Array([0x4D, 0x5A, 0x01, 0x00])
2767
+ * const result = parse(ksyDefinition, buffer)
2768
+ * console.log(result.version) // 1
2769
+ * ```
2770
+ */
2771
+ /**
2772
+ * @fileoverview CLI utility for parsing binary files with Kaitai Struct definitions
2773
+ * @module kaitai-struct-ts/cli
2774
+ * @author Fabiano Pinto
2775
+ * @license MIT
2776
+ */