@minduscript/compiler 1.0.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.
Files changed (42) hide show
  1. package/.npmignore +2 -0
  2. package/dist/asm/gen-asm.d.ts +2 -0
  3. package/dist/asm/gen-asm.js +432 -0
  4. package/dist/counter.d.ts +5 -0
  5. package/dist/counter.js +13 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +17 -0
  8. package/dist/ir/build-ir.d.ts +3 -0
  9. package/dist/ir/build-ir.js +1057 -0
  10. package/dist/ir/gather-macros.d.ts +8 -0
  11. package/dist/ir/gather-macros.js +204 -0
  12. package/dist/ir/ir.d.ts +478 -0
  13. package/dist/ir/ir.js +120 -0
  14. package/dist/ir/project.d.ts +24 -0
  15. package/dist/ir/project.js +339 -0
  16. package/dist/ir/symbol-table.d.ts +19 -0
  17. package/dist/ir/symbol-table.js +385 -0
  18. package/dist/macro/check-macro.d.ts +14 -0
  19. package/dist/macro/check-macro.js +136 -0
  20. package/dist/macro/expand-macros.d.ts +6 -0
  21. package/dist/macro/expand-macros.js +285 -0
  22. package/dist/optimizer/default-passes.d.ts +2 -0
  23. package/dist/optimizer/default-passes.js +20 -0
  24. package/dist/optimizer/merge-labels.d.ts +2 -0
  25. package/dist/optimizer/merge-labels.js +45 -0
  26. package/dist/optimizer/optimizer.d.ts +4 -0
  27. package/dist/optimizer/optimizer.js +26 -0
  28. package/dist/optimizer/passes/constant-folding.d.ts +2 -0
  29. package/dist/optimizer/passes/constant-folding.js +160 -0
  30. package/dist/optimizer/passes/control-flow.d.ts +5 -0
  31. package/dist/optimizer/passes/control-flow.js +138 -0
  32. package/dist/optimizer/passes/copy-propagation.d.ts +2 -0
  33. package/dist/optimizer/passes/copy-propagation.js +127 -0
  34. package/dist/optimizer/passes/dead-code.d.ts +2 -0
  35. package/dist/optimizer/passes/dead-code.js +200 -0
  36. package/dist/optimizer/passes/merge-labels-pass.d.ts +2 -0
  37. package/dist/optimizer/passes/merge-labels-pass.js +14 -0
  38. package/dist/optimizer/types.d.ts +10 -0
  39. package/dist/optimizer/types.js +3 -0
  40. package/dist/test.d.ts +1 -0
  41. package/dist/test.js +14 -0
  42. package/package.json +22 -0
