c-next 0.2.15 → 0.2.17

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.
Files changed (67) hide show
  1. package/README.md +16 -0
  2. package/dist/index.js +1403 -427
  3. package/dist/index.js.map +3 -3
  4. package/grammar/CNext.g4 +4 -0
  5. package/package.json +5 -1
  6. package/src/transpiler/Transpiler.ts +90 -22
  7. package/src/transpiler/__tests__/DualCodePaths.test.ts +1 -1
  8. package/src/transpiler/logic/analysis/FunctionCallAnalyzer.ts +57 -10
  9. package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +186 -14
  10. package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +124 -12
  11. package/src/transpiler/logic/analysis/__tests__/FunctionCallAnalyzer.test.ts +200 -0
  12. package/src/transpiler/logic/analysis/__tests__/InitializationAnalyzer.test.ts +386 -1
  13. package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +211 -0
  14. package/src/transpiler/logic/parser/grammar/CNext.interp +1 -1
  15. package/src/transpiler/logic/parser/grammar/CNextParser.ts +154 -86
  16. package/src/transpiler/logic/symbols/SymbolTable.ts +54 -12
  17. package/src/transpiler/logic/symbols/SymbolUtils.ts +21 -8
  18. package/src/transpiler/logic/symbols/__tests__/SymbolTable.test.ts +39 -4
  19. package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +2 -1
  20. package/src/transpiler/logic/symbols/c/utils/DeclaratorUtils.ts +2 -1
  21. package/src/transpiler/logic/symbols/cnext/__tests__/CNextResolver.integration.test.ts +5 -2
  22. package/src/transpiler/logic/symbols/cnext/__tests__/ScopeCollector.test.ts +5 -2
  23. package/src/transpiler/logic/symbols/cnext/collectors/ScopeCollector.ts +7 -2
  24. package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +15 -2
  25. package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +64 -50
  26. package/src/transpiler/logic/symbols/cpp/utils/DeclaratorUtils.ts +4 -2
  27. package/src/transpiler/output/codegen/CodeGenerator.ts +151 -94
  28. package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +167 -18
  29. package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +150 -34
  30. package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +2 -2
  31. package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +11 -0
  32. package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +32 -8
  33. package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +91 -1
  34. package/src/transpiler/output/codegen/generators/expressions/BinaryExprGenerator.ts +43 -24
  35. package/src/transpiler/output/codegen/generators/expressions/CallExprGenerator.ts +48 -43
  36. package/src/transpiler/output/codegen/generators/expressions/ExpressionGenerator.ts +9 -2
  37. package/src/transpiler/output/codegen/generators/expressions/__tests__/CallExprGenerator.test.ts +44 -0
  38. package/src/transpiler/output/codegen/generators/expressions/__tests__/ExpressionGenerator.test.ts +82 -1
  39. package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +17 -3
  40. package/src/transpiler/output/codegen/helpers/ParameterSignatureBuilder.ts +17 -4
  41. package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +227 -32
  42. package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +0 -21
  43. package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +60 -39
  44. package/src/transpiler/output/codegen/helpers/TypeRegistrationEngine.ts +170 -36
  45. package/src/transpiler/output/codegen/helpers/VariableDeclHelper.ts +37 -39
  46. package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +117 -0
  47. package/src/transpiler/output/codegen/helpers/__tests__/ParameterSignatureBuilder.test.ts +94 -2
  48. package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +268 -1
  49. package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +0 -64
  50. package/src/transpiler/output/codegen/helpers/__tests__/TypeRegistrationEngine.test.ts +101 -0
  51. package/src/transpiler/output/codegen/helpers/__tests__/VariableDeclHelper.test.ts +29 -5
  52. package/src/transpiler/output/codegen/types/ICallbackTypeInfo.ts +2 -1
  53. package/src/transpiler/output/codegen/types/IParameterInput.ts +7 -0
  54. package/src/transpiler/output/headers/BaseHeaderGenerator.ts +8 -0
  55. package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +75 -0
  56. package/src/transpiler/output/headers/__tests__/HeaderGeneratorUtils.test.ts +280 -0
  57. package/src/transpiler/output/headers/generators/IHeaderTypeInput.ts +13 -0
  58. package/src/transpiler/output/headers/generators/generateStructHeader.ts +48 -28
  59. package/src/transpiler/state/CodeGenState.ts +71 -6
  60. package/src/transpiler/state/__tests__/CodeGenState.test.ts +253 -11
  61. package/src/transpiler/types/symbols/c/ICFieldInfo.ts +6 -2
  62. package/src/transpiler/types/symbols/cpp/ICppFieldInfo.ts +5 -2
  63. package/src/utils/LiteralUtils.ts +23 -0
  64. package/src/utils/ScopeUtils.ts +19 -0
  65. package/src/utils/__tests__/LiteralUtils.test.ts +101 -0
  66. package/src/utils/__tests__/ScopeUtils.test.ts +10 -0
  67. package/src/utils/types/IParameterSymbol.ts +1 -0
