c-next 0.2.6 → 0.2.7
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/dist/index.js +387 -433
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
- package/src/transpiler/Transpiler.ts +50 -29
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -1
- package/src/transpiler/data/PathResolver.ts +10 -6
- package/src/transpiler/data/__tests__/PathResolver.test.ts +32 -0
- package/src/transpiler/logic/analysis/PassByValueAnalyzer.ts +1 -12
- package/src/transpiler/logic/analysis/SignedShiftAnalyzer.ts +239 -0
- package/src/transpiler/logic/analysis/__tests__/SignedShiftAnalyzer.test.ts +414 -0
- package/src/transpiler/logic/analysis/__tests__/StructFieldAnalyzer.test.ts +15 -75
- package/src/transpiler/logic/analysis/__tests__/runAnalyzers.test.ts +3 -15
- package/src/transpiler/logic/analysis/runAnalyzers.ts +11 -2
- package/src/transpiler/logic/analysis/types/ISignedShiftError.ts +15 -0
- package/src/transpiler/logic/symbols/SymbolUtils.ts +4 -1
- package/src/transpiler/logic/symbols/__tests__/SymbolUtils.test.ts +10 -11
- package/src/transpiler/logic/symbols/c/__tests__/CResolver.integration.test.ts +4 -4
- package/src/transpiler/logic/symbols/cpp/__tests__/CppResolver.integration.test.ts +4 -4
- package/src/transpiler/output/codegen/CodeGenerator.ts +12 -4
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.coverage.test.ts +3 -3
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +26 -26
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +12 -11
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +21 -21
- package/src/transpiler/output/codegen/generators/expressions/AccessExprGenerator.ts +7 -326
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +14 -275
- package/src/transpiler/output/codegen/generators/expressions/UnaryExprGenerator.ts +13 -1
- package/src/transpiler/output/codegen/generators/expressions/__tests__/AccessExprGenerator.test.ts +0 -573
- package/src/transpiler/output/codegen/generators/expressions/__tests__/PostfixExpressionGenerator.test.ts +8 -439
- package/src/transpiler/output/codegen/generators/expressions/__tests__/UnaryExprGenerator.test.ts +196 -0
- package/src/transpiler/output/codegen/helpers/ParameterInputAdapter.ts +7 -2
- package/src/transpiler/output/codegen/helpers/TypedefParamParser.ts +34 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ParameterInputAdapter.test.ts +48 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypedefParamParser.test.ts +88 -0
- package/src/transpiler/output/headers/HeaderGeneratorUtils.ts +15 -2
- package/src/transpiler/output/headers/__tests__/BaseHeaderGenerator.test.ts +87 -0
- package/src/utils/ExpressionUtils.ts +51 -0
- package/src/utils/types/IParameterSymbol.ts +2 -0
|
@@ -112,14 +112,13 @@ describe("SymbolUtils", () => {
|
|
|
112
112
|
|
|
113
113
|
// ========================================================================
|
|
114
114
|
// Reserved Field Names
|
|
115
|
+
// ADR-058: .length removed, so "length" is no longer reserved
|
|
115
116
|
// ========================================================================
|
|
116
117
|
|
|
117
118
|
describe("isReservedFieldName", () => {
|
|
118
|
-
it("should return
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
it("should return false for non-reserved names", () => {
|
|
119
|
+
it("should return false for all names (no reserved names after ADR-058)", () => {
|
|
120
|
+
// "length" is no longer reserved since .length was deprecated
|
|
121
|
+
expect(SymbolUtils.isReservedFieldName("length")).toBe(false);
|
|
123
122
|
expect(SymbolUtils.isReservedFieldName("x")).toBe(false);
|
|
124
123
|
expect(SymbolUtils.isReservedFieldName("data")).toBe(false);
|
|
125
124
|
expect(SymbolUtils.isReservedFieldName("size")).toBe(false);
|
|
@@ -128,10 +127,10 @@ describe("SymbolUtils", () => {
|
|
|
128
127
|
});
|
|
129
128
|
|
|
130
129
|
describe("getReservedFieldNames", () => {
|
|
131
|
-
it("should return array
|
|
130
|
+
it("should return empty array (no reserved names after ADR-058)", () => {
|
|
132
131
|
const reserved = SymbolUtils.getReservedFieldNames();
|
|
133
132
|
expect(Array.isArray(reserved)).toBe(true);
|
|
134
|
-
expect(reserved).
|
|
133
|
+
expect(reserved).toHaveLength(0);
|
|
135
134
|
});
|
|
136
135
|
});
|
|
137
136
|
|
|
@@ -140,11 +139,11 @@ describe("SymbolUtils", () => {
|
|
|
140
139
|
const msg = SymbolUtils.getReservedFieldWarning(
|
|
141
140
|
"C",
|
|
142
141
|
"MyStruct",
|
|
143
|
-
"
|
|
142
|
+
"someField",
|
|
144
143
|
);
|
|
145
144
|
expect(msg).toContain("C header struct");
|
|
146
145
|
expect(msg).toContain("MyStruct");
|
|
147
|
-
expect(msg).toContain("
|
|
146
|
+
expect(msg).toContain("someField");
|
|
148
147
|
expect(msg).toContain("conflicts with C-Next");
|
|
149
148
|
});
|
|
150
149
|
|
|
@@ -152,11 +151,11 @@ describe("SymbolUtils", () => {
|
|
|
152
151
|
const msg = SymbolUtils.getReservedFieldWarning(
|
|
153
152
|
"C++",
|
|
154
153
|
"MyClass",
|
|
155
|
-
"
|
|
154
|
+
"someField",
|
|
156
155
|
);
|
|
157
156
|
expect(msg).toContain("C++ header struct");
|
|
158
157
|
expect(msg).toContain("MyClass");
|
|
159
|
-
expect(msg).toContain("
|
|
158
|
+
expect(msg).toContain("someField");
|
|
160
159
|
});
|
|
161
160
|
});
|
|
162
161
|
});
|
|
@@ -350,14 +350,14 @@ describe("CResolver - Struct Fields", () => {
|
|
|
350
350
|
expect(fieldsMap?.get("inner")?.type).toBe("Inner");
|
|
351
351
|
});
|
|
352
352
|
|
|
353
|
-
|
|
353
|
+
// ADR-058: "length" is no longer reserved since .length was deprecated
|
|
354
|
+
it("does not warn about 'length' field names after ADR-058", () => {
|
|
354
355
|
const tree = TestHelpers.parseC(`struct Test { int length; };`);
|
|
355
356
|
const symbolTable = new SymbolTable();
|
|
356
357
|
const result = CResolver.resolve(tree!, "test.h", symbolTable);
|
|
357
358
|
|
|
358
|
-
|
|
359
|
-
expect(result.warnings
|
|
360
|
-
expect(result.warnings[0]).toContain("conflicts with C-Next");
|
|
359
|
+
// No warnings since "length" is no longer reserved
|
|
360
|
+
expect(result.warnings.length).toBe(0);
|
|
361
361
|
});
|
|
362
362
|
});
|
|
363
363
|
|
|
@@ -255,16 +255,16 @@ describe("CppResolver", () => {
|
|
|
255
255
|
});
|
|
256
256
|
});
|
|
257
257
|
|
|
258
|
+
// ADR-058: "length" is no longer reserved since .length was deprecated
|
|
258
259
|
describe("warnings", () => {
|
|
259
|
-
it("
|
|
260
|
+
it("does not warn about 'length' field names after ADR-058", () => {
|
|
260
261
|
const source = `class MyStruct { int length; };`;
|
|
261
262
|
const tree = TestHelpers.parseCpp(source);
|
|
262
263
|
expect(tree).not.toBeNull();
|
|
263
264
|
const result = CppResolver.resolve(tree!, "test.hpp", symbolTable);
|
|
264
265
|
|
|
265
|
-
|
|
266
|
-
expect(result.warnings
|
|
267
|
-
expect(result.warnings[0]).toContain("MyStruct");
|
|
266
|
+
// No warnings since "length" is no longer reserved
|
|
267
|
+
expect(result.warnings).toHaveLength(0);
|
|
268
268
|
});
|
|
269
269
|
});
|
|
270
270
|
});
|
|
@@ -749,17 +749,23 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
749
749
|
*/
|
|
750
750
|
private _isArrayAccessStringExpression(text: string): boolean {
|
|
751
751
|
// Pattern: identifier[expression] or identifier[expression][expression]...
|
|
752
|
-
// BUT NOT if accessing
|
|
752
|
+
// BUT NOT if accessing properties that return numbers, not strings
|
|
753
753
|
const arrayAccessMatch = /^([a-zA-Z_]\w*)\[/.exec(text);
|
|
754
754
|
if (!arrayAccessMatch) {
|
|
755
755
|
return false;
|
|
756
756
|
}
|
|
757
757
|
|
|
758
|
-
// ADR-045: String properties return numeric values, not strings
|
|
758
|
+
// ADR-045/ADR-058: String/array properties return numeric values, not strings
|
|
759
|
+
// ADR-058: .length deprecated, replaced by .bit_length, .byte_length,
|
|
760
|
+
// .element_count, .char_count
|
|
759
761
|
if (
|
|
760
762
|
text.endsWith(".length") ||
|
|
761
763
|
text.endsWith(".capacity") ||
|
|
762
|
-
text.endsWith(".size")
|
|
764
|
+
text.endsWith(".size") ||
|
|
765
|
+
text.endsWith(".bit_length") ||
|
|
766
|
+
text.endsWith(".byte_length") ||
|
|
767
|
+
text.endsWith(".element_count") ||
|
|
768
|
+
text.endsWith(".char_count")
|
|
763
769
|
) {
|
|
764
770
|
return false;
|
|
765
771
|
}
|
|
@@ -2202,7 +2208,9 @@ export default class CodeGenerator implements IOrchestrator {
|
|
|
2202
2208
|
const pathToUse =
|
|
2203
2209
|
options?.sourceRelativePath ||
|
|
2204
2210
|
CodeGenState.sourcePath.replace(/^.*[\\/]/, "");
|
|
2205
|
-
|
|
2211
|
+
// Issue #933: Use .hpp extension in C++ mode to match header file
|
|
2212
|
+
const ext = CodeGenState.cppMode ? ".hpp" : ".h";
|
|
2213
|
+
const headerName = pathToUse.replace(/\.cnx$|\.cnext$/, ext);
|
|
2206
2214
|
output.push(`#include "${headerName}"`, "");
|
|
2207
2215
|
CodeGenState.selfIncludeAdded = true;
|
|
2208
2216
|
}
|
|
@@ -61,15 +61,15 @@ describe("CodeGenerator Coverage Tests", () => {
|
|
|
61
61
|
// NEW CODE IN PR: _isArrayAccessStringExpression (lines 811-852)
|
|
62
62
|
// ==========================================================================
|
|
63
63
|
describe("_isArrayAccessStringExpression() - PR new code", () => {
|
|
64
|
-
it("should return false for string property access (.
|
|
64
|
+
it("should return false for string property access (.char_count)", () => {
|
|
65
65
|
const source = `
|
|
66
66
|
string<32> name <- "test";
|
|
67
67
|
void main() {
|
|
68
|
-
u32 len <- name.
|
|
68
|
+
u32 len <- name.char_count;
|
|
69
69
|
}
|
|
70
70
|
`;
|
|
71
71
|
const { code } = setupGenerator(source);
|
|
72
|
-
// .
|
|
72
|
+
// .char_count returns a number, not a string
|
|
73
73
|
expect(code).toContain("strlen(name)");
|
|
74
74
|
});
|
|
75
75
|
|
|
@@ -4719,11 +4719,11 @@ describe("CodeGenerator", () => {
|
|
|
4719
4719
|
});
|
|
4720
4720
|
});
|
|
4721
4721
|
|
|
4722
|
-
describe("Array
|
|
4723
|
-
it("should generate array
|
|
4722
|
+
describe("Array element_count", () => {
|
|
4723
|
+
it("should generate array element_count using sizeof", () => {
|
|
4724
4724
|
const source = `
|
|
4725
4725
|
u32[10] data;
|
|
4726
|
-
u32 len <- data.
|
|
4726
|
+
u32 len <- data.element_count;
|
|
4727
4727
|
`;
|
|
4728
4728
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
4729
4729
|
const generator = new CodeGenerator();
|
|
@@ -4735,7 +4735,7 @@ describe("CodeGenerator", () => {
|
|
|
4735
4735
|
sourcePath: "test.cnx",
|
|
4736
4736
|
});
|
|
4737
4737
|
|
|
4738
|
-
// .
|
|
4738
|
+
// .element_count compiles to sizeof(arr)/sizeof(arr[0]) or constant
|
|
4739
4739
|
expect(code).toContain("len =");
|
|
4740
4740
|
});
|
|
4741
4741
|
});
|
|
@@ -9750,7 +9750,7 @@ describe("CodeGenerator", () => {
|
|
|
9750
9750
|
it("should resolve string parameter", () => {
|
|
9751
9751
|
const source = `
|
|
9752
9752
|
void test(string<32> name) {
|
|
9753
|
-
u32 len <- name.
|
|
9753
|
+
u32 len <- name.char_count;
|
|
9754
9754
|
}
|
|
9755
9755
|
`;
|
|
9756
9756
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -10273,12 +10273,12 @@ describe("CodeGenerator", () => {
|
|
|
10273
10273
|
});
|
|
10274
10274
|
});
|
|
10275
10275
|
|
|
10276
|
-
describe("string
|
|
10277
|
-
it("should get string
|
|
10276
|
+
describe("string char_count helpers", () => {
|
|
10277
|
+
it("should get string char_count for global string variable", () => {
|
|
10278
10278
|
const source = `
|
|
10279
10279
|
string<64> message <- "Hello World";
|
|
10280
10280
|
void test() {
|
|
10281
|
-
u32 cap <- message.
|
|
10281
|
+
u32 cap <- message.char_count;
|
|
10282
10282
|
}
|
|
10283
10283
|
`;
|
|
10284
10284
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -10294,12 +10294,12 @@ describe("CodeGenerator", () => {
|
|
|
10294
10294
|
expect(code).toContain("message[65]");
|
|
10295
10295
|
});
|
|
10296
10296
|
|
|
10297
|
-
it("should get string
|
|
10297
|
+
it("should get string char_count for scoped string variable", () => {
|
|
10298
10298
|
const source = `
|
|
10299
10299
|
scope Logger {
|
|
10300
10300
|
string<128> buffer;
|
|
10301
10301
|
public u32 getCapacity() {
|
|
10302
|
-
return this.buffer.
|
|
10302
|
+
return this.buffer.char_count;
|
|
10303
10303
|
}
|
|
10304
10304
|
}
|
|
10305
10305
|
`;
|
|
@@ -13098,13 +13098,13 @@ describe("CodeGenerator", () => {
|
|
|
13098
13098
|
});
|
|
13099
13099
|
|
|
13100
13100
|
describe("strlen optimization (_setupLengthCache)", () => {
|
|
13101
|
-
it("should optimize multiple .
|
|
13101
|
+
it("should optimize multiple .char_count accesses on same string", () => {
|
|
13102
13102
|
const source = `
|
|
13103
13103
|
void test() {
|
|
13104
13104
|
string<32> name <- "hello";
|
|
13105
|
-
u32 a <- name.
|
|
13106
|
-
u32 b <- name.
|
|
13107
|
-
u32 c <- name.
|
|
13105
|
+
u32 a <- name.char_count;
|
|
13106
|
+
u32 b <- name.char_count;
|
|
13107
|
+
u32 c <- name.char_count;
|
|
13108
13108
|
}
|
|
13109
13109
|
`;
|
|
13110
13110
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -13117,15 +13117,15 @@ describe("CodeGenerator", () => {
|
|
|
13117
13117
|
sourcePath: "test.cnx",
|
|
13118
13118
|
});
|
|
13119
13119
|
|
|
13120
|
-
// Multiple .
|
|
13120
|
+
// Multiple .char_count accesses should be present
|
|
13121
13121
|
expect(code).toContain("strlen");
|
|
13122
13122
|
});
|
|
13123
13123
|
|
|
13124
|
-
it("should generate proper strlen call for single
|
|
13124
|
+
it("should generate proper strlen call for single char_count access", () => {
|
|
13125
13125
|
const source = `
|
|
13126
13126
|
void test() {
|
|
13127
13127
|
string<32> msg <- "test";
|
|
13128
|
-
u32 len <- msg.
|
|
13128
|
+
u32 len <- msg.char_count;
|
|
13129
13129
|
}
|
|
13130
13130
|
`;
|
|
13131
13131
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -13241,7 +13241,7 @@ describe("CodeGenerator", () => {
|
|
|
13241
13241
|
it("should handle string parameter with capacity", () => {
|
|
13242
13242
|
const source = `
|
|
13243
13243
|
void greet(string<64> name) {
|
|
13244
|
-
u32 len <- name.
|
|
13244
|
+
u32 len <- name.char_count;
|
|
13245
13245
|
}
|
|
13246
13246
|
`;
|
|
13247
13247
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -13726,8 +13726,8 @@ describe("CodeGenerator", () => {
|
|
|
13726
13726
|
const source = `
|
|
13727
13727
|
void test() {
|
|
13728
13728
|
string<64> msg <- "hello world";
|
|
13729
|
-
u32 len1 <- msg.
|
|
13730
|
-
u32 len2 <- msg.
|
|
13729
|
+
u32 len1 <- msg.char_count;
|
|
13730
|
+
u32 len2 <- msg.char_count;
|
|
13731
13731
|
}
|
|
13732
13732
|
`;
|
|
13733
13733
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -13743,12 +13743,12 @@ describe("CodeGenerator", () => {
|
|
|
13743
13743
|
expect(code).toContain("strlen");
|
|
13744
13744
|
});
|
|
13745
13745
|
|
|
13746
|
-
it("should generate cache for multiple .
|
|
13746
|
+
it("should generate cache for multiple .char_count accesses", () => {
|
|
13747
13747
|
const source = `
|
|
13748
13748
|
void test() {
|
|
13749
13749
|
string<32> s <- "hello";
|
|
13750
|
-
if (s.
|
|
13751
|
-
u32 x <- s.
|
|
13750
|
+
if (s.char_count > 0) {
|
|
13751
|
+
u32 x <- s.char_count;
|
|
13752
13752
|
}
|
|
13753
13753
|
}
|
|
13754
13754
|
`;
|
|
@@ -13762,15 +13762,15 @@ describe("CodeGenerator", () => {
|
|
|
13762
13762
|
sourcePath: "test.cnx",
|
|
13763
13763
|
});
|
|
13764
13764
|
|
|
13765
|
-
// Should generate cache variable for repeated .
|
|
13765
|
+
// Should generate cache variable for repeated .char_count access
|
|
13766
13766
|
expect(code).toContain("strlen");
|
|
13767
13767
|
});
|
|
13768
13768
|
|
|
13769
|
-
it("should not cache for single .
|
|
13769
|
+
it("should not cache for single .char_count access", () => {
|
|
13770
13770
|
const source = `
|
|
13771
13771
|
void test() {
|
|
13772
13772
|
string<32> s <- "hello";
|
|
13773
|
-
u32 len <- s.
|
|
13773
|
+
u32 len <- s.char_count;
|
|
13774
13774
|
}
|
|
13775
13775
|
`;
|
|
13776
13776
|
const { tree, tokenStream } = CNextSourceParser.parse(source);
|
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* StringLengthCounter - Counts .
|
|
2
|
+
* StringLengthCounter - Counts .char_count accesses on string variables
|
|
3
3
|
*
|
|
4
4
|
* Issue #644: Extracted from CodeGenerator to reduce code duplication.
|
|
5
|
-
* Used for strlen caching optimization - when a string's .
|
|
5
|
+
* Used for strlen caching optimization - when a string's .char_count is accessed
|
|
6
6
|
* multiple times, we cache the strlen result in a temp variable.
|
|
7
7
|
*
|
|
8
8
|
* Migrated to use CodeGenState instead of constructor DI.
|
|
9
|
+
* Updated for ADR-058: .length replaced with .char_count
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
12
13
|
import CodeGenState from "../../../state/CodeGenState";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
|
-
* Counts .
|
|
16
|
+
* Counts .char_count accesses on string variables in an expression tree.
|
|
16
17
|
* This enables strlen caching optimization.
|
|
17
18
|
*/
|
|
18
19
|
class StringLengthCounter {
|
|
19
20
|
/**
|
|
20
|
-
* Count .
|
|
21
|
+
* Count .char_count accesses in an expression.
|
|
21
22
|
*/
|
|
22
23
|
static countExpression(ctx: Parser.ExpressionContext): Map<string, number> {
|
|
23
24
|
const counts = new Map<string, number>();
|
|
@@ -26,7 +27,7 @@ class StringLengthCounter {
|
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
|
-
* Count .
|
|
30
|
+
* Count .char_count accesses in a block.
|
|
30
31
|
*/
|
|
31
32
|
static countBlock(ctx: Parser.BlockContext): Map<string, number> {
|
|
32
33
|
const counts = new Map<string, number>();
|
|
@@ -37,7 +38,7 @@ class StringLengthCounter {
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
|
-
* Count .
|
|
41
|
+
* Count .char_count accesses in a block, adding to existing counts.
|
|
41
42
|
*/
|
|
42
43
|
static countBlockInto(
|
|
43
44
|
ctx: Parser.BlockContext,
|
|
@@ -49,7 +50,7 @@ class StringLengthCounter {
|
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
|
-
* Walk an expression tree, counting .
|
|
53
|
+
* Walk an expression tree, counting .char_count accesses.
|
|
53
54
|
* Uses generic traversal - only postfix expressions need special handling.
|
|
54
55
|
*/
|
|
55
56
|
private static walkExpression(
|
|
@@ -177,7 +178,7 @@ class StringLengthCounter {
|
|
|
177
178
|
}
|
|
178
179
|
|
|
179
180
|
/**
|
|
180
|
-
* Walk a postfix expression - this is where we detect .
|
|
181
|
+
* Walk a postfix expression - this is where we detect .char_count accesses.
|
|
181
182
|
*/
|
|
182
183
|
private static walkPostfixExpr(
|
|
183
184
|
ctx: Parser.PostfixExpressionContext,
|
|
@@ -187,11 +188,11 @@ class StringLengthCounter {
|
|
|
187
188
|
const primaryId = primary.IDENTIFIER()?.getText();
|
|
188
189
|
const ops = ctx.postfixOp();
|
|
189
190
|
|
|
190
|
-
// Check for pattern: identifier.
|
|
191
|
+
// Check for pattern: identifier.char_count where identifier is a string
|
|
191
192
|
if (primaryId && ops.length > 0) {
|
|
192
193
|
for (const op of ops) {
|
|
193
194
|
const memberName = op.IDENTIFIER()?.getText();
|
|
194
|
-
if (memberName === "
|
|
195
|
+
if (memberName === "char_count") {
|
|
195
196
|
// Check if this is a string type
|
|
196
197
|
const typeInfo = CodeGenState.getVariableTypeInfo(primaryId);
|
|
197
198
|
if (typeInfo?.isString) {
|
|
@@ -213,7 +214,7 @@ class StringLengthCounter {
|
|
|
213
214
|
}
|
|
214
215
|
|
|
215
216
|
/**
|
|
216
|
-
* Walk a statement, counting .
|
|
217
|
+
* Walk a statement, counting .char_count accesses.
|
|
217
218
|
*/
|
|
218
219
|
private static walkStatement(
|
|
219
220
|
ctx: Parser.StatementContext,
|
|
@@ -37,7 +37,7 @@ describe("StringLengthCounter", () => {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
describe("countExpression", () => {
|
|
40
|
-
it("counts single .
|
|
40
|
+
it("counts single .char_count access on string variable", () => {
|
|
41
41
|
CodeGenState.setVariableTypeInfo("myStr", {
|
|
42
42
|
baseType: "char",
|
|
43
43
|
bitWidth: 8,
|
|
@@ -47,13 +47,13 @@ describe("StringLengthCounter", () => {
|
|
|
47
47
|
stringCapacity: 64,
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
const expr = parseExpression("myStr.
|
|
50
|
+
const expr = parseExpression("myStr.char_count");
|
|
51
51
|
const counts = StringLengthCounter.countExpression(expr);
|
|
52
52
|
|
|
53
53
|
expect(counts.get("myStr")).toBe(1);
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
it("counts multiple .
|
|
56
|
+
it("counts multiple .char_count accesses on same variable", () => {
|
|
57
57
|
CodeGenState.setVariableTypeInfo("str", {
|
|
58
58
|
baseType: "char",
|
|
59
59
|
bitWidth: 8,
|
|
@@ -63,14 +63,14 @@ describe("StringLengthCounter", () => {
|
|
|
63
63
|
stringCapacity: 32,
|
|
64
64
|
});
|
|
65
65
|
|
|
66
|
-
// Expression: str.
|
|
67
|
-
const expr = parseExpression("str.
|
|
66
|
+
// Expression: str.char_count + str.char_count
|
|
67
|
+
const expr = parseExpression("str.char_count + str.char_count");
|
|
68
68
|
const counts = StringLengthCounter.countExpression(expr);
|
|
69
69
|
|
|
70
70
|
expect(counts.get("str")).toBe(2);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
-
it("counts .
|
|
73
|
+
it("counts .char_count accesses on different string variables", () => {
|
|
74
74
|
CodeGenState.setVariableTypeInfo("a", {
|
|
75
75
|
baseType: "char",
|
|
76
76
|
bitWidth: 8,
|
|
@@ -88,14 +88,14 @@ describe("StringLengthCounter", () => {
|
|
|
88
88
|
stringCapacity: 16,
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
const expr = parseExpression("a.
|
|
91
|
+
const expr = parseExpression("a.char_count + b.char_count");
|
|
92
92
|
const counts = StringLengthCounter.countExpression(expr);
|
|
93
93
|
|
|
94
94
|
expect(counts.get("a")).toBe(1);
|
|
95
95
|
expect(counts.get("b")).toBe(1);
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
-
it("ignores .
|
|
98
|
+
it("ignores .char_count on non-string variables", () => {
|
|
99
99
|
CodeGenState.setVariableTypeInfo("arr", {
|
|
100
100
|
baseType: "u8",
|
|
101
101
|
bitWidth: 8,
|
|
@@ -104,7 +104,7 @@ describe("StringLengthCounter", () => {
|
|
|
104
104
|
arrayDimensions: [10],
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
-
const expr = parseExpression("arr.
|
|
107
|
+
const expr = parseExpression("arr.char_count");
|
|
108
108
|
const counts = StringLengthCounter.countExpression(expr);
|
|
109
109
|
|
|
110
110
|
expect(counts.get("arr")).toBeUndefined();
|
|
@@ -126,7 +126,7 @@ describe("StringLengthCounter", () => {
|
|
|
126
126
|
|
|
127
127
|
it("handles unknown variables gracefully", () => {
|
|
128
128
|
// No type registered for "unknown"
|
|
129
|
-
const expr = parseExpression("unknown.
|
|
129
|
+
const expr = parseExpression("unknown.char_count");
|
|
130
130
|
const counts = StringLengthCounter.countExpression(expr);
|
|
131
131
|
|
|
132
132
|
expect(counts.size).toBe(0);
|
|
@@ -134,7 +134,7 @@ describe("StringLengthCounter", () => {
|
|
|
134
134
|
});
|
|
135
135
|
|
|
136
136
|
describe("countBlock", () => {
|
|
137
|
-
it("counts .
|
|
137
|
+
it("counts .char_count accesses across multiple statements", () => {
|
|
138
138
|
CodeGenState.setVariableTypeInfo("msg", {
|
|
139
139
|
baseType: "char",
|
|
140
140
|
bitWidth: 8,
|
|
@@ -145,15 +145,15 @@ describe("StringLengthCounter", () => {
|
|
|
145
145
|
});
|
|
146
146
|
|
|
147
147
|
const block = parseBlock(`
|
|
148
|
-
u32 len <- msg.
|
|
149
|
-
u32 doubled <- msg.
|
|
148
|
+
u32 len <- msg.char_count;
|
|
149
|
+
u32 doubled <- msg.char_count * 2;
|
|
150
150
|
`);
|
|
151
151
|
const counts = StringLengthCounter.countBlock(block);
|
|
152
152
|
|
|
153
153
|
expect(counts.get("msg")).toBe(2);
|
|
154
154
|
});
|
|
155
155
|
|
|
156
|
-
it("counts .
|
|
156
|
+
it("counts .char_count in assignment statements", () => {
|
|
157
157
|
CodeGenState.setVariableTypeInfo("text", {
|
|
158
158
|
baseType: "char",
|
|
159
159
|
bitWidth: 8,
|
|
@@ -165,7 +165,7 @@ describe("StringLengthCounter", () => {
|
|
|
165
165
|
|
|
166
166
|
const block = parseBlock(`
|
|
167
167
|
u32 x;
|
|
168
|
-
x <- text.
|
|
168
|
+
x <- text.char_count;
|
|
169
169
|
`);
|
|
170
170
|
const counts = StringLengthCounter.countBlock(block);
|
|
171
171
|
|
|
@@ -197,8 +197,8 @@ describe("StringLengthCounter", () => {
|
|
|
197
197
|
counts.set("s1", 1);
|
|
198
198
|
|
|
199
199
|
const block = parseBlock(`
|
|
200
|
-
u32 a <- s1.
|
|
201
|
-
u32 b <- s2.
|
|
200
|
+
u32 a <- s1.char_count;
|
|
201
|
+
u32 b <- s2.char_count;
|
|
202
202
|
`);
|
|
203
203
|
StringLengthCounter.countBlockInto(block, counts);
|
|
204
204
|
|
|
@@ -208,7 +208,7 @@ describe("StringLengthCounter", () => {
|
|
|
208
208
|
});
|
|
209
209
|
|
|
210
210
|
describe("nested expressions", () => {
|
|
211
|
-
it("counts .
|
|
211
|
+
it("counts .char_count in ternary expressions", () => {
|
|
212
212
|
CodeGenState.setVariableTypeInfo("str", {
|
|
213
213
|
baseType: "char",
|
|
214
214
|
bitWidth: 8,
|
|
@@ -218,13 +218,13 @@ describe("StringLengthCounter", () => {
|
|
|
218
218
|
stringCapacity: 64,
|
|
219
219
|
});
|
|
220
220
|
|
|
221
|
-
const expr = parseExpression("(str.
|
|
221
|
+
const expr = parseExpression("(str.char_count > 0) ? str.char_count : 0");
|
|
222
222
|
const counts = StringLengthCounter.countExpression(expr);
|
|
223
223
|
|
|
224
224
|
expect(counts.get("str")).toBe(2);
|
|
225
225
|
});
|
|
226
226
|
|
|
227
|
-
it("counts .
|
|
227
|
+
it("counts .char_count in comparison expressions", () => {
|
|
228
228
|
CodeGenState.setVariableTypeInfo("name", {
|
|
229
229
|
baseType: "char",
|
|
230
230
|
bitWidth: 8,
|
|
@@ -234,7 +234,7 @@ describe("StringLengthCounter", () => {
|
|
|
234
234
|
stringCapacity: 50,
|
|
235
235
|
});
|
|
236
236
|
|
|
237
|
-
const expr = parseExpression("name.
|
|
237
|
+
const expr = parseExpression("name.char_count = 10");
|
|
238
238
|
const counts = StringLengthCounter.countExpression(expr);
|
|
239
239
|
|
|
240
240
|
expect(counts.get("name")).toBe(1);
|