bobe 0.0.3 → 0.0.6
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/README.md +0 -1
- package/dist/bobe.cjs.js +285 -560
- package/dist/bobe.cjs.js.map +1 -0
- package/dist/bobe.esm.js +285 -557
- package/dist/bobe.esm.js.map +1 -0
- package/dist/index.d.ts +206 -0
- package/dist/index.umd.js +412 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +14 -12
- package/dist/bobe.umd.js +0 -689
- package/dist/compiler/src/__test__/hello.d.ts +0 -0
- package/dist/compiler/src/index.d.ts +0 -138
- package/dist/shared/util.d.ts +0 -22
package/dist/bobe.cjs.js
CHANGED
|
@@ -1,425 +1,66 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var TokenType = /* @__PURE__ */ ((TokenType2) => {
|
|
4
|
+
TokenType2[TokenType2["NewLine"] = 1] = "NewLine";
|
|
5
|
+
TokenType2[TokenType2["Indent"] = 2] = "Indent";
|
|
6
|
+
TokenType2[TokenType2["Dedent"] = 4] = "Dedent";
|
|
7
|
+
TokenType2[TokenType2["Identifier"] = 8] = "Identifier";
|
|
8
|
+
TokenType2[TokenType2["Assign"] = 16] = "Assign";
|
|
9
|
+
TokenType2[TokenType2["Pipe"] = 32] = "Pipe";
|
|
10
|
+
TokenType2[TokenType2["Eof"] = 64] = "Eof";
|
|
11
|
+
return TokenType2;
|
|
12
|
+
})(TokenType || {});
|
|
13
|
+
var LogicType = /* @__PURE__ */ ((LogicType2) => {
|
|
14
|
+
LogicType2[LogicType2["If"] = 1] = "If";
|
|
15
|
+
LogicType2[LogicType2["ElseIf"] = 2] = "ElseIf";
|
|
16
|
+
LogicType2[LogicType2["Else"] = 4] = "Else";
|
|
17
|
+
LogicType2[LogicType2["For"] = 8] = "For";
|
|
18
|
+
return LogicType2;
|
|
19
|
+
})(LogicType || {});
|
|
4
20
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
_last: last
|
|
21
|
-
} = this;
|
|
22
|
-
const item = {
|
|
23
|
-
v: it
|
|
24
|
-
};
|
|
25
|
-
if (!last) {
|
|
26
|
-
this._first = this._last = item;
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
item.prev = this._last;
|
|
30
|
-
last.next = item;
|
|
31
|
-
this._last = item;
|
|
32
|
-
}
|
|
33
|
-
shift() {
|
|
34
|
-
const {
|
|
35
|
-
_first: first
|
|
36
|
-
} = this;
|
|
37
|
-
if (!first) return undefined;
|
|
38
|
-
this.len--;
|
|
39
|
-
const {
|
|
40
|
-
next
|
|
41
|
-
} = first;
|
|
42
|
-
first.next = undefined;
|
|
43
|
-
if (next) {
|
|
44
|
-
next.prev = undefined;
|
|
45
|
-
} else {
|
|
46
|
-
this._last = undefined;
|
|
47
|
-
}
|
|
48
|
-
this._first = next;
|
|
49
|
-
return first.v;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
function isNum(char) {
|
|
53
|
-
return char === '0' || char === '1' || char === '2' || char === '3' || char === '4' || char === '5' || char === '6' || char === '7' || char === '8' || char === '9';
|
|
54
|
-
}
|
|
55
|
-
// const queue = new Queue([1,2,3,4]);
|
|
56
|
-
// queue.shift()
|
|
57
|
-
// queue.pop()
|
|
58
|
-
// // @ts-ignore
|
|
59
|
-
// queue.unshift('a')
|
|
60
|
-
// // @ts-ignore
|
|
61
|
-
// queue.push('b')
|
|
62
|
-
// queue.shift()
|
|
63
|
-
// queue.pop()
|
|
64
|
-
// queue.shift()
|
|
65
|
-
// queue.pop()
|
|
66
|
-
// queue.shift()
|
|
67
|
-
// queue.pop()
|
|
68
|
-
// queue.push(10)
|
|
69
|
-
// queue.array();
|
|
70
|
-
|
|
71
|
-
exports.TokenType = void 0;
|
|
72
|
-
(function (TokenType) {
|
|
73
|
-
TokenType[TokenType["NewLine"] = 0] = "NewLine";
|
|
74
|
-
TokenType[TokenType["Indent"] = 1] = "Indent";
|
|
75
|
-
TokenType[TokenType["Dedent"] = 2] = "Dedent";
|
|
76
|
-
TokenType[TokenType["Identifier"] = 3] = "Identifier";
|
|
77
|
-
TokenType[TokenType["Assign"] = 4] = "Assign";
|
|
78
|
-
TokenType[TokenType["Pipe"] = 5] = "Pipe";
|
|
79
|
-
TokenType[TokenType["Eof"] = 6] = "Eof";
|
|
80
|
-
})(exports.TokenType || (exports.TokenType = {}));
|
|
81
|
-
class Compiler {
|
|
82
|
-
get char() {
|
|
83
|
-
return this.code[this.i];
|
|
84
|
-
}
|
|
85
|
-
get prev() {
|
|
86
|
-
return this.code[this.i - 1];
|
|
87
|
-
}
|
|
88
|
-
get after() {
|
|
89
|
-
return this.code[this.i + 1];
|
|
90
|
-
}
|
|
91
|
-
at(i) {
|
|
92
|
-
return this.code[i];
|
|
93
|
-
}
|
|
94
|
-
next() {
|
|
95
|
-
const prev = this.code[this.i];
|
|
96
|
-
this.i++;
|
|
97
|
-
const curr = this.code[this.i];
|
|
98
|
-
return [prev, curr];
|
|
99
|
-
}
|
|
100
|
-
nextToken() {
|
|
101
|
-
// 已遍历到文件结尾
|
|
102
|
-
if (this.isEof()) {
|
|
103
|
-
return this.token;
|
|
21
|
+
var __defProp = Object.defineProperty;
|
|
22
|
+
var __defProps = Object.defineProperties;
|
|
23
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
24
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
25
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
26
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
27
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
28
|
+
var __spreadValues = (a, b) => {
|
|
29
|
+
for (var prop in b || (b = {}))
|
|
30
|
+
if (__hasOwnProp.call(b, prop))
|
|
31
|
+
__defNormalProp(a, prop, b[prop]);
|
|
32
|
+
if (__getOwnPropSymbols)
|
|
33
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
34
|
+
if (__propIsEnum.call(b, prop))
|
|
35
|
+
__defNormalProp(a, prop, b[prop]);
|
|
104
36
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// 遍历到当前标识符非 空白为止
|
|
115
|
-
} else {
|
|
116
|
-
let {
|
|
117
|
-
char
|
|
118
|
-
} = this;
|
|
119
|
-
switch (char) {
|
|
120
|
-
case '\t':
|
|
121
|
-
case ' ':
|
|
122
|
-
// skip, 缩进通过 \n 匹配来激活 needIndent
|
|
123
|
-
break;
|
|
124
|
-
// 找后续所有 newLine
|
|
125
|
-
case '\n':
|
|
126
|
-
this.tokenCreator.newLine();
|
|
127
|
-
// 回车后需要判断缩进
|
|
128
|
-
this.needIndent = true;
|
|
129
|
-
break;
|
|
130
|
-
case '=':
|
|
131
|
-
this.tokenCreator.assignment();
|
|
132
|
-
break;
|
|
133
|
-
case '|':
|
|
134
|
-
this.tokenCreator.pipe();
|
|
135
|
-
break;
|
|
136
|
-
case "'":
|
|
137
|
-
case '"':
|
|
138
|
-
this.tokenCreator.str(char);
|
|
139
|
-
break;
|
|
140
|
-
case '$':
|
|
141
|
-
const handled = this.tokenCreator.dynamic(char);
|
|
142
|
-
if (handled) break;
|
|
143
|
-
default:
|
|
144
|
-
if (isNum(char)) {
|
|
145
|
-
this.tokenCreator.number(char);
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
if (this.testId(char)) {
|
|
149
|
-
this.tokenCreator.identifier(char);
|
|
150
|
-
}
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
// 指向下一个字符
|
|
154
|
-
this.next();
|
|
155
|
-
}
|
|
156
|
-
// 找到 token 即可停止
|
|
157
|
-
if (this.token) {
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return this.token;
|
|
162
|
-
}
|
|
163
|
-
consume() {
|
|
164
|
-
const token = this.token;
|
|
165
|
-
this.nextToken();
|
|
166
|
-
return token;
|
|
167
|
-
}
|
|
168
|
-
tokenize() {
|
|
169
|
-
var _a, _b;
|
|
170
|
-
do {
|
|
171
|
-
this.nextToken();
|
|
172
|
-
console.log('token:', exports.TokenType[(_a = this.token) === null || _a === void 0 ? void 0 : _a.type], JSON.stringify(((_b = this.token) === null || _b === void 0 ? void 0 : _b.value) || ''));
|
|
173
|
-
} while (!this.isEof());
|
|
174
|
-
}
|
|
175
|
-
constructor() {
|
|
176
|
-
this.i = 0;
|
|
177
|
-
this.tokenIs = (...types) => {
|
|
178
|
-
if (types.length === 1) return types[0] === this.token.type;
|
|
179
|
-
return types.includes(this.token.type);
|
|
180
|
-
};
|
|
181
|
-
this.isEof = () => {
|
|
182
|
-
// 刚开始时 token 不存在
|
|
183
|
-
if (!this.token) return false;
|
|
184
|
-
return this.tokenIs(exports.TokenType.Identifier) && this.token.value === this.EofId;
|
|
185
|
-
};
|
|
186
|
-
this.setToken = (type, value) => {
|
|
187
|
-
this.token = {
|
|
188
|
-
type,
|
|
189
|
-
typeName: exports.TokenType[type],
|
|
190
|
-
value
|
|
191
|
-
};
|
|
192
|
-
this.isFirstToken = false;
|
|
193
|
-
};
|
|
194
|
-
this.TabSize = 2;
|
|
195
|
-
this.Tab = Array.from({
|
|
196
|
-
length: this.TabSize
|
|
197
|
-
}, () => ' ').join('');
|
|
198
|
-
this.IdExp = /[\d\w\/]/;
|
|
199
|
-
this.EofId = `__EOF__${Date.now()}`;
|
|
200
|
-
this.testId = value => {
|
|
201
|
-
if (typeof value !== 'string') return false;
|
|
202
|
-
return this.IdExp.test(value);
|
|
203
|
-
};
|
|
204
|
-
/** 记录历史缩进的长度,相对于行首 */
|
|
205
|
-
this.dentStack = [0];
|
|
206
|
-
this.needIndent = false;
|
|
207
|
-
this.isFirstToken = true;
|
|
208
|
-
/**
|
|
209
|
-
* 有些标识符能产生多个 token
|
|
210
|
-
* 例如 dedent
|
|
211
|
-
* parent1
|
|
212
|
-
* child
|
|
213
|
-
* subChild
|
|
214
|
-
* parent2 <- 产生两个 dedent
|
|
215
|
-
*/
|
|
216
|
-
this.waitingTokens = new Queue();
|
|
217
|
-
this.tokenCreator = {
|
|
218
|
-
assignment: () => {
|
|
219
|
-
this.setToken(exports.TokenType.Assign, '=');
|
|
220
|
-
},
|
|
221
|
-
pipe: () => {
|
|
222
|
-
this.setToken(exports.TokenType.Pipe, '|');
|
|
223
|
-
},
|
|
224
|
-
dynamic: char => {
|
|
225
|
-
let nextC = this.after;
|
|
226
|
-
// 不是动态插值
|
|
227
|
-
if (nextC !== '{') {
|
|
228
|
-
return false;
|
|
229
|
-
}
|
|
230
|
-
this.next();
|
|
231
|
-
let value = '${';
|
|
232
|
-
let innerBrace = 0;
|
|
233
|
-
while (1) {
|
|
234
|
-
nextC = this.after;
|
|
235
|
-
value += nextC;
|
|
236
|
-
// 下一个属于本标识符再前进
|
|
237
|
-
this.next();
|
|
238
|
-
if (nextC === '{') {
|
|
239
|
-
innerBrace++;
|
|
240
|
-
}
|
|
241
|
-
if (nextC === '}') {
|
|
242
|
-
// 内部无左括号,说明完成匹配 TODO: 考虑js注释中的括号可能导致匹配错误
|
|
243
|
-
if (!innerBrace) {
|
|
244
|
-
break;
|
|
245
|
-
}
|
|
246
|
-
innerBrace--;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
this.setToken(exports.TokenType.Identifier, value);
|
|
250
|
-
return true;
|
|
251
|
-
},
|
|
252
|
-
newLine: () => {
|
|
253
|
-
let value = '\n';
|
|
254
|
-
let nextC;
|
|
255
|
-
while (1) {
|
|
256
|
-
nextC = this.after;
|
|
257
|
-
if (nextC !== '\n') {
|
|
258
|
-
break;
|
|
259
|
-
}
|
|
260
|
-
value += nextC;
|
|
261
|
-
// 下一个属于本标识符再前进
|
|
262
|
-
this.next();
|
|
263
|
-
}
|
|
264
|
-
// Program 希望第一个 token 一定是 node 节点
|
|
265
|
-
if (this.isFirstToken) {
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
this.setToken(exports.TokenType.NewLine, value);
|
|
269
|
-
},
|
|
270
|
-
dent: () => {
|
|
271
|
-
const handleDent = v => {
|
|
272
|
-
switch (v) {
|
|
273
|
-
case '\t':
|
|
274
|
-
return this.Tab;
|
|
275
|
-
case ' ':
|
|
276
|
-
return ' ';
|
|
277
|
-
case '\n':
|
|
278
|
-
return '\n';
|
|
279
|
-
default:
|
|
280
|
-
return '';
|
|
281
|
-
}
|
|
282
|
-
};
|
|
283
|
-
let value = '';
|
|
284
|
-
let nextC;
|
|
285
|
-
while (1) {
|
|
286
|
-
const nextChar = this.char;
|
|
287
|
-
nextC = handleDent(nextChar);
|
|
288
|
-
// \n 空白 \n 的情况,这行不算
|
|
289
|
-
if (nextC === '\n') {
|
|
290
|
-
this.needIndent = true;
|
|
291
|
-
// 这种情况下需要 next ,即后续从 \n 重新开始匹配
|
|
292
|
-
return true;
|
|
293
|
-
}
|
|
294
|
-
// 比较长度,比上个 indent 长,缩进,比上个 indent 短,dedent
|
|
295
|
-
if (!nextC) {
|
|
296
|
-
this.needIndent = false;
|
|
297
|
-
// 期望 firstToken 是 node,所以这里只要修改第一个节点的基础偏移值即可
|
|
298
|
-
if (this.isFirstToken) {
|
|
299
|
-
this.dentStack[0] = value.length;
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
let currLen = value.length;
|
|
303
|
-
const indentHasLen = currLen > 0;
|
|
304
|
-
const prevLen = this.dentStack[this.dentStack.length - 1];
|
|
305
|
-
if (currLen > prevLen) {
|
|
306
|
-
this.dentStack.push(currLen);
|
|
307
|
-
this.setToken(exports.TokenType.Indent, String(currLen));
|
|
308
|
-
return indentHasLen;
|
|
309
|
-
}
|
|
310
|
-
if (currLen < prevLen) {
|
|
311
|
-
// 一直找到最小
|
|
312
|
-
for (let i = this.dentStack.length - 2; i >= 0; i--) {
|
|
313
|
-
const expLen = this.dentStack[i];
|
|
314
|
-
const prevExpLen = this.dentStack[i + 1];
|
|
315
|
-
// 夹在两者说明缩进大小有问题
|
|
316
|
-
if (currLen > expLen && currLen < prevExpLen) {
|
|
317
|
-
throw SyntaxError('缩进大小不统一');
|
|
318
|
-
}
|
|
319
|
-
// current <= expLen 反缩进
|
|
320
|
-
this.dentStack.pop();
|
|
321
|
-
if (!this.token) {
|
|
322
|
-
this.setToken(exports.TokenType.Dedent, String(expLen));
|
|
323
|
-
}
|
|
324
|
-
// 多余的 dent 缓存在 waitingTokens
|
|
325
|
-
else {
|
|
326
|
-
this.waitingTokens.push({
|
|
327
|
-
type: exports.TokenType.Dedent,
|
|
328
|
-
typeName: exports.TokenType[exports.TokenType.Dedent],
|
|
329
|
-
value: String(expLen)
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
if (currLen === expLen) {
|
|
333
|
-
break;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
return indentHasLen;
|
|
337
|
-
}
|
|
338
|
-
// 同级则无视
|
|
339
|
-
return indentHasLen;
|
|
340
|
-
}
|
|
341
|
-
value += nextC;
|
|
342
|
-
this.next();
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
identifier: char => {
|
|
346
|
-
let value = char;
|
|
347
|
-
let nextC;
|
|
348
|
-
while (1) {
|
|
349
|
-
nextC = this.after;
|
|
350
|
-
if (!this.testId(nextC)) {
|
|
351
|
-
break;
|
|
352
|
-
}
|
|
353
|
-
value += nextC;
|
|
354
|
-
this.next();
|
|
355
|
-
}
|
|
356
|
-
let realValue = value === 'null' ? null : value === 'undefined' ? undefined : value === 'false' || value === 'true' ? Boolean(value) : value;
|
|
357
|
-
this.setToken(exports.TokenType.Identifier, realValue);
|
|
358
|
-
},
|
|
359
|
-
str: char => {
|
|
360
|
-
let value = '"';
|
|
361
|
-
let nextC;
|
|
362
|
-
let continuousBackslashCount = 0;
|
|
363
|
-
while (1) {
|
|
364
|
-
nextC = this.after;
|
|
365
|
-
value += nextC;
|
|
366
|
-
const memoCount = continuousBackslashCount;
|
|
367
|
-
if (nextC === '\\') {
|
|
368
|
-
continuousBackslashCount++;
|
|
369
|
-
} else {
|
|
370
|
-
continuousBackslashCount = 0;
|
|
371
|
-
}
|
|
372
|
-
this.next();
|
|
373
|
-
/**
|
|
374
|
-
* 引号前 \ 为双数时,全都是字符 \
|
|
375
|
-
* */
|
|
376
|
-
if (nextC === char && memoCount % 2 === 0) {
|
|
377
|
-
break;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
this.setToken(exports.TokenType.Identifier, JSON.parse(value.slice(0, -1) + '"'));
|
|
381
|
-
},
|
|
382
|
-
number: char => {
|
|
383
|
-
let value = char;
|
|
384
|
-
let nextC;
|
|
385
|
-
while (1) {
|
|
386
|
-
nextC = this.after;
|
|
387
|
-
if (!isNum(nextC)) {
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
390
|
-
value += nextC;
|
|
391
|
-
this.next();
|
|
392
|
-
}
|
|
393
|
-
this.setToken(exports.TokenType.Identifier, Number(value));
|
|
394
|
-
},
|
|
395
|
-
eof: () => {
|
|
396
|
-
this.setToken(exports.TokenType.Eof, 'End Of File');
|
|
397
|
-
}
|
|
398
|
-
};
|
|
399
|
-
this.HookId = '_h_o_o_k_';
|
|
37
|
+
return a;
|
|
38
|
+
};
|
|
39
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
40
|
+
class Interpreter {
|
|
41
|
+
constructor(tokenizer) {
|
|
42
|
+
this.tokenizer = tokenizer;
|
|
43
|
+
/** 模板字符串动态节点的占位符 */
|
|
44
|
+
this.HookId = "_h_o_o_k_";
|
|
45
|
+
/** 用于渲染的数据 */
|
|
400
46
|
this.data = {};
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
47
|
+
/** 模板字符串动态节点索引 */
|
|
48
|
+
this.hookI = 0;
|
|
49
|
+
this.stack = [];
|
|
50
|
+
this._hook = (props) => {
|
|
51
|
+
const value = this.tokenizer.token.value;
|
|
52
|
+
const isHook = typeof value === "string" && value.indexOf(this.HookId) === 0;
|
|
404
53
|
if (this.hook && isHook) {
|
|
405
|
-
const
|
|
406
|
-
|
|
54
|
+
const hookI = Number(value.slice(this.HookId.length));
|
|
55
|
+
const res = this.hook(__spreadProps(__spreadValues({}, props), {
|
|
407
56
|
HookId: this.HookId,
|
|
408
|
-
i:
|
|
409
|
-
});
|
|
57
|
+
i: hookI
|
|
58
|
+
}));
|
|
410
59
|
this.hookI++;
|
|
411
60
|
return [isHook, res];
|
|
412
61
|
}
|
|
413
62
|
return [isHook, value];
|
|
414
63
|
};
|
|
415
|
-
this.hookI = 0;
|
|
416
|
-
}
|
|
417
|
-
preprocess() {
|
|
418
|
-
// 保证开头能通过 换行进行 indent 计算
|
|
419
|
-
this.code = '\n' + this.code;
|
|
420
|
-
// 保证结尾 dedent 能正常配对
|
|
421
|
-
this.code = this.code.trimEnd() + `\n${this.EofId}`;
|
|
422
|
-
// console.log(this.code);
|
|
423
64
|
}
|
|
424
65
|
/**
|
|
425
66
|
* 根节点:
|
|
@@ -427,9 +68,51 @@ class Compiler {
|
|
|
427
68
|
* <program> ::= <nodeList>
|
|
428
69
|
*/
|
|
429
70
|
program() {
|
|
430
|
-
|
|
431
|
-
this.
|
|
432
|
-
|
|
71
|
+
this.tokenizer.consume();
|
|
72
|
+
const _program = this.createRoot();
|
|
73
|
+
this.nodeList(_program);
|
|
74
|
+
return _program;
|
|
75
|
+
}
|
|
76
|
+
experimentalProgram() {
|
|
77
|
+
this.tokenizer.consume();
|
|
78
|
+
let current;
|
|
79
|
+
let prevSibling;
|
|
80
|
+
const rootList = [];
|
|
81
|
+
while (1) {
|
|
82
|
+
if (this.tokenizer.isEof()) {
|
|
83
|
+
rootList.push(current);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
const token = this.tokenizer.token;
|
|
87
|
+
if (token.type & TokenType.Indent) {
|
|
88
|
+
this.tokenizer.consume();
|
|
89
|
+
this.stack.push({
|
|
90
|
+
prevSibling,
|
|
91
|
+
node: current
|
|
92
|
+
});
|
|
93
|
+
prevSibling = null;
|
|
94
|
+
current = this.declaration();
|
|
95
|
+
} else {
|
|
96
|
+
if (current) {
|
|
97
|
+
if (this.stack.length) {
|
|
98
|
+
const parent = this.stack[this.stack.length - 1].node;
|
|
99
|
+
this.insert(parent, current, prevSibling);
|
|
100
|
+
} else {
|
|
101
|
+
rootList.push(current);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (this.tokenizer.token.type & TokenType.Dedent) {
|
|
105
|
+
this.tokenizer.consume();
|
|
106
|
+
const { node: parent, prevSibling: prevParent } = this.stack.pop();
|
|
107
|
+
prevSibling = prevParent;
|
|
108
|
+
current = parent;
|
|
109
|
+
} else {
|
|
110
|
+
prevSibling = current;
|
|
111
|
+
current = this.declaration();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return rootList;
|
|
433
116
|
}
|
|
434
117
|
/**
|
|
435
118
|
* 节点列表:
|
|
@@ -437,24 +120,59 @@ class Compiler {
|
|
|
437
120
|
* <nodeList> ::= <node> <nodeList> <EOF|Dedent>
|
|
438
121
|
* |
|
|
439
122
|
*/
|
|
440
|
-
nodeList() {
|
|
441
|
-
const {
|
|
442
|
-
tokenIs
|
|
443
|
-
} = this;
|
|
444
|
-
const nodes = [];
|
|
123
|
+
nodeList(parent) {
|
|
445
124
|
let _node;
|
|
125
|
+
let prevSibling;
|
|
126
|
+
let prevItem;
|
|
446
127
|
while (1) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
return nodes;
|
|
128
|
+
if (this.tokenizer.isEof()) {
|
|
129
|
+
return;
|
|
450
130
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
return nodes;
|
|
131
|
+
if (this.tokenizer.token.type & TokenType.Dedent) {
|
|
132
|
+
this.tokenizer.consume();
|
|
133
|
+
return;
|
|
455
134
|
}
|
|
456
135
|
_node = this.node();
|
|
457
|
-
|
|
136
|
+
const insert = parent.__logicType ? this.defaultInsert : this.insert.bind(this);
|
|
137
|
+
parent.__logicType ? this.defaultRemove : this.remove.bind(this);
|
|
138
|
+
if (!_node.__logicType) {
|
|
139
|
+
const realPrev = this.getPrevRealSibling(prevSibling);
|
|
140
|
+
const currItem = insert(parent, _node, realPrev, prevItem);
|
|
141
|
+
prevItem = currItem;
|
|
142
|
+
prevSibling = _node;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (prevSibling) {
|
|
146
|
+
_node.anchor = prevSibling;
|
|
147
|
+
} else if (parent.__logicType) {
|
|
148
|
+
_node.anchor = parent;
|
|
149
|
+
} else ;
|
|
150
|
+
this.effect(() => {
|
|
151
|
+
if (_node.child && _node.condition()) {
|
|
152
|
+
let item = _node.child;
|
|
153
|
+
while (item != null) {
|
|
154
|
+
const { value: child } = item;
|
|
155
|
+
const realPrev = this.getPrevRealSibling(prevSibling);
|
|
156
|
+
const currItem = insert(parent, child, realPrev, prevItem);
|
|
157
|
+
item = item.next;
|
|
158
|
+
prevItem = currItem;
|
|
159
|
+
prevSibling = child;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** 考虑到同级 逻辑模块 */
|
|
166
|
+
getPrevRealSibling(prevSibling) {
|
|
167
|
+
if (!prevSibling || !prevSibling.__logicType) {
|
|
168
|
+
return prevSibling;
|
|
169
|
+
}
|
|
170
|
+
let point = prevSibling;
|
|
171
|
+
while (point != null) {
|
|
172
|
+
if (point.lastChild) {
|
|
173
|
+
return point.lastChild.value;
|
|
174
|
+
}
|
|
175
|
+
point = point.anchor;
|
|
458
176
|
}
|
|
459
177
|
}
|
|
460
178
|
/**
|
|
@@ -464,7 +182,10 @@ class Compiler {
|
|
|
464
182
|
* */
|
|
465
183
|
node() {
|
|
466
184
|
const _declaration = this.declaration();
|
|
467
|
-
_declaration.
|
|
185
|
+
if (_declaration.__logicType & LogicType.If && !_declaration.condition()) {
|
|
186
|
+
return _declaration;
|
|
187
|
+
}
|
|
188
|
+
this.childrenBlockOpt(_declaration);
|
|
468
189
|
return _declaration;
|
|
469
190
|
}
|
|
470
191
|
/**
|
|
@@ -473,44 +194,60 @@ class Compiler {
|
|
|
473
194
|
* <declaration> ::= <tagName=token> <headerLine> <extensionLines>
|
|
474
195
|
* */
|
|
475
196
|
declaration() {
|
|
476
|
-
this.consume();
|
|
477
197
|
const [isHook, value] = this._hook({});
|
|
478
198
|
let _node;
|
|
479
199
|
if (isHook) {
|
|
480
|
-
const {
|
|
481
|
-
tree,
|
|
482
|
-
data
|
|
483
|
-
} = value();
|
|
200
|
+
const { tree, data } = value();
|
|
484
201
|
_node = tree;
|
|
202
|
+
} else if (value === "if") {
|
|
203
|
+
return this.ifDeclaration();
|
|
485
204
|
} else {
|
|
486
205
|
_node = this.createNode(value);
|
|
487
206
|
}
|
|
207
|
+
this.tokenizer.consume();
|
|
488
208
|
this.headerLine(_node);
|
|
489
209
|
this.extensionLines(_node);
|
|
490
210
|
return _node;
|
|
491
211
|
}
|
|
212
|
+
ifDeclaration() {
|
|
213
|
+
this.tokenizer.consume();
|
|
214
|
+
const [isHook, value] = this._hook({});
|
|
215
|
+
const ifNode = {
|
|
216
|
+
__logicType: LogicType.If,
|
|
217
|
+
condition: value,
|
|
218
|
+
child: null,
|
|
219
|
+
lastChild: null,
|
|
220
|
+
anchor: null,
|
|
221
|
+
skip: null
|
|
222
|
+
};
|
|
223
|
+
this.effect(() => {
|
|
224
|
+
const needMount = value();
|
|
225
|
+
if (needMount) {
|
|
226
|
+
this.tokenizer.consume();
|
|
227
|
+
this.tokenizer.consume();
|
|
228
|
+
} else {
|
|
229
|
+
ifNode.skip = this.tokenizer.skip();
|
|
230
|
+
console.log("skip");
|
|
231
|
+
console.log(ifNode.skip);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
return ifNode;
|
|
235
|
+
}
|
|
492
236
|
/**
|
|
493
237
|
* <extensionLines> ::= PIPE <attributeList> NEWLINE <extensionLines>
|
|
494
238
|
* | ε
|
|
495
239
|
*/
|
|
496
240
|
extensionLines(_node) {
|
|
497
|
-
const {
|
|
498
|
-
tokenIs
|
|
499
|
-
} = this;
|
|
500
241
|
while (1) {
|
|
501
|
-
|
|
502
|
-
if (!tokenIs(exports.TokenType.Pipe)) {
|
|
242
|
+
if (!(this.tokenizer.token.type & TokenType.Pipe)) {
|
|
503
243
|
return;
|
|
504
244
|
}
|
|
505
|
-
|
|
506
|
-
this.consume();
|
|
245
|
+
this.tokenizer.consume();
|
|
507
246
|
this.attributeList(_node);
|
|
508
|
-
|
|
509
|
-
if (!tokenIs(exports.TokenType.NewLine)) {
|
|
247
|
+
if (!(this.tokenizer.token.type & TokenType.NewLine)) {
|
|
510
248
|
return;
|
|
511
249
|
}
|
|
512
|
-
|
|
513
|
-
this.consume();
|
|
250
|
+
this.tokenizer.consume();
|
|
514
251
|
}
|
|
515
252
|
}
|
|
516
253
|
/**
|
|
@@ -520,7 +257,7 @@ class Compiler {
|
|
|
520
257
|
*/
|
|
521
258
|
headerLine(_node) {
|
|
522
259
|
this.attributeList(_node);
|
|
523
|
-
this.consume();
|
|
260
|
+
this.tokenizer.consume();
|
|
524
261
|
}
|
|
525
262
|
/**
|
|
526
263
|
* 属性列表:
|
|
@@ -529,53 +266,57 @@ class Compiler {
|
|
|
529
266
|
* | ε
|
|
530
267
|
*
|
|
531
268
|
* <attribute> ::= <key> <=> <value or dataKey> <=> <value>
|
|
269
|
+
*
|
|
532
270
|
*/
|
|
533
271
|
attributeList(_node) {
|
|
534
|
-
let
|
|
535
|
-
let
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
272
|
+
let values = [];
|
|
273
|
+
let prevToken = void 0;
|
|
274
|
+
while (1) {
|
|
275
|
+
if ((prevToken == null ? void 0 : prevToken.type) === TokenType.Identifier && this.tokenizer.token.type !== TokenType.Assign) {
|
|
276
|
+
const [v1, v2, v3] = values;
|
|
277
|
+
const key = v1;
|
|
278
|
+
let dataKey, defaultVal;
|
|
279
|
+
if (v3 !== void 0) {
|
|
280
|
+
defaultVal = v3;
|
|
281
|
+
dataKey = v2;
|
|
282
|
+
} else if (v2 !== void 0) {
|
|
283
|
+
if (typeof v2 === "string" && v2[0] === "$" && v2[1] !== "{") {
|
|
284
|
+
dataKey = v2.slice(1);
|
|
285
|
+
} else {
|
|
286
|
+
defaultVal = v2;
|
|
287
|
+
}
|
|
288
|
+
} else {
|
|
549
289
|
dataKey = key;
|
|
550
290
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
else {
|
|
555
|
-
const valueOrKey = dataKey;
|
|
556
|
-
if (valueOrKey[0] === '$') {
|
|
557
|
-
dataKey = dataKey.slice(1);
|
|
558
|
-
}
|
|
559
|
-
// 值
|
|
560
|
-
else {
|
|
561
|
-
defaultValue = dataKey;
|
|
562
|
-
dataKey = undefined;
|
|
563
|
-
}
|
|
291
|
+
let val = defaultVal;
|
|
292
|
+
if (dataKey) {
|
|
293
|
+
val = this.setDataProp(this.data, dataKey, defaultVal);
|
|
564
294
|
}
|
|
565
|
-
this.
|
|
566
|
-
|
|
567
|
-
|
|
295
|
+
this.setProp(_node, key, val, this.hookI - 1);
|
|
296
|
+
const [isHook, value] = this._hook({});
|
|
297
|
+
values = [value];
|
|
298
|
+
} else if (this.tokenizer.token.type !== TokenType.Assign) {
|
|
299
|
+
const [isHook, value] = this._hook({});
|
|
300
|
+
values.push(value);
|
|
568
301
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if (!dataKey) {
|
|
572
|
-
dataKey = value;
|
|
573
|
-
} else {
|
|
574
|
-
defaultValue = value;
|
|
575
|
-
}
|
|
302
|
+
if (!(this.tokenizer.token.type & (TokenType.Identifier | TokenType.Assign))) {
|
|
303
|
+
break;
|
|
576
304
|
}
|
|
577
|
-
this.consume();
|
|
305
|
+
prevToken = this.tokenizer.consume();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/** 子节点块:
|
|
309
|
+
* 必须被缩进包裹
|
|
310
|
+
* <childrenBlockOpt> ::= INDENT <nodeList>
|
|
311
|
+
* | ε /* 空(表示叶子节点,没有孩子)
|
|
312
|
+
* */
|
|
313
|
+
childrenBlockOpt(parent) {
|
|
314
|
+
if (!(this.tokenizer.token.type & TokenType.Indent)) {
|
|
315
|
+
return [];
|
|
578
316
|
}
|
|
317
|
+
this.tokenizer.consume();
|
|
318
|
+
const list = this.nodeList(parent);
|
|
319
|
+
return list;
|
|
579
320
|
}
|
|
580
321
|
config(opt) {
|
|
581
322
|
Object.assign(this, opt);
|
|
@@ -586,96 +327,80 @@ class Compiler {
|
|
|
586
327
|
setDataProp(data, key, value) {
|
|
587
328
|
return data[key] = value;
|
|
588
329
|
}
|
|
330
|
+
setChildren(node, children) {
|
|
331
|
+
node.children = children;
|
|
332
|
+
}
|
|
589
333
|
createNode(name) {
|
|
590
334
|
return {
|
|
591
335
|
name,
|
|
592
336
|
props: {}
|
|
593
337
|
};
|
|
594
338
|
}
|
|
339
|
+
createRoot() {
|
|
340
|
+
return this.createNode("root");
|
|
341
|
+
}
|
|
342
|
+
insert(parent, node, prevSibling, prevItem) {
|
|
343
|
+
return this.defaultInsert(parent, node, prevSibling, prevItem);
|
|
344
|
+
}
|
|
345
|
+
defaultInsert(parent, node, prevSibling, prevItem) {
|
|
346
|
+
if (!parent.child) {
|
|
347
|
+
return parent.child = parent.lastChild = {
|
|
348
|
+
value: node,
|
|
349
|
+
next: null
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
const nextItem = prevItem.next;
|
|
353
|
+
const item = {
|
|
354
|
+
value: node,
|
|
355
|
+
next: nextItem
|
|
356
|
+
};
|
|
357
|
+
prevItem.next = item;
|
|
358
|
+
if (!nextItem) {
|
|
359
|
+
parent.lastChild = item;
|
|
360
|
+
}
|
|
361
|
+
return item;
|
|
362
|
+
}
|
|
363
|
+
remove(parent, node, prevSibling, prevItem) {
|
|
364
|
+
return this.defaultRemove(parent, node, prevSibling, prevItem);
|
|
365
|
+
}
|
|
366
|
+
// TODO: 默认改成 prevItem
|
|
367
|
+
defaultRemove(parent, node, prevSibling, prevItem) {
|
|
368
|
+
const currItem = prevItem.next;
|
|
369
|
+
const nextItem = currItem.next;
|
|
370
|
+
if (prevItem) {
|
|
371
|
+
if (nextItem) {
|
|
372
|
+
prevItem.next = nextItem;
|
|
373
|
+
} else {
|
|
374
|
+
prevItem.next = null;
|
|
375
|
+
parent.lastChild = prevItem;
|
|
376
|
+
}
|
|
377
|
+
} else {
|
|
378
|
+
if (nextItem) {
|
|
379
|
+
parent.child = nextItem;
|
|
380
|
+
} else {
|
|
381
|
+
parent.child = null;
|
|
382
|
+
parent.lastChild = null;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
currItem.next = null;
|
|
386
|
+
}
|
|
595
387
|
setProp(node, key, value, hookI) {
|
|
596
388
|
node.props[key] = value;
|
|
597
389
|
}
|
|
598
390
|
init(fragments) {
|
|
599
391
|
this.data = this.createData(this.data);
|
|
600
|
-
if (typeof fragments ===
|
|
601
|
-
this.
|
|
392
|
+
if (typeof fragments === "string") {
|
|
393
|
+
this.tokenizer.setCode(fragments);
|
|
602
394
|
} else {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
/** 子节点块:
|
|
608
|
-
* 必须被缩进包裹
|
|
609
|
-
* <childrenBlockOpt> ::= INDENT <nodeList>
|
|
610
|
-
* | ε /* 空(表示叶子节点,没有孩子)
|
|
611
|
-
* */
|
|
612
|
-
childrenBlockOpt() {
|
|
613
|
-
// 无 children
|
|
614
|
-
if (!this.tokenIs(exports.TokenType.Indent)) {
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
this.consume();
|
|
618
|
-
const list = this.nodeList();
|
|
619
|
-
return list;
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
let ast;
|
|
623
|
-
const updateList = [];
|
|
624
|
-
const cmp = new Compiler();
|
|
625
|
-
function bobe(fragments, ...values) {
|
|
626
|
-
// 增量更新
|
|
627
|
-
if (ast) {
|
|
628
|
-
updateList.forEach(({
|
|
629
|
-
old,
|
|
630
|
-
fn
|
|
631
|
-
}, i) => {
|
|
632
|
-
const val = values[i];
|
|
633
|
-
if (val !== old) {
|
|
634
|
-
console.log('增量更新', val);
|
|
635
|
-
fn(val);
|
|
395
|
+
let code = "";
|
|
396
|
+
for (let i = 0; i < fragments.length - 1; i++) {
|
|
397
|
+
const fragment = fragments[i];
|
|
398
|
+
code += fragment + `${this.HookId}${i}`;
|
|
636
399
|
}
|
|
637
|
-
|
|
638
|
-
console.log(JSON.stringify(ast, undefined, 2));
|
|
639
|
-
return ast;
|
|
640
|
-
}
|
|
641
|
-
// 初始化
|
|
642
|
-
cmp.config({
|
|
643
|
-
hook({
|
|
644
|
-
i
|
|
645
|
-
}) {
|
|
646
|
-
return values[i];
|
|
647
|
-
},
|
|
648
|
-
setProp(node, key, value, hookI) {
|
|
649
|
-
const fn = v => {
|
|
650
|
-
node.props[key] = v;
|
|
651
|
-
if (hookI != null) {
|
|
652
|
-
updateList[hookI] = {
|
|
653
|
-
fn,
|
|
654
|
-
old: v
|
|
655
|
-
};
|
|
656
|
-
}
|
|
657
|
-
};
|
|
658
|
-
fn(value);
|
|
400
|
+
this.tokenizer.setCode(code + fragments[fragments.length - 1]);
|
|
659
401
|
}
|
|
660
|
-
}
|
|
661
|
-
cmp.init(Array.from(fragments));
|
|
662
|
-
ast = cmp.program();
|
|
663
|
-
console.log(JSON.stringify(ast, undefined, 2));
|
|
664
|
-
return ast;
|
|
402
|
+
}
|
|
665
403
|
}
|
|
666
|
-
// bobe`
|
|
667
|
-
// node1 k1=1
|
|
668
|
-
// node1_1 k2=false k3=3
|
|
669
|
-
// node1_1_1 k6=null
|
|
670
|
-
// node2
|
|
671
|
-
// | p1=1
|
|
672
|
-
// | p2=2 p3='你好'
|
|
673
|
-
// node2_1
|
|
674
|
-
// | p4=4 p5=${{ v: '🤡' }} p6=6
|
|
675
|
-
// node2_2
|
|
676
|
-
// | p7=7 p8=\${{ v: '🤡' }} p9=aaa
|
|
677
|
-
// node3 v1=1 v2=2 v3=undefined
|
|
678
|
-
// `;
|
|
679
404
|
|
|
680
|
-
exports.
|
|
681
|
-
|
|
405
|
+
exports.Interpreter = Interpreter;
|
|
406
|
+
//# sourceMappingURL=bobe.cjs.js.map
|