c-next 0.2.16 → 0.2.18
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 +18 -2
- package/dist/index.js +8897 -6260
- package/dist/index.js.map +4 -4
- package/grammar/CNext.g4 +12 -0
- package/package.json +4 -2
- package/src/transpiler/Transpiler.ts +376 -48
- package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
- package/src/transpiler/__tests__/compileCommandsDiscovery.integration.test.ts +94 -0
- package/src/transpiler/__tests__/externalSymbolRecovery.integration.test.ts +215 -0
- package/src/transpiler/logic/__tests__/detectAssemblySyntax.test.ts +116 -0
- package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +65 -10
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
- package/src/transpiler/logic/analysis/MixedTypeCategoryAnalyzer.ts +408 -0
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +16 -97
- package/src/transpiler/logic/analysis/ReturnPathAnalyzer.ts +171 -0
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
- package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
- package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
- package/src/transpiler/logic/analysis/__tests__/MixedTypeCategoryAnalyzer.test.ts +346 -0
- package/src/transpiler/logic/analysis/__tests__/ReturnPathAnalyzer.test.ts +232 -0
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
- package/src/transpiler/logic/analysis/runAnalyzers.ts +23 -2
- package/src/transpiler/logic/analysis/types/IMixedTypeCategoryError.ts +17 -0
- package/src/transpiler/logic/analysis/types/IReturnPathError.ts +12 -0
- package/src/transpiler/logic/detectAssemblySyntax.ts +80 -0
- package/src/transpiler/logic/parser/grammar/CNext.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNext.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.interp +4 -1
- package/src/transpiler/logic/parser/grammar/CNextLexer.tokens +169 -167
- package/src/transpiler/logic/parser/grammar/CNextLexer.ts +545 -541
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +11 -0
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +1318 -1178
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +7 -0
- package/src/transpiler/logic/preprocessor/CompileCommandsReader.ts +332 -0
- package/src/transpiler/logic/preprocessor/ExternalDeclarationOracle.ts +202 -0
- package/src/transpiler/logic/preprocessor/Preprocessor.ts +24 -6
- package/src/transpiler/logic/preprocessor/ToolchainDetector.ts +42 -1
- package/src/transpiler/logic/preprocessor/__tests__/CompileCommandsReader.test.ts +216 -0
- package/src/transpiler/logic/preprocessor/__tests__/ExternalDeclarationOracle.test.ts +153 -0
- package/src/transpiler/logic/preprocessor/__tests__/Preprocessor.test.ts +43 -18
- package/src/transpiler/logic/preprocessor/__tests__/ToolchainDetector.crossCompiler.test.ts +75 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/dependent.h +7 -0
- package/src/transpiler/logic/preprocessor/__tests__/fixtures/oracle/predecessor.h +5 -0
- package/src/transpiler/logic/preprocessor/types/ICompileCommandsResult.ts +14 -0
- package/src/transpiler/logic/preprocessor/types/IPreprocessOptions.ts +17 -0
- package/src/transpiler/logic/symbols/SymbolTable.ts +62 -2
- package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +55 -4
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
- package/src/transpiler/output/MisraSuppressionUtils.ts +52 -0
- package/src/transpiler/output/__tests__/MisraSuppressionUtils.test.ts +67 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +196 -98
- package/src/transpiler/output/codegen/TypeResolver.ts +148 -0
- package/src/transpiler/output/codegen/TypeValidator.ts +179 -81
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +165 -17
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +163 -35
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +93 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.alwaysTrueLoop.test.ts +91 -0
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +97 -14
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +13 -0
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +1 -1
- package/src/transpiler/output/codegen/assignment/handlers/AccessPatternHandlers.ts +20 -12
- package/src/transpiler/output/codegen/assignment/handlers/ArrayHandlers.ts +424 -22
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +31 -18
- package/src/transpiler/output/codegen/assignment/handlers/BitmapHandlers.ts +7 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterHandlers.ts +6 -8
- package/src/transpiler/output/codegen/assignment/handlers/RegisterUtils.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/SimpleHandler.ts +3 -7
- package/src/transpiler/output/codegen/assignment/handlers/SpecialHandlers.ts +7 -9
- package/src/transpiler/output/codegen/assignment/handlers/StringHandlers.ts +12 -10
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/ArrayHandlers.test.ts +479 -11
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +3 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +26 -7
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +86 -0
- package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
- package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +76 -58
- package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +26 -13
- package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
- package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +129 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +64 -8
- package/src/transpiler/output/codegen/generators/statements/__tests__/ControlFlowGenerator.test.ts +8 -5
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +30 -2
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
- package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +240 -42
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
- package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
- package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +19 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +322 -9
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
- package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
- package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
- package/src/transpiler/output/codegen/subscript/SubscriptClassifier.ts +30 -11
- package/src/transpiler/output/codegen/subscript/TSubscriptKind.ts +1 -1
- package/src/transpiler/output/codegen/subscript/__tests__/SubscriptClassifier.test.ts +38 -6
- package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
- package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
- package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +140 -24
- package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
- package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
- package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
- package/src/transpiler/state/CodeGenState.ts +91 -22
- package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
- package/src/transpiler/types/ICachedFileEntry.ts +7 -0
- package/src/utils/LiteralUtils.ts +23 -0
- package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
- package/src/utils/cache/CacheManager.ts +13 -2
- package/src/utils/cache/__tests__/CacheManager.test.ts +18 -1
- package/src/utils/constants/TypeConstants.ts +13 -0
- package/src/utils/types/IParameterSymbol.ts +1 -0
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import * as Parser from "../../../logic/parser/grammar/CNextParser.js";
|
|
16
|
-
import FormatUtils from "../../../../utils/FormatUtils.js";
|
|
17
16
|
import StringUtils from "../../../../utils/StringUtils.js";
|
|
18
17
|
import CodeGenState from "../../../state/CodeGenState.js";
|
|
19
18
|
|
|
@@ -98,6 +97,21 @@ class StringDeclHelper {
|
|
|
98
97
|
isConst: boolean,
|
|
99
98
|
callbacks: IStringDeclCallbacks,
|
|
100
99
|
): IStringDeclResult {
|
|
100
|
+
// Issue #1029: Check for string array in arrayType syntax first
|
|
101
|
+
// For `string<32>[4] items`, the structure is: arrayType -> stringType arrayTypeDimension+
|
|
102
|
+
const arrayTypeCtx = typeCtx.arrayType?.();
|
|
103
|
+
if (arrayTypeCtx?.stringType?.()) {
|
|
104
|
+
return StringDeclHelper._generateStringArrayFromArrayType(
|
|
105
|
+
name,
|
|
106
|
+
arrayTypeCtx,
|
|
107
|
+
expression,
|
|
108
|
+
arrayDims,
|
|
109
|
+
modifiers,
|
|
110
|
+
isConst,
|
|
111
|
+
callbacks,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
101
115
|
const stringCtx = typeCtx.stringType();
|
|
102
116
|
if (!stringCtx) {
|
|
103
117
|
return { code: "", handled: false };
|
|
@@ -129,6 +143,182 @@ class StringDeclHelper {
|
|
|
129
143
|
}
|
|
130
144
|
}
|
|
131
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Issue #1029: Generate string array declaration from arrayType syntax.
|
|
148
|
+
* Handles: string<32>[4] items -> char items[4][33] = {0};
|
|
149
|
+
*/
|
|
150
|
+
private static _generateStringArrayFromArrayType(
|
|
151
|
+
name: string,
|
|
152
|
+
arrayTypeCtx: Parser.ArrayTypeContext,
|
|
153
|
+
expression: Parser.ExpressionContext | null,
|
|
154
|
+
trailingArrayDims: Parser.ArrayDimensionContext[],
|
|
155
|
+
modifiers: IStringDeclModifiers,
|
|
156
|
+
isConst: boolean,
|
|
157
|
+
callbacks: IStringDeclCallbacks,
|
|
158
|
+
): IStringDeclResult {
|
|
159
|
+
const stringCtx = arrayTypeCtx.stringType()!;
|
|
160
|
+
const intLiteral = stringCtx.INTEGER_LITERAL();
|
|
161
|
+
if (!intLiteral) {
|
|
162
|
+
// Unsized string array - not supported
|
|
163
|
+
throw new Error(
|
|
164
|
+
"Error: String arrays require explicit capacity, e.g., string<64>[4]",
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const capacity = Number.parseInt(intLiteral.getText(), 10);
|
|
169
|
+
// Ensure string.h is included for strncpy operations
|
|
170
|
+
callbacks.requireStringInclude();
|
|
171
|
+
|
|
172
|
+
const {
|
|
173
|
+
extern,
|
|
174
|
+
const: constMod,
|
|
175
|
+
atomic,
|
|
176
|
+
volatile: volatileMod,
|
|
177
|
+
} = modifiers;
|
|
178
|
+
|
|
179
|
+
// Build array dimensions from arrayType (e.g., [4] from string<32>[4])
|
|
180
|
+
let arrayDimStr = "";
|
|
181
|
+
for (const dim of arrayTypeCtx.arrayTypeDimension()) {
|
|
182
|
+
const sizeExpr = dim.expression();
|
|
183
|
+
if (sizeExpr) {
|
|
184
|
+
arrayDimStr += `[${sizeExpr.getText()}]`;
|
|
185
|
+
} else {
|
|
186
|
+
arrayDimStr += "[]";
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Add any trailing dimensions from variable declaration
|
|
191
|
+
arrayDimStr += callbacks.generateArrayDimensions(trailingArrayDims);
|
|
192
|
+
|
|
193
|
+
// Add string capacity dimension
|
|
194
|
+
arrayDimStr += `[${capacity + 1}]`;
|
|
195
|
+
|
|
196
|
+
let decl = `${extern}${constMod}${atomic}${volatileMod}char ${name}${arrayDimStr}`;
|
|
197
|
+
|
|
198
|
+
// Track as local array
|
|
199
|
+
CodeGenState.localArrays.add(name);
|
|
200
|
+
|
|
201
|
+
// No initializer - zero-initialize
|
|
202
|
+
if (!expression) {
|
|
203
|
+
return { code: `${decl} = {0};`, handled: true };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Reset array init tracking and generate initializer
|
|
207
|
+
CodeGenState.lastArrayInitCount = 0;
|
|
208
|
+
CodeGenState.lastArrayFillValue = undefined;
|
|
209
|
+
const initValue = callbacks.generateExpression(expression);
|
|
210
|
+
|
|
211
|
+
// Check if it was an array initializer
|
|
212
|
+
const isArrayInit =
|
|
213
|
+
CodeGenState.lastArrayInitCount > 0 ||
|
|
214
|
+
CodeGenState.lastArrayFillValue !== undefined;
|
|
215
|
+
|
|
216
|
+
if (!isArrayInit) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
`Error: String array initialization from variables not supported`,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Validate element count if declared size is available
|
|
223
|
+
const declaredSize =
|
|
224
|
+
StringDeclHelper._getArrayTypeDeclaredSize(arrayTypeCtx);
|
|
225
|
+
if (declaredSize !== null) {
|
|
226
|
+
const isFillAll = CodeGenState.lastArrayFillValue !== undefined;
|
|
227
|
+
const elementCount = CodeGenState.lastArrayInitCount;
|
|
228
|
+
|
|
229
|
+
if (!isFillAll && elementCount !== declaredSize) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`Error: Array size mismatch - declared [${declaredSize}] but got ${elementCount} elements`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Handle fill-all expansion if needed
|
|
237
|
+
const finalInitValue = StringDeclHelper._expandFillAllForArrayType(
|
|
238
|
+
initValue,
|
|
239
|
+
arrayTypeCtx,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
// MISRA C:2012 Rules 9.3/9.4 - String literals don't fill all inner array bytes,
|
|
243
|
+
// but C standard guarantees zero-initialization of remaining elements
|
|
244
|
+
const suppression =
|
|
245
|
+
"// cppcheck-suppress misra-c2012-9.3\n// cppcheck-suppress misra-c2012-9.4\n";
|
|
246
|
+
return {
|
|
247
|
+
code: `${suppression}${decl} = ${finalInitValue};`,
|
|
248
|
+
handled: true,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get the numeric size from the first arrayTypeDimension, or null if not numeric.
|
|
254
|
+
* Used by arrayType-based string arrays (string<N>[M]).
|
|
255
|
+
*/
|
|
256
|
+
private static _getArrayTypeDeclaredSize(
|
|
257
|
+
arrayTypeCtx: Parser.ArrayTypeContext,
|
|
258
|
+
): number | null {
|
|
259
|
+
const dims = arrayTypeCtx.arrayTypeDimension();
|
|
260
|
+
if (dims.length === 0) {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
const firstDimExpr = dims[0].expression();
|
|
264
|
+
return StringDeclHelper._parseNumericSize(firstDimExpr);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Expand fill-all syntax for arrayType-based string arrays.
|
|
269
|
+
* Delegates to the common fill-all expansion logic with size from arrayType.
|
|
270
|
+
*/
|
|
271
|
+
private static _expandFillAllForArrayType(
|
|
272
|
+
initValue: string,
|
|
273
|
+
arrayTypeCtx: Parser.ArrayTypeContext,
|
|
274
|
+
): string {
|
|
275
|
+
const declaredSize =
|
|
276
|
+
StringDeclHelper._getArrayTypeDeclaredSize(arrayTypeCtx);
|
|
277
|
+
return StringDeclHelper._expandFillAll(initValue, declaredSize);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Common fill-all expansion logic shared between arrayType and arrayDimension paths.
|
|
282
|
+
*/
|
|
283
|
+
private static _expandFillAll(
|
|
284
|
+
initValue: string,
|
|
285
|
+
declaredSize: number | null,
|
|
286
|
+
): string {
|
|
287
|
+
const fillVal = CodeGenState.lastArrayFillValue;
|
|
288
|
+
if (fillVal === undefined) {
|
|
289
|
+
return initValue;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Empty string fill doesn't need expansion (C handles {""} correctly)
|
|
293
|
+
if (fillVal === '""') {
|
|
294
|
+
return initValue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (declaredSize === null) {
|
|
298
|
+
return initValue;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const elements = new Array<string>(declaredSize).fill(fillVal);
|
|
302
|
+
return `{${elements.join(", ")}}`;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Parse a numeric size from an expression, or return null if not numeric.
|
|
307
|
+
* Shared helper to eliminate duplicate parsing logic.
|
|
308
|
+
*/
|
|
309
|
+
private static _parseNumericSize(
|
|
310
|
+
expr: Parser.ExpressionContext | null | undefined,
|
|
311
|
+
): number | null {
|
|
312
|
+
if (!expr) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
const sizeText = expr.getText();
|
|
316
|
+
if (!/^\d+$/.exec(sizeText)) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
return Number.parseInt(sizeText, 10);
|
|
320
|
+
}
|
|
321
|
+
|
|
132
322
|
/**
|
|
133
323
|
* Generate bounded string declaration (string<N>).
|
|
134
324
|
*/
|
|
@@ -207,24 +397,52 @@ class StringDeclHelper {
|
|
|
207
397
|
);
|
|
208
398
|
}
|
|
209
399
|
|
|
210
|
-
// Validate and
|
|
211
|
-
|
|
212
|
-
|
|
400
|
+
// Validate and check if it's a literal or variable
|
|
401
|
+
const exprText = expression.getText();
|
|
402
|
+
const isLiteral = StringDeclHelper._validateStringInit(
|
|
403
|
+
exprText,
|
|
213
404
|
capacity,
|
|
214
405
|
callbacks,
|
|
215
406
|
);
|
|
216
|
-
|
|
217
|
-
|
|
407
|
+
|
|
408
|
+
if (isLiteral) {
|
|
409
|
+
// String literal: can use direct initialization
|
|
410
|
+
const code = `${extern}${constMod}char ${name}[${capacity + 1}] = ${callbacks.generateExpression(expression)};`;
|
|
411
|
+
return { code, handled: true };
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// String variable: cannot use C array initialization, so declare empty and
|
|
415
|
+
// copy. Issue #1044: use the same bounded copy as the reassignment path
|
|
416
|
+
// (strncpy + explicit null terminator via StringUtils.copyWithNull) rather
|
|
417
|
+
// than an unbounded strcpy, which flawfinder flags as CWE-120.
|
|
418
|
+
// Issue #1030: string-to-string initialization
|
|
419
|
+
if (!CodeGenState.inFunctionBody) {
|
|
420
|
+
throw new Error(
|
|
421
|
+
`Error: String initialization from variable cannot be used at global scope. ` +
|
|
422
|
+
`Move the declaration inside a function, or use an empty initializer and assign later.`,
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const srcExpr = callbacks.generateExpression(expression);
|
|
427
|
+
// Issue #1037: continuation lines carry no indent of their own — the block
|
|
428
|
+
// emitter (CodeGenerator.generateBlock) prefixes every line.
|
|
429
|
+
const lines: string[] = [];
|
|
430
|
+
lines.push(
|
|
431
|
+
`${constMod}char ${name}[${capacity + 1}] = "";`,
|
|
432
|
+
StringUtils.copyWithNull(name, srcExpr, capacity),
|
|
433
|
+
);
|
|
434
|
+
return { code: lines.join("\n"), handled: true };
|
|
218
435
|
}
|
|
219
436
|
|
|
220
437
|
/**
|
|
221
438
|
* Validate string initialization (literal length and variable capacity)
|
|
439
|
+
* Returns true if the expression is a string literal, false if it's a variable.
|
|
222
440
|
*/
|
|
223
441
|
private static _validateStringInit(
|
|
224
442
|
exprText: string,
|
|
225
443
|
capacity: number,
|
|
226
444
|
callbacks: IStringDeclCallbacks,
|
|
227
|
-
):
|
|
445
|
+
): boolean {
|
|
228
446
|
// Validate string literal fits capacity
|
|
229
447
|
if (exprText.startsWith('"') && exprText.endsWith('"')) {
|
|
230
448
|
const content = StringUtils.literalLength(exprText);
|
|
@@ -233,6 +451,7 @@ class StringDeclHelper {
|
|
|
233
451
|
`Error: String literal (${content} chars) exceeds string<${capacity}> capacity`,
|
|
234
452
|
);
|
|
235
453
|
}
|
|
454
|
+
return true; // Is a literal
|
|
236
455
|
}
|
|
237
456
|
|
|
238
457
|
// Check for string variable assignment
|
|
@@ -242,6 +461,7 @@ class StringDeclHelper {
|
|
|
242
461
|
`Error: Cannot assign string<${srcCapacity}> to string<${capacity}> (potential truncation)`,
|
|
243
462
|
);
|
|
244
463
|
}
|
|
464
|
+
return false; // Is a variable (not a literal)
|
|
245
465
|
}
|
|
246
466
|
|
|
247
467
|
/**
|
|
@@ -373,47 +593,25 @@ class StringDeclHelper {
|
|
|
373
593
|
/**
|
|
374
594
|
* Expand fill-all syntax if needed.
|
|
375
595
|
* ["Hello"*] with size 3 -> {"Hello", "Hello", "Hello"}
|
|
596
|
+
* Delegates to the common fill-all expansion logic with size from arrayDimension.
|
|
376
597
|
*/
|
|
377
598
|
private static _expandFillAllIfNeeded(
|
|
378
599
|
initValue: string,
|
|
379
600
|
arrayDims: Parser.ArrayDimensionContext[],
|
|
380
601
|
): string {
|
|
381
|
-
const fillVal = CodeGenState.lastArrayFillValue;
|
|
382
|
-
if (fillVal === undefined) {
|
|
383
|
-
return initValue;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Empty string fill doesn't need expansion (C handles {""} correctly)
|
|
387
|
-
if (fillVal === '""') {
|
|
388
|
-
return initValue;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
602
|
const declaredSize = StringDeclHelper._getFirstDimNumericSize(arrayDims);
|
|
392
|
-
|
|
393
|
-
return initValue;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const elements = new Array<string>(declaredSize).fill(fillVal);
|
|
397
|
-
return `{${elements.join(", ")}}`;
|
|
603
|
+
return StringDeclHelper._expandFillAll(initValue, declaredSize);
|
|
398
604
|
}
|
|
399
605
|
|
|
400
606
|
/**
|
|
401
607
|
* Get the numeric size from the first array dimension, or null if not numeric.
|
|
608
|
+
* Used by arrayDimension-based string arrays (string<N> arr[M]).
|
|
402
609
|
*/
|
|
403
610
|
private static _getFirstDimNumericSize(
|
|
404
611
|
arrayDims: Parser.ArrayDimensionContext[],
|
|
405
612
|
): number | null {
|
|
406
613
|
const firstDimExpr = arrayDims[0]?.expression();
|
|
407
|
-
|
|
408
|
-
return null;
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const sizeText = firstDimExpr.getText();
|
|
412
|
-
if (!/^\d+$/.exec(sizeText)) {
|
|
413
|
-
return null;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
return Number.parseInt(sizeText, 10);
|
|
614
|
+
return StringDeclHelper._parseNumericSize(firstDimExpr);
|
|
417
615
|
}
|
|
418
616
|
|
|
419
617
|
/**
|
|
@@ -442,14 +640,14 @@ class StringDeclHelper {
|
|
|
442
640
|
);
|
|
443
641
|
}
|
|
444
642
|
|
|
445
|
-
// Generate safe concatenation code
|
|
446
|
-
|
|
643
|
+
// Generate safe concatenation code. Issue #1037: continuation lines carry
|
|
644
|
+
// no indent of their own — the block emitter prefixes every line.
|
|
447
645
|
const lines: string[] = [];
|
|
448
646
|
lines.push(
|
|
449
647
|
`${constMod}char ${name}[${capacity + 1}] = "";`,
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
`${
|
|
648
|
+
`strncpy(${name}, ${concatOps.left}, ${capacity});`,
|
|
649
|
+
`strncat(${name}, ${concatOps.right}, ${capacity} - strlen(${name}));`,
|
|
650
|
+
`${name}[${capacity}] = ${C_NULL_CHAR};`,
|
|
453
651
|
);
|
|
454
652
|
return { code: lines.join("\n"), handled: true };
|
|
455
653
|
}
|
|
@@ -493,13 +691,13 @@ class StringDeclHelper {
|
|
|
493
691
|
);
|
|
494
692
|
}
|
|
495
693
|
|
|
496
|
-
// Generate safe substring extraction code
|
|
497
|
-
|
|
694
|
+
// Generate safe substring extraction code. Issue #1037: continuation lines
|
|
695
|
+
// carry no indent of their own — the block emitter prefixes every line.
|
|
498
696
|
const lines: string[] = [];
|
|
499
697
|
lines.push(
|
|
500
698
|
`${constMod}char ${name}[${capacity + 1}] = "";`,
|
|
501
|
-
|
|
502
|
-
`${
|
|
699
|
+
`strncpy(${name}, ${substringOps.source} + ${substringOps.start}, ${substringOps.length});`,
|
|
700
|
+
`${name}[${substringOps.length}] = ${C_NULL_CHAR};`,
|
|
503
701
|
);
|
|
504
702
|
return { code: lines.join("\n"), handled: true };
|
|
505
703
|
}
|
|
@@ -87,27 +87,6 @@ class SymbolLookupHelper {
|
|
|
87
87
|
return symbols.some((sym) => sym.kind === "namespace");
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
/**
|
|
91
|
-
* Check if a type name is from a C++ header.
|
|
92
|
-
* Issue #304: Used to determine whether to use {} or {0} for initialization.
|
|
93
|
-
* C++ types with constructors may fail with {0} but work with {}.
|
|
94
|
-
*/
|
|
95
|
-
static isCppType(
|
|
96
|
-
symbolTable: ISymbolTable | null | undefined,
|
|
97
|
-
typeName: string,
|
|
98
|
-
): boolean {
|
|
99
|
-
if (!symbolTable) return false;
|
|
100
|
-
|
|
101
|
-
const symbols = symbolTable.getOverloads(typeName);
|
|
102
|
-
const cppTypeKinds = new Set<TSymbolKind>(["struct", "class", "type"]);
|
|
103
|
-
|
|
104
|
-
return symbols.some(
|
|
105
|
-
(sym) =>
|
|
106
|
-
sym.sourceLanguage === ESourceLanguage.Cpp &&
|
|
107
|
-
cppTypeKinds.has(sym.kind),
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
90
|
/**
|
|
112
91
|
* Check if a function is a C-Next function (uses pass-by-reference semantics).
|
|
113
92
|
* Returns true if the function is found in symbol table as C-Next.
|
|
@@ -29,6 +29,19 @@ interface ITypeGenerationDeps {
|
|
|
29
29
|
validateCrossScopeVisibility: (scope: string, member: string) => void;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Common interface for type contexts that share the same type accessors.
|
|
34
|
+
* Both TypeContext and ArrayTypeContext have these methods.
|
|
35
|
+
*/
|
|
36
|
+
interface ITypeAccessors {
|
|
37
|
+
primitiveType(): Parser.PrimitiveTypeContext | null;
|
|
38
|
+
userType(): Parser.UserTypeContext | null;
|
|
39
|
+
stringType(): Parser.StringTypeContext | null;
|
|
40
|
+
scopedType(): Parser.ScopedTypeContext | null;
|
|
41
|
+
qualifiedType(): Parser.QualifiedTypeContext | null;
|
|
42
|
+
globalType(): Parser.GlobalTypeContext | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
class TypeGenerationHelper {
|
|
33
46
|
/**
|
|
34
47
|
* Generate C type for a primitive type.
|
|
@@ -154,41 +167,35 @@ class TypeGenerationHelper {
|
|
|
154
167
|
}
|
|
155
168
|
|
|
156
169
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
170
|
+
* Dispatch type generation for contexts that share common type accessors.
|
|
171
|
+
* Handles scoped, qualified, global, primitive, string, and user types.
|
|
172
|
+
* Used by both bare type contexts and array element type contexts.
|
|
173
|
+
*
|
|
174
|
+
* @returns The resolved C type string, or null if no matching type accessor found
|
|
159
175
|
*/
|
|
160
|
-
static
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
// Note: caller is responsible for handling the include
|
|
166
|
-
return result.cType;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Bounded string type
|
|
170
|
-
if (ctx.stringType()) {
|
|
176
|
+
private static dispatchTypeGeneration(
|
|
177
|
+
accessors: ITypeAccessors,
|
|
178
|
+
deps: ITypeGenerationDeps,
|
|
179
|
+
): string | null {
|
|
180
|
+
if (accessors.stringType()) {
|
|
171
181
|
return TypeGenerationHelper.generateStringType();
|
|
172
182
|
}
|
|
173
183
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const typeName = ctx.scopedType()!.IDENTIFIER().getText();
|
|
184
|
+
if (accessors.scopedType()) {
|
|
185
|
+
const typeName = accessors.scopedType()!.IDENTIFIER().getText();
|
|
177
186
|
return TypeGenerationHelper.generateScopedType(
|
|
178
187
|
typeName,
|
|
179
188
|
deps.currentScope,
|
|
180
189
|
);
|
|
181
190
|
}
|
|
182
191
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const typeName = ctx.globalType()!.IDENTIFIER().getText();
|
|
192
|
+
if (accessors.globalType()) {
|
|
193
|
+
const typeName = accessors.globalType()!.IDENTIFIER().getText();
|
|
186
194
|
return TypeGenerationHelper.generateGlobalType(typeName);
|
|
187
195
|
}
|
|
188
196
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const identifiers = ctx.qualifiedType()!.IDENTIFIER();
|
|
197
|
+
if (accessors.qualifiedType()) {
|
|
198
|
+
const identifiers = accessors.qualifiedType()!.IDENTIFIER();
|
|
192
199
|
const identifierNames = identifiers.map((id) => id.getText());
|
|
193
200
|
const isCpp = deps.isCppScopeSymbol(identifierNames[0]);
|
|
194
201
|
return TypeGenerationHelper.generateQualifiedType(
|
|
@@ -198,30 +205,44 @@ class TypeGenerationHelper {
|
|
|
198
205
|
);
|
|
199
206
|
}
|
|
200
207
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
208
|
+
if (accessors.primitiveType()) {
|
|
209
|
+
const type = accessors.primitiveType()!.getText();
|
|
210
|
+
return TYPE_MAP[type] || type;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (accessors.userType()) {
|
|
214
|
+
const typeName = accessors.userType()!.getText();
|
|
215
|
+
// ADR-046: cstring maps to char* for C library interop
|
|
216
|
+
if (typeName === "cstring") {
|
|
217
|
+
return "char*";
|
|
218
|
+
}
|
|
204
219
|
const needsStruct = deps.checkNeedsStructKeyword(typeName);
|
|
205
220
|
return TypeGenerationHelper.generateUserType(typeName, needsStruct);
|
|
206
221
|
}
|
|
207
222
|
|
|
208
|
-
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Full type generation using all dependencies.
|
|
228
|
+
* This is the main entry point that handles all type contexts.
|
|
229
|
+
*/
|
|
230
|
+
static generate(ctx: Parser.TypeContext, deps: ITypeGenerationDeps): string {
|
|
231
|
+
// Array type - dispatch on the element type
|
|
209
232
|
if (ctx.arrayType()) {
|
|
210
233
|
const arrCtx = ctx.arrayType()!;
|
|
211
|
-
|
|
212
|
-
if (
|
|
213
|
-
return
|
|
234
|
+
const result = TypeGenerationHelper.dispatchTypeGeneration(arrCtx, deps);
|
|
235
|
+
if (result !== null) {
|
|
236
|
+
return result;
|
|
214
237
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
needsStruct,
|
|
224
|
-
);
|
|
238
|
+
// Fallback for array types without recognized element type
|
|
239
|
+
return ctx.getText();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Non-array types - dispatch directly
|
|
243
|
+
const result = TypeGenerationHelper.dispatchTypeGeneration(ctx, deps);
|
|
244
|
+
if (result !== null) {
|
|
245
|
+
return result;
|
|
225
246
|
}
|
|
226
247
|
|
|
227
248
|
// Void or fallback
|