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