c-next 0.1.1 → 0.1.2

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 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,14 +736,16 @@ 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 Commands
706
740
 
707
741
  ```bash
708
- npm run build # Full build: ANTLR + TypeScript
709
742
  npm run antlr # Regenerate parser from grammar
710
- npx tsc # TypeScript only
743
+ npm run typecheck # Type-check TypeScript (no build required)
744
+ npm test # Run all tests
711
745
  ```
712
746
 
747
+ **Note:** C-Next runs directly via `tsx` without a build step. The `typecheck` command validates types only and does not generate any output files.
748
+
713
749
  ## Contributing
714
750
 
715
751
  Ideas and feedback welcome via issues.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "c-next",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
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,6 +15,7 @@
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
+ "typecheck": "tsc --noEmit",
18
19
  "test": "tsx scripts/test.js",
19
20
  "test:cli": "node scripts/test-cli.js",
20
21
  "test:update": "tsx scripts/test.js --update",
@@ -4570,7 +4570,28 @@ export default class CodeGenerator {
4570
4570
  const isISRType = typeInfo?.baseType === "ISR";
4571
4571
 
4572
4572
  if (isActualArray || isISRType) {
4573
- // Normal array element assignment
4573
+ // Check for slice assignment: array[offset, length] <- value
4574
+ if (exprs.length === 2) {
4575
+ // Slice assignment - generate memcpy with bounds checking
4576
+ const offset = this.generateExpression(exprs[0]);
4577
+ const length = this.generateExpression(exprs[1]);
4578
+
4579
+ // Compound operators not supported for slice assignment
4580
+ if (cOp !== "=") {
4581
+ throw new Error(
4582
+ `Compound assignment operators not supported for slice assignment: ${cnextOp}`,
4583
+ );
4584
+ }
4585
+
4586
+ // Set flag to include string.h for memcpy
4587
+ this.needsString = true;
4588
+
4589
+ // Generate bounds-checked memcpy
4590
+ // if (offset + length <= sizeof(buffer)) { memcpy(&buffer[offset], &value, length); }
4591
+ return `if (${offset} + ${length} <= sizeof(${name})) { memcpy(&${name}[${offset}], &${value}, ${length}); }`;
4592
+ }
4593
+
4594
+ // Normal array element assignment (single index)
4574
4595
  const index = this.generateExpression(exprs[0]);
4575
4596
 
4576
4597
  // Check if this is a string array (e.g., string<64> arr[4])
@@ -6433,12 +6454,13 @@ export default class CodeGenerator {
6433
6454
  const isPrimaryArray = identifierTypeInfo?.isArray ?? false;
6434
6455
 
6435
6456
  // Determine if this subscript is array access or bit access
6436
- // Priority: register access > tracked member array > primary array > bit access
6457
+ // Priority: register access > tracked member array > primary array > primitive integer bit access > default array access
6437
6458
  if (isRegisterAccess) {
6438
6459
  // Register - use bit access: ((value >> index) & 1)
6439
6460
  result = `((${result} >> ${index}) & 1)`;
6440
6461
  } else if (currentMemberIsArray) {
6441
- // Struct member that is an array (e.g., buf.data[0])
6462
+ // Struct member that is an array (e.g., config.tempInputs[0])
6463
+ // This is the most reliable indicator - we tracked it through member access
6442
6464
  result = `${result}[${index}]`;
6443
6465
  currentMemberIsArray = false; // After subscript, no longer array
6444
6466
  isSubscripted = true; // Track for .length on element
@@ -6446,12 +6468,29 @@ export default class CodeGenerator {
6446
6468
  // Primary identifier is an array (e.g., arr[0] or global.arr[0])
6447
6469
  result = `${result}[${index}]`;
6448
6470
  isSubscripted = true; // Track for .length on element
6449
- } else if (identifierTypeInfo && !isPrimaryArray) {
6450
- // Non-array type (integer, etc.) - use bit access
6451
- result = `((${result} >> ${index}) & 1)`;
6471
+ // After subscripting an array, set currentStructType if the element is a struct
6472
+ if (identifierTypeInfo && !currentStructType) {
6473
+ const elementType = identifierTypeInfo.baseType;
6474
+ if (this.knownStructs.has(elementType)) {
6475
+ currentStructType = elementType;
6476
+ }
6477
+ }
6452
6478
  } else {
6453
- // Unknown type - default to array access
6454
- result = `${result}[${index}]`;
6479
+ // Check both currentStructType (for member access chains) and identifierTypeInfo (for simple variables)
6480
+ const typeToCheck =
6481
+ currentStructType || identifierTypeInfo?.baseType;
6482
+ const isPrimitiveInt =
6483
+ typeToCheck &&
6484
+ ["u8", "u16", "u32", "u64", "i8", "i16", "i32", "i64"].includes(
6485
+ typeToCheck,
6486
+ );
6487
+ if (isPrimitiveInt) {
6488
+ // Primitive integer - use bit access: ((value >> index) & 1)
6489
+ result = `((${result} >> ${index}) & 1)`;
6490
+ } else {
6491
+ // Non-primitive type or unknown - default to array access
6492
+ result = `${result}[${index}]`;
6493
+ }
6455
6494
  }
6456
6495
  } else if (exprs.length === 2) {
6457
6496
  // Bit range: flags[start, width]