c-next 0.1.61 → 0.1.63
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 +86 -63
- package/grammar/CNext.g4 +3 -17
- package/package.json +1 -1
- package/src/cli/serve/ServeCommand.ts +57 -45
- package/src/lib/__tests__/parseCHeader.mocked.test.ts +145 -0
- package/src/transpiler/Transpiler.ts +603 -613
- package/src/transpiler/__tests__/DualCodePaths.test.ts +5 -1
- package/src/transpiler/__tests__/Transpiler.coverage.test.ts +2 -99
- package/src/transpiler/__tests__/Transpiler.test.ts +3 -26
- package/src/transpiler/data/IncludeTreeWalker.ts +1 -1
- package/src/transpiler/logic/analysis/InitializationAnalyzer.ts +23 -52
- package/src/transpiler/logic/parser/grammar/CNext.interp +1 -3
- package/src/transpiler/logic/parser/grammar/CNextListener.ts +0 -22
- package/src/transpiler/logic/parser/grammar/CNextParser.ts +665 -1084
- package/src/transpiler/logic/parser/grammar/CNextVisitor.ts +0 -14
- package/src/transpiler/logic/symbols/CppSymbolCollector.ts +67 -43
- package/src/transpiler/logic/symbols/cnext/collectors/StructCollector.ts +156 -70
- package/src/transpiler/logic/symbols/cnext/collectors/VariableCollector.ts +31 -6
- package/src/transpiler/logic/symbols/cnext/utils/TypeUtils.ts +43 -11
- package/src/transpiler/output/codegen/CodeGenState.ts +811 -0
- package/src/transpiler/output/codegen/CodeGenerator.ts +1410 -2587
- package/src/transpiler/output/codegen/TypeResolver.ts +193 -149
- package/src/transpiler/output/codegen/TypeValidator.ts +148 -370
- package/src/transpiler/output/codegen/__tests__/CodeGenState.test.ts +446 -0
- package/src/transpiler/output/codegen/__tests__/CodeGenerator.test.ts +2082 -52
- package/src/transpiler/output/codegen/__tests__/TrackVariableTypeHelpers.test.ts +1 -1
- package/src/transpiler/output/codegen/__tests__/TypeResolver.test.ts +435 -196
- package/src/transpiler/output/codegen/__tests__/TypeValidator.resolution.test.ts +51 -67
- package/src/transpiler/output/codegen/__tests__/TypeValidator.test.ts +495 -471
- package/src/transpiler/output/codegen/analysis/MemberChainAnalyzer.ts +227 -66
- package/src/transpiler/output/codegen/analysis/StringLengthCounter.ts +55 -58
- package/src/transpiler/output/codegen/analysis/__tests__/MemberChainAnalyzer.test.ts +288 -275
- package/src/transpiler/output/codegen/analysis/__tests__/StringLengthCounter.test.ts +101 -144
- package/src/transpiler/output/codegen/assignment/AssignmentClassifier.ts +195 -133
- package/src/transpiler/output/codegen/assignment/AssignmentContextBuilder.ts +24 -74
- package/src/transpiler/output/codegen/assignment/AssignmentKind.ts +3 -0
- package/src/transpiler/output/codegen/assignment/IAssignmentContext.ts +3 -0
- package/src/transpiler/output/codegen/assignment/__tests__/AssignmentClassifier.test.ts +290 -320
- package/src/transpiler/output/codegen/assignment/handlers/BitAccessHandlers.ts +42 -0
- package/src/transpiler/output/codegen/assignment/handlers/__tests__/BitAccessHandlers.test.ts +76 -2
- package/src/transpiler/output/codegen/generators/GeneratorRegistry.ts +12 -0
- package/src/transpiler/output/codegen/generators/IOrchestrator.ts +5 -1
- package/src/transpiler/output/codegen/generators/__tests__/GeneratorRegistry.test.ts +28 -1
- package/src/transpiler/output/codegen/generators/declarationGenerators/ArrayDimensionUtils.ts +67 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterGenerator.ts +11 -24
- package/src/transpiler/output/codegen/generators/declarationGenerators/RegisterMacroGenerator.ts +64 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopeGenerator.ts +137 -61
- package/src/transpiler/output/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +18 -27
- package/src/transpiler/output/codegen/generators/declarationGenerators/StructGenerator.ts +100 -23
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ArrayDimensionUtils.test.ts +125 -0
- package/src/transpiler/output/codegen/generators/declarationGenerators/__tests__/ScopeGenerator.test.ts +157 -4
- package/src/transpiler/output/codegen/generators/expressions/PostfixExpressionGenerator.ts +5 -1
- package/src/transpiler/output/codegen/generators/statements/ControlFlowGenerator.ts +1 -17
- package/src/transpiler/output/codegen/generators/support/HelperGenerator.ts +23 -22
- package/src/transpiler/output/codegen/helpers/ArrayAccessHelper.ts +129 -0
- package/src/transpiler/output/codegen/helpers/ArrayInitHelper.ts +54 -61
- package/src/transpiler/output/codegen/helpers/AssignmentExpectedTypeResolver.ts +40 -44
- package/src/transpiler/output/codegen/helpers/AssignmentTargetExtractor.ts +17 -45
- package/src/transpiler/output/codegen/helpers/AssignmentValidator.ts +83 -78
- package/src/transpiler/output/codegen/helpers/CppModeHelper.ts +22 -30
- package/src/transpiler/output/codegen/helpers/EnumAssignmentValidator.ts +108 -50
- package/src/transpiler/output/codegen/helpers/FloatBitHelper.ts +16 -31
- package/src/transpiler/output/codegen/helpers/MemberSeparatorResolver.ts +10 -3
- package/src/transpiler/output/codegen/helpers/StringDeclHelper.ts +103 -96
- package/src/transpiler/output/codegen/helpers/SymbolLookupHelper.ts +44 -0
- package/src/transpiler/output/codegen/helpers/TypeGenerationHelper.ts +9 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayAccessHelper.test.ts +479 -0
- package/src/transpiler/output/codegen/helpers/__tests__/ArrayInitHelper.test.ts +58 -103
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentExpectedTypeResolver.test.ts +97 -40
- package/src/transpiler/output/codegen/helpers/__tests__/AssignmentValidator.test.ts +223 -128
- package/src/transpiler/output/codegen/helpers/__tests__/CppModeHelper.test.ts +68 -41
- package/src/transpiler/output/codegen/helpers/__tests__/EnumAssignmentValidator.test.ts +198 -47
- package/src/transpiler/output/codegen/helpers/__tests__/FloatBitHelper.test.ts +39 -37
- package/src/transpiler/output/codegen/helpers/__tests__/MemberSeparatorResolver.test.ts +1 -0
- package/src/transpiler/output/codegen/helpers/__tests__/StringDeclHelper.test.ts +191 -453
- package/src/transpiler/output/codegen/helpers/__tests__/SymbolLookupHelper.test.ts +201 -0
- package/src/transpiler/output/codegen/helpers/__tests__/TypeGenerationHelper.test.ts +50 -0
- package/src/transpiler/output/codegen/resolution/EnumTypeResolver.ts +229 -0
- package/src/transpiler/output/codegen/resolution/ScopeResolver.ts +60 -0
- package/src/transpiler/output/codegen/resolution/SizeofResolver.ts +177 -0
- package/src/transpiler/output/codegen/resolution/__tests__/EnumTypeResolver.test.ts +336 -0
- package/src/transpiler/output/codegen/resolution/__tests__/SizeofResolver.test.ts +201 -0
- package/src/transpiler/output/codegen/types/IArrayAccessDeps.ts +23 -0
- package/src/transpiler/output/codegen/types/IArrayAccessInfo.ts +26 -0
- package/src/transpiler/output/codegen/types/IMemberSeparatorDeps.ts +7 -0
- package/src/transpiler/output/codegen/utils/CodegenParserUtils.ts +98 -0
- package/src/transpiler/output/codegen/utils/ExpressionUnwrapper.ts +22 -22
- package/src/transpiler/output/codegen/utils/__tests__/CodegenParserUtils.test.ts +228 -0
- package/src/transpiler/types/IFileResult.ts +0 -4
- package/src/transpiler/types/IPipelineFile.ts +27 -0
- package/src/transpiler/types/IPipelineInput.ts +23 -0
- package/src/transpiler/types/TranspilerState.ts +1 -1
- package/src/utils/FormatUtils.ts +28 -2
- package/src/utils/MapUtils.ts +25 -0
- package/src/utils/PostfixAnalysisUtils.ts +48 -0
- package/src/utils/__tests__/FormatUtils.test.ts +42 -0
- package/src/utils/__tests__/MapUtils.test.ts +85 -0
- package/src/utils/constants/OperatorMappings.ts +19 -0
- package/src/transpiler/logic/StandaloneContextBuilder.ts +0 -150
- package/src/transpiler/logic/__tests__/StandaloneContextBuilder.test.ts +0 -647
- package/src/transpiler/output/codegen/types/ITypeResolverDeps.ts +0 -23
- package/src/transpiler/output/codegen/types/ITypeValidatorDeps.ts +0 -53
- package/src/transpiler/types/ITranspileContext.ts +0 -49
- package/src/transpiler/types/ITranspileContribution.ts +0 -32
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for SizeofResolver - sizeof expression generation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
6
|
+
import SizeofResolver from "../SizeofResolver";
|
|
7
|
+
import CodeGenState from "../../CodeGenState";
|
|
8
|
+
|
|
9
|
+
describe("SizeofResolver", () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
CodeGenState.reset();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
describe("sizeofParameter", () => {
|
|
15
|
+
it("throws E0601 for array parameter", () => {
|
|
16
|
+
// Set up parameter info indicating array
|
|
17
|
+
CodeGenState.currentParameters.set("arr", {
|
|
18
|
+
name: "arr",
|
|
19
|
+
baseType: "u32",
|
|
20
|
+
isArray: true,
|
|
21
|
+
isStruct: false,
|
|
22
|
+
isConst: false,
|
|
23
|
+
isCallback: false,
|
|
24
|
+
isString: false,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const mockCallbacks = {
|
|
28
|
+
generateType: vi.fn(),
|
|
29
|
+
generateExpression: vi.fn(),
|
|
30
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Create a mock context for sizeof(arr)
|
|
34
|
+
const mockTypeCtx = {
|
|
35
|
+
qualifiedType: () => null,
|
|
36
|
+
userType: () => ({ getText: () => "arr" }),
|
|
37
|
+
getText: () => "arr",
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const mockCtx = {
|
|
41
|
+
type: () => mockTypeCtx,
|
|
42
|
+
expression: () => null,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
expect(() =>
|
|
46
|
+
SizeofResolver.generate(mockCtx as never, mockCallbacks),
|
|
47
|
+
).toThrow("Error[E0601]");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("generates dereference for pass-by-reference parameter", () => {
|
|
51
|
+
// Non-array, non-callback, non-struct parameter is pass-by-reference
|
|
52
|
+
CodeGenState.currentParameters.set("value", {
|
|
53
|
+
name: "value",
|
|
54
|
+
baseType: "u32",
|
|
55
|
+
isArray: false,
|
|
56
|
+
isStruct: false,
|
|
57
|
+
isConst: false,
|
|
58
|
+
isCallback: false,
|
|
59
|
+
isString: false,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const mockCallbacks = {
|
|
63
|
+
generateType: vi.fn(),
|
|
64
|
+
generateExpression: vi.fn(),
|
|
65
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const mockTypeCtx = {
|
|
69
|
+
qualifiedType: () => null,
|
|
70
|
+
userType: () => ({ getText: () => "value" }),
|
|
71
|
+
getText: () => "value",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const mockCtx = {
|
|
75
|
+
type: () => mockTypeCtx,
|
|
76
|
+
expression: () => null,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const result = SizeofResolver.generate(mockCtx as never, mockCallbacks);
|
|
80
|
+
|
|
81
|
+
expect(result).toBe("sizeof(*value)");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("sizeofQualifiedType", () => {
|
|
86
|
+
it("handles struct.member access for local variable", () => {
|
|
87
|
+
CodeGenState.localVariables.add("myStruct");
|
|
88
|
+
|
|
89
|
+
const mockCallbacks = {
|
|
90
|
+
generateType: vi.fn(),
|
|
91
|
+
generateExpression: vi.fn(),
|
|
92
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const mockQualifiedCtx = {
|
|
96
|
+
IDENTIFIER: () => [
|
|
97
|
+
{ getText: () => "myStruct" },
|
|
98
|
+
{ getText: () => "field" },
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const mockTypeCtx = {
|
|
103
|
+
qualifiedType: () => mockQualifiedCtx,
|
|
104
|
+
userType: () => null,
|
|
105
|
+
getText: () => "myStruct.field",
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const mockCtx = {
|
|
109
|
+
type: () => mockTypeCtx,
|
|
110
|
+
expression: () => null,
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const result = SizeofResolver.generate(mockCtx as never, mockCallbacks);
|
|
114
|
+
|
|
115
|
+
expect(result).toBe("sizeof(myStruct.field)");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("handles struct parameter with arrow notation", () => {
|
|
119
|
+
CodeGenState.currentParameters.set("param", {
|
|
120
|
+
name: "param",
|
|
121
|
+
baseType: "MyStruct",
|
|
122
|
+
isArray: false,
|
|
123
|
+
isStruct: true,
|
|
124
|
+
isConst: false,
|
|
125
|
+
isCallback: false,
|
|
126
|
+
isString: false,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const mockCallbacks = {
|
|
130
|
+
generateType: vi.fn(),
|
|
131
|
+
generateExpression: vi.fn(),
|
|
132
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const mockQualifiedCtx = {
|
|
136
|
+
IDENTIFIER: () => [
|
|
137
|
+
{ getText: () => "param" },
|
|
138
|
+
{ getText: () => "field" },
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const mockTypeCtx = {
|
|
143
|
+
qualifiedType: () => mockQualifiedCtx,
|
|
144
|
+
userType: () => null,
|
|
145
|
+
getText: () => "param.field",
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const mockCtx = {
|
|
149
|
+
type: () => mockTypeCtx,
|
|
150
|
+
expression: () => null,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const result = SizeofResolver.generate(mockCtx as never, mockCallbacks);
|
|
154
|
+
|
|
155
|
+
expect(result).toBe("sizeof(param->field)");
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("handles non-struct parameter with dot notation", () => {
|
|
159
|
+
CodeGenState.currentParameters.set("param", {
|
|
160
|
+
name: "param",
|
|
161
|
+
baseType: "MyStruct",
|
|
162
|
+
isArray: false,
|
|
163
|
+
isStruct: false,
|
|
164
|
+
isConst: false,
|
|
165
|
+
isCallback: false,
|
|
166
|
+
isString: false,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const mockCallbacks = {
|
|
170
|
+
generateType: vi.fn(),
|
|
171
|
+
generateExpression: vi.fn(),
|
|
172
|
+
hasSideEffects: vi.fn().mockReturnValue(false),
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const mockQualifiedCtx = {
|
|
176
|
+
IDENTIFIER: () => [
|
|
177
|
+
{ getText: () => "param" },
|
|
178
|
+
{ getText: () => "field" },
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const mockTypeCtx = {
|
|
183
|
+
qualifiedType: () => mockQualifiedCtx,
|
|
184
|
+
userType: () => null,
|
|
185
|
+
getText: () => "param.field",
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const mockCtx = {
|
|
189
|
+
type: () => mockTypeCtx,
|
|
190
|
+
expression: () => null,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const result = SizeofResolver.generate(mockCtx as never, mockCallbacks);
|
|
194
|
+
|
|
195
|
+
expect(result).toBe("sizeof(param.field)");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Note: sizeofExpression with side effects is tested via integration tests
|
|
200
|
+
// The mock structure required is too complex for unit testing
|
|
201
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependencies required by ArrayAccessHelper for code generation.
|
|
3
|
+
* Allows the helper to request includes and access state without
|
|
4
|
+
* direct coupling to CodeGenerator.
|
|
5
|
+
*/
|
|
6
|
+
import TIncludeHeader from "../generators/TIncludeHeader";
|
|
7
|
+
|
|
8
|
+
type IArrayAccessDeps = {
|
|
9
|
+
/** Generate a bit mask for the given width */
|
|
10
|
+
generateBitMask(width: string, is64Bit?: boolean): string;
|
|
11
|
+
/** Request an include header */
|
|
12
|
+
requireInclude(header: TIncludeHeader): void;
|
|
13
|
+
/** Check if we're in a function body */
|
|
14
|
+
isInFunctionBody(): boolean;
|
|
15
|
+
/** Register a float shadow variable, returns true if newly registered */
|
|
16
|
+
registerFloatShadow(shadowName: string, shadowType: string): boolean;
|
|
17
|
+
/** Check if shadow is current (already synced) */
|
|
18
|
+
isShadowCurrent(shadowName: string): boolean;
|
|
19
|
+
/** Mark shadow as current */
|
|
20
|
+
markShadowCurrent(shadowName: string): void;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default IArrayAccessDeps;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intermediate representation for array access operations.
|
|
3
|
+
* Decouples code generation from ANTLR parser contexts.
|
|
4
|
+
*/
|
|
5
|
+
import TTypeInfo from "./TTypeInfo";
|
|
6
|
+
|
|
7
|
+
type IArrayAccessInfo = {
|
|
8
|
+
/** The raw identifier name from source */
|
|
9
|
+
rawName: string;
|
|
10
|
+
/** The resolved name (may include dereference like `(*param)`) */
|
|
11
|
+
resolvedName: string;
|
|
12
|
+
/** The access type */
|
|
13
|
+
accessType: "single-index" | "bit-range";
|
|
14
|
+
/** Index expression as generated C code (for single-index) */
|
|
15
|
+
indexExpr?: string;
|
|
16
|
+
/** For bit ranges: start position */
|
|
17
|
+
startExpr?: string;
|
|
18
|
+
/** For bit ranges: width */
|
|
19
|
+
widthExpr?: string;
|
|
20
|
+
/** Type info for the variable being accessed */
|
|
21
|
+
typeInfo?: TTypeInfo;
|
|
22
|
+
/** Source line for error messages */
|
|
23
|
+
line: number;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default IArrayAccessInfo;
|
|
@@ -11,6 +11,13 @@ interface IMemberSeparatorDeps {
|
|
|
11
11
|
/** Validate cross-scope visibility and throw if not visible */
|
|
12
12
|
validateCrossScopeVisibility(scopeName: string, memberName: string): void;
|
|
13
13
|
|
|
14
|
+
/** Validate register access from inside a scope requires global. prefix */
|
|
15
|
+
validateRegisterAccess(
|
|
16
|
+
registerName: string,
|
|
17
|
+
memberName: string,
|
|
18
|
+
hasGlobal: boolean,
|
|
19
|
+
): void;
|
|
20
|
+
|
|
14
21
|
/** Get struct param separator based on C/C++ mode */
|
|
15
22
|
getStructParamSeparator(): string;
|
|
16
23
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parser utilities specific to code generation.
|
|
3
|
+
*
|
|
4
|
+
* These utilities depend on ANTLR parser types and are used by CodeGenerator.
|
|
5
|
+
* Separated from src/utils/ParserUtils.ts to avoid circular dependencies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { ParserRuleContext, TerminalNode } from "antlr4ng";
|
|
9
|
+
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
10
|
+
import ExpressionUnwrapper from "./ExpressionUnwrapper";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Static utility methods for parser context operations in code generation.
|
|
14
|
+
*/
|
|
15
|
+
class CodegenParserUtils {
|
|
16
|
+
/**
|
|
17
|
+
* Extract operators from parse tree children in order.
|
|
18
|
+
*
|
|
19
|
+
* When parsing expressions like "a + b - c", ANTLR creates children
|
|
20
|
+
* with operands interleaved: [a, +, b, -, c]. This method extracts
|
|
21
|
+
* just the operators as terminal nodes.
|
|
22
|
+
*
|
|
23
|
+
* Note: Using children.filter() loses operator ordering when operators
|
|
24
|
+
* are detected using text.includes(), so we iterate explicitly.
|
|
25
|
+
*
|
|
26
|
+
* @param ctx - The parser rule context containing operands and operators
|
|
27
|
+
* @returns Array of operator strings in the order they appear
|
|
28
|
+
*/
|
|
29
|
+
static getOperatorsFromChildren(ctx: ParserRuleContext): string[] {
|
|
30
|
+
const operators: string[] = [];
|
|
31
|
+
for (const child of ctx.children) {
|
|
32
|
+
if (child instanceof TerminalNode) {
|
|
33
|
+
operators.push(child.getText());
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return operators;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Check if this is the main function with command-line args parameter.
|
|
41
|
+
* Supports: u8 args[][] (legacy) or string args[] (preferred)
|
|
42
|
+
*
|
|
43
|
+
* @param name - Function name
|
|
44
|
+
* @param paramList - Parameter list context
|
|
45
|
+
* @returns true if this is main with args parameter
|
|
46
|
+
*/
|
|
47
|
+
static isMainFunctionWithArgs(
|
|
48
|
+
name: string,
|
|
49
|
+
paramList: Parser.ParameterListContext | null,
|
|
50
|
+
): boolean {
|
|
51
|
+
if (name !== "main" || !paramList) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const params = paramList.parameter();
|
|
56
|
+
if (params.length !== 1) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const param = params[0];
|
|
61
|
+
const typeCtx = param.type();
|
|
62
|
+
const dims = param.arrayDimension();
|
|
63
|
+
|
|
64
|
+
// Check for string args[] (preferred - array of strings)
|
|
65
|
+
if (typeCtx.stringType() && dims.length === 1) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Check for u8 args[][] (legacy - 2D array of bytes)
|
|
70
|
+
const type = typeCtx.getText();
|
|
71
|
+
return (type === "u8" || type === "i8") && dims.length === 2;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Extract a simple identifier from an expression, if it is one.
|
|
76
|
+
* Returns null for complex expressions (binary ops, function calls, etc.)
|
|
77
|
+
*
|
|
78
|
+
* A "simple identifier" is an expression that is just a variable name
|
|
79
|
+
* with no operators, member access, or array indexing.
|
|
80
|
+
*
|
|
81
|
+
* @param ctx - The expression context to analyze
|
|
82
|
+
* @returns The identifier string, or null if not a simple identifier
|
|
83
|
+
*/
|
|
84
|
+
static getSimpleIdentifier(ctx: Parser.ExpressionContext): string | null {
|
|
85
|
+
const postfix = ExpressionUnwrapper.getPostfixExpression(ctx);
|
|
86
|
+
if (!postfix) return null;
|
|
87
|
+
|
|
88
|
+
// Has postfix operators like . or [] - not a simple identifier
|
|
89
|
+
if (postfix.postfixOp().length !== 0) return null;
|
|
90
|
+
|
|
91
|
+
const primary = postfix.primaryExpression();
|
|
92
|
+
if (!primary.IDENTIFIER()) return null;
|
|
93
|
+
|
|
94
|
+
return primary.IDENTIFIER()!.getText();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export default CodegenParserUtils;
|
|
@@ -62,17 +62,14 @@ class ExpressionUnwrapper {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
* Navigate from ExpressionContext to
|
|
66
|
-
*
|
|
67
|
-
* (indicating binary operations).
|
|
65
|
+
* Navigate from ExpressionContext to UnaryExpressionContext.
|
|
66
|
+
* Common helper for getPostfixExpression and getUnaryExpression.
|
|
68
67
|
*
|
|
69
|
-
*
|
|
70
|
-
* - Getting the primary expression (identifier, literal)
|
|
71
|
-
* - Checking postfix operators (member access, array indexing)
|
|
68
|
+
* Returns null if expression has multiple terms at any level.
|
|
72
69
|
*/
|
|
73
|
-
static
|
|
70
|
+
private static navigateToUnary(
|
|
74
71
|
ctx: Parser.ExpressionContext,
|
|
75
|
-
): Parser.
|
|
72
|
+
): Parser.UnaryExpressionContext | null {
|
|
76
73
|
const shift = this.navigateToShift(ctx);
|
|
77
74
|
if (!shift) return null;
|
|
78
75
|
|
|
@@ -84,9 +81,23 @@ class ExpressionUnwrapper {
|
|
|
84
81
|
const mult = add.multiplicativeExpression()[0];
|
|
85
82
|
if (mult.unaryExpression().length !== 1) return null;
|
|
86
83
|
|
|
87
|
-
|
|
88
|
-
|
|
84
|
+
return mult.unaryExpression()[0];
|
|
85
|
+
}
|
|
89
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Navigate from ExpressionContext to PostfixExpressionContext.
|
|
89
|
+
* Returns null if the expression has multiple terms at any level
|
|
90
|
+
* (indicating binary operations).
|
|
91
|
+
*
|
|
92
|
+
* Use this when you need to access the postfix expression for:
|
|
93
|
+
* - Getting the primary expression (identifier, literal)
|
|
94
|
+
* - Checking postfix operators (member access, array indexing)
|
|
95
|
+
*/
|
|
96
|
+
static getPostfixExpression(
|
|
97
|
+
ctx: Parser.ExpressionContext,
|
|
98
|
+
): Parser.PostfixExpressionContext | null {
|
|
99
|
+
const unary = this.navigateToUnary(ctx);
|
|
100
|
+
if (!unary?.postfixExpression()) return null;
|
|
90
101
|
return unary.postfixExpression()!;
|
|
91
102
|
}
|
|
92
103
|
|
|
@@ -99,18 +110,7 @@ class ExpressionUnwrapper {
|
|
|
99
110
|
static getUnaryExpression(
|
|
100
111
|
ctx: Parser.ExpressionContext,
|
|
101
112
|
): Parser.UnaryExpressionContext | null {
|
|
102
|
-
|
|
103
|
-
if (!shift) return null;
|
|
104
|
-
|
|
105
|
-
if (shift.additiveExpression().length !== 1) return null;
|
|
106
|
-
|
|
107
|
-
const add = shift.additiveExpression()[0];
|
|
108
|
-
if (add.multiplicativeExpression().length !== 1) return null;
|
|
109
|
-
|
|
110
|
-
const mult = add.multiplicativeExpression()[0];
|
|
111
|
-
if (mult.unaryExpression().length !== 1) return null;
|
|
112
|
-
|
|
113
|
-
return mult.unaryExpression()[0];
|
|
113
|
+
return this.navigateToUnary(ctx);
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
/**
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for CodegenParserUtils
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import CodegenParserUtils from "../CodegenParserUtils";
|
|
7
|
+
import CNextSourceParser from "../../../../logic/parser/CNextSourceParser";
|
|
8
|
+
import * as Parser from "../../../../logic/parser/grammar/CNextParser";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Helper to parse and get the expression from a variable declaration
|
|
12
|
+
*/
|
|
13
|
+
function parseExpression(exprSource: string): Parser.ExpressionContext {
|
|
14
|
+
const source = `void main() { u32 x <- ${exprSource}; }`;
|
|
15
|
+
const { tree, errors } = CNextSourceParser.parse(source);
|
|
16
|
+
|
|
17
|
+
if (errors.length > 0) {
|
|
18
|
+
throw new Error(`Parse failed: ${errors.map((e) => e.message).join(", ")}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Navigate to the expression in the AST
|
|
22
|
+
for (const decl of tree.declaration()) {
|
|
23
|
+
const funcDecl = decl.functionDeclaration();
|
|
24
|
+
if (funcDecl?.IDENTIFIER().getText() === "main") {
|
|
25
|
+
const block = funcDecl.block();
|
|
26
|
+
const stmt = block?.statement()[0];
|
|
27
|
+
const varDecl = stmt?.variableDeclaration();
|
|
28
|
+
const expr = varDecl?.expression();
|
|
29
|
+
if (expr) return expr;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
throw new Error("Could not find expression in parsed tree");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Helper to drill down to additive expression level
|
|
38
|
+
* Follows the same pattern as ExpressionUnwrapper for navigating expression tree
|
|
39
|
+
*/
|
|
40
|
+
function getAdditiveFromExpr(
|
|
41
|
+
expr: Parser.ExpressionContext,
|
|
42
|
+
): Parser.AdditiveExpressionContext | null {
|
|
43
|
+
const ternary = expr.ternaryExpression();
|
|
44
|
+
if (!ternary) return null;
|
|
45
|
+
|
|
46
|
+
const orExprs = ternary.orExpression();
|
|
47
|
+
if (orExprs.length !== 1) return null;
|
|
48
|
+
|
|
49
|
+
const or = orExprs[0];
|
|
50
|
+
const and = or.andExpression()[0];
|
|
51
|
+
if (!and) return null;
|
|
52
|
+
|
|
53
|
+
const eq = and.equalityExpression()[0];
|
|
54
|
+
if (!eq) return null;
|
|
55
|
+
|
|
56
|
+
const rel = eq.relationalExpression()[0];
|
|
57
|
+
if (!rel) return null;
|
|
58
|
+
|
|
59
|
+
const bor = rel.bitwiseOrExpression()[0];
|
|
60
|
+
if (!bor) return null;
|
|
61
|
+
|
|
62
|
+
const bxor = bor.bitwiseXorExpression()[0];
|
|
63
|
+
if (!bxor) return null;
|
|
64
|
+
|
|
65
|
+
const band = bxor.bitwiseAndExpression()[0];
|
|
66
|
+
if (!band) return null;
|
|
67
|
+
|
|
68
|
+
const shift = band.shiftExpression()[0];
|
|
69
|
+
if (!shift) return null;
|
|
70
|
+
|
|
71
|
+
return shift.additiveExpression()[0] ?? null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Helper to parse a function declaration and get its parameter list
|
|
76
|
+
*/
|
|
77
|
+
function parseFunctionDeclaration(source: string): {
|
|
78
|
+
name: string;
|
|
79
|
+
paramList: Parser.ParameterListContext | null;
|
|
80
|
+
} {
|
|
81
|
+
const { tree, errors } = CNextSourceParser.parse(source);
|
|
82
|
+
|
|
83
|
+
if (errors.length > 0) {
|
|
84
|
+
throw new Error(`Parse failed: ${errors.map((e) => e.message).join(", ")}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const decl of tree.declaration()) {
|
|
88
|
+
const funcDecl = decl.functionDeclaration();
|
|
89
|
+
if (funcDecl) {
|
|
90
|
+
return {
|
|
91
|
+
name: funcDecl.IDENTIFIER().getText(),
|
|
92
|
+
paramList: funcDecl.parameterList() ?? null,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
throw new Error("Could not find function declaration in parsed tree");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
describe("CodegenParserUtils", () => {
|
|
101
|
+
describe("getOperatorsFromChildren", () => {
|
|
102
|
+
it("extracts operators from additive expression", () => {
|
|
103
|
+
const expr = parseExpression("1 + 2 - 3");
|
|
104
|
+
const additive = getAdditiveFromExpr(expr);
|
|
105
|
+
expect(additive).not.toBeNull();
|
|
106
|
+
|
|
107
|
+
const operators = CodegenParserUtils.getOperatorsFromChildren(additive!);
|
|
108
|
+
expect(operators).toEqual(["+", "-"]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("extracts operators from multiplicative expression", () => {
|
|
112
|
+
const expr = parseExpression("2 * 3 / 4");
|
|
113
|
+
const additive = getAdditiveFromExpr(expr);
|
|
114
|
+
const mult = additive?.multiplicativeExpression(0);
|
|
115
|
+
expect(mult).toBeDefined();
|
|
116
|
+
|
|
117
|
+
const operators = CodegenParserUtils.getOperatorsFromChildren(mult!);
|
|
118
|
+
expect(operators).toEqual(["*", "/"]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("returns empty array for expression with no operators", () => {
|
|
122
|
+
// For a simple expression like "42", the additive expression has only one
|
|
123
|
+
// multiplicative child (no operators between terms)
|
|
124
|
+
const expr = parseExpression("42");
|
|
125
|
+
const additive = getAdditiveFromExpr(expr);
|
|
126
|
+
expect(additive).not.toBeNull();
|
|
127
|
+
|
|
128
|
+
// Additive expression with single term has no terminal operators
|
|
129
|
+
const operators = CodegenParserUtils.getOperatorsFromChildren(additive!);
|
|
130
|
+
expect(operators).toEqual([]);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("getSimpleIdentifier", () => {
|
|
135
|
+
it("returns identifier for simple variable", () => {
|
|
136
|
+
const expr = parseExpression("myVar");
|
|
137
|
+
expect(CodegenParserUtils.getSimpleIdentifier(expr)).toBe("myVar");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("returns null for member access", () => {
|
|
141
|
+
const expr = parseExpression("obj.field");
|
|
142
|
+
expect(CodegenParserUtils.getSimpleIdentifier(expr)).toBeNull();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("returns null for array access", () => {
|
|
146
|
+
const expr = parseExpression("arr[0]");
|
|
147
|
+
expect(CodegenParserUtils.getSimpleIdentifier(expr)).toBeNull();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("returns null for binary expression", () => {
|
|
151
|
+
const expr = parseExpression("a + b");
|
|
152
|
+
expect(CodegenParserUtils.getSimpleIdentifier(expr)).toBeNull();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("returns null for function call", () => {
|
|
156
|
+
const expr = parseExpression("foo()");
|
|
157
|
+
expect(CodegenParserUtils.getSimpleIdentifier(expr)).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("returns null for literal", () => {
|
|
161
|
+
const expr = parseExpression("42");
|
|
162
|
+
expect(CodegenParserUtils.getSimpleIdentifier(expr)).toBeNull();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe("isMainFunctionWithArgs", () => {
|
|
167
|
+
it("returns true for main with string args[]", () => {
|
|
168
|
+
const { name, paramList } = parseFunctionDeclaration(
|
|
169
|
+
"void main(string args[]) {}",
|
|
170
|
+
);
|
|
171
|
+
expect(CodegenParserUtils.isMainFunctionWithArgs(name, paramList)).toBe(
|
|
172
|
+
true,
|
|
173
|
+
);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("returns true for main with u8 args[][]", () => {
|
|
177
|
+
const { name, paramList } = parseFunctionDeclaration(
|
|
178
|
+
"void main(u8 args[][]) {}",
|
|
179
|
+
);
|
|
180
|
+
expect(CodegenParserUtils.isMainFunctionWithArgs(name, paramList)).toBe(
|
|
181
|
+
true,
|
|
182
|
+
);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("returns true for main with i8 args[][]", () => {
|
|
186
|
+
const { name, paramList } = parseFunctionDeclaration(
|
|
187
|
+
"void main(i8 args[][]) {}",
|
|
188
|
+
);
|
|
189
|
+
expect(CodegenParserUtils.isMainFunctionWithArgs(name, paramList)).toBe(
|
|
190
|
+
true,
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("returns false for main with no parameters", () => {
|
|
195
|
+
const { name, paramList } = parseFunctionDeclaration("void main() {}");
|
|
196
|
+
expect(CodegenParserUtils.isMainFunctionWithArgs(name, paramList)).toBe(
|
|
197
|
+
false,
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("returns false for non-main function", () => {
|
|
202
|
+
const { name, paramList } = parseFunctionDeclaration(
|
|
203
|
+
"void foo(string args[]) {}",
|
|
204
|
+
);
|
|
205
|
+
expect(CodegenParserUtils.isMainFunctionWithArgs(name, paramList)).toBe(
|
|
206
|
+
false,
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("returns false for main with wrong parameter type", () => {
|
|
211
|
+
const { name, paramList } = parseFunctionDeclaration(
|
|
212
|
+
"void main(u32 count) {}",
|
|
213
|
+
);
|
|
214
|
+
expect(CodegenParserUtils.isMainFunctionWithArgs(name, paramList)).toBe(
|
|
215
|
+
false,
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("returns false for main with multiple parameters", () => {
|
|
220
|
+
const { name, paramList } = parseFunctionDeclaration(
|
|
221
|
+
"void main(string args[], u32 count) {}",
|
|
222
|
+
);
|
|
223
|
+
expect(CodegenParserUtils.isMainFunctionWithArgs(name, paramList)).toBe(
|
|
224
|
+
false,
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import ITranspileError from "../../lib/types/ITranspileError";
|
|
2
|
-
import ITranspileContribution from "./ITranspileContribution";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Result of transpiling a single file
|
|
@@ -25,9 +24,6 @@ interface IFileResult {
|
|
|
25
24
|
|
|
26
25
|
/** Number of top-level declarations found */
|
|
27
26
|
declarationCount: number;
|
|
28
|
-
|
|
29
|
-
/** Contributions from this file for accumulation in run() */
|
|
30
|
-
contribution?: ITranspileContribution;
|
|
31
27
|
}
|
|
32
28
|
|
|
33
29
|
export default IFileResult;
|