bobe 0.0.15 → 0.0.17

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/index.umd.js CHANGED
@@ -15,25 +15,27 @@
15
15
  TokenType2[TokenType2["InsertionExp"] = 128] = "InsertionExp";
16
16
  return TokenType2;
17
17
  })(TokenType || {});
18
- var LogicType = /* @__PURE__ */ ((LogicType2) => {
19
- LogicType2[LogicType2["If"] = 1] = "If";
20
- LogicType2[LogicType2["Fail"] = 2] = "Fail";
21
- LogicType2[LogicType2["Else"] = 4] = "Else";
22
- LogicType2[LogicType2["For"] = 8] = "For";
23
- LogicType2[LogicType2["Component"] = 16] = "Component";
24
- LogicType2[LogicType2["Fragment"] = 32] = "Fragment";
25
- LogicType2[LogicType2["Root"] = 64] = "Root";
26
- LogicType2[LogicType2["Real"] = 128] = "Real";
27
- return LogicType2;
28
- })(LogicType || {});
29
- const Logical = 1 /* If */ | 2 /* Fail */ | 4 /* Else */ | 8 /* For */;
30
- const CondType = 1 /* If */ | 2 /* Fail */ | 4 /* Else */;
31
- var NodeType = /* @__PURE__ */ ((NodeType2) => {
32
- NodeType2[NodeType2["Logic"] = Logical] = "Logic";
33
- NodeType2[NodeType2["Real"] = 128 /* Real */] = "Real";
34
- NodeType2[NodeType2["Component"] = 16 /* Component */] = "Component";
35
- return NodeType2;
36
- })(NodeType || {});
18
+ var FakeType = /* @__PURE__ */ ((FakeType2) => {
19
+ FakeType2[FakeType2["If"] = 1] = "If";
20
+ FakeType2[FakeType2["Fail"] = 2] = "Fail";
21
+ FakeType2[FakeType2["Else"] = 4] = "Else";
22
+ FakeType2[FakeType2["For"] = 8] = "For";
23
+ FakeType2[FakeType2["Component"] = 16] = "Component";
24
+ FakeType2[FakeType2["Fragment"] = 32] = "Fragment";
25
+ FakeType2[FakeType2["ForItem"] = 64] = "ForItem";
26
+ return FakeType2;
27
+ })(FakeType || {});
28
+ const CondBit = 1 /* If */ | 2 /* Fail */ | 4 /* Else */;
29
+ const LogicalBit = 1 /* If */ | 2 /* Fail */ | 4 /* Else */ | 8 /* For */ | 64 /* ForItem */;
30
+ const TokenizerSwitcherBit = 16 /* Component */ | 32 /* Fragment */;
31
+ var NodeSort = /* @__PURE__ */ ((NodeSort2) => {
32
+ NodeSort2[NodeSort2["Logic"] = 1] = "Logic";
33
+ NodeSort2[NodeSort2["Real"] = 2] = "Real";
34
+ NodeSort2[NodeSort2["Component"] = 4] = "Component";
35
+ NodeSort2[NodeSort2["CtxProvider"] = 8] = "CtxProvider";
36
+ NodeSort2[NodeSort2["TokenizerSwitcher"] = 16] = "TokenizerSwitcher";
37
+ return NodeSort2;
38
+ })(NodeSort || {});
37
39
  var TerpEvt = /* @__PURE__ */ ((TerpEvt2) => {
38
40
  TerpEvt2["AllAttrGot"] = "all-attr-got";
39
41
  TerpEvt2["HandledComponentNode"] = "handled-component-node";
@@ -41,140 +43,123 @@
41
43
  })(TerpEvt || {});
42
44
  const IsAnchor = /* @__PURE__ */ Symbol("is-anchor");
43
45
 