@@ -0,0 +1,385 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSymbolTable = exports.BuildSymbolTableError = void 0;
4
+ const parser_1 = require("@minduscript/parser");
5
+ class BuildSymbolTableError extends Error {
6
+ node;
7
+ constructor(node, message) {
8
+ super(message);
9
+ this.node = node;
10
+ }
11
+ }
12
+ exports.BuildSymbolTableError = BuildSymbolTableError;
13
+ // 拷贝符号表,只拷贝2层,以节省内存以及保证引用的一致性
14
+ const copySymbolTable = (source) => ({
15
+ vars: { ...source.vars },
16
+ consts: { ...source.consts },
17
+ macros: { ...source.macros },
18
+ });
19
+ const hasOwn = (record, name) => Object.prototype.hasOwnProperty.call(record, name);
20
+ const hasAnySymbol = (table, name) => hasOwn(table.vars, name) || hasOwn(table.consts, name) || hasOwn(table.macros, name);
21
+ const findVarOrConst = (scope, globalTable, name) => {
22
+ if (hasOwn(scope.vars, name)) {
23
+ return scope.vars[name];
24
+ }
25
+ if (hasOwn(scope.consts, name)) {
26
+ return scope.consts[name];
27
+ }
28
+ if (hasOwn(globalTable.vars, name)) {
29
+ return globalTable.vars[name];
30
+ }
31
+ if (hasOwn(globalTable.consts, name)) {
32
+ return globalTable.consts[name];
33
+ }
34
+ return undefined;
35
+ };
36
+ const isConstSymbol = (scope, globalTable, name) => {
37
+ if (hasOwn(scope.consts, name)) {
38
+ return true;
39
+ }
40
+ if (hasOwn(scope.vars, name)) {
41
+ return false;
42
+ }
43
+ return hasOwn(globalTable.consts, name);
44
+ };
45
+ const createSyntheticIdentifier = (name, identifierType) => ({
46
+ type: parser_1.NodeType.IDENTIFIER_EXPRESSION,
47
+ tokens: [],
48
+ value: name,
49
+ identifierType,
50
+ isMindustry: false,
51
+ });
52
+ const ensureMindustryGlobal = (identifier, scope, globalTable) => {
53
+ if (!identifier.isMindustry) {
54
+ return;
55
+ }
56
+ if (hasAnySymbol(globalTable, identifier.value)) {
57
+ return;
58
+ }
59
+ const source = createSyntheticIdentifier(identifier.value, parser_1.IdentifierType.SIMPLE);
60
+ globalTable.vars[identifier.value] = source;
61
+ if (!hasAnySymbol(scope, identifier.value)) {
62
+ scope.vars[identifier.value] = source;
63
+ }
64
+ };
65
+ const defineVariable = (name, isConst, source, node, scope, context) => {
66
+ if (hasAnySymbol(scope, name)) {
67
+ throw new BuildSymbolTableError(node, `Symbol "${name}" is already defined in current scope`);
68
+ }
69
+ if (!context.isTopLevel && hasAnySymbol(context.globalTable, name)) {
70
+ throw new BuildSymbolTableError(node, `Local symbol "${name}" conflicts with global symbol`);
71
+ }
72
+ if (isConst) {
73
+ scope.consts[name] = source;
74
+ }
75
+ else {
76
+ scope.vars[name] = source;
77
+ }
78
+ };
79
+ const defineMacro = (name, source, node, scope) => {
80
+ if (hasAnySymbol(scope, name)) {
81
+ throw new BuildSymbolTableError(node, `Symbol "${name}" is already defined in current scope`);
82
+ }
83
+ scope.macros[name] = source;
84
+ };
85
+ const attachSymbolTable = (node, scope, patch) => {
86
+ return {
87
+ ...node,
88
+ ...(patch ?? {}),
89
+ symbolTable: copySymbolTable(scope),
90
+ };
91
+ };
92
+ const buildSymbolTable = (document) => {
93
+ const globalTable = {
94
+ vars: {},
95
+ consts: {},
96
+ macros: {},
97
+ };
98
+ const processExpression = (expression, scope, context) => {
99
+ const processOutputIdentifier = (identifier, localScope, localContext) => {
100
+ const transformed = attachSymbolTable(identifier, localScope);
101
+ if (identifier.isMindustry) {
102
+ ensureMindustryGlobal(identifier, localScope, localContext.globalTable);
103
+ return transformed;
104
+ }
105
+ if (identifier.identifierType === parser_1.IdentifierType.LET ||
106
+ identifier.identifierType === parser_1.IdentifierType.CONST) {
107
+ defineVariable(identifier.value, identifier.identifierType === parser_1.IdentifierType.CONST, identifier, identifier, localScope, localContext);
108
+ return transformed;
109
+ }
110
+ const symbol = findVarOrConst(localScope, localContext.globalTable, identifier.value);
111
+ if (!symbol) {
112
+ throw new BuildSymbolTableError(identifier, `Undefined variable or constant "${identifier.value}"`);
113
+ }
114
+ if (isConstSymbol(localScope, localContext.globalTable, identifier.value)) {
115
+ throw new BuildSymbolTableError(identifier, `Cannot assign to constant "${identifier.value}"`);
116
+ }
117
+ return transformed;
118
+ };
119
+ const processExpressionChild = (child, localScope, localContext) => {
120
+ switch (child.type) {
121
+ case parser_1.NodeType.UNARY_OP_EXPRESSION:
122
+ return attachSymbolTable(child, localScope, {
123
+ child: processExpressionChild(child.child, localScope, localContext),
124
+ });
125
+ case parser_1.NodeType.BINARY_OP_EXPRESSION:
126
+ return attachSymbolTable(child, localScope, {
127
+ lChild: processExpressionChild(child.lChild, localScope, localContext),
128
+ rChild: processExpressionChild(child.rChild, localScope, localContext),
129
+ });
130
+ case parser_1.NodeType.FUNCTION_CALL_EXPRESSION:
131
+ return attachSymbolTable(child, localScope, {
132
+ args: child.args.map((arg) => processExpressionChild(arg, localScope, localContext)),
133
+ });
134
+ case parser_1.NodeType.MACRO_CALL_EXPRESSION: {
135
+ const macroCall = child;
136
+ if (!hasOwn(localScope.macros, macroCall.name) &&
137
+ !hasOwn(localContext.globalTable.macros, macroCall.name)) {
138
+ throw new BuildSymbolTableError(child, `Undefined macro "${macroCall.name}"`);
139
+ }
140
+ return attachSymbolTable(child, localScope, {
141
+ inputArgs: macroCall.inputArgs.map((arg) => processExpressionChild(arg, localScope, localContext)),
142
+ outputArgs: macroCall.outputArgs.map((arg) => processOutputIdentifier(arg, localScope, localContext)),
143
+ });
144
+ }
145
+ case parser_1.NodeType.LITERAL_EXPRESSION:
146
+ return attachSymbolTable(child, localScope);
147
+ case parser_1.NodeType.IDENTIFIER_EXPRESSION:
148
+ if (child.isMindustry) {
149
+ ensureMindustryGlobal(child, localScope, localContext.globalTable);
150
+ return attachSymbolTable(child, localScope);
151
+ }
152
+ if (!findVarOrConst(localScope, localContext.globalTable, child.value)) {
153
+ throw new BuildSymbolTableError(child, `Undefined variable or constant "${child.value}"`);
154
+ }
155
+ return attachSymbolTable(child, localScope);
156
+ default:
157
+ return child;
158
+ }
159
+ };
160
+ return attachSymbolTable(expression, scope, {
161
+ child: processExpressionChild(expression.child, scope, context),
162
+ });
163
+ };
164
+ const processStatementList = (statements, scope, context) => {
165
+ const output = [];
166
+ for (const statement of statements) {
167
+ output.push(processStatement(statement, scope, context));
168
+ }
169
+ return output;
170
+ };
171
+ const processControlStatement = (statement, scope, context) => {
172
+ const patch = {};
173
+ for (const [key, value] of Object.entries(statement)) {
174
+ if (key === 'type' || key === 'statementType' || key === 'controlType' || key === 'tokens') {
175
+ continue;
176
+ }
177
+ if (!value || typeof value !== 'object') {
178
+ continue;
179
+ }
180
+ const maybeNode = value;
181
+ if (maybeNode.type === parser_1.NodeType.EXPRESSION) {
182
+ patch[key] = processExpression(value, scope, context);
183
+ }
184
+ }
185
+ for (const [key, value] of Object.entries(statement)) {
186
+ if (key in patch) {
187
+ continue;
188
+ }
189
+ if (!value || typeof value !== 'object') {
190
+ continue;
191
+ }
192
+ const maybeNode = value;
193
+ if (maybeNode.type !== parser_1.NodeType.IDENTIFIER_EXPRESSION) {
194
+ continue;
195
+ }
196
+ const identifier = value;
197
+ patch[key] = attachSymbolTable(identifier, scope);
198
+ if (identifier.isMindustry) {
199
+ ensureMindustryGlobal(identifier, scope, context.globalTable);
200
+ continue;
201
+ }
202
+ if (identifier.identifierType === parser_1.IdentifierType.LET ||
203
+ identifier.identifierType === parser_1.IdentifierType.CONST) {
204
+ defineVariable(identifier.value, identifier.identifierType === parser_1.IdentifierType.CONST, identifier, statement, scope, context);
205
+ continue;
206
+ }
207
+ const symbol = findVarOrConst(scope, context.globalTable, identifier.value);
208
+ if (!symbol) {
209
+ throw new BuildSymbolTableError(statement, `Undefined variable or constant "${identifier.value}"`);
210
+ }
211
+ if (isConstSymbol(scope, context.globalTable, identifier.value)) {
212
+ throw new BuildSymbolTableError(statement, `Cannot assign to constant "${identifier.value}"`);
213
+ }
214
+ }
215
+ return attachSymbolTable(statement, scope, patch);
216
+ };
217
+ const processStatement = (statement, scope, context) => {
218
+ switch (statement.statementType) {
219
+ case parser_1.StatementType.EMPTY:
220
+ return attachSymbolTable(statement, scope);
221
+ case parser_1.StatementType.VARIABLE_DEFINE: {
222
+ const variableStatement = statement;
223
+ const expression = processExpression(variableStatement.expression, scope, context);
224
+ const output = attachSymbolTable(statement, scope, {
225
+ expression,
226
+ });
227
+ defineVariable(variableStatement.variableName, variableStatement.isConst, variableStatement, statement, scope, context);
228
+ return output;
229
+ }
230
+ case parser_1.StatementType.ASSIGN: {
231
+ const expression = processExpression(statement.expression, scope, context);
232
+ if (!findVarOrConst(scope, context.globalTable, statement.variableName)) {
233
+ throw new BuildSymbolTableError(statement, `Undefined variable or constant "${statement.variableName}"`);
234
+ }
235
+ if (isConstSymbol(scope, context.globalTable, statement.variableName)) {
236
+ throw new BuildSymbolTableError(statement, `Cannot assign to constant "${statement.variableName}"`);
237
+ }
238
+ return attachSymbolTable(statement, scope, { expression });
239
+ }
240
+ case parser_1.StatementType.CONTROL:
241
+ return processControlStatement(statement, scope, context);
242
+ case parser_1.StatementType.MACRO_CALL: {
243
+ const macroCall = statement;
244
+ if (!hasOwn(scope.macros, macroCall.name) && !hasOwn(context.globalTable.macros, macroCall.name)) {
245
+ throw new BuildSymbolTableError(statement, `Undefined macro "${macroCall.name}"`);
246
+ }
247
+ const inputArgs = macroCall.inputArgs.map((arg) => processExpression(arg, scope, context));
248
+ const outputArgs = macroCall.outputArgs.map((identifier) => {
249
+ const transformed = attachSymbolTable(identifier, scope);
250
+ if (identifier.isMindustry) {
251
+ ensureMindustryGlobal(identifier, scope, context.globalTable);
252
+ return transformed;
253
+ }
254
+ if (identifier.identifierType === parser_1.IdentifierType.LET ||
255
+ identifier.identifierType === parser_1.IdentifierType.CONST) {
256
+ defineVariable(identifier.value, identifier.identifierType === parser_1.IdentifierType.CONST, identifier, statement, scope, context);
257
+ return transformed;
258
+ }
259
+ if (!findVarOrConst(scope, context.globalTable, identifier.value)) {
260
+ throw new BuildSymbolTableError(statement, `Undefined variable or constant "${identifier.value}"`);
261
+ }
262
+ if (isConstSymbol(scope, context.globalTable, identifier.value)) {
263
+ throw new BuildSymbolTableError(statement, `Cannot assign to constant "${identifier.value}"`);
264
+ }
265
+ return transformed;
266
+ });
267
+ return attachSymbolTable(statement, scope, { inputArgs, outputArgs });
268
+ }
269
+ case parser_1.StatementType.MACRO_DEFINE: {
270
+ const macroDefine = statement;
271
+ if (!context.isTopLevel) {
272
+ throw new BuildSymbolTableError(statement, 'Macro definition is only allowed at top level');
273
+ }
274
+ defineMacro(macroDefine.name, macroDefine, statement, scope);
275
+ const macroScope = copySymbolTable(scope);
276
+ const macroContext = {
277
+ ...context,
278
+ inLoop: 0,
279
+ inMacro: context.inMacro + 1,
280
+ isTopLevel: false,
281
+ };
282
+ for (const inputName of macroDefine.inputParams) {
283
+ defineVariable(inputName, true, createSyntheticIdentifier(inputName, parser_1.IdentifierType.CONST), statement, macroScope, macroContext);
284
+ }
285
+ for (const outputName of macroDefine.outputParams) {
286
+ defineVariable(outputName, false, createSyntheticIdentifier(outputName, parser_1.IdentifierType.LET), statement, macroScope, macroContext);
287
+ }
288
+ const body = processStatementList(macroDefine.body, macroScope, macroContext);
289
+ return attachSymbolTable(statement, scope, { body });
290
+ }
291
+ case parser_1.StatementType.IF: {
292
+ const ifStatement = statement;
293
+ const condition = processExpression(ifStatement.condition, scope, context);
294
+ const ifBody = processStatementList(ifStatement.ifBody, copySymbolTable(scope), {
295
+ ...context,
296
+ isTopLevel: false,
297
+ });
298
+ return attachSymbolTable(statement, scope, { condition, ifBody });
299
+ }
300
+ case parser_1.StatementType.IF_ELSE: {
301
+ const ifElse = statement;
302
+ const condition = processExpression(ifElse.condition, scope, context);
303
+ const ifBody = processStatementList(ifElse.ifBody, copySymbolTable(scope), {
304
+ ...context,
305
+ isTopLevel: false,
306
+ });
307
+ const elseBody = processStatementList(ifElse.elseBody, copySymbolTable(scope), {
308
+ ...context,
309
+ isTopLevel: false,
310
+ });
311
+ return attachSymbolTable(statement, scope, { condition, ifBody, elseBody });
312
+ }
313
+ case parser_1.StatementType.FOR: {
314
+ const forStatement = statement;
315
+ const forScope = copySymbolTable(scope);
316
+ const loopContext = {
317
+ ...context,
318
+ isTopLevel: false,
319
+ };
320
+ const init = processStatement(forStatement.init, forScope, loopContext);
321
+ const condition = processExpression(forStatement.condition, forScope, loopContext);
322
+ const increment = forStatement.increment
323
+ ? processStatement(forStatement.increment, forScope, loopContext)
324
+ : undefined;
325
+ const body = processStatementList(forStatement.body, copySymbolTable(forScope), {
326
+ ...loopContext,
327
+ inLoop: loopContext.inLoop + 1,
328
+ });
329
+ return attachSymbolTable(statement, scope, { init, condition, increment, body });
330
+ }
331
+ case parser_1.StatementType.WHILE: {
332
+ const whileStatement = statement;
333
+ const condition = processExpression(whileStatement.condition, scope, context);
334
+ const body = processStatementList(whileStatement.body, copySymbolTable(scope), {
335
+ ...context,
336
+ isTopLevel: false,
337
+ inLoop: context.inLoop + 1,
338
+ });
339
+ return attachSymbolTable(statement, scope, { condition, body });
340
+ }
341
+ case parser_1.StatementType.LOOP_CONTROL:
342
+ if (context.inLoop <= 0) {
343
+ throw new BuildSymbolTableError(statement, 'break/continue can only be used inside loops');
344
+ }
345
+ return attachSymbolTable(statement, scope);
346
+ case parser_1.StatementType.RETURN:
347
+ if (context.inMacro <= 0) {
348
+ throw new BuildSymbolTableError(statement, 'return can only be used inside macros');
349
+ }
350
+ return attachSymbolTable(statement, scope, {
351
+ expression: processExpression(statement.expression, scope, context),
352
+ });
353
+ case parser_1.StatementType.BIND: {
354
+ const bindStatement = statement;
355
+ if (!context.isTopLevel) {
356
+ throw new BuildSymbolTableError(statement, 'bind can only be used at top level');
357
+ }
358
+ const output = attachSymbolTable(statement, scope);
359
+ defineVariable(bindStatement.variableName, true, bindStatement, statement, scope, context);
360
+ return output;
361
+ }
362
+ default:
363
+ return statement;
364
+ }
365
+ };
366
+ const importList = document.importList.map((importStatement) => {
367
+ const transformedImport = attachSymbolTable(importStatement, globalTable);
368
+ defineMacro(importStatement.asName, importStatement, importStatement, globalTable);
369
+ return transformedImport;
370
+ });
371
+ const statementList = processStatementList(document.statementList, globalTable, {
372
+ globalTable,
373
+ inLoop: 0,
374
+ inMacro: 0,
375
+ isTopLevel: true,
376
+ });
377
+ return {
378
+ ...document,
379
+ importList,
380
+ statementList,
381
+ symbolTable: copySymbolTable(globalTable),
382
+ };
383
+ };
384
+ exports.buildSymbolTable = buildSymbolTable;
385
+ //# sourceMappingURL=symbol-table.js.map
@@ -0,0 +1,14 @@
1
+ import type { MacroCallExpressionNode, MacroCallStatementNode } from '@minduscript/parser';
2
+ import type { Project } from '../ir/project';
3
+ export declare enum ErrorPosition {
4
+ INPUT = 0,
5
+ OUTPUT = 1
6
+ }
7
+ export declare class CheckMacroError extends Error {
8
+ readonly node: MacroCallExpressionNode | MacroCallStatementNode;
9
+ readonly position: ErrorPosition;
10
+ readonly expectedArgs: number;
11
+ readonly actualArgs: number;
12
+ constructor(node: MacroCallExpressionNode | MacroCallStatementNode, position: ErrorPosition, expectedArgs: number, actualArgs: number);
13
+ }
14
+ export declare const checkMacro: (project: Project) => void;
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkMacro = exports.CheckMacroError = exports.ErrorPosition = void 0;
4
+ const parser_1 = require("@minduscript/parser");
5
+ var ErrorPosition;
6
+ (function (ErrorPosition) {
7
+ ErrorPosition[ErrorPosition["INPUT"] = 0] = "INPUT";
8
+ ErrorPosition[ErrorPosition["OUTPUT"] = 1] = "OUTPUT";
9
+ })(ErrorPosition || (exports.ErrorPosition = ErrorPosition = {}));
10
+ class CheckMacroError extends Error {
11
+ node;
12
+ position;
13
+ expectedArgs;
14
+ actualArgs;
15
+ constructor(node, position, expectedArgs, actualArgs) {
16
+ super(`宏参数错误:${node.name}的${expectedArgs == ErrorPosition.OUTPUT ? '输出' : '输入'}参数应为${expectedArgs}个,实为${actualArgs}个`);
17
+ this.node = node;
18
+ this.position = position;
19
+ this.expectedArgs = expectedArgs;
20
+ this.actualArgs = actualArgs;
21
+ }
22
+ }
23
+ exports.CheckMacroError = CheckMacroError;
24
+ const checkMacro = (project) => {
25
+ for (const source of Object.values(project.sources)) {
26
+ const macroMap = new Map();
27
+ for (const macro of [...source.macros, ...source.internalMacros]) {
28
+ macroMap.set(macro.name, macro);
29
+ }
30
+ const checkExpressionChild = (child) => {
31
+ switch (child.type) {
32
+ case parser_1.NodeType.UNARY_OP_EXPRESSION:
33
+ checkExpressionChild(child.child);
34
+ break;
35
+ case parser_1.NodeType.BINARY_OP_EXPRESSION:
36
+ checkExpressionChild(child.lChild);
37
+ checkExpressionChild(child.rChild);
38
+ break;
39
+ case parser_1.NodeType.FUNCTION_CALL_EXPRESSION:
40
+ child.args.forEach(checkExpressionChild);
41
+ break;
42
+ case parser_1.NodeType.MACRO_CALL_EXPRESSION: {
43
+ const macroDef = macroMap.get(child.name);
44
+ if (macroDef) {
45
+ if (child.inputArgs.length !== macroDef.inputParams.length) {
46
+ throw new CheckMacroError(child, ErrorPosition.INPUT, macroDef.inputParams.length, child.inputArgs.length);
47
+ }
48
+ if (child.outputArgs.length !== macroDef.outputParams.length) {
49
+ throw new CheckMacroError(child, ErrorPosition.OUTPUT, macroDef.outputParams.length, child.outputArgs.length);
50
+ }
51
+ }
52
+ child.inputArgs.forEach(checkExpressionChild);
53
+ break;
54
+ }
55
+ case parser_1.NodeType.LITERAL_EXPRESSION:
56
+ case parser_1.NodeType.IDENTIFIER_EXPRESSION:
57
+ break;
58
+ default:
59
+ child;
60
+ }
61
+ };
62
+ const checkExpression = (expression) => {
63
+ checkExpressionChild(expression.child);
64
+ };
65
+ const checkStatement = (statement) => {
66
+ switch (statement.statementType) {
67
+ case parser_1.StatementType.EMPTY:
68
+ case parser_1.StatementType.LOOP_CONTROL:
69
+ case parser_1.StatementType.BIND:
70
+ break;
71
+ case parser_1.StatementType.VARIABLE_DEFINE:
72
+ checkExpression(statement.expression);
73
+ break;
74
+ case parser_1.StatementType.ASSIGN:
75
+ checkExpression(statement.expression);
76
+ break;
77
+ case parser_1.StatementType.CONTROL:
78
+ for (const value of Object.values(statement)) {
79
+ if (value &&
80
+ typeof value === 'object' &&
81
+ 'type' in value &&
82
+ value.type === parser_1.NodeType.EXPRESSION) {
83
+ checkExpression(value);
84
+ }
85
+ }
86
+ break;
87
+ case parser_1.StatementType.MACRO_CALL: {
88
+ const macroDef = macroMap.get(statement.name);
89
+ if (macroDef) {
90
+ if (statement.inputArgs.length !== macroDef.inputParams.length) {
91
+ throw new CheckMacroError(statement, ErrorPosition.INPUT, macroDef.inputParams.length, statement.inputArgs.length);
92
+ }
93
+ if (statement.outputArgs.length !== macroDef.outputParams.length) {
94
+ throw new CheckMacroError(statement, ErrorPosition.OUTPUT, macroDef.outputParams.length, statement.outputArgs.length);
95
+ }
96
+ }
97
+ statement.inputArgs.forEach(checkExpression);
98
+ break;
99
+ }
100
+ case parser_1.StatementType.MACRO_DEFINE:
101
+ statement.body.forEach(checkStatement);
102
+ break;
103
+ case parser_1.StatementType.IF:
104
+ checkExpression(statement.condition);
105
+ statement.ifBody.forEach(checkStatement);
106
+ break;
107
+ case parser_1.StatementType.IF_ELSE:
108
+ checkExpression(statement.condition);
109
+ statement.ifBody.forEach(checkStatement);
110
+ statement.elseBody.forEach(checkStatement);
111
+ break;
112
+ case parser_1.StatementType.FOR:
113
+ checkStatement(statement.init);
114
+ checkExpression(statement.condition);
115
+ if (statement.increment)
116
+ checkStatement(statement.increment);
117
+ statement.body.forEach(checkStatement);
118
+ break;
119
+ case parser_1.StatementType.WHILE:
120
+ checkExpression(statement.condition);
121
+ statement.body.forEach(checkStatement);
122
+ break;
123
+ case parser_1.StatementType.RETURN:
124
+ checkExpression(statement.expression);
125
+ break;
126
+ default:
127
+ statement;
128
+ }
129
+ };
130
+ for (const statement of source.ast.statementList) {
131
+ checkStatement(statement);
132
+ }
133
+ }
134
+ };
135
+ exports.checkMacro = checkMacro;
136
+ //# sourceMappingURL=check-macro.js.map
@@ -0,0 +1,6 @@
1
+ import { IRProgram, type IRNode } from '../ir/ir';
2
+ export type SimplifiedIRProgram = {
3
+ body: IRNode[];
4
+ boundVariables: Set<string>;
5
+ };
6
+ export declare const expandMacros: (program: IRProgram) => SimplifiedIRProgram;