meaoiu 0.1.2 → 0.1.4

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 (73) hide show
  1. package/LANGUAGE_GUIDE.md +1 -0
  2. package/README.md +385 -336
  3. package/dist/api/serviceState.d.ts +1 -1
  4. package/dist/api/serviceState.d.ts.map +1 -1
  5. package/dist/api/serviceState.js +1 -1
  6. package/dist/api/serviceState.js.map +1 -1
  7. package/dist/api/services/completions.d.ts.map +1 -1
  8. package/dist/api/services/completions.js +4 -3
  9. package/dist/api/services/completions.js.map +1 -1
  10. package/dist/api/services/formatting.d.ts +6 -1
  11. package/dist/api/services/formatting.d.ts.map +1 -1
  12. package/dist/api/services/formatting.js +156 -146
  13. package/dist/api/services/formatting.js.map +1 -1
  14. package/dist/api/services/highlight.d.ts.map +1 -1
  15. package/dist/api/services/highlight.js +28 -38
  16. package/dist/api/services/highlight.js.map +1 -1
  17. package/dist/api/services/inlayHints.d.ts +1 -1
  18. package/dist/api/services/inlayHints.d.ts.map +1 -1
  19. package/dist/api/services/inlayHints.js +4 -8
  20. package/dist/api/services/inlayHints.js.map +1 -1
  21. package/dist/api/services/search.d.ts.map +1 -1
  22. package/dist/api/services/search.js +7 -3
  23. package/dist/api/services/search.js.map +1 -1
  24. package/dist/api/utils/astUtils.d.ts.map +1 -1
  25. package/dist/api/utils/astUtils.js +18 -12
  26. package/dist/api/utils/astUtils.js.map +1 -1
  27. package/dist/api/utils/symbolAnalyzer.d.ts +1 -1
  28. package/dist/api/utils/symbolAnalyzer.d.ts.map +1 -1
  29. package/dist/api/utils/symbolAnalyzer.js +60 -63
  30. package/dist/api/utils/symbolAnalyzer.js.map +1 -1
  31. package/dist/cli/main.d.ts +0 -1
  32. package/dist/cli/main.js +3 -9
  33. package/dist/cli/main.js.map +1 -1
  34. package/dist/cli/tools/formatter.d.ts.map +1 -1
  35. package/dist/cli/tools/formatter.js +13 -3
  36. package/dist/cli/tools/formatter.js.map +1 -1
  37. package/dist/cli/tools/searcher.js +1 -1
  38. package/dist/cli/tools/searcher.js.map +1 -1
  39. package/dist/cli/tools/starter.d.ts +1 -2
  40. package/dist/cli/tools/starter.d.ts.map +1 -1
  41. package/dist/cli/tools/starter.js +5 -3
  42. package/dist/cli/tools/starter.js.map +1 -1
  43. package/dist/cli/tools/toolUtils.d.ts.map +1 -1
  44. package/dist/cli/tools/toolUtils.js +9 -13
  45. package/dist/cli/tools/toolUtils.js.map +1 -1
  46. package/dist/core/ast.d.ts +3 -2
  47. package/dist/core/ast.d.ts.map +1 -1
  48. package/dist/core/builtIns.d.ts.map +1 -1
  49. package/dist/core/builtIns.js +3 -1
  50. package/dist/core/builtIns.js.map +1 -1
  51. package/dist/core/error.d.ts +1 -0
  52. package/dist/core/error.d.ts.map +1 -1
  53. package/dist/core/error.js +4 -11
  54. package/dist/core/error.js.map +1 -1
  55. package/dist/core/lexer/preprocessor.d.ts.map +1 -1
  56. package/dist/core/lexer/preprocessor.js +30 -57
  57. package/dist/core/lexer/preprocessor.js.map +1 -1
  58. package/dist/core/lexer/tokenizer.d.ts +52 -52
  59. package/dist/core/lexer/tokenizer.d.ts.map +1 -1
  60. package/dist/core/lexer/tokenizer.js +59 -52
  61. package/dist/core/lexer/tokenizer.js.map +1 -1
  62. package/dist/core/parser.d.ts.map +1 -1
  63. package/dist/core/parser.js +194 -209
  64. package/dist/core/parser.js.map +1 -1
  65. package/dist/core/run/environment.js +3 -3
  66. package/dist/core/run/interpreter.d.ts.map +1 -1
  67. package/dist/core/run/interpreter.js +152 -158
  68. package/dist/core/run/interpreter.js.map +1 -1
  69. package/dist/core/run/io.d.ts.map +1 -1
  70. package/dist/core/run/io.js +4 -0
  71. package/dist/core/run/io.js.map +1 -1
  72. package/dist/core/typedef.js +5 -5
  73. package/package.json +3 -3
@@ -1,21 +1,21 @@
1
1
  // src/core/parser.ts
2
2
  import { MeaoiuError, errorFrom } from './error.js';
3
- import { newToken, } from './lexer/tokenizer.js';
3
+ import { newToken } from './lexer/tokenizer.js';
4
4
  const blockKindInfo = [
5
5
  {
6
6
  name: '想法',
7
- startKind: 22 /* TokenKind.BLOCK_START */,
7
+ startKind: 26 /* TokenKind.BLOCK_START */,
8
8
  startValue: '[#',
9
- separatorKind: 24 /* TokenKind.TERMINATOR */,
10
- endKind: 23 /* TokenKind.BLOCK_END */,
9
+ separatorKind: 3 /* TokenKind.TERMINATOR */,
10
+ endKind: 27 /* TokenKind.BLOCK_END */,
11
11
  endValue: '#]',
12
12
  },
13
13
  {
14
14
  name: '纸箱',
15
- startKind: 20 /* TokenKind.COLLECTION_START */,
15
+ startKind: 24 /* TokenKind.COLLECTION_START */,
16
16
  startValue: '[=',
17
- separatorKind: 36 /* TokenKind.COMMA */,
18
- endKind: 21 /* TokenKind.COLLECTION_END */,
17
+ separatorKind: 4 /* TokenKind.COMMA */,
18
+ endKind: 25 /* TokenKind.COLLECTION_END */,
19
19
  endValue: '=]',
20
20
  },
21
21
  ]; // 0: 想法, 1: 纸箱
