c-next 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +726 -0
- package/bin/cnext.js +5 -0
- package/grammar/C.g4 +1112 -0
- package/grammar/CNext.g4 +817 -0
- package/grammar/CPP14Lexer.g4 +282 -0
- package/grammar/CPP14Parser.g4 +1072 -0
- package/package.json +85 -0
- package/src/analysis/DivisionByZeroAnalyzer.ts +378 -0
- package/src/analysis/FunctionCallAnalyzer.ts +526 -0
- package/src/analysis/InitializationAnalyzer.ts +725 -0
- package/src/analysis/NullCheckAnalyzer.ts +427 -0
- package/src/analysis/types/IDivisionByZeroError.ts +25 -0
- package/src/analysis/types/IFunctionCallError.ts +17 -0
- package/src/analysis/types/IInitializationError.ts +55 -0
- package/src/analysis/types/INullCheckError.ts +25 -0
- package/src/codegen/CodeGenerator.ts +7945 -0
- package/src/codegen/CommentExtractor.ts +240 -0
- package/src/codegen/CommentFormatter.ts +155 -0
- package/src/codegen/HeaderGenerator.ts +265 -0
- package/src/codegen/TypeResolver.ts +365 -0
- package/src/codegen/types/ECommentType.ts +10 -0
- package/src/codegen/types/IComment.ts +21 -0
- package/src/codegen/types/ICommentError.ts +15 -0
- package/src/codegen/types/TOverflowBehavior.ts +6 -0
- package/src/codegen/types/TParameterInfo.ts +13 -0
- package/src/codegen/types/TTypeConstants.ts +94 -0
- package/src/codegen/types/TTypeInfo.ts +22 -0
- package/src/index.ts +518 -0
- package/src/lib/IncludeDiscovery.ts +131 -0
- package/src/lib/InputExpansion.ts +121 -0
- package/src/lib/PlatformIODetector.ts +162 -0
- package/src/lib/transpiler.ts +439 -0
- package/src/lib/types/ITranspileResult.ts +80 -0
- package/src/parser/c/grammar/C.interp +338 -0
- package/src/parser/c/grammar/C.tokens +229 -0
- package/src/parser/c/grammar/CLexer.interp +415 -0
- package/src/parser/c/grammar/CLexer.tokens +229 -0
- package/src/parser/c/grammar/CLexer.ts +750 -0
- package/src/parser/c/grammar/CListener.ts +976 -0
- package/src/parser/c/grammar/CParser.ts +9663 -0
- package/src/parser/c/grammar/CVisitor.ts +626 -0
- package/src/parser/cpp/grammar/CPP14Lexer.interp +478 -0
- package/src/parser/cpp/grammar/CPP14Lexer.tokens +264 -0
- package/src/parser/cpp/grammar/CPP14Lexer.ts +848 -0
- package/src/parser/cpp/grammar/CPP14Parser.interp +492 -0
- package/src/parser/cpp/grammar/CPP14Parser.tokens +264 -0
- package/src/parser/cpp/grammar/CPP14Parser.ts +19961 -0
- package/src/parser/cpp/grammar/CPP14ParserListener.ts +2120 -0
- package/src/parser/cpp/grammar/CPP14ParserVisitor.ts +1354 -0
- package/src/parser/grammar/CNext.interp +340 -0
- package/src/parser/grammar/CNext.tokens +214 -0
- package/src/parser/grammar/CNextLexer.interp +374 -0
- package/src/parser/grammar/CNextLexer.tokens +214 -0
- package/src/parser/grammar/CNextLexer.ts +668 -0
- package/src/parser/grammar/CNextListener.ts +1020 -0
- package/src/parser/grammar/CNextParser.ts +9239 -0
- package/src/parser/grammar/CNextVisitor.ts +654 -0
- package/src/preprocessor/Preprocessor.ts +301 -0
- package/src/preprocessor/ToolchainDetector.ts +225 -0
- package/src/preprocessor/types/IPreprocessResult.ts +39 -0
- package/src/preprocessor/types/IToolchain.ts +27 -0
- package/src/project/FileDiscovery.ts +236 -0
- package/src/project/Project.ts +425 -0
- package/src/project/types/IProjectConfig.ts +64 -0
- package/src/symbols/CNextSymbolCollector.ts +326 -0
- package/src/symbols/CSymbolCollector.ts +457 -0
- package/src/symbols/CppSymbolCollector.ts +362 -0
- package/src/symbols/SymbolTable.ts +312 -0
- package/src/symbols/types/IConflict.ts +20 -0
- package/src/types/ESourceLanguage.ts +10 -0
- package/src/types/ESymbolKind.ts +20 -0
- package/src/types/ISymbol.ts +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
# C-Next
|
|
2
|
+
|
|
3
|
+
A safer C for embedded systems development. Transpiles to clean, readable C.
|
|
4
|
+
|
|
5
|
+
**Status: Working Transpiler** — Verified on Teensy MicroMod hardware.
|
|
6
|
+
|
|
7
|
+
## Quick Example
|
|
8
|
+
|
|
9
|
+
```cnx
|
|
10
|
+
// Register binding with type-safe access
|
|
11
|
+
register GPIO7 @ 0x42004000 {
|
|
12
|
+
DR: u32 rw @ 0x00,
|
|
13
|
+
DR_SET: u32 wo @ 0x84,
|
|
14
|
+
DR_TOGGLE: u32 wo @ 0x8C,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
u32 LED_BIT <- 3;
|
|
18
|
+
|
|
19
|
+
scope LED {
|
|
20
|
+
void toggle() {
|
|
21
|
+
// Type-aware bit indexing on write-only register
|
|
22
|
+
GPIO7.DR_TOGGLE[LED_BIT] <- true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Generates clean C:
|
|
28
|
+
|
|
29
|
+
```c
|
|
30
|
+
#define GPIO7_DR_TOGGLE (*(volatile uint32_t*)(0x42004000 + 0x8C))
|
|
31
|
+
|
|
32
|
+
uint32_t LED_BIT = 3;
|
|
33
|
+
|
|
34
|
+
void LED_toggle(void) {
|
|
35
|
+
GPIO7_DR_TOGGLE = (1 << LED_BIT);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
### From npm (Recommended)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm install -g c-next
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Verify the installation:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
cnext --version
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### From Source (Development)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git clone https://github.com/jlaustill/c-next.git
|
|
57
|
+
cd c-next
|
|
58
|
+
npm install
|
|
59
|
+
npm link
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Transpile to C (output alongside input file)
|
|
66
|
+
cnext examples/blink.cnx
|
|
67
|
+
|
|
68
|
+
# Explicit output path
|
|
69
|
+
cnext examples/blink.cnx -o blink.c
|
|
70
|
+
|
|
71
|
+
# Parse only (syntax check)
|
|
72
|
+
cnext examples/blink.cnx --parse
|
|
73
|
+
|
|
74
|
+
# Output as C++ (.cpp)
|
|
75
|
+
cnext examples/blink.cnx --cpp
|
|
76
|
+
|
|
77
|
+
# Target platform for atomic code generation (ADR-049)
|
|
78
|
+
cnext examples/blink.cnx --target teensy41
|
|
79
|
+
|
|
80
|
+
# Show all options
|
|
81
|
+
cnext --help
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Getting Started with PlatformIO
|
|
85
|
+
|
|
86
|
+
C-Next integrates seamlessly with PlatformIO embedded projects. The transpiler automatically converts `.cnx` files to `.c` before each build.
|
|
87
|
+
|
|
88
|
+
### Quick Setup
|
|
89
|
+
|
|
90
|
+
From your PlatformIO project root:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
cnext --pio-install
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
This command:
|
|
97
|
+
|
|
98
|
+
- Creates `cnext_build.py` (pre-build transpilation script)
|
|
99
|
+
- Modifies `platformio.ini` to add `extra_scripts = pre:cnext_build.py`
|
|
100
|
+
|
|
101
|
+
### Usage
|
|
102
|
+
|
|
103
|
+
1. **Create `.cnx` files in your `src/` directory** (alongside existing `.c`/`.cpp` files)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
src/
|
|
107
|
+
├── main.cpp # Existing C++ code
|
|
108
|
+
├── ConfigStorage.cnx # New c-next code
|
|
109
|
+
└── SensorProcessor.cnx # New c-next code
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
2. **Build as usual** — transpilation happens automatically:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pio run
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Output:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
Transpiling 2 c-next files...
|
|
122
|
+
✓ ConfigStorage.cnx
|
|
123
|
+
✓ SensorProcessor.cnx
|
|
124
|
+
Building...
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
3. **Commit both `.cnx` and generated `.c` files** to version control
|
|
128
|
+
|
|
129
|
+
### Why Commit Generated Files?
|
|
130
|
+
|
|
131
|
+
Generated `.c` files are **reviewable artifacts** in pull requests:
|
|
132
|
+
|
|
133
|
+
```diff
|
|
134
|
+
+ // ConfigStorage.cnx
|
|
135
|
+
+ u8 validate_config() {
|
|
136
|
+
+ counter +<- 1;
|
|
137
|
+
+ }
|
|
138
|
+
|
|
139
|
+
+ // ConfigStorage.c (generated)
|
|
140
|
+
+ uint8_t validate_config(void) {
|
|
141
|
+
+ counter = cnx_clamp_add_u8(counter, 1);
|
|
142
|
+
+ }
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Benefits**:
|
|
146
|
+
|
|
147
|
+
- See exactly what C code the transpiler generates
|
|
148
|
+
- Review safety features (overflow protection, atomic operations)
|
|
149
|
+
- Verify transpiler behavior
|
|
150
|
+
- Build succeeds even if transpiler isn't available
|
|
151
|
+
|
|
152
|
+
This follows the same pattern as TypeScript committing `.js` files or Bison committing generated parsers.
|
|
153
|
+
|
|
154
|
+
### Example Project Structure
|
|
155
|
+
|
|
156
|
+
```
|
|
157
|
+
my-teensy-project/
|
|
158
|
+
├── platformio.ini # PlatformIO config
|
|
159
|
+
├── cnext_build.py # Auto-generated transpilation script
|
|
160
|
+
├── src/
|
|
161
|
+
│ ├── main.cpp # C++ entry point
|
|
162
|
+
│ ├── ConfigStorage.cnx # c-next source
|
|
163
|
+
│ ├── ConfigStorage.c # Generated (committed)
|
|
164
|
+
│ ├── SensorProcessor.cnx # c-next source
|
|
165
|
+
│ └── SensorProcessor.c # Generated (committed)
|
|
166
|
+
└── include/
|
|
167
|
+
└── AppConfig.h # Shared types
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Uninstall
|
|
171
|
+
|
|
172
|
+
To remove c-next integration:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
cnext --pio-uninstall
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
This removes:
|
|
179
|
+
|
|
180
|
+
- `cnext_build.py` script
|
|
181
|
+
- `extra_scripts` reference from `platformio.ini`
|
|
182
|
+
|
|
183
|
+
Your `.cnx` files and generated `.c` files remain untouched.
|
|
184
|
+
|
|
185
|
+
### Manual Integration
|
|
186
|
+
|
|
187
|
+
If you prefer manual control, you can also run the transpiler explicitly:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
# Transpile all .cnx files in src/
|
|
191
|
+
cnext --project src
|
|
192
|
+
|
|
193
|
+
# Or transpile specific files
|
|
194
|
+
cnext src/ConfigStorage.cnx
|
|
195
|
+
cnext src/SensorProcessor.cnx
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Philosophy
|
|
199
|
+
|
|
200
|
+
C-Next follows the TypeScript model for adoption:
|
|
201
|
+
|
|
202
|
+
1. **Not all-or-nothing** — Drop a single `.cnx` file into an existing C project
|
|
203
|
+
2. **Clean escape hatch** — Generated C is idiomatic and maintainable
|
|
204
|
+
3. **Helpful, not burdensome** — If you know C, you can read C-Next immediately
|
|
205
|
+
|
|
206
|
+
### Core Principles
|
|
207
|
+
|
|
208
|
+
**KISS (Keep It Simple, Stupid)**
|
|
209
|
+
Every feature must pass the simplicity test: "Can a senior C developer read this cold and understand it in 30 seconds?" If not, it's too clever.
|
|
210
|
+
|
|
211
|
+
**DRY (Don't Repeat Yourself)**
|
|
212
|
+
Configuration belongs in one place. No magic numbers scattered through code. Named constants and register bindings enforce single sources of truth.
|
|
213
|
+
|
|
214
|
+
**Pragmatic, Not Dogmatic**
|
|
215
|
+
C-Next makes the right thing easy and the wrong thing hard, but doesn't prevent escape hatches. Generated C is always readable and maintainable.
|
|
216
|
+
|
|
217
|
+
### C Preprocessor Compatibility
|
|
218
|
+
|
|
219
|
+
C-Next uses the standard C preprocessor — no custom module system. This means:
|
|
220
|
+
|
|
221
|
+
- `#include` directives pass through to generated C
|
|
222
|
+
- Include C-Next files: `#include "utils.cnx"` → `#include "utils.h"` in generated C
|
|
223
|
+
- Works with both `<file.cnx>` and `"file.cnx"` syntax
|
|
224
|
+
- MISRA preprocessor guidelines apply
|
|
225
|
+
- Full compatibility with existing toolchains (PlatformIO, arm-gcc, etc.)
|
|
226
|
+
- Conditional compilation (`#ifdef`) works as expected
|
|
227
|
+
|
|
228
|
+
Generated headers automatically include guards:
|
|
229
|
+
|
|
230
|
+
```c
|
|
231
|
+
#ifndef MYFILE_H
|
|
232
|
+
#define MYFILE_H
|
|
233
|
+
// ...
|
|
234
|
+
#endif /* MYFILE_H */
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### The Simplicity Constraint
|
|
238
|
+
|
|
239
|
+
| Rust's Path | C-Next's Path |
|
|
240
|
+
| ---------------------------- | --------------------------------------- |
|
|
241
|
+
| Add concepts to catch errors | Remove the ability to make errors |
|
|
242
|
+
| Borrow checker complexity | Startup allocation = predictable memory |
|
|
243
|
+
| Lifetime annotations | Fixed runtime layout = clear lifetimes |
|
|
244
|
+
| `unsafe` escape hatch | Clean C is the escape hatch |
|
|
245
|
+
|
|
246
|
+
**Guiding Principle:** If Linus Torvalds wouldn't approve of the complexity, it doesn't ship. Safety through removal, not addition.
|
|
247
|
+
|
|
248
|
+
## Core Features
|
|
249
|
+
|
|
250
|
+
### Assignment: `<-` vs Equality: `=`
|
|
251
|
+
|
|
252
|
+
Eliminates the `if (x = 5)` bug by design:
|
|
253
|
+
|
|
254
|
+
```cnx
|
|
255
|
+
x <- 5; // assignment: value flows INTO x
|
|
256
|
+
if (x = 5) // comparison: single equals, just like math
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Fixed-Width Types
|
|
260
|
+
|
|
261
|
+
```cnx
|
|
262
|
+
u8, u16, u32, u64 // unsigned integers
|
|
263
|
+
i8, i16, i32, i64 // signed integers
|
|
264
|
+
f32, f64 // floating point
|
|
265
|
+
bool // boolean
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Register Bindings
|
|
269
|
+
|
|
270
|
+
Type-safe hardware access with access modifiers:
|
|
271
|
+
|
|
272
|
+
```cnx
|
|
273
|
+
register GPIO7 @ 0x42004000 {
|
|
274
|
+
DR: u32 rw @ 0x00, // Read-Write
|
|
275
|
+
PSR: u32 ro @ 0x08, // Read-Only
|
|
276
|
+
DR_SET: u32 wo @ 0x84, // Write-Only (atomic set)
|
|
277
|
+
DR_CLEAR: u32 wo @ 0x88, // Write-Only (atomic clear)
|
|
278
|
+
DR_TOGGLE: u32 wo @ 0x8C, // Write-Only (atomic toggle)
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Type-Aware Bit Indexing
|
|
283
|
+
|
|
284
|
+
Integers are indexable as bit arrays:
|
|
285
|
+
|
|
286
|
+
```cnx
|
|
287
|
+
u8 flags <- 0;
|
|
288
|
+
flags[3] <- true; // Set bit 3
|
|
289
|
+
flags[0, 3] <- 5; // Set 3 bits starting at bit 0
|
|
290
|
+
bool isSet <- flags[3]; // Read bit 3
|
|
291
|
+
|
|
292
|
+
// .length property
|
|
293
|
+
u8 buffer[16];
|
|
294
|
+
buffer.length; // 16 (array element count)
|
|
295
|
+
flags.length; // 8 (bit width of u8)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Write-only registers generate optimized code:
|
|
299
|
+
|
|
300
|
+
```cnx
|
|
301
|
+
GPIO7.DR_SET[LED_BIT] <- true; // Generates: GPIO7_DR_SET = (1 << LED_BIT);
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Scopes (ADR-016)
|
|
305
|
+
|
|
306
|
+
Organize code with automatic name prefixing. Inside scopes, explicit qualification is required:
|
|
307
|
+
|
|
308
|
+
- `this.X` for scope-local members
|
|
309
|
+
- `global.X` for global variables, functions, and registers
|
|
310
|
+
|
|
311
|
+
```cnx
|
|
312
|
+
const u8 LED_BIT <- 3;
|
|
313
|
+
|
|
314
|
+
scope LED {
|
|
315
|
+
u8 brightness <- 100;
|
|
316
|
+
|
|
317
|
+
void on() { global.GPIO7.DR_SET[global.LED_BIT] <- true; }
|
|
318
|
+
void off() { global.GPIO7.DR_CLEAR[global.LED_BIT] <- true; }
|
|
319
|
+
|
|
320
|
+
u8 getBrightness() { return this.brightness; }
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Call as:
|
|
324
|
+
LED.on();
|
|
325
|
+
LED.off();
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Transpiles to:
|
|
329
|
+
|
|
330
|
+
```c
|
|
331
|
+
const uint8_t LED_BIT = 3;
|
|
332
|
+
|
|
333
|
+
static uint8_t LED_brightness = 100;
|
|
334
|
+
|
|
335
|
+
void LED_on(void) { GPIO7_DR_SET = (1 << LED_BIT); }
|
|
336
|
+
void LED_off(void) { GPIO7_DR_CLEAR = (1 << LED_BIT); }
|
|
337
|
+
|
|
338
|
+
uint8_t LED_getBrightness(void) { return LED_brightness; }
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Switch Statements (ADR-025)
|
|
342
|
+
|
|
343
|
+
Safe switch with MISRA compliance:
|
|
344
|
+
|
|
345
|
+
- Braces replace break (no colons needed)
|
|
346
|
+
- No fallthrough allowed
|
|
347
|
+
- Multiple cases with `||` syntax
|
|
348
|
+
- Counted `default(n)` for enum exhaustiveness
|
|
349
|
+
|
|
350
|
+
```cnx
|
|
351
|
+
enum EState { IDLE, RUNNING, STOPPED }
|
|
352
|
+
|
|
353
|
+
void handleState(EState state) {
|
|
354
|
+
switch (state) {
|
|
355
|
+
case EState.IDLE {
|
|
356
|
+
startMotor();
|
|
357
|
+
}
|
|
358
|
+
case EState.RUNNING || EState.STOPPED {
|
|
359
|
+
checkSensors();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Transpiles to:
|
|
366
|
+
|
|
367
|
+
```c
|
|
368
|
+
switch (state) {
|
|
369
|
+
case EState_IDLE: {
|
|
370
|
+
startMotor();
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
case EState_RUNNING:
|
|
374
|
+
case EState_STOPPED: {
|
|
375
|
+
checkSensors();
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Ternary Operator (ADR-022)
|
|
382
|
+
|
|
383
|
+
Safe conditional expressions with MISRA compliance:
|
|
384
|
+
|
|
385
|
+
- Parentheses required around condition
|
|
386
|
+
- Condition must be boolean (comparison or logical)
|
|
387
|
+
- No nesting allowed (use if/else instead)
|
|
388
|
+
|
|
389
|
+
```cnx
|
|
390
|
+
u32 max <- (a > b) ? a : b;
|
|
391
|
+
u32 abs <- (x < 0) ? -x : x;
|
|
392
|
+
u32 result <- (a > 0 && b > 0) ? a : b;
|
|
393
|
+
|
|
394
|
+
// ERROR: Condition must be boolean
|
|
395
|
+
// u32 bad <- (x) ? 1 : 0;
|
|
396
|
+
|
|
397
|
+
// ERROR: Nested ternary not allowed
|
|
398
|
+
// i32 sign <- (x > 0) ? 1 : (x < 0) ? -1 : 0;
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Bounded Strings (ADR-045)
|
|
402
|
+
|
|
403
|
+
Safe, statically-allocated strings with compile-time capacity checking:
|
|
404
|
+
|
|
405
|
+
```cnx
|
|
406
|
+
string<64> name <- "Hello"; // 64-char capacity, transpiles to char[65]
|
|
407
|
+
string<128> message; // Empty string, initialized to ""
|
|
408
|
+
const string VERSION <- "1.0.0"; // Auto-sized to string<5>
|
|
409
|
+
|
|
410
|
+
// Properties
|
|
411
|
+
u32 len <- name.length; // Runtime: strlen(name)
|
|
412
|
+
u32 cap <- name.capacity; // Compile-time: 64
|
|
413
|
+
|
|
414
|
+
// Comparison - uses strcmp
|
|
415
|
+
if (name = "Hello") { } // strcmp(name, "Hello") == 0
|
|
416
|
+
|
|
417
|
+
// Concatenation with capacity validation
|
|
418
|
+
string<32> first <- "Hello";
|
|
419
|
+
string<32> second <- " World";
|
|
420
|
+
string<64> result <- first + second; // OK: 64 >= 32 + 32
|
|
421
|
+
|
|
422
|
+
// Substring extraction with bounds checking
|
|
423
|
+
string<5> greeting <- name[0, 5]; // First 5 chars
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
All operations are validated at compile time:
|
|
427
|
+
|
|
428
|
+
- Literal overflow → compile error
|
|
429
|
+
- Truncation on assignment → compile error
|
|
430
|
+
- Concatenation capacity mismatch → compile error
|
|
431
|
+
- Substring out of bounds → compile error
|
|
432
|
+
|
|
433
|
+
### Callbacks (ADR-029)
|
|
434
|
+
|
|
435
|
+
Type-safe function pointers with the Function-as-Type pattern:
|
|
436
|
+
|
|
437
|
+
- A function definition creates both a callable function AND a type
|
|
438
|
+
- Nominal typing: type identity is the function name, not just signature
|
|
439
|
+
- Never null: callbacks are always initialized to their default function
|
|
440
|
+
|
|
441
|
+
```cnx
|
|
442
|
+
// Define callback type with default behavior
|
|
443
|
+
void onReceive(const CAN_Message_T msg) {
|
|
444
|
+
// default: no-op
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
struct Controller {
|
|
448
|
+
onReceive _handler; // Type is onReceive, initialized to default
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// User implementation must match signature
|
|
452
|
+
void myHandler(const CAN_Message_T msg) {
|
|
453
|
+
Serial.println(msg.id);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
controller._handler <- myHandler; // OK: signature matches
|
|
457
|
+
controller._handler(msg); // Always safe - never null
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Transpiles to:
|
|
461
|
+
|
|
462
|
+
```c
|
|
463
|
+
void onReceive(const CAN_Message_T msg) { }
|
|
464
|
+
|
|
465
|
+
typedef void (*onReceive_fp)(const CAN_Message_T);
|
|
466
|
+
|
|
467
|
+
struct Controller {
|
|
468
|
+
onReceive_fp _handler;
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
// Initialization always sets to default
|
|
472
|
+
struct Controller Controller_init(void) {
|
|
473
|
+
return (struct Controller){ ._handler = onReceive };
|
|
474
|
+
}
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Atomic Variables (ADR-049)
|
|
478
|
+
|
|
479
|
+
ISR-safe variables with hardware-assisted atomicity:
|
|
480
|
+
|
|
481
|
+
```cnx
|
|
482
|
+
#pragma target teensy41
|
|
483
|
+
|
|
484
|
+
atomic u32 counter <- 0; // ISR-safe with LDREX/STREX
|
|
485
|
+
atomic clamp u8 brightness <- 100; // Combines atomic + clamp
|
|
486
|
+
|
|
487
|
+
void increment() {
|
|
488
|
+
counter +<- 1; // Lock-free atomic increment
|
|
489
|
+
}
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
Generates optimized code based on target platform:
|
|
493
|
+
|
|
494
|
+
- **Cortex-M3/M4/M7**: LDREX/STREX retry loops (lock-free)
|
|
495
|
+
- **Cortex-M0/M0+**: PRIMASK disable/restore (interrupt masking)
|
|
496
|
+
|
|
497
|
+
Target detection priority: `--target` CLI flag > `platformio.ini` > `#pragma target` > default
|
|
498
|
+
|
|
499
|
+
### Volatile Variables (ADR-108)
|
|
500
|
+
|
|
501
|
+
Prevent compiler optimization for variables that change outside normal program flow:
|
|
502
|
+
|
|
503
|
+
```cnx
|
|
504
|
+
// Delay loop - prevent optimization
|
|
505
|
+
void delay_ms(const u32 ms) {
|
|
506
|
+
volatile u32 i <- 0;
|
|
507
|
+
volatile u32 count <- ms * 2000;
|
|
508
|
+
|
|
509
|
+
while (i < count) {
|
|
510
|
+
i +<- 1; // Compiler cannot optimize away
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Hardware register - reads actual memory
|
|
515
|
+
volatile u32 status_register @ 0x40020000;
|
|
516
|
+
|
|
517
|
+
void waitReady() {
|
|
518
|
+
while (status_register & 0x01 = 0) {
|
|
519
|
+
// Always reads from hardware
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
**When to use:**
|
|
525
|
+
|
|
526
|
+
- ✅ Delay loops that must not be optimized away
|
|
527
|
+
- ✅ Memory-mapped hardware registers
|
|
528
|
+
- ✅ Variables polled in tight loops
|
|
529
|
+
- ❌ ISR-shared variables (use `atomic` instead for RMW safety)
|
|
530
|
+
|
|
531
|
+
**Key difference from `atomic`:**
|
|
532
|
+
|
|
533
|
+
- `volatile` = prevents optimization only
|
|
534
|
+
- `atomic` = prevents optimization + adds synchronization (ISR-safe)
|
|
535
|
+
|
|
536
|
+
### Critical Sections (ADR-050)
|
|
537
|
+
|
|
538
|
+
Multi-statement atomic blocks with automatic interrupt masking:
|
|
539
|
+
|
|
540
|
+
```cnx
|
|
541
|
+
u8 buffer[64];
|
|
542
|
+
u32 writeIdx <- 0;
|
|
543
|
+
|
|
544
|
+
void enqueue(u8 data) {
|
|
545
|
+
critical {
|
|
546
|
+
buffer[writeIdx] <- data;
|
|
547
|
+
writeIdx +<- 1;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
Transpiles to PRIMASK save/restore:
|
|
553
|
+
|
|
554
|
+
```c
|
|
555
|
+
void enqueue(uint8_t data) {
|
|
556
|
+
{
|
|
557
|
+
uint32_t __primask = __get_PRIMASK();
|
|
558
|
+
__disable_irq();
|
|
559
|
+
buffer[writeIdx] = data;
|
|
560
|
+
writeIdx += 1;
|
|
561
|
+
__set_PRIMASK(__primask);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
**Safety**: `return` inside `critical { }` is a compile error (E0853).
|
|
567
|
+
|
|
568
|
+
### NULL for C Library Interop (ADR-047)
|
|
569
|
+
|
|
570
|
+
Safe interop with C stream functions that can return NULL:
|
|
571
|
+
|
|
572
|
+
```cnx
|
|
573
|
+
#include <stdio.h>
|
|
574
|
+
|
|
575
|
+
string<64> buffer;
|
|
576
|
+
|
|
577
|
+
void readInput() {
|
|
578
|
+
// NULL check is REQUIRED - compiler enforces it
|
|
579
|
+
if (fgets(buffer, buffer.size, stdin) != NULL) {
|
|
580
|
+
printf("Got: %s", buffer);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
**Constraints:**
|
|
586
|
+
|
|
587
|
+
- NULL only valid in comparison context (`!= NULL` or `= NULL`)
|
|
588
|
+
- Only whitelisted stream functions: `fgets`, `fputs`, `fgetc`, `fputc`
|
|
589
|
+
- Cannot store C pointer returns in variables
|
|
590
|
+
- `fopen`, `malloc`, etc. are errors (see ADR-103 for future FILE\* support)
|
|
591
|
+
|
|
592
|
+
### Startup Allocation
|
|
593
|
+
|
|
594
|
+
Allocate at startup, run with fixed memory. Per MISRA C:2023 Dir 4.12: all memory is allocated during initialization, then forbidden. No runtime allocation means no fragmentation, no OOM, no leaks.
|
|
595
|
+
|
|
596
|
+
## Hardware Testing
|
|
597
|
+
|
|
598
|
+
Verified on **Teensy MicroMod** (NXP i.MX RT1062):
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
# Build and flash with PlatformIO
|
|
602
|
+
cd test-teensy
|
|
603
|
+
pio run -t upload
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
See `examples/blink.cnx` for the complete LED blink example.
|
|
607
|
+
|
|
608
|
+
## Project Structure
|
|
609
|
+
|
|
610
|
+
```
|
|
611
|
+
c-next/
|
|
612
|
+
├── grammar/CNext.g4 # ANTLR4 grammar definition
|
|
613
|
+
├── src/
|
|
614
|
+
│ ├── codegen/CodeGenerator.ts # Transpiler core
|
|
615
|
+
│ ├── parser/ # Generated ANTLR parser
|
|
616
|
+
│ └── index.ts # CLI entry point
|
|
617
|
+
├── examples/
|
|
618
|
+
│ ├── blink.cnx # LED blink (Teensy verified)
|
|
619
|
+
│ └── bit_test.cnx # Bit manipulation tests
|
|
620
|
+
├── test-teensy/ # PlatformIO test project
|
|
621
|
+
└── docs/decisions/ # Architecture Decision Records
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
## Architecture Decision Records
|
|
625
|
+
|
|
626
|
+
Decisions are documented in `/docs/decisions/`:
|
|
627
|
+
|
|
628
|
+
### Implemented
|
|
629
|
+
|
|
630
|
+
| ADR | Title | Description |
|
|
631
|
+
| ------------------------------------------------------------ | ----------------------- | ------------------------------------------------------------ |
|
|
632
|
+
| [ADR-001](docs/decisions/adr-001-assignment-operator.md) | Assignment Operator | `<-` for assignment, `=` for comparison |
|
|
633
|
+
| [ADR-003](docs/decisions/adr-003-static-allocation.md) | Static Allocation | No dynamic memory after init |
|
|
634
|
+
| [ADR-004](docs/decisions/adr-004-register-bindings.md) | Register Bindings | Type-safe hardware access |
|
|
635
|
+
| [ADR-006](docs/decisions/adr-006-simplified-references.md) | Simplified References | Pass by reference, no pointer syntax |
|
|
636
|
+
| [ADR-007](docs/decisions/adr-007-type-aware-bit-indexing.md) | Type-Aware Bit Indexing | Integers as bit arrays, `.length` property |
|
|
637
|
+
| [ADR-010](docs/decisions/adr-010-c-interoperability.md) | C Interoperability | Unified ANTLR parser architecture |
|
|
638
|
+
| [ADR-011](docs/decisions/adr-011-vscode-extension.md) | VS Code Extension | Live C preview with syntax highlighting |
|
|
639
|
+
| [ADR-012](docs/decisions/adr-012-static-analysis.md) | Static Analysis | cppcheck integration for generated C |
|
|
640
|
+
| [ADR-013](docs/decisions/adr-013-const-qualifier.md) | Const Qualifier | Compile-time const enforcement |
|
|
641
|
+
| [ADR-014](docs/decisions/adr-014-structs.md) | Structs | Data containers without methods |
|
|
642
|
+
| [ADR-015](docs/decisions/adr-015-null-state.md) | Null State | Zero initialization for all variables |
|
|
643
|
+
| [ADR-016](docs/decisions/adr-016-scope.md) | Scope | `this.`/`global.` explicit qualification |
|
|
644
|
+
| [ADR-017](docs/decisions/adr-017-enums.md) | Enums | Type-safe enums with C-style casting |
|
|
645
|
+
| [ADR-030](docs/decisions/adr-030-forward-declarations.md) | Define-Before-Use | Functions must be defined before called |
|
|
646
|
+
| [ADR-037](docs/decisions/adr-037-preprocessor.md) | Preprocessor | Flag-only defines, const for values |
|
|
647
|
+
| [ADR-043](docs/decisions/adr-043-comments.md) | Comments | Comment preservation with MISRA compliance |
|
|
648
|
+
| [ADR-044](docs/decisions/adr-044-primitive-types.md) | Primitive Types | Fixed-width types with `clamp`/`wrap` overflow |
|
|
649
|
+
| [ADR-024](docs/decisions/adr-024-type-casting.md) | Type Casting | Widening implicit, narrowing uses bit indexing |
|
|
650
|
+
| [ADR-022](docs/decisions/adr-022-conditional-expressions.md) | Conditional Expressions | Ternary with required parens, boolean condition, no nesting |
|
|
651
|
+
| [ADR-025](docs/decisions/adr-025-switch-statements.md) | Switch Statements | Safe switch with braces, `\|\|` syntax, counted `default(n)` |
|
|
652
|
+
| [ADR-029](docs/decisions/adr-029-function-pointers.md) | Callbacks | Function-as-Type pattern with nominal typing |
|
|
653
|
+
| [ADR-045](docs/decisions/adr-045-string-type.md) | Bounded Strings | `string<N>` with compile-time safety |
|
|
654
|
+
| [ADR-023](docs/decisions/adr-023-sizeof.md) | Sizeof | Type/value size queries with safety checks |
|
|
655
|
+
| [ADR-027](docs/decisions/adr-027-do-while.md) | Do-While | `do { } while ()` with boolean condition (E0701) |
|
|
656
|
+
| [ADR-032](docs/decisions/adr-032-nested-structs.md) | Nested Structs | Named nested structs only (no anonymous) |
|
|
657
|
+
| [ADR-035](docs/decisions/adr-035-array-initializers.md) | Array Initializers | `[1, 2, 3]` syntax with `[0*]` fill-all |
|
|
658
|
+
| [ADR-036](docs/decisions/adr-036-multidimensional-arrays.md) | Multi-dim Arrays | `arr[i][j]` with compile-time bounds enforcement |
|
|
659
|
+
| [ADR-040](docs/decisions/adr-040-isr-declaration.md) | ISR Type | Built-in `ISR` type for `void(void)` function pointers |
|
|
660
|
+
| [ADR-034](docs/decisions/adr-034-bit-fields.md) | Bitmap Types | `bitmap8`/`bitmap16`/`bitmap32` for portable bit-packed data |
|
|
661
|
+
| [ADR-048](docs/decisions/adr-048-cli-executable.md) | CLI Executable | `cnext` command with smart defaults |
|
|
662
|
+
| [ADR-049](docs/decisions/adr-049-atomic-types.md) | Atomic Types | `atomic` keyword with LDREX/STREX or PRIMASK fallback |
|
|
663
|
+
| [ADR-050](docs/decisions/adr-050-critical-sections.md) | Critical Sections | `critical { }` blocks with PRIMASK save/restore |
|
|
664
|
+
| [ADR-108](docs/decisions/adr-108-volatile-keyword.md) | Volatile Variables | `volatile` keyword prevents compiler optimization |
|
|
665
|
+
| [ADR-047](docs/decisions/adr-047-nullable-types.md) | NULL for C Interop | `NULL` keyword for C stream function comparisons |
|
|
666
|
+
|
|
667
|
+
### Research (v1 Roadmap)
|
|
668
|
+
|
|
669
|
+
| ADR | Title | Description |
|
|
670
|
+
| ------------------------------------------------------------ | ----------------------------- | --------------------------------------- |
|
|
671
|
+
| [ADR-008](docs/decisions/adr-008-language-bug-prevention.md) | Language-Level Bug Prevention | Top 15 embedded bugs and prevention |
|
|
672
|
+
| [ADR-009](docs/decisions/adr-009-isr-safety.md) | ISR Safety | Safe interrupts without `unsafe` blocks |
|
|
673
|
+
|
|
674
|
+
### Research (v2 Roadmap)
|
|
675
|
+
|
|
676
|
+
| ADR | Title | Description |
|
|
677
|
+
| --------------------------------------------------------------- | -------------------------- | --------------------------------------- |
|
|
678
|
+
| [ADR-100](docs/decisions/adr-100-multi-core-synchronization.md) | Multi-Core Synchronization | ESP32/RP2040 spinlock patterns |
|
|
679
|
+
| [ADR-101](docs/decisions/adr-101-heap-allocation.md) | Heap Allocation | Dynamic memory for desktop targets |
|
|
680
|
+
| [ADR-102](docs/decisions/adr-102-critical-section-analysis.md) | Critical Section Analysis | Complexity warnings and cycle analysis |
|
|
681
|
+
| [ADR-103](docs/decisions/adr-103-stream-handling.md) | Stream Handling | FILE\* and fopen patterns for file I/O |
|
|
682
|
+
| [ADR-104](docs/decisions/adr-104-isr-queues.md) | ISR-Safe Queues | Producer-consumer patterns for ISR/main |
|
|
683
|
+
| [ADR-105](docs/decisions/adr-105-prefixed-includes.md) | Prefixed Includes | Namespace control for includes |
|
|
684
|
+
| [ADR-106](docs/decisions/adr-106-isr-vector-bindings.md) | Vector Table Bindings | Register bindings for ISR vector tables |
|
|
685
|
+
|
|
686
|
+
### Rejected
|
|
687
|
+
|
|
688
|
+
| ADR | Title | Description |
|
|
689
|
+
| ---------------------------------------------------------------- | ------------------- | ----------------------------------------------------------------------- |
|
|
690
|
+
| [ADR-041](docs/decisions/adr-041-inline-assembly.md) | Inline Assembly | Write assembly in C files; C-Next transpiles to C anyway |
|
|
691
|
+
| [ADR-042](docs/decisions/adr-042-error-handling.md) | Error Handling | Works with existing features (enums, pass-by-reference, struct returns) |
|
|
692
|
+
| [ADR-039](docs/decisions/adr-039-null-safety.md) | Null Safety | Emergent from ADR-003 + ADR-006 + ADR-015; no additional feature needed |
|
|
693
|
+
| [ADR-020](docs/decisions/adr-020-size-type.md) | Size Type | Fixed-width types are more predictable than platform-sized |
|
|
694
|
+
| [ADR-019](docs/decisions/adr-019-type-aliases.md) | Type Aliases | Fixed-width primitives already solve the problem |
|
|
695
|
+
| [ADR-021](docs/decisions/adr-021-increment-decrement.md) | Increment/Decrement | Use `+<- 1` instead; separation of concerns |
|
|
696
|
+
| [ADR-002](docs/decisions/adr-002-namespaces.md) | Namespaces | Replaced by `scope` keyword (ADR-016) |
|
|
697
|
+
| [ADR-005](docs/decisions/adr-005-classes-without-inheritance.md) | Classes | Use structs + free functions instead (ADR-016) |
|
|
698
|
+
| [ADR-018](docs/decisions/adr-018-unions.md) | Unions | Use ADR-004 register bindings or explicit byte manipulation |
|
|
699
|
+
| [ADR-038](docs/decisions/adr-038-static-extern.md) | Static/Extern | Use `scope` for visibility; no `static` keyword in v1 |
|
|
700
|
+
| [ADR-026](docs/decisions/adr-026-break-continue.md) | Break/Continue | Use structured loop conditions instead |
|
|
701
|
+
| [ADR-028](docs/decisions/adr-028-goto.md) | Goto | Permanently rejected; use structured alternatives |
|
|
702
|
+
| [ADR-031](docs/decisions/adr-031-inline-functions.md) | Inline Functions | Trust compiler; `inline` is just a hint anyway |
|
|
703
|
+
| [ADR-033](docs/decisions/adr-033-packed-structs.md) | Packed Structs | Use ADR-004 register bindings or explicit serialization |
|
|
704
|
+
|
|
705
|
+
## Build Commands
|
|
706
|
+
|
|
707
|
+
```bash
|
|
708
|
+
npm run build # Full build: ANTLR + TypeScript
|
|
709
|
+
npm run antlr # Regenerate parser from grammar
|
|
710
|
+
npx tsc # TypeScript only
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
## Contributing
|
|
714
|
+
|
|
715
|
+
Ideas and feedback welcome via issues.
|
|
716
|
+
|
|
717
|
+
## License
|
|
718
|
+
|
|
719
|
+
MIT
|
|
720
|
+
|
|
721
|
+
## Acknowledgments
|
|
722
|
+
|
|
723
|
+
- The R community for proving `<-` works in practice
|
|
724
|
+
- MISRA C consortium for codifying embedded safety wisdom
|
|
725
|
+
- The TypeScript team for demonstrating gradual adoption works
|
|
726
|
+
- ANTLR for the parser infrastructure
|