c-next 0.1.62 → 0.1.63

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.
Files changed (55) hide show
  1. package/README.md +86 -63
  2. package/package.json +1 -1
  3. package/src/transpiler/Transpiler.ts +3 -2
  4. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  5. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +1 -1
  6. package/src/transpiler/__tests__/Transpiler.test.ts +0 -23
  7. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  8. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  9. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  10. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  11. package/src/transpiler/output/codegen/CodeGenerator.ts +817 -1377
  12. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  13. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  14. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  15. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +326 -60
  16. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  17. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  18. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  19. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  20. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +39 -43
  21. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +52 -55
  22. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +122 -62
  23. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  24. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +143 -126
  25. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +287 -320
  26. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  27. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  28. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  29. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +121 -51
  30. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  31. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  32. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  33. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  34. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  35. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +21 -30
  36. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +56 -53
  37. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  38. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  39. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  40. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  41. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  42. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  43. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  44. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  45. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  46. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  47. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  49. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  50. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  51. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  52. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  53. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  54. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  55. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
@@ -0,0 +1,811 @@
1
+ /**
2
+ * CodeGenState - Global state for code generation
3
+ *
4
+ * Centralizes all code generation state in a single static class.
5
+ * Eliminates dependency injection complexity and makes debugging easier.
6
+ *
7
+ * Usage:
8
+ * import CodeGenState from "./CodeGenState";
9
+ * const type = CodeGenState.typeRegistry.get(name);
10
+ * if (CodeGenState.isKnownStruct(name)) { ... }
11
+ *
12
+ * Lifecycle:
13
+ * 1. CodeGenerator.generate() calls CodeGenState.reset()
14
+ * 2. CodeGenerator populates state during initialization
15
+ * 3. All generators/helpers read from CodeGenState directly
16
+ * 4. State persists for the duration of one generate() call
17
+ */
18
+
19
+ import SymbolTable from "../../logic/symbols/SymbolTable";
20
+ import ICodeGenSymbols from "../../types/ICodeGenSymbols";
21
+ import TTypeInfo from "./types/TTypeInfo";
22
+ import TParameterInfo from "./types/TParameterInfo";
23
+ import IFunctionSignature from "./types/IFunctionSignature";
24
+ import ICallbackTypeInfo from "./types/ICallbackTypeInfo";
25
+ import ITargetCapabilities from "./types/ITargetCapabilities";
26
+ import TOverflowBehavior from "./types/TOverflowBehavior";
27
+ import TYPE_WIDTH from "./types/TYPE_WIDTH";
28
+
29
+ /**
30
+ * Default target capabilities (safe fallback)
31
+ */
32
+ const DEFAULT_TARGET: ITargetCapabilities = {
33
+ wordSize: 32,
34
+ hasLdrexStrex: false,
35
+ hasBasepri: false,
36
+ };
37
+
38
+ /**
39
+ * Assignment context for overflow behavior tracking (ADR-044)
40
+ */
41
+ interface IAssignmentContext {
42
+ targetName: string | null;
43
+ targetType: string | null;
44
+ overflowBehavior: TOverflowBehavior;
45
+ }
46
+
47
+ /**
48
+ * Function call graph entry for transitive modification analysis
49
+ */
50
+ interface ICallGraphEntry {
51
+ callee: string;
52
+ paramIndex: number;
53
+ argParamName: string;
54
+ }
55
+
56
+ /**
57
+ * Global state for code generation.
58
+ * All fields are static - import and use directly from any module.
59
+ *
60
+ * NOTE: All static properties are intentionally mutable (not readonly).
61
+ * This class holds session state that is reset via reset() and modified
62
+ * during code generation.
63
+ */
64
+ export default class CodeGenState {
65
+ // ===========================================================================
66
+ // SYMBOL DATA (read-only after initialization)
67
+ // ===========================================================================
68
+
69
+ /** ADR-055: Pre-collected symbol info from CNextResolver + TSymbolInfoAdapter */
70
+ static symbols: ICodeGenSymbols | null = null;
71
+
72
+ /** External symbol table for cross-language interop (C headers) */
73
+ static symbolTable: SymbolTable | null = null;
74
+
75
+ // ===========================================================================
76
+ // TYPE TRACKING
77
+ // ===========================================================================
78
+
79
+ /** Track variable types for bit access, .length, and type inference */
80
+ static typeRegistry: Map<string, TTypeInfo> = new Map();
81
+
82
+ /** Bug #8: Compile-time const values for array size resolution */
83
+ static constValues: Map<string, number> = new Map();
84
+
85
+ // ===========================================================================
86
+ // FUNCTION & CALLBACK TRACKING
87
+ // ===========================================================================
88
+
89
+ /** Track C-Next defined functions */
90
+ static knownFunctions: Set<string> = new Set();
91
+
92
+ /** ADR-013: Track function parameter const-ness for call-site validation */
93
+ static functionSignatures: Map<string, IFunctionSignature> = new Map();
94
+
95
+ /** ADR-029: Callback types registry (function-as-type pattern) */
96
+ static callbackTypes: Map<string, ICallbackTypeInfo> = new Map();
97
+
98
+ /** Callback field types: "Struct.field" -> callbackTypeName */
99
+ static callbackFieldTypes: Map<string, string> = new Map();
100
+
101
+ // ===========================================================================
102
+ // PASS-BY-VALUE ANALYSIS (Issue #269)
103
+ // ===========================================================================
104
+
105
+ /** Tracks which parameters are modified (directly or transitively) */
106
+ static modifiedParameters: Map<string, Set<string>> = new Map();
107
+
108
+ /** Issue #579: Parameters with subscript access (must become pointers) */
109
+ static subscriptAccessedParameters: Map<string, Set<string>> = new Map();
110
+
111
+ /** Parameters that should pass by value (small, unmodified primitives) */
112
+ static passByValueParams: Map<string, Set<string>> = new Map();
113
+
114
+ /** Function call relationships for transitive modification analysis */
115
+ static functionCallGraph: Map<string, ICallGraphEntry[]> = new Map();
116
+
117
+ /** Function parameter lists for call graph analysis */
118
+ static functionParamLists: Map<string, string[]> = new Map();
119
+
120
+ /** Issue #558: Cross-file modifications to inject */
121
+ static pendingCrossFileModifications: ReadonlyMap<
122
+ string,
123
+ ReadonlySet<string>
124
+ > | null = null;
125
+
126
+ /** Issue #558: Cross-file parameter lists to inject */
127
+ static pendingCrossFileParamLists: ReadonlyMap<
128
+ string,
129
+ readonly string[]
130
+ > | null = null;
131
+
132
+ // ===========================================================================
133
+ // OVERFLOW & DIVISION HELPERS (ADR-044, ADR-051)
134
+ // ===========================================================================
135
+
136
+ /** Track which overflow helper types/operations are needed: "add_u8", etc. */
137
+ static usedClampOps: Set<string> = new Set();
138
+
139
+ /** Track which safe division helpers are needed: "div_u32", "mod_i16" */
140
+ static usedSafeDivOps: Set<string> = new Set();
141
+
142
+ // ===========================================================================
143
+ // CURRENT CONTEXT (changes during AST traversal)
144
+ // ===========================================================================
145
+
146
+ /** ADR-016: Current scope for name resolution */
147
+ static currentScope: string | null = null;
148
+
149
+ /** Issue #269: Current function for modification tracking */
150
+ static currentFunctionName: string | null = null;
151
+
152
+ /** Issue #477: Current function return type for enum inference */
153
+ static currentFunctionReturnType: string | null = null;
154
+
155
+ /** ADR-006: Current function parameters for pointer semantics */
156
+ static currentParameters: Map<string, TParameterInfo> = new Map();
157
+
158
+ /** ADR-016: Local variables in current function (allowed as bare identifiers) */
159
+ static localVariables: Set<string> = new Set();
160
+
161
+ /** ADR-006: Local array variables (no & needed when passing) */
162
+ static localArrays: Set<string> = new Set();
163
+
164
+ /** Scope member names: scope -> Set of member names */
165
+ static scopeMembers: Map<string, Set<string>> = new Map();
166
+
167
+ /** Float bit indexing: declared shadow variables */
168
+ static floatBitShadows: Set<string> = new Set();
169
+
170
+ /** Float bit indexing: shadows with current value (skip redundant reads) */
171
+ static floatShadowCurrent: Set<string> = new Set();
172
+
173
+ // ===========================================================================
174
+ // GENERATION STATE
175
+ // ===========================================================================
176
+
177
+ /** Current indentation level */
178
+ static indentLevel: number = 0;
179
+
180
+ /** Whether we're inside a function body */
181
+ static inFunctionBody: boolean = false;
182
+
183
+ /** Expected type for struct initializers and enum inference */
184
+ static expectedType: string | null = null;
185
+
186
+ /** Track args parameter name for main() translation */
187
+ static mainArgsName: string | null = null;
188
+
189
+ /** ADR-044: Current assignment context for overflow behavior */
190
+ static assignmentContext: IAssignmentContext = {
191
+ targetName: null,
192
+ targetType: null,
193
+ overflowBehavior: "clamp",
194
+ };
195
+
196
+ /** ADR-035: Element count for array size inference */
197
+ static lastArrayInitCount: number = 0;
198
+
199
+ /** ADR-035: Fill-all value for array initialization */
200
+ static lastArrayFillValue: string | undefined = undefined;
201
+
202
+ /** strlen optimization: variable name -> temp variable name */
203
+ static lengthCache: Map<string, string> | null = null;
204
+
205
+ /** ADR-049: Target platform capabilities */
206
+ static targetCapabilities: ITargetCapabilities = DEFAULT_TARGET;
207
+
208
+ // ===========================================================================
209
+ // INCLUDE FLAGS (track required standard library includes)
210
+ // ===========================================================================
211
+
212
+ /** For u8, u16, u32, u64, i8, i16, i32, i64 */
213
+ static needsStdint: boolean = false;
214
+
215
+ /** For bool type */
216
+ static needsStdbool: boolean = false;
217
+
218
+ /** ADR-045: For strlen, strncpy, etc. */
219
+ static needsString: boolean = false;
220
+
221
+ /** For float bit indexing size verification */
222
+ static needsFloatStaticAssert: boolean = false;
223
+
224
+ /** ADR-040: For ISR function pointer type */
225
+ static needsISR: boolean = false;
226
+
227
+ /** ADR-049/050: For atomic intrinsics and critical sections */
228
+ static needsCMSIS: boolean = false;
229
+
230
+ /** Issue #632: For float-to-int clamp casts */
231
+ static needsLimits: boolean = false;
232
+
233
+ /** Issue #473: IRQ wrappers for critical sections */
234
+ static needsIrqWrappers: boolean = false;
235
+
236
+ // ===========================================================================
237
+ // C++ MODE STATE (Issue #250)
238
+ // ===========================================================================
239
+
240
+ /** Use temp vars instead of compound literals */
241
+ static cppMode: boolean = false;
242
+
243
+ /** Debug mode generates panic-on-overflow helpers (ADR-044) */
244
+ static debugMode: boolean = false;
245
+
246
+ /** Pending temp variable declarations for C++ mode */
247
+ static pendingTempDeclarations: string[] = [];
248
+
249
+ /** Counter for unique temp variable names */
250
+ static tempVarCounter: number = 0;
251
+
252
+ /** Issue #517: Pending field assignments for C++ class struct init */
253
+ static pendingCppClassAssignments: string[] = [];
254
+
255
+ /** Issue #369: Whether self-include was added */
256
+ static selfIncludeAdded: boolean = false;
257
+
258
+ // ===========================================================================
259
+ // SOURCE PATHS (ADR-010, Issue #349)
260
+ // ===========================================================================
261
+
262
+ /** Source file path for validating includes */
263
+ static sourcePath: string | null = null;
264
+
265
+ /** Include directories for resolving angle-bracket .cnx includes */
266
+ static includeDirs: string[] = [];
267
+
268
+ /** Input directories for calculating relative paths */
269
+ static inputs: string[] = [];
270
+
271
+ // ===========================================================================
272
+ // LIFECYCLE METHODS
273
+ // ===========================================================================
274
+
275
+ /**
276
+ * Reset all state for a fresh generation pass.
277
+ * Called at the start of CodeGenerator.generate()
278
+ */
279
+ static reset(targetCapabilities?: ITargetCapabilities): void {
280
+ // Symbol data
281
+ this.symbols = null;
282
+ this.symbolTable = null;
283
+
284
+ // Type tracking
285
+ this.typeRegistry = new Map();
286
+ this.constValues = new Map();
287
+
288
+ // Function & callback tracking
289
+ this.knownFunctions = new Set();
290
+ this.functionSignatures = new Map();
291
+ this.callbackTypes = new Map();
292
+ this.callbackFieldTypes = new Map();
293
+
294
+ // Pass-by-value analysis
295
+ this.modifiedParameters = new Map();
296
+ this.subscriptAccessedParameters = new Map();
297
+ this.passByValueParams = new Map();
298
+ this.functionCallGraph = new Map();
299
+ this.functionParamLists = new Map();
300
+ // Note: pendingCrossFileModifications/ParamLists are set externally, not reset
301
+
302
+ // Overflow & division helpers
303
+ this.usedClampOps = new Set();
304
+ this.usedSafeDivOps = new Set();
305
+
306
+ // Current context
307
+ this.currentScope = null;
308
+ this.currentFunctionName = null;
309
+ this.currentFunctionReturnType = null;
310
+ this.currentParameters = new Map();
311
+ this.localVariables = new Set();
312
+ this.localArrays = new Set();
313
+ this.scopeMembers = new Map();
314
+ this.floatBitShadows = new Set();
315
+ this.floatShadowCurrent = new Set();
316
+
317
+ // Generation state
318
+ this.indentLevel = 0;
319
+ this.inFunctionBody = false;
320
+ this.expectedType = null;
321
+ this.mainArgsName = null;
322
+ this.assignmentContext = {
323
+ targetName: null,
324
+ targetType: null,
325
+ overflowBehavior: "clamp",
326
+ };
327
+ this.lastArrayInitCount = 0;
328
+ this.lastArrayFillValue = undefined;
329
+ this.lengthCache = null;
330
+ this.targetCapabilities = targetCapabilities ?? DEFAULT_TARGET;
331
+
332
+ // Include flags
333
+ this.needsStdint = false;
334
+ this.needsStdbool = false;
335
+ this.needsString = false;
336
+ this.needsFloatStaticAssert = false;
337
+ this.needsISR = false;
338
+ this.needsCMSIS = false;
339
+ this.needsLimits = false;
340
+ this.needsIrqWrappers = false;
341
+
342
+ // C++ mode state
343
+ this.cppMode = false;
344
+ this.debugMode = false;
345
+ this.pendingTempDeclarations = [];
346
+ this.tempVarCounter = 0;
347
+ this.pendingCppClassAssignments = [];
348
+ this.selfIncludeAdded = false;
349
+
350
+ // Source paths
351
+ this.sourcePath = null;
352
+ this.includeDirs = [];
353
+ this.inputs = [];
354
+ }
355
+
356
+ /**
357
+ * Enter a function body context.
358
+ * Clears local tracking and sets inFunctionBody flag.
359
+ */
360
+ static enterFunctionBody(): void {
361
+ this.inFunctionBody = true;
362
+ this.localVariables.clear();
363
+ this.localArrays.clear();
364
+ this.floatBitShadows.clear();
365
+ this.floatShadowCurrent.clear();
366
+ }
367
+
368
+ /**
369
+ * Exit a function body context.
370
+ * Clears local tracking and sets inFunctionBody to false.
371
+ */
372
+ static exitFunctionBody(): void {
373
+ this.inFunctionBody = false;
374
+ this.localVariables.clear();
375
+ this.localArrays.clear();
376
+ this.floatBitShadows.clear();
377
+ this.floatShadowCurrent.clear();
378
+ }
379
+
380
+ // ===========================================================================
381
+ // CONVENIENCE LOOKUP METHODS
382
+ // ===========================================================================
383
+
384
+ /**
385
+ * Check if a type name is a known struct.
386
+ * Also includes bitmaps since they're struct-like (Issue #551).
387
+ */
388
+ static isKnownStruct(name: string): boolean {
389
+ if (this.symbols?.knownStructs.has(name)) return true;
390
+ if (this.symbols?.knownBitmaps.has(name)) return true;
391
+ if (this.symbolTable?.getStructFields?.(name)) return true;
392
+ return false;
393
+ }
394
+
395
+ /**
396
+ * Check if a type name is a known scope.
397
+ */
398
+ static isKnownScope(name: string): boolean {
399
+ return this.symbols?.knownScopes.has(name) ?? false;
400
+ }
401
+
402
+ /**
403
+ * Check if a type name is a known enum.
404
+ */
405
+ static isKnownEnum(name: string): boolean {
406
+ return this.symbols?.knownEnums.has(name) ?? false;
407
+ }
408
+
409
+ /**
410
+ * Check if a type name is a known bitmap.
411
+ */
412
+ static isKnownBitmap(name: string): boolean {
413
+ return this.symbols?.knownBitmaps.has(name) ?? false;
414
+ }
415
+
416
+ /**
417
+ * Check if a type name is a known register.
418
+ */
419
+ static isKnownRegister(name: string): boolean {
420
+ return this.symbols?.knownRegisters.has(name) ?? false;
421
+ }
422
+
423
+ /**
424
+ * Get type info for a variable.
425
+ */
426
+ static getTypeInfo(name: string): TTypeInfo | undefined {
427
+ return this.typeRegistry.get(name);
428
+ }
429
+
430
+ /**
431
+ * Check if a parameter in a function is modified.
432
+ */
433
+ static isParameterModified(funcName: string, paramName: string): boolean {
434
+ return this.modifiedParameters.get(funcName)?.has(paramName) ?? false;
435
+ }
436
+
437
+ /**
438
+ * Check if a parameter should pass by value.
439
+ */
440
+ static isPassByValue(funcName: string, paramName: string): boolean {
441
+ return this.passByValueParams.get(funcName)?.has(paramName) ?? false;
442
+ }
443
+
444
+ /**
445
+ * Check if a parameter has subscript access.
446
+ */
447
+ static hasSubscriptAccess(funcName: string, paramName: string): boolean {
448
+ return (
449
+ this.subscriptAccessedParameters.get(funcName)?.has(paramName) ?? false
450
+ );
451
+ }
452
+
453
+ /**
454
+ * Get the function signature for a function.
455
+ */
456
+ static getFunctionSignature(name: string): IFunctionSignature | undefined {
457
+ return this.functionSignatures.get(name);
458
+ }
459
+
460
+ /**
461
+ * Get callback type info for a function name.
462
+ */
463
+ static getCallbackType(name: string): ICallbackTypeInfo | undefined {
464
+ return this.callbackTypes.get(name);
465
+ }
466
+
467
+ /**
468
+ * Check if a type name is a known C-Next function.
469
+ */
470
+ static isCNextFunction(name: string): boolean {
471
+ return this.knownFunctions.has(name);
472
+ }
473
+
474
+ /**
475
+ * Check if a name is a compile-time const value.
476
+ */
477
+ static isConstValue(name: string): boolean {
478
+ return this.constValues.has(name);
479
+ }
480
+
481
+ /**
482
+ * Get the compile-time value of a const.
483
+ */
484
+ static getConstValue(name: string): number | undefined {
485
+ return this.constValues.get(name);
486
+ }
487
+
488
+ /**
489
+ * Get parameter info from current function context.
490
+ */
491
+ static getParameterInfo(name: string): TParameterInfo | undefined {
492
+ return this.currentParameters.get(name);
493
+ }
494
+
495
+ /**
496
+ * Check if a name is a local variable.
497
+ */
498
+ static isLocalVariable(name: string): boolean {
499
+ return this.localVariables.has(name);
500
+ }
501
+
502
+ /**
503
+ * Check if a name is a local array.
504
+ */
505
+ static isLocalArray(name: string): boolean {
506
+ return this.localArrays.has(name);
507
+ }
508
+
509
+ /**
510
+ * Get members of a scope.
511
+ */
512
+ static getScopeMembers(scopeName: string): Set<string> | undefined {
513
+ return this.scopeMembers.get(scopeName);
514
+ }
515
+
516
+ /**
517
+ * Check if an identifier is a member of the current scope.
518
+ */
519
+ static isCurrentScopeMember(identifier: string): boolean {
520
+ if (!this.currentScope) return false;
521
+ return this.scopeMembers.get(this.currentScope)?.has(identifier) ?? false;
522
+ }
523
+
524
+ /**
525
+ * Resolve an identifier to its fully-scoped name.
526
+ * Inside a scope, checks if the identifier is a scope member first.
527
+ */
528
+ static resolveIdentifier(identifier: string): string {
529
+ if (this.currentScope) {
530
+ const members = this.scopeMembers.get(this.currentScope);
531
+ if (members?.has(identifier)) {
532
+ return `${this.currentScope}_${identifier}`;
533
+ }
534
+ }
535
+ return identifier;
536
+ }
537
+
538
+ /**
539
+ * Get struct field type (simple lookup).
540
+ */
541
+ static getStructFieldType(
542
+ structName: string,
543
+ fieldName: string,
544
+ ): string | undefined {
545
+ return this.symbols?.structFields.get(structName)?.get(fieldName);
546
+ }
547
+
548
+ /**
549
+ * Get struct field info including dimensions (checks SymbolTable then local symbols).
550
+ */
551
+ static getStructFieldInfo(
552
+ structType: string,
553
+ fieldName: string,
554
+ ): { type: string; dimensions?: (number | string)[] } | null {
555
+ // First check SymbolTable (C header structs)
556
+ if (this.symbolTable) {
557
+ const fieldInfo = this.symbolTable.getStructFieldInfo(
558
+ structType,
559
+ fieldName,
560
+ );
561
+ if (fieldInfo) {
562
+ return {
563
+ type: fieldInfo.type,
564
+ dimensions: fieldInfo.arrayDimensions,
565
+ };
566
+ }
567
+ }
568
+
569
+ // Fall back to local C-Next struct fields
570
+ const localFields = this.symbols?.structFields.get(structType);
571
+ if (localFields) {
572
+ const fieldType = localFields.get(fieldName);
573
+ if (fieldType) {
574
+ const fieldDimensions =
575
+ this.symbols?.structFieldDimensions.get(structType);
576
+ const dimensions = fieldDimensions?.get(fieldName);
577
+ return {
578
+ type: fieldType,
579
+ dimensions: dimensions ? [...dimensions] : undefined,
580
+ };
581
+ }
582
+ }
583
+
584
+ return null;
585
+ }
586
+
587
+ /**
588
+ * Get member type info for a struct field.
589
+ * Returns full TTypeInfo for the field, or null if not found.
590
+ */
591
+ static getMemberTypeInfo(
592
+ structType: string,
593
+ memberName: string,
594
+ ): TTypeInfo | null {
595
+ const fieldInfo = this.getStructFieldInfo(structType, memberName);
596
+ if (!fieldInfo) return null;
597
+
598
+ const isArray =
599
+ (fieldInfo.dimensions !== undefined && fieldInfo.dimensions.length > 0) ||
600
+ (this.symbols?.structFieldArrays.get(structType)?.has(memberName) ??
601
+ false);
602
+ const dims = fieldInfo.dimensions?.filter(
603
+ (d): d is number => typeof d === "number",
604
+ );
605
+
606
+ return {
607
+ baseType: fieldInfo.type,
608
+ bitWidth: TYPE_WIDTH[fieldInfo.type] ?? 32,
609
+ isConst: false,
610
+ isArray,
611
+ arrayDimensions: dims && dims.length > 0 ? dims : undefined,
612
+ };
613
+ }
614
+
615
+ /**
616
+ * Check if a struct field is an array.
617
+ */
618
+ static isStructFieldArray(structName: string, fieldName: string): boolean {
619
+ return (
620
+ this.symbols?.structFieldArrays.get(structName)?.has(fieldName) ?? false
621
+ );
622
+ }
623
+
624
+ /**
625
+ * Get enum members for an enum.
626
+ */
627
+ static getEnumMembers(
628
+ enumName: string,
629
+ ): ReadonlyMap<string, number> | undefined {
630
+ return this.symbols?.enumMembers.get(enumName);
631
+ }
632
+
633
+ /**
634
+ * Get function return type.
635
+ */
636
+ static getFunctionReturnType(funcName: string): string | undefined {
637
+ return this.symbols?.functionReturnTypes.get(funcName);
638
+ }
639
+
640
+ // ===========================================================================
641
+ // INCLUDE FLAG HELPERS
642
+ // ===========================================================================
643
+
644
+ /**
645
+ * Mark that stdint.h is needed.
646
+ */
647
+ static requireStdint(): void {
648
+ this.needsStdint = true;
649
+ }
650
+
651
+ /**
652
+ * Mark that stdbool.h is needed.
653
+ */
654
+ static requireStdbool(): void {
655
+ this.needsStdbool = true;
656
+ }
657
+
658
+ /**
659
+ * Mark that string.h is needed.
660
+ */
661
+ static requireString(): void {
662
+ this.needsString = true;
663
+ }
664
+
665
+ /**
666
+ * Mark that CMSIS headers are needed.
667
+ */
668
+ static requireCMSIS(): void {
669
+ this.needsCMSIS = true;
670
+ }
671
+
672
+ /**
673
+ * Mark that limits.h is needed.
674
+ */
675
+ static requireLimits(): void {
676
+ this.needsLimits = true;
677
+ }
678
+
679
+ /**
680
+ * Mark that ISR type is needed.
681
+ */
682
+ static requireISR(): void {
683
+ this.needsISR = true;
684
+ }
685
+
686
+ // ===========================================================================
687
+ // TYPE REGISTRATION HELPERS
688
+ // ===========================================================================
689
+
690
+ /**
691
+ * Register a variable type.
692
+ */
693
+ static registerType(name: string, info: TTypeInfo): void {
694
+ this.typeRegistry.set(name, info);
695
+ }
696
+
697
+ /**
698
+ * Register a const value.
699
+ */
700
+ static registerConstValue(name: string, value: number): void {
701
+ this.constValues.set(name, value);
702
+ }
703
+
704
+ /**
705
+ * Register a local variable.
706
+ */
707
+ static registerLocalVariable(name: string, isArray: boolean = false): void {
708
+ this.localVariables.add(name);
709
+ if (isArray) {
710
+ this.localArrays.add(name);
711
+ }
712
+ }
713
+
714
+ /**
715
+ * Register a function signature.
716
+ */
717
+ static registerFunctionSignature(
718
+ name: string,
719
+ sig: IFunctionSignature,
720
+ ): void {
721
+ this.functionSignatures.set(name, sig);
722
+ this.knownFunctions.add(name);
723
+ }
724
+
725
+ /**
726
+ * Register a callback type.
727
+ */
728
+ static registerCallbackType(name: string, info: ICallbackTypeInfo): void {
729
+ this.callbackTypes.set(name, info);
730
+ }
731
+
732
+ /**
733
+ * Register a callback field type.
734
+ */
735
+ static registerCallbackFieldType(key: string, typeName: string): void {
736
+ this.callbackFieldTypes.set(key, typeName);
737
+ }
738
+
739
+ /**
740
+ * Mark a clamp operation as used.
741
+ */
742
+ static markClampOpUsed(operation: string, cnxType: string): void {
743
+ this.usedClampOps.add(`${operation}_${cnxType}`);
744
+ }
745
+
746
+ /**
747
+ * Mark a safe div operation as used.
748
+ */
749
+ static markSafeDivOpUsed(operation: string, cnxType: string): void {
750
+ this.usedSafeDivOps.add(`${operation}_${cnxType}`);
751
+ }
752
+
753
+ // ===========================================================================
754
+ // FLOAT BIT SHADOW HELPERS
755
+ // ===========================================================================
756
+
757
+ /**
758
+ * Register a float bit shadow variable.
759
+ */
760
+ static registerFloatBitShadow(name: string): void {
761
+ this.floatBitShadows.add(name);
762
+ }
763
+
764
+ /**
765
+ * Check if a float bit shadow exists.
766
+ */
767
+ static hasFloatBitShadow(name: string): boolean {
768
+ return this.floatBitShadows.has(name);
769
+ }
770
+
771
+ /**
772
+ * Mark a float shadow as having current value.
773
+ */
774
+ static markFloatShadowCurrent(name: string): void {
775
+ this.floatShadowCurrent.add(name);
776
+ }
777
+
778
+ /**
779
+ * Check if a float shadow has current value.
780
+ */
781
+ static isFloatShadowCurrent(name: string): boolean {
782
+ return this.floatShadowCurrent.has(name);
783
+ }
784
+
785
+ // ===========================================================================
786
+ // C++ MODE HELPERS
787
+ // ===========================================================================
788
+
789
+ /**
790
+ * Add a pending temp declaration for C++ mode.
791
+ */
792
+ static addPendingTempDeclaration(decl: string): void {
793
+ this.pendingTempDeclarations.push(decl);
794
+ }
795
+
796
+ /**
797
+ * Flush and return pending temp declarations.
798
+ */
799
+ static flushPendingTempDeclarations(): string[] {
800
+ const decls = this.pendingTempDeclarations;
801
+ this.pendingTempDeclarations = [];
802
+ return decls;
803
+ }
804
+
805
+ /**
806
+ * Get a unique temp variable name.
807
+ */
808
+ static getNextTempVarName(): string {
809
+ return `_tmp${this.tempVarCounter++}`;
810
+ }
811
+ }