@@ -23,42 +23,36 @@ const syntaxErrorFrom = (ele, message) => errorFrom(ele, message, 3 /* Phase.SYN
23
23
  function errorNodeWith({ message, line, col, endLine, endCol }) {
24
24
  return { kind: 0 /* NodeKind.ErrorNode */, message, line, col, endLine, endCol };
25
25
  }
26
- function isAdditiveToken(token) {
27
- return token.kind === 26 /* TokenKind.ARITHMETIC_PLUS */ || token.kind === 27 /* TokenKind.ARITHMETIC_MINUS */;
28
- }
29
- function isMultiplicativeToken(token) {
30
- return token.kind === 28 /* TokenKind.ARITHMETIC_MULTIPLY */ || token.kind === 29 /* TokenKind.ARITHMETIC_DIVIDE */;
31
- }
32
26
  function isComparisonToken(token) {
33
27
  switch (token.kind) {
34
- case 32 /* TokenKind.COMPARISON_GREATER */:
35
- case 33 /* TokenKind.COMPARISON_LESS */:
36
- case 34 /* TokenKind.COMPARISON_GREATER_EQUAL */:
37
- case 35 /* TokenKind.COMPARISON_LESS_EQUAL */:
38
- case 30 /* TokenKind.COMPARISON_EQUAL */:
39
- case 31 /* TokenKind.COMPARISON_NOT_EQUAL */:
28
+ case 35 /* TokenKind.COMPARISON_GREATER */:
29
+ case 36 /* TokenKind.COMPARISON_LESS */:
30
+ case 37 /* TokenKind.COMPARISON_GREATER_EQUAL */:
31
+ case 38 /* TokenKind.COMPARISON_LESS_EQUAL */:
32
+ case 33 /* TokenKind.COMPARISON_EQUAL */:
33
+ case 34 /* TokenKind.COMPARISON_NOT_EQUAL */:
40
34
  return true;
41
35
  default:
42
36
  return false;
43
37
  }
44
38
  }
