@flisk/analyze-tracking 0.7.1 → 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.
Files changed (71) hide show
  1. package/README.md +35 -61
  2. package/bin/cli.js +1 -1
  3. package/package.json +18 -3
  4. package/src/analyze/go/astTraversal.js +121 -0
  5. package/src/analyze/go/constants.js +20 -0
  6. package/src/analyze/go/eventDeduplicator.js +47 -0
  7. package/src/analyze/go/eventExtractor.js +156 -0
  8. package/src/analyze/go/goAstParser/constants.js +39 -0
  9. package/src/analyze/go/goAstParser/expressionParser.js +281 -0
  10. package/src/analyze/go/goAstParser/index.js +52 -0
  11. package/src/analyze/go/goAstParser/statementParser.js +387 -0
  12. package/src/analyze/go/goAstParser/tokenizer.js +196 -0
  13. package/src/analyze/go/goAstParser/typeParser.js +202 -0
  14. package/src/analyze/go/goAstParser/utils.js +99 -0
  15. package/src/analyze/go/index.js +55 -0
  16. package/src/analyze/go/propertyExtractor.js +670 -0
  17. package/src/analyze/go/trackingDetector.js +71 -0
  18. package/src/analyze/go/trackingExtractor.js +54 -0
  19. package/src/analyze/go/typeContext.js +88 -0
  20. package/src/analyze/go/utils.js +215 -0
  21. package/src/analyze/index.js +11 -7
  22. package/src/analyze/javascript/constants.js +115 -0
  23. package/src/analyze/javascript/detectors/analytics-source.js +119 -0
  24. package/src/analyze/javascript/detectors/index.js +10 -0
  25. package/src/analyze/javascript/extractors/event-extractor.js +179 -0
  26. package/src/analyze/javascript/extractors/index.js +13 -0
  27. package/src/analyze/javascript/extractors/property-extractor.js +172 -0
  28. package/src/analyze/javascript/index.js +38 -0
  29. package/src/analyze/javascript/parser.js +126 -0
  30. package/src/analyze/javascript/utils/function-finder.js +123 -0
  31. package/src/analyze/python/index.js +111 -0
  32. package/src/analyze/python/pythonTrackingAnalyzer.py +814 -0
  33. package/src/analyze/ruby/detectors.js +46 -0
  34. package/src/analyze/ruby/extractors.js +258 -0
  35. package/src/analyze/ruby/index.js +51 -0
  36. package/src/analyze/ruby/traversal.js +123 -0
  37. package/src/analyze/ruby/types.js +30 -0
  38. package/src/analyze/ruby/visitor.js +66 -0
  39. package/src/analyze/typescript/constants.js +109 -0
  40. package/src/analyze/typescript/detectors/analytics-source.js +120 -0
  41. package/src/analyze/typescript/detectors/index.js +10 -0
  42. package/src/analyze/typescript/extractors/event-extractor.js +269 -0
  43. package/src/analyze/typescript/extractors/index.js +14 -0
  44. package/src/analyze/typescript/extractors/property-extractor.js +395 -0
  45. package/src/analyze/typescript/index.js +48 -0
  46. package/src/analyze/typescript/parser.js +131 -0
  47. package/src/analyze/typescript/utils/function-finder.js +114 -0
  48. package/src/analyze/typescript/utils/type-resolver.js +193 -0
  49. package/src/generateDescriptions/index.js +81 -0
  50. package/src/generateDescriptions/llmUtils.js +33 -0
  51. package/src/generateDescriptions/promptUtils.js +62 -0
  52. package/src/generateDescriptions/schemaUtils.js +61 -0
  53. package/src/index.js +7 -2
  54. package/src/{fileProcessor.js → utils/fileProcessor.js} +5 -0
  55. package/src/{repoDetails.js → utils/repoDetails.js} +5 -0
  56. package/src/{yamlGenerator.js → utils/yamlGenerator.js} +5 -0
  57. package/.github/workflows/npm-publish.yml +0 -33
  58. package/.github/workflows/pr-check.yml +0 -17
  59. package/jest.config.js +0 -7
  60. package/src/analyze/analyzeGoFile.js +0 -1164
  61. package/src/analyze/analyzeJsFile.js +0 -72
  62. package/src/analyze/analyzePythonFile.js +0 -41
  63. package/src/analyze/analyzeRubyFile.js +0 -409
  64. package/src/analyze/analyzeTsFile.js +0 -69
  65. package/src/analyze/go2json.js +0 -1069
  66. package/src/analyze/helpers.js +0 -217
  67. package/src/analyze/pythonTrackingAnalyzer.py +0 -439
  68. package/src/generateDescriptions.js +0 -196
  69. package/tests/detectSource.test.js +0 -20
  70. package/tests/extractProperties.test.js +0 -109
  71. package/tests/findWrappingFunction.test.js +0 -30
@@ -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
+ };