@bis-toolkit/cppparser 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,772 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/lexer.ts
9
+ var CfgKeywords = /* @__PURE__ */ new Set([
10
+ "class",
11
+ "delete",
12
+ "enum",
13
+ "true",
14
+ "false",
15
+ "null",
16
+ "NULL"
17
+ ]);
18
+ var CfgPunctuations = /* @__PURE__ */ new Set([
19
+ "{",
20
+ "}",
21
+ ";",
22
+ ":",
23
+ "=",
24
+ "[",
25
+ "]",
26
+ ",",
27
+ "+",
28
+ "-",
29
+ "*",
30
+ "/"
31
+ ]);
32
+ var CfgOperators = /* @__PURE__ */ new Set([
33
+ "+=",
34
+ "-=",
35
+ "=",
36
+ "+",
37
+ "-",
38
+ "*",
39
+ "/"
40
+ ]);
41
+ var TokenKind = /* @__PURE__ */ ((TokenKind2) => {
42
+ TokenKind2[TokenKind2["Keyword"] = 0] = "Keyword";
43
+ TokenKind2[TokenKind2["Identifier"] = 1] = "Identifier";
44
+ TokenKind2[TokenKind2["Number"] = 2] = "Number";
45
+ TokenKind2[TokenKind2["String"] = 3] = "String";
46
+ TokenKind2[TokenKind2["Punctuation"] = 4] = "Punctuation";
47
+ TokenKind2[TokenKind2["EOF"] = 5] = "EOF";
48
+ TokenKind2[TokenKind2["Unknown"] = 6] = "Unknown";
49
+ return TokenKind2;
50
+ })(TokenKind || {});
51
+ var isCfgKeyword = (value) => {
52
+ return CfgKeywords.has(value);
53
+ };
54
+ var isCfgPunctuation = (value) => {
55
+ return CfgPunctuations.has(value);
56
+ };
57
+ var operators = Array.from(CfgOperators).sort((a, b) => b.length - a.length);
58
+ var lex = (input) => {
59
+ const tokens = [];
60
+ let i = 0;
61
+ let currentLine = 1;
62
+ let currentColumn = 1;
63
+ const push = (kind, value, start, end, tokenLine, tokenColumn) => {
64
+ tokens.push({ kind, value, start, end, line: tokenLine, column: tokenColumn });
65
+ };
66
+ const updatePosition = (text) => {
67
+ for (const char of text) {
68
+ if (char === "\n") {
69
+ currentLine++;
70
+ currentColumn = 1;
71
+ } else {
72
+ currentColumn++;
73
+ }
74
+ }
75
+ };
76
+ while (i < input.length) {
77
+ while (i < input.length && /\s/.test(input[i])) {
78
+ updatePosition(input[i]);
79
+ i++;
80
+ }
81
+ if (i >= input.length) {
82
+ break;
83
+ }
84
+ const start = i;
85
+ const startLine = currentLine;
86
+ const startColumn = currentColumn;
87
+ if (input[i] === "/" && i + 1 < input.length) {
88
+ if (input[i + 1] === "/") {
89
+ while (i < input.length && input[i] !== "\n") {
90
+ i++;
91
+ }
92
+ continue;
93
+ } else if (input[i + 1] === "*") {
94
+ i += 2;
95
+ while (i + 1 < input.length) {
96
+ if (input[i] === "*" && input[i + 1] === "/") {
97
+ i += 2;
98
+ break;
99
+ }
100
+ i++;
101
+ }
102
+ continue;
103
+ }
104
+ }
105
+ let matched = false;
106
+ for (const op of operators) {
107
+ if (input.startsWith(op, i)) {
108
+ push(4 /* Punctuation */, op, i, i + op.length, startLine, startColumn);
109
+ updatePosition(op);
110
+ i += op.length;
111
+ matched = true;
112
+ break;
113
+ }
114
+ }
115
+ if (matched) {
116
+ continue;
117
+ }
118
+ if (/[a-zA-Z_]/.test(input[i])) {
119
+ while (i < input.length && /[a-zA-Z0-9_]/.test(input[i])) {
120
+ i++;
121
+ }
122
+ const value = input.slice(start, i);
123
+ const kind = isCfgKeyword(value) ? 0 /* Keyword */ : 1 /* Identifier */;
124
+ push(kind, value, start, i, startLine, startColumn);
125
+ updatePosition(value);
126
+ } else if (/\d/.test(input[i])) {
127
+ const numStart = i;
128
+ while (i < input.length && /\d/.test(input[i])) {
129
+ i++;
130
+ }
131
+ if (i < input.length && input[i] === ".") {
132
+ i++;
133
+ while (i < input.length && /\d/.test(input[i])) {
134
+ i++;
135
+ }
136
+ }
137
+ if (i < input.length && /[eE]/.test(input[i])) {
138
+ const ePos = i;
139
+ i++;
140
+ if (i < input.length && /[+-]/.test(input[i])) {
141
+ i++;
142
+ }
143
+ if (i < input.length && /\d/.test(input[i])) {
144
+ while (i < input.length && /\d/.test(input[i])) {
145
+ i++;
146
+ }
147
+ } else {
148
+ i = ePos;
149
+ }
150
+ }
151
+ if (i < input.length && /[a-zA-Z_]/.test(input[i])) {
152
+ while (i < input.length && /[a-zA-Z0-9_]/.test(input[i])) {
153
+ i++;
154
+ }
155
+ const value2 = input.slice(numStart, i);
156
+ push(1 /* Identifier */, value2, numStart, i, startLine, startColumn);
157
+ updatePosition(value2);
158
+ continue;
159
+ }
160
+ const value = input.slice(numStart, i);
161
+ push(2 /* Number */, value, numStart, i, startLine, startColumn);
162
+ updatePosition(value);
163
+ } else if (input[i] === '"') {
164
+ const stringStart = i;
165
+ i++;
166
+ while (i < input.length) {
167
+ if (input[i] === '"') {
168
+ if (i + 1 < input.length && input[i + 1] === '"') {
169
+ i += 2;
170
+ continue;
171
+ }
172
+ break;
173
+ }
174
+ i++;
175
+ }
176
+ const value = input.slice(stringStart + 1, i);
177
+ push(3 /* String */, value, stringStart, i + 1, startLine, startColumn);
178
+ updatePosition(input.slice(stringStart, i + 1));
179
+ i++;
180
+ } else if (isCfgPunctuation(input[i])) {
181
+ push(4 /* Punctuation */, input[i], start, i + 1, startLine, startColumn);
182
+ updatePosition(input[i]);
183
+ i++;
184
+ } else {
185
+ const unknownStart = i;
186
+ while (i < input.length && !/[a-zA-Z_]/.test(input[i]) && !/\d/.test(input[i]) && input[i] !== '"' && !isCfgPunctuation(input[i]) && !/\s/.test(input[i])) {
187
+ i++;
188
+ }
189
+ const value = input.slice(unknownStart, i);
190
+ push(6 /* Unknown */, value, unknownStart, i, startLine, startColumn);
191
+ updatePosition(value);
192
+ }
193
+ }
194
+ return tokens;
195
+ };
196
+
197
+ // src/parser.ts
198
+ var Parser = class {
199
+ constructor(input, filename) {
200
+ this.current = 0;
201
+ this.tokens = lex(input);
202
+ this.filename = filename ?? "<unknown>";
203
+ }
204
+ peek() {
205
+ return this.current < this.tokens.length ? this.tokens[this.current] : null;
206
+ }
207
+ advance() {
208
+ if (this.current < this.tokens.length) {
209
+ return this.tokens[this.current++];
210
+ }
211
+ return null;
212
+ }
213
+ consume(kind, value) {
214
+ const token = this.peek();
215
+ if (token?.kind !== kind || value && token.value !== value) {
216
+ const expected = value ? `'${value}'` : TokenKind[kind];
217
+ const got = token ? `${TokenKind[token.kind]} '${token.value}'` : "EOF";
218
+ const location = token ? `${this.filename}:${token.line}:${token.column}` : this.filename;
219
+ throw new Error(`${location}: Expected ${expected}, got ${got}`);
220
+ }
221
+ return this.advance();
222
+ }
223
+ isAtEnd() {
224
+ return this.current >= this.tokens.length;
225
+ }
226
+ parseType() {
227
+ const token = this.peek();
228
+ if (!token) {
229
+ throw new Error(`${this.filename}: Unexpected end of input`);
230
+ }
231
+ if (token.kind === 4 /* Punctuation */ && (token.value === "-" || token.value === "+")) {
232
+ this.advance();
233
+ const numToken = this.peek();
234
+ if (numToken?.kind === 2 /* Number */) {
235
+ this.advance();
236
+ const value = parseFloat(numToken.value);
237
+ return token.value === "-" ? -value : value;
238
+ }
239
+ throw new Error(`${this.filename}:${token.line}:${token.column}: Expected number after '${token.value}'`);
240
+ }
241
+ if (token.kind === 2 /* Number */) {
242
+ this.advance();
243
+ return parseFloat(token.value);
244
+ } else if (token.kind === 3 /* String */) {
245
+ this.advance();
246
+ return token.value;
247
+ } else if (token.kind === 0 /* Keyword */) {
248
+ if (token.value === "true") {
249
+ this.advance();
250
+ return true;
251
+ } else if (token.value === "false") {
252
+ this.advance();
253
+ return false;
254
+ } else if (token.value === "null" || token.value === "NULL") {
255
+ this.advance();
256
+ return null;
257
+ }
258
+ }
259
+ throw new Error(`${this.filename}:${token.line}:${token.column}: Unexpected token for type: ${TokenKind[token.kind]} '${token.value}'`);
260
+ }
261
+ parseExpression() {
262
+ const token = this.peek();
263
+ if (token?.kind === 4 /* Punctuation */ && token.value === "{") {
264
+ this.advance();
265
+ const elements = this.parseArrayElements();
266
+ this.consume(4 /* Punctuation */, "}");
267
+ return elements;
268
+ }
269
+ return this.parseType();
270
+ }
271
+ parseArrayElements() {
272
+ const elements = [];
273
+ while (!this.isAtEnd()) {
274
+ const token = this.peek();
275
+ if (token?.value === "}") {
276
+ break;
277
+ }
278
+ elements.push(this.parseExpression());
279
+ const comma = this.peek();
280
+ if (comma?.value === ",") {
281
+ this.advance();
282
+ } else {
283
+ break;
284
+ }
285
+ }
286
+ return elements;
287
+ }
288
+ parseVariable() {
289
+ const name = this.consume(1 /* Identifier */)?.value;
290
+ if (!name) {
291
+ throw new Error(`${this.filename}: Expected variable name`);
292
+ }
293
+ const next = this.peek();
294
+ if (next?.value === "[") {
295
+ this.consume(4 /* Punctuation */, "[");
296
+ this.consume(4 /* Punctuation */, "]");
297
+ const op = this.consume(4 /* Punctuation */);
298
+ if (!op) {
299
+ throw new Error(`${this.filename}: Unexpected end of input after array variable '${name}'`);
300
+ }
301
+ if (op.value !== "=" && op.value !== "+=" && op.value !== "-=") {
302
+ throw new Error(`${this.filename}:${op.line}:${op.column}: Invalid operator for array: '${op.value}'`);
303
+ }
304
+ this.consume(4 /* Punctuation */, "{");
305
+ const elements = this.parseArrayElements();
306
+ this.consume(4 /* Punctuation */, "}");
307
+ this.consume(4 /* Punctuation */, ";");
308
+ if (op.value === "=") {
309
+ return {
310
+ kind: "array",
311
+ name,
312
+ values: elements
313
+ };
314
+ } else if (op.value === "+=") {
315
+ return {
316
+ kind: "array-extend",
317
+ name,
318
+ values: elements
319
+ };
320
+ } else {
321
+ return {
322
+ kind: "array-shrink",
323
+ name,
324
+ values: elements
325
+ };
326
+ }
327
+ } else {
328
+ this.consume(4 /* Punctuation */, "=");
329
+ const value = this.parseExpression();
330
+ this.consume(4 /* Punctuation */, ";");
331
+ return {
332
+ kind: "variable",
333
+ name,
334
+ value
335
+ };
336
+ }
337
+ }
338
+ parseClass() {
339
+ this.consume(0 /* Keyword */, "class");
340
+ const name = this.consume(1 /* Identifier */)?.value;
341
+ if (!name) {
342
+ throw new Error(`${this.filename}: Expected class name`);
343
+ }
344
+ let baseClassName;
345
+ const colon = this.peek();
346
+ if (colon?.value === ":") {
347
+ this.advance();
348
+ baseClassName = this.consume(1 /* Identifier */)?.value;
349
+ }
350
+ const brace = this.peek();
351
+ if (brace?.value === "{") {
352
+ this.advance();
353
+ const properties = /* @__PURE__ */ new Map();
354
+ while (!this.isAtEnd()) {
355
+ const token = this.peek();
356
+ if (!token || token.value === "}") {
357
+ break;
358
+ }
359
+ if (token.kind === 0 /* Keyword */ && token.value === "class") {
360
+ const nestedClass = this.parseClass();
361
+ properties.set(nestedClass.name, nestedClass);
362
+ } else if (token.kind === 0 /* Keyword */ && token.value === "delete") {
363
+ const deleteStmt = this.parseDelete();
364
+ properties.set(deleteStmt.name, deleteStmt);
365
+ } else {
366
+ const prop = this.parseVariable();
367
+ properties.set(prop.name, prop);
368
+ }
369
+ }
370
+ this.consume(4 /* Punctuation */, "}");
371
+ this.consume(4 /* Punctuation */, ";");
372
+ return {
373
+ kind: "class",
374
+ name,
375
+ baseClassName,
376
+ properties
377
+ };
378
+ } else {
379
+ this.consume(4 /* Punctuation */, ";");
380
+ return {
381
+ kind: "prototype",
382
+ name,
383
+ baseClassName
384
+ };
385
+ }
386
+ }
387
+ parseDelete() {
388
+ this.consume(0 /* Keyword */, "delete");
389
+ const name = this.consume(1 /* Identifier */)?.value;
390
+ if (!name) {
391
+ throw new Error(`${this.filename}: Expected delete name`);
392
+ }
393
+ this.consume(4 /* Punctuation */, ";");
394
+ return {
395
+ kind: "delete",
396
+ name
397
+ };
398
+ }
399
+ parseEnum() {
400
+ this.consume(0 /* Keyword */, "enum");
401
+ this.consume(4 /* Punctuation */, "{");
402
+ const members = [];
403
+ let nextValue = 0;
404
+ while (!this.isAtEnd()) {
405
+ const token = this.peek();
406
+ if (token?.value === "}") {
407
+ break;
408
+ }
409
+ const memberName = this.consume(1 /* Identifier */)?.value;
410
+ if (!memberName) {
411
+ throw new Error(`${this.filename}: Expected enum member name`);
412
+ }
413
+ let value;
414
+ const eq = this.peek();
415
+ if (eq?.value === "=") {
416
+ this.advance();
417
+ const numToken = this.consume(2 /* Number */);
418
+ if (!numToken) {
419
+ throw new Error(`${this.filename}: Expected number for enum member value`);
420
+ }
421
+ value = parseFloat(numToken.value);
422
+ nextValue = value + 1;
423
+ } else {
424
+ value = nextValue++;
425
+ }
426
+ members.push({ name: memberName, value });
427
+ const comma = this.peek();
428
+ if (comma?.value === ",") {
429
+ this.advance();
430
+ } else {
431
+ break;
432
+ }
433
+ }
434
+ this.consume(4 /* Punctuation */, "}");
435
+ this.consume(4 /* Punctuation */, ";");
436
+ return {
437
+ kind: "enum",
438
+ members
439
+ };
440
+ }
441
+ parse() {
442
+ const statements = [];
443
+ while (!this.isAtEnd()) {
444
+ const token = this.peek();
445
+ if (!token) {
446
+ break;
447
+ }
448
+ if (token.kind === 1 /* Identifier */) {
449
+ statements.push(this.parseVariable());
450
+ } else if (token.kind === 0 /* Keyword */ && token.value === "class") {
451
+ statements.push(this.parseClass());
452
+ } else if (token.kind === 0 /* Keyword */ && token.value === "delete") {
453
+ statements.push(this.parseDelete());
454
+ } else if (token.kind === 0 /* Keyword */ && token.value === "enum") {
455
+ statements.push(this.parseEnum());
456
+ } else {
457
+ const token2 = this.peek();
458
+ if (token2) {
459
+ throw new Error(`${this.filename}:${token2.line}:${token2.column}: Unexpected token: ${TokenKind[token2.kind]} '${token2.value}'`);
460
+ } else {
461
+ throw new Error(`${this.filename}: Unexpected end of input`);
462
+ }
463
+ }
464
+ }
465
+ return {
466
+ kind: "document",
467
+ statements
468
+ };
469
+ }
470
+ };
471
+
472
+ // src/preprocessor.ts
473
+ var readFileSync;
474
+ var resolve;
475
+ if (typeof process !== "undefined" && process.versions?.node) {
476
+ try {
477
+ const fs = __require("fs");
478
+ const path = __require("path");
479
+ readFileSync = fs.readFileSync;
480
+ resolve = path.resolve;
481
+ } catch {
482
+ }
483
+ }
484
+ var Preprocessor = class {
485
+ constructor(options = {}) {
486
+ this.defines = new Map(options.defines ?? []);
487
+ this.conditionStack = [];
488
+ this.skipLines = false;
489
+ this.processedFiles = /* @__PURE__ */ new Set();
490
+ this.includePaths = options.includePaths ?? [];
491
+ }
492
+ preprocess(filePath) {
493
+ if (!readFileSync) {
494
+ throw new Error("Preprocessor.preprocess() requires Node.js file system access");
495
+ }
496
+ const input = readFileSync(filePath, "utf8");
497
+ const lines = input.split("\n");
498
+ const output = [];
499
+ for (const rawLine of lines) {
500
+ const line = rawLine.trim();
501
+ if (line.startsWith("#")) {
502
+ const directive = this.parseDirective(line);
503
+ if (!directive) {
504
+ continue;
505
+ }
506
+ this.processDirective(directive, output, filePath);
507
+ } else {
508
+ if (!this.skipLines) {
509
+ let processedLine = line;
510
+ for (const [key, value] of this.defines) {
511
+ processedLine = processedLine.replace(new RegExp(`\\b${key}\\b`, "g"), value);
512
+ }
513
+ output.push(processedLine);
514
+ }
515
+ }
516
+ }
517
+ return output.join("\n");
518
+ }
519
+ processDirective(directive, output, currentFile) {
520
+ switch (directive.type) {
521
+ case "define":
522
+ if (!this.skipLines) {
523
+ this.defines.set(directive.name, directive.value ?? "");
524
+ }
525
+ break;
526
+ case "undef":
527
+ if (!this.skipLines) {
528
+ this.defines.delete(directive.name);
529
+ }
530
+ break;
531
+ case "ifdef":
532
+ this.conditionStack.push(this.skipLines);
533
+ this.skipLines = this.skipLines || !this.defines.has(directive.name);
534
+ break;
535
+ case "ifndef":
536
+ this.conditionStack.push(this.skipLines);
537
+ this.skipLines = this.skipLines || this.defines.has(directive.name);
538
+ break;
539
+ case "else":
540
+ if (this.conditionStack.length > 0) {
541
+ this.skipLines = !this.skipLines;
542
+ }
543
+ break;
544
+ case "endif":
545
+ if (this.conditionStack.length > 0) {
546
+ this.skipLines = this.conditionStack.pop();
547
+ }
548
+ break;
549
+ case "include":
550
+ if (!this.skipLines && directive.file) {
551
+ if (!resolve) {
552
+ console.warn("#include directive ignored in browser environment");
553
+ break;
554
+ }
555
+ const fullPath = currentFile ? resolve(currentFile, "..", directive.file) : directive.file;
556
+ if (this.processedFiles.has(fullPath)) {
557
+ console.warn(`Recursive include detected for ${directive.file}, skipping`);
558
+ } else {
559
+ try {
560
+ this.processedFiles.add(fullPath);
561
+ const includedContent = this.loadAndPreprocessFile(directive.file, currentFile);
562
+ output.push(includedContent);
563
+ } catch (error) {
564
+ console.error(`Failed to include ${directive.file}: `, error);
565
+ }
566
+ }
567
+ }
568
+ break;
569
+ default:
570
+ break;
571
+ }
572
+ }
573
+ parseDirective(line) {
574
+ const parts = line.split(/\s+/);
575
+ if (parts.length === 0) {
576
+ return null;
577
+ }
578
+ const type = parts[0].substring(1);
579
+ switch (type) {
580
+ case "define":
581
+ if (parts.length >= 2) {
582
+ const name = parts[1];
583
+ const value = parts.slice(2).join(" ");
584
+ return { type: "define", name, value };
585
+ }
586
+ break;
587
+ case "undef":
588
+ if (parts.length >= 2) {
589
+ return { type: "undef", name: parts[1] };
590
+ }
591
+ break;
592
+ case "ifdef":
593
+ case "ifndef":
594
+ if (parts.length >= 2) {
595
+ return { type, name: parts[1] };
596
+ }
597
+ break;
598
+ case "else":
599
+ return { type: "else" };
600
+ case "endif":
601
+ return { type: "endif" };
602
+ case "include":
603
+ const includeMatch = /^#include\s+["<]([^">]+)[">]$/.exec(line);
604
+ if (includeMatch) {
605
+ const file = includeMatch[1];
606
+ const isQuoted = line.includes('"');
607
+ if (isQuoted) {
608
+ return { type: "include", file };
609
+ }
610
+ }
611
+ break;
612
+ }
613
+ return null;
614
+ }
615
+ loadAndPreprocessFile(includePath, currentFile) {
616
+ if (!resolve || !readFileSync) {
617
+ throw new Error("#include requires Node.js file system access");
618
+ }
619
+ let fullPath;
620
+ if (currentFile) {
621
+ fullPath = resolve(currentFile, "..", includePath);
622
+ } else {
623
+ fullPath = includePath;
624
+ for (const includePath2 of this.includePaths) {
625
+ const candidate = resolve(includePath2, includePath2);
626
+ try {
627
+ readFileSync(candidate, "utf8");
628
+ fullPath = candidate;
629
+ break;
630
+ } catch {
631
+ }
632
+ }
633
+ }
634
+ return this.preprocess(fullPath);
635
+ }
636
+ };
637
+
638
+ // src/RvmatParser.ts
639
+ var RvmatParser = class {
640
+ /**
641
+ * Parse RVMAT file content
642
+ */
643
+ static parse(content, filename = "<rvmat>") {
644
+ const parser = new Parser(content, filename);
645
+ const document = parser.parse();
646
+ const data = { stages: [] };
647
+ data.ambient = this.readNumberArray(this.findVariable(document, "ambient"));
648
+ data.diffuse = this.readNumberArray(this.findVariable(document, "diffuse"));
649
+ data.forcedDiffuse = this.readNumberArray(this.findVariable(document, "forcedDiffuse"));
650
+ data.emmisive = this.readNumberArray(this.findVariable(document, "emmisive"));
651
+ data.specular = this.readNumberArray(this.findVariable(document, "specular"));
652
+ data.specularPower = this.readNumber(this.findVariable(document, "specularPower"));
653
+ data.pixelShaderID = this.readString(this.findVariable(document, "PixelShaderID"));
654
+ data.vertexShaderID = this.readString(this.findVariable(document, "VertexShaderID"));
655
+ this.extractStages(document, data);
656
+ return data;
657
+ }
658
+ static findVariable(document, name) {
659
+ const target = name.toLowerCase();
660
+ for (let i = document.statements.length - 1; i >= 0; i--) {
661
+ const statement = document.statements[i];
662
+ if (this.isVariable(statement) && statement.name.toLowerCase() === target) {
663
+ return statement;
664
+ }
665
+ }
666
+ return void 0;
667
+ }
668
+ static extractStages(document, data) {
669
+ const collectFromClass = (cls) => {
670
+ if (/^stage\d+$/i.test(cls.name)) {
671
+ const textureProp = this.findClassProperty(cls, "texture");
672
+ if (textureProp && this.isSimpleVariable(textureProp)) {
673
+ const texture = this.readString(textureProp);
674
+ if (texture && texture.trim() !== "") {
675
+ data.stages.push({ name: cls.name, texture });
676
+ }
677
+ }
678
+ }
679
+ for (const property of cls.properties.values()) {
680
+ if (this.isClass(property)) {
681
+ collectFromClass(property);
682
+ }
683
+ }
684
+ };
685
+ for (const statement of document.statements) {
686
+ if (this.isClass(statement)) {
687
+ collectFromClass(statement);
688
+ }
689
+ }
690
+ }
691
+ static findClassProperty(cls, propertyName) {
692
+ const target = propertyName.toLowerCase();
693
+ let match;
694
+ for (const [name, property] of cls.properties) {
695
+ if (name.toLowerCase() === target) {
696
+ match = property;
697
+ }
698
+ }
699
+ return match;
700
+ }
701
+ static readNumberArray(node) {
702
+ if (!node) {
703
+ return void 0;
704
+ }
705
+ const raw = node.kind === "array" ? node.values : node.value;
706
+ if (!Array.isArray(raw)) {
707
+ return void 0;
708
+ }
709
+ const numbers = raw.map((value) => this.toNumber(value)).filter((value) => value !== void 0);
710
+ return numbers.length > 0 ? numbers : void 0;
711
+ }
712
+ static readNumber(node) {
713
+ if (!node) {
714
+ return void 0;
715
+ }
716
+ if (node.kind === "variable") {
717
+ return this.toNumber(node.value);
718
+ }
719
+ return node.values.length === 1 ? this.toNumber(node.values[0]) : void 0;
720
+ }
721
+ static readString(node) {
722
+ if (!node || !this.isSimpleVariable(node)) {
723
+ return void 0;
724
+ }
725
+ const value = node.value;
726
+ return typeof value === "string" ? value : void 0;
727
+ }
728
+ static toNumber(value) {
729
+ if (typeof value === "number") {
730
+ return value;
731
+ }
732
+ if (typeof value === "string") {
733
+ const parsed = parseFloat(value);
734
+ return Number.isFinite(parsed) ? parsed : void 0;
735
+ }
736
+ return void 0;
737
+ }
738
+ static isVariable(node) {
739
+ return node.kind === "variable" || node.kind === "array";
740
+ }
741
+ static isSimpleVariable(node) {
742
+ return node.kind === "variable";
743
+ }
744
+ static isClass(node) {
745
+ return node.kind === "class";
746
+ }
747
+ /**
748
+ * Get all texture paths from parsed data
749
+ */
750
+ static getTexturePaths(data) {
751
+ return data.stages.map((stage) => stage.texture);
752
+ }
753
+ /**
754
+ * Get main diffuse/color texture (typically Stage1)
755
+ */
756
+ static getMainTexture(data) {
757
+ return data.stages.find((s) => s.name === "Stage1")?.texture;
758
+ }
759
+ };
760
+ export {
761
+ CfgKeywords,
762
+ CfgOperators,
763
+ CfgPunctuations,
764
+ Parser,
765
+ Preprocessor,
766
+ RvmatParser,
767
+ TokenKind,
768
+ isCfgKeyword,
769
+ isCfgPunctuation,
770
+ lex
771
+ };
772
+ //# sourceMappingURL=index.js.map