Skip to content

Commit 9859f33

Browse files
LeBouftyflorianbgt
andauthored
Feature/c module (#140)
* Feature/c header resolver (#127) * Added C parser * Update package-lock.json * Added C components to export * Export parent classes of nested classes (#126) * Types * JSDoc comments * First shot at parsing * Why doesn't my autoformatter work anymore * Added .c test file management * Fixed property name * Fixed property name * First round of tests * Fixed disastrous spelling mistake * Feature/c symbol registry (#129) * Added macros to header resolution * Added safety for unnamed structs * Typedef handling * Handling of struct declaration inside a typedef * Refactored tests * Symbol registry * New test file * Added tests for registry * Feature/c include resolver (#130) * Include resolver * Added tests to include resolver * Tests for several internal includes * Feature/c invocation resolver (#131) * Removed (useless) specifier checking for symbols * Invocation resolver * Added definitionPath to functions * Added getInvocationsForSymbol * Added registration of undeclared functions such as main * Tests for invocation resolver * Feature/c manifest (#134) * Dependency formatter * Added tests to formatter * C Manifest generation * Added C function to handler * Changed logic for function name resolution * Added C to languages * Fixed macro content not being parsed * Improved include path logic * Prevented overwriting of structs in header resolution * Recursively looks through inclusions * Changed standard inclusion data type * Safer query for typedef double-definitions * Updated JSDoc for header query * Separated function definition and signature * Emergency include resolution * Update crashcases.h * added metrics * added metrics to manifest * back merge main in to branch (#135) * back merge main in to branch * Fixed deno-exclusive errors * Fixed lint --------- Co-authored-by: LeBoufty <boufto@gmail.com> * Feature/c extractor (#137) * Update .gitignore * Added test:watch task to cli * Update deno.lock * Added typedefs to datatypes' invocations * Added C file content map to test files * Added .napirc to C test files * C Extractor * C Extraction binding * Class diagram * Fixed formatting * Feature/c warnings (#138) * Forced datatypes to have names * New test file * Warning manager * Added warnings to manifest generation * Added enum member management * Added support for unnamed enums * Removed unnamed enums from diagnostics * Fix/c manifest and extraction (#139) * Fixed definesToKeep can have undefined * Gave signature/definition linking responsibility to includeResolver * Removed leftover logging * Added caching to include resolution * Changed internal inclusion structure * Changed structure for included symbols * Switched invocations to IncludedSymbol * Added first-level include recursion management * Removed includes for deleted files * Includedirs and compaction * Extraction refinement * Fixed bad handling of typedefs with same name * Improved recursive inclusion resolution * Added credits --------- Co-authored-by: Florian <florian.bigot321@gmail.com>
1 parent 3c93788 commit 9859f33

File tree

62 files changed

+4285
-625
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+4285
-625
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ examples/csharp/EndpointExample/bin
1313
auditResponse.json
1414
.extracted/
1515
napi-output/
16-
coverage/
16+
coverage/
17+
.vite/

deno.lock

Lines changed: 325 additions & 550 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/cli/src/cli/handlers/init/prompts.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import pythonStdlibList from "../../../scripts/generate_python_stdlib_list/outpu
77
import { confirm, input, number, search, select } from "@inquirer/prompts";
88
import { globSync } from "npm:glob";
99
import {
10+
cLanguage,
1011
csharpLanguage,
1112
pythonLanguage,
1213
} from "../../../helpers/treeSitter/parsers.ts";
@@ -146,7 +147,7 @@ async function collectIncludePatterns(
146147
Include patterns define which files NanoAPI will process and analyze.
147148
148149
Examples:
149-
- '**/*.py' for all Python files
150+
- '**/*.py' for all Python files
150151
- 'src/**' for all files in src directory
151152
- '*.py' for all Python files in the root directory
152153
`,
@@ -500,6 +501,7 @@ export async function generateConfig(
500501
choices: [
501502
{ name: "Python", value: pythonLanguage },
502503
{ name: "C#", value: csharpLanguage },
504+
{ name: "C", value: cLanguage },
503505
],
504506
});
505507

packages/cli/src/cli/helpers/checkVersion.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ export async function checkVersionMiddleware() {
3333
if (currentVersion !== latestVersion) {
3434
console.warn(
3535
`
36-
You are using version ${currentVersion}.
37-
The latest version is ${latestVersion}.
36+
You are using version ${currentVersion}.
37+
The latest version is ${latestVersion}.
3838
Please update to the latest version to continue using napi.
3939
4040
You can update the version by running the following command:

packages/cli/src/config/localConfig.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import pythonStdlibList from "../scripts/generate_python_stdlib_list/output.json
44
type: "json",
55
};
66
import {
7+
cLanguage,
78
csharpLanguage,
89
pythonLanguage,
910
} from "../helpers/treeSitter/parsers.ts";
1011

1112
const pythonVersions = Object.keys(pythonStdlibList);
1213

1314
export const localConfigSchema = z.object({
14-
language: z.enum([pythonLanguage, csharpLanguage]),
15+
language: z.enum([pythonLanguage, csharpLanguage, cLanguage]),
1516
[pythonLanguage]: z
1617
.object({
1718
version: z
@@ -24,6 +25,11 @@ export const localConfigSchema = z.object({
2425
.optional(),
2526
})
2627
.optional(), // python specific config
28+
[cLanguage]: z
29+
.object({
30+
includedirs: z.array(z.string()).optional(),
31+
})
32+
.optional(), // c specific config
2733
project: z.object({
2834
include: z.array(z.string()),
2935
exclude: z.array(z.string()).optional(),

packages/cli/src/helpers/fileSystem/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { globSync } from "npm:glob";
22
import { dirname, join } from "@std/path";
3-
import { csharpLanguage, pythonLanguage } from "../treeSitter/parsers.ts";
3+
import {
4+
cLanguage,
5+
csharpLanguage,
6+
pythonLanguage,
7+
} from "../treeSitter/parsers.ts";
48

59
export function getExtensionsForLanguage(language: string) {
610
const supportedLanguages: Record<string, string[]> = {
711
[pythonLanguage]: ["py"],
812
[csharpLanguage]: ["cs", "csproj"],
13+
[cLanguage]: ["c", "h"],
914
};
1015

1116
const supportedLanguage = supportedLanguages[language];

packages/cli/src/helpers/treeSitter/parsers.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Parser, { type Language } from "npm:tree-sitter";
22
import Python from "npm:tree-sitter-python";
33
import CSharp from "npm:tree-sitter-c-sharp";
4+
import C from "npm:tree-sitter-c";
45

56
const pythonParser = new Parser();
67
pythonParser.setLanguage(Python as Language);
@@ -10,4 +11,15 @@ const csharpParser = new Parser();
1011
csharpParser.setLanguage(CSharp as Language);
1112
const csharpLanguage = CSharp.name as "c-sharp";
1213

13-
export { csharpLanguage, csharpParser, pythonLanguage, pythonParser };
14+
const cParser = new Parser();
15+
cParser.setLanguage(C as Language);
16+
const cLanguage = C.name as "c";
17+
18+
export {
19+
cLanguage,
20+
cParser,
21+
csharpLanguage,
22+
csharpParser,
23+
pythonLanguage,
24+
pythonParser,
25+
};
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# NanoAPI C Plugin
2+
3+
This plugin manages parsing and mapping of dependencies in C projects.
4+
5+
**Warning :** This plugin relies on tree-sitter, which has an unreliable parser
6+
for C. Not every C project is entirely compatible. Warnings may be issued where
7+
tree-sitter finds errors.
8+
9+
## Class diagram
10+
11+
```mermaid
12+
classDiagram
13+
class CMetricsAnalyzer {
14+
+analyzeNode(node: Parser.SyntaxNode): CComplexityMetrics
15+
}
16+
17+
class CExtractor {
18+
-manifest: DependencyManifest
19+
-registry: Map<string, CFile>
20+
-includeResolver: CIncludeResolver
21+
+extractSymbols(symbolsMap: Map<string, SymbolsToExtract>): Map<string, File>
22+
}
23+
24+
class CSymbolRegistry {
25+
-headerResolver: CHeaderResolver
26+
-files: Map<string, File>
27+
+getRegistry(): Map<string, CFile>
28+
}
29+
30+
class CHeaderResolver {
31+
+resolveSymbols(file: File): ExportedSymbol[]
32+
}
33+
34+
class CIncludeResolver {
35+
-symbolRegistry: Map<string, CFile>
36+
-files: Map<string, File>
37+
+getInclusions(): Map<string, Inclusions>
38+
}
39+
40+
class CInvocationResolver {
41+
-includeResolver: CIncludeResolver
42+
+getInvocationsForSymbol(symbol: Symbol): Invocations
43+
+getInvocationsForFile(filepath: string): Invocations
44+
}
45+
46+
class CDependencyFormatter {
47+
-symbolRegistry: CSymbolRegistry
48+
-includeResolver: CIncludeResolver
49+
-invocationResolver: CInvocationResolver
50+
+formatFile(filepath: string): CDepFile
51+
}
52+
53+
class Symbol {
54+
<<abstract>>
55+
-name: string
56+
-declaration: ExportedSymbol
57+
}
58+
59+
class FunctionSignature {
60+
-definition: FunctionDefinition
61+
-isMacro: boolean
62+
}
63+
64+
class FunctionDefinition {
65+
-signature: FunctionSignature
66+
-isMacro: boolean
67+
}
68+
69+
class DataType {
70+
-typedefs: Map<string, Typedef>
71+
}
72+
73+
class Typedef {
74+
-datatype: DataType
75+
}
76+
77+
class Variable {
78+
-isMacro: boolean
79+
}
80+
81+
class CFile {
82+
-file: File
83+
-symbols: Map<string, Symbol>
84+
-type: CFileType
85+
}
86+
87+
class ExportedSymbol {
88+
-name: string
89+
-type: SymbolType
90+
-specifiers: StorageClassSpecifier[]
91+
-qualifiers: TypeQualifier[]
92+
-node: Parser.SyntaxNode
93+
-identifierNode: Parser.SyntaxNode
94+
-filepath: string
95+
}
96+
97+
class Inclusions {
98+
-filepath: string
99+
-symbols: Map<string, Symbol>
100+
-internal: string[]
101+
-standard: Map<string, Parser.SyntaxNode>
102+
}
103+
104+
class Invocations {
105+
-resolved: Map<string, Symbol>
106+
-unresolved: Set<string>
107+
}
108+
109+
class CDependency {
110+
-id: string
111+
-isExternal: boolean
112+
-symbols: Record<string, string>
113+
}
114+
115+
class CDepFile {
116+
-id: string
117+
-filePath: string
118+
-rootNode: Parser.SyntaxNode
119+
-lineCount: number
120+
-characterCount: number
121+
-dependencies: Record<string, CDependency>
122+
-symbols: Record<string, CDepSymbol>
123+
}
124+
125+
class CDepSymbol {
126+
-id: string
127+
-type: CDepSymbolType
128+
-lineCount: number
129+
-characterCount: number
130+
-node: Parser.SyntaxNode
131+
-dependents: Record<string, CDependent>
132+
-dependencies: Record<string, CDependency>
133+
}
134+
135+
class CComplexityMetrics {
136+
-cyclomaticComplexity: number
137+
-codeLinesCount: number
138+
-linesCount: number
139+
-codeCharacterCount: number
140+
-characterCount: number
141+
}
142+
143+
class CodeCounts {
144+
-lines: number
145+
-characters: number
146+
}
147+
148+
class CommentSpan {
149+
-start: Point
150+
-end: Point
151+
}
152+
153+
%% Relationships
154+
Symbol <|-- FunctionSignature
155+
Symbol <|-- FunctionDefinition
156+
Symbol <|-- DataType
157+
Symbol <|-- Typedef
158+
Symbol <|-- Variable
159+
CSymbolRegistry --> CFile
160+
CSymbolRegistry --> CHeaderResolver
161+
CIncludeResolver --> CSymbolRegistry
162+
CInvocationResolver --> CIncludeResolver
163+
CDependencyFormatter --> CSymbolRegistry
164+
CDependencyFormatter --> CIncludeResolver
165+
CDependencyFormatter --> CInvocationResolver
166+
CExtractor --> CSymbolRegistry
167+
CExtractor --> CIncludeResolver
168+
CExtractor --> CFile
169+
CFile --> Symbol
170+
Typedef --> DataType
171+
DataType --> Typedef
172+
Invocations --> Symbol
173+
Inclusions --> Symbol
174+
CDepFile --> CDependency
175+
CDepFile --> CDepSymbol
176+
CDepSymbol --> CDependent
177+
CDepSymbol --> CDependency
178+
CMetricsAnalyzer --> CComplexityMetrics
179+
CMetricsAnalyzer --> CodeCounts
180+
CMetricsAnalyzer --> CommentSpan
181+
```
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { describe, test } from "@std/testing/bdd";
2+
import { expect } from "@std/expect";
3+
import { cFilesFolder, getCFilesMap } from "../testFiles/index.ts";
4+
import { CDependencyFormatter } from "./index.ts";
5+
import { join } from "@std/path";
6+
7+
describe("CDependencyFormatter", () => {
8+
const cFilesMap = getCFilesMap();
9+
const depFormatter = new CDependencyFormatter(cFilesMap);
10+
const burgersh = join(cFilesFolder, "burgers.h");
11+
const burgersc = join(cFilesFolder, "burgers.c");
12+
const personnelh = join(cFilesFolder, "personnel.h");
13+
const main = join(cFilesFolder, "main.c");
14+
15+
test("main.c", () => {
16+
const fmain = depFormatter.formatFile(main);
17+
expect(fmain).toBeDefined();
18+
expect(fmain.id).toBe(main);
19+
expect(fmain.dependencies[burgersh]).toBeDefined();
20+
expect(fmain.dependencies[burgersc]).not.toBeDefined();
21+
expect(fmain.dependencies[personnelh]).toBeDefined();
22+
expect(fmain.dependencies["<stdio.h>"]).toBeDefined();
23+
expect(fmain.dependencies[personnelh].isExternal).toBe(false);
24+
expect(fmain.dependencies[burgersh].isExternal).toBe(false);
25+
expect(fmain.dependencies["<stdio.h>"].isExternal).toBe(true);
26+
expect(fmain.dependencies[burgersh].symbols["Burger"]).toBeDefined();
27+
expect(fmain.dependencies[burgersh].symbols["create_burger"]).toBeDefined();
28+
expect(fmain.dependencies[personnelh].symbols["Employee"]).toBeDefined();
29+
expect(
30+
fmain.dependencies[personnelh].symbols["create_employee"],
31+
).toBeDefined();
32+
expect(
33+
fmain.dependencies[personnelh].symbols["print_employee_details"],
34+
).toBeDefined();
35+
expect(fmain.symbols["main"]).toBeDefined();
36+
expect(fmain.symbols["main"].type).toBe("function");
37+
expect(fmain.symbols["main"].lineCount > 1).toBe(true);
38+
expect(fmain.symbols["main"].characterCount > 1).toBe(true);
39+
expect(fmain.symbols["main"].dependents).toBeDefined();
40+
expect(fmain.symbols["main"].dependencies).toBeDefined();
41+
expect(fmain.symbols["main"].dependencies[burgersh]).toBeDefined();
42+
expect(fmain.symbols["main"].dependencies[burgersh].isExternal).toBe(false);
43+
expect(fmain.symbols["main"].dependencies[burgersh].symbols["Burger"]).toBe(
44+
"Burger",
45+
);
46+
expect(
47+
fmain.symbols["main"].dependencies[burgersh].symbols["create_burger"],
48+
).toBe("create_burger");
49+
expect(fmain.symbols["main"].dependencies[personnelh]).toBeDefined();
50+
expect(fmain.symbols["main"].dependencies[personnelh].isExternal).toBe(
51+
false,
52+
);
53+
expect(
54+
fmain.symbols["main"].dependencies[personnelh].symbols["Employee"],
55+
).toBe("Employee");
56+
expect(
57+
fmain.symbols["main"].dependencies[personnelh].symbols["create_employee"],
58+
).toBe("create_employee");
59+
expect(
60+
fmain.symbols["main"].dependencies[personnelh].symbols[
61+
"print_employee_details"
62+
],
63+
).toBe("print_employee_details");
64+
expect(fmain.symbols["main"].dependencies["<stdio.h>"]).not.toBeDefined();
65+
});
66+
});

0 commit comments

Comments
 (0)