bobe 0.0.22 → 0.0.24
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/dist/bobe.cjs.js +213 -327
- package/dist/bobe.cjs.js.map +1 -1
- package/dist/bobe.esm.js +215 -329
- package/dist/bobe.esm.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.umd.js +213 -327
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -3
package/dist/bobe.esm.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getPulling, setPulling, Keys,
|
|
1
|
+
import { getPulling, setPulling, Keys, Computed, Effect, toRaw, Scope, runWithPulling, deepSignal, Store, shareSignal, effect } from 'aoye';
|
|
2
2
|
export * from 'aoye';
|
|
3
|
-
import {
|
|
3
|
+
import { jsVarRegexp, Queue, isNum, matchIdStart2 } from 'bobe-shared';
|
|
4
4
|
|
|
5
5
|
function _arrayLikeToArray(r, a) {
|
|
6
6
|
(null == a || a > r.length) && (a = r.length);
|
|
@@ -71,54 +71,50 @@ function _unsupportedIterableToArray(r, a) {
|
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return
|
|
95
|
-
}
|
|
96
|
-
const CondBit =
|
|
97
|
-
const LogicalBit =
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
74
|
+
let TokenType = function (TokenType) {
|
|
75
|
+
TokenType[TokenType["NewLine"] = 1] = "NewLine";
|
|
76
|
+
TokenType[TokenType["Indent"] = 2] = "Indent";
|
|
77
|
+
TokenType[TokenType["Dedent"] = 4] = "Dedent";
|
|
78
|
+
TokenType[TokenType["Identifier"] = 8] = "Identifier";
|
|
79
|
+
TokenType[TokenType["Assign"] = 16] = "Assign";
|
|
80
|
+
TokenType[TokenType["Pipe"] = 32] = "Pipe";
|
|
81
|
+
TokenType[TokenType["Eof"] = 64] = "Eof";
|
|
82
|
+
TokenType[TokenType["InsertionExp"] = 128] = "InsertionExp";
|
|
83
|
+
TokenType[TokenType["Semicolon"] = 256] = "Semicolon";
|
|
84
|
+
return TokenType;
|
|
85
|
+
}({});
|
|
86
|
+
let FakeType = function (FakeType) {
|
|
87
|
+
FakeType[FakeType["If"] = 1] = "If";
|
|
88
|
+
FakeType[FakeType["Fail"] = 2] = "Fail";
|
|
89
|
+
FakeType[FakeType["Else"] = 4] = "Else";
|
|
90
|
+
FakeType[FakeType["For"] = 8] = "For";
|
|
91
|
+
FakeType[FakeType["Component"] = 16] = "Component";
|
|
92
|
+
FakeType[FakeType["Fragment"] = 32] = "Fragment";
|
|
93
|
+
FakeType[FakeType["ForItem"] = 64] = "ForItem";
|
|
94
|
+
return FakeType;
|
|
95
|
+
}({});
|
|
96
|
+
const CondBit = FakeType.If | FakeType.Fail | FakeType.Else;
|
|
97
|
+
const LogicalBit = FakeType.If | FakeType.Fail | FakeType.Else | FakeType.For | FakeType.ForItem;
|
|
98
|
+
FakeType.If | FakeType.Fail | FakeType.Else | FakeType.For | FakeType.ForItem | FakeType.Component | FakeType.Fragment;
|
|
99
|
+
const TokenizerSwitcherBit = FakeType.Component | FakeType.Fragment;
|
|
100
|
+
let NodeSort = function (NodeSort) {
|
|
101
|
+
NodeSort[NodeSort["Logic"] = 1] = "Logic";
|
|
102
|
+
NodeSort[NodeSort["Real"] = 2] = "Real";
|
|
103
|
+
NodeSort[NodeSort["Component"] = 4] = "Component";
|
|
104
|
+
NodeSort[NodeSort["CtxProvider"] = 8] = "CtxProvider";
|
|
105
|
+
NodeSort[NodeSort["TokenizerSwitcher"] = 16] = "TokenizerSwitcher";
|
|
106
|
+
return NodeSort;
|
|
107
|
+
}({});
|
|
108
|
+
(function (TerpEvt) {
|
|
109
|
+
TerpEvt["AllAttrGot"] = "all-attr-got";
|
|
110
|
+
TerpEvt["HandledComponentNode"] = "handled-component-node";
|
|
111
|
+
return TerpEvt;
|
|
112
|
+
})({});
|
|
107
113
|
|
|
108
114
|
class MultiTypeStack {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// 记录每个类别的当前最新节点(各分类的“栈顶”)
|
|
113
|
-
this.typeTops = {};
|
|
114
|
-
this.length = 0;
|
|
115
|
-
this.stack = [];
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* 入栈操作
|
|
119
|
-
* @param value 数据
|
|
120
|
-
* @param bits 该节点所属的类别数组
|
|
121
|
-
*/
|
|
115
|
+
typeTops = {};
|
|
116
|
+
length = 0;
|
|
117
|
+
stack = [];
|
|
122
118
|
push(value, bits) {
|
|
123
119
|
const newNode = {
|
|
124
120
|
value,
|
|
@@ -130,18 +126,15 @@ class MultiTypeStack {
|
|
|
130
126
|
bit = bits & ~bits + 1;
|
|
131
127
|
if (!bit) break;
|
|
132
128
|
bits &= ~bit;
|
|
133
|
-
newNode.prevByType[bit] = this.typeTops[bit] ||
|
|
129
|
+
newNode.prevByType[bit] = this.typeTops[bit] || undefined;
|
|
134
130
|
this.typeTops[bit] = newNode;
|
|
135
131
|
}
|
|
136
132
|
this.stack[this.length++] = newNode;
|
|
137
133
|
}
|
|
138
|
-
/**
|
|
139
|
-
* 出栈操作
|
|
140
|
-
*/
|
|
141
134
|
pop() {
|
|
142
135
|
const poppedNode = this.stack[this.length - 1];
|
|
143
136
|
this.stack[--this.length] = null;
|
|
144
|
-
if (!poppedNode) return
|
|
137
|
+
if (!poppedNode) return undefined;
|
|
145
138
|
let bits = poppedNode.types;
|
|
146
139
|
let bit;
|
|
147
140
|
while (1) {
|
|
@@ -152,63 +145,26 @@ class MultiTypeStack {
|
|
|
152
145
|
}
|
|
153
146
|
return [poppedNode.value, poppedNode.types];
|
|
154
147
|
}
|
|
155
|
-
/**
|
|
156
|
-
* 获取某个类别的当前“顶部”元素
|
|
157
|
-
*/
|
|
158
148
|
peekByType(cat) {
|
|
159
149
|
return this.typeTops[cat]?.value;
|
|
160
150
|
}
|
|
161
151
|
peekType() {
|
|
162
152
|
return this.stack.at(-1).types;
|
|
163
153
|
}
|
|
164
|
-
/**
|
|
165
|
-
* 获取全局栈顶
|
|
166
|
-
*/
|
|
167
154
|
peek() {
|
|
168
155
|
return this.stack.at(-1).value;
|
|
169
156
|
}
|
|
170
|
-
// /**
|
|
171
|
-
// * 1. 全局向前遍历 (不分类)
|
|
172
|
-
// * 从栈顶开始,沿着全局链条向栈底遍历
|
|
173
|
-
// */
|
|
174
|
-
// forEach(callback: (value: T, types: number) => any): void {
|
|
175
|
-
// let current = this.top;
|
|
176
|
-
// while (current !== null) {
|
|
177
|
-
// // 执行回调,如果返回 false 则立即停止
|
|
178
|
-
// const shouldBreak = callback(current.value, current.types);
|
|
179
|
-
// if (shouldBreak) break;
|
|
180
|
-
// current = current.prevGlobal;
|
|
181
|
-
// }
|
|
182
|
-
// }
|
|
183
|
-
// /**
|
|
184
|
-
// * 2. 按类别向前遍历
|
|
185
|
-
// * 仅遍历属于指定类别 cat 的节点
|
|
186
|
-
// */
|
|
187
|
-
// forEachByType(cat: number, callback: (value: T) => any): void {
|
|
188
|
-
// // 从该类别的当前“顶端”节点开始
|
|
189
|
-
// let current = this.typeTops[cat];
|
|
190
|
-
// while (current) {
|
|
191
|
-
// const shouldBreak = callback(current.value);
|
|
192
|
-
// if (shouldBreak) break;
|
|
193
|
-
// // 关键点:直接跳向该节点记录的“上一个同类节点”
|
|
194
|
-
// // 这比遍历全局栈再筛选类别要快得多 (O(m) vs O(n))
|
|
195
|
-
// current = current.prevByType[cat];
|
|
196
|
-
// }
|
|
197
|
-
// }
|
|
198
157
|
}
|
|
199
158
|
|
|
200
159
|
const _excluded = ["dentStack", "isFirstToken"];
|
|
201
|
-
new BaseEvent();
|
|
202
160
|
class Interpreter {
|
|
203
161
|
constructor(tokenizer) {
|
|
204
162
|
this.tokenizer = tokenizer;
|
|
205
|
-
this.rootComponent = null;
|
|
206
|
-
this.forItemId = 0;
|
|
207
|
-
this.oneRealPropParsed = this.onePropParsed.bind(this);
|
|
208
163
|
}
|
|
209
164
|
isLogicNode(node) {
|
|
210
165
|
return node && node.__logicType & LogicalBit;
|
|
211
166
|
}
|
|
167
|
+
rootComponent = null;
|
|
212
168
|
program(root, componentNode, before, ctxProvider) {
|
|
213
169
|
this.rootComponent = componentNode;
|
|
214
170
|
this.tokenizer.nextToken();
|
|
@@ -251,7 +207,7 @@ class Interpreter {
|
|
|
251
207
|
}, !ctx.current.__logicType ? NodeSort.Real : (ctx.current.__logicType & LogicalBit ? NodeSort.Logic : 0) | (ctx.current.__logicType & TokenizerSwitcherBit ? NodeSort.TokenizerSwitcher : 0) | (ctx.current.__logicType === FakeType.Component ? NodeSort.Component : 0) | NodeSort.CtxProvider);
|
|
252
208
|
if (ctx.current.__logicType) {
|
|
253
209
|
if (isLogicNode) {
|
|
254
|
-
setPulling(ctx.current.effect
|
|
210
|
+
setPulling(ctx.current.effect);
|
|
255
211
|
}
|
|
256
212
|
} else {
|
|
257
213
|
if (ctx.current) {
|
|
@@ -283,7 +239,7 @@ class Interpreter {
|
|
|
283
239
|
if (sort & NodeSort.Logic) {
|
|
284
240
|
const parentLogic = stack.peekByType(NodeSort.Logic)?.node;
|
|
285
241
|
if (parentLogic) {
|
|
286
|
-
setPulling(parentLogic.effect
|
|
242
|
+
setPulling(parentLogic.effect);
|
|
287
243
|
} else {
|
|
288
244
|
setPulling(rootPulling);
|
|
289
245
|
}
|
|
@@ -293,7 +249,8 @@ class Interpreter {
|
|
|
293
249
|
this.tokenizer = switcher.tokenizer;
|
|
294
250
|
}
|
|
295
251
|
if (parent.__logicType === FakeType.ForItem) {
|
|
296
|
-
const
|
|
252
|
+
const _ref = parent,
|
|
253
|
+
forNode = _ref.forNode;
|
|
297
254
|
const i = forNode.i,
|
|
298
255
|
arr = forNode.arr,
|
|
299
256
|
snapshot = forNode.snapshot;
|
|
@@ -319,7 +276,7 @@ class Interpreter {
|
|
|
319
276
|
}
|
|
320
277
|
return componentNode;
|
|
321
278
|
}
|
|
322
|
-
insertAfterAnchor(name =
|
|
279
|
+
insertAfterAnchor(name = 'anchor') {
|
|
323
280
|
const _this$ctx = this.ctx,
|
|
324
281
|
realParent = _this$ctx.realParent,
|
|
325
282
|
prevSibling = _this$ctx.prevSibling,
|
|
@@ -330,12 +287,6 @@ class Interpreter {
|
|
|
330
287
|
this.handleInsert(realParent, afterAnchor, prevSibling);
|
|
331
288
|
return afterAnchor;
|
|
332
289
|
}
|
|
333
|
-
/** 处理
|
|
334
|
-
* 是逻辑 是普通
|
|
335
|
-
* 父节点 将子节点加入 directList 调用 insert 方法挨个插入子节点
|
|
336
|
-
* 子节点 仅插入到父逻辑节点 将本节点插入父节点
|
|
337
|
-
* 理论上父节点不能是一个 逻辑节点,遇到if 时 Terp 会重新执行 program 这种情况下,会指定 root 为真实 dom
|
|
338
|
-
*/
|
|
339
290
|
handleInsert(parent, child, prev, parentComponent) {
|
|
340
291
|
if (!child.__logicType) {
|
|
341
292
|
if (!prev || !prev.__logicType) {
|
|
@@ -354,7 +305,6 @@ class Interpreter {
|
|
|
354
305
|
}
|
|
355
306
|
}
|
|
356
307
|
}
|
|
357
|
-
/** 考虑到同级 逻辑模块 */
|
|
358
308
|
getPrevRealSibling(prevSibling) {
|
|
359
309
|
if (!prevSibling || !prevSibling.__logicType) {
|
|
360
310
|
return prevSibling;
|
|
@@ -367,38 +317,33 @@ class Interpreter {
|
|
|
367
317
|
point = point.anchor;
|
|
368
318
|
}
|
|
369
319
|
}
|
|
370
|
-
/**
|
|
371
|
-
* 声明部分:
|
|
372
|
-
* 包含首行定义和(可选的)多行属性扩展
|
|
373
|
-
* <declaration> ::= <tagName=token> <headerLine> <extensionLines>
|
|
374
|
-
* */
|
|
375
320
|
declaration(ctx) {
|
|
376
321
|
const _this$tokenizer$_hook = this.tokenizer._hook({}),
|
|
377
322
|
_this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
|
|
378
323
|
hookType = _this$tokenizer$_hook2[0],
|
|
379
324
|
value = _this$tokenizer$_hook2[1];
|
|
380
325
|
let _node;
|
|
381
|
-
if (value ===
|
|
326
|
+
if (value === 'if' || value === 'else' || value === 'fail') {
|
|
382
327
|
return this.condDeclaration(ctx);
|
|
383
|
-
} else if (value ===
|
|
328
|
+
} else if (value === 'for') {
|
|
384
329
|
return this.forDeclaration();
|
|
385
330
|
} else if (hookType) {
|
|
386
331
|
const data = this.getData();
|
|
387
|
-
if (hookType ===
|
|
388
|
-
if (typeof value ===
|
|
332
|
+
if (hookType === 'static') {
|
|
333
|
+
if (typeof value === 'function') {
|
|
389
334
|
_node = this.componentOrFragmentDeclaration(value, ctx);
|
|
390
335
|
} else {
|
|
391
|
-
throw new SyntaxError(`declaration
|
|
336
|
+
throw new SyntaxError(`declaration 不支持 ${value} 类型的静态插值`);
|
|
392
337
|
}
|
|
393
338
|
} else {
|
|
394
339
|
const valueIsMapKey = Reflect.has(data[Keys.Raw], value);
|
|
395
340
|
const val = data[Keys.Raw][value];
|
|
396
|
-
if (typeof val ===
|
|
341
|
+
if (typeof val === 'function') {
|
|
397
342
|
_node = this.componentOrFragmentDeclaration(val, ctx);
|
|
398
343
|
} else {
|
|
399
344
|
const str = valueIsMapKey ? value : this.getFn(data, value);
|
|
400
|
-
_node = this.createNode(
|
|
401
|
-
this.onePropParsed(data, _node,
|
|
345
|
+
_node = this.createNode('text');
|
|
346
|
+
this.onePropParsed(data, _node, 'text', str, valueIsMapKey, false);
|
|
402
347
|
}
|
|
403
348
|
}
|
|
404
349
|
} else {
|
|
@@ -420,12 +365,12 @@ class Interpreter {
|
|
|
420
365
|
const isDestruct = itemToken.type === TokenType.InsertionExp;
|
|
421
366
|
let itemExp = itemToken.value;
|
|
422
367
|
if (isDestruct) {
|
|
423
|
-
itemExp =
|
|
424
|
-
const vars = itemExp.match(jsVarRegexp).join(
|
|
425
|
-
itemExp = new Function(
|
|
368
|
+
itemExp = '{' + itemExp + '}';
|
|
369
|
+
const vars = itemExp.match(jsVarRegexp).join(',');
|
|
370
|
+
itemExp = new Function('item', `let ${vars}; (${itemExp}=item); return {${vars}};`);
|
|
426
371
|
}
|
|
427
372
|
let indexName, keyExp;
|
|
428
|
-
while (this.tokenizer.code[this.tokenizer.i] !==
|
|
373
|
+
while (this.tokenizer.code[this.tokenizer.i] !== '\n') {
|
|
429
374
|
const next = this.tokenizer.nextToken();
|
|
430
375
|
if (next.type !== TokenType.Semicolon) {
|
|
431
376
|
if (!indexName) {
|
|
@@ -439,7 +384,7 @@ class Interpreter {
|
|
|
439
384
|
const prevSibling = this.ctx.prevSibling;
|
|
440
385
|
const forNode = {
|
|
441
386
|
__logicType: FakeType.For,
|
|
442
|
-
snapshot: this.tokenizer.snapshot([
|
|
387
|
+
snapshot: this.tokenizer.snapshot(['dentStack', 'isFirstToken']),
|
|
443
388
|
realParent: this.ctx.realParent,
|
|
444
389
|
prevSibling,
|
|
445
390
|
realBefore: prevSibling?.realAfter || prevSibling,
|
|
@@ -454,25 +399,21 @@ class Interpreter {
|
|
|
454
399
|
i: 0
|
|
455
400
|
};
|
|
456
401
|
if (keyExp) {
|
|
457
|
-
forNode.getKey = new Function(
|
|
402
|
+
forNode.getKey = new Function('data', `let v;with(data){v=${keyExp}};return v;`);
|
|
458
403
|
}
|
|
459
|
-
window[
|
|
404
|
+
window['for1'] = forNode;
|
|
460
405
|
const data = this.getData();
|
|
461
406
|
const cells = data[Keys.Meta].cells;
|
|
462
407
|
const hasArrExpKey = Reflect.has(data[Keys.Raw], arrExp);
|
|
463
|
-
const arrSignal = hasArrExpKey ? (
|
|
464
|
-
|
|
465
|
-
data[arrExp], cells.get(arrExp)) :
|
|
466
|
-
// 无key
|
|
467
|
-
$(this.getFn(data, arrExp));
|
|
468
|
-
forNode.realAfter = this.insertAfterAnchor("for-after");
|
|
408
|
+
const arrSignal = hasArrExpKey ? (data[arrExp], cells.get(arrExp)) : new Computed(this.getFn(data, arrExp));
|
|
409
|
+
forNode.realAfter = this.insertAfterAnchor('for-after');
|
|
469
410
|
const _forNode$snapshot = forNode.snapshot;
|
|
470
411
|
_forNode$snapshot.dentStack;
|
|
471
412
|
_forNode$snapshot.isFirstToken;
|
|
472
413
|
const snapshotForUpdate = _objectWithoutProperties(_forNode$snapshot, _excluded);
|
|
473
414
|
let isFirstRender = true;
|
|
474
|
-
forNode.effect =
|
|
475
|
-
let arr = forNode.arr = arrSignal.
|
|
415
|
+
forNode.effect = new Effect(() => {
|
|
416
|
+
let arr = forNode.arr = arrSignal.get();
|
|
476
417
|
arr[Keys.Iterator];
|
|
477
418
|
arr = toRaw(arr);
|
|
478
419
|
const children = forNode.children;
|
|
@@ -481,7 +422,7 @@ class Interpreter {
|
|
|
481
422
|
for (let i = len; i--;) {
|
|
482
423
|
const nextItem = children[i + 1];
|
|
483
424
|
const item = this.createForItem(forNode, i, data);
|
|
484
|
-
const anchor = this.insertAfterAnchor(
|
|
425
|
+
const anchor = this.insertAfterAnchor('for-item-after');
|
|
485
426
|
item.realAfter = anchor;
|
|
486
427
|
if (nextItem) {
|
|
487
428
|
nextItem.realBefore = anchor;
|
|
@@ -508,7 +449,7 @@ class Interpreter {
|
|
|
508
449
|
const child = children[i];
|
|
509
450
|
this.removeLogicNode(child);
|
|
510
451
|
this.remove(child.realAfter);
|
|
511
|
-
child.effect();
|
|
452
|
+
child.effect.dispose();
|
|
512
453
|
}
|
|
513
454
|
}
|
|
514
455
|
if (oldLen < newLen) {
|
|
@@ -517,7 +458,7 @@ class Interpreter {
|
|
|
517
458
|
const item = this.createForItem(forNode, i, data);
|
|
518
459
|
newChildren[i] = item;
|
|
519
460
|
const nextItem = newChildren[i + 1];
|
|
520
|
-
const anchor = this.createAnchor(
|
|
461
|
+
const anchor = this.createAnchor('for-item-after');
|
|
521
462
|
this.insertAfter(forNode.realParent, anchor, lastAfter);
|
|
522
463
|
item.realAfter = anchor;
|
|
523
464
|
if (nextItem) {
|
|
@@ -529,7 +470,7 @@ class Interpreter {
|
|
|
529
470
|
this.tokenizer.useDedentAsEof = false;
|
|
530
471
|
runWithPulling(() => {
|
|
531
472
|
this.program(forNode.realParent, forNode.owner, lastAfter, item);
|
|
532
|
-
}, item.effect
|
|
473
|
+
}, item.effect);
|
|
533
474
|
}
|
|
534
475
|
const firstInsert = newChildren[oldLen];
|
|
535
476
|
if (firstInsert) {
|
|
@@ -539,7 +480,7 @@ class Interpreter {
|
|
|
539
480
|
for (let i = minLen; i--;) {
|
|
540
481
|
const child = children[i];
|
|
541
482
|
newChildren[i] = child;
|
|
542
|
-
if (typeof itemExp ===
|
|
483
|
+
if (typeof itemExp === 'string') {
|
|
543
484
|
child.data[itemExp] = arr[i];
|
|
544
485
|
} else {
|
|
545
486
|
Object.assign(child.data, itemExp(arr[i]));
|
|
@@ -552,27 +493,32 @@ class Interpreter {
|
|
|
552
493
|
});
|
|
553
494
|
return forNode.children[0] || forNode;
|
|
554
495
|
}
|
|
496
|
+
forItemId = 0;
|
|
555
497
|
createForItem(forNode, i, parentData) {
|
|
556
498
|
let forItemNode;
|
|
557
|
-
const
|
|
499
|
+
const scope = new Scope(() => {});
|
|
500
|
+
scope.scope = null;
|
|
501
|
+
runWithPulling(() => {
|
|
502
|
+
scope.get();
|
|
503
|
+
}, null);
|
|
558
504
|
const arr = forNode.arr,
|
|
559
505
|
itemExp = forNode.itemExp,
|
|
560
506
|
indexName = forNode.indexName,
|
|
561
507
|
getKey = forNode.getKey;
|
|
562
508
|
let data;
|
|
563
|
-
if (typeof itemExp ===
|
|
564
|
-
data =
|
|
509
|
+
if (typeof itemExp === 'string') {
|
|
510
|
+
data = deepSignal(indexName ? {
|
|
565
511
|
[itemExp]: arr[i],
|
|
566
512
|
[indexName]: i
|
|
567
513
|
} : {
|
|
568
514
|
[itemExp]: arr[i]
|
|
569
|
-
});
|
|
515
|
+
}, getPulling());
|
|
570
516
|
} else {
|
|
571
517
|
const rawData = itemExp(arr[i]);
|
|
572
518
|
if (indexName) {
|
|
573
519
|
rawData[indexName] = i;
|
|
574
520
|
}
|
|
575
|
-
data =
|
|
521
|
+
data = deepSignal(rawData, getPulling());
|
|
576
522
|
}
|
|
577
523
|
Object.setPrototypeOf(data, parentData);
|
|
578
524
|
forItemNode = {
|
|
@@ -586,7 +532,7 @@ class Interpreter {
|
|
|
586
532
|
effect: null,
|
|
587
533
|
data
|
|
588
534
|
};
|
|
589
|
-
forItemNode.effect =
|
|
535
|
+
forItemNode.effect = scope;
|
|
590
536
|
return forItemNode;
|
|
591
537
|
}
|
|
592
538
|
getData() {
|
|
@@ -594,24 +540,16 @@ class Interpreter {
|
|
|
594
540
|
node = _this$ctx$stack$peekB.node;
|
|
595
541
|
return node.data || node.owner.data;
|
|
596
542
|
}
|
|
597
|
-
/**
|
|
598
|
-
* key 元素,组件的 key
|
|
599
|
-
* value
|
|
600
|
-
* 1. 静态类型值
|
|
601
|
-
* 2. 插值计算 函数,可以考虑 使用 effect 或 computed 做处理
|
|
602
|
-
*
|
|
603
|
-
* mapKey 映射, 对应子组件的属性
|
|
604
|
-
* */
|
|
605
543
|
onePropParsed(data, node, key, value, valueIsMapKey, isFn, hookI) {
|
|
606
544
|
if (isFn) {
|
|
607
545
|
this.setProp(node, key, value, hookI);
|
|
608
|
-
} else if (typeof value ===
|
|
609
|
-
|
|
546
|
+
} else if (typeof value === 'function') {
|
|
547
|
+
new Effect(() => {
|
|
610
548
|
const res = value();
|
|
611
549
|
this.setProp(node, key, res, hookI);
|
|
612
550
|
});
|
|
613
551
|
} else if (valueIsMapKey) {
|
|
614
|
-
|
|
552
|
+
new Effect(() => {
|
|
615
553
|
const res = data[value];
|
|
616
554
|
this.setProp(node, key, res, hookI);
|
|
617
555
|
});
|
|
@@ -619,6 +557,7 @@ class Interpreter {
|
|
|
619
557
|
this.setProp(node, key, value, hookI);
|
|
620
558
|
}
|
|
621
559
|
}
|
|
560
|
+
oneRealPropParsed = this.onePropParsed.bind(this);
|
|
622
561
|
componentOrFragmentDeclaration(ComponentOrRender, ctx) {
|
|
623
562
|
let Component, render, child;
|
|
624
563
|
const isCC = ComponentOrRender.prototype instanceof Store;
|
|
@@ -637,7 +576,7 @@ class Interpreter {
|
|
|
637
576
|
realBefore: null,
|
|
638
577
|
realAfter: null,
|
|
639
578
|
data: child,
|
|
640
|
-
tokenizer: render ? render(true) : child[
|
|
579
|
+
tokenizer: render ? render(true) : child['ui'](true)
|
|
641
580
|
};
|
|
642
581
|
this.onePropParsed = (data, _, key, value, valueIsMapKey, isFn, hookI) => {
|
|
643
582
|
if (isFn) {
|
|
@@ -647,32 +586,31 @@ class Interpreter {
|
|
|
647
586
|
} else {
|
|
648
587
|
const meta = child[Keys.Meta];
|
|
649
588
|
const cells = meta.cells;
|
|
650
|
-
if (typeof value ===
|
|
651
|
-
const computed =
|
|
589
|
+
if (typeof value === 'function') {
|
|
590
|
+
const computed = new Computed(value);
|
|
652
591
|
cells.set(key, computed);
|
|
653
|
-
child[Keys.Raw][key] =
|
|
592
|
+
child[Keys.Raw][key] = undefined;
|
|
654
593
|
} else {
|
|
655
594
|
cells.set(key, {
|
|
656
|
-
|
|
595
|
+
get: () => value
|
|
657
596
|
});
|
|
658
597
|
child[Keys.Raw][key] = value;
|
|
659
598
|
}
|
|
660
599
|
}
|
|
661
600
|
};
|
|
662
|
-
node.realAfter = this.insertAfterAnchor(
|
|
601
|
+
node.realAfter = this.insertAfterAnchor('component-after');
|
|
663
602
|
return node;
|
|
664
603
|
}
|
|
665
604
|
getFn(data, expression) {
|
|
666
|
-
return new Function(
|
|
605
|
+
return new Function('data', `let v;with(data){v=${expression}};return v;`).bind(undefined, data);
|
|
667
606
|
}
|
|
668
|
-
// TODO: 优化代码逻辑,拆分 if elseif else
|
|
669
607
|
condDeclaration(ctx) {
|
|
670
608
|
const prevSibling = ctx.prevSibling;
|
|
671
609
|
const keyWord = this.tokenizer.token;
|
|
672
610
|
const expToken = this.tokenizer.condExp();
|
|
673
611
|
const value = expToken.value;
|
|
674
|
-
const isElse = keyWord.value ===
|
|
675
|
-
const isIf = keyWord.value ===
|
|
612
|
+
const isElse = keyWord.value === 'else';
|
|
613
|
+
const isIf = keyWord.value === 'if';
|
|
676
614
|
const preIsCond = prevSibling?.__logicType & CondBit;
|
|
677
615
|
const data = this.getData();
|
|
678
616
|
const noCond = value === true;
|
|
@@ -680,7 +618,6 @@ class Interpreter {
|
|
|
680
618
|
const owner = ctx.stack.peekByType(NodeSort.TokenizerSwitcher)?.node;
|
|
681
619
|
const ifNode = {
|
|
682
620
|
__logicType: isElse ? FakeType.Else : isIf ? FakeType.If : FakeType.Fail,
|
|
683
|
-
// 此时 token 是 exp, 下次解析 从 \n 开始
|
|
684
621
|
snapshot: this.tokenizer.snapshot(),
|
|
685
622
|
realParent: null,
|
|
686
623
|
realBefore: null,
|
|
@@ -693,22 +630,22 @@ class Interpreter {
|
|
|
693
630
|
};
|
|
694
631
|
let signal;
|
|
695
632
|
switch (keyWord.value) {
|
|
696
|
-
case
|
|
633
|
+
case 'if':
|
|
697
634
|
if (valueIsMapKey) {
|
|
698
635
|
runWithPulling(() => data[value], null);
|
|
699
636
|
const cells = data[Keys.Meta].cells;
|
|
700
637
|
signal = cells.get(value);
|
|
701
638
|
} else {
|
|
702
639
|
const fn = this.getFn(data, value);
|
|
703
|
-
signal =
|
|
640
|
+
signal = new Computed(fn);
|
|
704
641
|
}
|
|
705
642
|
break;
|
|
706
|
-
case
|
|
643
|
+
case 'else':
|
|
707
644
|
if (noCond) {
|
|
708
|
-
signal =
|
|
645
|
+
signal = new Computed(() => {
|
|
709
646
|
let point = ifNode.preCond;
|
|
710
647
|
while (point) {
|
|
711
|
-
if (point.condition.
|
|
648
|
+
if (point.condition.get()) {
|
|
712
649
|
return false;
|
|
713
650
|
}
|
|
714
651
|
if (point.__logicType === FakeType.If) {
|
|
@@ -720,10 +657,10 @@ class Interpreter {
|
|
|
720
657
|
});
|
|
721
658
|
} else {
|
|
722
659
|
const fn = valueIsMapKey ? null : this.getFn(data, value);
|
|
723
|
-
signal =
|
|
660
|
+
signal = new Computed(() => {
|
|
724
661
|
let point = ifNode.preCond;
|
|
725
662
|
while (point) {
|
|
726
|
-
if (point.condition.
|
|
663
|
+
if (point.condition.get()) {
|
|
727
664
|
return false;
|
|
728
665
|
}
|
|
729
666
|
if (point.__logicType === FakeType.If) {
|
|
@@ -735,11 +672,11 @@ class Interpreter {
|
|
|
735
672
|
});
|
|
736
673
|
}
|
|
737
674
|
break;
|
|
738
|
-
case
|
|
739
|
-
signal =
|
|
675
|
+
case 'fail':
|
|
676
|
+
signal = new Computed(() => {
|
|
740
677
|
let point = ifNode.preCond;
|
|
741
678
|
while (point) {
|
|
742
|
-
if (point.condition.
|
|
679
|
+
if (point.condition.get()) {
|
|
743
680
|
return false;
|
|
744
681
|
}
|
|
745
682
|
point = point.preCond;
|
|
@@ -750,7 +687,7 @@ class Interpreter {
|
|
|
750
687
|
}
|
|
751
688
|
ifNode.condition = signal;
|
|
752
689
|
ifNode.realAfter = this.insertAfterAnchor(`${keyWord.value}-after`);
|
|
753
|
-
|
|
690
|
+
const ef = effect(({
|
|
754
691
|
val
|
|
755
692
|
}) => {
|
|
756
693
|
if (val) {
|
|
@@ -772,6 +709,7 @@ class Interpreter {
|
|
|
772
709
|
}
|
|
773
710
|
ifNode.isFirstRender = false;
|
|
774
711
|
}, [signal]);
|
|
712
|
+
ifNode.effect = ef.ins;
|
|
775
713
|
return ifNode;
|
|
776
714
|
}
|
|
777
715
|
removeLogicNode(node) {
|
|
@@ -785,10 +723,6 @@ class Interpreter {
|
|
|
785
723
|
point = next;
|
|
786
724
|
}
|
|
787
725
|
}
|
|
788
|
-
/**
|
|
789
|
-
* <extensionLines> ::= PIPE <attributeList> NEWLINE <extensionLines>
|
|
790
|
-
* | ε
|
|
791
|
-
*/
|
|
792
726
|
extensionLines(_node) {
|
|
793
727
|
while (1) {
|
|
794
728
|
if ((this.tokenizer.token.type & TokenType.Pipe) === 0) {
|
|
@@ -802,25 +736,10 @@ class Interpreter {
|
|
|
802
736
|
this.tokenizer.nextToken();
|
|
803
737
|
}
|
|
804
738
|
}
|
|
805
|
-
/**
|
|
806
|
-
* 首行:
|
|
807
|
-
* 节点名称 + 属性列表 + 换行
|
|
808
|
-
* <headerLine> ::= <attributeList> NEWLINE
|
|
809
|
-
*/
|
|
810
739
|
headerLine(_node) {
|
|
811
740
|
this.attributeList(_node);
|
|
812
741
|
this.tokenizer.nextToken();
|
|
813
742
|
}
|
|
814
|
-
/**
|
|
815
|
-
* 属性列表:
|
|
816
|
-
* 可以是空的,或者包含多个属性
|
|
817
|
-
* <attributeList> ::= <attribute> <attributeList>
|
|
818
|
-
* | ε
|
|
819
|
-
*
|
|
820
|
-
* <attribute> ::= <key> = <value>
|
|
821
|
-
* 1. 普通节点 执行 setProps 🪝
|
|
822
|
-
* 2. 组件节点 收集映射关系,或 产生 computed
|
|
823
|
-
*/
|
|
824
743
|
attributeList(_node) {
|
|
825
744
|
let key, eq;
|
|
826
745
|
const data = this.getData();
|
|
@@ -828,7 +747,7 @@ class Interpreter {
|
|
|
828
747
|
if (key == null) {
|
|
829
748
|
key = this.tokenizer.token.value;
|
|
830
749
|
} else if (eq == null) {
|
|
831
|
-
eq =
|
|
750
|
+
eq = '=';
|
|
832
751
|
} else {
|
|
833
752
|
const _this$tokenizer$_hook3 = this.tokenizer._hook({}),
|
|
834
753
|
_this$tokenizer$_hook4 = _slicedToArray(_this$tokenizer$_hook3, 3),
|
|
@@ -836,12 +755,12 @@ class Interpreter {
|
|
|
836
755
|
value = _this$tokenizer$_hook4[1],
|
|
837
756
|
hookI = _this$tokenizer$_hook4[2];
|
|
838
757
|
const rawVal = data[Keys.Raw][value];
|
|
839
|
-
const isFn = typeof rawVal ===
|
|
840
|
-
if (hookType ===
|
|
758
|
+
const isFn = typeof rawVal === 'function';
|
|
759
|
+
if (hookType === 'dynamic') {
|
|
841
760
|
const valueIsMapKey = Reflect.has(data[Keys.Raw], value);
|
|
842
761
|
const fn = isFn ? rawVal : valueIsMapKey ? value : this.getFn(data, value);
|
|
843
762
|
this.onePropParsed(data, _node, key, fn, valueIsMapKey, isFn, hookI);
|
|
844
|
-
} else if (hookType ===
|
|
763
|
+
} else if (hookType === 'static') {
|
|
845
764
|
this.onePropParsed(data, _node, key, value, false, isFn, hookI);
|
|
846
765
|
} else {
|
|
847
766
|
this.onePropParsed(data, _node, key, value, false, isFn, hookI);
|
|
@@ -892,7 +811,6 @@ class Interpreter {
|
|
|
892
811
|
remove(node, parent, prev) {
|
|
893
812
|
return this.defaultRemove(node, parent, prev);
|
|
894
813
|
}
|
|
895
|
-
// TODO: 默认改成 prevItem
|
|
896
814
|
defaultRemove(node, parent, prevSibling) {
|
|
897
815
|
const next = node.nextSibling;
|
|
898
816
|
if (prevSibling) {
|
|
@@ -908,76 +826,33 @@ class Interpreter {
|
|
|
908
826
|
}
|
|
909
827
|
|
|
910
828
|
class Tokenizer {
|
|
829
|
+
TabSize = 2;
|
|
830
|
+
Tab = Array.from({
|
|
831
|
+
length: this.TabSize
|
|
832
|
+
}, () => ' ').join('');
|
|
833
|
+
static EofId = `__EOF__${Date.now()}`;
|
|
834
|
+
static DedentId = `__DEDENT__${Date.now()}`;
|
|
835
|
+
needIndent = false;
|
|
836
|
+
isFirstToken = true;
|
|
837
|
+
dentStack = [0];
|
|
838
|
+
i = 0;
|
|
839
|
+
handledTokens = [];
|
|
840
|
+
waitingTokens = new Queue();
|
|
911
841
|
constructor(hook, useDedentAsEof) {
|
|
912
842
|
this.hook = hook;
|
|
913
843
|
this.useDedentAsEof = useDedentAsEof;
|
|
914
|
-
/** 缩进大小 默认 2 */
|
|
915
|
-
this.TabSize = 2;
|
|
916
|
-
/** 缩进字符 */
|
|
917
|
-
this.Tab = Array.from({
|
|
918
|
-
length: this.TabSize
|
|
919
|
-
}, () => " ").join("");
|
|
920
|
-
/** 回车后需要判断缩进 */
|
|
921
|
-
this.needIndent = false;
|
|
922
|
-
/** 用于跳过第一个节点前的空白字符串,以及生成基础缩进 */
|
|
923
|
-
this.isFirstToken = true;
|
|
924
|
-
/** 记录历史缩进的长度,相对于行首 */
|
|
925
|
-
this.dentStack = [0];
|
|
926
|
-
/** 当前字符 index */
|
|
927
|
-
this.i = 0;
|
|
928
|
-
// TODO: 生产环境不需要这个,导致不必要的内存占用
|
|
929
|
-
this.handledTokens = [];
|
|
930
|
-
/**
|
|
931
|
-
* 有些标识符能产生多个 token
|
|
932
|
-
* 例如 dedent
|
|
933
|
-
* parent1
|
|
934
|
-
* child
|
|
935
|
-
* subChild
|
|
936
|
-
* parent2 <- 产生两个 dedent
|
|
937
|
-
*/
|
|
938
|
-
this.waitingTokens = new Queue();
|
|
939
|
-
/** 模板字符串动态节点的占位符 */
|
|
940
|
-
this.HookId = "_h_o_o_k_";
|
|
941
|
-
/** 模板字符串动态节点索引 */
|
|
942
|
-
this.hookI = 0;
|
|
943
|
-
this._hook = props => {
|
|
944
|
-
const value = this.token.value;
|
|
945
|
-
const isDynamicHook = this.token.type & TokenType.InsertionExp;
|
|
946
|
-
const isStaticHook = typeof value === "string" && value.indexOf(this.HookId) === 0;
|
|
947
|
-
const hookType = isDynamicHook ? "dynamic" : isStaticHook ? "static" : void 0;
|
|
948
|
-
if (this.hook && isStaticHook) {
|
|
949
|
-
const hookI = Number(value.slice(this.HookId.length));
|
|
950
|
-
const res = this.hook({
|
|
951
|
-
...props,
|
|
952
|
-
HookId: this.HookId,
|
|
953
|
-
i: hookI
|
|
954
|
-
});
|
|
955
|
-
return [hookType, res, hookI];
|
|
956
|
-
} else if (isDynamicHook) {
|
|
957
|
-
return [hookType, value];
|
|
958
|
-
}
|
|
959
|
-
return [hookType, value];
|
|
960
|
-
};
|
|
961
844
|
if (useDedentAsEof) {
|
|
962
|
-
this.setToken(TokenType.Indent,
|
|
845
|
+
this.setToken(TokenType.Indent, '');
|
|
963
846
|
this.isFirstToken = true;
|
|
964
847
|
}
|
|
965
848
|
}
|
|
966
|
-
static {
|
|
967
|
-
/** Eof 标识符的值 */
|
|
968
|
-
this.EofId = `__EOF__${Date.now()}`;
|
|
969
|
-
}
|
|
970
|
-
static {
|
|
971
|
-
this.DedentId = `__DEDENT__${Date.now()}`;
|
|
972
|
-
}
|
|
973
849
|
consume() {
|
|
974
850
|
const token = this.token;
|
|
975
851
|
this.nextToken();
|
|
976
852
|
return token;
|
|
977
853
|
}
|
|
978
|
-
// /** 恢复至某一个现场,进行 token 重算 */
|
|
979
854
|
resume(_snapshot) {
|
|
980
|
-
this.token =
|
|
855
|
+
this.token = undefined;
|
|
981
856
|
this.needIndent = false;
|
|
982
857
|
this.isFirstToken = true;
|
|
983
858
|
this.dentStack = [0];
|
|
@@ -991,7 +866,7 @@ class Tokenizer {
|
|
|
991
866
|
if (keys) {
|
|
992
867
|
for (const k of keys) {
|
|
993
868
|
snap[k] = this[k];
|
|
994
|
-
if (k ===
|
|
869
|
+
if (k === 'dentStack') {
|
|
995
870
|
snap[k] = this[k].slice();
|
|
996
871
|
}
|
|
997
872
|
}
|
|
@@ -1002,10 +877,10 @@ class Tokenizer {
|
|
|
1002
877
|
const logicDentLen = this.dentStack[this.dentStack.length - 1];
|
|
1003
878
|
let needIndent = false;
|
|
1004
879
|
let skipFragment = ``;
|
|
1005
|
-
this.token =
|
|
880
|
+
this.token = undefined;
|
|
1006
881
|
while (1) {
|
|
1007
882
|
const char = this.code[this.i];
|
|
1008
|
-
if (char ===
|
|
883
|
+
if (char === '\n') {
|
|
1009
884
|
needIndent = true;
|
|
1010
885
|
skipFragment += char;
|
|
1011
886
|
this.i++;
|
|
@@ -1029,7 +904,7 @@ class Tokenizer {
|
|
|
1029
904
|
const expLen = this.dentStack[i];
|
|
1030
905
|
if (currLen === expLen) break;
|
|
1031
906
|
if (currLen > expLen) {
|
|
1032
|
-
throw SyntaxError(
|
|
907
|
+
throw SyntaxError(`缩进错误,缩进长度不匹配`);
|
|
1033
908
|
}
|
|
1034
909
|
if (this.shorterThanBaseDentEof()) {
|
|
1035
910
|
break;
|
|
@@ -1054,13 +929,12 @@ class Tokenizer {
|
|
|
1054
929
|
return skipFragment;
|
|
1055
930
|
}
|
|
1056
931
|
setCode(code) {
|
|
1057
|
-
this.code =
|
|
1058
|
-
${Tokenizer.EofId}`;
|
|
932
|
+
this.code = '\n' + code.trimEnd() + `\n${Tokenizer.EofId}`;
|
|
1059
933
|
}
|
|
1060
934
|
tokenize() {
|
|
1061
935
|
do {
|
|
1062
936
|
this.nextToken();
|
|
1063
|
-
console.log(
|
|
937
|
+
console.log('token:', TokenType[this.token?.type], JSON.stringify(this.token?.value || ''));
|
|
1064
938
|
} while (!this.isEof());
|
|
1065
939
|
}
|
|
1066
940
|
isEof() {
|
|
@@ -1080,7 +954,7 @@ ${Tokenizer.EofId}`;
|
|
|
1080
954
|
if (this.isEof()) {
|
|
1081
955
|
return this.token;
|
|
1082
956
|
}
|
|
1083
|
-
this.token =
|
|
957
|
+
this.token = undefined;
|
|
1084
958
|
if (this.waitingTokens.len) {
|
|
1085
959
|
const item = this.waitingTokens.shift();
|
|
1086
960
|
this.setToken(item.type, item.value);
|
|
@@ -1092,40 +966,39 @@ ${Tokenizer.EofId}`;
|
|
|
1092
966
|
} else {
|
|
1093
967
|
const char = this.code[this.i];
|
|
1094
968
|
switch (char) {
|
|
1095
|
-
case
|
|
1096
|
-
case
|
|
969
|
+
case '\t':
|
|
970
|
+
case ' ':
|
|
1097
971
|
break;
|
|
1098
|
-
|
|
1099
|
-
case "\n":
|
|
972
|
+
case '\n':
|
|
1100
973
|
this.newLine();
|
|
1101
974
|
this.needIndent = true;
|
|
1102
975
|
break;
|
|
1103
|
-
case
|
|
976
|
+
case '=':
|
|
1104
977
|
this.assignment();
|
|
1105
978
|
break;
|
|
1106
|
-
case
|
|
979
|
+
case '|':
|
|
1107
980
|
this.pipe();
|
|
1108
981
|
break;
|
|
1109
982
|
case "'":
|
|
1110
983
|
case '"':
|
|
1111
984
|
this.str(char);
|
|
1112
985
|
break;
|
|
1113
|
-
case
|
|
986
|
+
case '{':
|
|
1114
987
|
const braceToken = this.brace();
|
|
1115
988
|
this.setToken(TokenType.InsertionExp, braceToken);
|
|
1116
989
|
break;
|
|
1117
|
-
case
|
|
990
|
+
case '$':
|
|
1118
991
|
const handled = this.dynamic(char);
|
|
1119
992
|
if (handled) break;
|
|
1120
|
-
case
|
|
1121
|
-
this.setToken(TokenType.Semicolon,
|
|
993
|
+
case ';':
|
|
994
|
+
this.setToken(TokenType.Semicolon, ';');
|
|
1122
995
|
break;
|
|
1123
996
|
default:
|
|
1124
997
|
if (isNum(char)) {
|
|
1125
998
|
this.number(char);
|
|
1126
999
|
break;
|
|
1127
1000
|
}
|
|
1128
|
-
if (typeof char ===
|
|
1001
|
+
if (typeof char === 'string' && matchIdStart2(char, 0)) {
|
|
1129
1002
|
this.identifier(char);
|
|
1130
1003
|
}
|
|
1131
1004
|
break;
|
|
@@ -1145,11 +1018,11 @@ ${Tokenizer.EofId}`;
|
|
|
1145
1018
|
}
|
|
1146
1019
|
}
|
|
1147
1020
|
condExp() {
|
|
1148
|
-
let value =
|
|
1021
|
+
let value = '';
|
|
1149
1022
|
this.token = null;
|
|
1150
1023
|
while (1) {
|
|
1151
1024
|
const char = this.code[this.i];
|
|
1152
|
-
if (char ===
|
|
1025
|
+
if (char === '\n') {
|
|
1153
1026
|
break;
|
|
1154
1027
|
}
|
|
1155
1028
|
value += char;
|
|
@@ -1159,21 +1032,14 @@ ${Tokenizer.EofId}`;
|
|
|
1159
1032
|
this.setToken(TokenType.Identifier, value || true);
|
|
1160
1033
|
return this.token;
|
|
1161
1034
|
}
|
|
1162
|
-
/**
|
|
1163
|
-
* 解析到 for 时使用这个方法获取 for 后方的子表达式
|
|
1164
|
-
* 表达式通过 “;” 分割
|
|
1165
|
-
* // 最多可有三个表达式
|
|
1166
|
-
* for arr ; item index; item.key
|
|
1167
|
-
* @returns {boolean} 是否含有 key
|
|
1168
|
-
*/
|
|
1169
1035
|
forLoopSubExp() {
|
|
1170
1036
|
this.token = null;
|
|
1171
|
-
let value =
|
|
1037
|
+
let value = '';
|
|
1172
1038
|
let count = 0;
|
|
1173
1039
|
while (1) {
|
|
1174
1040
|
const char = this.code[this.i];
|
|
1175
|
-
const isSemicolon = char ===
|
|
1176
|
-
if (isSemicolon || char ===
|
|
1041
|
+
const isSemicolon = char === ';';
|
|
1042
|
+
if (isSemicolon || char === '\n') {
|
|
1177
1043
|
value = value.trim();
|
|
1178
1044
|
if (!this.token) {
|
|
1179
1045
|
this.setToken(TokenType.Identifier, value);
|
|
@@ -1184,10 +1050,10 @@ ${Tokenizer.EofId}`;
|
|
|
1184
1050
|
value
|
|
1185
1051
|
});
|
|
1186
1052
|
}
|
|
1187
|
-
value =
|
|
1053
|
+
value = '';
|
|
1188
1054
|
count++;
|
|
1189
1055
|
if (count > 3) {
|
|
1190
|
-
throw SyntaxError(`for
|
|
1056
|
+
throw SyntaxError(`for 循环最多可包含三个表达式, 分别为 arr ; item index [; key]`);
|
|
1191
1057
|
}
|
|
1192
1058
|
if (!isSemicolon) return count === 3;
|
|
1193
1059
|
} else {
|
|
@@ -1197,27 +1063,27 @@ ${Tokenizer.EofId}`;
|
|
|
1197
1063
|
}
|
|
1198
1064
|
}
|
|
1199
1065
|
assignment() {
|
|
1200
|
-
this.setToken(TokenType.Assign,
|
|
1066
|
+
this.setToken(TokenType.Assign, '=');
|
|
1201
1067
|
}
|
|
1202
1068
|
pipe() {
|
|
1203
|
-
this.setToken(TokenType.Pipe,
|
|
1069
|
+
this.setToken(TokenType.Pipe, '|');
|
|
1204
1070
|
}
|
|
1205
1071
|
dynamic(char) {
|
|
1206
1072
|
let nextC = this.code[this.i + 1];
|
|
1207
|
-
if (nextC !==
|
|
1073
|
+
if (nextC !== '{') {
|
|
1208
1074
|
return false;
|
|
1209
1075
|
}
|
|
1210
1076
|
this.i++;
|
|
1211
|
-
let value =
|
|
1077
|
+
let value = '${';
|
|
1212
1078
|
let innerBrace = 0;
|
|
1213
1079
|
while (1) {
|
|
1214
1080
|
nextC = this.code[this.i + 1];
|
|
1215
1081
|
value += nextC;
|
|
1216
1082
|
this.i++;
|
|
1217
|
-
if (nextC ===
|
|
1083
|
+
if (nextC === '{') {
|
|
1218
1084
|
innerBrace++;
|
|
1219
1085
|
}
|
|
1220
|
-
if (nextC ===
|
|
1086
|
+
if (nextC === '}') {
|
|
1221
1087
|
if (!innerBrace) {
|
|
1222
1088
|
break;
|
|
1223
1089
|
}
|
|
@@ -1231,14 +1097,14 @@ ${Tokenizer.EofId}`;
|
|
|
1231
1097
|
let inComment,
|
|
1232
1098
|
inString,
|
|
1233
1099
|
count = 0,
|
|
1234
|
-
value =
|
|
1100
|
+
value = '',
|
|
1235
1101
|
backslashCount = 0;
|
|
1236
1102
|
while (1) {
|
|
1237
1103
|
const char = this.code[this.i];
|
|
1238
1104
|
const nextChar = this.code[this.i + 1];
|
|
1239
|
-
if (inComment ===
|
|
1105
|
+
if (inComment === 'single' && char === '\n') {
|
|
1240
1106
|
inComment = null;
|
|
1241
|
-
} else if (inComment ===
|
|
1107
|
+
} else if (inComment === 'multi' && char === '*' && nextChar === '/') {
|
|
1242
1108
|
inComment = null;
|
|
1243
1109
|
value += this.code[this.i];
|
|
1244
1110
|
this.i++;
|
|
@@ -1246,21 +1112,21 @@ ${Tokenizer.EofId}`;
|
|
|
1246
1112
|
if (char === inString && backslashCount % 2 === 0) {
|
|
1247
1113
|
inString = null;
|
|
1248
1114
|
}
|
|
1249
|
-
backslashCount = char ===
|
|
1115
|
+
backslashCount = char === '\\' ? backslashCount + 1 : 0;
|
|
1250
1116
|
} else {
|
|
1251
|
-
if (char ===
|
|
1252
|
-
inComment =
|
|
1117
|
+
if (char === '/' && nextChar === '/') {
|
|
1118
|
+
inComment = 'single';
|
|
1253
1119
|
value += this.code[this.i];
|
|
1254
1120
|
this.i++;
|
|
1255
|
-
} else if (char ===
|
|
1256
|
-
inComment =
|
|
1121
|
+
} else if (char === '/' && nextChar === '*') {
|
|
1122
|
+
inComment = 'multi';
|
|
1257
1123
|
value += this.code[this.i];
|
|
1258
1124
|
this.i++;
|
|
1259
|
-
} else if (char === "'" || char === '"' || char ===
|
|
1125
|
+
} else if (char === "'" || char === '"' || char === '`') {
|
|
1260
1126
|
inString = char;
|
|
1261
|
-
} else if (char ===
|
|
1127
|
+
} else if (char === '{') {
|
|
1262
1128
|
count++;
|
|
1263
|
-
} else if (char ===
|
|
1129
|
+
} else if (char === '}') {
|
|
1264
1130
|
count--;
|
|
1265
1131
|
}
|
|
1266
1132
|
}
|
|
@@ -1272,11 +1138,11 @@ ${Tokenizer.EofId}`;
|
|
|
1272
1138
|
}
|
|
1273
1139
|
}
|
|
1274
1140
|
newLine() {
|
|
1275
|
-
let value =
|
|
1141
|
+
let value = '\n';
|
|
1276
1142
|
let nextC;
|
|
1277
1143
|
while (1) {
|
|
1278
1144
|
nextC = this.code[this.i + 1];
|
|
1279
|
-
if (nextC !==
|
|
1145
|
+
if (nextC !== '\n') {
|
|
1280
1146
|
break;
|
|
1281
1147
|
}
|
|
1282
1148
|
value += nextC;
|
|
@@ -1288,26 +1154,26 @@ ${Tokenizer.EofId}`;
|
|
|
1288
1154
|
this.setToken(TokenType.NewLine, value);
|
|
1289
1155
|
}
|
|
1290
1156
|
getDentValue() {
|
|
1291
|
-
let value =
|
|
1157
|
+
let value = '';
|
|
1292
1158
|
let nextC;
|
|
1293
1159
|
let isEmptyLine = false;
|
|
1294
1160
|
while (1) {
|
|
1295
1161
|
const nextChar = this.code[this.i];
|
|
1296
1162
|
switch (nextChar) {
|
|
1297
|
-
case
|
|
1163
|
+
case '\t':
|
|
1298
1164
|
nextC = this.Tab;
|
|
1299
1165
|
break;
|
|
1300
|
-
case
|
|
1301
|
-
nextC =
|
|
1166
|
+
case ' ':
|
|
1167
|
+
nextC = ' ';
|
|
1302
1168
|
break;
|
|
1303
|
-
case
|
|
1304
|
-
nextC =
|
|
1169
|
+
case '\n':
|
|
1170
|
+
nextC = '\n';
|
|
1305
1171
|
break;
|
|
1306
1172
|
default:
|
|
1307
|
-
nextC =
|
|
1173
|
+
nextC = '';
|
|
1308
1174
|
break;
|
|
1309
1175
|
}
|
|
1310
|
-
if (nextC ===
|
|
1176
|
+
if (nextC === '\n') {
|
|
1311
1177
|
isEmptyLine = true;
|
|
1312
1178
|
break;
|
|
1313
1179
|
}
|
|
@@ -1348,7 +1214,7 @@ ${Tokenizer.EofId}`;
|
|
|
1348
1214
|
const expLen = this.dentStack[i];
|
|
1349
1215
|
if (currLen === expLen) break;
|
|
1350
1216
|
if (currLen > expLen) {
|
|
1351
|
-
throw SyntaxError(
|
|
1217
|
+
throw SyntaxError('缩进大小不统一');
|
|
1352
1218
|
}
|
|
1353
1219
|
if (this.shorterThanBaseDentEof()) {
|
|
1354
1220
|
return;
|
|
@@ -1373,7 +1239,7 @@ ${Tokenizer.EofId}`;
|
|
|
1373
1239
|
if (yes) {
|
|
1374
1240
|
if (!this.token) {
|
|
1375
1241
|
if (this.useDedentAsEof) {
|
|
1376
|
-
this.setToken(TokenType.Dedent,
|
|
1242
|
+
this.setToken(TokenType.Dedent, '');
|
|
1377
1243
|
} else {
|
|
1378
1244
|
this.setToken(TokenType.Identifier, Tokenizer.EofId);
|
|
1379
1245
|
}
|
|
@@ -1382,7 +1248,7 @@ ${Tokenizer.EofId}`;
|
|
|
1382
1248
|
this.waitingTokens.push({
|
|
1383
1249
|
type: TokenType.Dedent,
|
|
1384
1250
|
typeName: TokenType[TokenType.Dedent],
|
|
1385
|
-
value:
|
|
1251
|
+
value: ''
|
|
1386
1252
|
});
|
|
1387
1253
|
} else {
|
|
1388
1254
|
this.waitingTokens.push({
|
|
@@ -1400,27 +1266,27 @@ ${Tokenizer.EofId}`;
|
|
|
1400
1266
|
let nextC;
|
|
1401
1267
|
while (1) {
|
|
1402
1268
|
nextC = this.code[this.i + 1];
|
|
1403
|
-
if (typeof nextC !==
|
|
1269
|
+
if (typeof nextC !== 'string' || !matchIdStart2(nextC, 0)) {
|
|
1404
1270
|
break;
|
|
1405
1271
|
}
|
|
1406
1272
|
value += nextC;
|
|
1407
1273
|
this.i++;
|
|
1408
1274
|
}
|
|
1409
1275
|
if (value === Tokenizer.EofId && this.useDedentAsEof) {
|
|
1410
|
-
this.setToken(TokenType.Dedent,
|
|
1276
|
+
this.setToken(TokenType.Dedent, '');
|
|
1411
1277
|
return;
|
|
1412
1278
|
}
|
|
1413
|
-
let realValue = value ===
|
|
1279
|
+
let realValue = value === 'null' ? null : value === 'undefined' ? undefined : value === 'false' ? false : value === 'true' ? true : value;
|
|
1414
1280
|
this.setToken(TokenType.Identifier, realValue);
|
|
1415
1281
|
}
|
|
1416
1282
|
str(char) {
|
|
1417
|
-
let value =
|
|
1283
|
+
let value = '';
|
|
1418
1284
|
let nextC;
|
|
1419
1285
|
let continuousBackslashCount = 0;
|
|
1420
1286
|
while (1) {
|
|
1421
1287
|
nextC = this.code[this.i + 1];
|
|
1422
1288
|
const memoCount = continuousBackslashCount;
|
|
1423
|
-
if (nextC ===
|
|
1289
|
+
if (nextC === '\\') {
|
|
1424
1290
|
continuousBackslashCount++;
|
|
1425
1291
|
} else {
|
|
1426
1292
|
continuousBackslashCount = 0;
|
|
@@ -1447,13 +1313,33 @@ ${Tokenizer.EofId}`;
|
|
|
1447
1313
|
this.setToken(TokenType.Identifier, Number(value));
|
|
1448
1314
|
}
|
|
1449
1315
|
eof() {
|
|
1450
|
-
this.setToken(TokenType.Eof,
|
|
1451
|
-
}
|
|
1316
|
+
this.setToken(TokenType.Eof, 'End Of File');
|
|
1317
|
+
}
|
|
1318
|
+
HookId = '_h_o_o_k_';
|
|
1319
|
+
hookI = 0;
|
|
1320
|
+
_hook = props => {
|
|
1321
|
+
const value = this.token.value;
|
|
1322
|
+
const isDynamicHook = this.token.type & TokenType.InsertionExp;
|
|
1323
|
+
const isStaticHook = typeof value === 'string' && value.indexOf(this.HookId) === 0;
|
|
1324
|
+
const hookType = isDynamicHook ? 'dynamic' : isStaticHook ? 'static' : undefined;
|
|
1325
|
+
if (this.hook && isStaticHook) {
|
|
1326
|
+
const hookI = Number(value.slice(this.HookId.length));
|
|
1327
|
+
const res = this.hook({
|
|
1328
|
+
...props,
|
|
1329
|
+
HookId: this.HookId,
|
|
1330
|
+
i: hookI
|
|
1331
|
+
});
|
|
1332
|
+
return [hookType, res, hookI];
|
|
1333
|
+
} else if (isDynamicHook) {
|
|
1334
|
+
return [hookType, value];
|
|
1335
|
+
}
|
|
1336
|
+
return [hookType, value];
|
|
1337
|
+
};
|
|
1452
1338
|
init(fragments) {
|
|
1453
|
-
if (typeof fragments ===
|
|
1339
|
+
if (typeof fragments === 'string') {
|
|
1454
1340
|
this.setCode(fragments);
|
|
1455
1341
|
} else {
|
|
1456
|
-
let code =
|
|
1342
|
+
let code = '';
|
|
1457
1343
|
for (let i = 0; i < fragments.length - 1; i++) {
|
|
1458
1344
|
const fragment = fragments[i];
|
|
1459
1345
|
code += fragment + `${this.HookId}${i}`;
|
|
@@ -1464,7 +1350,7 @@ ${Tokenizer.EofId}`;
|
|
|
1464
1350
|
}
|
|
1465
1351
|
|
|
1466
1352
|
function bobe(fragments, ...values) {
|
|
1467
|
-
const ui = function
|
|
1353
|
+
const ui = function ui(isSub) {
|
|
1468
1354
|
const tokenizer = new Tokenizer(({
|
|
1469
1355
|
i
|
|
1470
1356
|
}) => {
|
|
@@ -1479,7 +1365,7 @@ function bobe(fragments, ...values) {
|
|
|
1479
1365
|
function customRender(option) {
|
|
1480
1366
|
return function render(Ctor, root) {
|
|
1481
1367
|
const store = Ctor.new();
|
|
1482
|
-
const tokenizer = store[
|
|
1368
|
+
const tokenizer = store['ui'](false);
|
|
1483
1369
|
const terp = new Interpreter(tokenizer);
|
|
1484
1370
|
terp.config(option);
|
|
1485
1371
|
const componentNode = {
|