@flisk/analyze-tracking 0.7.2 → 0.7.3
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/bin/cli.js +1 -1
- package/package.json +9 -7
- package/src/analyze/go/astTraversal.js +121 -0
- package/src/analyze/go/constants.js +20 -0
- package/src/analyze/go/eventDeduplicator.js +47 -0
- package/src/analyze/go/eventExtractor.js +156 -0
- package/src/analyze/go/goAstParser/constants.js +39 -0
- package/src/analyze/go/goAstParser/expressionParser.js +281 -0
- package/src/analyze/go/goAstParser/index.js +52 -0
- package/src/analyze/go/goAstParser/statementParser.js +387 -0
- package/src/analyze/go/goAstParser/tokenizer.js +196 -0
- package/src/analyze/go/goAstParser/typeParser.js +202 -0
- package/src/analyze/go/goAstParser/utils.js +99 -0
- package/src/analyze/go/index.js +55 -0
- package/src/analyze/go/propertyExtractor.js +670 -0
- package/src/analyze/go/trackingDetector.js +71 -0
- package/src/analyze/go/trackingExtractor.js +54 -0
- package/src/analyze/go/typeContext.js +88 -0
- package/src/analyze/go/utils.js +215 -0
- package/src/analyze/index.js +11 -6
- package/src/analyze/javascript/constants.js +115 -0
- package/src/analyze/javascript/detectors/analytics-source.js +119 -0
- package/src/analyze/javascript/detectors/index.js +10 -0
- package/src/analyze/javascript/extractors/event-extractor.js +179 -0
- package/src/analyze/javascript/extractors/index.js +13 -0
- package/src/analyze/javascript/extractors/property-extractor.js +172 -0
- package/src/analyze/javascript/index.js +38 -0
- package/src/analyze/javascript/parser.js +126 -0
- package/src/analyze/javascript/utils/function-finder.js +123 -0
- package/src/analyze/python/index.js +111 -0
- package/src/analyze/python/pythonTrackingAnalyzer.py +814 -0
- package/src/analyze/ruby/detectors.js +46 -0
- package/src/analyze/ruby/extractors.js +258 -0
- package/src/analyze/ruby/index.js +51 -0
- package/src/analyze/ruby/traversal.js +123 -0
- package/src/analyze/ruby/types.js +30 -0
- package/src/analyze/ruby/visitor.js +66 -0
- package/src/analyze/typescript/constants.js +109 -0
- package/src/analyze/typescript/detectors/analytics-source.js +120 -0
- package/src/analyze/typescript/detectors/index.js +10 -0
- package/src/analyze/typescript/extractors/event-extractor.js +269 -0
- package/src/analyze/typescript/extractors/index.js +14 -0
- package/src/analyze/typescript/extractors/property-extractor.js +395 -0
- package/src/analyze/typescript/index.js +48 -0
- package/src/analyze/typescript/parser.js +131 -0
- package/src/analyze/typescript/utils/function-finder.js +114 -0
- package/src/analyze/typescript/utils/type-resolver.js +193 -0
- package/src/generateDescriptions/index.js +81 -0
- package/src/generateDescriptions/llmUtils.js +33 -0
- package/src/generateDescriptions/promptUtils.js +62 -0
- package/src/generateDescriptions/schemaUtils.js +61 -0
- package/src/index.js +7 -2
- package/src/{fileProcessor.js → utils/fileProcessor.js} +5 -0
- package/src/{repoDetails.js → utils/repoDetails.js} +5 -0
- package/src/{yamlGenerator.js → utils/yamlGenerator.js} +5 -0
- package/src/analyze/analyzeGoFile.js +0 -1164
- package/src/analyze/analyzeJsFile.js +0 -87
- package/src/analyze/analyzePythonFile.js +0 -42
- package/src/analyze/analyzeRubyFile.js +0 -419
- package/src/analyze/analyzeTsFile.js +0 -192
- package/src/analyze/go2json.js +0 -1069
- package/src/analyze/helpers.js +0 -656
- package/src/analyze/pythonTrackingAnalyzer.py +0 -541
- package/src/generateDescriptions.js +0 -196
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
const { OPERATOR, PRIMTYPES } = require('./constants');
|
|
2
|
+
const { tillNestEndImpl, splitTokensBy } = require('./utils');
|
|
3
|
+
const { parseType, parseArgs, parseRetTypes, setExpressionParser } = require('./typeParser');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parse an expression from tokens
|
|
7
|
+
* @param {Array<Object>} toks - Tokens to parse
|
|
8
|
+
* @returns {Object} AST node representing the expression
|
|
9
|
+
*/
|
|
10
|
+
function parseExpr(toks) {
|
|
11
|
+
let i;
|
|
12
|
+
let lvl = 0;
|
|
13
|
+
|
|
14
|
+
// Check for variable declaration (:=)
|
|
15
|
+
for (i = 0; i < toks.length; i++) {
|
|
16
|
+
if ("{[(".includes(toks[i].value)) {
|
|
17
|
+
lvl++;
|
|
18
|
+
}
|
|
19
|
+
if (")]}".includes(toks[i].value)) {
|
|
20
|
+
lvl--;
|
|
21
|
+
}
|
|
22
|
+
if (toks[i].value == ":=") {
|
|
23
|
+
if (lvl != 0) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
tag: "declare",
|
|
28
|
+
names: splitTokensBy(toks.slice(0, i), ",").map(x => x[0].value),
|
|
29
|
+
value: parseExpr(toks.slice(i + 1)),
|
|
30
|
+
type: { tag: "auto" },
|
|
31
|
+
isconst: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check for assignment (=)
|
|
37
|
+
lvl = 0;
|
|
38
|
+
for (i = 0; i < toks.length; i++) {
|
|
39
|
+
if ("{[(".includes(toks[i].value)) {
|
|
40
|
+
lvl++;
|
|
41
|
+
}
|
|
42
|
+
if (")]}".includes(toks[i].value)) {
|
|
43
|
+
lvl--;
|
|
44
|
+
}
|
|
45
|
+
if (toks[i].value == "=") {
|
|
46
|
+
if (lvl != 0) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
tag: "assign",
|
|
51
|
+
lhs: parseExpr(toks.slice(0, i)),
|
|
52
|
+
rhs: parseExpr(toks.slice(i + 1)),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for comma-separated expressions (tuple)
|
|
58
|
+
let groups = splitTokensBy(toks, ",");
|
|
59
|
+
if (groups.length > 1) {
|
|
60
|
+
return {
|
|
61
|
+
tag: "tuple",
|
|
62
|
+
items: groups.map(parseExpr),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Parse individual expression components
|
|
67
|
+
let body = [];
|
|
68
|
+
i = 0;
|
|
69
|
+
|
|
70
|
+
while (i < toks.length) {
|
|
71
|
+
function tillNestEnd(l, r) {
|
|
72
|
+
let [j, tk] = tillNestEndImpl(toks, i, l, r);
|
|
73
|
+
i = j;
|
|
74
|
+
return tk;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (toks[i].value == "(") {
|
|
78
|
+
// Handle parentheses
|
|
79
|
+
if (i > 0 && (toks[i - 1].tag == "ident") && (toks[i - 1].value == "make") && (body.length < 2 || body[body.length - 2].tag != "access")) {
|
|
80
|
+
// make() function
|
|
81
|
+
i++;
|
|
82
|
+
body.pop();
|
|
83
|
+
let args = splitTokensBy(tillNestEnd("(", ")"), ",");
|
|
84
|
+
body.push({
|
|
85
|
+
tag: "alloc",
|
|
86
|
+
type: parseType(args[0]),
|
|
87
|
+
size: args[1] ? parseExpr(args[1]) : null,
|
|
88
|
+
});
|
|
89
|
+
i++;
|
|
90
|
+
} else if (i > 0 && (toks[i - 1].tag == "ident" || toks[i - 1].value == "]" || toks[i - 1].value == "}" || toks[i - 1].value == ")")) {
|
|
91
|
+
// Function call or type cast
|
|
92
|
+
let fun = body.pop();
|
|
93
|
+
if (fun.tag == "ident" && PRIMTYPES.includes(fun.value)) {
|
|
94
|
+
// Type cast
|
|
95
|
+
i++;
|
|
96
|
+
body.push({
|
|
97
|
+
tag: "cast",
|
|
98
|
+
type: { tag: fun.value },
|
|
99
|
+
value: parseExpr(tillNestEnd("(", ")"))
|
|
100
|
+
});
|
|
101
|
+
i++;
|
|
102
|
+
} else {
|
|
103
|
+
// Function call
|
|
104
|
+
i++;
|
|
105
|
+
let startToken = fun.line ? fun : (toks[i - 1] || {});
|
|
106
|
+
body.push({
|
|
107
|
+
tag: "call",
|
|
108
|
+
func: fun,
|
|
109
|
+
args: splitTokensBy(tillNestEnd("(", ")"), ",").map(parseExpr),
|
|
110
|
+
line: startToken.line,
|
|
111
|
+
col: startToken.col
|
|
112
|
+
});
|
|
113
|
+
i++;
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
// Parenthesized expression
|
|
117
|
+
i++;
|
|
118
|
+
body.push(parseExpr(tillNestEnd("(", ")")));
|
|
119
|
+
i++;
|
|
120
|
+
}
|
|
121
|
+
} else if (toks[i].value == ".") {
|
|
122
|
+
// Member access
|
|
123
|
+
i++;
|
|
124
|
+
body.push({
|
|
125
|
+
tag: "access",
|
|
126
|
+
struct: body.pop(),
|
|
127
|
+
member: toks[i].value,
|
|
128
|
+
});
|
|
129
|
+
i++;
|
|
130
|
+
} else if (toks[i].value == "[") {
|
|
131
|
+
// Indexing or array literal
|
|
132
|
+
if (i > 0 && (toks[i - 1].tag == "ident" || toks[i - 1].value == "]")) {
|
|
133
|
+
// Indexing or slicing
|
|
134
|
+
i++;
|
|
135
|
+
let idx = tillNestEnd("[", "]");
|
|
136
|
+
let idc = splitTokensBy(idx, ":");
|
|
137
|
+
if (idc.length == 1) {
|
|
138
|
+
body.push({
|
|
139
|
+
tag: "index",
|
|
140
|
+
container: body.pop(),
|
|
141
|
+
index: parseExpr(idx),
|
|
142
|
+
});
|
|
143
|
+
} else {
|
|
144
|
+
body.push({
|
|
145
|
+
tag: "slice",
|
|
146
|
+
container: body.pop(),
|
|
147
|
+
lo: parseExpr(idc[0]),
|
|
148
|
+
hi: parseExpr(idc[1]),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
i++;
|
|
152
|
+
} else {
|
|
153
|
+
// Array literal
|
|
154
|
+
i++;
|
|
155
|
+
let size = tillNestEnd("[", "]");
|
|
156
|
+
let mode = 0;
|
|
157
|
+
for (var j = 0; j < toks.length; j++) {
|
|
158
|
+
if (toks[j].value == "{") {
|
|
159
|
+
mode = 0;
|
|
160
|
+
break;
|
|
161
|
+
} else if (toks[j].value == "(") {
|
|
162
|
+
mode = 1;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (mode == 0) {
|
|
167
|
+
let lit = {
|
|
168
|
+
tag: "arraylit",
|
|
169
|
+
size: parseExpr(size),
|
|
170
|
+
};
|
|
171
|
+
i++;
|
|
172
|
+
lit.type = parseType(tillNestEnd("}", "{"));
|
|
173
|
+
i++;
|
|
174
|
+
lit.items = splitTokensBy(tillNestEnd("{", "}"), ",");
|
|
175
|
+
body.push(lit);
|
|
176
|
+
i++;
|
|
177
|
+
} else {
|
|
178
|
+
i++;
|
|
179
|
+
let type = tillNestEnd(")", "(");
|
|
180
|
+
type = parseType(type);
|
|
181
|
+
i++;
|
|
182
|
+
let val = parseExpr(tillNestEnd("(", ")"));
|
|
183
|
+
body.push({
|
|
184
|
+
tag: "cast",
|
|
185
|
+
type: { tag: "array", size: parseExpr(size), item: type },
|
|
186
|
+
value: val
|
|
187
|
+
});
|
|
188
|
+
i++;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} else if (toks[i].value == "{") {
|
|
192
|
+
// Struct literal
|
|
193
|
+
i++;
|
|
194
|
+
let cont = tillNestEnd("{", "}");
|
|
195
|
+
cont = splitTokensBy(cont, ",");
|
|
196
|
+
let fields = [];
|
|
197
|
+
for (let j = 0; j < cont.length; j++) {
|
|
198
|
+
// Fix: Don't split by colon - check the structure of the field instead
|
|
199
|
+
let fieldTokens = cont[j];
|
|
200
|
+
|
|
201
|
+
// Check if this is a key:value field (field name followed by colon)
|
|
202
|
+
let colonIndex = -1;
|
|
203
|
+
let lvl = 0;
|
|
204
|
+
for (let k = 0; k < fieldTokens.length; k++) {
|
|
205
|
+
if ("{[(".includes(fieldTokens[k].value)) {
|
|
206
|
+
lvl++;
|
|
207
|
+
} else if (")]}".includes(fieldTokens[k].value)) {
|
|
208
|
+
lvl--;
|
|
209
|
+
} else if (fieldTokens[k].value === ":" && lvl === 0) {
|
|
210
|
+
colonIndex = k;
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (colonIndex > 0 && fieldTokens[colonIndex - 1].tag === "ident") {
|
|
216
|
+
// Named field: name: value
|
|
217
|
+
let name = fieldTokens[colonIndex - 1].value;
|
|
218
|
+
let valueTokens = fieldTokens.slice(colonIndex + 1);
|
|
219
|
+
fields.push({ name: name, value: parseExpr(valueTokens) });
|
|
220
|
+
} else {
|
|
221
|
+
// Unnamed field or map literal field
|
|
222
|
+
fields.push({ name: null, value: parseExpr(fieldTokens) });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
let structType = body.pop();
|
|
226
|
+
let startToken = structType.line ? structType : (toks[i - 1] || {});
|
|
227
|
+
body.push({
|
|
228
|
+
tag: "structlit",
|
|
229
|
+
struct: structType,
|
|
230
|
+
fields,
|
|
231
|
+
line: startToken.line,
|
|
232
|
+
col: startToken.col
|
|
233
|
+
});
|
|
234
|
+
i++;
|
|
235
|
+
} else if (toks[i].tag == "sigil" && OPERATOR.includes(toks[i].value)) {
|
|
236
|
+
// Operator
|
|
237
|
+
body.push({ tag: "op", value: toks[i].value });
|
|
238
|
+
i++;
|
|
239
|
+
} else if (toks[i].tag == "newline") {
|
|
240
|
+
// Skip newlines
|
|
241
|
+
i++;
|
|
242
|
+
} else if (toks[i].value == "func") {
|
|
243
|
+
// Lambda function
|
|
244
|
+
let stmt = { tag: "lambda" };
|
|
245
|
+
i++;
|
|
246
|
+
i++;
|
|
247
|
+
let args = tillNestEnd("(", ")");
|
|
248
|
+
args = parseArgs(args);
|
|
249
|
+
stmt.args = args;
|
|
250
|
+
i++;
|
|
251
|
+
stmt.returns = [];
|
|
252
|
+
if (toks[i].value != "{") {
|
|
253
|
+
if (toks[i].value != "(") {
|
|
254
|
+
stmt.returns.push({ name: null, type: parseType(tillNestEnd("}", "{")) });
|
|
255
|
+
} else {
|
|
256
|
+
i++;
|
|
257
|
+
stmt.returns = parseRetTypes(tillNestEnd("(", ")"));
|
|
258
|
+
i++;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
i++;
|
|
262
|
+
const { parseTokensToAst } = require('./statementParser');
|
|
263
|
+
stmt.body = parseTokensToAst(tillNestEnd("{", "}"));
|
|
264
|
+
body.push(stmt);
|
|
265
|
+
i++;
|
|
266
|
+
} else {
|
|
267
|
+
// Regular token
|
|
268
|
+
body.push(toks[i]);
|
|
269
|
+
i++;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return { tag: "expr", body };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Set the expression parser in type parser to avoid circular dependency
|
|
277
|
+
setExpressionParser(parseExpr);
|
|
278
|
+
|
|
279
|
+
module.exports = {
|
|
280
|
+
parseExpr
|
|
281
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const { tokenize } = require('./tokenizer');
|
|
2
|
+
const { parseTokensToAst } = require('./statementParser');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Go AST Parser class
|
|
6
|
+
* Provides methods to parse Go source code into an Abstract Syntax Tree
|
|
7
|
+
*/
|
|
8
|
+
class GoAstParser {
|
|
9
|
+
/**
|
|
10
|
+
* Parse Go source code to AST
|
|
11
|
+
* @param {string} src - Go source code
|
|
12
|
+
* @returns {Array<Object>} AST nodes representing the parsed Go code
|
|
13
|
+
*/
|
|
14
|
+
parse(src) {
|
|
15
|
+
const tokens = this.tokenize(src);
|
|
16
|
+
const tree = this.parseTokens(tokens);
|
|
17
|
+
return tree;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Tokenize Go source code
|
|
22
|
+
* @param {string} src - Go source code
|
|
23
|
+
* @returns {Array<Object>} Array of token objects
|
|
24
|
+
*/
|
|
25
|
+
tokenize(src) {
|
|
26
|
+
return tokenize(src);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Parse tokens to AST
|
|
31
|
+
* @param {Array<Object>} tokens - Array of token objects
|
|
32
|
+
* @returns {Array<Object>} AST nodes representing the parsed Go code
|
|
33
|
+
*/
|
|
34
|
+
parseTokens(tokens) {
|
|
35
|
+
return parseTokensToAst(tokens);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Extract Go Abstract Syntax Tree from source code
|
|
41
|
+
* @param {string} src - Go source code
|
|
42
|
+
* @returns {Array<Object>} AST nodes representing the parsed Go code
|
|
43
|
+
*/
|
|
44
|
+
function extractGoAST(src) {
|
|
45
|
+
const parser = new GoAstParser();
|
|
46
|
+
return parser.parse(src);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
GoAstParser,
|
|
51
|
+
extractGoAST
|
|
52
|
+
};
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
const { tillNestEndImpl, splitTokensBy, getToken, tokensHasValue } = require('./utils');
|
|
2
|
+
const { parseExpr } = require('./expressionParser');
|
|
3
|
+
const { parseType, parseArgs, parseRetTypes, parseFuncSig } = require('./typeParser');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Parse tokens into AST statements
|
|
7
|
+
* @param {Array<Object>} tokens - Array of token objects
|
|
8
|
+
* @returns {Array<Object>} AST nodes representing the parsed Go code
|
|
9
|
+
*/
|
|
10
|
+
function parseTokensToAst(tokens) {
|
|
11
|
+
let tree = [];
|
|
12
|
+
let i = 0;
|
|
13
|
+
|
|
14
|
+
while (i < tokens.length) {
|
|
15
|
+
function gtok(idx) {
|
|
16
|
+
return getToken(tokens, idx);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function tillStmtEnd() {
|
|
20
|
+
let toks = [];
|
|
21
|
+
let lvl = 0;
|
|
22
|
+
while (true) {
|
|
23
|
+
if (i >= tokens.length) {
|
|
24
|
+
return toks;
|
|
25
|
+
}
|
|
26
|
+
if (gtok(i).tag == "sigil" && gtok(i).value == ";") {
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
if ("[{(".includes(gtok(i).value)) {
|
|
30
|
+
lvl++;
|
|
31
|
+
} else if (")}]".includes(gtok(i).value)) {
|
|
32
|
+
lvl--;
|
|
33
|
+
} else if (gtok(i).tag == "newline") {
|
|
34
|
+
if (lvl == 0) {
|
|
35
|
+
if (gtok(i - 1).tag != "sigil" ||
|
|
36
|
+
gtok(i - 1).value == ";" ||
|
|
37
|
+
gtok(i - 1).value == "++" ||
|
|
38
|
+
gtok(i - 1).value == "--" ||
|
|
39
|
+
"}])".includes(gtok(i - 1).value)) {
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
toks.push(tokens[i]);
|
|
45
|
+
i++;
|
|
46
|
+
}
|
|
47
|
+
return toks;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function tillNestEnd(l, r) {
|
|
51
|
+
let [j, tk] = tillNestEndImpl(tokens, i, l, r);
|
|
52
|
+
i = j;
|
|
53
|
+
return tk;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Package declaration
|
|
57
|
+
if (gtok(i).tag == "ident" && gtok(i).value == "package") {
|
|
58
|
+
tree.push({ tag: "package", name: gtok(i + 1).value });
|
|
59
|
+
i += 2;
|
|
60
|
+
}
|
|
61
|
+
// Type declarations
|
|
62
|
+
else if (gtok(i).tag == "ident" && gtok(i).value == "type") {
|
|
63
|
+
if (gtok(i + 2).value == "struct") {
|
|
64
|
+
let stmt = { tag: "typedef", name: gtok(i + 1).value, fields: [], embeds: [] };
|
|
65
|
+
i += 4;
|
|
66
|
+
let lns = splitTokensBy(tillNestEnd("{", "}"), "\n", ";");
|
|
67
|
+
for (let j = 0; j < lns.length; j++) {
|
|
68
|
+
let names = splitTokensBy(lns[j], ",");
|
|
69
|
+
if (!names.length) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (names.length == 1 && names[0].length == 1) {
|
|
73
|
+
stmt.embeds.push(names[0][0].value);
|
|
74
|
+
} else {
|
|
75
|
+
let type = names[names.length - 1].slice(1);
|
|
76
|
+
type = parseType(type);
|
|
77
|
+
for (let k = 0; k < names.length; k++) {
|
|
78
|
+
stmt.fields.push({ name: names[k][0].value, type });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
i++;
|
|
83
|
+
tree.push(stmt);
|
|
84
|
+
} else if (gtok(i + 2).value == "interface") {
|
|
85
|
+
let stmt = { tag: "interface", name: gtok(i + 1).value, methods: [] };
|
|
86
|
+
i += 4;
|
|
87
|
+
let lns = splitTokensBy(tillNestEnd("{", "}"), "\n", ";");
|
|
88
|
+
for (let j = 0; j < lns.length; j++) {
|
|
89
|
+
let name = lns[j][0];
|
|
90
|
+
if (!name) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
name = name.value;
|
|
94
|
+
let sig = Object.assign({ name }, parseFuncSig(lns[j].slice(1)));
|
|
95
|
+
stmt.methods.push(sig);
|
|
96
|
+
}
|
|
97
|
+
tree.push(stmt);
|
|
98
|
+
i++;
|
|
99
|
+
} else {
|
|
100
|
+
let stmt = { tag: "typealias", name: gtok(i + 1).value };
|
|
101
|
+
i += 2;
|
|
102
|
+
let typ = tillStmtEnd();
|
|
103
|
+
stmt.value = parseType(typ);
|
|
104
|
+
tree.push(stmt);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Function declarations
|
|
108
|
+
else if (gtok(i).tag == "ident" && gtok(i).value == "func") {
|
|
109
|
+
let stmt = { tag: "func", receiver: null };
|
|
110
|
+
if (gtok(i + 1).value == "(") {
|
|
111
|
+
stmt.receiver = { name: gtok(i + 2).value };
|
|
112
|
+
i += 3;
|
|
113
|
+
stmt.receiver.type = parseType(tillNestEnd("(", ")"));
|
|
114
|
+
}
|
|
115
|
+
i++;
|
|
116
|
+
stmt.name = gtok(i).value;
|
|
117
|
+
i += 2;
|
|
118
|
+
let args = tillNestEnd("(", ")");
|
|
119
|
+
args = parseArgs(args);
|
|
120
|
+
stmt.args = args;
|
|
121
|
+
i++;
|
|
122
|
+
stmt.returns = [];
|
|
123
|
+
if (gtok(i).value != "{") {
|
|
124
|
+
if (gtok(i).value != "(") {
|
|
125
|
+
stmt.returns.push({ name: null, type: parseType(tillNestEnd("}", "{")) });
|
|
126
|
+
} else {
|
|
127
|
+
i++;
|
|
128
|
+
stmt.returns = parseRetTypes(tillNestEnd("(", ")"));
|
|
129
|
+
i++;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
i++;
|
|
133
|
+
stmt.body = parseTokensToAst(tillNestEnd("{", "}"));
|
|
134
|
+
tree.push(stmt);
|
|
135
|
+
i++;
|
|
136
|
+
}
|
|
137
|
+
// If statements
|
|
138
|
+
else if (gtok(i).tag == "ident" && gtok(i).value == "if") {
|
|
139
|
+
let stmt = { tag: "if" };
|
|
140
|
+
i += 1;
|
|
141
|
+
let cond = tillNestEnd("}", "{");
|
|
142
|
+
let conds = splitTokensBy(cond, ";");
|
|
143
|
+
stmt.prepare = conds.slice(0, -1).map(parseExpr);
|
|
144
|
+
stmt.condition = parseExpr(conds[conds.length - 1]);
|
|
145
|
+
i++;
|
|
146
|
+
stmt.body = parseTokensToAst(tillNestEnd("{", "}"));
|
|
147
|
+
tree.push(stmt);
|
|
148
|
+
i++;
|
|
149
|
+
}
|
|
150
|
+
// Else statements
|
|
151
|
+
else if (gtok(i).tag == "ident" && gtok(i).value == "else") {
|
|
152
|
+
if (gtok(i + 1).tag == "ident" && gtok(i + 1).value == "if") {
|
|
153
|
+
let stmt = { tag: "elseif" };
|
|
154
|
+
i += 2;
|
|
155
|
+
let cond = tillNestEnd("}", "{");
|
|
156
|
+
stmt.condition = parseExpr(cond);
|
|
157
|
+
i++;
|
|
158
|
+
stmt.body = parseTokensToAst(tillNestEnd("{", "}"));
|
|
159
|
+
tree.push(stmt);
|
|
160
|
+
i++;
|
|
161
|
+
} else {
|
|
162
|
+
let stmt = { tag: "else" };
|
|
163
|
+
i++;
|
|
164
|
+
i++;
|
|
165
|
+
stmt.body = parseTokensToAst(tillNestEnd("{", "}"));
|
|
166
|
+
tree.push(stmt);
|
|
167
|
+
i++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// For loops
|
|
171
|
+
else if (gtok(i).tag == "ident" && gtok(i).value == "for") {
|
|
172
|
+
let stmt = { tag: "for" };
|
|
173
|
+
i += 1;
|
|
174
|
+
let head = tillNestEnd("}", "{");
|
|
175
|
+
if (tokensHasValue(head, "range")) {
|
|
176
|
+
stmt.tag = "foreach";
|
|
177
|
+
let [lhs, rhs] = splitTokensBy(head, ":=");
|
|
178
|
+
stmt.names = splitTokensBy(lhs, ",").map(x => x[0].value);
|
|
179
|
+
stmt.container = parseExpr(rhs.slice(1));
|
|
180
|
+
i++;
|
|
181
|
+
} else {
|
|
182
|
+
stmt.headers = splitTokensBy(head, ";").map(parseExpr);
|
|
183
|
+
i++;
|
|
184
|
+
}
|
|
185
|
+
stmt.body = parseTokensToAst(tillNestEnd("{", "}"));
|
|
186
|
+
tree.push(stmt);
|
|
187
|
+
i++;
|
|
188
|
+
}
|
|
189
|
+
// Variable/constant declarations
|
|
190
|
+
else if (gtok(i).tag == "ident" && (gtok(i).value == "var" || gtok(i).value == "const")) {
|
|
191
|
+
let isconst = (gtok(i).value == "const");
|
|
192
|
+
if (gtok(i + 1).value == "(") {
|
|
193
|
+
// Block declarations
|
|
194
|
+
i++;
|
|
195
|
+
let toks = tillStmtEnd().slice(1, -1);
|
|
196
|
+
let lns = splitTokensBy(toks, "\n", ";").filter(x => x.length);
|
|
197
|
+
let lastval = null;
|
|
198
|
+
let iota = 0;
|
|
199
|
+
|
|
200
|
+
function inferval(rhs) {
|
|
201
|
+
if (rhs == null) {
|
|
202
|
+
if (lastval) {
|
|
203
|
+
rhs = lastval.slice();
|
|
204
|
+
} else {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
lastval = rhs.slice();
|
|
209
|
+
let diduseiota = false;
|
|
210
|
+
for (let j = 0; j < rhs.length; j++) {
|
|
211
|
+
if (rhs[j].value == "iota") {
|
|
212
|
+
rhs[j] = { tag: "number", value: `${iota}` };
|
|
213
|
+
diduseiota = true;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (diduseiota) {
|
|
217
|
+
iota++;
|
|
218
|
+
}
|
|
219
|
+
return parseExpr(rhs);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
for (let j = 0; j < lns.length; j++) {
|
|
223
|
+
let sides = splitTokensBy(lns[j], "=");
|
|
224
|
+
let lhs = sides[0];
|
|
225
|
+
let type = lhs.slice(1);
|
|
226
|
+
if (type.length) {
|
|
227
|
+
type = parseType(type);
|
|
228
|
+
} else {
|
|
229
|
+
type = null;
|
|
230
|
+
}
|
|
231
|
+
let rhs = sides[1];
|
|
232
|
+
let stmt = {
|
|
233
|
+
tag: "declare",
|
|
234
|
+
names: [lhs[0].value],
|
|
235
|
+
value: inferval(rhs),
|
|
236
|
+
type: type || { tag: "auto" },
|
|
237
|
+
isconst,
|
|
238
|
+
};
|
|
239
|
+
tree.push(stmt);
|
|
240
|
+
}
|
|
241
|
+
i++;
|
|
242
|
+
} else {
|
|
243
|
+
// Single declaration
|
|
244
|
+
i++;
|
|
245
|
+
let toks = tillStmtEnd();
|
|
246
|
+
let sides = splitTokensBy(toks, "=");
|
|
247
|
+
let lhs = sides[0];
|
|
248
|
+
let rhs = sides[1];
|
|
249
|
+
let names = splitTokensBy(lhs, ",");
|
|
250
|
+
let type = names[names.length - 1].slice(1);
|
|
251
|
+
if (type) {
|
|
252
|
+
type = parseType(type);
|
|
253
|
+
}
|
|
254
|
+
names = names.map(x => x[0].value);
|
|
255
|
+
let stmt = {
|
|
256
|
+
tag: "declare",
|
|
257
|
+
names,
|
|
258
|
+
value: (!rhs) ? null : parseExpr(rhs),
|
|
259
|
+
type: type || { tag: "auto" },
|
|
260
|
+
isconst,
|
|
261
|
+
};
|
|
262
|
+
tree.push(stmt);
|
|
263
|
+
i++;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
// Switch statements
|
|
267
|
+
else if (gtok(i).tag == "ident" && (gtok(i).value == "switch")) {
|
|
268
|
+
let stmt = { tag: "switch" };
|
|
269
|
+
i += 1;
|
|
270
|
+
let cond = tillNestEnd("}", "{");
|
|
271
|
+
stmt.condition = parseExpr(cond);
|
|
272
|
+
i++;
|
|
273
|
+
let body = tillNestEnd("{", "}");
|
|
274
|
+
stmt.cases = [];
|
|
275
|
+
let j = 0;
|
|
276
|
+
let s0 = null;
|
|
277
|
+
let s1 = null;
|
|
278
|
+
while (j < body.length) {
|
|
279
|
+
if (body[j].value == "case" || body[j].value == "default") {
|
|
280
|
+
if (s0 == null) {
|
|
281
|
+
s1 = null;
|
|
282
|
+
s0 = [];
|
|
283
|
+
} else {
|
|
284
|
+
if (s0.length) {
|
|
285
|
+
stmt.cases.push({
|
|
286
|
+
tag: "case",
|
|
287
|
+
condition: parseExpr(s0),
|
|
288
|
+
body: parseTokensToAst(s1),
|
|
289
|
+
});
|
|
290
|
+
} else {
|
|
291
|
+
stmt.cases.push({
|
|
292
|
+
tag: "default",
|
|
293
|
+
body: parseTokensToAst(s1),
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
s0 = [];
|
|
297
|
+
s1 = null;
|
|
298
|
+
}
|
|
299
|
+
} else if (body[j].value == ":") {
|
|
300
|
+
s1 = [];
|
|
301
|
+
} else {
|
|
302
|
+
if (s1 != null) {
|
|
303
|
+
s1.push(body[j]);
|
|
304
|
+
} else if (s0 != null) {
|
|
305
|
+
s0.push(body[j]);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
j++;
|
|
309
|
+
}
|
|
310
|
+
if (s0 != null) {
|
|
311
|
+
if (s0.length) {
|
|
312
|
+
stmt.cases.push({
|
|
313
|
+
tag: "case",
|
|
314
|
+
condition: parseExpr(s0),
|
|
315
|
+
body: parseTokensToAst(s1),
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
stmt.cases.push({
|
|
319
|
+
tag: "default",
|
|
320
|
+
body: parseTokensToAst(s1),
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
tree.push(stmt);
|
|
325
|
+
i++;
|
|
326
|
+
}
|
|
327
|
+
// Newlines
|
|
328
|
+
else if (gtok(i).tag == "newline") {
|
|
329
|
+
i++;
|
|
330
|
+
}
|
|
331
|
+
// Comments
|
|
332
|
+
else if (gtok(i).tag == "comment") {
|
|
333
|
+
tree.push({ tag: "comment", text: gtok(i).value });
|
|
334
|
+
i++;
|
|
335
|
+
}
|
|
336
|
+
// Return statements
|
|
337
|
+
else if (gtok(i).value == "return") {
|
|
338
|
+
i++;
|
|
339
|
+
tree.push({ tag: "return", value: parseExpr(tillStmtEnd()) });
|
|
340
|
+
i++;
|
|
341
|
+
}
|
|
342
|
+
// Import statements
|
|
343
|
+
else if (gtok(i).value == "import") {
|
|
344
|
+
i++;
|
|
345
|
+
let imps = tillStmtEnd();
|
|
346
|
+
for (let j = 0; j < imps.length - 1; j++) {
|
|
347
|
+
if (imps[j].tag == "string") {
|
|
348
|
+
tree.push({ tag: "import", value: imps[j].value });
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
i++;
|
|
352
|
+
}
|
|
353
|
+
// Go statements
|
|
354
|
+
else if (gtok(i).value == "go") {
|
|
355
|
+
i++;
|
|
356
|
+
let e = tillStmtEnd();
|
|
357
|
+
tree.push({ tag: "invoke", func: parseExpr(e) });
|
|
358
|
+
i++;
|
|
359
|
+
}
|
|
360
|
+
// Defer statements
|
|
361
|
+
else if (gtok(i).value == "defer") {
|
|
362
|
+
i++;
|
|
363
|
+
let e = tillStmtEnd();
|
|
364
|
+
tree.push({ tag: "defer", expr: parseExpr(e) });
|
|
365
|
+
i++;
|
|
366
|
+
}
|
|
367
|
+
// Expression statements
|
|
368
|
+
else {
|
|
369
|
+
let toks = tillStmtEnd();
|
|
370
|
+
let stmt = parseExpr(toks);
|
|
371
|
+
if (stmt.tag == "expr") {
|
|
372
|
+
stmt = {
|
|
373
|
+
tag: "exec",
|
|
374
|
+
expr: stmt,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
tree.push(stmt);
|
|
378
|
+
i++;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return tree;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
module.exports = {
|
|
386
|
+
parseTokensToAst
|
|
387
|
+
};
|