c-next 0.1.0 → 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 +39 -3
- package/package.json +2 -1
- package/src/codegen/CodeGenerator.ts +54 -8
- package/src/codegen/types/ICallbackTypeInfo.ts +20 -0
- package/src/codegen/types/IFunctionSignature.ts +16 -0
- package/src/codegen/types/ITargetCapabilities.ts +12 -0
- package/src/codegen/types/TCodeGenContext.ts +41 -0
- package/src/codegen/types/index.ts +17 -0
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
|
-
##
|
|
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
|
-
|
|
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.
|
|
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",
|
|
@@ -898,6 +898,10 @@ export default class CodeGenerator {
|
|
|
898
898
|
const scopeDecl = decl.scopeDeclaration()!;
|
|
899
899
|
const scopeName = scopeDecl.IDENTIFIER().getText();
|
|
900
900
|
|
|
901
|
+
// Set currentScope so that this.Type references resolve correctly
|
|
902
|
+
const savedScope = this.context.currentScope;
|
|
903
|
+
this.context.currentScope = scopeName;
|
|
904
|
+
|
|
901
905
|
for (const member of scopeDecl.scopeMember()) {
|
|
902
906
|
if (member.variableDeclaration()) {
|
|
903
907
|
const varDecl = member.variableDeclaration()!;
|
|
@@ -907,6 +911,9 @@ export default class CodeGenerator {
|
|
|
907
911
|
this.trackVariableTypeWithName(varDecl, fullName);
|
|
908
912
|
}
|
|
909
913
|
}
|
|
914
|
+
|
|
915
|
+
// Restore previous scope
|
|
916
|
+
this.context.currentScope = savedScope;
|
|
910
917
|
}
|
|
911
918
|
|
|
912
919
|
// Note: Function parameters are registered per-function during generation
|
|
@@ -4563,7 +4570,28 @@ export default class CodeGenerator {
|
|
|
4563
4570
|
const isISRType = typeInfo?.baseType === "ISR";
|
|
4564
4571
|
|
|
4565
4572
|
if (isActualArray || isISRType) {
|
|
4566
|
-
//
|
|
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)
|
|
4567
4595
|
const index = this.generateExpression(exprs[0]);
|
|
4568
4596
|
|
|
4569
4597
|
// Check if this is a string array (e.g., string<64> arr[4])
|
|
@@ -6426,12 +6454,13 @@ export default class CodeGenerator {
|
|
|
6426
6454
|
const isPrimaryArray = identifierTypeInfo?.isArray ?? false;
|
|
6427
6455
|
|
|
6428
6456
|
// Determine if this subscript is array access or bit access
|
|
6429
|
-
// 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
|
|
6430
6458
|
if (isRegisterAccess) {
|
|
6431
6459
|
// Register - use bit access: ((value >> index) & 1)
|
|
6432
6460
|
result = `((${result} >> ${index}) & 1)`;
|
|
6433
6461
|
} else if (currentMemberIsArray) {
|
|
6434
|
-
// Struct member that is an array (e.g.,
|
|
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
|
|
6435
6464
|
result = `${result}[${index}]`;
|
|
6436
6465
|
currentMemberIsArray = false; // After subscript, no longer array
|
|
6437
6466
|
isSubscripted = true; // Track for .length on element
|
|
@@ -6439,12 +6468,29 @@ export default class CodeGenerator {
|
|
|
6439
6468
|
// Primary identifier is an array (e.g., arr[0] or global.arr[0])
|
|
6440
6469
|
result = `${result}[${index}]`;
|
|
6441
6470
|
isSubscripted = true; // Track for .length on element
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
|
|
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
|
+
}
|
|
6445
6478
|
} else {
|
|
6446
|
-
//
|
|
6447
|
-
|
|
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
|
+
}
|
|
6448
6494
|
}
|
|
6449
6495
|
} else if (exprs.length === 2) {
|
|
6450
6496
|
// Bit range: flags[start, width]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback type info for Function-as-Type pattern
|
|
3
|
+
* Each function definition creates both a callable function AND a type
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
interface ICallbackTypeInfo {
|
|
7
|
+
functionName: string; // The original function name (also the type name)
|
|
8
|
+
returnType: string; // Return type for typedef (C type)
|
|
9
|
+
parameters: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
type: string; // C type
|
|
12
|
+
isConst: boolean;
|
|
13
|
+
isPointer: boolean; // Non-array params become pointers
|
|
14
|
+
isArray: boolean; // Array parameters pass naturally as pointers
|
|
15
|
+
arrayDims: string; // Array dimensions if applicable
|
|
16
|
+
}>;
|
|
17
|
+
typedefName: string; // e.g., "onReceive_fp"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default ICallbackTypeInfo;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function signature for const parameter tracking
|
|
3
|
+
* Used to validate const-to-non-const errors at call sites
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
interface IFunctionSignature {
|
|
7
|
+
name: string;
|
|
8
|
+
parameters: Array<{
|
|
9
|
+
name: string;
|
|
10
|
+
baseType: string; // The C-Next type (e.g., 'u32', 'f32')
|
|
11
|
+
isConst: boolean;
|
|
12
|
+
isArray: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default IFunctionSignature;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Target platform capabilities for code generation
|
|
3
|
+
* Determines which atomic instructions and features are available
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
interface ITargetCapabilities {
|
|
7
|
+
wordSize: 8 | 16 | 32;
|
|
8
|
+
hasLdrexStrex: boolean;
|
|
9
|
+
hasBasepri: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default ITargetCapabilities;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code generation context - shared state across all codegen components
|
|
3
|
+
* Tracks current scope, variables, types, and generation state
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import TParameterInfo from "./TParameterInfo";
|
|
7
|
+
import TTypeInfo from "./TTypeInfo";
|
|
8
|
+
import TOverflowBehavior from "./TOverflowBehavior";
|
|
9
|
+
import ITargetCapabilities from "./ITargetCapabilities";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Assignment context for overflow behavior tracking
|
|
13
|
+
*/
|
|
14
|
+
export interface IAssignmentContext {
|
|
15
|
+
targetName: string | null;
|
|
16
|
+
targetType: string | null;
|
|
17
|
+
overflowBehavior: TOverflowBehavior;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Code generation context - mutable state passed to all codegen components
|
|
22
|
+
*/
|
|
23
|
+
type TCodeGenContext = {
|
|
24
|
+
currentScope: string | null;
|
|
25
|
+
indentLevel: number;
|
|
26
|
+
scopeMembers: Map<string, Set<string>>;
|
|
27
|
+
currentParameters: Map<string, TParameterInfo>;
|
|
28
|
+
localArrays: Set<string>;
|
|
29
|
+
localVariables: Set<string>;
|
|
30
|
+
inFunctionBody: boolean;
|
|
31
|
+
typeRegistry: Map<string, TTypeInfo>;
|
|
32
|
+
expectedType: string | null;
|
|
33
|
+
mainArgsName: string | null;
|
|
34
|
+
assignmentContext: IAssignmentContext;
|
|
35
|
+
lastArrayInitCount: number;
|
|
36
|
+
lastArrayFillValue: string | undefined;
|
|
37
|
+
lengthCache: Map<string, string> | null;
|
|
38
|
+
targetCapabilities: ITargetCapabilities;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default TCodeGenContext;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codegen types barrel export
|
|
3
|
+
* Central export point for all codegen type definitions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { default as ECommentType } from "./ECommentType";
|
|
7
|
+
export { default as IComment } from "./IComment";
|
|
8
|
+
export { default as ICommentError } from "./ICommentError";
|
|
9
|
+
export { default as TParameterInfo } from "./TParameterInfo";
|
|
10
|
+
export { default as TOverflowBehavior } from "./TOverflowBehavior";
|
|
11
|
+
export { default as TTypeInfo } from "./TTypeInfo";
|
|
12
|
+
export { default as TCodeGenContext } from "./TCodeGenContext";
|
|
13
|
+
export type { IAssignmentContext } from "./TCodeGenContext";
|
|
14
|
+
export { default as ITargetCapabilities } from "./ITargetCapabilities";
|
|
15
|
+
export { default as IFunctionSignature } from "./IFunctionSignature";
|
|
16
|
+
export { default as ICallbackTypeInfo } from "./ICallbackTypeInfo";
|
|
17
|
+
export * from "./TTypeConstants";
|