c-next 0.1.61 → 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 (104) hide show
  1. package/README.md +86 -63
  2. package/grammar/CNext.g4 +3 -17
  3. package/package.json +1 -1
  4. package/src/cli/serve/ServeCommand.ts +57 -45
  5. package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
  6. package/src/transpiler/Transpiler.ts +603 -613
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
  8. package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
  9. package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
  10. package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
  11. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
  12. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
  13. package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
  14. package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
  15. package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
  16. package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
  17. package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
  18. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
  19. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
  20. package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
  21. package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
  22. package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
  23. package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
  24. package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
  25. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
  26. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
  27. package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
  28. package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
  29. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
  30. package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
  31. package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
  32. package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
  33. package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
  34. package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
  35. package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
  36. package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
  37. package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
  38. package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
  39. package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
  40. package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
  41. package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
  42. package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
  43. package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
  44. package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
  45. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
  46. package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
  47. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
  48. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
  49. package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
  50. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
  51. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
  52. package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
  53. package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
  54. package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
  55. package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
  56. package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
  57. package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
  58. package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
  59. package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
  60. package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
  61. package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
  62. package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
  63. package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
  64. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
  65. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
  66. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
  67. package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
  68. package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
  69. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
  70. package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
  71. package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
  72. package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
  73. package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
  74. package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
  75. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
  76. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
  77. package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
  78. package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
  79. package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
  80. package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
  81. package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
  82. package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
  83. package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
  84. package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
  85. package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
  86. package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
  87. package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
  88. package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
  89. package/src/transpiler/types/IFileResult.ts +0 -4
  90. package/src/transpiler/types/IPipelineFile.ts +27 -0
  91. package/src/transpiler/types/IPipelineInput.ts +23 -0
  92. package/src/transpiler/types/TranspilerState.ts +1 -1
  93. package/src/utils/FormatUtils.ts +28 -2
  94. package/src/utils/MapUtils.ts +25 -0
  95. package/src/utils/PostfixAnalysisUtils.ts +48 -0
  96. package/src/utils/__tests__/FormatUtils.test.ts +42 -0
  97. package/src/utils/__tests__/MapUtils.test.ts +85 -0
  98. package/src/utils/constants/OperatorMappings.ts +19 -0
  99. package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
  100. package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
  101. package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
  102. package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
  103. package/src/transpiler/types/ITranspileContext.ts +0 -49
  104. package/src/transpiler/types/ITranspileContribution.ts +0 -32
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  A safer C for embedded systems development. Transpiles to clean, readable C.
11
11
 
12
- **Status: Working Transpiler** — Verified on Teensy MicroMod hardware.
12
+ **Status: Working Transpiler** — Verified on Teensy MicroMod, 4.0, and stm32 hardware.
13
13
 
14
14
  ## Quick Example
15
15
 
@@ -104,7 +104,7 @@ The C-Next VS Code extension provides syntax highlighting, live C preview, Intel
104
104
 
105
105
  ## Getting Started with PlatformIO
106
106
 
107
- C-Next integrates seamlessly with PlatformIO embedded projects. The transpiler automatically converts `.cnx` files to `.c` before each build.
107
+ C-Next integrates seamlessly with PlatformIO embedded projects. The transpiler automatically converts `.cnx` files to `.c`, `.h`, and `.cpp` as needed before each build.
108
108
 
109
109
  ### Quick Setup
110
110
 
@@ -145,11 +145,11 @@ Transpiling 2 c-next files...
145
145
  Building...
146
146
  ```
147
147
 
148
- 3. **Commit both `.cnx` and generated `.c` files** to version control
148
+ 3. **Commit both `.cnx` and generated `.c|.cpp|.h` files** to version control
149
149
 
150
150
  ### Why Commit Generated Files?
151
151
 
152
- Generated `.c` files are **reviewable artifacts** in pull requests:
152
+ Generated `.c|.cpp|.h` files are **reviewable artifacts** in pull requests:
153
153
 
154
154
  ```diff
155
155
  + // ConfigStorage.cnx
@@ -165,7 +165,7 @@ Generated `.c` files are **reviewable artifacts** in pull requests:
165
165
 
166
166
  **Benefits**:
167
167
 
168
- - See exactly what C code the transpiler generates
168
+ - See exactly what C/CPP code the transpiler generates
169
169
  - Review safety features (overflow protection, atomic operations)
170
170
  - Verify transpiler behavior
171
171
  - Build succeeds even if transpiler isn't available
@@ -181,9 +181,9 @@ my-teensy-project/
181
181
  ├── src/
182
182
  │ ├── main.cpp # C++ entry point
