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,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SizeofResolver - Handles sizeof expression generation
|
|
3
|
+
*
|
|
4
|
+
* Extracted from CodeGenerator to reduce complexity.
|
|
5
|
+
* Uses CodeGenState for all state access.
|
|
6
|
+
*
|
|
7
|
+
* ADR-023: sizeof expression handling with safety checks:
|
|
8
|
+
* - E0601: sizeof on array parameter is error (returns pointer size)
|
|
9
|
+
* - E0602: Side effects in sizeof are error (MISRA C:2012 Rule 13.6)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as Parser from "../../../logic/parser/grammar/CNextParser";
|
|
13
|
+
import CodeGenState from "../CodeGenState";
|
|
14
|
+
import ExpressionUnwrapper from "../utils/ExpressionUnwrapper";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Callbacks for operations that require CodeGenerator context.
|
|
18
|
+
* These are the minimal dependencies that can't be replaced with CodeGenState.
|
|
19
|
+
*/
|
|
20
|
+
interface ISizeofCallbacks {
|
|
21
|
+
generateType: (ctx: Parser.TypeContext) => string;
|
|
22
|
+
generateExpression: (ctx: Parser.ExpressionContext) => string;
|
|
23
|
+
hasSideEffects: (ctx: Parser.ExpressionContext) => boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Resolves sizeof expressions to C code.
|
|
28
|
+
*/
|
|
29
|
+
export default class SizeofResolver {
|
|
30
|
+
/**
|
|
31
|
+
* Generate sizeof expression.
|
|
32
|
+
* sizeof(type) -> sizeof(c_type)
|
|
33
|
+
* sizeof(variable) -> sizeof(variable)
|
|
34
|
+
*/
|
|
35
|
+
static generate(
|
|
36
|
+
ctx: Parser.SizeofExpressionContext,
|
|
37
|
+
callbacks: ISizeofCallbacks,
|
|
38
|
+
): string {
|
|
39
|
+
// Check if it's sizeof(type) or sizeof(expression)
|
|
40
|
+
// Note: Due to grammar ambiguity, sizeof(variable) may parse as sizeof(type)
|
|
41
|
+
// when the variable name matches userType (just an identifier)
|
|
42
|
+
if (ctx.type()) {
|
|
43
|
+
return this.sizeofType(ctx.type()!, callbacks);
|
|
44
|
+
}
|
|
45
|
+
return this.sizeofExpression(ctx.expression()!, callbacks);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Handle sizeof(type) - may actually be sizeof(variable) due to grammar ambiguity
|
|
50
|
+
*/
|
|
51
|
+
private static sizeofType(
|
|
52
|
+
typeCtx: Parser.TypeContext,
|
|
53
|
+
callbacks: ISizeofCallbacks,
|
|
54
|
+
): string {
|
|
55
|
+
// qualifiedType matches IDENTIFIER.IDENTIFIER, could be struct.member
|
|
56
|
+
if (typeCtx.qualifiedType()) {
|
|
57
|
+
const result = this.sizeofQualifiedType(typeCtx.qualifiedType()!);
|
|
58
|
+
if (result) return result;
|
|
59
|
+
// Fall through to generateType for actual type references (Scope.Type)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// userType is just IDENTIFIER, could be a variable reference
|
|
63
|
+
if (typeCtx.userType()) {
|
|
64
|
+
return this.sizeofUserType(typeCtx.getText());
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// It's a primitive or other type - generate normally
|
|
68
|
+
return `sizeof(${callbacks.generateType(typeCtx)})`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Handle sizeof(qualified.type) - may be struct.member access
|
|
73
|
+
* Returns null if this is actually a type reference (Scope.Type)
|
|
74
|
+
*/
|
|
75
|
+
private static sizeofQualifiedType(
|
|
76
|
+
qualifiedCtx: Parser.QualifiedTypeContext,
|
|
77
|
+
): string | null {
|
|
78
|
+
const identifiers = qualifiedCtx.IDENTIFIER();
|
|
79
|
+
const firstName = identifiers[0].getText();
|
|
80
|
+
const memberName = identifiers[1].getText();
|
|
81
|
+
|
|
82
|
+
// Check if first identifier is a local variable (struct instance)
|
|
83
|
+
if (CodeGenState.localVariables.has(firstName)) {
|
|
84
|
+
return `sizeof(${firstName}.${memberName})`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check if first identifier is a parameter (struct parameter)
|
|
88
|
+
const paramInfo = CodeGenState.currentParameters.get(firstName);
|
|
89
|
+
if (paramInfo) {
|
|
90
|
+
const sep = paramInfo.isStruct ? "->" : ".";
|
|
91
|
+
return `sizeof(${firstName}${sep}${memberName})`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Check if first identifier is a global variable
|
|
95
|
+
// If not a scope or enum, it's likely a global struct variable
|
|
96
|
+
if (
|
|
97
|
+
!CodeGenState.isKnownScope(firstName) &&
|
|
98
|
+
!CodeGenState.isKnownEnum(firstName)
|
|
99
|
+
) {
|
|
100
|
+
return `sizeof(${firstName}.${memberName})`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// It's an actual type reference (Scope.Type), return null to fall through
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Handle sizeof(identifier) - could be variable or type name
|
|
109
|
+
*/
|
|
110
|
+
private static sizeofUserType(varName: string): string {
|
|
111
|
+
// Check if it's a known parameter
|
|
112
|
+
const paramInfo = CodeGenState.currentParameters.get(varName);
|
|
113
|
+
if (paramInfo) {
|
|
114
|
+
return this.sizeofParameter(varName, paramInfo);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check if it's a known local variable, struct type, or enum type
|
|
118
|
+
// For all these cases, generate sizeof(name) directly
|
|
119
|
+
// Unknown identifiers are also treated as variables for safety
|
|
120
|
+
return `sizeof(${varName})`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Handle sizeof on a parameter - validates and generates appropriate code
|
|
125
|
+
*/
|
|
126
|
+
private static sizeofParameter(
|
|
127
|
+
varName: string,
|
|
128
|
+
paramInfo: { isArray?: boolean; isCallback?: boolean; isStruct?: boolean },
|
|
129
|
+
): string {
|
|
130
|
+
// E0601: Array parameters decay to pointers
|
|
131
|
+
if (paramInfo.isArray) {
|
|
132
|
+
this.throwArrayParamSizeofError(varName);
|
|
133
|
+
}
|
|
134
|
+
// For pass-by-reference parameters (non-array, non-callback, non-struct),
|
|
135
|
+
// use pointer dereference
|
|
136
|
+
if (!paramInfo.isCallback && !paramInfo.isStruct) {
|
|
137
|
+
return `sizeof(*${varName})`;
|
|
138
|
+
}
|
|
139
|
+
return `sizeof(${varName})`;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Throw E0601 error for sizeof on array parameter
|
|
144
|
+
*/
|
|
145
|
+
private static throwArrayParamSizeofError(varName: string): never {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`Error[E0601]: sizeof() on array parameter '${varName}' returns pointer size. ` +
|
|
148
|
+
`Use ${varName}.length for element count or sizeof(elementType) * ${varName}.length for bytes`,
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Handle sizeof(expression) with validation
|
|
154
|
+
*/
|
|
155
|
+
private static sizeofExpression(
|
|
156
|
+
expr: Parser.ExpressionContext,
|
|
157
|
+
callbacks: ISizeofCallbacks,
|
|
158
|
+
): string {
|
|
159
|
+
// E0601: Check if expression is an array parameter
|
|
160
|
+
const varName = ExpressionUnwrapper.getSimpleIdentifier(expr);
|
|
161
|
+
if (varName) {
|
|
162
|
+
const paramInfo = CodeGenState.currentParameters.get(varName);
|
|
163
|
+
if (paramInfo?.isArray) {
|
|
164
|
+
this.throwArrayParamSizeofError(varName);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// E0602: Check for side effects
|
|
169
|
+
if (callbacks.hasSideEffects(expr)) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`Error[E0602]: sizeof() operand must not have side effects (MISRA C:2012 Rule 13.6)`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return `sizeof(${callbacks.generateExpression(expr)})`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for EnumTypeResolver - enum type inference from expressions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
6
|
+
import EnumTypeResolver from "../EnumTypeResolver";
|
|
7
|
+
import CodeGenState from "../../CodeGenState";
|
|
8
|
+
import SymbolTable from "../../../../logic/symbols/SymbolTable";
|
|
9
|
+
import ICodeGenSymbols from "../../../../types/ICodeGenSymbols";
|
|
10
|
+
|
|
11
|
+
describe("EnumTypeResolver", () => {
|
|
12
|
+
const createMockSymbols = (
|
|
13
|
+
overrides: Partial<ICodeGenSymbols> = {},
|
|
14
|
+
): ICodeGenSymbols =>
|
|
15
|
+
({
|
|
16
|
+
knownScopes: new Set(),
|
|
17
|
+
knownEnums: new Set(),
|
|
18
|
+
knownBitmaps: new Set(),
|
|
19
|
+
knownStructs: new Set(),
|
|
20
|
+
knownRegisters: new Set(),
|
|
21
|
+
bitmapBitWidth: new Map(),
|
|
22
|
+
bitmapFields: new Map(),
|
|
23
|
+
bitmapBackingType: new Map(),
|
|
24
|
+
enumMembers: new Map(),
|
|
25
|
+
structFields: new Map(),
|
|
26
|
+
structFieldArrays: new Map(),
|
|
27
|
+
structFieldDimensions: new Map(),
|
|
28
|
+
functionReturnTypes: new Map(),
|
|
29
|
+
scopeMembers: new Map(),
|
|
30
|
+
scopeMemberVisibility: new Map(),
|
|
31
|
+
scopedRegisters: new Map(),
|
|
32
|
+
registerMemberAccess: new Map(),
|
|
33
|
+
registerMemberTypes: new Map(),
|
|
34
|
+
registerBaseAddresses: new Map(),
|
|
35
|
+
registerMemberOffsets: new Map(),
|
|
36
|
+
registerMemberCTypes: new Map(),
|
|
37
|
+
scopeVariableUsage: new Map(),
|
|
38
|
+
scopePrivateConstValues: new Map(),
|
|
39
|
+
getSingleFunctionForVariable: () => null,
|
|
40
|
+
hasPublicSymbols: () => false,
|
|
41
|
+
...overrides,
|
|
42
|
+
}) as ICodeGenSymbols;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
CodeGenState.reset();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("resolve() - function call patterns", () => {
|
|
49
|
+
it("resolves function call returning enum type", () => {
|
|
50
|
+
CodeGenState.symbols = createMockSymbols({
|
|
51
|
+
knownEnums: new Set(["State"]),
|
|
52
|
+
functionReturnTypes: new Map([["getState", "State"]]),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const mockCtx = { getText: () => "getState()" };
|
|
56
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("resolves this.method() returning enum type", () => {
|
|
60
|
+
CodeGenState.currentScope = "Motor";
|
|
61
|
+
CodeGenState.symbols = createMockSymbols({
|
|
62
|
+
knownEnums: new Set(["State"]),
|
|
63
|
+
functionReturnTypes: new Map([["Motor_getState", "State"]]),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const mockCtx = { getText: () => "this.getState()" };
|
|
67
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("resolves global.func() returning enum type", () => {
|
|
71
|
+
CodeGenState.symbols = createMockSymbols({
|
|
72
|
+
knownEnums: new Set(["State"]),
|
|
73
|
+
functionReturnTypes: new Map([["getGlobalState", "State"]]),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const mockCtx = { getText: () => "global.getGlobalState()" };
|
|
77
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("resolves Scope.method() returning enum type", () => {
|
|
81
|
+
CodeGenState.symbols = createMockSymbols({
|
|
82
|
+
knownScopes: new Set(["Motor"]),
|
|
83
|
+
knownEnums: new Set(["State"]),
|
|
84
|
+
functionReturnTypes: new Map([["Motor_getState", "State"]]),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const mockCtx = { getText: () => "Motor.getState()" };
|
|
88
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("resolves global.Scope.method() returning enum type", () => {
|
|
92
|
+
CodeGenState.symbols = createMockSymbols({
|
|
93
|
+
knownScopes: new Set(["Motor"]),
|
|
94
|
+
knownEnums: new Set(["State"]),
|
|
95
|
+
functionReturnTypes: new Map([["Motor_getState", "State"]]),
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const mockCtx = { getText: () => "global.Motor.getState()" };
|
|
99
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("returns null for function returning non-enum type", () => {
|
|
103
|
+
CodeGenState.symbols = createMockSymbols({
|
|
104
|
+
functionReturnTypes: new Map([["getValue", "u32"]]),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const mockCtx = { getText: () => "getValue()" };
|
|
108
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("returns null for unknown function", () => {
|
|
112
|
+
CodeGenState.symbols = createMockSymbols();
|
|
113
|
+
|
|
114
|
+
const mockCtx = { getText: () => "unknownFunc()" };
|
|
115
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("resolve() - simple identifier patterns", () => {
|
|
120
|
+
it("resolves enum variable by type registry lookup", () => {
|
|
121
|
+
CodeGenState.symbols = createMockSymbols({
|
|
122
|
+
knownEnums: new Set(["State"]),
|
|
123
|
+
});
|
|
124
|
+
CodeGenState.typeRegistry.set("currentState", {
|
|
125
|
+
baseType: "State",
|
|
126
|
+
bitWidth: 0,
|
|
127
|
+
isArray: false,
|
|
128
|
+
isConst: false,
|
|
129
|
+
isEnum: true,
|
|
130
|
+
enumTypeName: "State",
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const mockCtx = { getText: () => "currentState" };
|
|
134
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("returns null for non-enum variable", () => {
|
|
138
|
+
CodeGenState.typeRegistry.set("count", {
|
|
139
|
+
baseType: "u32",
|
|
140
|
+
bitWidth: 32,
|
|
141
|
+
isArray: false,
|
|
142
|
+
isConst: false,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const mockCtx = { getText: () => "count" };
|
|
146
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("resolve() - member access patterns", () => {
|
|
151
|
+
it("resolves simple enum member access: State.IDLE", () => {
|
|
152
|
+
CodeGenState.symbols = createMockSymbols({
|
|
153
|
+
knownEnums: new Set(["State"]),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const mockCtx = { getText: () => "State.IDLE" };
|
|
157
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("State");
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("resolves scoped enum: Motor.State.IDLE -> Motor_State", () => {
|
|
161
|
+
CodeGenState.symbols = createMockSymbols({
|
|
162
|
+
knownEnums: new Set(["Motor_State"]),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const mockCtx = { getText: () => "Motor.State.IDLE" };
|
|
166
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("Motor_State");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("resolves this.Enum.MEMBER inside scope", () => {
|
|
170
|
+
CodeGenState.currentScope = "Motor";
|
|
171
|
+
CodeGenState.symbols = createMockSymbols({
|
|
172
|
+
knownEnums: new Set(["Motor_State"]),
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
const mockCtx = { getText: () => "this.State.IDLE" };
|
|
176
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("Motor_State");
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("resolves global.Enum.MEMBER pattern", () => {
|
|
180
|
+
CodeGenState.symbols = createMockSymbols({
|
|
181
|
+
knownEnums: new Set(["ECategory"]),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const mockCtx = { getText: () => "global.ECategory.CAT_A" };
|
|
185
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("ECategory");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("resolves this.variable pattern for enum-typed scope member", () => {
|
|
189
|
+
CodeGenState.currentScope = "Motor";
|
|
190
|
+
CodeGenState.symbols = createMockSymbols({
|
|
191
|
+
knownEnums: new Set(["Motor_State"]),
|
|
192
|
+
});
|
|
193
|
+
CodeGenState.typeRegistry.set("Motor_current", {
|
|
194
|
+
baseType: "Motor_State",
|
|
195
|
+
bitWidth: 0,
|
|
196
|
+
isArray: false,
|
|
197
|
+
isConst: false,
|
|
198
|
+
isEnum: true,
|
|
199
|
+
enumTypeName: "Motor_State",
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const mockCtx = { getText: () => "this.current" };
|
|
203
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBe("Motor_State");
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
describe("resolve() - TypeResolver fallback for struct member chains", () => {
|
|
208
|
+
/**
|
|
209
|
+
* Helper to build a mock ExpressionContext that contains a full postfix
|
|
210
|
+
* expression tree: global.input.assignedValue
|
|
211
|
+
*/
|
|
212
|
+
const buildStructChainCtx = (
|
|
213
|
+
primaryToken: "GLOBAL" | "THIS" | "IDENTIFIER",
|
|
214
|
+
primaryText: string,
|
|
215
|
+
suffixes: string[],
|
|
216
|
+
) => {
|
|
217
|
+
const primary = {
|
|
218
|
+
IDENTIFIER: () =>
|
|
219
|
+
primaryToken === "IDENTIFIER" ? { getText: () => primaryText } : null,
|
|
220
|
+
GLOBAL: () =>
|
|
221
|
+
primaryToken === "GLOBAL" ? { getText: () => "global" } : null,
|
|
222
|
+
THIS: () =>
|
|
223
|
+
primaryToken === "THIS" ? { getText: () => "this" } : null,
|
|
224
|
+
literal: () => null,
|
|
225
|
+
expression: () => null,
|
|
226
|
+
castExpression: () => null,
|
|
227
|
+
};
|
|
228
|
+
const children = [
|
|
229
|
+
{ getText: () => primaryText },
|
|
230
|
+
...suffixes.map((s) => ({ getText: () => s })),
|
|
231
|
+
];
|
|
232
|
+
const postfix = { primaryExpression: () => primary, children };
|
|
233
|
+
|
|
234
|
+
// Build the full expression tree wrapping the postfix
|
|
235
|
+
const unary = {
|
|
236
|
+
postfixExpression: () => postfix,
|
|
237
|
+
unaryExpression: () => null,
|
|
238
|
+
};
|
|
239
|
+
const mult = { unaryExpression: () => [unary] };
|
|
240
|
+
const add = { multiplicativeExpression: () => [mult] };
|
|
241
|
+
const shift = { additiveExpression: () => [add] };
|
|
242
|
+
const bitAnd = { shiftExpression: () => [shift] };
|
|
243
|
+
const bitXor = { bitwiseAndExpression: () => [bitAnd] };
|
|
244
|
+
const bitOr = { bitwiseXorExpression: () => [bitXor] };
|
|
245
|
+
const rel = { bitwiseOrExpression: () => [bitOr] };
|
|
246
|
+
const eq = { relationalExpression: () => [rel] };
|
|
247
|
+
const and = { equalityExpression: () => [eq] };
|
|
248
|
+
const or = { andExpression: () => [and] };
|
|
249
|
+
const ternary = { orExpression: () => [or] };
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
getText: () => primaryText + suffixes.join(""),
|
|
253
|
+
ternaryExpression: () => ternary,
|
|
254
|
+
} as never;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
it("resolves global.struct.enumField via TypeResolver fallback", () => {
|
|
258
|
+
const symbolTable = new SymbolTable();
|
|
259
|
+
symbolTable.addStructField("TInput", "assignedValue", "EValueId");
|
|
260
|
+
CodeGenState.symbolTable = symbolTable;
|
|
261
|
+
CodeGenState.symbols = createMockSymbols({
|
|
262
|
+
knownEnums: new Set(["EValueId"]),
|
|
263
|
+
});
|
|
264
|
+
CodeGenState.typeRegistry.set("input", {
|
|
265
|
+
baseType: "TInput",
|
|
266
|
+
bitWidth: 0,
|
|
267
|
+
isArray: false,
|
|
268
|
+
isConst: false,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const ctx = buildStructChainCtx("GLOBAL", "global", [
|
|
272
|
+
".input",
|
|
273
|
+
".assignedValue",
|
|
274
|
+
]);
|
|
275
|
+
expect(EnumTypeResolver.resolve(ctx)).toBe("EValueId");
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it("returns null when struct field is not an enum type", () => {
|
|
279
|
+
const symbolTable = new SymbolTable();
|
|
280
|
+
symbolTable.addStructField("TInput", "count", "u32");
|
|
281
|
+
CodeGenState.symbolTable = symbolTable;
|
|
282
|
+
CodeGenState.symbols = createMockSymbols();
|
|
283
|
+
CodeGenState.typeRegistry.set("input", {
|
|
284
|
+
baseType: "TInput",
|
|
285
|
+
bitWidth: 0,
|
|
286
|
+
isArray: false,
|
|
287
|
+
isConst: false,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
const ctx = buildStructChainCtx("GLOBAL", "global", [".input", ".count"]);
|
|
291
|
+
expect(EnumTypeResolver.resolve(ctx)).toBeNull();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("returns null for RelationalExpressionContext (no ternaryExpression)", () => {
|
|
295
|
+
CodeGenState.symbols = createMockSymbols();
|
|
296
|
+
// RelationalExpressionContext doesn't have ternaryExpression
|
|
297
|
+
const ctx = { getText: () => "something.weird" } as never;
|
|
298
|
+
expect(EnumTypeResolver.resolve(ctx)).toBeNull();
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe("resolve() - edge cases", () => {
|
|
303
|
+
it("returns null for this.Enum.MEMBER when not in a scope", () => {
|
|
304
|
+
CodeGenState.currentScope = null;
|
|
305
|
+
CodeGenState.symbols = createMockSymbols({
|
|
306
|
+
knownEnums: new Set(["Motor_State"]),
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const mockCtx = { getText: () => "this.State.IDLE" };
|
|
310
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it("returns null for this.variable when not in a scope", () => {
|
|
314
|
+
CodeGenState.currentScope = null;
|
|
315
|
+
|
|
316
|
+
const mockCtx = { getText: () => "this.current" };
|
|
317
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("returns null for unknown enum in scoped pattern", () => {
|
|
321
|
+
CodeGenState.symbols = createMockSymbols({
|
|
322
|
+
knownEnums: new Set(), // No enums
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const mockCtx = { getText: () => "Motor.State.IDLE" };
|
|
326
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it("returns null for single identifier that is not in type registry", () => {
|
|
330
|
+
CodeGenState.symbols = createMockSymbols();
|
|
331
|
+
|
|
332
|
+
const mockCtx = { getText: () => "unknownVar" };
|
|
333
|
+
expect(EnumTypeResolver.resolve(mockCtx as never)).toBeNull();
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
});
|