c-next 0.1.1 → 0.1.3

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 (61) hide show
  1. package/README.md +62 -4
  2. package/package.json +9 -13
  3. package/src/analysis/DivisionByZeroAnalyzer.ts +2 -2
  4. package/src/analysis/FloatModuloAnalyzer.ts +196 -0
  5. package/src/analysis/FunctionCallAnalyzer.ts +2 -4
  6. package/src/analysis/InitializationAnalyzer.ts +81 -9
  7. package/src/analysis/NullCheckAnalyzer.ts +2 -4
  8. package/src/analysis/types/IDeclarationInfo.ts +13 -0
  9. package/src/analysis/types/IDivisionByZeroError.ts +1 -1
  10. package/src/analysis/types/IFloatModuloError.ts +20 -0
  11. package/src/analysis/types/IFunctionCallError.ts +1 -1
  12. package/src/analysis/types/IInitializationError.ts +2 -29
  13. package/src/analysis/types/INullCheckError.ts +1 -1
  14. package/src/analysis/types/formatInitializationError.ts +20 -0
  15. package/src/codegen/CodeGenerator.ts +838 -78
  16. package/src/codegen/HeaderGenerator.ts +39 -26
  17. package/src/codegen/TypeResolver.ts +6 -8
  18. package/src/codegen/types/BITMAP_BACKING_TYPE.ts +11 -0
  19. package/src/codegen/types/BITMAP_SIZE.ts +11 -0
  20. package/src/codegen/types/C_TYPE_WIDTH.ts +31 -0
  21. package/src/codegen/types/FLOAT_TYPES.ts +6 -0
  22. package/src/codegen/types/IAssignmentContext.ts +12 -0
  23. package/src/codegen/types/ICodeGeneratorOptions.ts +13 -0
  24. package/src/codegen/types/IHeaderOptions.ts +15 -0
  25. package/src/codegen/types/INTEGER_TYPES.ts +8 -0
  26. package/src/codegen/types/SIGNED_TYPES.ts +6 -0
  27. package/src/codegen/types/TCodeGenContext.ts +1 -10
  28. package/src/codegen/types/TYPE_RANGES.ts +16 -0
  29. package/src/codegen/types/TYPE_WIDTH.ts +18 -0
  30. package/src/codegen/types/UNSIGNED_TYPES.ts +6 -0
  31. package/src/index.ts +4 -29
  32. package/src/lib/IncludeDiscovery.ts +3 -1
  33. package/src/lib/InputExpansion.ts +6 -2
  34. package/src/lib/PlatformIODetector.ts +1 -1
  35. package/src/lib/parse.ts +16 -0
  36. package/src/lib/parseWithSymbols.ts +148 -0
  37. package/src/lib/transpiler.ts +33 -175
  38. package/src/lib/types/IParseWithSymbolsResult.ts +16 -0
  39. package/src/lib/types/ISymbolInfo.ts +28 -0
  40. package/src/lib/types/ITranspileError.ts +15 -0
  41. package/src/lib/types/ITranspileOptions.ts +13 -0
  42. package/src/lib/types/ITranspileResult.ts +2 -65
  43. package/src/lib/types/TSymbolKind.ts +15 -0
  44. package/src/preprocessor/Preprocessor.ts +4 -19
  45. package/src/preprocessor/types/IPreprocessOptions.ts +20 -0
  46. package/src/preprocessor/types/IPreprocessResult.ts +1 -14
  47. package/src/preprocessor/types/ISourceMapping.ts +15 -0
  48. package/src/project/FileDiscovery.ts +3 -42
  49. package/src/project/Project.ts +73 -19
  50. package/src/project/types/EFileType.ts +13 -0
  51. package/src/project/types/IDiscoveredFile.ts +17 -0
  52. package/src/project/types/IDiscoveryOptions.ts +15 -0
  53. package/src/project/types/IProjectConfig.ts +0 -31
  54. package/src/project/types/IProjectResult.ts +27 -0
  55. package/src/symbols/CNextSymbolCollector.ts +19 -0
  56. package/src/symbols/CppSymbolCollector.ts +110 -1
  57. package/src/symbols/SymbolTable.ts +1 -11
  58. package/src/symbols/types/IStructFieldInfo.ts +11 -0
  59. package/src/types/ISymbol.ts +9 -0
  60. package/src/codegen/types/TTypeConstants.ts +0 -94
  61. package/src/codegen/types/index.ts +0 -17