183
183
  │ ├── ConfigStorage.cnx # c-next source
184
- │ ├── ConfigStorage.c # Generated (committed)
184
+ │ ├── ConfigStorage.cpp # Generated (committed)
185
185
  │ ├── SensorProcessor.cnx # c-next source
186
- │ └── SensorProcessor.c # Generated (committed)
186
+ │ └── SensorProcessor.cpp # Generated (committed)
187
187
  └── include/
188
188
  └── AppConfig.h # Shared types
189
189
  ```
@@ -201,7 +201,7 @@ This removes:
201
201
  - `cnext_build.py` script
202
202
  - `extra_scripts` reference from `platformio.ini`
203
203
 
204
- Your `.cnx` files and generated `.c` files remain untouched.
204
+ Your `.cnx` files and generated `.c|.cpp|.h` files remain untouched.
205
205
 
206
206
  ### Manual Integration
207
207
 
@@ -262,7 +262,7 @@ Generated headers automatically include guards:
262
262
  | Add concepts to catch errors | Remove the ability to make errors |
263
263
  | Borrow checker complexity | Startup allocation = predictable memory |
264
264
  | Lifetime annotations | Fixed runtime layout = clear lifetimes |
265
- | `unsafe` escape hatch | Clean C is the escape hatch |
265
+ | `unsafe` escape hatch | No escape hatch needed! |
266
266
 
267
267
  **Guiding Principle:** If Linus Torvalds wouldn't approve of the complexity, it doesn't ship. Safety through removal, not addition.
268
268
 
@@ -360,11 +360,13 @@ memcpy(&buffer[8], &timestamp, 8);
360
360
 
361
361
  ### Scopes (ADR-016)
362
362
 
363
- Organize code with automatic name prefixing. Inside scopes, explicit qualification is required:
363
+ Organize code with automatic name prefixing. Inside scopes, explicit qualification is available to avoid naming collisions:
364
364
 
365
365
  - `this.X` for scope-local members
366
366
  - `global.X` for global variables, functions, and registers
367
367
 
368
+ If the same name exists in local, scope, and global levels, the precedence is local, scope, global just like you are used to in other languages.
369
+
368
370
  ```cnx
369
371
  const u8 LED_BIT <- 3;
370
372
 
@@ -652,7 +654,7 @@ Allocate at startup, run with fixed memory. Per MISRA C:2023 Dir 4.12: all memor
652
654
 
653
655
  ## Hardware Testing
654
656
 
655
- Verified on **Teensy MicroMod** (NXP i.MX RT1062):
657
+ Verified on **Teensy MicroMod**, **Teensy 4.0**, and **STM32** hardware:
656
658
 
657
659
  ```bash
658
660
  # Build and flash with PlatformIO
@@ -674,16 +676,20 @@ _Using C-Next in your project? Open an issue to get listed!_
674
676
 
675
677
  ```
676
678
  c-next/
677
- ├── grammar/CNext.g4 # ANTLR4 grammar definition
679
+ ├── grammar/CNext.g4 # ANTLR4 grammar definition
678
680
  ├── src/
679
- │ ├── codegen/CodeGenerator.ts # Transpiler core
680
- │ ├── parser/ # Generated ANTLR parser
681
- └── index.ts # CLI entry point
681
+ │ ├── index.ts # CLI entry point
682
+ │ ├── transpiler/
683
+ │ ├── Transpiler.ts # Orchestrator
684
+ │ │ ├── data/ # Discovery layer (files, includes, deps)
685
+ │ │ ├── logic/ # Business logic (parser, symbols, analysis)
686
+ │ │ └── output/ # Generation (codegen, headers)
687
+ │ └── utils/ # Shared utilities
682
688
  ├── examples/
