linguclaw 0.4.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/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/agent-system.d.ts +196 -0
- package/dist/agent-system.d.ts.map +1 -0
- package/dist/agent-system.js +738 -0
- package/dist/agent-system.js.map +1 -0
- package/dist/alphabeta.d.ts +54 -0
- package/dist/alphabeta.d.ts.map +1 -0
- package/dist/alphabeta.js +193 -0
- package/dist/alphabeta.js.map +1 -0
- package/dist/browser.d.ts +62 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +224 -0
- package/dist/browser.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +565 -0
- package/dist/cli.js.map +1 -0
- package/dist/code-parser.d.ts +39 -0
- package/dist/code-parser.d.ts.map +1 -0
- package/dist/code-parser.js +385 -0
- package/dist/code-parser.js.map +1 -0
- package/dist/config.d.ts +66 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +232 -0
- package/dist/config.js.map +1 -0
- package/dist/core/engine.d.ts +359 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +127 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/daemon.d.ts +29 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +212 -0
- package/dist/daemon.js.map +1 -0
- package/dist/email-receiver.d.ts +63 -0
- package/dist/email-receiver.d.ts.map +1 -0
- package/dist/email-receiver.js +553 -0
- package/dist/email-receiver.js.map +1 -0
- package/dist/git-integration.d.ts +180 -0
- package/dist/git-integration.d.ts.map +1 -0
- package/dist/git-integration.js +850 -0
- package/dist/git-integration.js.map +1 -0
- package/dist/inbox.d.ts +84 -0
- package/dist/inbox.d.ts.map +1 -0
- package/dist/inbox.js +198 -0
- package/dist/inbox.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/languages/cpp.d.ts +51 -0
- package/dist/languages/cpp.d.ts.map +1 -0
- package/dist/languages/cpp.js +930 -0
- package/dist/languages/cpp.js.map +1 -0
- package/dist/languages/csharp.d.ts +79 -0
- package/dist/languages/csharp.d.ts.map +1 -0
- package/dist/languages/csharp.js +1776 -0
- package/dist/languages/csharp.js.map +1 -0
- package/dist/languages/go.d.ts +50 -0
- package/dist/languages/go.d.ts.map +1 -0
- package/dist/languages/go.js +882 -0
- package/dist/languages/go.js.map +1 -0
- package/dist/languages/java.d.ts +47 -0
- package/dist/languages/java.d.ts.map +1 -0
- package/dist/languages/java.js +649 -0
- package/dist/languages/java.js.map +1 -0
- package/dist/languages/python.d.ts +47 -0
- package/dist/languages/python.d.ts.map +1 -0
- package/dist/languages/python.js +655 -0
- package/dist/languages/python.js.map +1 -0
- package/dist/languages/rust.d.ts +61 -0
- package/dist/languages/rust.d.ts.map +1 -0
- package/dist/languages/rust.js +1064 -0
- package/dist/languages/rust.js.map +1 -0
- package/dist/logger.d.ts +20 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +133 -0
- package/dist/logger.js.map +1 -0
- package/dist/longterm-memory.d.ts +47 -0
- package/dist/longterm-memory.d.ts.map +1 -0
- package/dist/longterm-memory.js +300 -0
- package/dist/longterm-memory.js.map +1 -0
- package/dist/memory.d.ts +42 -0
- package/dist/memory.d.ts.map +1 -0
- package/dist/memory.js +274 -0
- package/dist/memory.js.map +1 -0
- package/dist/messaging.d.ts +103 -0
- package/dist/messaging.d.ts.map +1 -0
- package/dist/messaging.js +645 -0
- package/dist/messaging.js.map +1 -0
- package/dist/multi-provider.d.ts +69 -0
- package/dist/multi-provider.d.ts.map +1 -0
- package/dist/multi-provider.js +484 -0
- package/dist/multi-provider.js.map +1 -0
- package/dist/orchestrator.d.ts +65 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +441 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/plugins.d.ts +52 -0
- package/dist/plugins.d.ts.map +1 -0
- package/dist/plugins.js +215 -0
- package/dist/plugins.js.map +1 -0
- package/dist/prism-orchestrator.d.ts +26 -0
- package/dist/prism-orchestrator.d.ts.map +1 -0
- package/dist/prism-orchestrator.js +191 -0
- package/dist/prism-orchestrator.js.map +1 -0
- package/dist/prism.d.ts +46 -0
- package/dist/prism.d.ts.map +1 -0
- package/dist/prism.js +188 -0
- package/dist/prism.js.map +1 -0
- package/dist/privacy.d.ts +23 -0
- package/dist/privacy.d.ts.map +1 -0
- package/dist/privacy.js +220 -0
- package/dist/privacy.js.map +1 -0
- package/dist/proactive.d.ts +30 -0
- package/dist/proactive.d.ts.map +1 -0
- package/dist/proactive.js +260 -0
- package/dist/proactive.js.map +1 -0
- package/dist/refactoring-engine.d.ts +100 -0
- package/dist/refactoring-engine.d.ts.map +1 -0
- package/dist/refactoring-engine.js +717 -0
- package/dist/refactoring-engine.js.map +1 -0
- package/dist/resilience.d.ts +43 -0
- package/dist/resilience.d.ts.map +1 -0
- package/dist/resilience.js +200 -0
- package/dist/resilience.js.map +1 -0
- package/dist/safety.d.ts +40 -0
- package/dist/safety.d.ts.map +1 -0
- package/dist/safety.js +133 -0
- package/dist/safety.js.map +1 -0
- package/dist/sandbox.d.ts +33 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +173 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/scheduler.d.ts +72 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +374 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/semantic-memory.d.ts +70 -0
- package/dist/semantic-memory.d.ts.map +1 -0
- package/dist/semantic-memory.js +430 -0
- package/dist/semantic-memory.js.map +1 -0
- package/dist/skills.d.ts +97 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +575 -0
- package/dist/skills.js.map +1 -0
- package/dist/static/dashboard.html +853 -0
- package/dist/static/hub.html +772 -0
- package/dist/static/index.html +818 -0
- package/dist/static/logo.svg +24 -0
- package/dist/static/workflow-editor.html +913 -0
- package/dist/tools.d.ts +67 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +303 -0
- package/dist/tools.js.map +1 -0
- package/dist/types.d.ts +295 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +90 -0
- package/dist/types.js.map +1 -0
- package/dist/web.d.ts +76 -0
- package/dist/web.d.ts.map +1 -0
- package/dist/web.js +2139 -0
- package/dist/web.js.map +1 -0
- package/dist/workflow-engine.d.ts +114 -0
- package/dist/workflow-engine.d.ts.map +1 -0
- package/dist/workflow-engine.js +855 -0
- package/dist/workflow-engine.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,1064 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rust Language Support for LinguClaw
|
|
4
|
+
* Advanced Rust parser with ownership analysis
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.RustLanguageSupport = exports.RustAnalyzer = exports.RustParser = void 0;
|
|
8
|
+
class RustParser {
|
|
9
|
+
source = '';
|
|
10
|
+
lines = [];
|
|
11
|
+
parse(source, filePath) {
|
|
12
|
+
this.source = source;
|
|
13
|
+
this.lines = source.split('\n');
|
|
14
|
+
try {
|
|
15
|
+
// Rust syntax is complex - use tree-sitter in production
|
|
16
|
+
// Here we implement a robust regex-based parser
|
|
17
|
+
const ast = this.parseRust(source, filePath);
|
|
18
|
+
return {
|
|
19
|
+
ast,
|
|
20
|
+
errors: this.findSyntaxErrors(),
|
|
21
|
+
warnings: [],
|
|
22
|
+
tokens: [],
|
|
23
|
+
comments: this.extractComments(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return this.fallbackParse(filePath);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
async *parseStream(source) {
|
|
31
|
+
const reader = source.getReader();
|
|
32
|
+
let buffer = '';
|
|
33
|
+
while (true) {
|
|
34
|
+
const { done, value } = await reader.read();
|
|
35
|
+
if (done)
|
|
36
|
+
break;
|
|
37
|
+
buffer += value;
|
|
38
|
+
// Rust items are often separated by newlines at top level
|
|
39
|
+
const lines = buffer.split('\n');
|
|
40
|
+
const completeLines = [];
|
|
41
|
+
let remaining = '';
|
|
42
|
+
let braceCount = 0;
|
|
43
|
+
let inString = false;
|
|
44
|
+
let stringChar = '';
|
|
45
|
+
for (let i = 0; i < lines.length; i++) {
|
|
46
|
+
const line = lines[i];
|
|
47
|
+
for (const char of line) {
|
|
48
|
+
if (!inString && (char === '"' || char === "'" || char === '`')) {
|
|
49
|
+
inString = true;
|
|
50
|
+
stringChar = char;
|
|
51
|
+
}
|
|
52
|
+
else if (inString && char === stringChar && !this.isEscaped(line, line.indexOf(char))) {
|
|
53
|
+
inString = false;
|
|
54
|
+
}
|
|
55
|
+
else if (!inString) {
|
|
56
|
+
if (char === '{' || char === '[' || char === '(')
|
|
57
|
+
braceCount++;
|
|
58
|
+
if (char === '}' || char === ']' || char === ')')
|
|
59
|
+
braceCount--;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (braceCount === 0 && !inString) {
|
|
63
|
+
completeLines.push(line);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
remaining += line + '\n';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (completeLines.length > 0) {
|
|
70
|
+
yield this.parse(completeLines.join('\n'), '<stream>');
|
|
71
|
+
}
|
|
72
|
+
buffer = remaining;
|
|
73
|
+
}
|
|
74
|
+
if (buffer.trim()) {
|
|
75
|
+
yield this.parse(buffer, '<stream>');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
parseRust(source, filePath) {
|
|
79
|
+
const root = {
|
|
80
|
+
type: 'Program',
|
|
81
|
+
id: `${filePath}:0:0`,
|
|
82
|
+
location: this.createLocation(filePath, 0, 0, this.lines.length, 0),
|
|
83
|
+
children: [],
|
|
84
|
+
metadata: { language: 'rust' },
|
|
85
|
+
};
|
|
86
|
+
// Parse Rust modules and items
|
|
87
|
+
const items = this.parseItems(source, filePath);
|
|
88
|
+
root.children = items;
|
|
89
|
+
return root;
|
|
90
|
+
}
|
|
91
|
+
parseItems(source, filePath) {
|
|
92
|
+
const items = [];
|
|
93
|
+
const lines = source.split('\n');
|
|
94
|
+
let currentIndex = 0;
|
|
95
|
+
while (currentIndex < lines.length) {
|
|
96
|
+
const line = lines[currentIndex].trim();
|
|
97
|
+
// Module declarations
|
|
98
|
+
if (line.startsWith('mod ')) {
|
|
99
|
+
const match = line.match(/mod\s+(\w+)/);
|
|
100
|
+
if (match) {
|
|
101
|
+
items.push({
|
|
102
|
+
type: 'ModuleDeclaration',
|
|
103
|
+
id: `${filePath}:${currentIndex + 1}:0`,
|
|
104
|
+
location: this.createLocation(filePath, currentIndex + 1, 0, currentIndex + 1, line.length),
|
|
105
|
+
children: [],
|
|
106
|
+
metadata: { name: match[1] },
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Use declarations (imports)
|
|
111
|
+
if (line.startsWith('use ')) {
|
|
112
|
+
const endIdx = this.findStatementEnd(lines, currentIndex);
|
|
113
|
+
const useStatement = lines.slice(currentIndex, endIdx + 1).join('\n');
|
|
114
|
+
items.push({
|
|
115
|
+
type: 'ImportDeclaration',
|
|
116
|
+
id: `${filePath}:${currentIndex + 1}:0`,
|
|
117
|
+
location: this.createLocation(filePath, currentIndex + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
118
|
+
children: [],
|
|
119
|
+
metadata: {
|
|
120
|
+
source: useStatement,
|
|
121
|
+
isGlob: useStatement.includes('*'),
|
|
122
|
+
isCrate: useStatement.startsWith('use crate::'),
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
currentIndex = endIdx;
|
|
126
|
+
}
|
|
127
|
+
// Struct definitions
|
|
128
|
+
if (line.startsWith('struct ')) {
|
|
129
|
+
const structInfo = this.parseStruct(lines, currentIndex, filePath);
|
|
130
|
+
if (structInfo) {
|
|
131
|
+
items.push(structInfo.node);
|
|
132
|
+
currentIndex = structInfo.endIndex;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Enum definitions
|
|
136
|
+
if (line.startsWith('enum ')) {
|
|
137
|
+
const enumInfo = this.parseEnum(lines, currentIndex, filePath);
|
|
138
|
+
if (enumInfo) {
|
|
139
|
+
items.push(enumInfo.node);
|
|
140
|
+
currentIndex = enumInfo.endIndex;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Trait definitions
|
|
144
|
+
if (line.startsWith('trait ')) {
|
|
145
|
+
const traitInfo = this.parseTrait(lines, currentIndex, filePath);
|
|
146
|
+
if (traitInfo) {
|
|
147
|
+
items.push(traitInfo.node);
|
|
148
|
+
currentIndex = traitInfo.endIndex;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Implementation blocks
|
|
152
|
+
if (line.startsWith('impl ')) {
|
|
153
|
+
const implInfo = this.parseImpl(lines, currentIndex, filePath);
|
|
154
|
+
if (implInfo) {
|
|
155
|
+
items.push(implInfo.node);
|
|
156
|
+
currentIndex = implInfo.endIndex;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Function definitions
|
|
160
|
+
if (this.isFunctionDeclaration(line)) {
|
|
161
|
+
const funcInfo = this.parseFunction(lines, currentIndex, filePath);
|
|
162
|
+
if (funcInfo) {
|
|
163
|
+
items.push(funcInfo.node);
|
|
164
|
+
currentIndex = funcInfo.endIndex;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Static/Const declarations
|
|
168
|
+
if (line.startsWith('static ') || line.startsWith('const ')) {
|
|
169
|
+
const constInfo = this.parseConst(lines, currentIndex, filePath);
|
|
170
|
+
if (constInfo) {
|
|
171
|
+
items.push(constInfo.node);
|
|
172
|
+
currentIndex = constInfo.endIndex;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Type aliases
|
|
176
|
+
if (line.startsWith('type ')) {
|
|
177
|
+
const typeInfo = this.parseTypeAlias(lines, currentIndex, filePath);
|
|
178
|
+
if (typeInfo) {
|
|
179
|
+
items.push(typeInfo.node);
|
|
180
|
+
currentIndex = typeInfo.endIndex;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Macros
|
|
184
|
+
if (line.startsWith('macro_rules! ') || line.includes('!')) {
|
|
185
|
+
const macroInfo = this.parseMacro(lines, currentIndex, filePath);
|
|
186
|
+
if (macroInfo) {
|
|
187
|
+
items.push(macroInfo.node);
|
|
188
|
+
currentIndex = macroInfo.endIndex;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
currentIndex++;
|
|
192
|
+
}
|
|
193
|
+
return items;
|
|
194
|
+
}
|
|
195
|
+
isFunctionDeclaration(line) {
|
|
196
|
+
// Match fn declarations with various visibility modifiers
|
|
197
|
+
const fnPattern = /^(pub\s+)?(async\s+)?(unsafe\s+)?fn\s+\w+/;
|
|
198
|
+
return fnPattern.test(line.trim());
|
|
199
|
+
}
|
|
200
|
+
parseFunction(lines, startIdx, filePath) {
|
|
201
|
+
const line = lines[startIdx].trim();
|
|
202
|
+
const signatureMatch = line.match(/(pub\s+)?(async\s+)?(unsafe\s+)?fn\s+(\w+)\s*[<(]/);
|
|
203
|
+
if (!signatureMatch)
|
|
204
|
+
return null;
|
|
205
|
+
const isPub = !!signatureMatch[1];
|
|
206
|
+
const isAsync = !!signatureMatch[2];
|
|
207
|
+
const isUnsafe = !!signatureMatch[3];
|
|
208
|
+
const funcName = signatureMatch[4];
|
|
209
|
+
// Find function body boundaries
|
|
210
|
+
let braceCount = 0;
|
|
211
|
+
let endIdx = startIdx;
|
|
212
|
+
let foundOpeningBrace = false;
|
|
213
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
214
|
+
for (const char of lines[i]) {
|
|
215
|
+
if (char === '{') {
|
|
216
|
+
braceCount++;
|
|
217
|
+
foundOpeningBrace = true;
|
|
218
|
+
}
|
|
219
|
+
else if (char === '}') {
|
|
220
|
+
braceCount--;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (foundOpeningBrace && braceCount === 0) {
|
|
224
|
+
endIdx = i;
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
const body = lines.slice(startIdx, endIdx + 1).join('\n');
|
|
229
|
+
const signature = this.extractSignature(body);
|
|
230
|
+
const generics = this.extractGenerics(body);
|
|
231
|
+
const returnType = this.extractReturnType(body);
|
|
232
|
+
const node = {
|
|
233
|
+
type: 'FunctionDeclaration',
|
|
234
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
235
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
236
|
+
children: this.parseStatements(lines.slice(startIdx + 1, endIdx), filePath, startIdx + 1),
|
|
237
|
+
metadata: {
|
|
238
|
+
name: funcName,
|
|
239
|
+
isPublic: isPub,
|
|
240
|
+
isAsync,
|
|
241
|
+
isUnsafe,
|
|
242
|
+
signature,
|
|
243
|
+
generics,
|
|
244
|
+
returnType,
|
|
245
|
+
parameters: this.extractParameters(body),
|
|
246
|
+
hasSelf: body.includes('&self') || body.includes('&mut self') || body.includes('self'),
|
|
247
|
+
isMethod: body.includes('&self') || body.includes('self:'),
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
return { node, endIndex: endIdx };
|
|
251
|
+
}
|
|
252
|
+
parseStruct(lines, startIdx, filePath) {
|
|
253
|
+
const line = lines[startIdx].trim();
|
|
254
|
+
const match = line.match(/struct\s+(\w+)(?:<([^>]+)>)?/);
|
|
255
|
+
if (!match)
|
|
256
|
+
return null;
|
|
257
|
+
const structName = match[1];
|
|
258
|
+
const generics = match[2];
|
|
259
|
+
let endIdx = startIdx;
|
|
260
|
+
let braceCount = 0;
|
|
261
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
262
|
+
for (const char of lines[i]) {
|
|
263
|
+
if (char === '{')
|
|
264
|
+
braceCount++;
|
|
265
|
+
if (char === '}')
|
|
266
|
+
braceCount--;
|
|
267
|
+
}
|
|
268
|
+
if (braceCount === 0 && i > startIdx) {
|
|
269
|
+
endIdx = i;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const body = lines.slice(startIdx, endIdx + 1).join('\n');
|
|
274
|
+
const fields = this.extractStructFields(body);
|
|
275
|
+
const node = {
|
|
276
|
+
type: 'StructDeclaration',
|
|
277
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
278
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
279
|
+
children: fields.map((f, idx) => ({
|
|
280
|
+
type: 'FieldDeclaration',
|
|
281
|
+
id: `${filePath}:${startIdx + 1 + idx}:0`,
|
|
282
|
+
location: this.createLocation(filePath, startIdx + 1 + idx, 0, startIdx + 1 + idx, 0),
|
|
283
|
+
children: [],
|
|
284
|
+
metadata: f,
|
|
285
|
+
})),
|
|
286
|
+
metadata: {
|
|
287
|
+
name: structName,
|
|
288
|
+
generics: generics ? generics.split(',').map(g => g.trim()) : [],
|
|
289
|
+
isTupleStruct: fields.length === 0 && body.includes('('),
|
|
290
|
+
isUnitStruct: fields.length === 0 && !body.includes('('),
|
|
291
|
+
derives: this.extractDerives(lines, startIdx),
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
return { node, endIndex: endIdx };
|
|
295
|
+
}
|
|
296
|
+
parseEnum(lines, startIdx, filePath) {
|
|
297
|
+
const line = lines[startIdx].trim();
|
|
298
|
+
const match = line.match(/enum\s+(\w+)(?:<([^>]+)>)?/);
|
|
299
|
+
if (!match)
|
|
300
|
+
return null;
|
|
301
|
+
const enumName = match[1];
|
|
302
|
+
let endIdx = startIdx;
|
|
303
|
+
let braceCount = 0;
|
|
304
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
305
|
+
for (const char of lines[i]) {
|
|
306
|
+
if (char === '{')
|
|
307
|
+
braceCount++;
|
|
308
|
+
if (char === '}')
|
|
309
|
+
braceCount--;
|
|
310
|
+
}
|
|
311
|
+
if (braceCount === 0 && i > startIdx) {
|
|
312
|
+
endIdx = i;
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
const body = lines.slice(startIdx, endIdx + 1).join('\n');
|
|
317
|
+
const variants = this.extractEnumVariants(body);
|
|
318
|
+
const node = {
|
|
319
|
+
type: 'EnumDeclaration',
|
|
320
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
321
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
322
|
+
children: variants.map((v, idx) => ({
|
|
323
|
+
type: 'EnumVariant',
|
|
324
|
+
id: `${filePath}:${startIdx + 1 + idx}:0`,
|
|
325
|
+
location: this.createLocation(filePath, startIdx + 1 + idx, 0, startIdx + 1 + idx, 0),
|
|
326
|
+
children: [],
|
|
327
|
+
metadata: v,
|
|
328
|
+
})),
|
|
329
|
+
metadata: {
|
|
330
|
+
name: enumName,
|
|
331
|
+
variants,
|
|
332
|
+
isOptionLike: enumName === 'Option' || variants.some(v => v.name === 'Some' || v.name === 'None'),
|
|
333
|
+
isResultLike: enumName === 'Result' || variants.some(v => v.name === 'Ok' || v.name === 'Err'),
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
return { node, endIndex: endIdx };
|
|
337
|
+
}
|
|
338
|
+
parseImpl(lines, startIdx, filePath) {
|
|
339
|
+
const line = lines[startIdx].trim();
|
|
340
|
+
const match = line.match(/impl(?:<([^>]+)>)?\s+(?:\w+\s+for\s+)?(\w+)/);
|
|
341
|
+
if (!match)
|
|
342
|
+
return null;
|
|
343
|
+
let endIdx = startIdx;
|
|
344
|
+
let braceCount = 0;
|
|
345
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
346
|
+
for (const char of lines[i]) {
|
|
347
|
+
if (char === '{')
|
|
348
|
+
braceCount++;
|
|
349
|
+
if (char === '}')
|
|
350
|
+
braceCount--;
|
|
351
|
+
}
|
|
352
|
+
if (braceCount === 0 && i > startIdx) {
|
|
353
|
+
endIdx = i;
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Parse methods within impl
|
|
358
|
+
const implBody = lines.slice(startIdx + 1, endIdx);
|
|
359
|
+
const methods = [];
|
|
360
|
+
let methodStart = 0;
|
|
361
|
+
while (methodStart < implBody.length) {
|
|
362
|
+
const methodLine = implBody[methodStart].trim();
|
|
363
|
+
if (this.isFunctionDeclaration(methodLine)) {
|
|
364
|
+
const methodInfo = this.parseFunction(implBody, methodStart, filePath);
|
|
365
|
+
if (methodInfo) {
|
|
366
|
+
methods.push(methodInfo.node);
|
|
367
|
+
methodStart = methodInfo.endIndex + 1;
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
methodStart++;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
methodStart++;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const isTraitImpl = line.includes(' for ');
|
|
378
|
+
const parts = line.split(/\s+for\s+/);
|
|
379
|
+
const traitName = isTraitImpl ? parts[0].replace('impl', '').trim() : undefined;
|
|
380
|
+
const targetType = isTraitImpl ? parts[1].replace('{', '').trim() : line.replace('impl', '').replace('{', '').trim();
|
|
381
|
+
const node = {
|
|
382
|
+
type: 'Implementation',
|
|
383
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
384
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
385
|
+
children: methods,
|
|
386
|
+
metadata: {
|
|
387
|
+
trait: traitName,
|
|
388
|
+
targetType,
|
|
389
|
+
methods: methods.map(m => m.metadata.name),
|
|
390
|
+
isTraitImpl,
|
|
391
|
+
},
|
|
392
|
+
};
|
|
393
|
+
return { node, endIndex: endIdx };
|
|
394
|
+
}
|
|
395
|
+
extractSignature(body) {
|
|
396
|
+
const match = body.match(/fn\s+\w+\s*[^(]*\([^)]*\)(?:\s*->\s*[^{]+)?/);
|
|
397
|
+
return match ? match[0] : 'fn unknown()';
|
|
398
|
+
}
|
|
399
|
+
extractGenerics(body) {
|
|
400
|
+
const match = body.match(/<([^>]+)>/);
|
|
401
|
+
return match ? match[1].split(',').map(g => g.trim()) : [];
|
|
402
|
+
}
|
|
403
|
+
extractReturnType(body) {
|
|
404
|
+
const match = body.match(/->\s*([^{]+)/);
|
|
405
|
+
return match ? match[1].trim() : undefined;
|
|
406
|
+
}
|
|
407
|
+
extractParameters(body) {
|
|
408
|
+
const params = [];
|
|
409
|
+
const match = body.match(/\(([^)]*)\)/);
|
|
410
|
+
if (match) {
|
|
411
|
+
const paramStr = match[1];
|
|
412
|
+
const paramList = this.splitParams(paramStr);
|
|
413
|
+
for (const param of paramList) {
|
|
414
|
+
const parts = param.trim().split(':');
|
|
415
|
+
if (parts.length === 2) {
|
|
416
|
+
const name = parts[0].trim();
|
|
417
|
+
const isMut = name.startsWith('mut ');
|
|
418
|
+
params.push({
|
|
419
|
+
name: isMut ? name.replace('mut ', '') : name,
|
|
420
|
+
type: parts[1].trim(),
|
|
421
|
+
mutable: isMut,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return params;
|
|
427
|
+
}
|
|
428
|
+
splitParams(paramStr) {
|
|
429
|
+
const params = [];
|
|
430
|
+
let current = '';
|
|
431
|
+
let depth = 0;
|
|
432
|
+
for (const char of paramStr) {
|
|
433
|
+
if (char === '<' || char === '(' || char === '[')
|
|
434
|
+
depth++;
|
|
435
|
+
if (char === '>' || char === ')' || char === ']')
|
|
436
|
+
depth--;
|
|
437
|
+
if (char === ',' && depth === 0) {
|
|
438
|
+
params.push(current.trim());
|
|
439
|
+
current = '';
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
current += char;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (current.trim()) {
|
|
446
|
+
params.push(current.trim());
|
|
447
|
+
}
|
|
448
|
+
return params;
|
|
449
|
+
}
|
|
450
|
+
extractStructFields(body) {
|
|
451
|
+
const fields = [];
|
|
452
|
+
const fieldMatch = body.match(/\{([^}]*)\}/s);
|
|
453
|
+
if (fieldMatch) {
|
|
454
|
+
const fieldStr = fieldMatch[1];
|
|
455
|
+
const fieldLines = fieldStr.split(',');
|
|
456
|
+
for (const line of fieldLines) {
|
|
457
|
+
const trimmed = line.trim();
|
|
458
|
+
const match = trimmed.match(/(pub\s+)?(\w+)\s*:\s*(.+)/);
|
|
459
|
+
if (match) {
|
|
460
|
+
fields.push({
|
|
461
|
+
name: match[2],
|
|
462
|
+
type: match[3].trim(),
|
|
463
|
+
public: !!match[1],
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return fields;
|
|
469
|
+
}
|
|
470
|
+
extractEnumVariants(body) {
|
|
471
|
+
const variants = [];
|
|
472
|
+
const variantMatch = body.match(/\{([^}]*)\}/s);
|
|
473
|
+
if (variantMatch) {
|
|
474
|
+
const variantStr = variantMatch[1];
|
|
475
|
+
const variantLines = variantStr.split(',');
|
|
476
|
+
for (const line of variantLines) {
|
|
477
|
+
const trimmed = line.trim();
|
|
478
|
+
// Match variant name and optional data
|
|
479
|
+
const match = trimmed.match(/(\w+)(?:\s*\(([^)]*)\))?/);
|
|
480
|
+
if (match) {
|
|
481
|
+
const hasData = !!match[2];
|
|
482
|
+
variants.push({
|
|
483
|
+
name: match[1],
|
|
484
|
+
hasData,
|
|
485
|
+
dataTypes: hasData ? match[2].split(',').map(t => t.trim()) : [],
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return variants;
|
|
491
|
+
}
|
|
492
|
+
extractDerives(lines, startIdx) {
|
|
493
|
+
for (let i = Math.max(0, startIdx - 5); i < startIdx; i++) {
|
|
494
|
+
const line = lines[i].trim();
|
|
495
|
+
if (line.startsWith('#[derive(')) {
|
|
496
|
+
const match = line.match(/#\[derive\(([^)]+)\)\]/);
|
|
497
|
+
return match ? match[1].split(',').map(d => d.trim()) : [];
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return [];
|
|
501
|
+
}
|
|
502
|
+
parseStatements(lines, filePath, lineOffset) {
|
|
503
|
+
const statements = [];
|
|
504
|
+
for (let i = 0; i < lines.length; i++) {
|
|
505
|
+
const line = lines[i].trim();
|
|
506
|
+
// Variable declarations with ownership tracking
|
|
507
|
+
const letMatch = line.match(/let\s+(mut\s+)?(\w+)\s*(?::\s*([^=]+))?\s*=\s*(.+);/);
|
|
508
|
+
if (letMatch) {
|
|
509
|
+
statements.push({
|
|
510
|
+
type: 'VariableDeclaration',
|
|
511
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
512
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
513
|
+
children: [],
|
|
514
|
+
metadata: {
|
|
515
|
+
name: letMatch[2],
|
|
516
|
+
mutable: !!letMatch[1],
|
|
517
|
+
type: letMatch[3]?.trim(),
|
|
518
|
+
initializer: letMatch[4]?.trim(),
|
|
519
|
+
ownership: this.inferOwnership(letMatch[4]?.trim()),
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
// Match expressions
|
|
524
|
+
if (line.includes('match ')) {
|
|
525
|
+
statements.push({
|
|
526
|
+
type: 'MatchExpression',
|
|
527
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
528
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
529
|
+
children: [],
|
|
530
|
+
metadata: { isExhaustive: this.checkExhaustive(lines, i) },
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
// Unsafe blocks
|
|
534
|
+
if (line === 'unsafe {' || line === 'unsafe') {
|
|
535
|
+
statements.push({
|
|
536
|
+
type: 'UnsafeBlock',
|
|
537
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
538
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
539
|
+
children: [],
|
|
540
|
+
metadata: {},
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
// Async/await
|
|
544
|
+
if (line.includes('.await')) {
|
|
545
|
+
statements.push({
|
|
546
|
+
type: 'AwaitExpression',
|
|
547
|
+
id: `${filePath}:${lineOffset + i + 1}:0`,
|
|
548
|
+
location: this.createLocation(filePath, lineOffset + i + 1, 0, lineOffset + i + 1, line.length),
|
|
549
|
+
children: [],
|
|
550
|
+
metadata: {},
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return statements;
|
|
555
|
+
}
|
|
556
|
+
inferOwnership(initializer) {
|
|
557
|
+
if (!initializer)
|
|
558
|
+
return 'owned';
|
|
559
|
+
if (initializer.startsWith('&mut '))
|
|
560
|
+
return 'mut_borrowed';
|
|
561
|
+
if (initializer.startsWith('&'))
|
|
562
|
+
return 'borrowed';
|
|
563
|
+
if (initializer.includes('.clone()'))
|
|
564
|
+
return 'owned';
|
|
565
|
+
if (initializer.includes('.to_owned()'))
|
|
566
|
+
return 'owned';
|
|
567
|
+
// Check if it's a function call that might move
|
|
568
|
+
if (/\w+\([^)]*\)$/.test(initializer))
|
|
569
|
+
return 'move';
|
|
570
|
+
return 'owned';
|
|
571
|
+
}
|
|
572
|
+
checkExhaustive(lines, matchIdx) {
|
|
573
|
+
// Simple check for _ => or all variants covered
|
|
574
|
+
let braceCount = 0;
|
|
575
|
+
let foundWildcard = false;
|
|
576
|
+
for (let i = matchIdx; i < Math.min(lines.length, matchIdx + 50); i++) {
|
|
577
|
+
for (const char of lines[i]) {
|
|
578
|
+
if (char === '{')
|
|
579
|
+
braceCount++;
|
|
580
|
+
if (char === '}')
|
|
581
|
+
braceCount--;
|
|
582
|
+
}
|
|
583
|
+
if (lines[i].includes('_ =>') || lines[i].includes('_ =>')) {
|
|
584
|
+
foundWildcard = true;
|
|
585
|
+
}
|
|
586
|
+
if (braceCount === 0 && i > matchIdx)
|
|
587
|
+
break;
|
|
588
|
+
}
|
|
589
|
+
return foundWildcard;
|
|
590
|
+
}
|
|
591
|
+
parseConst(lines, startIdx, filePath) {
|
|
592
|
+
const line = lines[startIdx].trim();
|
|
593
|
+
const isStatic = line.startsWith('static ');
|
|
594
|
+
const match = line.match(/(?:static|const)\s+(mut\s+)?(\w+)\s*(?::\s*([^=]+))?\s*=/);
|
|
595
|
+
if (!match)
|
|
596
|
+
return null;
|
|
597
|
+
const endIdx = this.findStatementEnd(lines, startIdx);
|
|
598
|
+
const node = {
|
|
599
|
+
type: 'VariableDeclaration',
|
|
600
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
601
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
602
|
+
children: [],
|
|
603
|
+
metadata: {
|
|
604
|
+
name: match[2],
|
|
605
|
+
type: match[3]?.trim() || 'inferred',
|
|
606
|
+
isStatic,
|
|
607
|
+
mutable: !!match[1],
|
|
608
|
+
isConst: !isStatic,
|
|
609
|
+
},
|
|
610
|
+
};
|
|
611
|
+
return { node, endIndex: endIdx };
|
|
612
|
+
}
|
|
613
|
+
parseTypeAlias(lines, startIdx, filePath) {
|
|
614
|
+
const line = lines[startIdx].trim();
|
|
615
|
+
const match = line.match(/type\s+(\w+)(?:<([^>]+)>)?\s*=\s*(.+);/);
|
|
616
|
+
if (!match)
|
|
617
|
+
return null;
|
|
618
|
+
const node = {
|
|
619
|
+
type: 'TypeAliasDeclaration',
|
|
620
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
621
|
+
location: this.createLocation(filePath, startIdx + 1, 0, startIdx + 1, line.length),
|
|
622
|
+
children: [],
|
|
623
|
+
metadata: {
|
|
624
|
+
name: match[1],
|
|
625
|
+
generics: match[2] ? match[2].split(',').map(g => g.trim()) : [],
|
|
626
|
+
target: match[3],
|
|
627
|
+
},
|
|
628
|
+
};
|
|
629
|
+
return { node, endIndex: startIdx };
|
|
630
|
+
}
|
|
631
|
+
parseTrait(lines, startIdx, filePath) {
|
|
632
|
+
const line = lines[startIdx].trim();
|
|
633
|
+
const match = line.match(/trait\s+(\w+)(?:<([^>]+)>)?/);
|
|
634
|
+
if (!match)
|
|
635
|
+
return null;
|
|
636
|
+
let endIdx = startIdx;
|
|
637
|
+
let braceCount = 0;
|
|
638
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
639
|
+
for (const char of lines[i]) {
|
|
640
|
+
if (char === '{')
|
|
641
|
+
braceCount++;
|
|
642
|
+
if (char === '}')
|
|
643
|
+
braceCount--;
|
|
644
|
+
}
|
|
645
|
+
if (braceCount === 0 && i > startIdx) {
|
|
646
|
+
endIdx = i;
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
const body = lines.slice(startIdx, endIdx + 1).join('\n');
|
|
651
|
+
const methods = this.extractTraitMethods(body);
|
|
652
|
+
const node = {
|
|
653
|
+
type: 'TraitDeclaration',
|
|
654
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
655
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
656
|
+
children: methods.map((m, idx) => ({
|
|
657
|
+
type: 'MethodSignature',
|
|
658
|
+
id: `${filePath}:${startIdx + 1 + idx}:0`,
|
|
659
|
+
location: this.createLocation(filePath, startIdx + 1 + idx, 0, startIdx + 1 + idx, 0),
|
|
660
|
+
children: [],
|
|
661
|
+
metadata: m,
|
|
662
|
+
})),
|
|
663
|
+
metadata: {
|
|
664
|
+
name: match[1],
|
|
665
|
+
generics: match[2] ? match[2].split(',').map(g => g.trim()) : [],
|
|
666
|
+
methods,
|
|
667
|
+
isAuto: body.includes('#[auto_trait]'),
|
|
668
|
+
isUnsafe: line.includes('unsafe trait'),
|
|
669
|
+
},
|
|
670
|
+
};
|
|
671
|
+
return { node, endIndex: endIdx };
|
|
672
|
+
}
|
|
673
|
+
parseMacro(lines, startIdx, filePath) {
|
|
674
|
+
const line = lines[startIdx].trim();
|
|
675
|
+
if (!line.includes('!'))
|
|
676
|
+
return null;
|
|
677
|
+
const isMacroRules = line.startsWith('macro_rules!');
|
|
678
|
+
let endIdx = startIdx;
|
|
679
|
+
if (isMacroRules) {
|
|
680
|
+
// Macro rules end with matching braces
|
|
681
|
+
let braceCount = 0;
|
|
682
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
683
|
+
for (const char of lines[i]) {
|
|
684
|
+
if (char === '{')
|
|
685
|
+
braceCount++;
|
|
686
|
+
if (char === '}')
|
|
687
|
+
braceCount--;
|
|
688
|
+
}
|
|
689
|
+
if (braceCount === 0 && i > startIdx) {
|
|
690
|
+
endIdx = i;
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
else {
|
|
696
|
+
// Attribute macros and bang macros
|
|
697
|
+
endIdx = this.findStatementEnd(lines, startIdx);
|
|
698
|
+
}
|
|
699
|
+
const node = {
|
|
700
|
+
type: 'MacroDeclaration',
|
|
701
|
+
id: `${filePath}:${startIdx + 1}:0`,
|
|
702
|
+
location: this.createLocation(filePath, startIdx + 1, 0, endIdx + 1, lines[endIdx]?.length || 0),
|
|
703
|
+
children: [],
|
|
704
|
+
metadata: {
|
|
705
|
+
isMacroRules,
|
|
706
|
+
name: isMacroRules ? line.match(/macro_rules!\s+(\w+)/)?.[1] : undefined,
|
|
707
|
+
},
|
|
708
|
+
};
|
|
709
|
+
return { node, endIndex: endIdx };
|
|
710
|
+
}
|
|
711
|
+
findStatementEnd(lines, startIdx) {
|
|
712
|
+
// Find where statement ends (semicolon or closing brace)
|
|
713
|
+
for (let i = startIdx; i < lines.length; i++) {
|
|
714
|
+
const line = lines[i];
|
|
715
|
+
if (line.includes(';') && !line.includes('for ') && !line.includes('while ')) {
|
|
716
|
+
return i;
|
|
717
|
+
}
|
|
718
|
+
if (line.includes('{')) {
|
|
719
|
+
// Find matching }
|
|
720
|
+
let braceCount = 0;
|
|
721
|
+
for (let j = i; j < lines.length; j++) {
|
|
722
|
+
for (const char of lines[j]) {
|
|
723
|
+
if (char === '{')
|
|
724
|
+
braceCount++;
|
|
725
|
+
if (char === '}')
|
|
726
|
+
braceCount--;
|
|
727
|
+
}
|
|
728
|
+
if (braceCount === 0)
|
|
729
|
+
return j;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
return lines.length - 1;
|
|
734
|
+
}
|
|
735
|
+
extractTraitMethods(body) {
|
|
736
|
+
const methods = [];
|
|
737
|
+
const lines = body.split('\n');
|
|
738
|
+
for (const line of lines) {
|
|
739
|
+
const trimmed = line.trim();
|
|
740
|
+
if (trimmed.startsWith('fn ') && !trimmed.includes('{')) {
|
|
741
|
+
const match = trimmed.match(/fn\s+(\w+)\s*[^(]*\([^)]*\)(?:\s*->\s*[^{;]+)?/);
|
|
742
|
+
if (match) {
|
|
743
|
+
methods.push({
|
|
744
|
+
name: match[1],
|
|
745
|
+
signature: match[0],
|
|
746
|
+
hasDefault: false,
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
else if (trimmed.startsWith('fn ') && trimmed.includes('{')) {
|
|
751
|
+
const match = trimmed.match(/fn\s+(\w+)\s*[^(]*\([^)]*\)/);
|
|
752
|
+
if (match) {
|
|
753
|
+
methods.push({
|
|
754
|
+
name: match[1],
|
|
755
|
+
signature: match[0],
|
|
756
|
+
hasDefault: true,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
return methods;
|
|
762
|
+
}
|
|
763
|
+
createLocation(file, startLine, startCol, endLine, endCol) {
|
|
764
|
+
return {
|
|
765
|
+
file,
|
|
766
|
+
startLine,
|
|
767
|
+
startColumn: startCol,
|
|
768
|
+
endLine,
|
|
769
|
+
endColumn: endCol,
|
|
770
|
+
byteOffset: 0,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
isEscaped(line, pos) {
|
|
774
|
+
let backslashes = 0;
|
|
775
|
+
for (let i = pos - 1; i >= 0 && line[i] === '\\'; i--) {
|
|
776
|
+
backslashes++;
|
|
777
|
+
}
|
|
778
|
+
return backslashes % 2 === 1;
|
|
779
|
+
}
|
|
780
|
+
extractComments() {
|
|
781
|
+
const comments = [];
|
|
782
|
+
for (let i = 0; i < this.lines.length; i++) {
|
|
783
|
+
const line = this.lines[i];
|
|
784
|
+
const commentIdx = line.indexOf('//');
|
|
785
|
+
if (commentIdx !== -1 && !this.isInString(line, commentIdx)) {
|
|
786
|
+
comments.push({
|
|
787
|
+
type: 'Line',
|
|
788
|
+
value: line.substring(commentIdx + 2).trim(),
|
|
789
|
+
line: i + 1,
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
// Block comments
|
|
794
|
+
const source = this.source;
|
|
795
|
+
let idx = 0;
|
|
796
|
+
while (true) {
|
|
797
|
+
const blockStart = source.indexOf('/*', idx);
|
|
798
|
+
if (blockStart === -1)
|
|
799
|
+
break;
|
|
800
|
+
const blockEnd = source.indexOf('*/', blockStart + 2);
|
|
801
|
+
if (blockEnd === -1)
|
|
802
|
+
break;
|
|
803
|
+
const value = source.substring(blockStart + 2, blockEnd);
|
|
804
|
+
const lineNum = source.substring(0, blockStart).split('\n').length;
|
|
805
|
+
comments.push({
|
|
806
|
+
type: 'Block',
|
|
807
|
+
value: value.trim(),
|
|
808
|
+
line: lineNum,
|
|
809
|
+
});
|
|
810
|
+
idx = blockEnd + 2;
|
|
811
|
+
}
|
|
812
|
+
return comments;
|
|
813
|
+
}
|
|
814
|
+
isInString(line, pos) {
|
|
815
|
+
let inString = false;
|
|
816
|
+
let stringChar = '';
|
|
817
|
+
for (let i = 0; i < pos; i++) {
|
|
818
|
+
const char = line[i];
|
|
819
|
+
if (!inString && (char === '"' || char === "'")) {
|
|
820
|
+
inString = true;
|
|
821
|
+
stringChar = char;
|
|
822
|
+
}
|
|
823
|
+
else if (inString && char === stringChar && !this.isEscaped(line, i)) {
|
|
824
|
+
inString = false;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return inString;
|
|
828
|
+
}
|
|
829
|
+
findSyntaxErrors() {
|
|
830
|
+
const errors = [];
|
|
831
|
+
let braceCount = 0;
|
|
832
|
+
let parenCount = 0;
|
|
833
|
+
let bracketCount = 0;
|
|
834
|
+
for (let i = 0; i < this.lines.length; i++) {
|
|
835
|
+
const line = this.lines[i];
|
|
836
|
+
for (const char of line) {
|
|
837
|
+
if (char === '{')
|
|
838
|
+
braceCount++;
|
|
839
|
+
if (char === '}')
|
|
840
|
+
braceCount--;
|
|
841
|
+
if (char === '(')
|
|
842
|
+
parenCount++;
|
|
843
|
+
if (char === ')')
|
|
844
|
+
parenCount--;
|
|
845
|
+
if (char === '[')
|
|
846
|
+
bracketCount++;
|
|
847
|
+
if (char === ']')
|
|
848
|
+
bracketCount--;
|
|
849
|
+
}
|
|
850
|
+
if (braceCount < 0) {
|
|
851
|
+
errors.push({
|
|
852
|
+
message: 'Unexpected closing brace',
|
|
853
|
+
line: i + 1,
|
|
854
|
+
severity: 'error',
|
|
855
|
+
});
|
|
856
|
+
braceCount = 0;
|
|
857
|
+
}
|
|
858
|
+
if (parenCount < 0) {
|
|
859
|
+
errors.push({
|
|
860
|
+
message: 'Unexpected closing parenthesis',
|
|
861
|
+
line: i + 1,
|
|
862
|
+
severity: 'error',
|
|
863
|
+
});
|
|
864
|
+
parenCount = 0;
|
|
865
|
+
}
|
|
866
|
+
if (bracketCount < 0) {
|
|
867
|
+
errors.push({
|
|
868
|
+
message: 'Unexpected closing bracket',
|
|
869
|
+
line: i + 1,
|
|
870
|
+
severity: 'error',
|
|
871
|
+
});
|
|
872
|
+
bracketCount = 0;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
if (braceCount > 0) {
|
|
876
|
+
errors.push({
|
|
877
|
+
message: `Unclosed braces: ${braceCount}`,
|
|
878
|
+
line: this.lines.length,
|
|
879
|
+
severity: 'error',
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
if (parenCount > 0) {
|
|
883
|
+
errors.push({
|
|
884
|
+
message: `Unclosed parentheses: ${parenCount}`,
|
|
885
|
+
line: this.lines.length,
|
|
886
|
+
severity: 'error',
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
if (bracketCount > 0) {
|
|
890
|
+
errors.push({
|
|
891
|
+
message: `Unclosed brackets: ${bracketCount}`,
|
|
892
|
+
line: this.lines.length,
|
|
893
|
+
severity: 'error',
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
return errors;
|
|
897
|
+
}
|
|
898
|
+
fallbackParse(filePath) {
|
|
899
|
+
return {
|
|
900
|
+
ast: this.parseRust(this.source, filePath),
|
|
901
|
+
errors: this.findSyntaxErrors(),
|
|
902
|
+
warnings: [{ message: 'Using fallback parser' }],
|
|
903
|
+
tokens: [],
|
|
904
|
+
comments: this.extractComments(),
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
exports.RustParser = RustParser;
|
|
909
|
+
class RustAnalyzer {
|
|
910
|
+
analyze(ast, context) {
|
|
911
|
+
const symbolTable = this.buildSymbolTable(ast);
|
|
912
|
+
const ownershipGraph = this.analyzeOwnership(ast);
|
|
913
|
+
const lifetimeAnalysis = this.analyzeLifetimes(ast);
|
|
914
|
+
const unsafeAnalysis = this.analyzeUnsafe(ast);
|
|
915
|
+
const asyncAnalysis = this.analyzeAsync(ast);
|
|
916
|
+
return {
|
|
917
|
+
symbols: symbolTable,
|
|
918
|
+
callGraph: this.buildCallGraph(ast),
|
|
919
|
+
dataFlow: ownershipGraph,
|
|
920
|
+
controlFlow: this.buildControlFlow(ast),
|
|
921
|
+
typeInference: new Map(),
|
|
922
|
+
metrics: this.calculateMetrics(ast),
|
|
923
|
+
suggestions: [
|
|
924
|
+
...unsafeAnalysis.suggestions,
|
|
925
|
+
...asyncAnalysis.suggestions,
|
|
926
|
+
...this.generateRefactoringSuggestions(ast),
|
|
927
|
+
],
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
buildSymbolTable(ast) {
|
|
931
|
+
// Implementation similar to Python analyzer
|
|
932
|
+
return {
|
|
933
|
+
variables: new Map(),
|
|
934
|
+
functions: new Map(),
|
|
935
|
+
classes: new Map(),
|
|
936
|
+
modules: new Map(),
|
|
937
|
+
imports: [],
|
|
938
|
+
exports: [],
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
analyzeOwnership(ast) {
|
|
942
|
+
// Analyze Rust ownership system
|
|
943
|
+
const borrowViolations = [];
|
|
944
|
+
const moveLocations = [];
|
|
945
|
+
const traverse = (node, scope) => {
|
|
946
|
+
// Track variable ownership states
|
|
947
|
+
if (node.type === 'VariableDeclaration' && node.metadata.ownership) {
|
|
948
|
+
scope.variables.set(node.metadata.name, {
|
|
949
|
+
ownership: node.metadata.ownership,
|
|
950
|
+
mutable: node.metadata.mutable,
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
// Check for potential borrow violations
|
|
954
|
+
if (node.type === 'CallExpression') {
|
|
955
|
+
// Check if passing borrowed values
|
|
956
|
+
}
|
|
957
|
+
node.children.forEach(child => traverse(child, scope));
|
|
958
|
+
};
|
|
959
|
+
traverse(ast, { variables: new Map() });
|
|
960
|
+
return {
|
|
961
|
+
definitions: new Map(),
|
|
962
|
+
uses: new Map(),
|
|
963
|
+
taintedSources: [],
|
|
964
|
+
sinks: [],
|
|
965
|
+
borrowViolations,
|
|
966
|
+
moveLocations,
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
analyzeLifetimes(ast) {
|
|
970
|
+
// Analyze lifetime annotations
|
|
971
|
+
const explicitLifetimes = [];
|
|
972
|
+
const inferredLifetimes = new Map();
|
|
973
|
+
const traverse = (node) => {
|
|
974
|
+
if (node.metadata.generics) {
|
|
975
|
+
for (const generic of node.metadata.generics) {
|
|
976
|
+
if (generic.startsWith("'")) {
|
|
977
|
+
explicitLifetimes.push(generic);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
node.children.forEach(traverse);
|
|
982
|
+
};
|
|
983
|
+
traverse(ast);
|
|
984
|
+
return { explicitLifetimes, inferredLifetimes };
|
|
985
|
+
}
|
|
986
|
+
analyzeUnsafe(ast) {
|
|
987
|
+
const suggestions = [];
|
|
988
|
+
let unsafeBlockCount = 0;
|
|
989
|
+
let unsafeFunctionCount = 0;
|
|
990
|
+
const traverse = (node) => {
|
|
991
|
+
if (node.type === 'UnsafeBlock') {
|
|
992
|
+
unsafeBlockCount++;
|
|
993
|
+
}
|
|
994
|
+
if (node.type === 'FunctionDeclaration' && node.metadata.isUnsafe) {
|
|
995
|
+
unsafeFunctionCount++;
|
|
996
|
+
suggestions.push({
|
|
997
|
+
type: 'security',
|
|
998
|
+
severity: 'warning',
|
|
999
|
+
message: `Unsafe function ${node.metadata.name} - ensure safety invariants are documented`,
|
|
1000
|
+
remediation: 'Add SAFETY comment explaining invariants',
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
node.children.forEach(traverse);
|
|
1004
|
+
};
|
|
1005
|
+
traverse(ast);
|
|
1006
|
+
if (unsafeBlockCount > 5) {
|
|
1007
|
+
suggestions.push({
|
|
1008
|
+
type: 'refactor',
|
|
1009
|
+
severity: 'warning',
|
|
1010
|
+
message: `High number of unsafe blocks (${unsafeBlockCount}) - consider abstraction`,
|
|
1011
|
+
remediation: 'Encapsulate unsafe operations in safe wrappers',
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
return { suggestions };
|
|
1015
|
+
}
|
|
1016
|
+
analyzeAsync(ast) {
|
|
1017
|
+
const suggestions = [];
|
|
1018
|
+
let asyncFunctionCount = 0;
|
|
1019
|
+
let awaitCount = 0;
|
|
1020
|
+
const traverse = (node) => {
|
|
1021
|
+
if (node.type === 'FunctionDeclaration' && node.metadata.isAsync) {
|
|
1022
|
+
asyncFunctionCount++;
|
|
1023
|
+
}
|
|
1024
|
+
if (node.type === 'AwaitExpression') {
|
|
1025
|
+
awaitCount++;
|
|
1026
|
+
}
|
|
1027
|
+
node.children.forEach(traverse);
|
|
1028
|
+
};
|
|
1029
|
+
traverse(ast);
|
|
1030
|
+
return { suggestions };
|
|
1031
|
+
}
|
|
1032
|
+
buildCallGraph(ast) {
|
|
1033
|
+
// Implementation
|
|
1034
|
+
return { nodes: [], edges: [], entryPoints: [], deadCode: [] };
|
|
1035
|
+
}
|
|
1036
|
+
buildControlFlow(ast) {
|
|
1037
|
+
return { nodes: [], edges: [], loops: [], branches: [] };
|
|
1038
|
+
}
|
|
1039
|
+
calculateMetrics(ast) {
|
|
1040
|
+
return {
|
|
1041
|
+
linesOfCode: 0,
|
|
1042
|
+
logicalLines: 0,
|
|
1043
|
+
commentLines: 0,
|
|
1044
|
+
blankLines: 0,
|
|
1045
|
+
cyclomaticComplexity: 0,
|
|
1046
|
+
cognitiveComplexity: 0,
|
|
1047
|
+
halsteadMetrics: { operators: 0, operands: 0, uniqueOperators: 0, uniqueOperands: 0, volume: 0, difficulty: 0, effort: 0, timeToProgram: 0, bugsDelivered: 0 },
|
|
1048
|
+
maintainabilityIndex: 0,
|
|
1049
|
+
duplicateRate: 0,
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
generateRefactoringSuggestions(ast) {
|
|
1053
|
+
return [];
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
exports.RustAnalyzer = RustAnalyzer;
|
|
1057
|
+
exports.RustLanguageSupport = {
|
|
1058
|
+
id: 'rust',
|
|
1059
|
+
name: 'Rust',
|
|
1060
|
+
extensions: ['.rs'],
|
|
1061
|
+
parser: new RustParser(),
|
|
1062
|
+
analyzer: new RustAnalyzer(),
|
|
1063
|
+
};
|
|
1064
|
+
//# sourceMappingURL=rust.js.map
|