c-next 0.1.0
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 +726 -0
- package/bin/cnext.js +5 -0
- package/grammar/C.g4 +1112 -0
- package/grammar/CNext.g4 +817 -0
- package/grammar/CPP14Lexer.g4 +282 -0
- package/grammar/CPP14Parser.g4 +1072 -0
- package/package.json +85 -0
- package/src/analysis/DivisionByZeroAnalyzer.ts +378 -0
- package/src/analysis/FunctionCallAnalyzer.ts +526 -0
- package/src/analysis/InitializationAnalyzer.ts +725 -0
- package/src/analysis/NullCheckAnalyzer.ts +427 -0
- package/src/analysis/types/IDivisionByZeroError.ts +25 -0
- package/src/analysis/types/IFunctionCallError.ts +17 -0
- package/src/analysis/types/IInitializationError.ts +55 -0
- package/src/analysis/types/INullCheckError.ts +25 -0
- package/src/codegen/CodeGenerator.ts +7945 -0
- package/src/codegen/CommentExtractor.ts +240 -0
- package/src/codegen/CommentFormatter.ts +155 -0
- package/src/codegen/HeaderGenerator.ts +265 -0
- package/src/codegen/TypeResolver.ts +365 -0
- package/src/codegen/types/ECommentType.ts +10 -0
- package/src/codegen/types/IComment.ts +21 -0
- package/src/codegen/types/ICommentError.ts +15 -0
- package/src/codegen/types/TOverflowBehavior.ts +6 -0
- package/src/codegen/types/TParameterInfo.ts +13 -0
- package/src/codegen/types/TTypeConstants.ts +94 -0
- package/src/codegen/types/TTypeInfo.ts +22 -0
- package/src/index.ts +518 -0
- package/src/lib/IncludeDiscovery.ts +131 -0
- package/src/lib/InputExpansion.ts +121 -0
- package/src/lib/PlatformIODetector.ts +162 -0
- package/src/lib/transpiler.ts +439 -0
- package/src/lib/types/ITranspileResult.ts +80 -0
- package/src/parser/c/grammar/C.interp +338 -0
- package/src/parser/c/grammar/C.tokens +229 -0
- package/src/parser/c/grammar/CLexer.interp +415 -0
- package/src/parser/c/grammar/CLexer.tokens +229 -0
- package/src/parser/c/grammar/CLexer.ts +750 -0
- package/src/parser/c/grammar/CListener.ts +976 -0
- package/src/parser/c/grammar/CParser.ts +9663 -0
- package/src/parser/c/grammar/CVisitor.ts +626 -0
- package/src/parser/cpp/grammar/CPP14Lexer.interp +478 -0
- package/src/parser/cpp/grammar/CPP14Lexer.tokens +264 -0
- package/src/parser/cpp/grammar/CPP14Lexer.ts +848 -0
- package/src/parser/cpp/grammar/CPP14Parser.interp +492 -0
- package/src/parser/cpp/grammar/CPP14Parser.tokens +264 -0
- package/src/parser/cpp/grammar/CPP14Parser.ts +19961 -0
- package/src/parser/cpp/grammar/CPP14ParserListener.ts +2120 -0
- package/src/parser/cpp/grammar/CPP14ParserVisitor.ts +1354 -0
- package/src/parser/grammar/CNext.interp +340 -0
- package/src/parser/grammar/CNext.tokens +214 -0
- package/src/parser/grammar/CNextLexer.interp +374 -0
- package/src/parser/grammar/CNextLexer.tokens +214 -0
- package/src/parser/grammar/CNextLexer.ts +668 -0
- package/src/parser/grammar/CNextListener.ts +1020 -0
- package/src/parser/grammar/CNextParser.ts +9239 -0
- package/src/parser/grammar/CNextVisitor.ts +654 -0
- package/src/preprocessor/Preprocessor.ts +301 -0
- package/src/preprocessor/ToolchainDetector.ts +225 -0
- package/src/preprocessor/types/IPreprocessResult.ts +39 -0
- package/src/preprocessor/types/IToolchain.ts +27 -0
- package/src/project/FileDiscovery.ts +236 -0
- package/src/project/Project.ts +425 -0
- package/src/project/types/IProjectConfig.ts +64 -0
- package/src/symbols/CNextSymbolCollector.ts +326 -0
- package/src/symbols/CSymbolCollector.ts +457 -0
- package/src/symbols/CppSymbolCollector.ts +362 -0
- package/src/symbols/SymbolTable.ts +312 -0
- package/src/symbols/types/IConflict.ts +20 -0
- package/src/types/ESourceLanguage.ts +10 -0
- package/src/types/ESymbolKind.ts +20 -0
- package/src/types/ISymbol.ts +45 -0
|
@@ -0,0 +1,526 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function Call Analyzer
|
|
3
|
+
* Enforces define-before-use for functions (ADR-030)
|
|
4
|
+
*
|
|
5
|
+
* C-Next requires functions to be defined before they can be called.
|
|
6
|
+
* This catches errors at C-Next compile time rather than deferring to
|
|
7
|
+
* the C compiler or runtime.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ParseTreeWalker } from "antlr4ng";
|
|
11
|
+
import { CNextListener } from "../parser/grammar/CNextListener";
|
|
12
|
+
import * as Parser from "../parser/grammar/CNextParser";
|
|
13
|
+
import SymbolTable from "../symbols/SymbolTable";
|
|
14
|
+
import ESourceLanguage from "../types/ESourceLanguage";
|
|
15
|
+
import ESymbolKind from "../types/ESymbolKind";
|
|
16
|
+
import { IFunctionCallError } from "./types/IFunctionCallError";
|
|
17
|
+
|
|
18
|
+
export { IFunctionCallError };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* C-Next built-in functions
|
|
22
|
+
* These are compiler intrinsics that don't need to be defined by the user
|
|
23
|
+
*/
|
|
24
|
+
const CNEXT_BUILTINS: Set<string> = new Set([
|
|
25
|
+
"safe_div", // ADR-051: Safe division with default value
|
|
26
|
+
"safe_mod", // ADR-051: Safe modulo with default value
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Standard library functions from common C headers
|
|
31
|
+
* These are considered "external" and don't need to be defined in C-Next
|
|
32
|
+
*/
|
|
33
|
+
const STDLIB_FUNCTIONS: Map<string, Set<string>> = new Map([
|
|
34
|
+
[
|
|
35
|
+
"stdio.h",
|
|
36
|
+
new Set([
|
|
37
|
+
"printf",
|
|
38
|
+
"fprintf",
|
|
39
|
+
"sprintf",
|
|
40
|
+
"snprintf",
|
|
41
|
+
"scanf",
|
|
42
|
+
"fscanf",
|
|
43
|
+
"sscanf",
|
|
44
|
+
"fopen",
|
|
45
|
+
"fclose",
|
|
46
|
+
"fread",
|
|
47
|
+
"fwrite",
|
|
48
|
+
"fgets",
|
|
49
|
+
"fputs",
|
|
50
|
+
"fgetc",
|
|
51
|
+
"fputc",
|
|
52
|
+
"puts",
|
|
53
|
+
"putchar",
|
|
54
|
+
"getchar",
|
|
55
|
+
"gets",
|
|
56
|
+
"perror",
|
|
57
|
+
"fflush",
|
|
58
|
+
"fseek",
|
|
59
|
+
"ftell",
|
|
60
|
+
"rewind",
|
|
61
|
+
"feof",
|
|
62
|
+
"ferror",
|
|
63
|
+
"clearerr",
|
|
64
|
+
"remove",
|
|
65
|
+
"rename",
|
|
66
|
+
"tmpfile",
|
|
67
|
+
"tmpnam",
|
|
68
|
+
"setbuf",
|
|
69
|
+
"setvbuf",
|
|
70
|
+
]),
|
|
71
|
+
],
|
|
72
|
+
[
|
|
73
|
+
"stdlib.h",
|
|
74
|
+
new Set([
|
|
75
|
+
"malloc",
|
|
76
|
+
"calloc",
|
|
77
|
+
"realloc",
|
|
78
|
+
"free",
|
|
79
|
+
"atoi",
|
|
80
|
+
"atof",
|
|
81
|
+
"atol",
|
|
82
|
+
"atoll",
|
|
83
|
+
"strtol",
|
|
84
|
+
"strtoul",
|
|
85
|
+
"strtoll",
|
|
86
|
+
"strtoull",
|
|
87
|
+
"strtof",
|
|
88
|
+
"strtod",
|
|
89
|
+
"strtold",
|
|
90
|
+
"rand",
|
|
91
|
+
"srand",
|
|
92
|
+
"exit",
|
|
93
|
+
"abort",
|
|
94
|
+
"atexit",
|
|
95
|
+
"system",
|
|
96
|
+
"getenv",
|
|
97
|
+
"abs",
|
|
98
|
+
"labs",
|
|
99
|
+
"llabs",
|
|
100
|
+
"div",
|
|
101
|
+
"ldiv",
|
|
102
|
+
"lldiv",
|
|
103
|
+
"qsort",
|
|
104
|
+
"bsearch",
|
|
105
|
+
]),
|
|
106
|
+
],
|
|
107
|
+
[
|
|
108
|
+
"string.h",
|
|
109
|
+
new Set([
|
|
110
|
+
"strlen",
|
|
111
|
+
"strcpy",
|
|
112
|
+
"strncpy",
|
|
113
|
+
"strcat",
|
|
114
|
+
"strncat",
|
|
115
|
+
"strcmp",
|
|
116
|
+
"strncmp",
|
|
117
|
+
"strchr",
|
|
118
|
+
"strrchr",
|
|
119
|
+
"strstr",
|
|
120
|
+
"strtok",
|
|
121
|
+
"memcpy",
|
|
122
|
+
"memmove",
|
|
123
|
+
"memset",
|
|
124
|
+
"memcmp",
|
|
125
|
+
"memchr",
|
|
126
|
+
]),
|
|
127
|
+
],
|
|
128
|
+
[
|
|
129
|
+
"math.h",
|
|
130
|
+
new Set([
|
|
131
|
+
"sin",
|
|
132
|
+
"cos",
|
|
133
|
+
"tan",
|
|
134
|
+
"asin",
|
|
135
|
+
"acos",
|
|
136
|
+
"atan",
|
|
137
|
+
"atan2",
|
|
138
|
+
"sinh",
|
|
139
|
+
"cosh",
|
|
140
|
+
"tanh",
|
|
141
|
+
"exp",
|
|
142
|
+
"log",
|
|
143
|
+
"log10",
|
|
144
|
+
"log2",
|
|
145
|
+
"pow",
|
|
146
|
+
"sqrt",
|
|
147
|
+
"cbrt",
|
|
148
|
+
"ceil",
|
|
149
|
+
"floor",
|
|
150
|
+
"round",
|
|
151
|
+
"trunc",
|
|
152
|
+
"fabs",
|
|
153
|
+
"fmod",
|
|
154
|
+
"remainder",
|
|
155
|
+
"fmax",
|
|
156
|
+
"fmin",
|
|
157
|
+
"hypot",
|
|
158
|
+
"ldexp",
|
|
159
|
+
"frexp",
|
|
160
|
+
"modf",
|
|
161
|
+
]),
|
|
162
|
+
],
|
|
163
|
+
[
|
|
164
|
+
"ctype.h",
|
|
165
|
+
new Set([
|
|
166
|
+
"isalnum",
|
|
167
|
+
"isalpha",
|
|
168
|
+
"isdigit",
|
|
169
|
+
"isxdigit",
|
|
170
|
+
"islower",
|
|
171
|
+
"isupper",
|
|
172
|
+
"isspace",
|
|
173
|
+
"ispunct",
|
|
174
|
+
"isprint",
|
|
175
|
+
"isgraph",
|
|
176
|
+
"iscntrl",
|
|
177
|
+
"tolower",
|
|
178
|
+
"toupper",
|
|
179
|
+
]),
|
|
180
|
+
],
|
|
181
|
+
[
|
|
182
|
+
"time.h",
|
|
183
|
+
new Set([
|
|
184
|
+
"time",
|
|
185
|
+
"clock",
|
|
186
|
+
"difftime",
|
|
187
|
+
"mktime",
|
|
188
|
+
"strftime",
|
|
189
|
+
"localtime",
|
|
190
|
+
"gmtime",
|
|
191
|
+
"asctime",
|
|
192
|
+
"ctime",
|
|
193
|
+
]),
|
|
194
|
+
],
|
|
195
|
+
["assert.h", new Set(["assert"])],
|
|
196
|
+
// Arduino framework
|
|
197
|
+
[
|
|
198
|
+
"Arduino.h",
|
|
199
|
+
new Set([
|
|
200
|
+
"pinMode",
|
|
201
|
+
"digitalWrite",
|
|
202
|
+
"digitalRead",
|
|
203
|
+
"analogRead",
|
|
204
|
+
"analogWrite",
|
|
205
|
+
"delay",
|
|
206
|
+
"delayMicroseconds",
|
|
207
|
+
"millis",
|
|
208
|
+
"micros",
|
|
209
|
+
"attachInterrupt",
|
|
210
|
+
"detachInterrupt",
|
|
211
|
+
"noInterrupts",
|
|
212
|
+
"interrupts",
|
|
213
|
+
"Serial",
|
|
214
|
+
"Wire",
|
|
215
|
+
"SPI",
|
|
216
|
+
]),
|
|
217
|
+
],
|
|
218
|
+
]);
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Listener that walks the parse tree and checks function calls
|
|
222
|
+
*/
|
|
223
|
+
class FunctionCallListener extends CNextListener {
|
|
224
|
+
private analyzer: FunctionCallAnalyzer;
|
|
225
|
+
|
|
226
|
+
/** Current scope name (for member function resolution) */
|
|
227
|
+
private currentScope: string | null = null;
|
|
228
|
+
|
|
229
|
+
constructor(analyzer: FunctionCallAnalyzer) {
|
|
230
|
+
super();
|
|
231
|
+
this.analyzer = analyzer;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// ========================================================================
|
|
235
|
+
// Function Definitions
|
|
236
|
+
// ========================================================================
|
|
237
|
+
|
|
238
|
+
override enterFunctionDeclaration = (
|
|
239
|
+
ctx: Parser.FunctionDeclarationContext,
|
|
240
|
+
): void => {
|
|
241
|
+
const name = ctx.IDENTIFIER().getText();
|
|
242
|
+
|
|
243
|
+
// If inside a scope, the full name is Scope_functionName
|
|
244
|
+
let fullName: string;
|
|
245
|
+
if (this.currentScope) {
|
|
246
|
+
fullName = `${this.currentScope}_${name}`;
|
|
247
|
+
} else {
|
|
248
|
+
fullName = name;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Track that we're currently inside this function (for self-recursion detection)
|
|
252
|
+
this.analyzer.enterFunction(fullName);
|
|
253
|
+
this.analyzer.defineFunction(fullName);
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
override exitFunctionDeclaration = (
|
|
257
|
+
_ctx: Parser.FunctionDeclarationContext,
|
|
258
|
+
): void => {
|
|
259
|
+
// We're leaving the function definition
|
|
260
|
+
this.analyzer.exitFunction();
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// ========================================================================
|
|
264
|
+
// Scope Handling
|
|
265
|
+
// ========================================================================
|
|
266
|
+
|
|
267
|
+
override enterScopeDeclaration = (
|
|
268
|
+
ctx: Parser.ScopeDeclarationContext,
|
|
269
|
+
): void => {
|
|
270
|
+
this.currentScope = ctx.IDENTIFIER().getText();
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
override exitScopeDeclaration = (
|
|
274
|
+
_ctx: Parser.ScopeDeclarationContext,
|
|
275
|
+
): void => {
|
|
276
|
+
this.currentScope = null;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// ========================================================================
|
|
280
|
+
// Function Calls
|
|
281
|
+
// ========================================================================
|
|
282
|
+
|
|
283
|
+
override enterPostfixExpression = (
|
|
284
|
+
ctx: Parser.PostfixExpressionContext,
|
|
285
|
+
): void => {
|
|
286
|
+
const ops = ctx.postfixOp();
|
|
287
|
+
|
|
288
|
+
// Find function call pattern: identifier followed by () or (args)
|
|
289
|
+
// This could be:
|
|
290
|
+
// 1. Simple call: foo()
|
|
291
|
+
// 2. Scope member call: Scope.member() -> Scope_member
|
|
292
|
+
// 3. Method-style call: obj.method() - not a C-Next function
|
|
293
|
+
|
|
294
|
+
const primary = ctx.primaryExpression();
|
|
295
|
+
if (!primary.IDENTIFIER()) {
|
|
296
|
+
return; // Not a simple identifier-based call
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const baseName = primary.IDENTIFIER()!.getText();
|
|
300
|
+
let resolvedName = baseName;
|
|
301
|
+
let callOpIndex = -1;
|
|
302
|
+
|
|
303
|
+
// Walk through postfix ops to find the call and resolve the name
|
|
304
|
+
for (let i = 0; i < ops.length; i++) {
|
|
305
|
+
const op = ops[i];
|
|
306
|
+
|
|
307
|
+
// Member access: check if it's Scope.member pattern
|
|
308
|
+
if (op.IDENTIFIER()) {
|
|
309
|
+
const memberName = op.IDENTIFIER()!.getText();
|
|
310
|
+
|
|
311
|
+
// Check if base is a known scope
|
|
312
|
+
if (this.analyzer.isScope(resolvedName)) {
|
|
313
|
+
// Scope.member -> Scope_member
|
|
314
|
+
resolvedName = `${resolvedName}_${memberName}`;
|
|
315
|
+
} else {
|
|
316
|
+
// Object.method or chained access - not a C-Next function call
|
|
317
|
+
// The method belongs to the object, not a standalone function
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// Function call: () or (args)
|
|
322
|
+
else if (op.argumentList() || op.getChildCount() === 2) {
|
|
323
|
+
// Check if this looks like a function call (has parens)
|
|
324
|
+
const text = op.getText();
|
|
325
|
+
if (text.startsWith("(")) {
|
|
326
|
+
callOpIndex = i;
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// If we found a call, check if the function is defined
|
|
333
|
+
if (callOpIndex >= 0) {
|
|
334
|
+
const line = ctx.start?.line ?? 0;
|
|
335
|
+
const column = ctx.start?.column ?? 0;
|
|
336
|
+
this.analyzer.checkFunctionCall(resolvedName, line, column);
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Analyzes C-Next AST for function calls before definition
|
|
343
|
+
*/
|
|
344
|
+
export class FunctionCallAnalyzer {
|
|
345
|
+
private errors: IFunctionCallError[] = [];
|
|
346
|
+
|
|
347
|
+
/** Functions that have been defined (in order of appearance) */
|
|
348
|
+
private definedFunctions: Set<string> = new Set();
|
|
349
|
+
|
|
350
|
+
/** Known scopes (for Scope.member -> Scope_member resolution) */
|
|
351
|
+
private knownScopes: Set<string> = new Set();
|
|
352
|
+
|
|
353
|
+
/** External symbol table for C/C++ interop */
|
|
354
|
+
private symbolTable: SymbolTable | null = null;
|
|
355
|
+
|
|
356
|
+
/** Included headers (for stdlib function lookup) */
|
|
357
|
+
private includedHeaders: Set<string> = new Set();
|
|
358
|
+
|
|
359
|
+
/** Current function being defined (for self-recursion detection) */
|
|
360
|
+
private currentFunctionName: string | null = null;
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Analyze a parsed program for function call errors
|
|
364
|
+
* @param tree The parsed program AST
|
|
365
|
+
* @param symbolTable Optional symbol table for external function lookup
|
|
366
|
+
* @returns Array of function call errors
|
|
367
|
+
*/
|
|
368
|
+
public analyze(
|
|
369
|
+
tree: Parser.ProgramContext,
|
|
370
|
+
symbolTable?: SymbolTable,
|
|
371
|
+
): IFunctionCallError[] {
|
|
372
|
+
this.errors = [];
|
|
373
|
+
this.definedFunctions = new Set();
|
|
374
|
+
this.knownScopes = new Set();
|
|
375
|
+
this.includedHeaders = new Set();
|
|
376
|
+
this.symbolTable = symbolTable ?? null;
|
|
377
|
+
this.currentFunctionName = null;
|
|
378
|
+
|
|
379
|
+
// First pass: collect scope names and included headers
|
|
380
|
+
this.collectScopes(tree);
|
|
381
|
+
this.collectIncludes(tree);
|
|
382
|
+
|
|
383
|
+
// Second pass: walk tree in order, tracking definitions and checking calls
|
|
384
|
+
const listener = new FunctionCallListener(this);
|
|
385
|
+
ParseTreeWalker.DEFAULT.walk(listener, tree);
|
|
386
|
+
|
|
387
|
+
return this.errors;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Collect scope names for member function resolution
|
|
392
|
+
*/
|
|
393
|
+
private collectScopes(tree: Parser.ProgramContext): void {
|
|
394
|
+
for (const decl of tree.declaration()) {
|
|
395
|
+
if (decl.scopeDeclaration()) {
|
|
396
|
+
const name = decl.scopeDeclaration()!.IDENTIFIER().getText();
|
|
397
|
+
this.knownScopes.add(name);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Collect included headers for stdlib function lookup
|
|
404
|
+
*/
|
|
405
|
+
private collectIncludes(tree: Parser.ProgramContext): void {
|
|
406
|
+
for (const include of tree.includeDirective()) {
|
|
407
|
+
// Extract header name from #include <header.h> or #include "header.h"
|
|
408
|
+
const text = include.getText();
|
|
409
|
+
const match = text.match(/#include\s*[<"]([^>"]+)[>"]/);
|
|
410
|
+
if (match) {
|
|
411
|
+
this.includedHeaders.add(match[1]);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Check if a function is from an included standard library header
|
|
418
|
+
*/
|
|
419
|
+
private isStdlibFunction(name: string): boolean {
|
|
420
|
+
for (const header of this.includedHeaders) {
|
|
421
|
+
const funcs = STDLIB_FUNCTIONS.get(header);
|
|
422
|
+
if (funcs && funcs.has(name)) {
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return false;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Register a function as defined
|
|
431
|
+
*/
|
|
432
|
+
public defineFunction(name: string): void {
|
|
433
|
+
this.definedFunctions.add(name);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Track entering a function definition (for self-recursion detection)
|
|
438
|
+
*/
|
|
439
|
+
public enterFunction(name: string): void {
|
|
440
|
+
this.currentFunctionName = name;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Track exiting a function definition
|
|
445
|
+
*/
|
|
446
|
+
public exitFunction(): void {
|
|
447
|
+
this.currentFunctionName = null;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Check if a function name is a known scope
|
|
452
|
+
*/
|
|
453
|
+
public isScope(name: string): boolean {
|
|
454
|
+
return this.knownScopes.has(name);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Check if a function call is valid (function is defined or external)
|
|
459
|
+
*/
|
|
460
|
+
public checkFunctionCall(name: string, line: number, column: number): void {
|
|
461
|
+
// Check for self-recursion (MISRA C:2012 Rule 17.2)
|
|
462
|
+
if (this.currentFunctionName && name === this.currentFunctionName) {
|
|
463
|
+
this.errors.push({
|
|
464
|
+
code: "E0423",
|
|
465
|
+
functionName: name,
|
|
466
|
+
line,
|
|
467
|
+
column,
|
|
468
|
+
message: `recursive call to '${name}' is forbidden (MISRA C:2012 Rule 17.2)`,
|
|
469
|
+
});
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Check if function is defined in C-Next
|
|
474
|
+
if (this.definedFunctions.has(name)) {
|
|
475
|
+
return; // OK - defined before use
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Check if function is a C-Next built-in
|
|
479
|
+
if (CNEXT_BUILTINS.has(name)) {
|
|
480
|
+
return; // OK - built-in function
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Check if function is from an included standard library header
|
|
484
|
+
if (this.isStdlibFunction(name)) {
|
|
485
|
+
return; // OK - standard library function
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Check if function is external (from symbol table)
|
|
489
|
+
if (this.isExternalFunction(name)) {
|
|
490
|
+
return; // OK - external C/C++ function
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Not defined - report error
|
|
494
|
+
this.errors.push({
|
|
495
|
+
code: "E0422",
|
|
496
|
+
functionName: name,
|
|
497
|
+
line,
|
|
498
|
+
column,
|
|
499
|
+
message: `function '${name}' called before definition`,
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/**
|
|
504
|
+
* Check if a function is defined externally (C/C++ interop)
|
|
505
|
+
*/
|
|
506
|
+
private isExternalFunction(name: string): boolean {
|
|
507
|
+
if (!this.symbolTable) {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const symbols = this.symbolTable.getOverloads(name);
|
|
512
|
+
for (const sym of symbols) {
|
|
513
|
+
if (
|
|
514
|
+
(sym.sourceLanguage === ESourceLanguage.C ||
|
|
515
|
+
sym.sourceLanguage === ESourceLanguage.Cpp) &&
|
|
516
|
+
sym.kind === ESymbolKind.Function
|
|
517
|
+
) {
|
|
518
|
+
return true;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return false;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export default FunctionCallAnalyzer;
|