683
- │ ├── blink.cnx # LED blink (Teensy verified)
684
- │ └── bit_test.cnx # Bit manipulation tests
685
- ├── test-teensy/ # PlatformIO test project
686
- └── docs/decisions/ # Architecture Decision Records
689
+ │ ├── blink.cnx # LED blink (Teensy verified)
690
+ │ └── bit_test.cnx # Bit manipulation tests
691
+ ├── test-teensy/ # PlatformIO test project
692
+ └── docs/decisions/ # Architecture Decision Records
687
693
  ```
688
694
 
689
695
  ## Architecture Decision Records
@@ -692,53 +698,70 @@ Decisions are documented in `/docs/decisions/`:
692
698
 
693
699
  ### Implemented
694
700
 
695
- | ADR | Title | Description |
696
- | --------------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------- |
697
- | [ADR-001](docs/decisions/adr-001-assignment-operator.md) | Assignment Operator | `<-` for assignment, `=` for comparison |
698
- | [ADR-003](docs/decisions/adr-003-static-allocation.md) | Static Allocation | No dynamic memory after init |
699
- | [ADR-004](docs/decisions/adr-004-register-bindings.md) | Register Bindings | Type-safe hardware access |
700
- | [ADR-006](docs/decisions/adr-006-simplified-references.md) | Simplified References | Pass by reference, no pointer syntax |
701
- | [ADR-007](docs/decisions/adr-007-type-aware-bit-indexing.md) | Type-Aware Bit Indexing | Integers as bit arrays, `.length` property |
702
- | [ADR-010](docs/decisions/adr-010-c-interoperability.md) | C Interoperability | Unified ANTLR parser architecture |
703
- | [ADR-011](docs/decisions/adr-011-vscode-extension.md) | VS Code Extension | Live C preview with syntax highlighting |
704
- | [ADR-012](docs/decisions/adr-012-static-analysis.md) | Static Analysis | cppcheck integration for generated C |
705
- | [ADR-013](docs/decisions/adr-013-const-qualifier.md) | Const Qualifier | Compile-time const enforcement |
706
- | [ADR-014](docs/decisions/adr-014-structs.md) | Structs | Data containers without methods |
707
- | [ADR-015](docs/decisions/adr-015-null-state.md) | Null State | Zero initialization for all variables |
708
- | [ADR-016](docs/decisions/adr-016-scope.md) | Scope | `this.`/`global.` explicit qualification |
709
- | [ADR-017](docs/decisions/adr-017-enums.md) | Enums | Type-safe enums with C-style casting |
710
- | [ADR-030](docs/decisions/adr-030-forward-declarations.md) | Define-Before-Use | Functions must be defined before called |
711
- | [ADR-037](docs/decisions/adr-037-preprocessor.md) | Preprocessor | Flag-only defines, const for values |
712
- | [ADR-043](docs/decisions/adr-043-comments.md) | Comments | Comment preservation with MISRA compliance |
713
- | [ADR-044](docs/decisions/adr-044-primitive-types.md) | Primitive Types | Fixed-width types with `clamp`/`wrap` overflow |
714
- | [ADR-024](docs/decisions/adr-024-type-casting.md) | Type Casting | Widening implicit, narrowing uses bit indexing |
715
- | [ADR-022](docs/decisions/adr-022-conditional-expressions.md) | Conditional Expressions | Ternary with required parens, boolean condition, no nesting |
716
- | [ADR-025](docs/decisions/adr-025-switch-statements.md) | Switch Statements | Safe switch with braces, `\|\|` syntax, counted `default(n)` |
717
- | [ADR-029](docs/decisions/adr-029-function-pointers.md) | Callbacks | Function-as-Type pattern with nominal typing |
718
- | [ADR-045](docs/decisions/adr-045-string-type.md) | Bounded Strings | `string<N>` with compile-time safety |
719
- | [ADR-023](docs/decisions/adr-023-sizeof.md) | Sizeof | Type/value size queries with safety checks |
720
- | [ADR-027](docs/decisions/adr-027-do-while.md) | Do-While | `do { } while ()` with boolean condition (E0701) |
721
- | [ADR-032](docs/decisions/adr-032-nested-structs.md) | Nested Structs | Named nested structs only (no anonymous) |
722
- | [ADR-035](docs/decisions/adr-035-array-initializers.md) | Array Initializers | `[1, 2, 3]` syntax with `[0*]` fill-all |
723
- | [ADR-036](docs/decisions/adr-036-multidimensional-arrays.md) | Multi-dim Arrays | `arr[i][j]` with compile-time bounds enforcement |
724
- | [ADR-040](docs/decisions/adr-040-isr-declaration.md) | ISR Type | Built-in `ISR` type for `void(void)` function pointers |
725
- | [ADR-034](docs/decisions/adr-034-bit-fields.md) | Bitmap Types | `bitmap8`/`bitmap16`/`bitmap32` for portable bit-packed data |
726
- | [ADR-048](docs/decisions/adr-048-cli-executable.md) | CLI Executable | `cnext` command with smart defaults |
727
- | [ADR-049](docs/decisions/adr-049-atomic-types.md) | Atomic Types | `atomic` keyword with LDREX/STREX or PRIMASK fallback |
728
- | [ADR-050](docs/decisions/adr-050-critical-sections.md) | Critical Sections | `critical { }` blocks with PRIMASK save/restore |
729
- | [ADR-108](docs/decisions/adr-108-volatile-keyword.md) | Volatile Variables | `volatile` keyword prevents compiler optimization |
730
- | [ADR-046](docs/decisions/adr-046-nullable-c-interop.md) | Nullable C Interop | `c_` prefix for nullable C pointer types (supersedes ADR-047) |
731
- | [ADR-047](docs/decisions/adr-047-nullable-types.md) | NULL for C Interop | `NULL` keyword for C stream functions (superseded by ADR-046) |
732
- | [ADR-052](docs/decisions/adr-052-safe-numeric-literal-generation.md) | Safe Numeric Literals | `type_MIN`/`type_MAX` constants + safe hex conversion |
733
- | [ADR-053](docs/decisions/adr-053-transpiler-pipeline-architecture.md) | Transpiler Pipeline | Unified multi-pass pipeline with header symbol extraction |
734
- | [ADR-057](docs/decisions/adr-057-implicit-scope-resolution.md) | Implicit Scope Resolution | Bare identifiers resolve local → scope → global |
701
+ | ADR | Title | Description |
702
+ | --------------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------ |
703
+ | [ADR-001](docs/decisions/adr-001-assignment-operator.md) | Assignment Operator | `<-` for assignment, `=` for comparison |
704
+ | [ADR-003](docs/decisions/adr-003-static-allocation.md) | Static Allocation | No dynamic memory after init |
705
+ | [ADR-004](docs/decisions/adr-004-register-bindings.md) | Register Bindings | Type-safe hardware access |
706
+ | [ADR-006](docs/decisions/adr-006-simplified-references.md) | Simplified References | Pass by reference, no pointer syntax |
707
+ | [ADR-007](docs/decisions/adr-007-type-aware-bit-indexing.md) | Type-Aware Bit Indexing | Integers as bit arrays, `.length` property |
708
+ | [ADR-010](docs/decisions/adr-010-c-interoperability.md) | C Interoperability | Unified ANTLR parser architecture |
709
+ | [ADR-011](docs/decisions/adr-011-vscode-extension.md) | VS Code Extension | Live C preview with syntax highlighting |
710
+ | [ADR-012](docs/decisions/adr-012-static-analysis.md) | Static Analysis | cppcheck integration for generated C |
711
+ | [ADR-013](docs/decisions/adr-013-const-qualifier.md) | Const Qualifier | Compile-time const enforcement |
712
+ | [ADR-014](docs/decisions/adr-014-structs.md) | Structs | Data containers without methods |
713
+ | [ADR-015](docs/decisions/adr-015-null-state.md) | Null State | Zero initialization for all variables |
714
+ | [ADR-016](docs/decisions/adr-016-scope.md) | Scope | `this.`/`global.` explicit qualification |
715
+ | [ADR-017](docs/decisions/adr-017-enums.md) | Enums | Type-safe enums with C-style casting |
716
+ | [ADR-030](docs/decisions/adr-030-forward-declarations.md) | Define-Before-Use | Functions must be defined before called |
717
+ | [ADR-037](docs/decisions/adr-037-preprocessor.md) | Preprocessor | Flag-only defines, const for values |
718
+ | [ADR-043](docs/decisions/adr-043-comments.md) | Comments | Comment preservation with MISRA compliance |
719
+ | [ADR-044](docs/decisions/adr-044-primitive-types.md) | Primitive Types | Fixed-width types with `clamp`/`wrap` overflow |
720
+ | [ADR-024](docs/decisions/adr-024-type-casting.md) | Type Casting | Widening implicit, narrowing uses bit indexing |
721
+ | [ADR-022](docs/decisions/adr-022-conditional-expressions.md) | Conditional Expressions | Ternary with required parens, boolean condition, no nesting |
722
+ | [ADR-025](docs/decisions/adr-025-switch-statements.md) | Switch Statements | Safe switch with braces, `\|\|` syntax, counted `default(n)` |
723
+ | [ADR-029](docs/decisions/adr-029-function-pointers.md) | Callbacks | Function-as-Type pattern with nominal typing |
724
+ | [ADR-045](docs/decisions/adr-045-string-type.md) | Bounded Strings | `string<N>` with compile-time safety |
725
+ | [ADR-023](docs/decisions/adr-023-sizeof.md) | Sizeof | Type/value size queries with safety checks |
726
+ | [ADR-027](docs/decisions/adr-027-do-while.md) | Do-While | `do { } while ()` with boolean condition (E0701) |
727
+ | [ADR-032](docs/decisions/adr-032-nested-structs.md) | Nested Structs | Named nested structs only (no anonymous) |
728
+ | [ADR-035](docs/decisions/adr-035-array-initializers.md) | Array Initializers | `[1, 2, 3]` syntax with `[0*]` fill-all |
729
+ | [ADR-036](docs/decisions/adr-036-multidimensional-arrays.md) | Multi-dim Arrays | `arr[i][j]` with compile-time bounds enforcement |
730
+ | [ADR-040](docs/decisions/adr-040-isr-declaration.md) | ISR Type | Built-in `ISR` type for `void(void)` function pointers |
731
+ | [ADR-034](docs/decisions/adr-034-bit-fields.md) | Bitmap Types | `bitmap8`/`bitmap16`/`bitmap32` for portable bit-packed data |
732
+ | [ADR-048](docs/decisions/adr-048-cli-executable.md) | CLI Executable | `cnext` command with smart defaults |
733
+ | [ADR-049](docs/decisions/adr-049-atomic-types.md) | Atomic Types | `atomic` keyword with LDREX/STREX or PRIMASK fallback |
734
+ | [ADR-050](docs/decisions/adr-050-critical-sections.md) | Critical Sections | `critical { }` blocks with PRIMASK save/restore |
735
+ | [ADR-108](docs/decisions/adr-108-volatile-keyword.md) | Volatile Variables | `volatile` keyword prevents compiler optimization |
736
+ | [ADR-046](docs/decisions/adr-046-nullable-c-interop.md) | Nullable C Interop | `c_` prefix for nullable C pointer types |
737
+ | [ADR-053](docs/decisions/adr-053-transpiler-pipeline-architecture.md) | Transpiler Pipeline | Unified multi-pass pipeline with header symbol extraction |
738
+ | [ADR-057](docs/decisions/adr-057-implicit-scope-resolution.md) | Implicit Scope Resolution | Bare identifiers resolve local scope → global |
739
+
740
+ ### Accepted
741
+
742
+ | ADR | Title | Description |
743
+ | -------------------------------------------------------------------- | --------------------- | ----------------------------------------------------- |
744
+ | [ADR-051](docs/decisions/adr-051-division-by-zero.md) | Division by Zero | Compile-time and runtime division-by-zero detection |
745
+ | [ADR-052](docs/decisions/adr-052-safe-numeric-literal-generation.md) | Safe Numeric Literals | `type_MIN`/`type_MAX` constants + safe hex conversion |
746
+
747
+ ### Superseded
748
+
749
+ | ADR | Title | Description |
750
+ | --------------------------------------------------- | ------------------ | ----------------------------------------------------------- |
751
+ | [ADR-047](docs/decisions/adr-047-nullable-types.md) | NULL for C Interop | `NULL` keyword for C stream functions (replaced by ADR-046) |
735
752
 
736
753
  ### Research (v1 Roadmap)
737
754
 
738
- | ADR | Title | Description |
739
- | ------------------------------------------------------------ | ----------------------------- | --------------------------------------- |
740
- | [ADR-008](docs/decisions/adr-008-language-bug-prevention.md) | Language-Level Bug Prevention | Top 15 embedded bugs and prevention |
741
- | [ADR-009](docs/decisions/adr-009-isr-safety.md) | ISR Safety | Safe interrupts without `unsafe` blocks |
755
+ | ADR | Title | Description |
756
+ | ---------------------------------------------------------------- | ----------------------------- | --------------------------------------------------- |
757
+ | [ADR-008](docs/decisions/adr-008-language-bug-prevention.md) | Language-Level Bug Prevention | Top 15 embedded bugs and prevention |
758
+ | [ADR-009](docs/decisions/adr-009-isr-safety.md) | ISR Safety | Safe interrupts without `unsafe` blocks |
759
+ | [ADR-054](docs/decisions/adr-054-array-index-overflow.md) | Array Index Overflow | Overflow semantics for array index expressions |
760
+ | [ADR-055](docs/decisions/adr-055-symbol-parser-architecture.md) | Symbol Parser Architecture | Unified symbol resolution design |
761
+ | [ADR-056](docs/decisions/adr-056-cast-overflow-behavior.md) | Cast Overflow Behavior | Consistent overflow semantics for type casts |
762
+ | [ADR-060](docs/decisions/adr-060-vscode-extension-separation.md) | VS Code Extension Separation | Separate repository for VS Code extension |
763
+ | [ADR-058](docs/decisions/adr-058-explicit-length-properties.md) | Explicit Length Properties | Replace `.length` with `.bit_length`/`.byte_length` |
764
+ | [ADR-109](docs/decisions/adr-109-codegenerator-decomposition.md) | CodeGenerator Decomposition | Breaking down CodeGenerator into modules |
742
765
 
743
766
  ### Research (v2 Roadmap)
744
767
 
package/grammar/CNext.g4 CHANGED
@@ -248,14 +248,11 @@ assignmentOperator
248
248
  ;
249
249
 
250
250
  // Assignment target with unified postfix chain approach (Issue #387)
251
- // global/this prefixes use postfixTargetOp* for any combination of member/array access
252
- // Bare identifiers fall back to existing memberAccess/arrayAccess rules for compatibility
251
+ // All patterns use postfixTargetOp* for member/array access chains
253
252
  assignmentTarget
254
253
  : 'global' '.' IDENTIFIER postfixTargetOp* // global.x, global.x[i][j].y, etc.
255
254
  | 'this' '.' IDENTIFIER postfixTargetOp* // this.x, this.x[i].y, etc.
256
- | arrayAccess // arr[i], reg[start, width]
257
- | memberAccess // GPIO7.DR_SET, arr[i].field
258
- | IDENTIFIER // Simple identifier
255
+ | IDENTIFIER postfixTargetOp* // x, arr[i], obj.field, arr[i].field, etc.
259
256
  ;
260
257
 
261
258
  // Unified postfix operation for assignment targets (Issue #387)
@@ -459,18 +456,7 @@ arrayInitializerElement
459
456
  | arrayInitializer // For nested arrays: [[1,2], [3,4]]
460
457
  ;
461
458
 
462
- memberAccess
463
- : IDENTIFIER ('.' IDENTIFIER)+ ('[' expression ']')+ // ADR-036: screen.pixels[0][0]
464
- | IDENTIFIER ('.' IDENTIFIER)+ '[' expression ',' expression ']' // GPIO7.DR[start, width]
465
- | IDENTIFIER ('.' IDENTIFIER)+ // GPIO7.DR_SET
466
- | IDENTIFIER ('[' expression ']')+ ('.' IDENTIFIER)+ // arr[i].field1.field2...
467
- | IDENTIFIER (('[' expression ']') | ('.' IDENTIFIER))+ // arr[i].field[j].member... (any mix)
468
- ;
469
-
470
- arrayAccess
471
- : IDENTIFIER '[' expression ']' // Single element/bit
472
- | IDENTIFIER '[' expression ',' expression ']' // Bit range [start, width]
473
- ;
459
+ // Note: memberAccess and arrayAccess rules removed - unified into assignmentTarget with postfixTargetOp*
474
460
 
475
461
  argumentList
476
462
  : expression (',' expression)*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c-next",
3
- "version": "0.1.61",
3
+ "version": "0.1.63",
4
4
  "description": "A safer C for embedded systems development. Transpiles to clean, readable C.",
5
5
  "packageManager": "npm@11.9.0",
6
6
  "type": "module",
@@ -35,6 +35,14 @@ interface IMethodResult {
35
35
  errorMessage?: string;
36
36
  }
37
37
 
38
+ /**
39
+ * Result of validating and extracting source parameters.
40
+ */
41
+ interface ISourceParams {
42
+ source: string;
43
+ filePath: string | undefined;
44
+ }
45
+
38
46
  /**
39
47
  * Options for the serve command
40
48
  */
@@ -58,9 +66,15 @@ class ServeCommand {
58
66
  private static readonly methods: Record<string, MethodHandler> = {
59
67
  getVersion: ServeCommand.handleGetVersion,
60
68
  initialize: ServeCommand.handleInitialize,
61
- transpile: ServeCommand.handleTranspile,
62
- parseSymbols: ServeCommand.handleParseSymbols,
63
- parseCHeader: ServeCommand.handleParseCHeader,
69
+ transpile: ServeCommand._withSourceValidation(
70
+ ServeCommand._handleTranspile,
71
+ ),
72
+ parseSymbols: ServeCommand._withSourceValidation(
73
+ ServeCommand._handleParseSymbols,
74
+ ),
75
+ parseCHeader: ServeCommand._withSourceValidation(
76
+ ServeCommand._handleParseCHeader,
77
+ ),
64
78
  shutdown: ServeCommand.handleShutdown,
65
79
  };
66
80
 
@@ -104,6 +118,34 @@ class ServeCommand {
104
118
  }
105
119
  }
106
120
 
121
+ // ========================================================================
122
+ // Parameter Validation Helpers (Issue #707: Reduce code duplication)
123
+ // ========================================================================
124
+
125
+ /**
126
+ * Wrapper that validates source params before calling handler.
127
+ * Eliminates duplicate validation code across handlers.
128
+ */
129
+ private static _withSourceValidation(
130
+ handler: (params: ISourceParams) => Promise<IMethodResult>,
131
+ ): MethodHandler {
132
+ return async (params?: Record<string, unknown>): Promise<IMethodResult> => {
133
+ if (!params || typeof params.source !== "string") {
134
+ return {
135
+ success: false,
136
+ errorCode: JsonRpcHandler.ERROR_INVALID_PARAMS,
137
+ errorMessage: "Missing required param: source",
138
+ };
139
+ }
140
+ const validated: ISourceParams = {
141
+ source: String(params.source),
142
+ filePath:
143
+ typeof params.filePath === "string" ? params.filePath : undefined,
144
+ };
145
+ return handler(validated);
146
+ };
147
+ }
148
+
107
149
  /**
108
150
  * Handle a single line of input
109
151
  */
@@ -224,20 +266,12 @@ class ServeCommand {
224
266
  }
225
267
 
226
268
  /**
227
- * Handle transpile method
269
+ * Handle transpile method (called via _withSourceValidation wrapper)
228
270
  * Uses full Transpiler for include resolution and C++ auto-detection
229
271
  */
230
- private static async handleTranspile(
231
- params?: Record<string, unknown>,
272
+ private static async _handleTranspile(
273
+ params: ISourceParams,
232
274
  ): Promise<IMethodResult> {
233
- if (!params || typeof params.source !== "string") {
234
- return {
235
- success: false,
236
- errorCode: JsonRpcHandler.ERROR_INVALID_PARAMS,
237
- errorMessage: "Missing required param: source",
238
- };
239
- }
240
-
241
275
  if (!ServeCommand.transpiler) {
242
276
  return {
243
277
  success: false,
@@ -246,9 +280,7 @@ class ServeCommand {
246
280
  };
247
281
  }
248
282
 
249
- const source = String(params.source);
250
- const filePath =
251
- typeof params.filePath === "string" ? params.filePath : undefined;
283
+ const { source, filePath } = params;
252
284
 
253
285
  const options = filePath
254
286
  ? { workingDir: dirname(filePath), sourcePath: filePath }
@@ -271,24 +303,14 @@ class ServeCommand {
271
303
  }
272
304
 
273
305
  /**
274
- * Handle parseSymbols method
306
+ * Handle parseSymbols method (called via _withSourceValidation wrapper)
275
307
  * Runs full transpilation for include/C++ detection, then extracts symbols
276
308
  * from the parse tree (preserving "extract symbols even with parse errors" behavior)
277
309
  */
278
- private static async handleParseSymbols(
279
- params?: Record<string, unknown>,
310
+ private static async _handleParseSymbols(
311
+ params: ISourceParams,
280
312
  ): Promise<IMethodResult> {
281
- if (!params || typeof params.source !== "string") {
282
- return {
283
- success: false,
284
- errorCode: JsonRpcHandler.ERROR_INVALID_PARAMS,
285
- errorMessage: "Missing required param: source",
286
- };
287
- }
288
-
289
- const source = String(params.source);
290
- const filePath =
291
- typeof params.filePath === "string" ? params.filePath : undefined;
313
+ const { source, filePath } = params;
292
314
 
293
315
  // If transpiler is initialized, run transpileSource to trigger header
294
316
  // resolution and C++ detection (results are discarded, we just want
@@ -314,23 +336,13 @@ class ServeCommand {
314
336
  }
315
337
 
316
338
  /**
317
- * Handle parseCHeader method
339
+ * Handle parseCHeader method (called via _withSourceValidation wrapper)
318
340
  * Parses C/C++ header files and extracts symbols
319
341
  */
320
- private static async handleParseCHeader(
321
- params?: Record<string, unknown>,
342
+ private static async _handleParseCHeader(
343
+ params: ISourceParams,
322
344
  ): Promise<IMethodResult> {
323
- if (!params || typeof params.source !== "string") {
324
- return {
325
- success: false,
326
- errorCode: JsonRpcHandler.ERROR_INVALID_PARAMS,
327
- errorMessage: "Missing required param: source",
328
- };
329
- }
330
-
331
- const source = String(params.source);
332
- const filePath =
333
- typeof params.filePath === "string" ? params.filePath : undefined;
345
+ const { source, filePath } = params;
334
346
 
335
347
  const result = parseCHeader(source, filePath);
336
348
 
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Unit tests for parseCHeader edge cases requiring mocks
3
+ * Separate file to avoid mock pollution with main test file
4
+ */
5
+
6
+ import { describe, it, expect, vi, beforeEach } from "vitest";
7
+ import ESymbolKind from "../../utils/types/ESymbolKind";
8
+
9
+ // Create mock collect function that we can control per test
10
+ const mockCollect = vi.fn();
11
+
12
+ // Mock CSymbolCollector before importing parseCHeader
13
+ vi.mock("../../transpiler/logic/symbols/CSymbolCollector", () => {
14
+ return {
15
+ default: class MockCSymbolCollector {
16
+ collect() {
17
+ return mockCollect();
18
+ }
19
+ },
20
+ };
21
+ });
22
+
23
+ // Import after mock is set up
24
+ import parseCHeader from "../parseCHeader";
25
+
26
+ describe("parseCHeader mocked scenarios", () => {
27
+ beforeEach(() => {
28
+ mockCollect.mockReset();
29
+ // Default to returning empty array
30
+ mockCollect.mockReturnValue([]);
31
+ });
32
+
33
+ describe("mapSymbolKind default case", () => {
34
+ it("maps Namespace kind to variable (default case)", () => {
35
+ // Return a symbol with Namespace kind (not explicitly handled in switch)
36
+ mockCollect.mockReturnValue([
37
+ {
38
+ name: "TestNamespace",
39
+ kind: ESymbolKind.Namespace,
40
+ type: undefined,
41
+ parent: undefined,
42
+ sourceLine: 1,
43
+ },
44
+ ]);
45
+
46
+ const result = parseCHeader("// any valid C");
47
+
48
+ expect(result.success).toBe(true);
49
+ expect(result.symbols).toHaveLength(1);
50
+ expect(result.symbols[0].kind).toBe("variable");
51
+ expect(result.symbols[0].name).toBe("TestNamespace");
52
+ });
53
+
54
+ it("maps Class kind to variable (default case)", () => {
55
+ mockCollect.mockReturnValue([
56
+ {
57
+ name: "TestClass",
58
+ kind: ESymbolKind.Class,
59
+ type: "class",
60
+ parent: undefined,
61
+ sourceLine: 5,
62
+ },
63
+ ]);
64
+
65
+ const result = parseCHeader("// any valid C");
66
+
67
+ expect(result.success).toBe(true);
68
+ expect(result.symbols[0].kind).toBe("variable");
69
+ });
70
+
71
+ it("maps Bitmap kind to variable (default case)", () => {
72
+ mockCollect.mockReturnValue([
73
+ {
74
+ name: "TestBitmap",
75
+ kind: ESymbolKind.Bitmap,
76
+ type: undefined,
77
+ parent: undefined,
78
+ sourceLine: 1,
79
+ },
80
+ ]);
81
+
82
+ const result = parseCHeader("// any valid C");
83
+
84
+ expect(result.success).toBe(true);
85
+ expect(result.symbols[0].kind).toBe("variable");
86
+ });
87
+
88
+ it("defaults line to 0 when sourceLine is undefined", () => {
89
+ mockCollect.mockReturnValue([
90
+ {
91
+ name: "NoLineSymbol",
92
+ kind: ESymbolKind.Function,
93
+ type: "void",
94
+ parent: undefined,
95
+ sourceLine: undefined, // No source line info
96
+ },
97
+ ]);
98
+
99
+ const result = parseCHeader("// any valid C");
100
+
101
+ expect(result.success).toBe(true);
102
+ expect(result.symbols[0].line).toBe(0);
103
+ });
104
+ });
105
+
106
+ describe("error handling catch block", () => {
107
+ it("returns error result when collector throws Error", () => {
108
+ mockCollect.mockImplementation(() => {
109
+ throw new Error("Collector failed");
110
+ });
111
+
112
+ const result = parseCHeader("int x;");
113
+
114
+ expect(result.success).toBe(false);
115
+ expect(result.symbols).toHaveLength(0);
116
+ expect(result.errors).toHaveLength(1);
117
+ expect(result.errors[0].message).toBe("Collector failed");
118
+ expect(result.errors[0].severity).toBe("error");
119
+ expect(result.errors[0].line).toBe(1);
120
+ expect(result.errors[0].column).toBe(0);
121
+ });
122
+
123
+ it("handles non-Error exceptions (string throw)", () => {
124
+ mockCollect.mockImplementation(() => {
125
+ throw "String error message";
126
+ });
127
+
128
+ const result = parseCHeader("int x;");
129
+
130
+ expect(result.success).toBe(false);
131
+ expect(result.errors[0].message).toBe("String error message");
132
+ });
133
+
134
+ it("handles non-Error exceptions (number throw)", () => {
135
+ mockCollect.mockImplementation(() => {
136
+ throw 42;
137
+ });
138
+
139
+ const result = parseCHeader("int x;");
140
+
141
+ expect(result.success).toBe(false);
142
+ expect(result.errors[0].message).toBe("42");
143
+ });
144
+ });
145
+ });