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,240 @@
|
|
|
1
|
+
import { CommonTokenStream, Token } from "antlr4ng";
|
|
2
|
+
import { CNextLexer } from "../parser/grammar/CNextLexer";
|
|
3
|
+
import ECommentType from "./types/ECommentType";
|
|
4
|
+
import IComment from "./types/IComment";
|
|
5
|
+
import ICommentError from "./types/ICommentError";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Extracts and validates comments from the HIDDEN channel (ADR-043)
|
|
9
|
+
*
|
|
10
|
+
* - Extracts LINE_COMMENT, BLOCK_COMMENT, DOC_COMMENT tokens
|
|
11
|
+
* - Validates MISRA C:2012 Rule 3.1 (no nested comment markers)
|
|
12
|
+
* - Validates MISRA C:2012 Rule 3.2 (no line-splice in line comments)
|
|
13
|
+
*/
|
|
14
|
+
class CommentExtractor {
|
|
15
|
+
private tokenStream: CommonTokenStream;
|
|
16
|
+
|
|
17
|
+
private comments: IComment[] | null = null;
|
|
18
|
+
|
|
19
|
+
private errors: ICommentError[] = [];
|
|
20
|
+
|
|
21
|
+
constructor(tokenStream: CommonTokenStream) {
|
|
22
|
+
this.tokenStream = tokenStream;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Extract all comments from the token stream
|
|
27
|
+
*/
|
|
28
|
+
extractAll(): IComment[] {
|
|
29
|
+
if (this.comments !== null) {
|
|
30
|
+
return this.comments;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.tokenStream.fill();
|
|
34
|
+
this.comments = [];
|
|
35
|
+
|
|
36
|
+
// Get all tokens and filter for comments on HIDDEN channel
|
|
37
|
+
const size = this.tokenStream.size;
|
|
38
|
+
for (let i = 0; i < size; i++) {
|
|
39
|
+
const token = this.tokenStream.get(i);
|
|
40
|
+
if (token.channel === Token.HIDDEN_CHANNEL) {
|
|
41
|
+
const comment = this.tokenToComment(token);
|
|
42
|
+
if (comment) {
|
|
43
|
+
this.comments.push(comment);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return this.comments;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get comments that appear before a given token index
|
|
53
|
+
*/
|
|
54
|
+
getCommentsBefore(tokenIndex: number): IComment[] {
|
|
55
|
+
const hiddenTokens = this.tokenStream.getHiddenTokensToLeft(
|
|
56
|
+
tokenIndex,
|
|
57
|
+
Token.HIDDEN_CHANNEL,
|
|
58
|
+
);
|
|
59
|
+
if (!hiddenTokens) return [];
|
|
60
|
+
|
|
61
|
+
const comments: IComment[] = [];
|
|
62
|
+
for (const token of hiddenTokens) {
|
|
63
|
+
const comment = this.tokenToComment(token);
|
|
64
|
+
if (comment) {
|
|
65
|
+
comments.push(comment);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return comments;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get inline comments that appear after a given token index (same line)
|
|
73
|
+
*/
|
|
74
|
+
getCommentsAfter(tokenIndex: number): IComment[] {
|
|
75
|
+
const hiddenTokens = this.tokenStream.getHiddenTokensToRight(
|
|
76
|
+
tokenIndex,
|
|
77
|
+
Token.HIDDEN_CHANNEL,
|
|
78
|
+
);
|
|
79
|
+
if (!hiddenTokens) return [];
|
|
80
|
+
|
|
81
|
+
const comments: IComment[] = [];
|
|
82
|
+
const sourceToken = this.tokenStream.get(tokenIndex);
|
|
83
|
+
const sourceLine = sourceToken.line;
|
|
84
|
+
|
|
85
|
+
for (const token of hiddenTokens) {
|
|
86
|
+
// Only include comments on the same line (inline comments)
|
|
87
|
+
if (token.line !== sourceLine) break;
|
|
88
|
+
|
|
89
|
+
const comment = this.tokenToComment(token);
|
|
90
|
+
if (comment) {
|
|
91
|
+
comments.push(comment);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return comments;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Validate all comments against MISRA C:2012 Rules 3.1 and 3.2
|
|
99
|
+
*/
|
|
100
|
+
validate(): ICommentError[] {
|
|
101
|
+
this.errors = [];
|
|
102
|
+
const comments = this.extractAll();
|
|
103
|
+
|
|
104
|
+
for (const comment of comments) {
|
|
105
|
+
this.validateMisra31(comment);
|
|
106
|
+
this.validateMisra32(comment);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return this.errors;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Get validation errors
|
|
114
|
+
*/
|
|
115
|
+
getErrors(): ICommentError[] {
|
|
116
|
+
return this.errors;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Convert a token to an IComment, or null if not a comment token
|
|
121
|
+
*/
|
|
122
|
+
private tokenToComment(token: Token): IComment | null {
|
|
123
|
+
const text = token.text;
|
|
124
|
+
if (!text) return null;
|
|
125
|
+
|
|
126
|
+
let type: ECommentType;
|
|
127
|
+
let content: string;
|
|
128
|
+
|
|
129
|
+
switch (token.type) {
|
|
130
|
+
case CNextLexer.DOC_COMMENT:
|
|
131
|
+
type = ECommentType.Doc;
|
|
132
|
+
content = text.slice(3).trim(); // Remove ///
|
|
133
|
+
break;
|
|
134
|
+
case CNextLexer.LINE_COMMENT:
|
|
135
|
+
type = ECommentType.Line;
|
|
136
|
+
content = text.slice(2); // Remove //
|
|
137
|
+
break;
|
|
138
|
+
case CNextLexer.BLOCK_COMMENT:
|
|
139
|
+
type = ECommentType.Block;
|
|
140
|
+
content = text.slice(2, -2); // Remove /* and */
|
|
141
|
+
break;
|
|
142
|
+
default:
|
|
143
|
+
return null; // Not a comment token (e.g., whitespace)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
type,
|
|
148
|
+
raw: text,
|
|
149
|
+
content,
|
|
150
|
+
line: token.line,
|
|
151
|
+
column: token.column,
|
|
152
|
+
tokenIndex: token.tokenIndex,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* MISRA C:2012 Rule 3.1: No nested comment markers
|
|
158
|
+
* The character sequences /* and // shall not appear within a comment.
|
|
159
|
+
* Exception: :// (URI pattern) is allowed per Amendment 4
|
|
160
|
+
*/
|
|
161
|
+
private validateMisra31(comment: IComment): void {
|
|
162
|
+
const content = comment.content;
|
|
163
|
+
|
|
164
|
+
// Check for nested /* (not part of a URI)
|
|
165
|
+
const nestedBlockStart = content.indexOf("/*");
|
|
166
|
+
if (nestedBlockStart !== -1) {
|
|
167
|
+
this.errors.push({
|
|
168
|
+
rule: "3.1",
|
|
169
|
+
message:
|
|
170
|
+
"Nested comment marker '/*' found inside comment (MISRA C:2012 Rule 3.1)",
|
|
171
|
+
line: comment.line,
|
|
172
|
+
column:
|
|
173
|
+
comment.column +
|
|
174
|
+
this.getMarkerLength(comment.type) +
|
|
175
|
+
nestedBlockStart,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Check for nested // (not part of a URI like ://)
|
|
180
|
+
// Find all // occurrences and check if preceded by :
|
|
181
|
+
let searchStart = 0;
|
|
182
|
+
while (true) {
|
|
183
|
+
const slashSlash = content.indexOf("//", searchStart);
|
|
184
|
+
if (slashSlash === -1) break;
|
|
185
|
+
|
|
186
|
+
// Check if this is part of a URI (preceded by :)
|
|
187
|
+
const isUri = slashSlash > 0 && content[slashSlash - 1] === ":";
|
|
188
|
+
|
|
189
|
+
if (!isUri) {
|
|
190
|
+
this.errors.push({
|
|
191
|
+
rule: "3.1",
|
|
192
|
+
message:
|
|
193
|
+
"Nested comment marker '//' found inside comment (MISRA C:2012 Rule 3.1)",
|
|
194
|
+
line: comment.line,
|
|
195
|
+
column:
|
|
196
|
+
comment.column + this.getMarkerLength(comment.type) + slashSlash,
|
|
197
|
+
});
|
|
198
|
+
break; // Only report first occurrence
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
searchStart = slashSlash + 2;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* MISRA C:2012 Rule 3.2: No line-splice in line comments
|
|
207
|
+
* Line comments ending with \ cause undefined behavior
|
|
208
|
+
*/
|
|
209
|
+
private validateMisra32(comment: IComment): void {
|
|
210
|
+
// Only applies to line comments (// and ///)
|
|
211
|
+
if (comment.type === ECommentType.Block) return;
|
|
212
|
+
|
|
213
|
+
const content = comment.content;
|
|
214
|
+
if (content.endsWith("\\")) {
|
|
215
|
+
this.errors.push({
|
|
216
|
+
rule: "3.2",
|
|
217
|
+
message:
|
|
218
|
+
"Line comment ends with '\\' which causes line-splice (MISRA C:2012 Rule 3.2)",
|
|
219
|
+
line: comment.line,
|
|
220
|
+
column: comment.column,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Get the length of the comment marker for column calculation
|
|
227
|
+
*/
|
|
228
|
+
private getMarkerLength(type: ECommentType): number {
|
|
229
|
+
switch (type) {
|
|
230
|
+
case ECommentType.Doc:
|
|
231
|
+
return 3; // ///
|
|
232
|
+
case ECommentType.Line:
|
|
233
|
+
return 2; // //
|
|
234
|
+
case ECommentType.Block:
|
|
235
|
+
return 2; // /*
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export default CommentExtractor;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import ECommentType from "./types/ECommentType";
|
|
2
|
+
import IComment from "./types/IComment";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Formats comments for C output (ADR-043)
|
|
6
|
+
*
|
|
7
|
+
* - Line comments: Keep as // (C99+ mode)
|
|
8
|
+
* - Block comments: Keep as block comments
|
|
9
|
+
* - Doc comments: Convert /// to Doxygen format
|
|
10
|
+
*/
|
|
11
|
+
class CommentFormatter {
|
|
12
|
+
/**
|
|
13
|
+
* Format a single comment for C output
|
|
14
|
+
* @param comment The comment to format
|
|
15
|
+
* @param indent Indentation string (spaces)
|
|
16
|
+
* @returns Formatted comment string
|
|
17
|
+
*/
|
|
18
|
+
format(comment: IComment, indent: string = ""): string {
|
|
19
|
+
switch (comment.type) {
|
|
20
|
+
case ECommentType.Line:
|
|
21
|
+
return `${indent}//${comment.content}`;
|
|
22
|
+
|
|
23
|
+
case ECommentType.Block:
|
|
24
|
+
return this.formatBlockComment(comment, indent);
|
|
25
|
+
|
|
26
|
+
case ECommentType.Doc:
|
|
27
|
+
return this.formatDocComment(comment, indent);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Format consecutive doc comments as a single Doxygen block
|
|
33
|
+
* @param comments Array of consecutive doc comments
|
|
34
|
+
* @param indent Indentation string
|
|
35
|
+
* @returns Formatted Doxygen comment block
|
|
36
|
+
*/
|
|
37
|
+
formatDocCommentGroup(comments: IComment[], indent: string = ""): string {
|
|
38
|
+
if (comments.length === 0) return "";
|
|
39
|
+
|
|
40
|
+
if (comments.length === 1) {
|
|
41
|
+
return this.formatDocComment(comments[0], indent);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Multiple doc comments -> single /** ... */ block
|
|
45
|
+
const lines: string[] = [];
|
|
46
|
+
lines.push(`${indent}/**`);
|
|
47
|
+
|
|
48
|
+
for (const comment of comments) {
|
|
49
|
+
const content = comment.content.trim();
|
|
50
|
+
if (content) {
|
|
51
|
+
lines.push(`${indent} * ${content}`);
|
|
52
|
+
} else {
|
|
53
|
+
lines.push(`${indent} *`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
lines.push(`${indent} */`);
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Format leading comments (comments that appear before code)
|
|
63
|
+
* Groups consecutive doc comments into Doxygen blocks
|
|
64
|
+
* @param comments Array of comments
|
|
65
|
+
* @param indent Indentation string
|
|
66
|
+
* @returns Array of formatted comment strings
|
|
67
|
+
*/
|
|
68
|
+
formatLeadingComments(comments: IComment[], indent: string = ""): string[] {
|
|
69
|
+
if (comments.length === 0) return [];
|
|
70
|
+
|
|
71
|
+
const result: string[] = [];
|
|
72
|
+
let docGroup: IComment[] = [];
|
|
73
|
+
|
|
74
|
+
for (const comment of comments) {
|
|
75
|
+
if (comment.type === ECommentType.Doc) {
|
|
76
|
+
docGroup.push(comment);
|
|
77
|
+
} else {
|
|
78
|
+
// Flush any accumulated doc comments
|
|
79
|
+
if (docGroup.length > 0) {
|
|
80
|
+
result.push(this.formatDocCommentGroup(docGroup, indent));
|
|
81
|
+
docGroup = [];
|
|
82
|
+
}
|
|
83
|
+
result.push(this.format(comment, indent));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Flush remaining doc comments
|
|
88
|
+
if (docGroup.length > 0) {
|
|
89
|
+
result.push(this.formatDocCommentGroup(docGroup, indent));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Format a trailing/inline comment
|
|
97
|
+
* @param comment The comment
|
|
98
|
+
* @returns Formatted inline comment (with leading spaces)
|
|
99
|
+
*/
|
|
100
|
+
formatTrailingComment(comment: IComment): string {
|
|
101
|
+
switch (comment.type) {
|
|
102
|
+
case ECommentType.Line:
|
|
103
|
+
case ECommentType.Doc:
|
|
104
|
+
return ` //${comment.content}`;
|
|
105
|
+
|
|
106
|
+
case ECommentType.Block:
|
|
107
|
+
// For inline block comments, keep on single line if possible
|
|
108
|
+
if (!comment.content.includes("\n")) {
|
|
109
|
+
return ` /*${comment.content}*/`;
|
|
110
|
+
}
|
|
111
|
+
// Multi-line block comment shouldn't be inline
|
|
112
|
+
return ` /*${comment.content}*/`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Format a block comment preserving internal structure
|
|
118
|
+
*/
|
|
119
|
+
private formatBlockComment(comment: IComment, indent: string): string {
|
|
120
|
+
const content = comment.content;
|
|
121
|
+
|
|
122
|
+
// Single-line block comment
|
|
123
|
+
if (!content.includes("\n")) {
|
|
124
|
+
return `${indent}/*${content}*/`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Multi-line block comment - preserve structure
|
|
128
|
+
const lines = content.split("\n");
|
|
129
|
+
const result: string[] = [];
|
|
130
|
+
result.push(`${indent}/*${lines[0]}`);
|
|
131
|
+
|
|
132
|
+
for (let i = 1; i < lines.length - 1; i++) {
|
|
133
|
+
result.push(`${indent}${lines[i]}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (lines.length > 1) {
|
|
137
|
+
result.push(`${indent}${lines[lines.length - 1]}*/`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return result.join("\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Format a single doc comment as Doxygen
|
|
145
|
+
*/
|
|
146
|
+
private formatDocComment(comment: IComment, indent: string): string {
|
|
147
|
+
const content = comment.content.trim();
|
|
148
|
+
if (!content) {
|
|
149
|
+
return `${indent}/** */`;
|
|
150
|
+
}
|
|
151
|
+
return `${indent}/** ${content} */`;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export default CommentFormatter;
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header Generator
|
|
3
|
+
* Generates C header (.h) files from C-Next source
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import ISymbol from "../types/ISymbol";
|
|
7
|
+
import ESymbolKind from "../types/ESymbolKind";
|
|
8
|
+
import ESourceLanguage from "../types/ESourceLanguage";
|
|
9
|
+
import SymbolTable from "../symbols/SymbolTable";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Maps C-Next types to C types
|
|
13
|
+
*/
|
|
14
|
+
const TYPE_MAP: Record<string, string> = {
|
|
15
|
+
u8: "uint8_t",
|
|
16
|
+
u16: "uint16_t",
|
|
17
|
+
u32: "uint32_t",
|
|
18
|
+
u64: "uint64_t",
|
|
19
|
+
i8: "int8_t",
|
|
20
|
+
i16: "int16_t",
|
|
21
|
+
i32: "int32_t",
|
|
22
|
+
i64: "int64_t",
|
|
23
|
+
f32: "float",
|
|
24
|
+
f64: "double",
|
|
25
|
+
bool: "bool",
|
|
26
|
+
void: "void",
|
|
27
|
+
ISR: "ISR", // ADR-040: Interrupt Service Routine function pointer
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Options for header generation
|
|
32
|
+
*/
|
|
33
|
+
interface IHeaderOptions {
|
|
34
|
+
/** Guard prefix (default: derived from filename) */
|
|
35
|
+
guardPrefix?: string;
|
|
36
|
+
|
|
37
|
+
/** Include system headers in the output */
|
|
38
|
+
includeSystemHeaders?: boolean;
|
|
39
|
+
|
|
40
|
+
/** Only generate declarations for exported symbols */
|
|
41
|
+
exportedOnly?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generates C header files from symbol information
|
|
46
|
+
*/
|
|
47
|
+
class HeaderGenerator {
|
|
48
|
+
/**
|
|
49
|
+
* Generate a header file from symbols
|
|
50
|
+
*/
|
|
51
|
+
generate(
|
|
52
|
+
symbols: ISymbol[],
|
|
53
|
+
filename: string,
|
|
54
|
+
options: IHeaderOptions = {},
|
|
55
|
+
): string {
|
|
56
|
+
const lines: string[] = [];
|
|
57
|
+
const guard = this.makeGuard(filename, options.guardPrefix);
|
|
58
|
+
|
|
59
|
+
// Header guard
|
|
60
|
+
lines.push(`#ifndef ${guard}`);
|
|
61
|
+
lines.push(`#define ${guard}`);
|
|
62
|
+
lines.push("");
|
|
63
|
+
|
|
64
|
+
// Header comment
|
|
65
|
+
lines.push("/**");
|
|
66
|
+
lines.push(" * Generated by C-Next Transpiler");
|
|
67
|
+
lines.push(" * Header file for cross-language interoperability");
|
|
68
|
+
lines.push(" */");
|
|
69
|
+
lines.push("");
|
|
70
|
+
|
|
71
|
+
// System includes
|
|
72
|
+
if (options.includeSystemHeaders !== false) {
|
|
73
|
+
lines.push("#include <stdint.h>");
|
|
74
|
+
lines.push("#include <stdbool.h>");
|
|
75
|
+
lines.push("");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// C++ compatibility
|
|
79
|
+
lines.push("#ifdef __cplusplus");
|
|
80
|
+
lines.push('extern "C" {');
|
|
81
|
+
lines.push("#endif");
|
|
82
|
+
lines.push("");
|
|
83
|
+
|
|
84
|
+
// Filter symbols
|
|
85
|
+
const exportedSymbols = options.exportedOnly
|
|
86
|
+
? symbols.filter((s) => s.isExported)
|
|
87
|
+
: symbols;
|
|
88
|
+
|
|
89
|
+
// Group symbols by kind
|
|
90
|
+
const structs = exportedSymbols.filter(
|
|
91
|
+
(s) => s.kind === ESymbolKind.Struct,
|
|
92
|
+
);
|
|
93
|
+
const classes = exportedSymbols.filter((s) => s.kind === ESymbolKind.Class);
|
|
94
|
+
const functions = exportedSymbols.filter(
|
|
95
|
+
(s) => s.kind === ESymbolKind.Function,
|
|
96
|
+
);
|
|
97
|
+
const variables = exportedSymbols.filter(
|
|
98
|
+
(s) => s.kind === ESymbolKind.Variable,
|
|
99
|
+
);
|
|
100
|
+
const enums = exportedSymbols.filter((s) => s.kind === ESymbolKind.Enum);
|
|
101
|
+
const types = exportedSymbols.filter((s) => s.kind === ESymbolKind.Type);
|
|
102
|
+
|
|
103
|
+
// Forward declarations for structs
|
|
104
|
+
if (structs.length > 0 || classes.length > 0) {
|
|
105
|
+
lines.push("/* Forward declarations */");
|
|
106
|
+
for (const sym of structs) {
|
|
107
|
+
lines.push(`typedef struct ${sym.name} ${sym.name};`);
|
|
108
|
+
}
|
|
109
|
+
for (const sym of classes) {
|
|
110
|
+
// Classes become typedefs to structs
|
|
111
|
+
lines.push(`typedef struct ${sym.name} ${sym.name};`);
|
|
112
|
+
}
|
|
113
|
+
lines.push("");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Enum declarations
|
|
117
|
+
if (enums.length > 0) {
|
|
118
|
+
lines.push("/* Enumerations */");
|
|
119
|
+
for (const sym of enums) {
|
|
120
|
+
// For now, just forward declare - full definition would require enum values
|
|
121
|
+
lines.push(`/* Enum: ${sym.name} (see implementation for values) */`);
|
|
122
|
+
}
|
|
123
|
+
lines.push("");
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Type aliases
|
|
127
|
+
if (types.length > 0) {
|
|
128
|
+
lines.push("/* Type aliases */");
|
|
129
|
+
for (const sym of types) {
|
|
130
|
+
if (sym.type) {
|
|
131
|
+
const cType = this.mapType(sym.type);
|
|
132
|
+
lines.push(`typedef ${cType} ${sym.name};`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
lines.push("");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Extern variable declarations
|
|
139
|
+
if (variables.length > 0) {
|
|
140
|
+
lines.push("/* External variables */");
|
|
141
|
+
for (const sym of variables) {
|
|
142
|
+
const cType = sym.type ? this.mapType(sym.type) : "int";
|
|
143
|
+
lines.push(`extern ${cType} ${sym.name};`);
|
|
144
|
+
}
|
|
145
|
+
lines.push("");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Function prototypes
|
|
149
|
+
if (functions.length > 0) {
|
|
150
|
+
lines.push("/* Function prototypes */");
|
|
151
|
+
for (const sym of functions) {
|
|
152
|
+
const proto = this.generateFunctionPrototype(sym);
|
|
153
|
+
if (proto) {
|
|
154
|
+
lines.push(proto);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
lines.push("");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// C++ compatibility end
|
|
161
|
+
lines.push("#ifdef __cplusplus");
|
|
162
|
+
lines.push("}");
|
|
163
|
+
lines.push("#endif");
|
|
164
|
+
lines.push("");
|
|
165
|
+
|
|
166
|
+
// End header guard
|
|
167
|
+
lines.push(`#endif /* ${guard} */`);
|
|
168
|
+
lines.push("");
|
|
169
|
+
|
|
170
|
+
return lines.join("\n");
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Generate header from a symbol table, filtering by source file
|
|
175
|
+
*/
|
|
176
|
+
generateFromSymbolTable(
|
|
177
|
+
symbolTable: SymbolTable,
|
|
178
|
+
sourceFile: string,
|
|
179
|
+
options: IHeaderOptions = {},
|
|
180
|
+
): string {
|
|
181
|
+
const symbols = symbolTable.getSymbolsByFile(sourceFile);
|
|
182
|
+
const basename = sourceFile.replace(/\.[^.]+$/, "");
|
|
183
|
+
const headerName = `${basename}.h`;
|
|
184
|
+
|
|
185
|
+
return this.generate(symbols, headerName, options);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Generate header for all C-Next symbols in the symbol table
|
|
190
|
+
*/
|
|
191
|
+
generateCNextHeader(
|
|
192
|
+
symbolTable: SymbolTable,
|
|
193
|
+
filename: string,
|
|
194
|
+
options: IHeaderOptions = {},
|
|
195
|
+
): string {
|
|
196
|
+
const symbols = symbolTable.getSymbolsByLanguage(ESourceLanguage.CNext);
|
|
197
|
+
return this.generate(symbols, filename, options);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Create an include guard macro from filename
|
|
202
|
+
*/
|
|
203
|
+
private makeGuard(filename: string, prefix?: string): string {
|
|
204
|
+
// Remove path and extension
|
|
205
|
+
const base = filename.replace(/^.*[\\/]/, "").replace(/\.[^.]+$/, "");
|
|
206
|
+
|
|
207
|
+
// Convert to uppercase and replace non-alphanumeric with underscore
|
|
208
|
+
const sanitized = base.toUpperCase().replace(/[^A-Z0-9]/g, "_");
|
|
209
|
+
|
|
210
|
+
if (prefix) {
|
|
211
|
+
return `${prefix.toUpperCase()}_${sanitized}_H`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return `${sanitized}_H`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Map a C-Next type to C type
|
|
219
|
+
*/
|
|
220
|
+
private mapType(type: string): string {
|
|
221
|
+
// Check direct mapping first
|
|
222
|
+
if (TYPE_MAP[type]) {
|
|
223
|
+
return TYPE_MAP[type];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Handle pointer types
|
|
227
|
+
if (type.endsWith("*")) {
|
|
228
|
+
const baseType = type.slice(0, -1).trim();
|
|
229
|
+
return `${this.mapType(baseType)}*`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Handle array types (simplified)
|
|
233
|
+
const arrayMatch = type.match(/^(\w+)\[(\d*)\]$/);
|
|
234
|
+
if (arrayMatch) {
|
|
235
|
+
const baseType = this.mapType(arrayMatch[1]);
|
|
236
|
+
const size = arrayMatch[2] || "";
|
|
237
|
+
return `${baseType}[${size}]`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// User-defined types pass through
|
|
241
|
+
return type;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Generate a function prototype from symbol info
|
|
246
|
+
*/
|
|
247
|
+
private generateFunctionPrototype(sym: ISymbol): string | null {
|
|
248
|
+
// Use signature if available
|
|
249
|
+
if (sym.signature) {
|
|
250
|
+
// Parse signature to extract return type and params
|
|
251
|
+
// Signature format is typically: "return_type(param1_type, param2_type, ...)"
|
|
252
|
+
return `${sym.signature};`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Generate from type info
|
|
256
|
+
const returnType = sym.type ? this.mapType(sym.type) : "void";
|
|
257
|
+
|
|
258
|
+
// For C-Next functions, all non-array parameters become pointers
|
|
259
|
+
// This is a simplified version - full implementation would need param info
|
|
260
|
+
return `${returnType} ${sym.name}(void);`;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export default HeaderGenerator;
|
|
265
|
+
export type { IHeaderOptions };
|