c-next 0.1.33 → 0.1.34
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 +7 -0
- package/package.json +7 -6
- package/src/analysis/DivisionByZeroAnalyzer.test.ts +2 -2
- package/src/analysis/DivisionByZeroAnalyzer.ts +9 -190
- package/src/analysis/FloatModuloAnalyzer.test.ts +2 -2
- package/src/analysis/FloatModuloAnalyzer.ts +9 -21
- package/src/analysis/FunctionCallAnalyzer.test.ts +105 -3
- package/src/analysis/FunctionCallAnalyzer.ts +53 -13
- package/src/analysis/InitializationAnalyzer.ts +18 -54
- package/src/analysis/NullCheckAnalyzer.test.ts +1347 -0
- package/src/analysis/NullCheckAnalyzer.ts +16 -20
- package/src/analysis/ParameterNamingAnalyzer.test.ts +2 -2
- package/src/analysis/ParameterNamingAnalyzer.ts +2 -2
- package/src/analysis/StructFieldAnalyzer.ts +3 -3
- package/src/codegen/CodeGenerator.ts +98 -95
- package/src/codegen/CommentExtractor.test.ts +1 -1
- package/src/codegen/CommentExtractor.ts +1 -1
- package/src/codegen/CommentFormatter.ts +1 -1
- package/src/codegen/HeaderGenerator.test.ts +131 -0
- package/src/codegen/HeaderGenerator.ts +90 -6
- package/src/codegen/TypeResolver.test.ts +1 -1
- package/src/codegen/TypeResolver.ts +9 -9
- package/src/codegen/TypeValidator.test.ts +1 -1
- package/src/codegen/TypeValidator.ts +23 -23
- package/src/codegen/generators/IGeneratorInput.ts +1 -1
- package/src/codegen/generators/IOrchestrator.ts +1 -1
- package/src/codegen/generators/ISymbolInfo.ts +14 -0
- package/src/codegen/generators/declarationGenerators/BitmapGenerator.ts +1 -1
- package/src/codegen/generators/declarationGenerators/EnumGenerator.ts +1 -1
- package/src/codegen/generators/declarationGenerators/FunctionGenerator.ts +1 -1
- package/src/codegen/generators/declarationGenerators/RegisterGenerator.ts +1 -1
- package/src/codegen/generators/declarationGenerators/ScopeGenerator.ts +4 -4
- package/src/codegen/generators/declarationGenerators/ScopedRegisterGenerator.ts +1 -1
- package/src/codegen/generators/declarationGenerators/StructGenerator.ts +1 -1
- package/src/codegen/generators/expressions/BinaryExprGenerator.ts +7 -7
- package/src/codegen/generators/expressions/CallExprGenerator.ts +1 -1
- package/src/codegen/generators/expressions/ExpressionGenerator.ts +1 -1
- package/src/codegen/generators/expressions/LiteralGenerator.ts +6 -6
- package/src/codegen/generators/expressions/UnaryExprGenerator.ts +1 -1
- package/src/codegen/generators/statements/ControlFlowGenerator.ts +1 -1
- package/src/codegen/generators/statements/CriticalGenerator.ts +1 -1
- package/src/codegen/generators/statements/SwitchGenerator.ts +1 -1
- package/src/codegen/generators/support/IncludeGenerator.ts +6 -6
- package/src/codegen/headerGenerators/mapType.test.ts +73 -1
- package/src/codegen/headerGenerators/mapType.ts +28 -2
- package/src/codegen/types/ICodeGeneratorOptions.ts +7 -0
- package/src/codegen/types/IHeaderOptions.ts +8 -0
- package/src/codegen/types/ITypeResolverDeps.ts +4 -4
- package/src/codegen/types/ITypeValidatorDeps.ts +4 -4
- package/src/commands/CleanCommand.ts +2 -2
- package/src/constants/TypeConstants.test.ts +29 -0
- package/src/constants/TypeConstants.ts +25 -0
- package/src/index.ts +2 -2
- package/src/lib/IncludeDiscovery.ts +2 -2
- package/src/lib/IncludeResolver.test.ts +2 -2
- package/src/lib/IncludeResolver.ts +2 -2
- package/src/lib/InputExpansion.ts +2 -2
- package/src/lib/parseWithSymbols.ts +9 -6
- package/src/lib/transpiler.ts +2 -2
- package/src/pipeline/CacheManager.ts +47 -24
- package/src/pipeline/Pipeline.ts +131 -25
- package/src/pipeline/__tests__/CacheManager.test.ts +838 -0
- package/src/pipeline/cache/CacheKeyGenerator.ts +36 -0
- package/src/pipeline/cache/__tests__/CacheKeyGenerator.test.ts +104 -0
- package/src/pipeline/runAnalyzers.ts +2 -2
- package/src/pipeline/types/ICachedFileEntry.ts +3 -3
- package/src/pipeline/types/IFileResult.ts +3 -0
- package/src/preprocessor/Preprocessor.ts +9 -9
- package/src/preprocessor/ToolchainDetector.ts +3 -3
- package/src/project/FileDiscovery.ts +2 -2
- package/src/project/Project.ts +1 -1
- package/src/{symbols → symbol_resolution}/CSymbolCollector.ts +1 -1
- package/src/{symbols → symbol_resolution}/CppSymbolCollector.ts +1 -1
- package/src/{symbols → symbol_resolution}/SymbolUtils.ts +2 -2
- package/src/symbol_resolution/cnext/__tests__/BitmapCollector.test.ts +162 -0
- package/src/symbol_resolution/cnext/__tests__/CNextResolver.integration.test.ts +359 -0
- package/src/symbol_resolution/cnext/__tests__/EnumCollector.test.ts +158 -0
- package/src/symbol_resolution/cnext/__tests__/FunctionCollector.test.ts +241 -0
- package/src/symbol_resolution/cnext/__tests__/RegisterCollector.test.ts +189 -0
- package/src/symbol_resolution/cnext/__tests__/ScopeCollector.test.ts +290 -0
- package/src/symbol_resolution/cnext/__tests__/StructCollector.test.ts +214 -0
- package/src/symbol_resolution/cnext/__tests__/TSymbolAdapter.test.ts +447 -0
- package/src/symbol_resolution/cnext/__tests__/TSymbolInfoAdapter.test.ts +595 -0
- package/src/symbol_resolution/cnext/__tests__/VariableCollector.test.ts +156 -0
- package/src/symbol_resolution/cnext/__tests__/testHelpers.ts +43 -0
- package/src/symbol_resolution/cnext/adapters/TSymbolAdapter.ts +281 -0
- package/src/symbol_resolution/cnext/adapters/TSymbolInfoAdapter.ts +370 -0
- package/src/symbol_resolution/cnext/collectors/BitmapCollector.ts +72 -0
- package/src/symbol_resolution/cnext/collectors/EnumCollector.ts +68 -0
- package/src/symbol_resolution/cnext/collectors/FunctionCollector.ts +101 -0
- package/src/symbol_resolution/cnext/collectors/RegisterCollector.ts +82 -0
- package/src/symbol_resolution/cnext/collectors/ScopeCollector.ts +156 -0
- package/src/symbol_resolution/cnext/collectors/StructCollector.ts +108 -0
- package/src/symbol_resolution/cnext/collectors/VariableCollector.ts +87 -0
- package/src/symbol_resolution/cnext/index.ts +170 -0
- package/src/symbol_resolution/cnext/types/IScopeCollectorResult.ts +14 -0
- package/src/symbol_resolution/cnext/utils/ExpressionEvaluator.ts +39 -0
- package/src/symbol_resolution/cnext/utils/TypeUtils.ts +91 -0
- package/src/symbol_resolution/types/IBaseSymbol.ts +24 -0
- package/src/symbol_resolution/types/IBitmapFieldInfo.ts +13 -0
- package/src/symbol_resolution/types/IBitmapSymbol.ts +23 -0
- package/src/symbol_resolution/types/IEnumSymbol.ts +18 -0
- package/src/symbol_resolution/types/IFieldInfo.ts +18 -0
- package/src/symbol_resolution/types/IFunctionSymbol.ts +25 -0
- package/src/symbol_resolution/types/IParameterInfo.ts +24 -0
- package/src/symbol_resolution/types/IRegisterMemberInfo.ts +19 -0
- package/src/symbol_resolution/types/IRegisterSymbol.ts +20 -0
- package/src/symbol_resolution/types/IScopeSymbol.ts +19 -0
- package/src/symbol_resolution/types/IStructSymbol.ts +16 -0
- package/src/symbol_resolution/types/IVariableSymbol.ts +27 -0
- package/src/symbol_resolution/types/TSymbol.ts +36 -0
- package/src/symbol_resolution/types/__tests__/SymbolGuards.test.ts +243 -0
- package/src/symbol_resolution/types/typeGuards.ts +44 -0
- package/src/types/ESymbolKind.ts +0 -1
- package/src/utils/ExpressionUtils.test.ts +316 -0
- package/src/utils/ExpressionUtils.ts +144 -0
- package/src/utils/LiteralUtils.test.ts +344 -0
- package/src/utils/LiteralUtils.ts +92 -0
- package/src/utils/ParserUtils.test.ts +104 -0
- package/src/utils/ParserUtils.ts +34 -0
- package/src/utils/types/ISourcePosition.ts +9 -0
- package/src/analysis/types/formatInitializationError.ts +0 -20
- package/src/codegen/SymbolCollector.ts +0 -758
- package/src/codegen/generators/expressions/index.ts +0 -33
- package/src/codegen/generators/statements/index.ts +0 -29
- package/src/codegen/generators/support/index.ts +0 -37
- package/src/codegen/headerGenerators/index.ts +0 -18
- package/src/codegen/types/IAssignmentContext.ts +0 -12
- package/src/codegen/types/TCodeGenContext.ts +0 -33
- package/src/lib/PlatformIODetector.ts +0 -162
- package/src/lib/parse.ts +0 -16
- package/src/symbols/CNextSymbolCollector.ts +0 -507
- /package/src/{parser → antlr_parser}/c/grammar/C.interp +0 -0
- /package/src/{parser → antlr_parser}/c/grammar/C.tokens +0 -0
- /package/src/{parser → antlr_parser}/c/grammar/CLexer.interp +0 -0
- /package/src/{parser → antlr_parser}/c/grammar/CLexer.tokens +0 -0
- /package/src/{parser → antlr_parser}/c/grammar/CLexer.ts +0 -0
- /package/src/{parser → antlr_parser}/c/grammar/CListener.ts +0 -0
- /package/src/{parser → antlr_parser}/c/grammar/CParser.ts +0 -0
- /package/src/{parser → antlr_parser}/c/grammar/CVisitor.ts +0 -0
- /package/src/{parser → antlr_parser}/cpp/grammar/CPP14Lexer.interp +0 -0
- /package/src/{parser → antlr_parser}/cpp/grammar/CPP14Lexer.tokens +0 -0
- /package/src/{parser → antlr_parser}/cpp/grammar/CPP14Lexer.ts +0 -0
- /package/src/{parser → antlr_parser}/cpp/grammar/CPP14Parser.interp +0 -0
- /package/src/{parser → antlr_parser}/cpp/grammar/CPP14Parser.tokens +0 -0
- /package/src/{parser → antlr_parser}/cpp/grammar/CPP14Parser.ts +0 -0
- /package/src/{parser → antlr_parser}/cpp/grammar/CPP14ParserListener.ts +0 -0
- /package/src/{parser → antlr_parser}/cpp/grammar/CPP14ParserVisitor.ts +0 -0
- /package/src/{parser → antlr_parser}/grammar/CNext.interp +0 -0
- /package/src/{parser → antlr_parser}/grammar/CNext.tokens +0 -0
- /package/src/{parser → antlr_parser}/grammar/CNextLexer.interp +0 -0
- /package/src/{parser → antlr_parser}/grammar/CNextLexer.tokens +0 -0
- /package/src/{parser → antlr_parser}/grammar/CNextLexer.ts +0 -0
- /package/src/{parser → antlr_parser}/grammar/CNextListener.ts +0 -0
- /package/src/{parser → antlr_parser}/grammar/CNextParser.ts +0 -0
- /package/src/{parser → antlr_parser}/grammar/CNextVisitor.ts +0 -0
- /package/src/{symbols → symbol_resolution}/SymbolTable.test.ts +0 -0
- /package/src/{symbols → symbol_resolution}/SymbolTable.ts +0 -0
- /package/src/{symbols → symbol_resolution}/SymbolUtils.test.ts +0 -0
- /package/src/{symbols → symbol_resolution}/types/IConflict.ts +0 -0
- /package/src/{symbols → symbol_resolution}/types/IStructFieldInfo.ts +0 -0
package/README.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# C-Next
|
|
2
2
|
|
|
3
|
+
[](https://github.com/jlaustill/c-next/actions/workflows/pr-checks.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/c-next)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://sonarcloud.io/summary/new_code?id=jlaustill_c-next)
|
|
7
|
+
[](https://sonarcloud.io/summary/new_code?id=jlaustill_c-next)
|
|
8
|
+
[](https://sonarcloud.io/summary/new_code?id=jlaustill_c-next)
|
|
9
|
+
|
|
3
10
|
A safer C for embedded systems development. Transpiles to clean, readable C.
|
|
4
11
|
|
|
5
12
|
**Status: Working Transpiler** — Verified on Teensy MicroMod hardware.
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "c-next",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"description": "A safer C for embedded systems development. Transpiles to clean, readable C.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cnext": "./bin/cnext.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"antlr": "antlr4ng -Dlanguage=TypeScript -visitor -listener -o src/
|
|
11
|
-
"antlr:c": "antlr4ng -Dlanguage=TypeScript -visitor -listener -o src/
|
|
12
|
-
"antlr:cpp:lexer": "antlr4ng -Dlanguage=TypeScript -o src/
|
|
13
|
-
"antlr:cpp:parser": "antlr4ng -Dlanguage=TypeScript -visitor -listener -o src/
|
|
10
|
+
"antlr": "antlr4ng -Dlanguage=TypeScript -visitor -listener -o src/antlr_parser grammar/CNext.g4",
|
|
11
|
+
"antlr:c": "antlr4ng -Dlanguage=TypeScript -visitor -listener -o src/antlr_parser/c grammar/C.g4",
|
|
12
|
+
"antlr:cpp:lexer": "antlr4ng -Dlanguage=TypeScript -o src/antlr_parser/cpp grammar/CPP14Lexer.g4",
|
|
13
|
+
"antlr:cpp:parser": "antlr4ng -Dlanguage=TypeScript -visitor -listener -o src/antlr_parser/cpp -lib src/antlr_parser/cpp/grammar grammar/CPP14Parser.g4",
|
|
14
14
|
"antlr:cpp": "npm run antlr:cpp:lexer && npm run antlr:cpp:parser",
|
|
15
15
|
"antlr:all": "npm run antlr && npm run antlr:c && npm run antlr:cpp",
|
|
16
16
|
"start": "tsx src/index.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"test:q": "tsx scripts/test.ts -q",
|
|
22
22
|
"test:update": "tsx scripts/test.ts --update",
|
|
23
23
|
"analyze": "./scripts/static-analysis.sh",
|
|
24
|
-
"clean": "rm -rf src/
|
|
24
|
+
"clean": "rm -rf src/antlr_parser",
|
|
25
25
|
"prettier:check": "prettier --check .",
|
|
26
26
|
"prettier:fix": "prettier --write .",
|
|
27
27
|
"oxlint:check": "oxlint src/",
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"@vitest/coverage-v8": "^3.2.4",
|
|
74
74
|
"antlr4ng-cli": "^2.0.0",
|
|
75
75
|
"husky": "^9.1.7",
|
|
76
|
+
"knip": "^5.82.1",
|
|
76
77
|
"lint-staged": "^16.2.7",
|
|
77
78
|
"oxlint": "^1.39.0",
|
|
78
79
|
"prettier": "^3.7.4",
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { describe, it, expect } from "vitest";
|
|
6
6
|
import { CharStream, CommonTokenStream } from "antlr4ng";
|
|
7
|
-
import { CNextLexer } from "../
|
|
8
|
-
import { CNextParser } from "../
|
|
7
|
+
import { CNextLexer } from "../antlr_parser/grammar/CNextLexer";
|
|
8
|
+
import { CNextParser } from "../antlr_parser/grammar/CNextParser";
|
|
9
9
|
import DivisionByZeroAnalyzer from "./DivisionByZeroAnalyzer";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -11,9 +11,12 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { ParseTreeWalker } from "antlr4ng";
|
|
14
|
-
import { CNextListener } from "../
|
|
15
|
-
import * as Parser from "../
|
|
14
|
+
import { CNextListener } from "../antlr_parser/grammar/CNextListener";
|
|
15
|
+
import * as Parser from "../antlr_parser/grammar/CNextParser";
|
|
16
16
|
import IDivisionByZeroError from "./types/IDivisionByZeroError";
|
|
17
|
+
import LiteralUtils from "../utils/LiteralUtils";
|
|
18
|
+
import ExpressionUtils from "../utils/ExpressionUtils";
|
|
19
|
+
import ParserUtils from "../utils/ParserUtils";
|
|
17
20
|
|
|
18
21
|
/**
|
|
19
22
|
* First pass: Collect const declarations that are zero
|
|
@@ -49,147 +52,11 @@ class ConstZeroCollector extends CNextListener {
|
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
// Check if the expression is a literal zero
|
|
52
|
-
|
|
55
|
+
const literal = ExpressionUtils.extractLiteral(expr);
|
|
56
|
+
if (literal && LiteralUtils.isZero(literal)) {
|
|
53
57
|
this.constZeros.add(name);
|
|
54
58
|
}
|
|
55
59
|
};
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Check if an expression evaluates to literal zero
|
|
59
|
-
* Navigate: expression -> ternaryExpression -> orExpression -> ... -> literal
|
|
60
|
-
*/
|
|
61
|
-
private isExpressionZero(ctx: Parser.ExpressionContext): boolean {
|
|
62
|
-
// expression -> ternaryExpression
|
|
63
|
-
const ternary = ctx.ternaryExpression();
|
|
64
|
-
if (!ternary) return false;
|
|
65
|
-
|
|
66
|
-
// ternaryExpression -> orExpression
|
|
67
|
-
const orExprs = ternary.orExpression();
|
|
68
|
-
if (!orExprs || orExprs.length === 0) return false;
|
|
69
|
-
|
|
70
|
-
const orExpr = orExprs[0];
|
|
71
|
-
|
|
72
|
-
// orExpression -> andExpression
|
|
73
|
-
const andExprs = orExpr.andExpression();
|
|
74
|
-
if (!andExprs || andExprs.length === 0) return false;
|
|
75
|
-
|
|
76
|
-
const andExpr = andExprs[0];
|
|
77
|
-
|
|
78
|
-
// andExpression -> equalityExpression
|
|
79
|
-
const eqExprs = andExpr.equalityExpression();
|
|
80
|
-
if (!eqExprs || eqExprs.length === 0) return false;
|
|
81
|
-
|
|
82
|
-
const eqExpr = eqExprs[0];
|
|
83
|
-
|
|
84
|
-
// equalityExpression -> relationalExpression
|
|
85
|
-
const relExprs = eqExpr.relationalExpression();
|
|
86
|
-
if (!relExprs || relExprs.length === 0) return false;
|
|
87
|
-
|
|
88
|
-
const relExpr = relExprs[0];
|
|
89
|
-
|
|
90
|
-
// relationalExpression -> bitwiseOrExpression
|
|
91
|
-
const bitorExprs = relExpr.bitwiseOrExpression();
|
|
92
|
-
if (!bitorExprs || bitorExprs.length === 0) return false;
|
|
93
|
-
|
|
94
|
-
const bitorExpr = bitorExprs[0];
|
|
95
|
-
|
|
96
|
-
// bitwiseOrExpression -> bitwiseXorExpression
|
|
97
|
-
const bitxorExprs = bitorExpr.bitwiseXorExpression();
|
|
98
|
-
if (!bitxorExprs || bitxorExprs.length === 0) return false;
|
|
99
|
-
|
|
100
|
-
const bitxorExpr = bitxorExprs[0];
|
|
101
|
-
|
|
102
|
-
// bitwiseXorExpression -> bitwiseAndExpression
|
|
103
|
-
const bitandExprs = bitxorExpr.bitwiseAndExpression();
|
|
104
|
-
if (!bitandExprs || bitandExprs.length === 0) return false;
|
|
105
|
-
|
|
106
|
-
const bitandExpr = bitandExprs[0];
|
|
107
|
-
|
|
108
|
-
// bitwiseAndExpression -> shiftExpression
|
|
109
|
-
const shiftExprs = bitandExpr.shiftExpression();
|
|
110
|
-
if (!shiftExprs || shiftExprs.length === 0) return false;
|
|
111
|
-
|
|
112
|
-
const shiftExpr = shiftExprs[0];
|
|
113
|
-
|
|
114
|
-
// shiftExpression -> additiveExpression
|
|
115
|
-
const addExprs = shiftExpr.additiveExpression();
|
|
116
|
-
if (!addExprs || addExprs.length === 0) return false;
|
|
117
|
-
|
|
118
|
-
const addExpr = addExprs[0];
|
|
119
|
-
|
|
120
|
-
// additiveExpression -> multiplicativeExpression
|
|
121
|
-
const multExprs = addExpr.multiplicativeExpression();
|
|
122
|
-
if (!multExprs || multExprs.length === 0) return false;
|
|
123
|
-
|
|
124
|
-
const multExpr = multExprs[0];
|
|
125
|
-
|
|
126
|
-
// multiplicativeExpression -> unaryExpression
|
|
127
|
-
const unaryExprs = multExpr.unaryExpression();
|
|
128
|
-
if (!unaryExprs || unaryExprs.length === 0) return false;
|
|
129
|
-
|
|
130
|
-
const unary = unaryExprs[0];
|
|
131
|
-
|
|
132
|
-
// unaryExpression -> postfixExpression
|
|
133
|
-
const postfix = unary.postfixExpression();
|
|
134
|
-
if (!postfix) return false;
|
|
135
|
-
|
|
136
|
-
// postfixExpression -> primaryExpression
|
|
137
|
-
const primary = postfix.primaryExpression();
|
|
138
|
-
if (!primary) return false;
|
|
139
|
-
|
|
140
|
-
// primaryExpression -> literal
|
|
141
|
-
const literal = primary.literal();
|
|
142
|
-
if (!literal) return false;
|
|
143
|
-
|
|
144
|
-
return this.isLiteralZero(literal);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Check if a literal is zero
|
|
149
|
-
*/
|
|
150
|
-
private isLiteralZero(ctx: Parser.LiteralContext): boolean {
|
|
151
|
-
const text = ctx.getText();
|
|
152
|
-
|
|
153
|
-
// Check integer literals
|
|
154
|
-
if (ctx.INTEGER_LITERAL()) {
|
|
155
|
-
return text === "0";
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Check hex literals
|
|
159
|
-
if (ctx.HEX_LITERAL()) {
|
|
160
|
-
return text === "0x0" || text === "0X0";
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Check binary literals
|
|
164
|
-
if (ctx.BINARY_LITERAL()) {
|
|
165
|
-
return text === "0b0" || text === "0B0";
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Check suffixed literals
|
|
169
|
-
if (ctx.SUFFIXED_DECIMAL()) {
|
|
170
|
-
return text.startsWith("0u") || text.startsWith("0i");
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
if (ctx.SUFFIXED_HEX()) {
|
|
174
|
-
return (
|
|
175
|
-
text.startsWith("0x0u") ||
|
|
176
|
-
text.startsWith("0x0i") ||
|
|
177
|
-
text.startsWith("0X0u") ||
|
|
178
|
-
text.startsWith("0X0i")
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (ctx.SUFFIXED_BINARY()) {
|
|
183
|
-
return (
|
|
184
|
-
text.startsWith("0b0u") ||
|
|
185
|
-
text.startsWith("0b0i") ||
|
|
186
|
-
text.startsWith("0B0u") ||
|
|
187
|
-
text.startsWith("0B0i")
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return false;
|
|
192
|
-
}
|
|
193
60
|
}
|
|
194
61
|
|
|
195
62
|
/**
|
|
@@ -230,8 +97,7 @@ class DivisionByZeroListener extends CNextListener {
|
|
|
230
97
|
}
|
|
231
98
|
|
|
232
99
|
const rightOperand = operands[i + 1];
|
|
233
|
-
const line = rightOperand
|
|
234
|
-
const column = rightOperand.start?.column ?? 0;
|
|
100
|
+
const { line, column } = ParserUtils.getPosition(rightOperand);
|
|
235
101
|
|
|
236
102
|
// Check if right operand is zero
|
|
237
103
|
if (this.isZero(rightOperand)) {
|
|
@@ -259,7 +125,7 @@ class DivisionByZeroListener extends CNextListener {
|
|
|
259
125
|
// Check if it's a literal
|
|
260
126
|
const literal = primaryExpr.literal();
|
|
261
127
|
if (literal) {
|
|
262
|
-
return
|
|
128
|
+
return LiteralUtils.isZero(literal);
|
|
263
129
|
}
|
|
264
130
|
|
|
265
131
|
// Check if it's a const identifier that evaluates to zero
|
|
@@ -271,53 +137,6 @@ class DivisionByZeroListener extends CNextListener {
|
|
|
271
137
|
|
|
272
138
|
return false;
|
|
273
139
|
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Check if a literal is zero
|
|
277
|
-
*/
|
|
278
|
-
private isLiteralZero(ctx: Parser.LiteralContext): boolean {
|
|
279
|
-
const text = ctx.getText();
|
|
280
|
-
|
|
281
|
-
// Check integer literals
|
|
282
|
-
if (ctx.INTEGER_LITERAL()) {
|
|
283
|
-
return text === "0";
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Check hex literals
|
|
287
|
-
if (ctx.HEX_LITERAL()) {
|
|
288
|
-
return text === "0x0" || text === "0X0";
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Check binary literals
|
|
292
|
-
if (ctx.BINARY_LITERAL()) {
|
|
293
|
-
return text === "0b0" || text === "0B0";
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// Check suffixed literals
|
|
297
|
-
if (ctx.SUFFIXED_DECIMAL()) {
|
|
298
|
-
return text.startsWith("0u") || text.startsWith("0i");
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
if (ctx.SUFFIXED_HEX()) {
|
|
302
|
-
return (
|
|
303
|
-
text.startsWith("0x0u") ||
|
|
304
|
-
text.startsWith("0x0i") ||
|
|
305
|
-
text.startsWith("0X0u") ||
|
|
306
|
-
text.startsWith("0X0i")
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (ctx.SUFFIXED_BINARY()) {
|
|
311
|
-
return (
|
|
312
|
-
text.startsWith("0b0u") ||
|
|
313
|
-
text.startsWith("0b0i") ||
|
|
314
|
-
text.startsWith("0B0u") ||
|
|
315
|
-
text.startsWith("0B0i")
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
return false;
|
|
320
|
-
}
|
|
321
140
|
}
|
|
322
141
|
|
|
323
142
|
/**
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { describe, it, expect } from "vitest";
|
|
6
6
|
import { CharStream, CommonTokenStream } from "antlr4ng";
|
|
7
|
-
import { CNextLexer } from "../
|
|
8
|
-
import { CNextParser } from "../
|
|
7
|
+
import { CNextLexer } from "../antlr_parser/grammar/CNextLexer";
|
|
8
|
+
import { CNextParser } from "../antlr_parser/grammar/CNextParser";
|
|
9
9
|
import FloatModuloAnalyzer from "./FloatModuloAnalyzer";
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { ParseTreeWalker } from "antlr4ng";
|
|
14
|
-
import { CNextListener } from "../
|
|
15
|
-
import * as Parser from "../
|
|
14
|
+
import { CNextListener } from "../antlr_parser/grammar/CNextListener";
|
|
15
|
+
import * as Parser from "../antlr_parser/grammar/CNextParser";
|
|
16
16
|
import IFloatModuloError from "./types/IFloatModuloError";
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
import LiteralUtils from "../utils/LiteralUtils";
|
|
18
|
+
import ParserUtils from "../utils/ParserUtils";
|
|
19
|
+
import TypeConstants from "../constants/TypeConstants";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* First pass: Collect variable declarations with float types
|
|
@@ -37,7 +38,7 @@ class FloatVariableCollector extends CNextListener {
|
|
|
37
38
|
if (!typeCtx) return;
|
|
38
39
|
|
|
39
40
|
const typeName = typeCtx.getText();
|
|
40
|
-
if (!FLOAT_TYPES.includes(typeName)) return;
|
|
41
|
+
if (!TypeConstants.FLOAT_TYPES.includes(typeName)) return;
|
|
41
42
|
|
|
42
43
|
const identifier = ctx.IDENTIFIER();
|
|
43
44
|
if (!identifier) return;
|
|
@@ -53,7 +54,7 @@ class FloatVariableCollector extends CNextListener {
|
|
|
53
54
|
if (!typeCtx) return;
|
|
54
55
|
|
|
55
56
|
const typeName = typeCtx.getText();
|
|
56
|
-
if (!FLOAT_TYPES.includes(typeName)) return;
|
|
57
|
+
if (!TypeConstants.FLOAT_TYPES.includes(typeName)) return;
|
|
57
58
|
|
|
58
59
|
const identifier = ctx.IDENTIFIER();
|
|
59
60
|
if (!identifier) return;
|
|
@@ -102,8 +103,7 @@ class FloatModuloListener extends CNextListener {
|
|
|
102
103
|
const rightIsFloat = this.isFloatOperand(rightOperand);
|
|
103
104
|
|
|
104
105
|
if (leftIsFloat || rightIsFloat) {
|
|
105
|
-
const line = leftOperand
|
|
106
|
-
const column = leftOperand.start?.column ?? 0;
|
|
106
|
+
const { line, column } = ParserUtils.getPosition(leftOperand);
|
|
107
107
|
this.analyzer.addError(line, column);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
@@ -122,7 +122,7 @@ class FloatModuloListener extends CNextListener {
|
|
|
122
122
|
// Check for float literal
|
|
123
123
|
const literal = primaryExpr.literal();
|
|
124
124
|
if (literal) {
|
|
125
|
-
return
|
|
125
|
+
return LiteralUtils.isFloat(literal);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
// Check for identifier that's a float variable
|
|
@@ -133,18 +133,6 @@ class FloatModuloListener extends CNextListener {
|
|
|
133
133
|
|
|
134
134
|
return false;
|
|
135
135
|
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Check if a literal is a floating-point number
|
|
139
|
-
*/
|
|
140
|
-
private isFloatLiteral(ctx: Parser.LiteralContext): boolean {
|
|
141
|
-
// Check for FLOAT_LITERAL token
|
|
142
|
-
if (ctx.FLOAT_LITERAL()) return true;
|
|
143
|
-
|
|
144
|
-
// Check text for decimal point (fallback)
|
|
145
|
-
const text = ctx.getText();
|
|
146
|
-
return text.includes(".") && !text.startsWith('"');
|
|
147
|
-
}
|
|
148
136
|
}
|
|
149
137
|
|
|
150
138
|
/**
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { describe, it, expect } from "vitest";
|
|
6
6
|
import { CharStream, CommonTokenStream } from "antlr4ng";
|
|
7
|
-
import { CNextLexer } from "../
|
|
8
|
-
import { CNextParser } from "../
|
|
7
|
+
import { CNextLexer } from "../antlr_parser/grammar/CNextLexer";
|
|
8
|
+
import { CNextParser } from "../antlr_parser/grammar/CNextParser";
|
|
9
9
|
import FunctionCallAnalyzer from "./FunctionCallAnalyzer";
|
|
10
|
-
import SymbolTable from "../
|
|
10
|
+
import SymbolTable from "../symbol_resolution/SymbolTable";
|
|
11
11
|
import ESourceLanguage from "../types/ESourceLanguage";
|
|
12
12
|
import ESymbolKind from "../types/ESymbolKind";
|
|
13
13
|
|
|
@@ -156,6 +156,108 @@ describe("FunctionCallAnalyzer", () => {
|
|
|
156
156
|
expect(errors).toHaveLength(1);
|
|
157
157
|
expect(errors[0].code).toBe("E0422");
|
|
158
158
|
});
|
|
159
|
+
|
|
160
|
+
it("should allow this.name() qualified calls within scope", () => {
|
|
161
|
+
const code = `
|
|
162
|
+
scope Test {
|
|
163
|
+
void helper() {
|
|
164
|
+
u32 x <- 1;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
public void callsHelper() {
|
|
168
|
+
this.helper();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
const tree = parse(code);
|
|
173
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
174
|
+
const errors = analyzer.analyze(tree);
|
|
175
|
+
|
|
176
|
+
expect(errors).toHaveLength(0);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("should detect undefined method via this.methodName()", () => {
|
|
180
|
+
const code = `
|
|
181
|
+
scope Test {
|
|
182
|
+
void helper() {
|
|
183
|
+
u32 x <- 1;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
public void callsUndefined() {
|
|
187
|
+
this.undefinedMethod();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
`;
|
|
191
|
+
const tree = parse(code);
|
|
192
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
193
|
+
const errors = analyzer.analyze(tree);
|
|
194
|
+
|
|
195
|
+
expect(errors).toHaveLength(1);
|
|
196
|
+
expect(errors[0].code).toBe("E0422");
|
|
197
|
+
expect(errors[0].message).toContain("called before definition");
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("should suggest this.name() for unqualified scope calls", () => {
|
|
201
|
+
const code = `
|
|
202
|
+
scope Test {
|
|
203
|
+
void helper() {
|
|
204
|
+
u32 x <- 1;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
public void callsHelper() {
|
|
208
|
+
helper();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
`;
|
|
212
|
+
const tree = parse(code);
|
|
213
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
214
|
+
const errors = analyzer.analyze(tree);
|
|
215
|
+
|
|
216
|
+
expect(errors).toHaveLength(1);
|
|
217
|
+
expect(errors[0].code).toBe("E0422");
|
|
218
|
+
expect(errors[0].functionName).toBe("helper");
|
|
219
|
+
expect(errors[0].message).toContain("scope");
|
|
220
|
+
expect(errors[0].message).toContain("this.helper()");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("should not suggest this. for truly undefined in scope", () => {
|
|
224
|
+
const code = `
|
|
225
|
+
scope Test {
|
|
226
|
+
public void callsUnknown() {
|
|
227
|
+
unknownFunc();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
const tree = parse(code);
|
|
232
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
233
|
+
const errors = analyzer.analyze(tree);
|
|
234
|
+
|
|
235
|
+
expect(errors).toHaveLength(1);
|
|
236
|
+
expect(errors[0].code).toBe("E0422");
|
|
237
|
+
expect(errors[0].message).toContain("called before definition");
|
|
238
|
+
expect(errors[0].message).not.toContain("this.");
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("should not suggest this. for calls outside scope", () => {
|
|
242
|
+
const code = `
|
|
243
|
+
scope Test {
|
|
244
|
+
public void helper() {
|
|
245
|
+
u32 x <- 1;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
void main() {
|
|
250
|
+
helper();
|
|
251
|
+
}
|
|
252
|
+
`;
|
|
253
|
+
const tree = parse(code);
|
|
254
|
+
const analyzer = new FunctionCallAnalyzer();
|
|
255
|
+
const errors = analyzer.analyze(tree);
|
|
256
|
+
|
|
257
|
+
expect(errors).toHaveLength(1);
|
|
258
|
+
expect(errors[0].message).toContain("called before definition");
|
|
259
|
+
expect(errors[0].message).not.toContain("this.");
|
|
260
|
+
});
|
|
159
261
|
});
|
|
160
262
|
|
|
161
263
|
// ========================================================================
|
|
@@ -8,12 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { ParseTreeWalker } from "antlr4ng";
|
|
11
|
-
import { CNextListener } from "../
|
|
12
|
-
import * as Parser from "../
|
|
13
|
-
import SymbolTable from "../
|
|
11
|
+
import { CNextListener } from "../antlr_parser/grammar/CNextListener";
|
|
12
|
+
import * as Parser from "../antlr_parser/grammar/CNextParser";
|
|
13
|
+
import SymbolTable from "../symbol_resolution/SymbolTable";
|
|
14
14
|
import ESourceLanguage from "../types/ESourceLanguage";
|
|
15
15
|
import ESymbolKind from "../types/ESymbolKind";
|
|
16
16
|
import IFunctionCallError from "./types/IFunctionCallError";
|
|
17
|
+
import ParserUtils from "../utils/ParserUtils";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* C-Next built-in functions
|
|
@@ -326,11 +327,17 @@ class FunctionCallListener extends CNextListener {
|
|
|
326
327
|
// 3. Method-style call: obj.method() - not a C-Next function
|
|
327
328
|
|
|
328
329
|
const primary = ctx.primaryExpression();
|
|
329
|
-
|
|
330
|
-
|
|
330
|
+
|
|
331
|
+
// Determine the base name: could be an identifier or 'this'
|
|
332
|
+
let baseName: string;
|
|
333
|
+
if (primary.IDENTIFIER()) {
|
|
334
|
+
baseName = primary.IDENTIFIER()!.getText();
|
|
335
|
+
} else if (primary.THIS()) {
|
|
336
|
+
baseName = "this";
|
|
337
|
+
} else {
|
|
338
|
+
return; // Not a simple identifier-based or this-based call
|
|
331
339
|
}
|
|
332
340
|
|
|
333
|
-
const baseName = primary.IDENTIFIER()!.getText();
|
|
334
341
|
let resolvedName = baseName;
|
|
335
342
|
let callOpIndex = -1;
|
|
336
343
|
|
|
@@ -338,12 +345,16 @@ class FunctionCallListener extends CNextListener {
|
|
|
338
345
|
for (let i = 0; i < ops.length; i++) {
|
|
339
346
|
const op = ops[i];
|
|
340
347
|
|
|
341
|
-
// Member access: check if it's Scope.member pattern
|
|
348
|
+
// Member access: check if it's Scope.member or this.member pattern
|
|
342
349
|
if (op.IDENTIFIER()) {
|
|
343
350
|
const memberName = op.IDENTIFIER()!.getText();
|
|
344
351
|
|
|
352
|
+
// Handle this.member -> CurrentScope_member (when inside a scope)
|
|
353
|
+
if (resolvedName === "this" && this.currentScope) {
|
|
354
|
+
resolvedName = `${this.currentScope}_${memberName}`;
|
|
355
|
+
}
|
|
345
356
|
// Check if base is a known scope
|
|
346
|
-
if (this.analyzer.isScope(resolvedName)) {
|
|
357
|
+
else if (this.analyzer.isScope(resolvedName)) {
|
|
347
358
|
// Scope.member -> Scope_member
|
|
348
359
|
resolvedName = `${resolvedName}_${memberName}`;
|
|
349
360
|
} else {
|
|
@@ -365,9 +376,13 @@ class FunctionCallListener extends CNextListener {
|
|
|
365
376
|
|
|
366
377
|
// If we found a call, check if the function is defined
|
|
367
378
|
if (callOpIndex >= 0) {
|
|
368
|
-
const line = ctx
|
|
369
|
-
|
|
370
|
-
|
|
379
|
+
const { line, column } = ParserUtils.getPosition(ctx);
|
|
380
|
+
this.analyzer.checkFunctionCall(
|
|
381
|
+
resolvedName,
|
|
382
|
+
line,
|
|
383
|
+
column,
|
|
384
|
+
this.currentScope,
|
|
385
|
+
);
|
|
371
386
|
}
|
|
372
387
|
};
|
|
373
388
|
}
|
|
@@ -449,7 +464,7 @@ class FunctionCallAnalyzer {
|
|
|
449
464
|
for (const include of tree.includeDirective()) {
|
|
450
465
|
// Extract header name from #include <header.h> or #include "header.h"
|
|
451
466
|
const text = include.getText();
|
|
452
|
-
const match =
|
|
467
|
+
const match = /#include\s*[<"]([^>"]+)[>"]/.exec(text);
|
|
453
468
|
if (match) {
|
|
454
469
|
this.includedHeaders.add(match[1]);
|
|
455
470
|
}
|
|
@@ -526,8 +541,17 @@ class FunctionCallAnalyzer {
|
|
|
526
541
|
|
|
527
542
|
/**
|
|
528
543
|
* Check if a function call is valid (function is defined or external)
|
|
544
|
+
* @param name The function name being called
|
|
545
|
+
* @param line Source line number
|
|
546
|
+
* @param column Source column number
|
|
547
|
+
* @param currentScope The current scope name (if inside a scope)
|
|
529
548
|
*/
|
|
530
|
-
public checkFunctionCall(
|
|
549
|
+
public checkFunctionCall(
|
|
550
|
+
name: string,
|
|
551
|
+
line: number,
|
|
552
|
+
column: number,
|
|
553
|
+
currentScope: string | null,
|
|
554
|
+
): void {
|
|
531
555
|
// Check for self-recursion (MISRA C:2012 Rule 17.2)
|
|
532
556
|
if (this.currentFunctionName && name === this.currentFunctionName) {
|
|
533
557
|
this.errors.push({
|
|
@@ -565,6 +589,22 @@ class FunctionCallAnalyzer {
|
|
|
565
589
|
return; // OK - invoking a function pointer variable
|
|
566
590
|
}
|
|
567
591
|
|
|
592
|
+
// Check if this is an unqualified call to a scope function
|
|
593
|
+
// e.g., calling helper() instead of this.helper() inside a scope
|
|
594
|
+
if (currentScope) {
|
|
595
|
+
const qualifiedName = `${currentScope}_${name}`;
|
|
596
|
+
if (this.definedFunctions.has(qualifiedName)) {
|
|
597
|
+
this.errors.push({
|
|
598
|
+
code: "E0422",
|
|
599
|
+
functionName: name,
|
|
600
|
+
line,
|
|
601
|
+
column,
|
|
602
|
+
message: `'${name}' is a scope function - use 'this.${name}()' to call it`,
|
|
603
|
+
});
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
568
608
|
// Not defined - report error
|
|
569
609
|
this.errors.push({
|
|
570
610
|
code: "E0422",
|