c-next 0.1.3 → 0.1.4
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 +37 -36
- package/grammar/CNext.g4 +2 -2
- package/package.json +1 -1
- package/src/codegen/CodeGenerator.ts +171 -49
- package/src/codegen/TypeResolver.ts +30 -3
- package/src/parser/grammar/CNext.interp +1 -1
- package/src/parser/grammar/CNextParser.ts +565 -538
package/README.md
CHANGED
|
@@ -661,42 +661,43 @@ Decisions are documented in `/docs/decisions/`:
|
|
|
661
661
|
|
|
662
662
|
### Implemented
|
|
663
663
|
|
|
664
|
-
| ADR
|
|
665
|
-
|
|
|
666
|
-
| [ADR-001](docs/decisions/adr-001-assignment-operator.md)
|
|
667
|
-
| [ADR-003](docs/decisions/adr-003-static-allocation.md)
|
|
668
|
-
| [ADR-004](docs/decisions/adr-004-register-bindings.md)
|
|
669
|
-
| [ADR-006](docs/decisions/adr-006-simplified-references.md)
|
|
670
|
-
| [ADR-007](docs/decisions/adr-007-type-aware-bit-indexing.md)
|
|
671
|
-
| [ADR-010](docs/decisions/adr-010-c-interoperability.md)
|
|
672
|
-
| [ADR-011](docs/decisions/adr-011-vscode-extension.md)
|
|
673
|
-
| [ADR-012](docs/decisions/adr-012-static-analysis.md)
|
|
674
|
-
| [ADR-013](docs/decisions/adr-013-const-qualifier.md)
|
|
675
|
-
| [ADR-014](docs/decisions/adr-014-structs.md)
|
|
676
|
-
| [ADR-015](docs/decisions/adr-015-null-state.md)
|
|
677
|
-
| [ADR-016](docs/decisions/adr-016-scope.md)
|
|
678
|
-
| [ADR-017](docs/decisions/adr-017-enums.md)
|
|
679
|
-
| [ADR-030](docs/decisions/adr-030-forward-declarations.md)
|
|
680
|
-
| [ADR-037](docs/decisions/adr-037-preprocessor.md)
|
|
681
|
-
| [ADR-043](docs/decisions/adr-043-comments.md)
|
|
682
|
-
| [ADR-044](docs/decisions/adr-044-primitive-types.md)
|
|
683
|
-
| [ADR-024](docs/decisions/adr-024-type-casting.md)
|
|
684
|
-
| [ADR-022](docs/decisions/adr-022-conditional-expressions.md)
|
|
685
|
-
| [ADR-025](docs/decisions/adr-025-switch-statements.md)
|
|
686
|
-
| [ADR-029](docs/decisions/adr-029-function-pointers.md)
|
|
687
|
-
| [ADR-045](docs/decisions/adr-045-string-type.md)
|
|
688
|
-
| [ADR-023](docs/decisions/adr-023-sizeof.md)
|
|
689
|
-
| [ADR-027](docs/decisions/adr-027-do-while.md)
|
|
690
|
-
| [ADR-032](docs/decisions/adr-032-nested-structs.md)
|
|
691
|
-
| [ADR-035](docs/decisions/adr-035-array-initializers.md)
|
|
692
|
-
| [ADR-036](docs/decisions/adr-036-multidimensional-arrays.md)
|
|
693
|
-
| [ADR-040](docs/decisions/adr-040-isr-declaration.md)
|
|
694
|
-
| [ADR-034](docs/decisions/adr-034-bit-fields.md)
|
|
695
|
-
| [ADR-048](docs/decisions/adr-048-cli-executable.md)
|
|
696
|
-
| [ADR-049](docs/decisions/adr-049-atomic-types.md)
|
|
697
|
-
| [ADR-050](docs/decisions/adr-050-critical-sections.md)
|
|
698
|
-
| [ADR-108](docs/decisions/adr-108-volatile-keyword.md)
|
|
699
|
-
| [ADR-047](docs/decisions/adr-047-nullable-types.md)
|
|
664
|
+
| ADR | Title | Description |
|
|
665
|
+
| -------------------------------------------------------------------- | ----------------------- | ------------------------------------------------------------ |
|
|
666
|
+
| [ADR-001](docs/decisions/adr-001-assignment-operator.md) | Assignment Operator | `<-` for assignment, `=` for comparison |
|
|
667
|
+
| [ADR-003](docs/decisions/adr-003-static-allocation.md) | Static Allocation | No dynamic memory after init |
|
|
668
|
+
| [ADR-004](docs/decisions/adr-004-register-bindings.md) | Register Bindings | Type-safe hardware access |
|
|
669
|
+
| [ADR-006](docs/decisions/adr-006-simplified-references.md) | Simplified References | Pass by reference, no pointer syntax |
|
|
670
|
+
| [ADR-007](docs/decisions/adr-007-type-aware-bit-indexing.md) | Type-Aware Bit Indexing | Integers as bit arrays, `.length` property |
|
|
671
|
+
| [ADR-010](docs/decisions/adr-010-c-interoperability.md) | C Interoperability | Unified ANTLR parser architecture |
|
|
672
|
+
| [ADR-011](docs/decisions/adr-011-vscode-extension.md) | VS Code Extension | Live C preview with syntax highlighting |
|
|
673
|
+
| [ADR-012](docs/decisions/adr-012-static-analysis.md) | Static Analysis | cppcheck integration for generated C |
|
|
674
|
+
| [ADR-013](docs/decisions/adr-013-const-qualifier.md) | Const Qualifier | Compile-time const enforcement |
|
|
675
|
+
| [ADR-014](docs/decisions/adr-014-structs.md) | Structs | Data containers without methods |
|
|
676
|
+
| [ADR-015](docs/decisions/adr-015-null-state.md) | Null State | Zero initialization for all variables |
|
|
677
|
+
| [ADR-016](docs/decisions/adr-016-scope.md) | Scope | `this.`/`global.` explicit qualification |
|
|
678
|
+
| [ADR-017](docs/decisions/adr-017-enums.md) | Enums | Type-safe enums with C-style casting |
|
|
679
|
+
| [ADR-030](docs/decisions/adr-030-forward-declarations.md) | Define-Before-Use | Functions must be defined before called |
|
|
680
|
+
| [ADR-037](docs/decisions/adr-037-preprocessor.md) | Preprocessor | Flag-only defines, const for values |
|
|
681
|
+
| [ADR-043](docs/decisions/adr-043-comments.md) | Comments | Comment preservation with MISRA compliance |
|
|
682
|
+
| [ADR-044](docs/decisions/adr-044-primitive-types.md) | Primitive Types | Fixed-width types with `clamp`/`wrap` overflow |
|
|
683
|
+
| [ADR-024](docs/decisions/adr-024-type-casting.md) | Type Casting | Widening implicit, narrowing uses bit indexing |
|
|
684
|
+
| [ADR-022](docs/decisions/adr-022-conditional-expressions.md) | Conditional Expressions | Ternary with required parens, boolean condition, no nesting |
|
|
685
|
+
| [ADR-025](docs/decisions/adr-025-switch-statements.md) | Switch Statements | Safe switch with braces, `\|\|` syntax, counted `default(n)` |
|
|
686
|
+
| [ADR-029](docs/decisions/adr-029-function-pointers.md) | Callbacks | Function-as-Type pattern with nominal typing |
|
|
687
|
+
| [ADR-045](docs/decisions/adr-045-string-type.md) | Bounded Strings | `string<N>` with compile-time safety |
|
|
688
|
+
| [ADR-023](docs/decisions/adr-023-sizeof.md) | Sizeof | Type/value size queries with safety checks |
|
|
689
|
+
| [ADR-027](docs/decisions/adr-027-do-while.md) | Do-While | `do { } while ()` with boolean condition (E0701) |
|
|
690
|
+
| [ADR-032](docs/decisions/adr-032-nested-structs.md) | Nested Structs | Named nested structs only (no anonymous) |
|
|
691
|
+
| [ADR-035](docs/decisions/adr-035-array-initializers.md) | Array Initializers | `[1, 2, 3]` syntax with `[0*]` fill-all |
|
|
692
|
+
| [ADR-036](docs/decisions/adr-036-multidimensional-arrays.md) | Multi-dim Arrays | `arr[i][j]` with compile-time bounds enforcement |
|
|
693
|
+
| [ADR-040](docs/decisions/adr-040-isr-declaration.md) | ISR Type | Built-in `ISR` type for `void(void)` function pointers |
|
|
694
|
+
| [ADR-034](docs/decisions/adr-034-bit-fields.md) | Bitmap Types | `bitmap8`/`bitmap16`/`bitmap32` for portable bit-packed data |
|
|
695
|
+
| [ADR-048](docs/decisions/adr-048-cli-executable.md) | CLI Executable | `cnext` command with smart defaults |
|
|
696
|
+
| [ADR-049](docs/decisions/adr-049-atomic-types.md) | Atomic Types | `atomic` keyword with LDREX/STREX or PRIMASK fallback |
|
|
697
|
+
| [ADR-050](docs/decisions/adr-050-critical-sections.md) | Critical Sections | `critical { }` blocks with PRIMASK save/restore |
|
|
698
|
+
| [ADR-108](docs/decisions/adr-108-volatile-keyword.md) | Volatile Variables | `volatile` keyword prevents compiler optimization |
|
|
699
|
+
| [ADR-047](docs/decisions/adr-047-nullable-types.md) | NULL for C Interop | `NULL` keyword for C stream function comparisons |
|
|
700
|
+
| [ADR-052](docs/decisions/adr-052-safe-numeric-literal-generation.md) | Safe Numeric Literals | `type_MIN`/`type_MAX` constants + safe hex conversion |
|
|
700
701
|
|
|
701
702
|
### Research (v1 Roadmap)
|
|
702
703
|
|
package/grammar/CNext.g4
CHANGED
|
@@ -345,8 +345,8 @@ switchCase
|
|
|
345
345
|
caseLabel
|
|
346
346
|
: qualifiedType // Enum value: EState.IDLE
|
|
347
347
|
| IDENTIFIER // Const or enum member
|
|
348
|
-
| INTEGER_LITERAL
|
|
349
|
-
| HEX_LITERAL
|
|
348
|
+
| '-'? INTEGER_LITERAL // Allow negative integers
|
|
349
|
+
| '-'? HEX_LITERAL // Allow negative hex (e.g., -0x80)
|
|
350
350
|
| BINARY_LITERAL
|
|
351
351
|
| CHAR_LITERAL
|
|
352
352
|
;
|
package/package.json
CHANGED
|
@@ -42,6 +42,21 @@ const TYPE_MAP: Record<string, string> = {
|
|
|
42
42
|
ISR: "ISR", // ADR-040: Interrupt Service Routine function pointer
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Maps C-Next types to wider C types for clamp helper operands
|
|
47
|
+
* Issue #94: Prevents silent truncation when operand exceeds target type range
|
|
48
|
+
*/
|
|
49
|
+
const WIDER_TYPE_MAP: Record<string, string> = {
|
|
50
|
+
u8: "uint32_t",
|
|
51
|
+
u16: "uint32_t",
|
|
52
|
+
u32: "uint64_t",
|
|
53
|
+
u64: "uint64_t", // Already widest
|
|
54
|
+
i8: "int32_t",
|
|
55
|
+
i16: "int32_t",
|
|
56
|
+
i32: "int64_t",
|
|
57
|
+
i64: "int64_t", // Already widest
|
|
58
|
+
};
|
|
59
|
+
|
|
45
60
|
/**
|
|
46
61
|
* Maps C-Next assignment operators to C assignment operators
|
|
47
62
|
*/
|
|
@@ -329,6 +344,23 @@ export default class CodeGenerator {
|
|
|
329
344
|
return undefined;
|
|
330
345
|
}
|
|
331
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Check if a type name is a known struct (C-Next or C header).
|
|
349
|
+
* Issue #103: Must check both local knownStructs AND SymbolTable
|
|
350
|
+
* for proper type chain tracking through nested C header structs.
|
|
351
|
+
*/
|
|
352
|
+
private isKnownStruct(typeName: string): boolean {
|
|
353
|
+
// Check C-Next structs first (local definitions)
|
|
354
|
+
if (this.knownStructs.has(typeName)) {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
// Check SymbolTable for C header structs
|
|
358
|
+
if (this.symbolTable?.getStructFields(typeName)) {
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
|
|
332
364
|
/**
|
|
333
365
|
* Check if a function is a C-Next function (uses pass-by-reference semantics).
|
|
334
366
|
* Checks both internal tracking and external symbol table.
|
|
@@ -1896,7 +1928,7 @@ export default class CodeGenerator {
|
|
|
1896
1928
|
);
|
|
1897
1929
|
}
|
|
1898
1930
|
|
|
1899
|
-
if (this.
|
|
1931
|
+
if (this.isKnownStruct(identifier)) {
|
|
1900
1932
|
throw new Error(
|
|
1901
1933
|
`Error: Use 'global.${identifier}' to access global struct '${identifier}' inside scope '${this.context.currentScope}'`,
|
|
1902
1934
|
);
|
|
@@ -4147,7 +4179,7 @@ export default class CodeGenerator {
|
|
|
4147
4179
|
|
|
4148
4180
|
// ADR-029: Validate callback field assignments with nominal typing
|
|
4149
4181
|
const rootTypeInfo = this.context.typeRegistry.get(rootName);
|
|
4150
|
-
if (rootTypeInfo && this.
|
|
4182
|
+
if (rootTypeInfo && this.isKnownStruct(rootTypeInfo.baseType)) {
|
|
4151
4183
|
const structType = rootTypeInfo.baseType;
|
|
4152
4184
|
const callbackFieldKey = `${structType}.${memberName}`;
|
|
4153
4185
|
const expectedCallbackType =
|
|
@@ -4439,7 +4471,7 @@ export default class CodeGenerator {
|
|
|
4439
4471
|
let _lastMemberStructType: string | undefined; // Struct type containing the last member (kept for future use)
|
|
4440
4472
|
const firstTypeInfo = this.context.typeRegistry.get(firstId);
|
|
4441
4473
|
if (firstTypeInfo) {
|
|
4442
|
-
currentStructType = this.
|
|
4474
|
+
currentStructType = this.isKnownStruct(firstTypeInfo.baseType)
|
|
4443
4475
|
? firstTypeInfo.baseType
|
|
4444
4476
|
: undefined;
|
|
4445
4477
|
}
|
|
@@ -4472,7 +4504,7 @@ export default class CodeGenerator {
|
|
|
4472
4504
|
this.structFieldArrays.get(currentStructType);
|
|
4473
4505
|
lastMemberIsArray = arrayFields?.has(memberName) ?? false;
|
|
4474
4506
|
// Check if this member is itself a struct
|
|
4475
|
-
if (lastMemberType && this.
|
|
4507
|
+
if (lastMemberType && this.isKnownStruct(lastMemberType)) {
|
|
4476
4508
|
currentStructType = lastMemberType;
|
|
4477
4509
|
} else {
|
|
4478
4510
|
currentStructType = undefined;
|
|
@@ -4518,7 +4550,7 @@ export default class CodeGenerator {
|
|
|
4518
4550
|
if (firstTypeInfo?.isArray && exprIndex === 1) {
|
|
4519
4551
|
// First subscript on array - element type might be a struct
|
|
4520
4552
|
const elementType = firstTypeInfo.baseType;
|
|
4521
|
-
if (this.
|
|
4553
|
+
if (this.isKnownStruct(elementType)) {
|
|
4522
4554
|
currentStructType = elementType;
|
|
4523
4555
|
}
|
|
4524
4556
|
}
|
|
@@ -4543,10 +4575,7 @@ export default class CodeGenerator {
|
|
|
4543
4575
|
const fieldName = identifiers[1].getText();
|
|
4544
4576
|
|
|
4545
4577
|
const structTypeInfo = this.context.typeRegistry.get(structName);
|
|
4546
|
-
if (
|
|
4547
|
-
structTypeInfo &&
|
|
4548
|
-
this.knownStructs.has(structTypeInfo.baseType)
|
|
4549
|
-
) {
|
|
4578
|
+
if (structTypeInfo && this.isKnownStruct(structTypeInfo.baseType)) {
|
|
4550
4579
|
const structType = structTypeInfo.baseType;
|
|
4551
4580
|
const fieldDimensions =
|
|
4552
4581
|
this.structFieldDimensions.get(structType);
|
|
@@ -5084,7 +5113,7 @@ export default class CodeGenerator {
|
|
|
5084
5113
|
const fieldName = identifiers[1].getText();
|
|
5085
5114
|
|
|
5086
5115
|
const structTypeInfo = this.context.typeRegistry.get(structName);
|
|
5087
|
-
if (structTypeInfo && this.
|
|
5116
|
+
if (structTypeInfo && this.isKnownStruct(structTypeInfo.baseType)) {
|
|
5088
5117
|
const structType = structTypeInfo.baseType;
|
|
5089
5118
|
|
|
5090
5119
|
// Check if this field is a string array in the struct
|
|
@@ -5759,13 +5788,19 @@ export default class CodeGenerator {
|
|
|
5759
5788
|
return ctx.IDENTIFIER()!.getText();
|
|
5760
5789
|
}
|
|
5761
5790
|
|
|
5762
|
-
// Numeric literals
|
|
5791
|
+
// Numeric literals (may have optional minus prefix)
|
|
5763
5792
|
if (ctx.INTEGER_LITERAL()) {
|
|
5764
|
-
|
|
5793
|
+
const num = ctx.INTEGER_LITERAL()!.getText();
|
|
5794
|
+
// Check if minus token exists (first child would be '-')
|
|
5795
|
+
const hasNeg = ctx.children && ctx.children[0]?.getText() === "-";
|
|
5796
|
+
return hasNeg ? `-${num}` : num;
|
|
5765
5797
|
}
|
|
5766
5798
|
|
|
5767
5799
|
if (ctx.HEX_LITERAL()) {
|
|
5768
|
-
|
|
5800
|
+
const hex = ctx.HEX_LITERAL()!.getText();
|
|
5801
|
+
// Check if minus token exists (first child would be '-')
|
|
5802
|
+
const hasNeg = ctx.children && ctx.children[0]?.getText() === "-";
|
|
5803
|
+
return hasNeg ? `-${hex}` : hex;
|
|
5769
5804
|
}
|
|
5770
5805
|
|
|
5771
5806
|
if (ctx.BINARY_LITERAL()) {
|
|
@@ -5928,17 +5963,24 @@ export default class CodeGenerator {
|
|
|
5928
5963
|
return ctx.IDENTIFIER()!.getText();
|
|
5929
5964
|
}
|
|
5930
5965
|
if (ctx.INTEGER_LITERAL()) {
|
|
5931
|
-
|
|
5966
|
+
const num = ctx.INTEGER_LITERAL()!.getText();
|
|
5967
|
+
// Check if minus token exists (first child would be '-')
|
|
5968
|
+
const hasNeg = ctx.children && ctx.children[0]?.getText() === "-";
|
|
5969
|
+
const value = BigInt(num);
|
|
5970
|
+
return String(hasNeg ? -value : value);
|
|
5932
5971
|
}
|
|
5933
5972
|
if (ctx.HEX_LITERAL()) {
|
|
5934
5973
|
// Normalize hex to decimal for comparison
|
|
5935
5974
|
const hex = ctx.HEX_LITERAL()!.getText();
|
|
5936
|
-
|
|
5975
|
+
// Check if minus token exists (first child would be '-')
|
|
5976
|
+
const hasNeg = ctx.children && ctx.children[0]?.getText() === "-";
|
|
5977
|
+
const value = BigInt(hex); // BigInt handles 0x prefix natively
|
|
5978
|
+
return String(hasNeg ? -value : value);
|
|
5937
5979
|
}
|
|
5938
5980
|
if (ctx.BINARY_LITERAL()) {
|
|
5939
5981
|
// Normalize binary to decimal for comparison
|
|
5940
5982
|
const bin = ctx.BINARY_LITERAL()!.getText();
|
|
5941
|
-
return String(
|
|
5983
|
+
return String(BigInt(bin)); // BigInt handles 0b prefix natively
|
|
5942
5984
|
}
|
|
5943
5985
|
if (ctx.CHAR_LITERAL()) {
|
|
5944
5986
|
return ctx.CHAR_LITERAL()!.getText();
|
|
@@ -6854,7 +6896,7 @@ export default class CodeGenerator {
|
|
|
6854
6896
|
const resolvedTypeInfo = this.context.typeRegistry.get(result);
|
|
6855
6897
|
if (
|
|
6856
6898
|
resolvedTypeInfo &&
|
|
6857
|
-
this.
|
|
6899
|
+
this.isKnownStruct(resolvedTypeInfo.baseType)
|
|
6858
6900
|
) {
|
|
6859
6901
|
currentStructType = resolvedTypeInfo.baseType;
|
|
6860
6902
|
}
|
|
@@ -6908,7 +6950,7 @@ export default class CodeGenerator {
|
|
|
6908
6950
|
this.context.typeRegistry.get(currentIdentifier);
|
|
6909
6951
|
if (
|
|
6910
6952
|
identifierTypeInfo &&
|
|
6911
|
-
this.
|
|
6953
|
+
this.isKnownStruct(identifierTypeInfo.baseType)
|
|
6912
6954
|
) {
|
|
6913
6955
|
currentStructType = identifierTypeInfo.baseType;
|
|
6914
6956
|
}
|
|
@@ -6937,7 +6979,7 @@ export default class CodeGenerator {
|
|
|
6937
6979
|
this.context.typeRegistry.get(currentIdentifier);
|
|
6938
6980
|
if (
|
|
6939
6981
|
identifierTypeInfo &&
|
|
6940
|
-
this.
|
|
6982
|
+
this.isKnownStruct(identifierTypeInfo.baseType)
|
|
6941
6983
|
) {
|
|
6942
6984
|
currentStructType = identifierTypeInfo.baseType;
|
|
6943
6985
|
}
|
|
@@ -6977,7 +7019,7 @@ export default class CodeGenerator {
|
|
|
6977
7019
|
const resolvedTypeInfo = this.context.typeRegistry.get(result);
|
|
6978
7020
|
if (
|
|
6979
7021
|
resolvedTypeInfo &&
|
|
6980
|
-
this.
|
|
7022
|
+
this.isKnownStruct(resolvedTypeInfo.baseType)
|
|
6981
7023
|
) {
|
|
6982
7024
|
currentStructType = resolvedTypeInfo.baseType;
|
|
6983
7025
|
}
|
|
@@ -7096,7 +7138,7 @@ export default class CodeGenerator {
|
|
|
7096
7138
|
// After consuming all array dimensions, set struct type if element is struct
|
|
7097
7139
|
if (remainingArrayDims === 0 && primaryTypeInfo) {
|
|
7098
7140
|
const elementType = primaryTypeInfo.baseType;
|
|
7099
|
-
if (this.
|
|
7141
|
+
if (this.isKnownStruct(elementType)) {
|
|
7100
7142
|
currentStructType = elementType;
|
|
7101
7143
|
}
|
|
7102
7144
|
}
|
|
@@ -7112,7 +7154,7 @@ export default class CodeGenerator {
|
|
|
7112
7154
|
// After subscripting an array, set currentStructType if the element is a struct
|
|
7113
7155
|
if (identifierTypeInfo && !currentStructType) {
|
|
7114
7156
|
const elementType = identifierTypeInfo.baseType;
|
|
7115
|
-
if (this.
|
|
7157
|
+
if (this.isKnownStruct(elementType)) {
|
|
7116
7158
|
currentStructType = elementType;
|
|
7117
7159
|
}
|
|
7118
7160
|
}
|
|
@@ -7344,11 +7386,19 @@ export default class CodeGenerator {
|
|
|
7344
7386
|
return id;
|
|
7345
7387
|
}
|
|
7346
7388
|
if (ctx.literal()) {
|
|
7347
|
-
|
|
7389
|
+
let literalText = ctx.literal()!.getText();
|
|
7348
7390
|
// Track boolean literal usage to include stdbool.h
|
|
7349
7391
|
if (literalText === "true" || literalText === "false") {
|
|
7350
7392
|
this.needsStdbool = true;
|
|
7351
7393
|
}
|
|
7394
|
+
// ADR-024: Transform C-Next float suffixes to standard C syntax
|
|
7395
|
+
// 3.14f32 -> 3.14f (C float)
|
|
7396
|
+
// 3.14f64 -> 3.14 (C double, no suffix needed)
|
|
7397
|
+
if (/[fF]32$/.test(literalText)) {
|
|
7398
|
+
literalText = literalText.replace(/[fF]32$/, "f");
|
|
7399
|
+
} else if (/[fF]64$/.test(literalText)) {
|
|
7400
|
+
literalText = literalText.replace(/[fF]64$/, "");
|
|
7401
|
+
}
|
|
7352
7402
|
return literalText;
|
|
7353
7403
|
}
|
|
7354
7404
|
if (ctx.expression()) {
|
|
@@ -7450,7 +7500,7 @@ export default class CodeGenerator {
|
|
|
7450
7500
|
}
|
|
7451
7501
|
|
|
7452
7502
|
// Check if it's a known struct (actual type)
|
|
7453
|
-
if (this.
|
|
7503
|
+
if (this.isKnownStruct(varName)) {
|
|
7454
7504
|
return `sizeof(${varName})`;
|
|
7455
7505
|
}
|
|
7456
7506
|
|
|
@@ -7725,7 +7775,7 @@ export default class CodeGenerator {
|
|
|
7725
7775
|
let lastMemberIsArray = false; // Track if last accessed member is an array
|
|
7726
7776
|
const firstTypeInfo = this.context.typeRegistry.get(firstPart);
|
|
7727
7777
|
if (firstTypeInfo) {
|
|
7728
|
-
currentStructType = this.
|
|
7778
|
+
currentStructType = this.isKnownStruct(firstTypeInfo.baseType)
|
|
7729
7779
|
? firstTypeInfo.baseType
|
|
7730
7780
|
: undefined;
|
|
7731
7781
|
}
|
|
@@ -7754,7 +7804,7 @@ export default class CodeGenerator {
|
|
|
7754
7804
|
this.structFieldArrays.get(currentStructType);
|
|
7755
7805
|
lastMemberIsArray = arrayFields?.has(memberName) ?? false;
|
|
7756
7806
|
// Check if this member is itself a struct
|
|
7757
|
-
if (lastMemberType && this.
|
|
7807
|
+
if (lastMemberType && this.isKnownStruct(lastMemberType)) {
|
|
7758
7808
|
currentStructType = lastMemberType;
|
|
7759
7809
|
} else {
|
|
7760
7810
|
currentStructType = undefined;
|
|
@@ -7795,7 +7845,7 @@ export default class CodeGenerator {
|
|
|
7795
7845
|
if (firstTypeInfo?.isArray && exprIndex === 1) {
|
|
7796
7846
|
// First subscript on array - element type might be a struct
|
|
7797
7847
|
const elementType = firstTypeInfo.baseType;
|
|
7798
|
-
if (this.
|
|
7848
|
+
if (this.isKnownStruct(elementType)) {
|
|
7799
7849
|
currentStructType = elementType;
|
|
7800
7850
|
}
|
|
7801
7851
|
}
|
|
@@ -8363,6 +8413,7 @@ export default class CodeGenerator {
|
|
|
8363
8413
|
cnxType: string,
|
|
8364
8414
|
): string | null {
|
|
8365
8415
|
const cType = TYPE_MAP[cnxType];
|
|
8416
|
+
const widerType = WIDER_TYPE_MAP[cnxType] || cType;
|
|
8366
8417
|
const maxValue = CodeGenerator.TYPE_MAX[cnxType];
|
|
8367
8418
|
const minValue = CodeGenerator.TYPE_MIN[cnxType];
|
|
8368
8419
|
|
|
@@ -8372,16 +8423,29 @@ export default class CodeGenerator {
|
|
|
8372
8423
|
|
|
8373
8424
|
const isUnsigned = cnxType.startsWith("u");
|
|
8374
8425
|
|
|
8426
|
+
// For signed types narrower than i64, use wider arithmetic to avoid UB (Issue #94)
|
|
8427
|
+
const useWiderArithmetic =
|
|
8428
|
+
!isUnsigned && widerType !== cType && cnxType !== "i64";
|
|
8429
|
+
|
|
8375
8430
|
switch (operation) {
|
|
8376
8431
|
case "add":
|
|
8377
8432
|
if (isUnsigned) {
|
|
8378
|
-
// Unsigned addition:
|
|
8379
|
-
return `static inline ${cType} cnx_clamp_add_${cnxType}(${cType} a, ${
|
|
8380
|
-
if (
|
|
8381
|
-
return a + b;
|
|
8433
|
+
// Unsigned addition: use wider type for b to prevent truncation (Issue #94)
|
|
8434
|
+
return `static inline ${cType} cnx_clamp_add_${cnxType}(${cType} a, ${widerType} b) {
|
|
8435
|
+
if (b > ${maxValue} - a) return ${maxValue};
|
|
8436
|
+
return a + (${cType})b;
|
|
8437
|
+
}`;
|
|
8438
|
+
} else if (useWiderArithmetic) {
|
|
8439
|
+
// Signed addition: compute in wider type, then clamp (Issue #94)
|
|
8440
|
+
// This avoids UB from casting out-of-range values
|
|
8441
|
+
return `static inline ${cType} cnx_clamp_add_${cnxType}(${cType} a, ${widerType} b) {
|
|
8442
|
+
${widerType} result = (${widerType})a + b;
|
|
8443
|
+
if (result > ${maxValue}) return ${maxValue};
|
|
8444
|
+
if (result < ${minValue}) return ${minValue};
|
|
8445
|
+
return (${cType})result;
|
|
8382
8446
|
}`;
|
|
8383
8447
|
} else {
|
|
8384
|
-
//
|
|
8448
|
+
// i64: already widest type, use original check logic
|
|
8385
8449
|
return `static inline ${cType} cnx_clamp_add_${cnxType}(${cType} a, ${cType} b) {
|
|
8386
8450
|
if (b > 0 && a > ${maxValue} - b) return ${maxValue};
|
|
8387
8451
|
if (b < 0 && a < ${minValue} - b) return ${minValue};
|
|
@@ -8391,13 +8455,22 @@ export default class CodeGenerator {
|
|
|
8391
8455
|
|
|
8392
8456
|
case "sub":
|
|
8393
8457
|
if (isUnsigned) {
|
|
8394
|
-
// Unsigned subtraction:
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8458
|
+
// Unsigned subtraction: use wider type for b to prevent truncation (Issue #94)
|
|
8459
|
+
// Cast a to wider type for comparison to handle b > type max
|
|
8460
|
+
return `static inline ${cType} cnx_clamp_sub_${cnxType}(${cType} a, ${widerType} b) {
|
|
8461
|
+
if (b >= (${widerType})a) return 0;
|
|
8462
|
+
return a - (${cType})b;
|
|
8463
|
+
}`;
|
|
8464
|
+
} else if (useWiderArithmetic) {
|
|
8465
|
+
// Signed subtraction: compute in wider type, then clamp (Issue #94)
|
|
8466
|
+
return `static inline ${cType} cnx_clamp_sub_${cnxType}(${cType} a, ${widerType} b) {
|
|
8467
|
+
${widerType} result = (${widerType})a - b;
|
|
8468
|
+
if (result > ${maxValue}) return ${maxValue};
|
|
8469
|
+
if (result < ${minValue}) return ${minValue};
|
|
8470
|
+
return (${cType})result;
|
|
8398
8471
|
}`;
|
|
8399
8472
|
} else {
|
|
8400
|
-
//
|
|
8473
|
+
// i64: already widest type, use original check logic
|
|
8401
8474
|
return `static inline ${cType} cnx_clamp_sub_${cnxType}(${cType} a, ${cType} b) {
|
|
8402
8475
|
if (b < 0 && a > ${maxValue} + b) return ${maxValue};
|
|
8403
8476
|
if (b > 0 && a < ${minValue} + b) return ${minValue};
|
|
@@ -8407,13 +8480,21 @@ export default class CodeGenerator {
|
|
|
8407
8480
|
|
|
8408
8481
|
case "mul":
|
|
8409
8482
|
if (isUnsigned) {
|
|
8410
|
-
// Unsigned multiplication
|
|
8411
|
-
return `static inline ${cType} cnx_clamp_mul_${cnxType}(${cType} a, ${
|
|
8483
|
+
// Unsigned multiplication: use wider type for b to prevent truncation (Issue #94)
|
|
8484
|
+
return `static inline ${cType} cnx_clamp_mul_${cnxType}(${cType} a, ${widerType} b) {
|
|
8412
8485
|
if (b != 0 && a > ${maxValue} / b) return ${maxValue};
|
|
8413
|
-
return a * b;
|
|
8486
|
+
return a * (${cType})b;
|
|
8487
|
+
}`;
|
|
8488
|
+
} else if (useWiderArithmetic) {
|
|
8489
|
+
// Signed multiplication: compute in wider type, then clamp (Issue #94)
|
|
8490
|
+
return `static inline ${cType} cnx_clamp_mul_${cnxType}(${cType} a, ${widerType} b) {
|
|
8491
|
+
${widerType} result = (${widerType})a * b;
|
|
8492
|
+
if (result > ${maxValue}) return ${maxValue};
|
|
8493
|
+
if (result < ${minValue}) return ${minValue};
|
|
8494
|
+
return (${cType})result;
|
|
8414
8495
|
}`;
|
|
8415
8496
|
} else {
|
|
8416
|
-
//
|
|
8497
|
+
// i64: already widest type, use original check logic
|
|
8417
8498
|
return `static inline ${cType} cnx_clamp_mul_${cnxType}(${cType} a, ${cType} b) {
|
|
8418
8499
|
if (a == 0 || b == 0) return 0;
|
|
8419
8500
|
if (a > 0 && b > 0 && a > ${maxValue} / b) return ${maxValue};
|
|
@@ -8437,6 +8518,7 @@ export default class CodeGenerator {
|
|
|
8437
8518
|
cnxType: string,
|
|
8438
8519
|
): string | null {
|
|
8439
8520
|
const cType = TYPE_MAP[cnxType];
|
|
8521
|
+
const widerType = WIDER_TYPE_MAP[cnxType] || cType;
|
|
8440
8522
|
const maxValue = CodeGenerator.TYPE_MAX[cnxType];
|
|
8441
8523
|
const minValue = CodeGenerator.TYPE_MIN[cnxType];
|
|
8442
8524
|
|
|
@@ -8452,17 +8534,33 @@ export default class CodeGenerator {
|
|
|
8452
8534
|
? "subtraction"
|
|
8453
8535
|
: "multiplication";
|
|
8454
8536
|
|
|
8537
|
+
// For signed types narrower than i64, use wider arithmetic to avoid UB (Issue #94)
|
|
8538
|
+
const useWiderArithmetic =
|
|
8539
|
+
!isUnsigned && widerType !== cType && cnxType !== "i64";
|
|
8540
|
+
|
|
8455
8541
|
switch (operation) {
|
|
8456
8542
|
case "add":
|
|
8457
8543
|
if (isUnsigned) {
|
|
8458
|
-
|
|
8459
|
-
|
|
8544
|
+
// Use wider type for b to prevent truncation (Issue #94)
|
|
8545
|
+
return `static inline ${cType} cnx_clamp_add_${cnxType}(${cType} a, ${widerType} b) {
|
|
8546
|
+
if (b > ${maxValue} - a) {
|
|
8460
8547
|
fprintf(stderr, "PANIC: Integer overflow in ${cnxType} ${opName}\\n");
|
|
8461
8548
|
abort();
|
|
8462
8549
|
}
|
|
8463
|
-
return a + b;
|
|
8550
|
+
return a + (${cType})b;
|
|
8551
|
+
}`;
|
|
8552
|
+
} else if (useWiderArithmetic) {
|
|
8553
|
+
// Signed addition: compute in wider type, check bounds (Issue #94)
|
|
8554
|
+
return `static inline ${cType} cnx_clamp_add_${cnxType}(${cType} a, ${widerType} b) {
|
|
8555
|
+
${widerType} result = (${widerType})a + b;
|
|
8556
|
+
if (result > ${maxValue} || result < ${minValue}) {
|
|
8557
|
+
fprintf(stderr, "PANIC: Integer overflow in ${cnxType} ${opName}\\n");
|
|
8558
|
+
abort();
|
|
8559
|
+
}
|
|
8560
|
+
return (${cType})result;
|
|
8464
8561
|
}`;
|
|
8465
8562
|
} else {
|
|
8563
|
+
// i64: already widest type, use original check logic
|
|
8466
8564
|
return `static inline ${cType} cnx_clamp_add_${cnxType}(${cType} a, ${cType} b) {
|
|
8467
8565
|
if ((b > 0 && a > ${maxValue} - b) || (b < 0 && a < ${minValue} - b)) {
|
|
8468
8566
|
fprintf(stderr, "PANIC: Integer overflow in ${cnxType} ${opName}\\n");
|
|
@@ -8474,14 +8572,26 @@ export default class CodeGenerator {
|
|
|
8474
8572
|
|
|
8475
8573
|
case "sub":
|
|
8476
8574
|
if (isUnsigned) {
|
|
8477
|
-
|
|
8478
|
-
|
|
8575
|
+
// Use wider type for b to prevent truncation (Issue #94)
|
|
8576
|
+
return `static inline ${cType} cnx_clamp_sub_${cnxType}(${cType} a, ${widerType} b) {
|
|
8577
|
+
if (b >= (${widerType})a) {
|
|
8479
8578
|
fprintf(stderr, "PANIC: Integer underflow in ${cnxType} ${opName}\\n");
|
|
8480
8579
|
abort();
|
|
8481
8580
|
}
|
|
8482
|
-
return a - b;
|
|
8581
|
+
return a - (${cType})b;
|
|
8582
|
+
}`;
|
|
8583
|
+
} else if (useWiderArithmetic) {
|
|
8584
|
+
// Signed subtraction: compute in wider type, check bounds (Issue #94)
|
|
8585
|
+
return `static inline ${cType} cnx_clamp_sub_${cnxType}(${cType} a, ${widerType} b) {
|
|
8586
|
+
${widerType} result = (${widerType})a - b;
|
|
8587
|
+
if (result > ${maxValue} || result < ${minValue}) {
|
|
8588
|
+
fprintf(stderr, "PANIC: Integer overflow in ${cnxType} ${opName}\\n");
|
|
8589
|
+
abort();
|
|
8590
|
+
}
|
|
8591
|
+
return (${cType})result;
|
|
8483
8592
|
}`;
|
|
8484
8593
|
} else {
|
|
8594
|
+
// i64: already widest type, use original check logic
|
|
8485
8595
|
return `static inline ${cType} cnx_clamp_sub_${cnxType}(${cType} a, ${cType} b) {
|
|
8486
8596
|
if ((b < 0 && a > ${maxValue} + b) || (b > 0 && a < ${minValue} + b)) {
|
|
8487
8597
|
fprintf(stderr, "PANIC: Integer overflow in ${cnxType} ${opName}\\n");
|
|
@@ -8493,14 +8603,26 @@ export default class CodeGenerator {
|
|
|
8493
8603
|
|
|
8494
8604
|
case "mul":
|
|
8495
8605
|
if (isUnsigned) {
|
|
8496
|
-
|
|
8606
|
+
// Use wider type for b to prevent truncation (Issue #94)
|
|
8607
|
+
return `static inline ${cType} cnx_clamp_mul_${cnxType}(${cType} a, ${widerType} b) {
|
|
8497
8608
|
if (b != 0 && a > ${maxValue} / b) {
|
|
8498
8609
|
fprintf(stderr, "PANIC: Integer overflow in ${cnxType} ${opName}\\n");
|
|
8499
8610
|
abort();
|
|
8500
8611
|
}
|
|
8501
|
-
return a * b;
|
|
8612
|
+
return a * (${cType})b;
|
|
8613
|
+
}`;
|
|
8614
|
+
} else if (useWiderArithmetic) {
|
|
8615
|
+
// Signed multiplication: compute in wider type, check bounds (Issue #94)
|
|
8616
|
+
return `static inline ${cType} cnx_clamp_mul_${cnxType}(${cType} a, ${widerType} b) {
|
|
8617
|
+
${widerType} result = (${widerType})a * b;
|
|
8618
|
+
if (result > ${maxValue} || result < ${minValue}) {
|
|
8619
|
+
fprintf(stderr, "PANIC: Integer overflow in ${cnxType} ${opName}\\n");
|
|
8620
|
+
abort();
|
|
8621
|
+
}
|
|
8622
|
+
return (${cType})result;
|
|
8502
8623
|
}`;
|
|
8503
8624
|
} else {
|
|
8625
|
+
// i64: already widest type, use original check logic
|
|
8504
8626
|
return `static inline ${cType} cnx_clamp_mul_${cnxType}(${cType} a, ${cType} b) {
|
|
8505
8627
|
if (a != 0 && b != 0) {
|
|
8506
8628
|
if ((a > 0 && b > 0 && a > ${maxValue} / b) ||
|
|
@@ -47,12 +47,22 @@ class TypeResolver {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Check if a type is a user-defined struct
|
|
50
|
+
* Check if a type is a user-defined struct (C-Next or C header).
|
|
51
|
+
* Issue #103: Now checks both knownStructs AND SymbolTable.
|
|
51
52
|
*/
|
|
52
53
|
isStructType(typeName: string): boolean {
|
|
53
|
-
//
|
|
54
|
+
// Check C-Next structs first
|
|
54
55
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
55
|
-
|
|
56
|
+
if (this.codeGen["knownStructs"].has(typeName)) {
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
// Check SymbolTable for C header structs
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
61
|
+
const symbolTable = this.codeGen["symbolTable"];
|
|
62
|
+
if (symbolTable?.getStructFields(typeName)) {
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
return false;
|
|
56
66
|
}
|
|
57
67
|
|
|
58
68
|
/**
|
|
@@ -340,11 +350,28 @@ class TypeResolver {
|
|
|
340
350
|
/**
|
|
341
351
|
* Get type info for a struct member field
|
|
342
352
|
* Used to track types through member access chains like buf.data[0]
|
|
353
|
+
* Issue #103: Now checks SymbolTable first for C header structs
|
|
343
354
|
*/
|
|
344
355
|
getMemberTypeInfo(
|
|
345
356
|
structType: string,
|
|
346
357
|
memberName: string,
|
|
347
358
|
): { isArray: boolean; baseType: string } | undefined {
|
|
359
|
+
// First check SymbolTable (C header structs) - Issue #103 fix
|
|
360
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
361
|
+
const symbolTable = this.codeGen["symbolTable"];
|
|
362
|
+
if (symbolTable) {
|
|
363
|
+
const fieldInfo = symbolTable.getStructFieldInfo(structType, memberName);
|
|
364
|
+
if (fieldInfo) {
|
|
365
|
+
return {
|
|
366
|
+
isArray:
|
|
367
|
+
fieldInfo.arrayDimensions !== undefined &&
|
|
368
|
+
fieldInfo.arrayDimensions.length > 0,
|
|
369
|
+
baseType: fieldInfo.type,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Fall back to local C-Next struct fields
|
|
348
375
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
349
376
|
const fieldType = this.codeGen["structFields"]
|
|
350
377
|
.get(structType)
|