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.
Files changed (72) hide show
  1. package/README.md +726 -0
  2. package/bin/cnext.js +5 -0
  3. package/grammar/C.g4 +1112 -0
  4. package/grammar/CNext.g4 +817 -0
  5. package/grammar/CPP14Lexer.g4 +282 -0
  6. package/grammar/CPP14Parser.g4 +1072 -0
  7. package/package.json +85 -0
  8. package/src/analysis/DivisionByZeroAnalyzer.ts +378 -0
  9. package/src/analysis/FunctionCallAnalyzer.ts +526 -0
  10. package/src/analysis/InitializationAnalyzer.ts +725 -0
  11. package/src/analysis/NullCheckAnalyzer.ts +427 -0
  12. package/src/analysis/types/IDivisionByZeroError.ts +25 -0
  13. package/src/analysis/types/IFunctionCallError.ts +17 -0
  14. package/src/analysis/types/IInitializationError.ts +55 -0
  15. package/src/analysis/types/INullCheckError.ts +25 -0
  16. package/src/codegen/CodeGenerator.ts +7945 -0
  17. package/src/codegen/CommentExtractor.ts +240 -0
  18. package/src/codegen/CommentFormatter.ts +155 -0
  19. package/src/codegen/HeaderGenerator.ts +265 -0
  20. package/src/codegen/TypeResolver.ts +365 -0
  21. package/src/codegen/types/ECommentType.ts +10 -0
  22. package/src/codegen/types/IComment.ts +21 -0
  23. package/src/codegen/types/ICommentError.ts +15 -0
  24. package/src/codegen/types/TOverflowBehavior.ts +6 -0
  25. package/src/codegen/types/TParameterInfo.ts +13 -0
  26. package/src/codegen/types/TTypeConstants.ts +94 -0
  27. package/src/codegen/types/TTypeInfo.ts +22 -0
  28. package/src/index.ts +518 -0
  29. package/src/lib/IncludeDiscovery.ts +131 -0
  30. package/src/lib/InputExpansion.ts +121 -0
  31. package/src/lib/PlatformIODetector.ts +162 -0
  32. package/src/lib/transpiler.ts +439 -0
  33. package/src/lib/types/ITranspileResult.ts +80 -0
  34. package/src/parser/c/grammar/C.interp +338 -0
  35. package/src/parser/c/grammar/C.tokens +229 -0
  36. package/src/parser/c/grammar/CLexer.interp +415 -0
  37. package/src/parser/c/grammar/CLexer.tokens +229 -0
  38. package/src/parser/c/grammar/CLexer.ts +750 -0
  39. package/src/parser/c/grammar/CListener.ts +976 -0
  40. package/src/parser/c/grammar/CParser.ts +9663 -0
  41. package/src/parser/c/grammar/CVisitor.ts +626 -0
  42. package/src/parser/cpp/grammar/CPP14Lexer.interp +478 -0
  43. package/src/parser/cpp/grammar/CPP14Lexer.tokens +264 -0
  44. package/src/parser/cpp/grammar/CPP14Lexer.ts +848 -0
  45. package/src/parser/cpp/grammar/CPP14Parser.interp +492 -0
  46. package/src/parser/cpp/grammar/CPP14Parser.tokens +264 -0
  47. package/src/parser/cpp/grammar/CPP14Parser.ts +19961 -0
  48. package/src/parser/cpp/grammar/CPP14ParserListener.ts +2120 -0
  49. package/src/parser/cpp/grammar/CPP14ParserVisitor.ts +1354 -0
  50. package/src/parser/grammar/CNext.interp +340 -0
  51. package/src/parser/grammar/CNext.tokens +214 -0
  52. package/src/parser/grammar/CNextLexer.interp +374 -0
  53. package/src/parser/grammar/CNextLexer.tokens +214 -0
  54. package/src/parser/grammar/CNextLexer.ts +668 -0
  55. package/src/parser/grammar/CNextListener.ts +1020 -0
  56. package/src/parser/grammar/CNextParser.ts +9239 -0
  57. package/src/parser/grammar/CNextVisitor.ts +654 -0
  58. package/src/preprocessor/Preprocessor.ts +301 -0
  59. package/src/preprocessor/ToolchainDetector.ts +225 -0
  60. package/src/preprocessor/types/IPreprocessResult.ts +39 -0
  61. package/src/preprocessor/types/IToolchain.ts +27 -0
  62. package/src/project/FileDiscovery.ts +236 -0
  63. package/src/project/Project.ts +425 -0
  64. package/src/project/types/IProjectConfig.ts +64 -0
  65. package/src/symbols/CNextSymbolCollector.ts +326 -0
  66. package/src/symbols/CSymbolCollector.ts +457 -0
  67. package/src/symbols/CppSymbolCollector.ts +362 -0
  68. package/src/symbols/SymbolTable.ts +312 -0
  69. package/src/symbols/types/IConflict.ts +20 -0
  70. package/src/types/ESourceLanguage.ts +10 -0
  71. package/src/types/ESymbolKind.ts +20 -0
  72. 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;