45
- function isStartToken(kind) {
39
+ function isStartKind(kind) {
46
40
  switch (kind) {
47
- case 1 /* TokenKind.KEYWORD_USE */:
48
- case 9 /* TokenKind.KEYWORD_LOOP */:
49
- case 10 /* TokenKind.KEYWORD_BREAK */:
50
- case 11 /* TokenKind.KEYWORD_AMBUSH */:
51
- case 12 /* TokenKind.KEYWORD_DEF */:
52
- case 13 /* TokenKind.KEYWORD_CALL */:
53
- case 14 /* TokenKind.KEYWORD_RETURN */:
54
- case 22 /* TokenKind.BLOCK_START */:
41
+ case 5 /* TokenKind.KEYWORD_USE */:
42
+ case 13 /* TokenKind.KEYWORD_LOOP */:
43
+ case 14 /* TokenKind.KEYWORD_BREAK */:
44
+ case 15 /* TokenKind.KEYWORD_AMBUSH */:
45
+ case 16 /* TokenKind.KEYWORD_DEF */:
46
+ case 17 /* TokenKind.KEYWORD_CALL */:
47
+ case 18 /* TokenKind.KEYWORD_RETURN */:
48
+ case 26 /* TokenKind.BLOCK_START */:
55
49
  return true;
56
50
  default:
57
51
  return false;
58
52
  }
59
53
  }
60
54
  function isAssignmentOperator(kind) {
61
- return kind === 2 /* TokenKind.ASSIGNMENT_IS */ || kind === 3 /* TokenKind.ASSIGNMENT_LIKE */ || kind === 4 /* TokenKind.ASSIGNMENT_ONLY */;
55
+ return kind === 6 /* TokenKind.ASSIGNMENT_IS */ || kind === 7 /* TokenKind.ASSIGNMENT_LIKE */ || kind === 8 /* TokenKind.ASSIGNMENT_ONLY */;
62
56
  }
63
57
  class Parser {
64
58
  tokens;
@@ -67,56 +61,35 @@ class Parser {
67
61
  blockDepth = new Int16Array(2); // 0: 想法, 1: 纸箱
68
62
  errors = [];
69
63
  position = 0;
70
- // 悄悄话缓存:advance / drainCommentsAhead 会把遇到的 COMMENT 放进这里
71
- commentBuffer = [];
64
+ prevOne; // 上一个非悄悄话词元
65
+ commentBuffer = []; // 悄悄话缓存:drainCommentsAhead 会把遇到的 COMMENT 放进这里
72
66
  constructor(tokens, mode = 0 /* ParseMode.STRICT */) {
73
67
  this.MODE = mode;
74
68
  // 确保有 EOF 哨兵,避免边界问题
75
- if (!tokens.length || tokens[tokens.length - 1].kind !== 44 /* TokenKind.EOF */) {
76
- const EOF = newToken(44 /* TokenKind.EOF */, 'EndOfFile', -1, -1, -1, -1);
69
+ if (!tokens.length || tokens[tokens.length - 1].kind !== 1 /* TokenKind.EOF */) {
70
+ const EOF = newToken(1 /* TokenKind.EOF */, 'EndOfFile', -1, -1, -1, -1);
77
71
  tokens.push((this.reportError(errorFrom(EOF, '分词器可能坏了喵~', 1 /* Phase.INVARIANT */)), EOF));
78
72
  }
79
73
  this.tokens = tokens;
80
74
  this.MAX_POS = tokens.length - 1;
75
+ this.prevOne = this.drainCommentsAhead(); // 收集前导悄悄话,确保起步位置可食用
81
76
  }
82
- // 当前 token(安全!)
83
77
  current() {
84
78
  return this.tokens[this.position];
85
79
  }
86
- // 安全前视 n 个 token
87
- peek(n = 1) {
88
- return this.tokens[Math.min(this.position + n, this.MAX_POS)];
80
+ // 前视一位
81
+ peek() {
82
+ return this.tokens[Math.min(this.position + 1, this.MAX_POS)];
89
83
  }
90
84
  /**
91
- * 返回当前位置的「非悄悄话 token」,并将位置推进到下一个 token(非悄悄话或 EOF),
92
- * 同时把遇到的悄悄话收集到 commentBuffer
93
- * @returns 当前位置的 token(非 COMMENT)
85
+ * 推进位置并取得下一词元,同时将途经悄悄话收入 commentBuffer
86
+ * @returns 下一个非悄悄话词元(可能为 EOF)
94
87
  */
95
- advance() {
96
- const current = this.drainCommentsAhead(); // 当前非悄悄话 token(或 EOF)
88
+ next() {
89
+ this.prevOne = this.current();
97
90
  if (this.position < this.MAX_POS)
98
91
  this.position++;
99
- return current;
100
- }
101
- /**
102
- * 推进位置(同时掠取悄悄话)并取得下一 token
103
- * @returns 悄悄话之后一个 token 的再下一 token
104
- */
105
- next() {
106
- this.advance();
107
- return this.current();
108
- }
109
- /**
110
- * 返回上一个非悄悄话 token(不动位置)
111
- * @returns 上一个非悄悄话 token
112
- */
113
- lookBack() {
114
- if (this.position < 1)
115
- return this.current();
116
- let token;
117
- for (let pos = this.position; (token = this.tokens[--pos]).kind === 43 /* TokenKind.COMMENT */ && pos > 0;)
118
- ;
119
- return token;
92
+ return this.drainCommentsAhead();
120
93
  }
121
94
  /**
122
95
  * 取得当前非悄悄话 token,若当前为悄悄话则连续推进至目标(同时收集悄悄话到 commentBuffer)
@@ -124,17 +97,17 @@ class Parser {
124
97
  */
125
98
  drainCommentsAhead() {
126
99
  let token = this.current();
127
- while (token.kind === 43 /* TokenKind.COMMENT */ && this.position < this.MAX_POS) {
100
+ while (token.kind === 2 /* TokenKind.COMMENT */ && this.position < this.MAX_POS) {
128
101
  this.commentBuffer.push(token);
129
102
  token = this.tokens[++this.position];
130
103
  }
131
104
  return token;
132
105
  }
133
106
  /**
134
- * 从缓存中拿走所有待分配的 leading 悄悄话(并清空缓存)
107
+ * 从缓存中拿走所有待分配的悄悄话并清空缓存
135
108
  * @returns 所有待分配的悄悄话
136
109
  */
137
- takeLeadingComments() {
110
+ takeUnclaimedComments() {
138
111
  const comments = this.commentBuffer;
139
112
  this.commentBuffer = [];
140
113
  return comments;
@@ -155,10 +128,9 @@ class Parser {
155
128
  (comment.line === anchorToken.line ? trailing : remaining).push(comment);
156
129
  if (trailing.length)
157
130
  (node.trailingComments ??= []).push(...trailing);
158
- // 将非 trailing 的悄悄话保留为新的 commentBuffer
159
- this.commentBuffer = remaining;
131
+ this.commentBuffer = remaining; // 将非 trailing 的悄悄话保留为新 commentBuffer
160
132
  }
161
- endLoc({ endLine, endCol } = this.lookBack()) {
133
+ endLoc({ endLine, endCol } = this.prevOne) {
162
134
  return { endLine, endCol };
163
135
  }
164
136
  reportError(error) {
@@ -172,29 +144,25 @@ class Parser {
172
144
  * @returns 虚构 token
173
145
  */
174
146
  makeErrorTail(kind, why, where) {
175
- // 找到前一个 token 做锚点
176
- const { endLine, endCol, value } = this.lookBack();
147
+ const { endLine, endCol, value } = this.prevOne; // 前一词做锚点
177
148
  const fakeTermToken = newToken(kind, '', endLine, endCol, endLine, endCol);
178
149
  const errMsg = `${where ?? `在「${value}」后`}${why}`;
179
150
  this.reportError(syntaxErrorFrom(fakeTermToken, errMsg));
180
151
  return fakeTermToken;
181
152
  }
182
153
  expect(kind, why, where) {
183
- const token = this.current();
184
- if (token.kind === kind)
185
- return this.advance();
186
- // 返回合成 token(但不移动 position),解析器会以为期望的 token 存在于此锚点
187
- return this.makeErrorTail(kind, why, where);
154
+ // 当前词元符合期望即前进并取走,否则在原地用假词元迎合解析器的期望
155
+ return this.current().kind === kind ? (this.next(), this.prevOne) : this.makeErrorTail(kind, why, where);
188
156
  }
189
157
  synchronize() {
190
158
  // 首先尝试找到 TERMINATOR 或语句开始,跳过闭块符,顺便捕获块配对问题
191
- for (let tokenKind = this.current().kind; tokenKind !== 44 /* TokenKind.EOF */; tokenKind = this.next().kind) {
192
- if (tokenKind === 24 /* TokenKind.TERMINATOR */)
193
- return void this.advance();
194
- if (isStartToken(tokenKind))
159
+ for (let tokenKind = this.current().kind; tokenKind !== 1 /* TokenKind.EOF */; tokenKind = this.next().kind) {
160
+ if (tokenKind === 3 /* TokenKind.TERMINATOR */)
161
+ return void this.next();
162
+ if (isStartKind(tokenKind))
195
163
  return;
196
- const depthIndex = +(tokenKind === 21 /* TokenKind.COLLECTION_END */);
197
- if (!depthIndex && tokenKind !== 23 /* TokenKind.BLOCK_END */)
164
+ const depthIndex = +(tokenKind === 25 /* TokenKind.COLLECTION_END */);
165
+ if (!depthIndex && tokenKind !== 27 /* TokenKind.BLOCK_END */)
198
166
  continue;
199
167
  if (this.blockDepth[depthIndex] > 0)
200
168
  return;
@@ -205,52 +173,49 @@ class Parser {
205
173
  }
206
174
  // 用于 parseExpression 内部:跳到 terminator / statement start / EOF
207
175
  consumeToRecoveryPoint() {
208
- for (let tokenKind = this.current().kind; tokenKind !== 44 /* TokenKind.EOF */ && tokenKind !== 24 /* TokenKind.TERMINATOR */ && !isStartToken(tokenKind); tokenKind = this.next().kind)
176
+ for (let tokenKind = this.current().kind; tokenKind !== 1 /* TokenKind.EOF */ && tokenKind !== 3 /* TokenKind.TERMINATOR */ && !isStartKind(tokenKind); tokenKind = this.next().kind)
209
177
  ;
210
178
  }
211
179
  parse() {
212
- const { kind: startKind, line, col, endLine, endCol } = this.current();
213
- const program = { kind: 1 /* NodeKind.Program */, body: [], line, col, endLine, endCol };
214
- for (let tokenKind = startKind; tokenKind !== 44 /* TokenKind.EOF */; tokenKind = this.current().kind) {
215
- if (tokenKind === 24 /* TokenKind.TERMINATOR */)
216
- this.advance();
180
+ const body = [];
181
+ for (let tokenKind; (tokenKind = this.current().kind) !== 1 /* TokenKind.EOF */;) {
182
+ if (tokenKind === 3 /* TokenKind.TERMINATOR */)
183
+ this.next();
217
184
  else
218
- program.body.push(this.parseStatement());
219
- }
220
- // 如果文件结束后仍有悄悄话缓存在 buffer,把它当成最后一个语句的 trailing(如果存在)
221
- if (this.commentBuffer.length > 0 && program.body.length > 0) {
222
- const lastStatement = program.body[program.body.length - 1];
223
- (lastStatement.trailingComments ??= []).push(...this.commentBuffer);
224
- this.commentBuffer = [];
185
+ body.push(this.parseStatement());
225
186
  }
226
- ({ endLine: program.endLine, endCol: program.endCol } = this.tokens[Math.max(0, this.position - 1)]);
187
+ const { line, col } = this.tokens[0];
188
+ const { endLine, endCol } = this.tokens[this.MAX_POS];
189
+ const program = { kind: 1 /* NodeKind.Program */, body, line, col, endLine, endCol };
190
+ // 遗留在缓存的悄悄话挂到根节点
191
+ if (this.commentBuffer.length > 0)
192
+ program.trailingComments = this.takeUnclaimedComments();
227
193
  return { program, errors: this.errors };
228
194
  }
229
195
  parseStatement() {
230
- // 先把当前位置连续的悄悄话收进 buffer(避免 current() 是 COMMENT 的情况)
231
- const startToken = this.drainCommentsAhead();
232
- const leading = this.takeLeadingComments(); // 取走当前节点的 leading 悄悄话(如果有)
196
+ const startToken = this.current();
197
+ const leading = this.takeUnclaimedComments(); // 取走当前节点的 leading 悄悄话(如果有)
233
198
  try {
234
199
  let node;
235
200
  switch (startToken.kind) {
236
- case 1 /* TokenKind.KEYWORD_USE */:
201
+ case 5 /* TokenKind.KEYWORD_USE */:
237
202
  node = this.parseVariableDeclaration(true);
238
203
  break;
239
- case 12 /* TokenKind.KEYWORD_DEF */:
204
+ case 16 /* TokenKind.KEYWORD_DEF */:
240
205
  node = this.parseFunctionDeclaration();
241
206
  break;
242
- case 14 /* TokenKind.KEYWORD_RETURN */:
207
+ case 18 /* TokenKind.KEYWORD_RETURN */:
243
208
  node = this.parseReturnOrAmbushStatement(5 /* NodeKind.ReturnStatement */);
244
209
  break;
245
- case 11 /* TokenKind.KEYWORD_AMBUSH */:
210
+ case 15 /* TokenKind.KEYWORD_AMBUSH */:
246
211
  node = this.parseReturnOrAmbushStatement(6 /* NodeKind.AmbushStatement */);
247
212
  break;
248
- case 10 /* TokenKind.KEYWORD_BREAK */:
249
- this.advance();
213
+ case 14 /* TokenKind.KEYWORD_BREAK */:
214
+ this.next();
250
215
  node = { kind: 7 /* NodeKind.BreakStatement */, line: startToken.line, col: startToken.col, ...this.endLoc() };
251
216
  break;
252
- case 21 /* TokenKind.COLLECTION_END */:
253
- case 23 /* TokenKind.BLOCK_END */:
217
+ case 25 /* TokenKind.COLLECTION_END */:
218
+ case 27 /* TokenKind.BLOCK_END */:
254
219
  throw syntaxErrorFrom(startToken, '假的语句喵!');
255
220
  default:
256
221
  const expression = this.parseExpression();
@@ -263,11 +228,8 @@ class Parser {
263
228
  node = { kind: 8 /* NodeKind.ExpressionStatement */, expression, line, col, ...this.endLoc() };
264
229
  }
265
230
  }
266
- // 检查终结符但不消耗,消耗的工作交给 parse 和 parseBlockExpression
267
- const trailingToken = this.current();
268
- const termToken = trailingToken.kind !== 24 /* TokenKind.TERMINATOR */
269
- ? this.makeErrorTail(24 /* TokenKind.TERMINATOR */, '必须有尾巴「~」喵!')
270
- : trailingToken;
231
+ // 检查并消耗终结符
232
+ const termToken = this.expect(3 /* TokenKind.TERMINATOR */, '必须有尾巴「~」喵!');
271
233
  if (leading.length)
272
234
  (node.leadingComments ??= []).push(...leading);
273
235
  // 收集并分配 terminator 后面的悄悄话(同一行视为 trailing)
@@ -282,7 +244,7 @@ class Parser {
282
244
  }
283
245
  }
284
246
  parseVariableDeclaration(hasUse) {
285
- const { line, col } = hasUse ? this.advance() : this.current();
247
+ const { line, col } = hasUse ? (this.next(), this.prevOne) : this.current();
286
248
  const identifier = this.parseIdentifier();
287
249
  // 检查后面是否紧跟赋值关键字
288
250
  const tokenKind = this.current().kind;
@@ -292,30 +254,33 @@ class Parser {
292
254
  return { kind: 2 /* NodeKind.VariableDeclaration */, identifier, initialization, line, col, ...this.endLoc() };
293
255
  }
294
256
  parseAssignmentStatement(assignee, operator) {
295
- this.advance();
257
+ this.next();
296
258
  const value = this.parseExpression();
297
259
  const { line, col } = assignee;
298
260
  return { kind: 3 /* NodeKind.AssignmentStatement */, assignee, value, operator, line, col, ...this.endLoc() };
299
261
  }
300
262
  parseFunctionDeclaration() {
301
- const { line, col } = this.advance();
263
+ const { line, col } = (this.next(), this.prevOne);
302
264
  const parameters = this.parseBlockExpression(true);
303
265
  const name = this.parseIdentifier();
304
266
  const body = this.parseBlockExpression(false);
305
267
  return { kind: 4 /* NodeKind.FunctionDeclaration */, name, parameters, body, line, col, ...this.endLoc() };
306
268
  }
307
269
  parseReturnOrAmbushStatement(nodeKind) {
308
- const { line, col } = this.advance();
309
- const argument = this.current().kind !== 24 /* TokenKind.TERMINATOR */ ? this.parseExpression() : undefined;
270
+ const { line, col } = (this.next(), this.prevOne);
271
+ const argument = this.current().kind !== 3 /* TokenKind.TERMINATOR */ ? this.parseExpression() : undefined;
310
272
  return { kind: nodeKind, argument, line, col, ...this.endLoc() };
311
273
  }
312
274
  parseIdentifier() {
313
- this.drainCommentsAhead(); // identifier 也应接收可能的 leading 悄悄话
314
- const leading = this.takeLeadingComments();
315
- const { line, col, value: symbol } = this.expect(19 /* TokenKind.IDENTIFIER */, '需要一个标识符喵!');
275
+ const leading = this.takeUnclaimedComments();
276
+ const { line, col, value: symbol } = this.expect(23 /* TokenKind.IDENTIFIER */, '需要一个标识符喵!');
316
277
  const node = { kind: 13 /* NodeKind.Identifier */, symbol, line, col, ...this.endLoc() };
317
- if (leading.length)
318
- node.leadingComments = leading;
278
+ if (leading.length) {
279
+ if (symbol)
280
+ node.leadingComments = leading;
281
+ else
282
+ this.commentBuffer = (leading.push(...this.commentBuffer), leading); // 用不了,归还
283
+ }
319
284
  return node;
320
285
  }
321
286
  parseExpression() {
@@ -332,37 +297,37 @@ class Parser {
332
297
  parseLogicalExpression() {
333
298
  let left = this.parseSequenceExpression();
334
299
  // 循环处理连续的逻辑操作:A 和 B 都好 或 C 不坏
335
- for (let tokenKind = this.current().kind, isOr; (isOr = tokenKind === 38 /* TokenKind.LOGIC_OR */) || tokenKind === 37 /* TokenKind.LOGIC_AND */; tokenKind = this.current().kind) {
336
- this.advance();
300
+ for (let tokenKind, isOr; (isOr = (tokenKind = this.current().kind) === 40 /* TokenKind.LOGIC_OR */) || tokenKind === 39 /* TokenKind.LOGIC_AND */;) {
301
+ this.next();
337
302
  const right = this.parseLogicalExpression();
338
303
  const closeToken = this.current();
339
304
  const operator = closeToken.kind;
340
305
  switch (operator) {
341
- case 40 /* TokenKind.LOGIC_CLOSE_OR */:
306
+ case 42 /* TokenKind.LOGIC_CLOSE_OR */:
342
307
  if (!isOr)
343
308
  this.reportError(syntaxErrorFrom(closeToken, '逻辑「和」不能用「不坏」闭合喵!'));
344
309
  break;
345
- case 42 /* TokenKind.LOGIC_CLOSE_NAND */:
310
+ case 44 /* TokenKind.LOGIC_CLOSE_NAND */:
346
311
  if (!isOr)
347
312
  this.reportError(syntaxErrorFrom(closeToken, '逻辑「和」不能用「不好」闭合喵!'));
348
313
  break;
349
- case 39 /* TokenKind.LOGIC_CLOSE_AND */:
314
+ case 41 /* TokenKind.LOGIC_CLOSE_AND */:
350
315
  if (isOr)
351
316
  this.reportError(syntaxErrorFrom(closeToken, '逻辑「或」不能用「都好」闭合喵!'));
352
317
  break;
353
- case 41 /* TokenKind.LOGIC_CLOSE_NOR */:
318
+ case 43 /* TokenKind.LOGIC_CLOSE_NOR */:
354
319
  if (isOr)
355
320
  this.reportError(syntaxErrorFrom(closeToken, '逻辑「或」不能用「都坏」闭合喵!'));
356
321
  break;
357
- case 38 /* TokenKind.LOGIC_OR */:
358
- case 37 /* TokenKind.LOGIC_AND */:
322
+ case 40 /* TokenKind.LOGIC_OR */:
323
+ case 39 /* TokenKind.LOGIC_AND */:
359
324
  throw errorFrom(closeToken, '你不该看到这个喵!', 1 /* Phase.INVARIANT */);
360
325
  default:
361
326
  const [logic, close, nClose] = isOr ? ['或', '不坏', '不好'] : ['和', '都好', '都坏'];
362
327
  this.makeErrorTail(0 /* TokenKind.ERROR */, `逻辑「${logic}」要有「${close}」或「${nClose}」闭合喵!`);
363
328
  continue;
364
329
  }
365
- this.advance();
330
+ this.next();
366
331
  const { line, col } = left;
367
332
  left = { kind: 23 /* NodeKind.LogicalExpression */, left, right, operator, line, col, ...this.endLoc() };
368
333
  }
@@ -372,30 +337,30 @@ class Parser {
372
337
  const { line, col } = this.current();
373
338
  const sections = [this.parseComparisonExpression()];
374
339
  const operators = [];
375
- sequence: for (let isArithmeticMode = true; this.peek().kind === 36 /* TokenKind.COMMA */;) {
340
+ sequence: for (let isArithmeticMode = true; this.peek().kind === 4 /* TokenKind.COMMA */;) {
376
341
  const token = this.current();
377
342
  switch (token.kind) {
378
- case 26 /* TokenKind.ARITHMETIC_PLUS */:
379
- case 27 /* TokenKind.ARITHMETIC_MINUS */:
380
- case 28 /* TokenKind.ARITHMETIC_MULTIPLY */:
381
- case 29 /* TokenKind.ARITHMETIC_DIVIDE */:
343
+ case 29 /* TokenKind.ARITHMETIC_PLUS */:
344
+ case 30 /* TokenKind.ARITHMETIC_MINUS */:
345
+ case 31 /* TokenKind.ARITHMETIC_MULTIPLY */:
346
+ case 32 /* TokenKind.ARITHMETIC_DIVIDE */:
382
347
  if (isArithmeticMode)
383
348
  break; // 模式正确
384
349
  throw syntaxErrorFrom(token, '比较之后就不能做算术了喵!');
385
- case 30 /* TokenKind.COMPARISON_EQUAL */:
386
- case 31 /* TokenKind.COMPARISON_NOT_EQUAL */:
350
+ case 33 /* TokenKind.COMPARISON_EQUAL */:
351
+ case 34 /* TokenKind.COMPARISON_NOT_EQUAL */:
387
352
  isArithmeticMode = false; // 禁用算术模式
388
353
  break;
389
- case 32 /* TokenKind.COMPARISON_GREATER */:
390
- case 33 /* TokenKind.COMPARISON_LESS */:
391
- case 34 /* TokenKind.COMPARISON_GREATER_EQUAL */:
392
- case 35 /* TokenKind.COMPARISON_LESS_EQUAL */:
354
+ case 35 /* TokenKind.COMPARISON_GREATER */:
355
+ case 36 /* TokenKind.COMPARISON_LESS */:
356
+ case 37 /* TokenKind.COMPARISON_GREATER_EQUAL */:
357
+ case 38 /* TokenKind.COMPARISON_LESS_EQUAL */:
393
358
  throw syntaxErrorFrom(token, `'${token.value}' 不能用在节之间喵!`);
394
359
  default:
395
360
  break sequence;
396
361
  }
397
- this.advance(); // 消费操作符
398
- this.advance(); // 跳过逗号
362
+ this.next(); // 消费操作符
363
+ this.next(); // 跳过逗号
399
364
  operators.push(token);
400
365
  sections.push(this.parseComparisonExpression());
401
366
  }
@@ -405,12 +370,12 @@ class Parser {
405
370
  }
406
371
  parseComparisonExpression() {
407
372
  const { line, col } = this.current();
408
- const expressions = [this.parseAdditiveExpression()];
373
+ const expressions = [this.parseArithmeticExpression(0 /* ArithmeticPrecedence.NONE */)];
409
374
  const operators = [];
410
- for (let token; isComparisonToken((token = this.current())) && this.peek().kind !== 36 /* TokenKind.COMMA */;) {
411
- this.advance();
375
+ for (let token; isComparisonToken((token = this.current())) && this.peek().kind !== 4 /* TokenKind.COMMA */;) {
376
+ this.next();
412
377
  operators.push(token);
413
- expressions.push(this.parseAdditiveExpression());
378
+ expressions.push(this.parseArithmeticExpression(0 /* ArithmeticPrecedence.NONE */));
414
379
  }
415
380
  // 如果没有比较(只有一个操作数),就返回那个操作数本身
416
381
  if (expressions.length === 1)
@@ -418,21 +383,28 @@ class Parser {
418
383
  // 否则,创建一个链式比较节点
419
384
  return { kind: 21 /* NodeKind.ComparisonExpression */, expressions, operators, line, col, ...this.endLoc() };
420
385
  }
421
- parseAdditiveExpression() {
422
- let left = this.parseMultiplicativeExpression();
423
- for (let token; isAdditiveToken((token = this.current())) && this.peek().kind !== 36 /* TokenKind.COMMA */;) {
424
- const { line, col } = this.advance();
425
- const right = this.parseMultiplicativeExpression();
426
- left = { kind: 20 /* NodeKind.ArithmeticExpression */, left, operator: token, right, line, col, ...this.endLoc(token) };
427
- }
428
- return left;
429
- }
430
- parseMultiplicativeExpression() {
386
+ parseArithmeticExpression(minPrecedence) {
431
387
  let left = this.parseUnaryExpression();
432
- for (let token; isMultiplicativeToken((token = this.current())) && this.peek().kind !== 36 /* TokenKind.COMMA */;) {
433
- const { line, col } = this.advance();
434
- const right = this.parseUnaryExpression();
435
- left = { kind: 20 /* NodeKind.ArithmeticExpression */, left, operator: token, right, line, col, ...this.endLoc(token) };
388
+ arithmetic: for (let precedence = minPrecedence; this.peek().kind !== 4 /* TokenKind.COMMA */;) {
389
+ const operator = this.current();
390
+ switch (operator.kind) {
391
+ case 31 /* TokenKind.ARITHMETIC_MULTIPLY */:
392
+ case 32 /* TokenKind.ARITHMETIC_DIVIDE */:
393
+ precedence = 2 /* ArithmeticPrecedence.MULTIPLICATIVE */;
394
+ break;
395
+ case 29 /* TokenKind.ARITHMETIC_PLUS */:
396
+ case 30 /* TokenKind.ARITHMETIC_MINUS */:
397
+ precedence = 1 /* ArithmeticPrecedence.ADDITIVE */;
398
+ break;
399
+ default:
400
+ break arithmetic;
401
+ }
402
+ if (precedence <= minPrecedence)
403
+ break arithmetic;
404
+ this.next();
405
+ const right = this.parseArithmeticExpression(precedence);
406
+ const { line, col } = left;
407
+ left = { kind: 20 /* NodeKind.ArithmeticExpression */, left, operator, right, line, col, ...this.endLoc() };
436
408
  }
437
409
  return left;
438
410
  }
@@ -440,16 +412,16 @@ class Parser {
440
412
  const { kind: tokenKind, line, col } = this.current();
441
413
  let operator;
442
414
  switch (tokenKind) {
443
- case 5 /* TokenKind.KEYWORD_CLONE */:
444
- operator = 3 /* TokenKind.ASSIGNMENT_LIKE */;
415
+ case 9 /* TokenKind.KEYWORD_CLONE */:
416
+ operator = 7 /* TokenKind.ASSIGNMENT_LIKE */;
445
417
  break;
446
- case 6 /* TokenKind.KEYWORD_MOVE */:
447
- operator = 4 /* TokenKind.ASSIGNMENT_ONLY */;
418
+ case 10 /* TokenKind.KEYWORD_MOVE */:
419
+ operator = 8 /* TokenKind.ASSIGNMENT_ONLY */;
448
420
  break;
449
421
  default:
450
422
  return this.parseMemberAccessExpression();
451
423
  }
452
- this.advance();
424
+ this.next();
453
425
  // 递归调用,这样就可以处理像“高仿 高仿 a”这样的写法
454
426
  const argument = this.parseUnaryExpression();
455
427
  return { kind: 19 /* NodeKind.UnaryExpression */, operator, argument, line, col, ...this.endLoc() };
@@ -457,9 +429,9 @@ class Parser {
457
429
  parseMemberAccessExpression() {
458
430
  let object = this.parsePrimaryExpression();
459
431
  // 循环处理连续的 @ 访问
460
- for (let startToken; (startToken = this.current()).kind === 25 /* TokenKind.ACCESSOR */;) {
432
+ for (let startToken; (startToken = this.current()).kind === 28 /* TokenKind.ACCESSOR */;) {
461
433
  const { kind: tokenKind, value } = this.next();
462
- if (tokenKind === 25 /* TokenKind.ACCESSOR */) {
434
+ if (tokenKind === 28 /* TokenKind.ACCESSOR */) {
463
435
  this.makeErrorTail(0 /* TokenKind.ERROR */, `不能连着写一长串「${value}」喵!`);
464
436
  continue;
465
437
  }
@@ -470,73 +442,86 @@ class Parser {
470
442
  return object;
471
443
  }
472
444
  parsePrimaryExpression() {
473
- const startToken = this.drainCommentsAhead();
474
- const leading = this.takeLeadingComments();
445
+ const startToken = this.current();
446
+ const leading = this.takeUnclaimedComments();
475
447
  const { kind: tokenKind, value, line, col } = startToken;
476
448
  let node;
477
449
  switch (tokenKind) {
478
- case 15 /* TokenKind.NUMBER */:
479
- node = {
480
- kind: 9 /* NodeKind.NumericLiteral */,
481
- value: Number.parseFloat(value),
482
- line,
483
- col,
484
- ...this.endLoc(startToken),
485
- };
450
+ case 19 /* TokenKind.NUMBER */:
451
+ node = { kind: 9 /* NodeKind.NumericLiteral */, value: +value, line, col, ...this.endLoc(startToken) };
486
452
  break;
487
- case 16 /* TokenKind.STRING */:
453
+ case 20 /* TokenKind.STRING */:
488
454
  node = { kind: 10 /* NodeKind.StringLiteral */, value, line, col, ...this.endLoc(startToken) };
489
455
  break;
490
- case 17 /* TokenKind.BOOLEAN */:
456
+ case 21 /* TokenKind.BOOLEAN */:
491
457
  node = { kind: 11 /* NodeKind.BooleanLiteral */, value: value === '好喵', line, col, ...this.endLoc(startToken) };
492
458
  break;
493
- case 18 /* TokenKind.NULL */:
459
+ case 22 /* TokenKind.NULL */:
494
460
  node = { kind: 12 /* NodeKind.NullLiteral */, value: null, line, col, ...this.endLoc(startToken) };
495
461
  break;
496
- case 19 /* TokenKind.IDENTIFIER */:
462
+ case 23 /* TokenKind.IDENTIFIER */:
497
463
  node = { kind: 13 /* NodeKind.Identifier */, symbol: value, line, col, ...this.endLoc(startToken) };
498
464
  break;
499
- case 13 /* TokenKind.KEYWORD_CALL */:
465
+ case 17 /* TokenKind.KEYWORD_CALL */:
500
466
  return this.parseCallExpression();
501
- case 22 /* TokenKind.BLOCK_START */:
467
+ case 26 /* TokenKind.BLOCK_START */:
502
468
  return this.parseBlockOrIfExpression();
503
- case 20 /* TokenKind.COLLECTION_START */:
469
+ case 24 /* TokenKind.COLLECTION_START */:
504
470
  return this.parseBlockExpression(true);
505
- case 9 /* TokenKind.KEYWORD_LOOP */:
471
+ case 13 /* TokenKind.KEYWORD_LOOP */:
506
472
  return this.parseLoopExpression();
507
- case 24 /* TokenKind.TERMINATOR */:
508
- case 44 /* TokenKind.EOF */:
473
+ case 3 /* TokenKind.TERMINATOR */:
474
+ case 1 /* TokenKind.EOF */:
509
475
  throw syntaxErrorFrom(startToken, '只说半句看不懂喵!');
510
476
  case 0 /* TokenKind.ERROR */:
511
477
  throw errorFrom(startToken, `这个词不对劲喵:${value}`, 2 /* Phase.LEXICAL */);
512
478
  default:
513
479
  throw syntaxErrorFrom(startToken, `看不懂的把戏喵:${value}`);
514
480
  }
515
- this.advance();
481
+ this.next();
516
482
  if (leading.length)
517
483
  (node.leadingComments ??= []).push(...leading);
518
484
  return node;
519
485
  }
520
486
  parseBlockOrIfExpression() {
521
487
  const block = this.parseBlockExpression(false);
522
- return this.current().kind === 7 /* TokenKind.KEYWORD_CONFIRM */ ? this.parseIfExpression(block) : block;
488
+ return this.current().kind === 11 /* TokenKind.KEYWORD_CONFIRM */ ? this.parseIfExpression(block) : block;
523
489
  }
524
490
  parseBlockExpression(isCollection) {
525
491
  const modeSwitch = +isCollection;
526
492
  const { name, startKind, startValue, separatorKind, endKind, endValue } = blockKindInfo[modeSwitch];
527
493
  const { line, col } = this.expect(startKind, `${name}需要以「${startValue}」开头喵!`);
528
- const statementParser = isCollection ? () => this.parseCollectionElement() : () => this.parseStatement();
529
494
  const body = [];
530
495
  this.blockDepth[modeSwitch]++;
531
- for (let tokenKind = this.current().kind; tokenKind !== endKind && tokenKind !== 44 /* TokenKind.EOF */; tokenKind = this.next().kind) {
532
- if (tokenKind === separatorKind) {
533
- if (isCollection)
496
+ if (isCollection) {
497
+ collection: for (let tokenKind; (tokenKind = this.current().kind) !== endKind &&
498
+ tokenKind !== 3 /* TokenKind.TERMINATOR */ &&
499
+ tokenKind !== 1 /* TokenKind.EOF */;) {
500
+ if (tokenKind === separatorKind) {
534
501
  this.makeErrorTail(0 /* TokenKind.ERROR */, `这个「${this.current().value}」是多余的喵!`);
535
- continue;
502
+ this.next();
503
+ continue;
504
+ }
505
+ body.push(this.parseCollectionElement());
506
+ const trailingKind = this.current().kind;
507
+ switch (trailingKind) {
508
+ case 16 /* TokenKind.KEYWORD_DEF */:
509
+ case 18 /* TokenKind.KEYWORD_RETURN */:
510
+ case 15 /* TokenKind.KEYWORD_AMBUSH */:
511
+ case 14 /* TokenKind.KEYWORD_BREAK */:
512
+ case 25 /* TokenKind.COLLECTION_END */:
513
+ break collection;
514
+ default:
515
+ this.expect(separatorKind, `要用「,」隔开才好看喵!`);
516
+ }
517
+ }
518
+ }
519
+ else {
520
+ for (let tokenKind; (tokenKind = this.current().kind) !== endKind && tokenKind !== 1 /* TokenKind.EOF */;) {
521
+ if (tokenKind === separatorKind && (this.next(), true))
522
+ continue;
523
+ body.push(this.parseStatement());
536
524
  }
537
- body.push(statementParser());
538
- if (this.current().kind !== separatorKind)
539
- break;
540
525
  }
541
526
  this.expect(endKind, `${name}需要以「${endValue}」结尾喵!`);
542
527
  this.blockDepth[modeSwitch]--;
@@ -544,10 +529,10 @@ class Parser {
544
529
  }
545
530
  parseCollectionElement() {
546
531
  const tokenKind = this.current().kind;
547
- let hasUse = tokenKind === 1 /* TokenKind.KEYWORD_USE */;
532
+ let hasUse = tokenKind === 5 /* TokenKind.KEYWORD_USE */;
548
533
  // 模式一: 显式声明,例如 `蹭 a 就是 1`
549
534
  // 模式二: 隐式声明,例如 `a 就是 1`
550
- if (hasUse || (tokenKind === 19 /* TokenKind.IDENTIFIER */ && isAssignmentOperator(this.peek().kind))) {
535
+ if (hasUse || (tokenKind === 23 /* TokenKind.IDENTIFIER */ && isAssignmentOperator(this.peek().kind))) {
551
536
  return this.parseVariableDeclaration(hasUse);
552
537
  }
553
538
  // 模式三:其他所有情况,都是一个独立的表达式
@@ -556,29 +541,29 @@ class Parser {
556
541
  return { kind: 8 /* NodeKind.ExpressionStatement */, expression, line, col, ...this.endLoc() };
557
542
  }
558
543
  parseIfExpression(consequent) {
559
- this.advance();
544
+ this.next();
560
545
  const condition = this.parseExpression();
561
546
  let alternate;
562
547
  const tokenKind = this.current().kind;
563
- if (tokenKind === 8 /* TokenKind.KEYWORD_ELSE */ ||
564
- (tokenKind === 24 /* TokenKind.TERMINATOR */ &&
565
- this.peek().kind === 8 /* TokenKind.KEYWORD_ELSE */ &&
548
+ if (tokenKind === 12 /* TokenKind.KEYWORD_ELSE */ ||
549
+ (tokenKind === 3 /* TokenKind.TERMINATOR */ &&
550
+ this.peek().kind === 12 /* TokenKind.KEYWORD_ELSE */ &&
566
551
  (this.next() /* 吃掉可选「~」*/, true))) {
567
- if (this.next().kind === 22 /* TokenKind.BLOCK_START */)
552
+ if (this.next().kind === 26 /* TokenKind.BLOCK_START */)
568
553
  alternate = this.parseBlockOrIfExpression();
569
554
  else
570
- this.makeErrorTail(22 /* TokenKind.BLOCK_START */, `要有想法「[#...#]」喵!`);
555
+ this.makeErrorTail(26 /* TokenKind.BLOCK_START */, `要有想法「[#...#]」喵!`);
571
556
  }
572
557
  const { line, col } = consequent;
573
558
  return { kind: 15 /* NodeKind.IfExpression */, consequent, condition, alternate, line, col, ...this.endLoc() };
574
559
  }
575
560
  parseLoopExpression() {
576
- const { line, col } = this.advance();
561
+ const { line, col } = (this.next(), this.prevOne);
577
562
  const body = this.parseBlockExpression(false);
578
563
  return { kind: 16 /* NodeKind.LoopExpression */, body, line, col, ...this.endLoc() };
579
564
  }
580
565
  parseCallExpression() {
581
- const { line, col } = this.advance();
566
+ const { line, col } = (this.next(), this.prevOne);
582
567
  const args = this.parseExpression();
583
568
  const callee = this.parseIdentifier();
584
569
  return { kind: 17 /* NodeKind.CallExpression */, args, callee, line, col, ...this.endLoc() };