package/README.md CHANGED
@@ -301,6 +301,40 @@ Write-only registers generate optimized code:
301
301
  GPIO7.DR_SET[LED_BIT] <- true; // Generates: GPIO7_DR_SET = (1 << LED_BIT);
302
302
  ```
303
303
 
304
+ ### Slice Assignment for Memory Operations
305
+
306
+ Multi-byte copying with bounds-checked `memcpy` generation:
307
+
308
+ ```cnx
309
+ u8 buffer[256];
310
+ u32 magic <- 0x12345678;
311
+ u8 length <- 4;
312
+ u32 offset <- 0;
313
+
314
+ // Copy 4 bytes from value into buffer at offset
315
+ buffer[offset, length] <- magic;
316
+ ```
317
+
318
+ Transpiles to runtime bounds-checked code:
319
+
320
+ ```c
321
+ uint8_t buffer[256] = {0};
322
+ uint32_t magic = 0x12345678;
323
+ uint8_t length = 4;
324
+ uint32_t offset = 0;
325
+
326
+ if (offset + length <= sizeof(buffer)) {
327
+ memcpy(&buffer[offset], &magic, length);
328
+ }
329
+ ```
330
+
331
+ **Key Features:**
332
+
333
+ - Runtime bounds checking prevents buffer overflows
334
+ - Enables binary serialization and protocol implementation
335
+ - Works with struct fields: `buffer[offset, len] <- config.magic`
336
+ - Distinct from bit operations: array slices use `memcpy`, scalar bit ranges use bit manipulation
337
+
304
338
  ### Scopes (ADR-016)
305
339
 
306
340
  Organize code with automatic name prefixing. Inside scopes, explicit qualification is required:
@@ -702,17 +736,41 @@ Decisions are documented in `/docs/decisions/`:
702
736
  | [ADR-031](docs/decisions/adr-031-inline-functions.md) | Inline Functions | Trust compiler; `inline` is just a hint anyway |
703
737
  | [ADR-033](docs/decisions/adr-033-packed-structs.md) | Packed Structs | Use ADR-004 register bindings or explicit serialization |
704
738
 
705
- ## Build Commands
739
+ ## Development
740
+
741
+ ### Setup
742
+
743
+ ```bash
744
+ # Clone and install (IMPORTANT: npm install sets up pre-commit hooks)
745
+ git clone https://github.com/jlaustill/c-next.git
746
+ cd c-next
747
+ npm install # Installs dependencies and Husky pre-commit hooks
748
+ ```
749
+
750
+ **Pre-commit hooks:** The project uses [Husky](https://typicode.github.io/husky/) to automatically format code (Prettier) and fix linting (ESLint) before every commit. This prevents formatting errors in PRs.
751
+
752
+ ### Commands
706
753
 
707
754
  ```bash
708
- npm run build # Full build: ANTLR + TypeScript
709
755
  npm run antlr # Regenerate parser from grammar
