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.
- package/LANGUAGE_GUIDE.md +1 -0
- package/README.md +385 -336
- package/dist/api/serviceState.d.ts +1 -1
- package/dist/api/serviceState.d.ts.map +1 -1
- package/dist/api/serviceState.js +1 -1
- package/dist/api/serviceState.js.map +1 -1
- package/dist/api/services/completions.d.ts.map +1 -1
- package/dist/api/services/completions.js +4 -3
- package/dist/api/services/completions.js.map +1 -1
- package/dist/api/services/formatting.d.ts +6 -1
- package/dist/api/services/formatting.d.ts.map +1 -1
- package/dist/api/services/formatting.js +156 -146
- package/dist/api/services/formatting.js.map +1 -1
- package/dist/api/services/highlight.d.ts.map +1 -1
- package/dist/api/services/highlight.js +28 -38
- package/dist/api/services/highlight.js.map +1 -1
- package/dist/api/services/inlayHints.d.ts +1 -1
- package/dist/api/services/inlayHints.d.ts.map +1 -1
- package/dist/api/services/inlayHints.js +4 -8
- package/dist/api/services/inlayHints.js.map +1 -1
- package/dist/api/services/search.d.ts.map +1 -1
- package/dist/api/services/search.js +7 -3
- package/dist/api/services/search.js.map +1 -1
- package/dist/api/utils/astUtils.d.ts.map +1 -1
- package/dist/api/utils/astUtils.js +18 -12
- package/dist/api/utils/astUtils.js.map +1 -1
- package/dist/api/utils/symbolAnalyzer.d.ts +1 -1
- package/dist/api/utils/symbolAnalyzer.d.ts.map +1 -1
- package/dist/api/utils/symbolAnalyzer.js +60 -63
- package/dist/api/utils/symbolAnalyzer.js.map +1 -1
- package/dist/cli/main.d.ts +0 -1
- package/dist/cli/main.js +3 -9
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/tools/formatter.d.ts.map +1 -1
- package/dist/cli/tools/formatter.js +13 -3
- package/dist/cli/tools/formatter.js.map +1 -1
- package/dist/cli/tools/searcher.js +1 -1
- package/dist/cli/tools/searcher.js.map +1 -1
- package/dist/cli/tools/starter.d.ts +1 -2
- package/dist/cli/tools/starter.d.ts.map +1 -1
- package/dist/cli/tools/starter.js +5 -3
- package/dist/cli/tools/starter.js.map +1 -1
- package/dist/cli/tools/toolUtils.d.ts.map +1 -1
- package/dist/cli/tools/toolUtils.js +9 -13
- package/dist/cli/tools/toolUtils.js.map +1 -1
- package/dist/core/ast.d.ts +3 -2
- package/dist/core/ast.d.ts.map +1 -1
- package/dist/core/builtIns.d.ts.map +1 -1
- package/dist/core/builtIns.js +3 -1
- package/dist/core/builtIns.js.map +1 -1
- package/dist/core/error.d.ts +1 -0
- package/dist/core/error.d.ts.map +1 -1
- package/dist/core/error.js +4 -11
- package/dist/core/error.js.map +1 -1
- package/dist/core/lexer/preprocessor.d.ts.map +1 -1
- package/dist/core/lexer/preprocessor.js +30 -57
- package/dist/core/lexer/preprocessor.js.map +1 -1
- package/dist/core/lexer/tokenizer.d.ts +52 -52
- package/dist/core/lexer/tokenizer.d.ts.map +1 -1
- package/dist/core/lexer/tokenizer.js +59 -52
- package/dist/core/lexer/tokenizer.js.map +1 -1
- package/dist/core/parser.d.ts.map +1 -1
- package/dist/core/parser.js +194 -209
- package/dist/core/parser.js.map +1 -1
- package/dist/core/run/environment.js +3 -3
- package/dist/core/run/interpreter.d.ts.map +1 -1
- package/dist/core/run/interpreter.js +152 -158
- package/dist/core/run/interpreter.js.map +1 -1
- package/dist/core/run/io.d.ts.map +1 -1
- package/dist/core/run/io.js +4 -0
- package/dist/core/run/io.js.map +1 -1
- package/dist/core/typedef.js +5 -5
- package/package.json +3 -3
package/dist/core/parser.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
// src/core/parser.ts
|
|
2
2
|
import { MeaoiuError, errorFrom } from './error.js';
|
|
3
|
-
import { newToken
|
|
3
|
+
import { newToken } from './lexer/tokenizer.js';
|
|
4
4
|
const blockKindInfo = [
|
|
5
5
|
{
|
|
6
6
|
name: '想法',
|
|
7
|
-
startKind:
|
|
7
|
+
startKind: 26 /* TokenKind.BLOCK_START */,
|
|
8
8
|
startValue: '[#',
|
|
9
|
-
separatorKind:
|
|
10
|
-
endKind:
|
|
9
|
+
separatorKind: 3 /* TokenKind.TERMINATOR */,
|
|
10
|
+
endKind: 27 /* TokenKind.BLOCK_END */,
|
|
11
11
|
endValue: '#]',
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
name: '纸箱',
|
|
15
|
-
startKind:
|
|
15
|
+
startKind: 24 /* TokenKind.COLLECTION_START */,
|
|
16
16
|
startValue: '[=',
|
|
17
|
-
separatorKind:
|
|
18
|
-
endKind:
|
|
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
|
|
35
|
-
case
|
|
36
|
-
case
|
|
37
|
-
case
|
|
38
|
-
case
|
|
39
|
-
case
|
|
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
|
|
39
|
+
function isStartKind(kind) {
|
|
46
40
|
switch (kind) {
|
|
47
|
-
case
|
|
48
|
-
case
|
|
49
|
-
case
|
|
50
|
-
case
|
|
51
|
-
case
|
|
52
|
-
case
|
|
53
|
-
case
|
|
54
|
-
case
|
|
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 ===
|
|
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
|
-
//
|
|
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 !==
|
|
76
|
-
const EOF = newToken(
|
|
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
|
-
//
|
|
87
|
-
peek(
|
|
88
|
-
return this.tokens[Math.min(this.position +
|
|
80
|
+
// 前视一位
|
|
81
|
+
peek() {
|
|
82
|
+
return this.tokens[Math.min(this.position + 1, this.MAX_POS)];
|
|
89
83
|
}
|
|
90
84
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* @returns 当前位置的 token(非 COMMENT)
|
|
85
|
+
* 推进位置并取得下一词元,同时将途经悄悄话收入 commentBuffer
|
|
86
|
+
* @returns 下一个非悄悄话词元(可能为 EOF)
|
|
94
87
|
*/
|
|
95
|
-
|
|
96
|
-
|
|
88
|
+
next() {
|
|
89
|
+
this.prevOne = this.current();
|
|
97
90
|
if (this.position < this.MAX_POS)
|
|
98
91
|
this.position++;
|
|
99
|
-
return
|
|
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 ===
|
|
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
|
-
*
|
|
107
|
+
* 从缓存中拿走所有待分配的悄悄话并清空缓存
|
|
135
108
|
* @returns 所有待分配的悄悄话
|
|
136
109
|
*/
|
|
137
|
-
|
|
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
|
|
159
|
-
this.commentBuffer = remaining;
|
|
131
|
+
this.commentBuffer = remaining; // 将非 trailing 的悄悄话保留为新 commentBuffer
|
|
160
132
|
}
|
|
161
|
-
endLoc({ endLine, endCol } = this.
|
|
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
|
-
|
|
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
|
-
|
|
184
|
-
|
|
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 !==
|
|
192
|
-
if (tokenKind ===
|
|
193
|
-
return void this.
|
|
194
|
-
if (
|
|
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 ===
|
|
197
|
-
if (!depthIndex && tokenKind !==
|
|
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 !==
|
|
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
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
231
|
-
const
|
|
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
|
|
201
|
+
case 5 /* TokenKind.KEYWORD_USE */:
|
|
237
202
|
node = this.parseVariableDeclaration(true);
|
|
238
203
|
break;
|
|
239
|
-
case
|
|
204
|
+
case 16 /* TokenKind.KEYWORD_DEF */:
|
|
240
205
|
node = this.parseFunctionDeclaration();
|
|
241
206
|
break;
|
|
242
|
-
case
|
|
207
|
+
case 18 /* TokenKind.KEYWORD_RETURN */:
|
|
243
208
|
node = this.parseReturnOrAmbushStatement(5 /* NodeKind.ReturnStatement */);
|
|
244
209
|
break;
|
|
245
|
-
case
|
|
210
|
+
case 15 /* TokenKind.KEYWORD_AMBUSH */:
|
|
246
211
|
node = this.parseReturnOrAmbushStatement(6 /* NodeKind.AmbushStatement */);
|
|
247
212
|
break;
|
|
248
|
-
case
|
|
249
|
-
this.
|
|
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
|
|
253
|
-
case
|
|
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
|
-
//
|
|
267
|
-
const
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
309
|
-
const argument = this.current().kind !==
|
|
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.
|
|
314
|
-
const
|
|
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
|
-
|
|
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
|
|
336
|
-
this.
|
|
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
|
|
306
|
+
case 42 /* TokenKind.LOGIC_CLOSE_OR */:
|
|
342
307
|
if (!isOr)
|
|
343
308
|
this.reportError(syntaxErrorFrom(closeToken, '逻辑「和」不能用「不坏」闭合喵!'));
|
|
344
309
|
break;
|
|
345
|
-
case
|
|
310
|
+
case 44 /* TokenKind.LOGIC_CLOSE_NAND */:
|
|
346
311
|
if (!isOr)
|
|
347
312
|
this.reportError(syntaxErrorFrom(closeToken, '逻辑「和」不能用「不好」闭合喵!'));
|
|
348
313
|
break;
|
|
349
|
-
case
|
|
314
|
+
case 41 /* TokenKind.LOGIC_CLOSE_AND */:
|
|
350
315
|
if (isOr)
|
|
351
316
|
this.reportError(syntaxErrorFrom(closeToken, '逻辑「或」不能用「都好」闭合喵!'));
|
|
352
317
|
break;
|
|
353
|
-
case
|
|
318
|
+
case 43 /* TokenKind.LOGIC_CLOSE_NOR */:
|
|
354
319
|
if (isOr)
|
|
355
320
|
this.reportError(syntaxErrorFrom(closeToken, '逻辑「或」不能用「都坏」闭合喵!'));
|
|
356
321
|
break;
|
|
357
|
-
case
|
|
358
|
-
case
|
|
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.
|
|
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 ===
|
|
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
|
|
379
|
-
case
|
|
380
|
-
case
|
|
381
|
-
case
|
|
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
|
|
386
|
-
case
|
|
350
|
+
case 33 /* TokenKind.COMPARISON_EQUAL */:
|
|
351
|
+
case 34 /* TokenKind.COMPARISON_NOT_EQUAL */:
|
|
387
352
|
isArithmeticMode = false; // 禁用算术模式
|
|
388
353
|
break;
|
|
389
|
-
case
|
|
390
|
-
case
|
|
391
|
-
case
|
|
392
|
-
case
|
|
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.
|
|
398
|
-
this.
|
|
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.
|
|
373
|
+
const expressions = [this.parseArithmeticExpression(0 /* ArithmeticPrecedence.NONE */)];
|
|
409
374
|
const operators = [];
|
|
410
|
-
for (let token; isComparisonToken((token = this.current())) && this.peek().kind !==
|
|
411
|
-
this.
|
|
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.
|
|
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
|
-
|
|
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
|
|
433
|
-
const
|
|
434
|
-
|
|
435
|
-
|
|
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
|
|
444
|
-
operator =
|
|
415
|
+
case 9 /* TokenKind.KEYWORD_CLONE */:
|
|
416
|
+
operator = 7 /* TokenKind.ASSIGNMENT_LIKE */;
|
|
445
417
|
break;
|
|
446
|
-
case
|
|
447
|
-
operator =
|
|
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.
|
|
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 ===
|
|
432
|
+
for (let startToken; (startToken = this.current()).kind === 28 /* TokenKind.ACCESSOR */;) {
|
|
461
433
|
const { kind: tokenKind, value } = this.next();
|
|
462
|
-
if (tokenKind ===
|
|
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.
|
|
474
|
-
const leading = this.
|
|
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
|
|
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
|
|
453
|
+
case 20 /* TokenKind.STRING */:
|
|
488
454
|
node = { kind: 10 /* NodeKind.StringLiteral */, value, line, col, ...this.endLoc(startToken) };
|
|
489
455
|
break;
|
|
490
|
-
case
|
|
456
|
+
case 21 /* TokenKind.BOOLEAN */:
|
|
491
457
|
node = { kind: 11 /* NodeKind.BooleanLiteral */, value: value === '好喵', line, col, ...this.endLoc(startToken) };
|
|
492
458
|
break;
|
|
493
|
-
case
|
|
459
|
+
case 22 /* TokenKind.NULL */:
|
|
494
460
|
node = { kind: 12 /* NodeKind.NullLiteral */, value: null, line, col, ...this.endLoc(startToken) };
|
|
495
461
|
break;
|
|
496
|
-
case
|
|
462
|
+
case 23 /* TokenKind.IDENTIFIER */:
|
|
497
463
|
node = { kind: 13 /* NodeKind.Identifier */, symbol: value, line, col, ...this.endLoc(startToken) };
|
|
498
464
|
break;
|
|
499
|
-
case
|
|
465
|
+
case 17 /* TokenKind.KEYWORD_CALL */:
|
|
500
466
|
return this.parseCallExpression();
|
|
501
|
-
case
|
|
467
|
+
case 26 /* TokenKind.BLOCK_START */:
|
|
502
468
|
return this.parseBlockOrIfExpression();
|
|
503
|
-
case
|
|
469
|
+
case 24 /* TokenKind.COLLECTION_START */:
|
|
504
470
|
return this.parseBlockExpression(true);
|
|
505
|
-
case
|
|
471
|
+
case 13 /* TokenKind.KEYWORD_LOOP */:
|
|
506
472
|
return this.parseLoopExpression();
|
|
507
|
-
case
|
|
508
|
-
case
|
|
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.
|
|
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 ===
|
|
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
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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
|
-
|
|
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 ===
|
|
532
|
+
let hasUse = tokenKind === 5 /* TokenKind.KEYWORD_USE */;
|
|
548
533
|
// 模式一: 显式声明,例如 `蹭 a 就是 1`
|
|
549
534
|
// 模式二: 隐式声明,例如 `a 就是 1`
|
|
550
|
-
if (hasUse || (tokenKind ===
|
|
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.
|
|
544
|
+
this.next();
|
|
560
545
|
const condition = this.parseExpression();
|
|
561
546
|
let alternate;
|
|
562
547
|
const tokenKind = this.current().kind;
|
|
563
|
-
if (tokenKind ===
|
|
564
|
-
(tokenKind ===
|
|
565
|
-
this.peek().kind ===
|
|
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 ===
|
|
552
|
+
if (this.next().kind === 26 /* TokenKind.BLOCK_START */)
|
|
568
553
|
alternate = this.parseBlockOrIfExpression();
|
|
569
554
|
else
|
|
570
|
-
this.makeErrorTail(
|
|
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.
|
|
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.
|
|
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() };
|