@@ -376,6 +376,7 @@ describe("CodeGenState", () => {
376
376
  isArray: false,
377
377
  isConst: false,
378
378
  isPointer: false,
379
+ isStruct: false,
379
380
  arrayDims: "",
380
381
  },
381
382
  ],
@@ -1011,25 +1012,35 @@ describe("CodeGenState", () => {
1011
1012
  describe("Opaque Scope Variable Helpers (Issue #948)", () => {
1012
1013
  it("markOpaqueScopeVariable adds to opaqueScopeVariables", () => {
1013
1014
  CodeGenState.markOpaqueScopeVariable("MyScope_widget");
1014
- expect(CodeGenState.isOpaqueScopeVariable("MyScope_widget")).toBe(true);
1015
+ expect(CodeGenState.isOpaqueScopeVariableAccess("MyScope_widget")).toBe(
1016
+ true,
1017
+ );
1015
1018
  });
1016
1019
 
1017
- it("isOpaqueScopeVariable returns false for unknown variable", () => {
1018
- expect(CodeGenState.isOpaqueScopeVariable("Unknown_var")).toBe(false);
1020
+ it("isOpaqueScopeVariableAccess returns false for unknown variable", () => {
1021
+ expect(CodeGenState.isOpaqueScopeVariableAccess("Unknown_var")).toBe(
1022
+ false,
1023
+ );
1019
1024
  });
1020
1025
 
1021
- it("isOpaqueScopeVariable returns true for marked variable", () => {
1026
+ it("isOpaqueScopeVariableAccess returns true for marked variable", () => {
1022
1027
  CodeGenState.markOpaqueScopeVariable("Gui_display");
1023
- expect(CodeGenState.isOpaqueScopeVariable("Gui_display")).toBe(true);
1028
+ expect(CodeGenState.isOpaqueScopeVariableAccess("Gui_display")).toBe(
1029
+ true,
1030
+ );
1024
1031
  });
1025
1032
 
1026
1033
  it("reset clears opaqueScopeVariables", () => {
1027
1034
  CodeGenState.markOpaqueScopeVariable("Test_opaque");
1028
- expect(CodeGenState.isOpaqueScopeVariable("Test_opaque")).toBe(true);
1035
+ expect(CodeGenState.isOpaqueScopeVariableAccess("Test_opaque")).toBe(
1036
+ true,
1037
+ );
1029
1038
 
1030
1039
  CodeGenState.reset();
1031
1040
 
1032
- expect(CodeGenState.isOpaqueScopeVariable("Test_opaque")).toBe(false);
1041
+ expect(CodeGenState.isOpaqueScopeVariableAccess("Test_opaque")).toBe(
1042
+ false,
1043
+ );
1033
1044
  });
1034
1045
 
1035
1046
  it("handles multiple opaque scope variables", () => {
@@ -1037,10 +1048,241 @@ describe("CodeGenState", () => {
1037
1048
  CodeGenState.markOpaqueScopeVariable("Scope1_display");
1038
1049
  CodeGenState.markOpaqueScopeVariable("Scope2_handle");
1039
1050
 
1040
- expect(CodeGenState.isOpaqueScopeVariable("Scope1_widget")).toBe(true);
1041
- expect(CodeGenState.isOpaqueScopeVariable("Scope1_display")).toBe(true);
1042
- expect(CodeGenState.isOpaqueScopeVariable("Scope2_handle")).toBe(true);
1043
- expect(CodeGenState.isOpaqueScopeVariable("Scope1_other")).toBe(false);
1051
+ expect(CodeGenState.isOpaqueScopeVariableAccess("Scope1_widget")).toBe(
1052
+ true,
1053
+ );
1054
+ expect(CodeGenState.isOpaqueScopeVariableAccess("Scope1_display")).toBe(
1055
+ true,
1056
+ );
1057
+ expect(CodeGenState.isOpaqueScopeVariableAccess("Scope2_handle")).toBe(
1058
+ true,
1059
+ );
1060
+ expect(CodeGenState.isOpaqueScopeVariableAccess("Scope1_other")).toBe(
1061
+ false,
1062
+ );
1063
+ });
1064
+
1065
+ // Issue #996: An element of an opaque-handle array is itself a pointer.
1066
+ it("isOpaqueScopeVariableAccess matches array-element access of an opaque array", () => {
1067
+ CodeGenState.markOpaqueScopeVariable("UI_widgets");
1068
+
1069
+ expect(CodeGenState.isOpaqueScopeVariableAccess("UI_widgets[i]")).toBe(
1070
+ true,
1071
+ );
1072
+ expect(CodeGenState.isOpaqueScopeVariableAccess("UI_widgets[0]")).toBe(
1073
+ true,
1074
+ );
1075
+ });
1076
+
1077
+ it("isOpaqueScopeVariableAccess does not match subscript of a non-opaque array", () => {
1078
+ // Base array name was never marked opaque.
1079
+ expect(CodeGenState.isOpaqueScopeVariableAccess("UI_counts[i]")).toBe(
1080
+ false,
1081
+ );
1082
+ });
1083
+
1084
+ it("isOpaqueScopeVariableAccess does not match a different array that shares a prefix", () => {
1085
+ CodeGenState.markOpaqueScopeVariable("UI_widgets");
1086
+
1087
+ // "UI_widgetsExtra" is a distinct variable, not a subscript of UI_widgets.
1088
+ expect(CodeGenState.isOpaqueScopeVariableAccess("UI_widgetsExtra")).toBe(
1089
+ false,
1090
+ );
1091
+ });
1092
+ });
1093
+
1094
+ describe("withDeclarationInit()", () => {
1095
+ it("sets inDeclarationInit to true during callback", () => {
1096
+ CodeGenState.inDeclarationInit = false;
1097
+ let valueInside = false;
1098
+
1099
+ CodeGenState.withDeclarationInit(() => {
1100
+ valueInside = CodeGenState.inDeclarationInit;
1101
+ });
1102
+
1103
+ expect(valueInside).toBe(true);
1104
+ });
1105
+
1106
+ it("restores prior value after callback", () => {
1107
+ CodeGenState.inDeclarationInit = false;
1108
+
1109
+ CodeGenState.withDeclarationInit(() => {
1110
+ // inside: true
1111
+ });
1112
+
1113
+ expect(CodeGenState.inDeclarationInit).toBe(false);
1114
+ });
1115
+
1116
+ it("restores prior value even when already true", () => {
1117
+ CodeGenState.inDeclarationInit = true;
1118
+
1119
+ CodeGenState.withDeclarationInit(() => {
1120
+ expect(CodeGenState.inDeclarationInit).toBe(true);
1121
+ });
1122
+
1123
+ expect(CodeGenState.inDeclarationInit).toBe(true);
1124
+ });
1125
+
1126
+ it("returns the callback result", () => {
1127
+ const result = CodeGenState.withDeclarationInit(() => "hello");
1128
+ expect(result).toBe("hello");
1129
+ });
1130
+
1131
+ it("restores prior value on exception", () => {
1132
+ CodeGenState.inDeclarationInit = false;
1133
+
1134
+ expect(() =>
1135
+ CodeGenState.withDeclarationInit(() => {
1136
+ throw new Error("test error");
1137
+ }),
1138
+ ).toThrow("test error");
1139
+
1140
+ expect(CodeGenState.inDeclarationInit).toBe(false);
1141
+ });
1142
+ });
1143
+
1144
+ describe("withoutDeclarationInit()", () => {
1145
+ it("sets inDeclarationInit to false during callback", () => {
1146
+ CodeGenState.inDeclarationInit = true;
1147
+ let valueInside = true;
1148
+
1149
+ CodeGenState.withoutDeclarationInit(() => {
1150
+ valueInside = CodeGenState.inDeclarationInit;
1151
+ });
1152
+
1153
+ expect(valueInside).toBe(false);
1154
+ });
1155
+
1156
+ it("restores prior value after callback", () => {
1157
+ CodeGenState.inDeclarationInit = true;
1158
+
1159
+ CodeGenState.withoutDeclarationInit(() => {
1160
+ // inside: false
1161
+ });
1162
+
1163
+ expect(CodeGenState.inDeclarationInit).toBe(true);
1164
+ });
1165
+
1166
+ it("restores prior value even when already false", () => {
1167
+ CodeGenState.inDeclarationInit = false;
1168
+
1169
+ CodeGenState.withoutDeclarationInit(() => {
1170
+ expect(CodeGenState.inDeclarationInit).toBe(false);
1171
+ });
1172
+
1173
+ expect(CodeGenState.inDeclarationInit).toBe(false);
1174
+ });
1175
+
1176
+ it("returns the callback result", () => {
1177
+ const result = CodeGenState.withoutDeclarationInit(() => 42);
1178
+ expect(result).toBe(42);
1179
+ });
1180
+
1181
+ it("restores prior value on exception", () => {
1182
+ CodeGenState.inDeclarationInit = true;
1183
+
1184
+ expect(() =>
1185
+ CodeGenState.withoutDeclarationInit(() => {
1186
+ throw new Error("test error");
1187
+ }),
1188
+ ).toThrow("test error");
1189
+
1190
+ expect(CodeGenState.inDeclarationInit).toBe(true);
1191
+ });
1192
+
1193
+ it("nests correctly with withDeclarationInit", () => {
1194
+ CodeGenState.inDeclarationInit = false;
1195
+
1196
+ CodeGenState.withDeclarationInit(() => {
1197
+ expect(CodeGenState.inDeclarationInit).toBe(true);
1198
+
1199
+ CodeGenState.withoutDeclarationInit(() => {
1200
+ expect(CodeGenState.inDeclarationInit).toBe(false);
1201
+ });
1202
+
1203
+ expect(CodeGenState.inDeclarationInit).toBe(true);
1204
+ });
1205
+
1206
+ expect(CodeGenState.inDeclarationInit).toBe(false);
1207
+ });
1208
+ });
1209
+
1210
+ describe("withoutExpectedType()", () => {
1211
+ it("clears expectedType during callback", () => {
1212
+ CodeGenState.expectedType = "u32";
1213
+ let typeInside: string | null = "notCleared";
1214
+
1215
+ CodeGenState.withoutExpectedType(() => {
1216
+ typeInside = CodeGenState.expectedType;
1217
+ });
1218
+
1219
+ expect(typeInside).toBeNull();
1220
+ });
1221
+
1222
+ it("clears suppressBareEnumResolution during callback", () => {
1223
+ CodeGenState.suppressBareEnumResolution = true;
1224
+ let suppressInside = true;
1225
+
1226
+ CodeGenState.withoutExpectedType(() => {
1227
+ suppressInside = CodeGenState.suppressBareEnumResolution;
1228
+ });
1229
+
1230
+ expect(suppressInside).toBe(false);
1231
+ });
1232
+
1233
+ it("restores expectedType after callback", () => {
1234
+ CodeGenState.expectedType = "i32";
1235
+
1236
+ CodeGenState.withoutExpectedType(() => {
1237
+ // inside: null
1238
+ });
1239
+
1240
+ expect(CodeGenState.expectedType).toBe("i32");
1241
+ });
1242
+
1243
+ it("restores suppressBareEnumResolution after callback", () => {
1244
+ CodeGenState.suppressBareEnumResolution = true;
1245
+
1246
+ CodeGenState.withoutExpectedType(() => {
1247
+ // inside: false
1248
+ });
1249
+
1250
+ expect(CodeGenState.suppressBareEnumResolution).toBe(true);
1251
+ });
1252
+
1253
+ it("returns the callback result", () => {
1254
+ const result = CodeGenState.withoutExpectedType(() => 123);
1255
+ expect(result).toBe(123);
1256
+ });
1257
+
1258
+ it("restores values on exception", () => {
1259
+ CodeGenState.expectedType = "bool";
1260
+ CodeGenState.suppressBareEnumResolution = true;
1261
+
1262
+ expect(() =>
1263
+ CodeGenState.withoutExpectedType(() => {
1264
+ throw new Error("test error");
1265
+ }),
1266
+ ).toThrow("test error");
1267
+
1268
+ expect(CodeGenState.expectedType).toBe("bool");
1269
+ expect(CodeGenState.suppressBareEnumResolution).toBe(true);
1270
+ });
1271
+
1272
+ it("handles null expectedType correctly", () => {
1273
+ CodeGenState.expectedType = null;
1274
+ CodeGenState.suppressBareEnumResolution = false;
1275
+
1276
+ let executed = false;
1277
+ CodeGenState.withoutExpectedType(() => {
1278
+ executed = true;
1279
+ expect(CodeGenState.expectedType).toBeNull();
1280
+ expect(CodeGenState.suppressBareEnumResolution).toBe(false);
1281
+ });
1282
+
1283
+ expect(executed).toBe(true);
1284
+ expect(CodeGenState.expectedType).toBeNull();
1285
+ expect(CodeGenState.suppressBareEnumResolution).toBe(false);
1044
1286
  });
1045
1287
  });
1046
1288
  });
@@ -9,8 +9,12 @@ interface ICFieldInfo {
9
9
  /** Field type as string (e.g., "int", "char*") */
10
10
  readonly type: string;
11
11
 
12
- /** Array dimensions if this field is an array */
13
- readonly arrayDimensions?: ReadonlyArray<number>;
12
+ /**
13
+ * Array dimensions if this field is an array.
14
+ * Issue #981: Numbers for resolved dimensions, strings for macro names.
15
+ * Example: [8] for numeric, ["BUF_SIZE"] for macro-sized.
16
+ */
17
+ readonly arrayDimensions?: ReadonlyArray<number | string>;
14
18
  }
15
19
 
16
20
  export default ICFieldInfo;
@@ -9,8 +9,11 @@ interface ICppFieldInfo {
9
9
  /** Field type as string (e.g., "int", "std::string") */
10
10
  readonly type: string;
11
11
 
12
- /** Array dimensions if this field is an array */
13
- readonly arrayDimensions?: ReadonlyArray<number>;
12
+ /**
13
+ * Array dimensions if this field is an array.
14
+ * Issue #981: Supports both numeric literals and macro names.
15
+ */
16
+ readonly arrayDimensions?: ReadonlyArray<number | string>;
14
17
  }
15
18
 
16
19
  export default ICppFieldInfo;
@@ -68,9 +68,32 @@ class LiteralUtils {
68
68
  );
69
69
  }
70
70
 
71
+ // Issue #1010: Float literals (0.0, 0.0f, .0, etc.)
72
+ if (ctx.FLOAT_LITERAL()) {
73
+ return LiteralUtils.isFloatZero(text);
74
+ }
75
+
71
76
  return false;
72
77
  }
73
78
 
79
+ /**
80
+ * Check if a float literal string represents zero.
81
+ * Issue #1010: Detect float zero for division-by-zero checking.
82
+ *
83
+ * Handles: 0.0, .0, 0., 0.0f, 0.0F, 0.0e0, 0.0E0, etc.
84
+ *
85
+ * @param text - The float literal text
86
+ * @returns true if the float is zero
87
+ */
88
+ static isFloatZero(text: string): boolean {
89
+ // Remove optional float suffix (f, F)
90
+ const withoutSuffix = text.replace(/[fF]$/, "");
91
+
92
+ // Parse to number and check if zero
93
+ const value = Number.parseFloat(withoutSuffix);
94
+ return value === 0;
95
+ }
96
+
74
97
  /**
75
98
  * Check if a literal is a floating-point number.
76
99
  *
@@ -4,6 +4,7 @@
4
4
  * Provides utilities for creating and inspecting C-Next scopes.
5
5
  */
6
6
  import type IScopeSymbol from "../transpiler/types/symbols/IScopeSymbol";
7
+ import type TVisibility from "../transpiler/types/TVisibility";
7
8
  import ESourceLanguage from "./types/ESourceLanguage";
8
9
 
9
10
  class ScopeUtils {
@@ -79,6 +80,24 @@ class ScopeUtils {
79
80
  return scope.name === "" && scope.parent === scope;
80
81
  }
81
82
 
83
+ // ============================================================================
84
+ // Visibility Utilities
85
+ // ============================================================================
86
+
87
+ /**
88
+ * ADR-016: Get the default visibility for a scope member based on its type.
89
+ *
90
+ * Member-type-aware defaults reduce boilerplate:
91
+ * - Functions: public by default (API surface)
92
+ * - Variables/types: private by default (internal state)
93
+ *
94
+ * @param isFunction - Whether the member is a function declaration
95
+ * @returns The default visibility for this member type
96
+ */
97
+ static getDefaultVisibility(isFunction: boolean): TVisibility {
98
+ return isFunction ? "public" : "private";
99
+ }
100
+
82
101
  // ============================================================================
83
102
  // Path Utilities
84
103
  // ============================================================================
@@ -306,6 +306,107 @@ describe("LiteralUtils", () => {
306
306
  });
307
307
  });
308
308
 
309
+ // ========================================================================
310
+ // isZero: Float Literals (Issue #1010)
311
+ // ========================================================================
312
+
313
+ describe("isZero - float literals (Issue #1010)", () => {
314
+ it("should return true for float zero (0.0)", () => {
315
+ const literal = extractFloatLiteral("0.0");
316
+ expect(literal).not.toBeNull();
317
+ expect(LiteralUtils.isZero(literal!)).toBe(true);
318
+ });
319
+
320
+ it("should return true for float zero with suffix (0.0f)", () => {
321
+ const literal = extractFloatLiteral("0.0f");
322
+ expect(literal).not.toBeNull();
323
+ expect(LiteralUtils.isZero(literal!)).toBe(true);
324
+ });
325
+
326
+ it("should return true for float zero with uppercase suffix (0.0F)", () => {
327
+ const literal = extractFloatLiteral("0.0F");
328
+ expect(literal).not.toBeNull();
329
+ expect(LiteralUtils.isZero(literal!)).toBe(true);
330
+ });
331
+
332
+ it("should return true for leading-dot zero (.0)", () => {
333
+ const literal = extractFloatLiteral(".0");
334
+ expect(literal).not.toBeNull();
335
+ expect(LiteralUtils.isZero(literal!)).toBe(true);
336
+ });
337
+
338
+ it("should return false for non-zero float (1.0)", () => {
339
+ const literal = extractFloatLiteral("1.0");
340
+ expect(literal).not.toBeNull();
341
+ expect(LiteralUtils.isZero(literal!)).toBe(false);
342
+ });
343
+
344
+ it("should return false for small non-zero float (0.001)", () => {
345
+ const literal = extractFloatLiteral("0.001");
346
+ expect(literal).not.toBeNull();
347
+ expect(LiteralUtils.isZero(literal!)).toBe(false);
348
+ });
349
+
350
+ it("should return false for negative float (-0.5)", () => {
351
+ const literal = extractFloatLiteral("-0.5");
352
+ // Note: -0.5 may not parse as a single literal due to negation
353
+ // This is expected - the unary minus is a separate operator
354
+ if (literal) {
355
+ expect(LiteralUtils.isZero(literal!)).toBe(false);
356
+ }
357
+ });
358
+ });
359
+
360
+ // ========================================================================
361
+ // isFloatZero (Issue #1010)
362
+ // ========================================================================
363
+
364
+ describe("isFloatZero - static method (Issue #1010)", () => {
365
+ it("should return true for 0.0", () => {
366
+ expect(LiteralUtils.isFloatZero("0.0")).toBe(true);
367
+ });
368
+
369
+ it("should return true for .0", () => {
370
+ expect(LiteralUtils.isFloatZero(".0")).toBe(true);
371
+ });
372
+
373
+ it("should return true for 0.", () => {
374
+ expect(LiteralUtils.isFloatZero("0.")).toBe(true);
375
+ });
376
+
377
+ it("should return true for 0.0f", () => {
378
+ expect(LiteralUtils.isFloatZero("0.0f")).toBe(true);
379
+ });
380
+
381
+ it("should return true for 0.0F", () => {
382
+ expect(LiteralUtils.isFloatZero("0.0F")).toBe(true);
383
+ });
384
+
385
+ it("should return true for scientific notation zero (0.0e0)", () => {
386
+ expect(LiteralUtils.isFloatZero("0.0e0")).toBe(true);
387
+ });
388
+
389
+ it("should return true for scientific notation zero (0e0)", () => {
390
+ expect(LiteralUtils.isFloatZero("0e0")).toBe(true);
391
+ });
392
+
393
+ it("should return false for 1.0", () => {
394
+ expect(LiteralUtils.isFloatZero("1.0")).toBe(false);
395
+ });
396
+
397
+ it("should return false for 0.5", () => {
398
+ expect(LiteralUtils.isFloatZero("0.5")).toBe(false);
399
+ });
400
+
401
+ it("should return false for 3.14", () => {
402
+ expect(LiteralUtils.isFloatZero("3.14")).toBe(false);
403
+ });
404
+
405
+ it("should return false for scientific notation non-zero (1e-10)", () => {
406
+ expect(LiteralUtils.isFloatZero("1e-10")).toBe(false);
407
+ });
408
+ });
409
+
309
410
  // ========================================================================
310
411
  // isFloat
311
412
  // ========================================================================
@@ -50,4 +50,14 @@ describe("IScopeSymbol", () => {
50
50
  expect(ScopeUtils.isGlobalScope(scope)).toBe(false);
51
51
  });
52
52
  });
53
+
54
+ describe("getDefaultVisibility", () => {
55
+ it("returns 'public' for functions (API surface)", () => {
56
+ expect(ScopeUtils.getDefaultVisibility(true)).toBe("public");
57
+ });
58
+
59
+ it("returns 'private' for non-functions (internal state)", () => {
60
+ expect(ScopeUtils.getDefaultVisibility(false)).toBe("private");
61
+ });
62
+ });
53
63
  });
@@ -10,6 +10,7 @@ interface IParameterSymbol {
10
10
  isAutoConst?: boolean; // Issue #268: true if parameter should get auto-const (unmodified pointer)
11
11
  isCallbackPointer?: boolean; // Issue #914: typedef says this param must be a pointer
12
12
  isCallbackConst?: boolean; // Issue #914: typedef says this param must be const
13
+ isOpaqueHandle?: boolean; // Issue #995: type is opaque (incomplete struct), needs pointer not reference
13
14
  }
14
15
 
15
16
  export default IParameterSymbol;