710
- npx tsc # TypeScript only
756
+ npm run typecheck # Type-check TypeScript (no build required)
757
+ npm test # Run all tests
758
+ npm test -- --quiet # Minimal output (errors + summary only)
759
+ npm test -- tests/enum # Run specific directory
760
+ npm test -- tests/enum/my.test.cnx # Run single test file
761
+
762
+ # Code quality (auto-run by pre-commit hooks)
763
+ npm run prettier:fix # Format all code
764
+ npm run eslint:check # Check for lint errors
711
765
  ```
712
766
 
767
+ **Note:** C-Next runs directly via `tsx` without a build step. The `typecheck` command validates types only and does not generate any output files.
768
+
713
769
  ## Contributing
714
770
 
715
- Ideas and feedback welcome via issues.
771
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for the complete development workflow, testing requirements, and PR process.
772
+
773
+ **Quick start:** Ideas and feedback welcome via issues.
716
774
 
717
775
  ## License
718
776
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c-next",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "A safer C for embedded systems development. Transpiles to clean, readable C.",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
@@ -15,17 +15,18 @@
15
15
  "antlr:all": "npm run antlr && npm run antlr:c && npm run antlr:cpp",
16
16
  "start": "tsx src/index.ts",
17
17
  "dev": "tsx src/index.ts",
18
- "test": "tsx scripts/test.js",
18
+ "typecheck": "tsc --noEmit",
19
+ "test": "tsx scripts/test.ts",
19
20
  "test:cli": "node scripts/test-cli.js",
20
- "test:update": "tsx scripts/test.js --update",
21
+ "test:update": "tsx scripts/test.ts --update",
21
22
  "analyze": "./scripts/static-analysis.sh",
22
23
  "clean": "rm -rf src/parser",
23
24
  "prettier:check": "prettier --check .",
24
25
  "prettier:fix": "prettier --write .",
25
- "eslint:check": "eslint src/",
26
- "eslint:fix": "eslint src/ --fix",
26
+ "oxlint:check": "oxlint src/",
27
+ "oxlint:fix": "oxlint src/ --fix",
27
28
  "prepare": "husky",
28
- "prepublishOnly": "npm run prettier:check && npm run eslint:check && npm test"
29
+ "prepublishOnly": "npm run prettier:check && npm run oxlint:check && npm test"
29
30
  },
30
31
  "keywords": [
31
32
  "c",
@@ -56,16 +57,11 @@
56
57
  ],
57
58
  "devDependencies": {
58
59
  "@types/node": "^25.0.3",
59
- "@typescript-eslint/eslint-plugin": "^7.18.0",
60
- "@typescript-eslint/parser": "^7.18.0",
61
60
  "antlr4ng-cli": "^2.0.0",
62
- "eslint": "^8.57.1",
63
- "eslint-config-airbnb-typescript": "^18.0.0",
64
- "eslint-config-prettier": "^10.1.8",
65
- "eslint-plugin-import": "^2.32.0",
66
61
  "husky": "^9.1.7",
67
62
  "jest": "^30.2.0",
68
63
  "lint-staged": "^16.2.7",
64
+ "oxlint": "^1.39.0",
69
65
  "prettier": "^3.7.4",
70
66
  "ts-jest": "^29.4.6"
71
67
  },
@@ -79,7 +75,7 @@
79
75
  "prettier --write --ignore-unknown"
80
76
  ],
81
77
  "*.ts": [
82
- "eslint --fix"
78
+ "oxlint --fix"
83
79
  ]
84
80
  }
85
81
  }
@@ -13,7 +13,7 @@
13
13
  import { ParseTreeWalker } from "antlr4ng";
14
14
  import { CNextListener } from "../parser/grammar/CNextListener";
15
15
  import * as Parser from "../parser/grammar/CNextParser";
16
- import { IDivisionByZeroError } from "./types/IDivisionByZeroError";
16
+ import IDivisionByZeroError from "./types/IDivisionByZeroError";
17
17
 
18
18
  /**
19
19
  * First pass: Collect const declarations that are zero
@@ -323,7 +323,7 @@ class DivisionByZeroListener extends CNextListener {
323
323
  /**
324
324
  * Analyzer that detects division by zero
325
325
  */
326
- export class DivisionByZeroAnalyzer {
326
+ class DivisionByZeroAnalyzer {
327
327
  private errors: IDivisionByZeroError[] = [];
328
328
 
329
329
  /**
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Float Modulo Analyzer
3
+ * Detects modulo operator usage with floating-point types at compile time
4
+ *
5
+ * The modulo operator (%) is only valid for integer types in C.
6
+ * C-Next catches this early with a clear error message.
7
+ *
8
+ * Two-pass analysis:
9
+ * 1. Collect variable declarations with f32/f64 types
10
+ * 2. Detect modulo operations using float variables or literals
11
+ */
12
+
13
+ import { ParseTreeWalker } from "antlr4ng";
14
+ import { CNextListener } from "../parser/grammar/CNextListener";
15
+ import * as Parser from "../parser/grammar/CNextParser";
16
+ import IFloatModuloError from "./types/IFloatModuloError";
17
+
18
+ const FLOAT_TYPES = ["f32", "f64", "float", "double"];
19
+
20
+ /**
21
+ * First pass: Collect variable declarations with float types
22
+ */
23
+ class FloatVariableCollector extends CNextListener {
24
+ private floatVars: Set<string> = new Set();
25
+
26
+ public getFloatVars(): Set<string> {
27
+ return this.floatVars;
28
+ }
29
+
30
+ /**
31
+ * Track variable declarations with f32/f64 types
32
+ */
33
+ override enterVariableDeclaration = (
34
+ ctx: Parser.VariableDeclarationContext,
35
+ ): void => {
36
+ const typeCtx = ctx.type();
37
+ if (!typeCtx) return;
38
+
39
+ const typeName = typeCtx.getText();
40
+ if (!FLOAT_TYPES.includes(typeName)) return;
41
+
42
+ const identifier = ctx.IDENTIFIER();
43
+ if (!identifier) return;
44
+
45
+ this.floatVars.add(identifier.getText());
46
+ };
47
+
48
+ /**
49
+ * Track function parameters with f32/f64 types
50
+ */
51
+ override enterParameter = (ctx: Parser.ParameterContext): void => {
52
+ const typeCtx = ctx.type();
53
+ if (!typeCtx) return;
54
+
55
+ const typeName = typeCtx.getText();
56
+ if (!FLOAT_TYPES.includes(typeName)) return;
57
+
58
+ const identifier = ctx.IDENTIFIER();
59
+ if (!identifier) return;
60
+
61
+ this.floatVars.add(identifier.getText());
62
+ };
63
+ }
64
+
65
+ /**
66
+ * Second pass: Detect modulo operations with float operands
67
+ */
68
+ class FloatModuloListener extends CNextListener {
69
+ private analyzer: FloatModuloAnalyzer;
70
+
71
+ // eslint-disable-next-line @typescript-eslint/lines-between-class-members
72
+ private floatVars: Set<string>;
73
+
74
+ constructor(analyzer: FloatModuloAnalyzer, floatVars: Set<string>) {
75
+ super();
76
+ this.analyzer = analyzer;
77
+ this.floatVars = floatVars;
78
+ }
79
+
80
+ /**
81
+ * Check multiplicative expressions for modulo with float operands
82
+ * multiplicativeExpression: unaryExpression (('*' | '/' | '%') unaryExpression)*
83
+ */
84
+ override enterMultiplicativeExpression = (
85
+ ctx: Parser.MultiplicativeExpressionContext,
86
+ ): void => {
87
+ const operands = ctx.unaryExpression();
88
+ if (operands.length < 2) return;
89
+
90
+ // Check each operator
91
+ for (let i = 0; i < operands.length - 1; i++) {
92
+ const operatorToken = ctx.getChild(i * 2 + 1);
93
+ if (!operatorToken) continue;
94
+
95
+ const operator = operatorToken.getText();
96
+ if (operator !== "%") continue;
97
+
98
+ const leftOperand = operands[i];
99
+ const rightOperand = operands[i + 1];
100
+
101
+ const leftIsFloat = this.isFloatOperand(leftOperand);
102
+ const rightIsFloat = this.isFloatOperand(rightOperand);
103
+
104
+ if (leftIsFloat || rightIsFloat) {
105
+ const line = leftOperand.start?.line ?? 0;
106
+ const column = leftOperand.start?.column ?? 0;
107
+ this.analyzer.addError(line, column);
108
+ }
109
+ }
110
+ };
111
+
112
+ /**
113
+ * Check if a unary expression is a float type
114
+ */
115
+ private isFloatOperand(ctx: Parser.UnaryExpressionContext): boolean {
116
+ const postfixExpr = ctx.postfixExpression();
117
+ if (!postfixExpr) return false;
118
+
119
+ const primaryExpr = postfixExpr.primaryExpression();
120
+ if (!primaryExpr) return false;
121
+
122
+ // Check for float literal
123
+ const literal = primaryExpr.literal();
124
+ if (literal) {
125
+ return this.isFloatLiteral(literal);
126
+ }
127
+
128
+ // Check for identifier that's a float variable
129
+ const identifier = primaryExpr.IDENTIFIER();
130
+ if (identifier) {
131
+ return this.floatVars.has(identifier.getText());
132
+ }
133
+
134
+ return false;
135
+ }
136
+
137
+ /**
138
+ * Check if a literal is a floating-point number
139
+ */
140
+ private isFloatLiteral(ctx: Parser.LiteralContext): boolean {
141
+ // Check for FLOAT_LITERAL token
142
+ if (ctx.FLOAT_LITERAL()) return true;
143
+
144
+ // Check text for decimal point (fallback)
145
+ const text = ctx.getText();
146
+ return text.includes(".") && !text.startsWith('"');
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Analyzer that detects modulo operations with floating-point types
152
+ */
153
+ class FloatModuloAnalyzer {
154
+ private errors: IFloatModuloError[] = [];
155
+
156
+ /**
157
+ * Analyze the parse tree for float modulo operations
158
+ */
159
+ public analyze(tree: Parser.ProgramContext): IFloatModuloError[] {
160
+ this.errors = [];
161
+
162
+ // First pass: collect float variables
163
+ const collector = new FloatVariableCollector();
164
+ ParseTreeWalker.DEFAULT.walk(collector, tree);
165
+ const floatVars = collector.getFloatVars();
166
+
167
+ // Second pass: detect modulo with floats
168
+ const listener = new FloatModuloListener(this, floatVars);
169
+ ParseTreeWalker.DEFAULT.walk(listener, tree);
170
+
171
+ return this.errors;
172
+ }
173
+
174
+ /**
175
+ * Add a float modulo error
176
+ */
177
+ public addError(line: number, column: number): void {
178
+ this.errors.push({
179
+ code: "E0804",
180
+ line,
181
+ column,
182
+ message: "Modulo operator not supported for floating-point types",
183
+ helpText:
184
+ "The % operator only works with integer types. Use fmod() from <math.h> for floating-point remainder.",
185
+ });
186
+ }
187
+
188
+ /**
189
+ * Get all detected errors
190
+ */
191
+ public getErrors(): IFloatModuloError[] {
192
+ return this.errors;
193
+ }
194
+ }
195
+
196
+ export default FloatModuloAnalyzer;
@@ -13,9 +13,7 @@ import * as Parser from "../parser/grammar/CNextParser";
13
13
  import SymbolTable from "../symbols/SymbolTable";
14
14
  import ESourceLanguage from "../types/ESourceLanguage";
15
15
  import ESymbolKind from "../types/ESymbolKind";
16
- import { IFunctionCallError } from "./types/IFunctionCallError";
17
-
18
- export { IFunctionCallError };
16
+ import IFunctionCallError from "./types/IFunctionCallError";
19
17
 
20
18
  /**
21
19
  * C-Next built-in functions
@@ -341,7 +339,7 @@ class FunctionCallListener extends CNextListener {
341
339
  /**
342
340
  * Analyzes C-Next AST for function calls before definition
343
341
  */
344
- export class FunctionCallAnalyzer {
342
+ class FunctionCallAnalyzer {
345
343
  private errors: IFunctionCallError[] = [];
346
344
 
347
345
  /** Functions that have been defined (in order of appearance) */
@@ -14,10 +14,8 @@
14
14
  import { ParseTreeWalker } from "antlr4ng";
15
15
  import { CNextListener } from "../parser/grammar/CNextListener";
16
16
  import * as Parser from "../parser/grammar/CNextParser";
17
- import {
18
- IInitializationError,
19
- IDeclarationInfo,
20
- } from "./types/IInitializationError";
17
+ import IInitializationError from "./types/IInitializationError";
18
+ import IDeclarationInfo from "./types/IDeclarationInfo";
21
19
 
22
20
  /**
23
21
  * Tracks the initialization state of a variable
@@ -156,14 +154,19 @@ class InitializationListener extends CNextListener {
156
154
  this.analyzer.recordAssignment(name);
157
155
  }
158
156
 
159
- // Member access: p.x <- value (only first-level field)
157
+ // Member access: p.x <- value (struct field) or arr[i][j] <- value (multi-dim array)
160
158
  if (targetCtx.memberAccess()) {
161
159
  const memberCtx = targetCtx.memberAccess()!;
162
160
  const identifiers = memberCtx.IDENTIFIER();
163
161
  if (identifiers.length >= 2) {
162
+ // Struct field access: p.x <- value
164
163
  const varName = identifiers[0].getText();
165
164
  const fieldName = identifiers[1].getText();
166
165
  this.analyzer.recordAssignment(varName, fieldName);
166
+ } else if (identifiers.length === 1) {
167
+ // Multi-dimensional array access: arr[i][j] <- value
168
+ const varName = identifiers[0].getText();
169
+ this.analyzer.recordAssignment(varName);
167
170
  }
168
171
  }
169
172
 
@@ -346,11 +349,54 @@ class InitializationListener extends CNextListener {
346
349
  this.savedStates.push(this.analyzer.cloneScopeState());
347
350
  };
348
351
 
349
- override exitForStatement = (_ctx: Parser.ForStatementContext): void => {
350
- // Same as while - conservative approach
352
+ /**
353
+ * Check if a for-loop is deterministic (will definitely run at least once)
354
+ * A loop is deterministic if it has the form: for (var <- 0; var < CONSTANT; ...)
355
+ * where CONSTANT > 0
356
+ */
357
+ private isDeterministicForLoop(ctx: Parser.ForStatementContext): boolean {
358
+ const init = ctx.forInit();
359
+ const cond = ctx.expression();
360
+
361
+ if (!init || !cond) return false;
362
+
363
+ // Check if init is a variable declaration starting at 0
364
+ const forVarDecl = init.forVarDecl();
365
+ if (forVarDecl) {
366
+ const initExpr = forVarDecl.expression();
367
+ if (!initExpr) return false;
368
+ const initText = initExpr.getText();
369
+ if (initText !== "0") return false;
370
+ } else {
371
+ // forAssignment case: check if assigning 0
372
+ const forAssign = init.forAssignment();
373
+ if (!forAssign) return false;
374
+ const assignExpr = forAssign.expression();
375
+ if (!assignExpr) return false;
376
+ const assignText = assignExpr.getText();
377
+ if (assignText !== "0") return false;
378
+ }
379
+
380
+ // Check if condition is var < POSITIVE_CONSTANT
381
+ const condText = cond.getText();
382
+ // Match patterns like "i<4" or "ti<3" (no spaces in AST getText())
383
+ const match = condText.match(/^\w+<(\d+)$/);
384
+ if (!match) return false;
385
+ const bound = parseInt(match[1], 10);
386
+ return bound > 0;
387
+ }
388
+
389
+ override exitForStatement = (ctx: Parser.ForStatementContext): void => {
351
390
  const stateBeforeLoop = this.savedStates.pop();
352
391
  if (stateBeforeLoop) {
353
- this.analyzer.restoreFromState(stateBeforeLoop);
392
+ const isDeterministic = this.isDeterministicForLoop(ctx);
393
+ if (isDeterministic) {
394
+ // Deterministic loop - preserve initialization (loop WILL run)
395
+ this.analyzer.mergeInitializationState(stateBeforeLoop);
396
+ } else {
397
+ // Non-deterministic loop - conservative restore (loop might not run)
398
+ this.analyzer.restoreFromState(stateBeforeLoop);
399
+ }
354
400
  }
355
401
  };
356
402
  }
@@ -358,7 +404,7 @@ class InitializationListener extends CNextListener {
358
404
  /**
359
405
  * Analyzes C-Next AST for use-before-initialization errors
360
406
  */
361
- export class InitializationAnalyzer {
407
+ class InitializationAnalyzer {
362
408
  private errors: IInitializationError[] = [];
363
409
 
364
410
  private currentScope: IScope | null = null;
@@ -653,6 +699,32 @@ export class InitializationAnalyzer {
653
699
  }
654
700
  }
655
701
 
702
+ /**
703
+ * Merge initialization state from a saved snapshot
704
+ * Used for deterministic loops where initialization inside the loop
705
+ * should be preserved (the loop WILL run at least once)
706
+ */
707
+ public mergeInitializationState(
708
+ beforeState: Map<string, IVariableState>,
709
+ ): void {
710
+ let scope = this.currentScope;
711
+ while (scope) {
712
+ for (const [name, currentState] of scope.variables) {
713
+ const beforeVar = beforeState.get(name);
714
+ if (beforeVar) {
715
+ // Preserve initialization if it happened inside the loop
716
+ // (currentState.initialized stays true if set inside loop)
717
+ // Merge initializedFields from before state to preserve any
718
+ // fields that were initialized before the loop
719
+ for (const field of beforeVar.initializedFields) {
720
+ currentState.initializedFields.add(field);
721
+ }
722
+ }
723
+ }
724
+ scope = scope.parent;
725
+ }
726
+ }
727
+
656
728
  // ========================================================================
657
729
  // Error Reporting
658
730
  // ========================================================================
@@ -16,9 +16,7 @@
16
16
  import { ParseTreeWalker } from "antlr4ng";
17
17
  import { CNextListener } from "../parser/grammar/CNextListener";
18
18
  import * as Parser from "../parser/grammar/CNextParser";
19
- import { INullCheckError } from "./types/INullCheckError";
20
-
21
- export { INullCheckError };
19
+ import INullCheckError from "./types/INullCheckError";
22
20
 
23
21
  /**
24
22
  * Metadata for C library functions that can return NULL
@@ -284,7 +282,7 @@ class NullCheckListener extends CNextListener {
284
282
  /**
285
283
  * Analyzes C-Next AST for NULL safety violations
286
284
  */
287
- export class NullCheckAnalyzer {
285
+ class NullCheckAnalyzer {
288
286
  private errors: INullCheckError[] = [];
289
287
 
290
288
  /** Included headers (for context) */
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Information about where a variable was declared
3
+ */
4
+ interface IDeclarationInfo {
5
+ /** Variable name */
6
+ name: string;
7
+ /** Line where variable was declared */
8
+ line: number;
9
+ /** Column where variable was declared */
10
+ column: number;
11
+ }
12
+
13
+ export default IDeclarationInfo;
@@ -7,7 +7,7 @@
7
7
  * - E0802: Modulo by literal zero
8
8
  * - E0803: Modulo by const that evaluates to zero
9
9
  */
10
- export interface IDivisionByZeroError {
10
+ interface IDivisionByZeroError {
11
11
  /** Error code (E0800-E0803) */
12
12
  code: string;
13
13
  /** Operator used ('/' or '%') */
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Error reported when modulo operator is used with floating-point types
3
+ *
4
+ * Error codes:
5
+ * - E0804: Modulo operator used with floating-point operand
6
+ */
7
+ interface IFloatModuloError {
8
+ /** Error code (E0804) */
9
+ code: string;
10
+ /** Line number where the error occurred */
11
+ line: number;
12
+ /** Column number where the error occurred */
13
+ column: number;
14
+ /** Human-readable error message */
15
+ message: string;
16
+ /** Optional help text with suggested fix */
17
+ helpText?: string;
18
+ }
19
+
20
+ export default IFloatModuloError;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Error reported when a function is called before it's defined (ADR-030)
3
3
  */
4
- export interface IFunctionCallError {
4
+ interface IFunctionCallError {
5
5
  /** Error code (E0422) */
6
6
  code: string;
7
7
  /** Name of the function that was called */
@@ -3,22 +3,12 @@
3
3
  * Used for Rust-style "use before initialization" detection
4
4
  */
5
5
 
6
- /**
7
- * Information about where a variable was declared
8
- */
9
- export interface IDeclarationInfo {
10
- /** Variable name */
11
- name: string;
12
- /** Line where variable was declared */
13
- line: number;
14
- /** Column where variable was declared */
15
- column: number;
16
- }
6
+ import IDeclarationInfo from "./IDeclarationInfo";
17
7
 
18
8
  /**
19
9
  * Error for using a variable before initialization
20
10
  */
21
- export interface IInitializationError {
11
+ interface IInitializationError {
22
12
  /** Error code (E0381 matches Rust's error code) */
23
13
  code: "E0381";
24
14
  /** The variable or field that was used before initialization */
@@ -35,21 +25,4 @@ export interface IInitializationError {
35
25
  message: string;
36
26
  }
37
27
 
38
- /**
39
- * Format an initialization error into a Rust-style error message
40
- */
41
- export function formatInitializationError(error: IInitializationError): string {
42
- const certainty = error.mayBeUninitialized ? "possibly " : "";
43
- return `error[${error.code}]: use of ${certainty}uninitialized variable '${error.variable}'
44
- --> line ${error.line}:${error.column}
45
- |
46
- ${error.declaration.line} | ${error.declaration.name}
47
- | - variable declared here
48
- ...
49
- ${error.line} | ${error.variable}
50
- | ^ use of ${certainty}uninitialized '${error.variable}'
51
- |
52
- = help: consider initializing '${error.variable}'`;
53
- }
54
-
55
28
  export default IInitializationError;
@@ -7,7 +7,7 @@
7
7
  * - E0903: NULL can only be used in comparison context
8
8
  * - E0904: Cannot store C function pointer return in variable
9
9
  */
10
- export interface INullCheckError {
10
+ interface INullCheckError {
11
11
  /** Error code (E0901-E0904) */
12
12
  code: string;
13
13
  /** Name of the function or literal involved */
@@ -0,0 +1,20 @@
1
+ import IInitializationError from "./IInitializationError";
2
+
3
+ /**
4
+ * Format an initialization error into a Rust-style error message
5
+ */
6
+ function formatInitializationError(error: IInitializationError): string {
7
+ const certainty = error.mayBeUninitialized ? "possibly " : "";
8
+ return `error[${error.code}]: use of ${certainty}uninitialized variable '${error.variable}'
9
+ --> line ${error.line}:${error.column}
10
+ |
11
+ ${error.declaration.line} | ${error.declaration.name}
12
+ | - variable declared here
13
+ ...
14
+ ${error.line} | ${error.variable}
15
+ | ^ use of ${certainty}uninitialized '${error.variable}'
16
+ |
17
+ = help: consider initializing '${error.variable}'`;
18
+ }
19
+
20
+ export default formatInitializationError;