44
- class TypedStack {
46
+ class MultiTypeStack {
45
47
  constructor() {
48
+ // 记录全局栈顶
46
49
  this.top = null;
47
- // 存储每种类型最近一次出现的包装单元引用
48
- // 使用 Record 来动态支持不同的类型标签
49
- this.lastNodes = {};
50
+ // 记录每个类别的当前最新节点(各分类的“栈顶”)
51
+ this.typeTops = {};
50
52
  this.length = 0;
51
53
  }
52
54
  /**
53
- * @param rawNode 原始节点数据
54
- * @param type 节点类型
55
+ * 入栈操作
56
+ * @param value 数据
57
+ * @param bits 该节点所属的类别数组
55
58
  */
56
- push(rawNode, type) {
57
- var _a;
59
+ push(value, bits) {
58
60
  const newNode = {
59
- data: rawNode,
60
- type,
61
- prev: this.top,
62
- prevSameType: (_a = this.lastNodes[type]) != null ? _a : null
61
+ value,
62
+ types: bits,
63
+ prevGlobal: this.top,
64
+ prevByType: {}
63
65
  };
66
+ let bit;
67
+ while (1) {
68
+ bit = bits & ~bits + 1;
69
+ if (!bit) break;
70
+ bits &= ~bit;
71
+ newNode.prevByType[bit] = this.typeTops[bit] || void 0;
72
+ this.typeTops[bit] = newNode;
73
+ }
64
74
  this.top = newNode;
65
75
  this.length++;
66
- this.lastNodes[type] = newNode;
67
76
  }
68
77
  /**
69
78
  * 出栈操作
70
- * @returns 原始节点数据或 null
71
79
  */
72
80
  pop() {
73
- if (!this.top) return null;
74
- const popped = this.top;
75
- this.lastNodes[popped.type] = popped.prevSameType;
76
- this.top = popped.prev;
81
+ if (!this.top) return void 0;
82
+ const poppedNode = this.top;
83
+ let { types: bits } = poppedNode;
84
+ let bit;
85
+ while (1) {
86
+ bit = bits & ~bits + 1;
87
+ if (!bit) break;
88
+ bits &= ~bit;
89
+ this.typeTops[bit] = poppedNode.prevByType[bit];
90
+ }
91
+ this.top = poppedNode.prevGlobal;
77
92
  this.length--;
78
- return popped.data;
93
+ return [poppedNode.value, poppedNode.types];
79
94
  }
80
95
  /**
81
- * O(1) 获取栈顶节点的前一个同类型节点
96
+ * 获取某个类别的当前“顶部”元素
82
97
  */
83
- getPrevSameType() {
84
- if (!this.top || !this.top.prevSameType) {
85
- return null;
86
- }
87
- return this.top.prevSameType.data;
88
- }
89
- findPrevSameType(cb) {
90
- if (!this.top) {
91
- return null;
92
- }
93
- let point = this.top.prevSameType;
94
- while (point) {
95
- if (cb(point.data)) {
96
- return point.data;
97
- }
98
- point = point.prevSameType;
99
- }
100
- return null;
98
+ peekByType(cat) {
99
+ var _a;
100
+ return (_a = this.typeTops[cat]) == null ? void 0 : _a.value;
101
101
  }
102
- /**
103
- * 获取当前栈顶的类型
104
- */
105
- get peekType() {
106
- return this.top ? this.top.type : null;
102
+ peekType() {
103
+ var _a;
104
+ return (_a = this.top) == null ? void 0 : _a.types;
107
105
  }
108
106
  /**
109
- * 获取栈顶元素
107
+ * 获取全局栈顶
110
108
  */
111
109
  peek() {
112
- return this.top.data;
110
+ var _a;
111
+ return (_a = this.top) == null ? void 0 : _a.value;
113
112
  }
113
+ // /**
114
+ // * 1. 全局向前遍历 (不分类)
115
+ // * 从栈顶开始,沿着全局链条向栈底遍历
116
+ // */
117
+ // forEach(callback: (value: T, types: number) => any): void {
118
+ // let current = this.top;
119
+ // while (current !== null) {
120
+ // // 执行回调,如果返回 false 则立即停止
121
+ // const shouldBreak = callback(current.value, current.types);
122
+ // if (shouldBreak) break;
123
+ // current = current.prevGlobal;
124
+ // }
125
+ // }
126
+ // /**
127
+ // * 2. 按类别向前遍历
128
+ // * 仅遍历属于指定类别 cat 的节点
129
+ // */
130
+ // forEachByType(cat: number, callback: (value: T) => any): void {
131
+ // // 从该类别的当前“顶端”节点开始
132
+ // let current = this.typeTops[cat];
133
+ // while (current) {
134
+ // const shouldBreak = callback(current.value);
135
+ // if (shouldBreak) break;
136
+ // // 关键点:直接跳向该节点记录的“上一个同类节点”
137
+ // // 这比遍历全局栈再筛选类别要快得多 (O(m) vs O(n))
138
+ // current = current.prevByType[cat];
139
+ // }
140
+ // }
114
141
  }
115
142
 
116
- var __defProp$1 = Object.defineProperty;
117
- var __defProps$1 = Object.defineProperties;
118
- var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors;
119
- var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
120
- var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
121
- var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
122
- var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
123
- var __spreadValues$1 = (a, b) => {
124
- for (var prop in b || (b = {}))
125
- if (__hasOwnProp$1.call(b, prop))
126
- __defNormalProp$1(a, prop, b[prop]);
127
- if (__getOwnPropSymbols$1)
128
- for (var prop of __getOwnPropSymbols$1(b)) {
129
- if (__propIsEnum$1.call(b, prop))
130
- __defNormalProp$1(a, prop, b[prop]);
131
- }
132
- return a;
133
- };
134
- var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b));
135
143
  const tap = new bobeShared.BaseEvent();
136
144
  class Interpreter {
137
145
  constructor(tokenizer) {
138
146
  this.tokenizer = tokenizer;
139
- /** 模板字符串动态节点的占位符 */
140
- this.HookId = "_h_o_o_k_";
141
- /** 用于渲染的数据 */
142
- this.data = {};
143
- /** 模板字符串动态节点索引 */
144
- this.hookI = 0;
145
- this._hook = (props) => {
146
- const value = this.tokenizer.token.value;
147
- const isDynamicHook = this.tokenizer.token.type & TokenType.InsertionExp;
148
- const isStaticHook = typeof value === "string" && value.indexOf(this.HookId) === 0;
149
- const hookType = isDynamicHook ? "dynamic" : isStaticHook ? "static" : void 0;
150
- if (this.hook && isStaticHook) {
151
- const hookI = Number(value.slice(this.HookId.length));
152
- const res = this.hook(__spreadProps$1(__spreadValues$1({}, props), {
153
- HookId: this.HookId,
154
- i: hookI
155
- }));
156
- this.hookI++;
157
- return [hookType, res];
158
- } else if (isDynamicHook) {
159
- return [hookType, value];
160
- }
161
- return [hookType, value];
162
- };
147
+ this.rootComponent = null;
163
148
  }
164
149
  isLogicNode(node) {
165
- return node && node.__logicType & Logical;
150
+ return node && node.__logicType & LogicalBit;
166
151
  }
167
- program(root, before) {
168
- var _a;
169
- const componentNode = {
170
- __logicType: LogicType.Component,
171
- realParent: root,
172
- store: new aoye.Store()
173
- };
174
- this.tokenizer.consume();
175
- const stack = new TypedStack();
176
- stack.push({ node: root, prev: null }, NodeType.Real);
177
- const ctx = {
152
+ program(root, componentNode, before) {
153
+ var _a, _b;
154
+ this.rootComponent = componentNode;
155
+ this.tokenizer.nextToken();
156
+ const stack = new MultiTypeStack();
157
+ stack.push({ node: root, prev: null }, NodeSort.Real);
158
+ stack.push(
159
+ { node: componentNode, prev: null },
160
+ NodeSort.Component | NodeSort.CtxProvider | NodeSort.TokenizerSwitcher
161
+ );
162
+ const ctx = this.ctx = {
178
163
  realParent: root,
179
164
  prevSibling: before,
180
165
  current: null,
@@ -190,14 +175,14 @@
190
175
  }
191
176
  const token = this.tokenizer.token;
192
177
  if (token.type & TokenType.Indent) {
193
- this.tokenizer.consume();
178
+ this.tokenizer.nextToken();
194
179
  const isLogicNode = this.isLogicNode(ctx.current);
195
180
  stack.push(
196
181
  {
197
182
  node: ctx.current,
198
183
  prev: ctx.prevSibling
199
184
  },
200
- ctx.current.__logicType ? isLogicNode ? NodeType.Logic : NodeType.Component : NodeType.Real
185
+ !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
201
186
  );
202
187
  if (ctx.current.__logicType) {
203
188
  if (isLogicNode) {
@@ -213,28 +198,31 @@
213
198
  continue;
214
199
  }
215
200
  if (ctx.current) {
216
- if (stack.length === 1 && !ctx.prevSibling) {
201
+ if (stack.length === 2 && !ctx.prevSibling) {
217
202
  ctx.prevSibling = before;
218
203
  }
219
204
  this.handleInsert(ctx.realParent, ctx.current, ctx.prevSibling);
220
205
  }
221
206
  if (this.tokenizer.token.type & TokenType.Dedent) {
222
- this.tokenizer.consume();
223
- const { node: parent, prev } = stack.peek();
207
+ this.tokenizer.nextToken();
208
+ const [{ node: parent, prev }, sort] = stack.pop();
224
209
  if (!parent.__logicType) {
225
- const prevSameType = stack.getPrevSameType();
210
+ const prevSameType = stack.peekByType(NodeSort.Real);
226
211
  ctx.realParent = (prevSameType == null ? void 0 : prevSameType.node) || root;
227
212
  } else {
228
- if (this.isLogicNode(parent)) {
229
- const parentLogic = (_a = stack.getPrevSameType()) == null ? void 0 : _a.node;
213
+ if (sort & NodeSort.Logic) {
214
+ const parentLogic = (_a = stack.peekByType(NodeSort.Logic)) == null ? void 0 : _a.node;
230
215
  if (parentLogic) {
231
216
  aoye.setPulling(parentLogic.effect.ins);
232
217
  } else {
233
218
  aoye.setPulling(rootPulling);
234
219
  }
235
220
  }
221
+ if (sort & NodeSort.TokenizerSwitcher) {
222
+ const switcher = (_b = stack.peekByType(NodeSort.TokenizerSwitcher)) == null ? void 0 : _b.node;
223
+ this.tokenizer = switcher.tokenizer;
224
+ }
236
225
  }
237
- stack.pop();
238
226
  ctx.prevSibling = prev;
239
227
  ctx.current = parent;
240
228
  } else {
@@ -244,10 +232,15 @@
244
232
  }
245
233
  return componentNode;
246
234
  }
235
+ switcherIsRootComponent() {
236
+ var _a;
237
+ const currentSwitcher = (_a = this.ctx.stack.peekByType(NodeSort.TokenizerSwitcher)) == null ? void 0 : _a.node;
238
+ return currentSwitcher === this.rootComponent;
239
+ }
247
240
  insertAfterAnchor(ctx) {
248
241
  const { realParent, prevSibling, stack, before } = ctx;
249
242
  const afterAnchor = this.createAnchor();
250
- ctx.prevSibling = stack.length === 1 && !prevSibling ? before : prevSibling;
243
+ ctx.prevSibling = stack.length === 2 && !prevSibling ? before : prevSibling;
251
244
  this.handleInsert(realParent, afterAnchor, prevSibling);
252
245
  return afterAnchor;
253
246
  }
@@ -294,7 +287,7 @@
294
287
  * <declaration> ::= <tagName=token> <headerLine> <extensionLines>
295
288
  * */
296
289
  declaration(ctx) {
297
- const [hookType, value] = this._hook({});
290
+ const [hookType, value] = this.tokenizer._hook({});
298
291
  let _node;
299
292
  if (value === "if" || value === "else" || value === "fail") {
300
293
  return this.condDeclaration(ctx);
@@ -303,34 +296,42 @@
303
296
  if (typeof value === "function" && value.prototype instanceof aoye.Store) {
304
297
  _node = this.componentDeclaration(value, ctx);
305
298
  } else if (typeof value === "function") {
306
- _node = this.fragmentDeclaration(value);
299
+ _node = this.fragmentDeclaration(value, ctx);
307
300
  } else {
308
301
  throw new SyntaxError(`declaration \u4E0D\u652F\u6301 ${value} \u7C7B\u578B\u7684\u9759\u6001\u63D2\u503C`);
309
302
  }
310
303
  } else {
311
- Boolean(this.data[aoye.Keys.Raw][value]);
304
+ const data = this.getData();
305
+ Boolean(data[aoye.Keys.Raw][value]);
312
306
  new Function("data", `let v;with(data){v=(${value})};return v`);
313
307
  _node = this.createNode(value);
314
308
  }
315
309
  } else {
316
310
  _node = this.createNode(value);
317
311
  }
318
- this.tokenizer.consume();
312
+ this.tokenizer.nextToken();
319
313
  this.headerLine(_node);
320
314
  this.extensionLines(_node);
321
- if (_node.__logicType === LogicType.Component) {
315
+ if (_node.__logicType === FakeType.Component) {
322
316
  tap.once(TerpEvt.HandledComponentNode, (node) => _node = node);
323
317
  tap.emit(TerpEvt.AllAttrGot);
324
318
  }
325
319
  return _node;
326
320
  }
321
+ getData() {
322
+ const { node } = this.ctx.stack.peekByType(NodeSort.CtxProvider);
323
+ return node.data || node.owner.data;
324
+ }
327
325
  // TODO: 指定挂载位置
328
- fragmentDeclaration(renderFragment) {
326
+ fragmentDeclaration(renderFragment, ctx) {
327
+ const data = this.getData();
328
+ const tokenizer = renderFragment.call(data, this.opt, { data, root: "", anchor: "" });
329
329
  const fragmentNode = {
330
- __logicType: LogicType.Fragment,
331
- realParent: null
330
+ __logicType: FakeType.Fragment,
331
+ realParent: null,
332
+ tokenizer,
333
+ data: null
332
334
  };
333
- renderFragment.call(this.data, this.opt, { data: this.data, root: "", anchor: "" });
334
335
  return fragmentNode;
335
336
  }
336
337
  /**
@@ -341,15 +342,17 @@
341
342
  *
342
343
  * mapKey 映射, 对应子组件的属性
343
344
  * */
344
- onePropParsed(node, key, value, valueIsMapKey, hookI) {
345
- if (typeof value === "function") {
345
+ onePropParsed(data, node, key, value, valueIsMapKey, isFn, hookI) {
346
+ if (isFn) {
347
+ this.setProp(node, key, value, hookI);
348
+ } else if (typeof value === "function") {
346
349
  aoye.effect(() => {
347
350
  const res = value();
348
351
  this.setProp(node, key, res, hookI);
349
352
  });
350
353
  } else if (valueIsMapKey) {
351
354
  aoye.effect(() => {
352
- const res = this.data[value];
355
+ const res = data[value];
353
356
  this.setProp(node, key, res, hookI);
354
357
  });
355
358
  } else {
@@ -358,10 +361,18 @@
358
361
  }
359
362
  componentDeclaration(Component, ctx) {
360
363
  const child = Component.new();
364
+ const componentNode = {
365
+ __logicType: FakeType.Component,
366
+ realParent: ctx.realParent,
367
+ data: child,
368
+ tokenizer: null
369
+ };
361
370
  const prevOnePropParsed = this.onePropParsed;
362
- this.onePropParsed = (node, key, value, valueIsMapKey, hookI) => {
363
- if (valueIsMapKey) {
364
- aoye.shareSignal(this.data, value, child, key);
371
+ this.onePropParsed = (data, node, key, value, valueIsMapKey, isFn, hookI) => {
372
+ if (isFn) {
373
+ child[aoye.Keys.Raw][key] = value;
374
+ } else if (valueIsMapKey) {
375
+ aoye.shareSignal(data, value, child, key);
365
376
  } else if (typeof value === "function") {
366
377
  const meta = child[aoye.Keys.Meta];
367
378
  const cells = meta.cells;
@@ -371,37 +382,41 @@
371
382
  child[aoye.Keys.Raw][key] = value;
372
383
  }
373
384
  };
374
- const afterAnchor = this.insertAfterAnchor(ctx);
385
+ componentNode.realAfter = this.insertAfterAnchor(ctx);
375
386
  const { realParent, prevSibling } = ctx;
376
387
  tap.once(TerpEvt.AllAttrGot, () => {
377
- const parent = realParent;
378
- const prev = prevSibling;
379
388
  this.onePropParsed = prevOnePropParsed;
380
- const componentNode = child["ui"](this.opt, { data: child }, parent, prev);
381
- componentNode.realAfter = afterAnchor;
389
+ const subTkr = child["ui"](true);
390
+ componentNode.tokenizer = subTkr;
391
+ this.tokenizer = subTkr;
382
392
  tap.emit(TerpEvt.HandledComponentNode, componentNode);
383
393
  });
384
- return { __logicType: LogicType.Component };
394
+ return componentNode;
385
395
  }
386
396
  // TODO: 优化代码逻辑,拆分 if elseif else
387
397
  condDeclaration(ctx) {
398
+ var _a;
388
399
  const { prevSibling } = ctx;
389
400
  const snapbackUp = this.tokenizer.snapshot();
390
- const keyWord = this.tokenizer.consume();
401
+ const keyWord = this.tokenizer.token;
402
+ this.tokenizer.nextToken();
391
403
  const noSelfCond = this.tokenizer.token.type === TokenType.NewLine;
392
- const [hookType, value] = this._hook({});
404
+ const [hookType, value] = this.tokenizer._hook({});
393
405
  const isElse = keyWord.value === "else";
394
406
  const isIf = keyWord.value === "if";
395
- const preIsCond = (prevSibling == null ? void 0 : prevSibling.__logicType) & CondType;
407
+ const preIsCond = (prevSibling == null ? void 0 : prevSibling.__logicType) & CondBit;
396
408
  const needCalcWithPrevIf = isElse && preIsCond;
409
+ const data = this.getData();
410
+ const owner = (_a = ctx.stack.peekByType(NodeSort.TokenizerSwitcher)) == null ? void 0 : _a.node;
397
411
  const ifNode = {
398
- __logicType: isElse ? LogicType.Else : isIf ? LogicType.If : LogicType.Fail,
412
+ __logicType: isElse ? FakeType.Else : isIf ? FakeType.If : FakeType.Fail,
399
413
  snapshot: noSelfCond ? snapbackUp : this.tokenizer.snapshot(),
400
414
  condition: null,
401
415
  realParent: null,
402
416
  preCond: preIsCond ? prevSibling : null,
403
417
  isFirstRender: true,
404
- effect: null
418
+ effect: null,
419
+ owner
405
420
  };
406
421
  let signal;
407
422
  if (noSelfCond) {
@@ -412,7 +427,7 @@
412
427
  if (point.condition.v) {
413
428
  return false;
414
429
  }
415
- if (point.__logicType === LogicType.If) {
430
+ if (point.__logicType === FakeType.If) {
416
431
  break;
417
432
  }
418
433
  point = point.preCond;
@@ -432,13 +447,13 @@
432
447
  });
433
448
  }
434
449
  } else {
435
- const valueIsMapKey = Reflect.has(this.data[aoye.Keys.Raw], value);
450
+ const valueIsMapKey = Reflect.has(data[aoye.Keys.Raw], value);
436
451
  if (valueIsMapKey && !needCalcWithPrevIf) {
437
- aoye.runWithPulling(() => this.data[value], null);
438
- const { cells } = this.data[aoye.Keys.Meta];
452
+ aoye.runWithPulling(() => data[value], null);
453
+ const { cells } = data[aoye.Keys.Meta];
439
454
  signal = cells.get(value);
440
455
  } else {
441
- const fn = new Function("data", `let v;with(data){v=${value}};return v;`).bind(void 0, this.data);
456
+ const fn = new Function("data", `let v;with(data){v=${value}};return v;`).bind(void 0, data);
442
457
  if (needCalcWithPrevIf) {
443
458
  signal = aoye.$(() => {
444
459
  let point = ifNode.preCond;
@@ -446,7 +461,7 @@
446
461
  if (point.condition.v) {
447
462
  return false;
448
463
  }
449
- if (point.__logicType === LogicType.If) {
464
+ if (point.__logicType === FakeType.If) {
450
465
  break;
451
466
  }
452
467
  point = point.preCond;
@@ -465,12 +480,13 @@
465
480
  if (val) {
466
481
  if (ifNode.isFirstRender) {
467
482
  if (!noSelfCond) {
468
- this.tokenizer.consume();
483
+ this.tokenizer.nextToken();
469
484
  }
470
- this.tokenizer.consume();
485
+ this.tokenizer.nextToken();
471
486
  } else {
487
+ this.tokenizer = ifNode.owner.tokenizer;
472
488
  this.tokenizer.resume(ifNode.snapshot);
473
- this.program(ifNode.realParent, ifNode.realBefore);
489
+ this.program(ifNode.realParent, ifNode.owner, ifNode.realBefore);
474
490
  }
475
491
  } else {
476
492
  if (ifNode.isFirstRender) {
@@ -481,7 +497,7 @@
481
497
  this.tokenizer.skip();
482
498
  } else {
483
499
  const { realBefore, realAfter, realParent } = ifNode;
484
- let point = realBefore ? this.nextSib(realBefore) : this.kid(realParent);
500
+ let point = realBefore ? this.nextSib(realBefore) : this.firstChild(realParent);
485
501
  while (point !== realAfter) {
486
502
  const next = this.nextSib(point);
487
503
  this.remove(point, realParent, realBefore);
@@ -501,15 +517,15 @@
501
517
  */
502
518
  extensionLines(_node) {
503
519
  while (1) {
504
- if (!(this.tokenizer.token.type & TokenType.Pipe)) {
520
+ if ((this.tokenizer.token.type & TokenType.Pipe) === 0) {
505
521
  return;
506
522
  }
507
- this.tokenizer.consume();
523
+ this.tokenizer.nextToken();
508
524
  this.attributeList(_node);
509
- if (!(this.tokenizer.token.type & TokenType.NewLine)) {
525
+ if ((this.tokenizer.token.type & TokenType.NewLine) === 0) {
510
526
  return;
511
527
  }
512
- this.tokenizer.consume();
528
+ this.tokenizer.nextToken();
513
529
  }
514
530
  }
515
531
  /**
@@ -519,7 +535,7 @@
519
535
  */
520
536
  headerLine(_node) {
521
537
  this.attributeList(_node);
522
- this.tokenizer.consume();
538
+ this.tokenizer.nextToken();
523
539
  }
524
540
  /**
525
541
  * 属性列表:
@@ -529,30 +545,33 @@
529
545
  *
530
546
  * <attribute> ::= <key> = <value>
531
547
  * 1. 普通节点 执行 setProps 🪝
532
- * 2. 组件节点 收集映射关系,或通过 effect 直接设值
548
+ * 2. 组件节点 收集映射关系,或 产生 computed
533
549
  */
534
550
  attributeList(_node) {
535
551
  let key, eq;
536
- while (!(this.tokenizer.token.type & TokenType.NewLine)) {
552
+ const data = this.getData();
553
+ while ((this.tokenizer.token.type & TokenType.NewLine) === 0) {
537
554
  if (key == null) {
538
555
  key = this.tokenizer.token.value;
539
556
  } else if (eq == null) {
540
557
  eq = "=";
541
558
  } else {
542
- const [hookType, value] = this._hook({});
559
+ const [hookType, value, hookI] = this.tokenizer._hook({});
560
+ const rawVal = data[aoye.Keys.Raw][value];
561
+ const isFn = typeof rawVal === "function";
543
562
  if (hookType === "dynamic") {
544
- const valueIsMapKey = Reflect.has(this.data[aoye.Keys.Raw], value);
545
- const fn = valueIsMapKey ? value : new Function("data", `let v;with(data){v=${value}};return v;`).bind(void 0, this.data);
546
- this.onePropParsed(_node, key, fn, valueIsMapKey, this.hookI);
563
+ const valueIsMapKey = Reflect.has(data[aoye.Keys.Raw], value);
564
+ const fn = isFn ? rawVal : valueIsMapKey ? value : new Function("data", `let v;with(data){v=${value}};return v;`).bind(void 0, data);
565
+ this.onePropParsed(data, _node, key, fn, valueIsMapKey, isFn, hookI);
547
566
  } else if (hookType === "static") {
548
- this.onePropParsed(_node, key, value, false, this.hookI);
567
+ this.onePropParsed(data, _node, key, value, false, isFn, hookI);
549
568
  } else {
550
- this.onePropParsed(_node, key, value, false, this.hookI);
569
+ this.onePropParsed(data, _node, key, value, false, isFn, hookI);
551
570
  }
552
571
  key = null;
553
572
  eq = null;
554
573
  }
555
- this.tokenizer.consume();
574
+ this.tokenizer.nextToken();
556
575
  }
557
576
  }
558
577
  config(opt) {
@@ -569,7 +588,7 @@
569
588
  nextSib(node) {
570
589
  return node.nextSibling;
571
590
  }
572
- kid(node) {
591
+ firstChild(node) {
573
592
  return node.firstChild;
574
593
  }
575
594
  _createAnchor() {
@@ -613,28 +632,35 @@
613
632
  setProp(node, key, value, hookI) {
614
633
  node.props[key] = value;
615
634
  }
616
- init(fragments) {
617
- if (typeof fragments === "string") {
618
- this.tokenizer.setCode(fragments);
619
- } else {
620
- let code = "";
621
- for (let i = 0; i < fragments.length - 1; i++) {
622
- const fragment = fragments[i];
623
- code += fragment + `${this.HookId}${i}`;
624
- }
625
- this.tokenizer.setCode(code + fragments[fragments.length - 1]);
626
- }
627
- }
628
635
  }
629
636
 
637
+ var __defProp = Object.defineProperty;
638
+ var __defProps = Object.defineProperties;
639
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
640
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
641
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
642
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
643
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
644
+ var __spreadValues = (a, b) => {
645
+ for (var prop in b || (b = {}))
646
+ if (__hasOwnProp.call(b, prop))
647
+ __defNormalProp(a, prop, b[prop]);
648
+ if (__getOwnPropSymbols)
649
+ for (var prop of __getOwnPropSymbols(b)) {
650
+ if (__propIsEnum.call(b, prop))
651
+ __defNormalProp(a, prop, b[prop]);
652
+ }
653
+ return a;
654
+ };
655
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
630
656
  const _Tokenizer = class _Tokenizer {
631
- constructor() {
657
+ constructor(hook, isSubToken) {
658
+ this.hook = hook;
659
+ this.isSubToken = isSubToken;
632
660
  /** 缩进大小 默认 2 */
633
661
  this.TabSize = 2;
634
662
  /** 缩进字符 */
635
663
  this.Tab = Array.from({ length: this.TabSize }, () => " ").join("");
636
- /** 匹配标识符 */
637
- this.IdExp = /[\d\w\/]/;
638
664
  /** 回车后需要判断缩进 */
639
665
  this.needIndent = false;
640
666
  /** 用于跳过第一个节点前的空白字符串,以及生成基础缩进 */
@@ -643,6 +669,7 @@
643
669
  this.dentStack = [0];
644
670
  /** 当前字符 index */
645
671
  this.i = 0;
672
+ // TODO: 生产环境不需要这个,导致不必要的内存占用
646
673
  this.handledTokens = [];
647
674
  /**
648
675
  * 有些标识符能产生多个 token
@@ -653,6 +680,31 @@
653
680
  * parent2 <- 产生两个 dedent
654
681
  */
655
682
  this.waitingTokens = new bobeShared.Queue();
683
+ /** 模板字符串动态节点的占位符 */
684
+ this.HookId = "_h_o_o_k_";
685
+ /** 模板字符串动态节点索引 */
686
+ this.hookI = 0;
687
+ this._hook = (props) => {
688
+ const value = this.token.value;
689
+ const isDynamicHook = this.token.type & TokenType.InsertionExp;
690
+ const isStaticHook = typeof value === "string" && value.indexOf(this.HookId) === 0;
691
+ const hookType = isDynamicHook ? "dynamic" : isStaticHook ? "static" : void 0;
692
+ if (this.hook && isStaticHook) {
693
+ const hookI = Number(value.slice(this.HookId.length));
694
+ const res = this.hook(__spreadProps(__spreadValues({}, props), {
695
+ HookId: this.HookId,
696
+ i: hookI
697
+ }));
698
+ return [hookType, res, hookI];
699
+ } else if (isDynamicHook) {
700
+ return [hookType, value];
701
+ }
702
+ return [hookType, value];
703
+ };
704
+ if (isSubToken) {
705
+ this.setToken(TokenType.Indent, "");
706
+ this.isFirstToken = true;
707
+ }
656
708
  }
657
709
  consume() {
658
710
  const token = this.token;
@@ -679,16 +731,16 @@
679
731
  let skipFragment = ``;
680
732
  this.token = void 0;
681
733
  while (1) {
682
- const char = this.char;
734
+ const char = this.code[this.i];
683
735
  if (char === "\n") {
684
736
  needIndent = true;
685
737
  skipFragment += char;
686
- this.next();
738
+ this.i++;
687
739
  continue;
688
740
  }
689
741
  if (!needIndent) {
690
742
  skipFragment += char;
691
- this.next();
743
+ this.i++;
692
744
  continue;
693
745
  }
694
746
  needIndent = false;
@@ -741,21 +793,6 @@ ${_Tokenizer.EofId}`;
741
793
  if (!this.token) return false;
742
794
  return this.token.type & TokenType.Identifier && this.token.value === _Tokenizer.EofId;
743
795
  }
744
- get char() {
745
- return this.code[this.i];
746
- }
747
- get prev() {
748
- return this.code[this.i - 1];
749
- }
750
- get after() {
751
- return this.code[this.i + 1];
752
- }
753
- next() {
754
- const prev = this.code[this.i];
755
- this.i++;
756
- const curr = this.code[this.i];
757
- return [prev, curr];
758
- }
759
796
  setToken(type, value) {
760
797
  this.token = {
761
798
  type,
@@ -764,10 +801,6 @@ ${_Tokenizer.EofId}`;
764
801
  };
765
802
  this.isFirstToken = false;
766
803
  }
767
- testId(value) {
768
- if (typeof value !== "string") return false;
769
- return this.IdExp.test(value);
770
- }
771
804
  nextToken() {
772
805
  try {
773
806
  if (this.isEof()) {
@@ -783,7 +816,7 @@ ${_Tokenizer.EofId}`;
783
816
  if (this.needIndent) {
784
817
  this.dent();
785
818
  } else {
786
- let { char } = this;
819
+ const char = this.code[this.i];
787
820
  switch (char) {
788
821
  case " ":
789
822
  case " ":
@@ -814,12 +847,12 @@ ${_Tokenizer.EofId}`;
814
847
  this.number(char);
815
848
  break;
816
849
  }
817
- if (this.testId(char)) {
850
+ if (typeof char === "string" && bobeShared.matchIdStart(char)) {
818
851
  this.identifier(char);
819
852
  }
820
853
  break;
821
854
  }
822
- this.next();
855
+ this.i++;
823
856
  }
824
857
  if (this.token) {
825
858
  break;
@@ -828,6 +861,7 @@ ${_Tokenizer.EofId}`;
828
861
  return this.token;
829
862
  } catch (error) {
830
863
  console.error(error);
864
+ return this.token;
831
865
  } finally {
832
866
  this.handledTokens.push(this.token);
833
867
  }
@@ -839,17 +873,17 @@ ${_Tokenizer.EofId}`;
839
873
  this.setToken(TokenType.Pipe, "|");
840
874
  }
841
875
  dynamic(char) {
842
- let nextC = this.after;
876
+ let nextC = this.code[this.i + 1];
843
877
  if (nextC !== "{") {
844
878
  return false;
845
879
  }
846
- this.next();
880
+ this.i++;
847
881
  let value = "${";
848
882
  let innerBrace = 0;
849
883
  while (1) {
850
- nextC = this.after;
884
+ nextC = this.code[this.i + 1];
851
885
  value += nextC;
852
- this.next();
886
+ this.i++;
853
887
  if (nextC === "{") {
854
888
  innerBrace++;
855
889
  }
@@ -866,13 +900,14 @@ ${_Tokenizer.EofId}`;
866
900
  brace() {
867
901
  let inComment, inString, count = 0, value = "", backslashCount = 0;
868
902
  while (1) {
869
- const char = this.char;
870
- const nextChar = this.after;
903
+ const char = this.code[this.i];
904
+ const nextChar = this.code[this.i + 1];
871
905
  if (inComment === "single" && char === "\n") {
872
906
  inComment = null;
873
907
  } else if (inComment === "multi" && char === "*" && nextChar === "/") {
874
908
  inComment = null;
875
- value += this.next()[0];
909
+ value += this.code[this.i];
910
+ this.i++;
876
911
  } else if (inString) {
877
912
  if (char === inString && backslashCount % 2 === 0) {
878
913
  inString = null;
@@ -881,10 +916,12 @@ ${_Tokenizer.EofId}`;
881
916
  } else {
882
917
  if (char === "/" && nextChar === "/") {
883
918
  inComment = "single";
884
- value += this.next()[0];
919
+ value += this.code[this.i];
920
+ this.i++;
885
921
  } else if (char === "/" && nextChar === "*") {
886
922
  inComment = "multi";
887
- value += this.next()[0];
923
+ value += this.code[this.i];
924
+ this.i++;
888
925
  } else if (char === "'" || char === '"' || char === "`") {
889
926
  inString = char;
890
927
  } else if (char === "{") {
@@ -897,19 +934,20 @@ ${_Tokenizer.EofId}`;
897
934
  this.setToken(TokenType.InsertionExp, value.slice(1));
898
935
  return;
899
936
  }
900
- value += this.next()[0];
937
+ value += this.code[this.i];
938
+ this.i++;
901
939
  }
902
940
  }
903
941
  newLine() {
904
942
  let value = "\n";
905
943
  let nextC;
906
944
  while (1) {
907
- nextC = this.after;
945
+ nextC = this.code[this.i + 1];
908
946
  if (nextC !== "\n") {
909
947
  break;
910
948
  }
911
949
  value += nextC;
912
- this.next();
950
+ this.i++;
913
951
  }
914
952
  if (this.isFirstToken) {
915
953
  return;
@@ -921,7 +959,7 @@ ${_Tokenizer.EofId}`;
921
959
  let nextC;
922
960
  let isEmptyLine = false;
923
961
  while (1) {
924
- const nextChar = this.char;
962
+ const nextChar = this.code[this.i];
925
963
  switch (nextChar) {
926
964
  case " ":
927
965
  nextC = this.Tab;
@@ -944,7 +982,7 @@ ${_Tokenizer.EofId}`;
944
982
  break;
945
983
  }
946
984
  value += nextC;
947
- this.next();
985
+ this.i++;
948
986
  }
949
987
  return {
950
988
  value,
@@ -967,7 +1005,7 @@ ${_Tokenizer.EofId}`;
967
1005
  const prevLen = this.dentStack[this.dentStack.length - 1];
968
1006
  if (currLen > prevLen) {
969
1007
  this.dentStack.push(currLen);
970
- this.setToken(TokenType.Indent, String(currLen));
1008
+ this.setToken(TokenType.Indent, currLen);
971
1009
  return indentHasLen;
972
1010
  }
973
1011
  if (currLen < prevLen) {
@@ -999,13 +1037,25 @@ ${_Tokenizer.EofId}`;
999
1037
  const yes = this.dentStack.length === 1;
1000
1038
  if (yes) {
1001
1039
  if (!this.token) {
1002
- this.setToken(TokenType.Identifier, _Tokenizer.EofId);
1040
+ if (this.isSubToken) {
1041
+ this.setToken(TokenType.Dedent, "");
1042
+ } else {
1043
+ this.setToken(TokenType.Identifier, _Tokenizer.EofId);
1044
+ }
1003
1045
  } else {
1004
- this.waitingTokens.push({
1005
- type: TokenType.Identifier,
1006
- typeName: TokenType[TokenType.Identifier],
1007
- value: _Tokenizer.EofId
1008
- });
1046
+ if (this.isSubToken) {
1047
+ this.waitingTokens.push({
1048
+ type: TokenType.Dedent,
1049
+ typeName: TokenType[TokenType.Dedent],
1050
+ value: ""
1051
+ });
1052
+ } else {
1053
+ this.waitingTokens.push({
1054
+ type: TokenType.Identifier,
1055
+ typeName: TokenType[TokenType.Identifier],
1056
+ value: _Tokenizer.EofId
1057
+ });
1058
+ }
1009
1059
  }
1010
1060
  }
1011
1061
  return yes;
@@ -1014,98 +1064,98 @@ ${_Tokenizer.EofId}`;
1014
1064
  let value = char;
1015
1065
  let nextC;
1016
1066
  while (1) {
1017
- nextC = this.after;
1018
- if (!this.testId(nextC)) {
1067
+ nextC = this.code[this.i + 1];
1068
+ if (typeof nextC !== "string" || !bobeShared.matchIdStart(nextC)) {
1019
1069
  break;
1020
1070
  }
1021
1071
  value += nextC;
1022
- this.next();
1072
+ this.i++;
1073
+ }
1074
+ if (value === _Tokenizer.EofId && this.isSubToken) {
1075
+ this.setToken(TokenType.Dedent, "");
1076
+ return;
1023
1077
  }
1024
1078
  let realValue = value === "null" ? null : value === "undefined" ? void 0 : value === "false" ? false : value === "true" ? true : value;
1025
1079
  this.setToken(TokenType.Identifier, realValue);
1026
1080
  }
1027
1081
  str(char) {
1028
- let value = '"';
1082
+ let value = "";
1029
1083
  let nextC;
1030
1084
  let continuousBackslashCount = 0;
1031
1085
  while (1) {
1032
- nextC = this.after;
1033
- value += nextC;
1086
+ nextC = this.code[this.i + 1];
1034
1087
  const memoCount = continuousBackslashCount;
1035
1088
  if (nextC === "\\") {
1036
1089
  continuousBackslashCount++;
1037
1090
  } else {
1038
1091
  continuousBackslashCount = 0;
1039
1092
  }
1040
- this.next();
1093
+ this.i++;
1041
1094
  if (nextC === char && memoCount % 2 === 0) {
1042
1095
  break;
1043
1096
  }
1097
+ value += nextC;
1044
1098
  }
1045
- this.setToken(TokenType.Identifier, JSON.parse(value.slice(0, -1) + '"'));
1099
+ this.setToken(TokenType.Identifier, value);
1046
1100
  }
1047
1101
  number(char) {
1048
1102
  let value = char;
1049
1103
  let nextC;
1050
1104
  while (1) {
1051
- nextC = this.after;
1105
+ nextC = this.code[this.i + 1];
1052
1106
  if (!bobeShared.isNum(nextC)) {
1053
1107
  break;
1054
1108
  }
1055
1109
  value += nextC;
1056
- this.next();
1110
+ this.i++;
1057
1111
  }
1058
1112
  this.setToken(TokenType.Identifier, Number(value));
1059
1113
  }
1060
1114
  eof() {
1061
1115
  this.setToken(TokenType.Eof, "End Of File");
1062
1116
  }
1117
+ init(fragments) {
1118
+ if (typeof fragments === "string") {
1119
+ this.setCode(fragments);
1120
+ } else {
1121
+ let code = "";
1122
+ for (let i = 0; i < fragments.length - 1; i++) {
1123
+ const fragment = fragments[i];
1124
+ code += fragment + `${this.HookId}${i}`;
1125
+ }
1126
+ this.setCode(code + fragments[fragments.length - 1]);
1127
+ }
1128
+ }
1063
1129
  };
1064
1130
  /** Eof 标识符的值 */
1065
1131
  _Tokenizer.EofId = `__EOF__${Date.now()}`;
1132
+ _Tokenizer.DedentId = `__DEDENT__${Date.now()}`;
1066
1133
  let Tokenizer = _Tokenizer;
1067
1134
 
1068
- var __defProp = Object.defineProperty;
1069
- var __defProps = Object.defineProperties;
1070
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
1071
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
1072
- var __hasOwnProp = Object.prototype.hasOwnProperty;
1073
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
1074
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1075
- var __spreadValues = (a, b) => {
1076
- for (var prop in b || (b = {}))
1077
- if (__hasOwnProp.call(b, prop))
1078
- __defNormalProp(a, prop, b[prop]);
1079
- if (__getOwnPropSymbols)
1080
- for (var prop of __getOwnPropSymbols(b)) {
1081
- if (__propIsEnum.call(b, prop))
1082
- __defNormalProp(a, prop, b[prop]);
1083
- }
1084
- return a;
1085
- };
1086
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
1087
1135
  function bobe(fragments, ...values) {
1088
- const ui = function ui2(options, valueOpt, root, before) {
1089
- const tokenizer = new Tokenizer();
1090
- const cmp = new Interpreter(tokenizer);
1091
- Object.assign(cmp, valueOpt);
1092
- cmp.config(__spreadProps(__spreadValues({}, options), {
1093
- hook({ i }) {
1094
- return values[i];
1095
- },
1096
- setProp(node, key, value, hookI) {
1097
- node.props[key] = value;
1098
- }
1099
- }));
1100
- cmp.init(Array.from(fragments));
1101
- return cmp.program(root, before);
1136
+ const ui = function ui2(isSub) {
1137
+ const tokenizer = new Tokenizer(({ i }) => {
1138
+ return values[i];
1139
+ }, isSub);
1140
+ tokenizer.init(Array.from(fragments));
1141
+ return tokenizer;
1102
1142
  };
1103
1143
  return ui;
1104
1144
  }
1105
1145
  function customRender(option) {
1106
1146
  return function render(Ctor, root) {
1107
1147
  const store = Ctor.new();
1108
- return [store["ui"](option, { data: store }, root), store];
1148
+ const tokenizer = store["ui"](false);
1149
+ const terp = new Interpreter(tokenizer);
1150
+ terp.config(option);
1151
+ const componentNode = {
1152
+ __logicType: FakeType.Component,
1153
+ realParent: root,
1154
+ data: store,
1155
+ tokenizer
1156
+ };
1157
+ terp.program(root, componentNode);
1158
+ return [componentNode, store];
1109
1159
  };
1110
1160
  }
1111
1161