bobe 0.0.35 → 0.0.37

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.esm.js CHANGED
@@ -42,6 +42,30 @@ let NodeSort = function (NodeSort) {
42
42
  TerpEvt["HandledComponentNode"] = "handled-component-node";
43
43
  return TerpEvt;
44
44
  })({});
45
+ let ParseErrorCode = function (ParseErrorCode) {
46
+ ParseErrorCode[ParseErrorCode["UNCLOSED_BRACE"] = 9001] = "UNCLOSED_BRACE";
47
+ ParseErrorCode[ParseErrorCode["UNCLOSED_STRING"] = 9002] = "UNCLOSED_STRING";
48
+ ParseErrorCode[ParseErrorCode["UNCLOSED_STATIC_INS"] = 9003] = "UNCLOSED_STATIC_INS";
49
+ ParseErrorCode[ParseErrorCode["INCONSISTENT_INDENT"] = 9004] = "INCONSISTENT_INDENT";
50
+ ParseErrorCode[ParseErrorCode["INDENT_MISMATCH"] = 9005] = "INDENT_MISMATCH";
51
+ ParseErrorCode[ParseErrorCode["MISSING_ASSIGN"] = 9006] = "MISSING_ASSIGN";
52
+ ParseErrorCode[ParseErrorCode["INVALID_TAG_NAME"] = 9007] = "INVALID_TAG_NAME";
53
+ ParseErrorCode[ParseErrorCode["ELSE_WITHOUT_IF"] = 9008] = "ELSE_WITHOUT_IF";
54
+ ParseErrorCode[ParseErrorCode["EMPTY_IF_BODY"] = 9009] = "EMPTY_IF_BODY";
55
+ ParseErrorCode[ParseErrorCode["EMPTY_FOR_BODY"] = 9010] = "EMPTY_FOR_BODY";
56
+ ParseErrorCode[ParseErrorCode["MISSING_FOR_COLLECTION"] = 9011] = "MISSING_FOR_COLLECTION";
57
+ ParseErrorCode[ParseErrorCode["MISSING_FOR_SEMICOLON"] = 9012] = "MISSING_FOR_SEMICOLON";
58
+ ParseErrorCode[ParseErrorCode["MISSING_FOR_ITEM"] = 9013] = "MISSING_FOR_ITEM";
59
+ ParseErrorCode[ParseErrorCode["PIPE_IN_WRONG_CONTEXT"] = 9014] = "PIPE_IN_WRONG_CONTEXT";
60
+ return ParseErrorCode;
61
+ }({});
62
+ class ParseSyntaxError extends SyntaxError {
63
+ constructor(code, message, loc) {
64
+ super(message);
65
+ this.code = code;
66
+ this.loc = loc;
67
+ }
68
+ }
45
69
 
46
70
  class Tokenizer {
47
71
  TabSize = 2;
@@ -80,6 +104,25 @@ class Tokenizer {
80
104
  column: this.column
81
105
  };
82
106
  }
107
+ unclosedLoc(startOffset, startLine, startCol) {
108
+ const end = this.code.length - 1;
109
+ return {
110
+ start: {
111
+ offset: startOffset,
112
+ line: startLine,
113
+ column: startCol
114
+ },
115
+ end: {
116
+ offset: end,
117
+ line: this.line,
118
+ column: this.column
119
+ },
120
+ source: this.code.slice(startOffset, end)
121
+ };
122
+ }
123
+ throwUnclosed(code, message, startOffset, startLine, startCol) {
124
+ throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
125
+ }
83
126
  resume(_snapshot) {
84
127
  this.token = undefined;
85
128
  this.needIndent = false;
@@ -133,7 +176,7 @@ class Tokenizer {
133
176
  const expLen = this.dentStack[i];
134
177
  if (currLen === expLen) break;
135
178
  if (currLen > expLen) {
136
- throw SyntaxError(`缩进错误,缩进长度不匹配`);
179
+ throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
137
180
  }
138
181
  if (this.shorterThanBaseDentEof()) {
139
182
  break;
@@ -248,8 +291,7 @@ class Tokenizer {
248
291
  }
249
292
  return this.token;
250
293
  } catch (error) {
251
- console.error(error);
252
- return this.token;
294
+ throw error;
253
295
  } finally {
254
296
  this.handledTokens.push(this.token);
255
297
  }
@@ -312,6 +354,9 @@ class Tokenizer {
312
354
  this.setToken(TokenType.Pipe, '|');
313
355
  }
314
356
  staticIns() {
357
+ const startOffset = this.preI,
358
+ startLine = this.line,
359
+ startCol = this.preCol;
315
360
  let nextC = this.code[this.i + 1];
316
361
  if (nextC !== '{') {
317
362
  return false;
@@ -321,6 +366,9 @@ class Tokenizer {
321
366
  let innerBrace = 0;
322
367
  while (1) {
323
368
  nextC = this.code[this.i + 1];
369
+ if (nextC === undefined) {
370
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_STATIC_INS, '未闭合的 "${...}"', startOffset, startLine, startCol);
371
+ }
324
372
  value += nextC;
325
373
  this.next();
326
374
  if (nextC === '{') {
@@ -337,6 +385,9 @@ class Tokenizer {
337
385
  return true;
338
386
  }
339
387
  brace() {
388
+ const startOffset = this.preI,
389
+ startLine = this.line,
390
+ startCol = this.preCol;
340
391
  let inComment,
341
392
  inString,
342
393
  count = 0,
@@ -344,6 +395,9 @@ class Tokenizer {
344
395
  backslashCount = 0;
345
396
  while (1) {
346
397
  const char = this.code[this.i];
398
+ if (char === undefined) {
399
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_BRACE, '未闭合的 "{"', startOffset, startLine, startCol);
400
+ }
347
401
  const nextChar = this.code[this.i + 1];
348
402
  if (inComment === 'single' && char === '\n') {
349
403
  inComment = null;
@@ -431,6 +485,18 @@ class Tokenizer {
431
485
  isEmptyLine
432
486
  };
433
487
  }
488
+ emptyLoc() {
489
+ const pos = this.getCurrentPos();
490
+ return {
491
+ start: pos,
492
+ end: {
493
+ offset: pos.offset + 1,
494
+ line: pos.line,
495
+ column: pos.column + 1
496
+ },
497
+ source: ' '
498
+ };
499
+ }
434
500
  dent() {
435
501
  const _this$getDentValue2 = this.getDentValue(),
436
502
  value = _this$getDentValue2.value,
@@ -457,7 +523,7 @@ class Tokenizer {
457
523
  const expLen = this.dentStack[i];
458
524
  if (currLen === expLen) break;
459
525
  if (currLen > expLen) {
460
- throw SyntaxError('缩进大小不统一');
526
+ throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
461
527
  }
462
528
  if (this.shorterThanBaseDentEof()) {
463
529
  return;
@@ -526,11 +592,17 @@ class Tokenizer {
526
592
  this.setToken(TokenType.Identifier, realValue);
527
593
  }
528
594
  str(char) {
595
+ const startOffset = this.preI,
596
+ startLine = this.line,
597
+ startCol = this.preCol;
529
598
  let value = '';
530
599
  let nextC;
531
600
  let continuousBackslashCount = 0;
532
601
  while (1) {
533
602
  nextC = this.code[this.i + 1];
603
+ if (nextC === undefined) {
604
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_STRING, '未闭合的字符串字面量', startOffset, startLine, startCol);
605
+ }
534
606
  const memoCount = continuousBackslashCount;
535
607
  if (nextC === '\\') {
536
608
  continuousBackslashCount++;
@@ -838,18 +910,33 @@ class Compiler {
838
910
  var _applyDecs$e = _slicedToArray(_applyDecs2311(this, [], [[NodeHook, 2, "parseProgram"], [[NodeHook, NodeLoc], 2, "parseComponentNode"], [[NodeHook, NodeLoc], 2, "parseElementNode"], [[NodeHook, NodeLoc], 2, "parseConditionalNode"], [[NodeHook, NodeLoc], 2, "parseLoopNode"], [NodeHook, 2, "parseProperty"], [[NodeHook, TokenLoc], 2, "parsePropertyKey"], [[NodeHook, TokenLoc], 2, "parsePropertyValue"], [[NodeHook, TokenLoc], 2, "parseName"]]).e, 1);
839
911
  _initProto = _applyDecs$e[0];
840
912
  }
913
+ errors = (_initProto(this), []);
841
914
  constructor(tokenizer, hooks = {}) {
842
915
  this.tokenizer = tokenizer;
843
916
  this.hooks = hooks;
844
- _initProto(this);
917
+ }
918
+ addError(code, message, loc) {
919
+ this.errors.push({
920
+ code,
921
+ message,
922
+ loc
923
+ });
845
924
  }
846
925
  parseProgram() {
847
- this.tokenizer.nextToken();
848
926
  const body = [];
849
- while (!this.tokenizer.isEof()) {
850
- const node = this.templateNode();
851
- if (node) {
852
- body.push(node);
927
+ try {
928
+ this.tokenizer.nextToken();
929
+ while (!this.tokenizer.isEof()) {
930
+ const node = this.templateNode(body);
931
+ if (node) {
932
+ body.push(node);
933
+ }
934
+ }
935
+ } catch (error) {
936
+ if (error instanceof ParseSyntaxError) {
937
+ this.addError(error.code, error.message, error.loc);
938
+ } else {
939
+ this.addError(error.toString(), '未知错误', this.tokenizer.emptyLoc());
853
940
  }
854
941
  }
855
942
  return {
@@ -875,7 +962,7 @@ class Compiler {
875
962
  if (this.tokenizer.token.type & TokenType.Indent) {
876
963
  this.tokenizer.nextToken();
877
964
  while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
878
- const child = this.templateNode();
965
+ const child = this.templateNode(children);
879
966
  if (child) {
880
967
  children.push(child);
881
968
  }
@@ -886,13 +973,26 @@ class Compiler {
886
973
  }
887
974
  return children;
888
975
  }
889
- templateNode() {
890
- this.tokenizer.token;
976
+ templateNode(siblings) {
977
+ const token = this.tokenizer.token;
978
+ if (token.type & TokenType.Pipe) {
979
+ this.addError(ParseErrorCode.PIPE_IN_WRONG_CONTEXT, '"|" 只能出现在元素属性扩展行中', token.loc ?? this.tokenizer.emptyLoc());
980
+ this.tokenizer.nextToken();
981
+ return null;
982
+ }
891
983
  const _this$tokenizer$_hook = this.tokenizer._hook({}),
892
984
  _this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
893
985
  hookType = _this$tokenizer$_hook2[0],
894
986
  value = _this$tokenizer$_hook2[1];
895
- if (value === 'if' || value === 'else' || value === 'fail') {
987
+ const isElseOrFail = value === 'else' || value === 'fail';
988
+ if (value === 'if' || isElseOrFail) {
989
+ if (isElseOrFail) {
990
+ const lastSibling = siblings[siblings.length - 1];
991
+ const lastType = lastSibling?.type;
992
+ if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
993
+ this.addError(ParseErrorCode.ELSE_WITHOUT_IF, `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.tokenizer.emptyLoc());
994
+ }
995
+ }
896
996
  return this.parseConditionalNode();
897
997
  }
898
998
  if (value === 'for') {
@@ -917,6 +1017,13 @@ class Compiler {
917
1017
  }
918
1018
  parseElementNode(node) {
919
1019
  const tagToken = this.tokenizer.token;
1020
+ if (!(tagToken.type & TokenType.Identifier)) {
1021
+ this.addError(ParseErrorCode.INVALID_TAG_NAME, `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.tokenizer.emptyLoc());
1022
+ while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
1023
+ this.tokenizer.nextToken();
1024
+ }
1025
+ return null;
1026
+ }
920
1027
  const tagName = tagToken.value;
921
1028
  this.tokenizer.nextToken();
922
1029
  const props = this.headerLineAndExtensions();
@@ -942,15 +1049,25 @@ class Compiler {
942
1049
  return node;
943
1050
  }
944
1051
  parseLoopNode(node) {
1052
+ const forLoc = this.tokenizer.token.loc ?? this.tokenizer.emptyLoc();
945
1053
  this.tokenizer.nextToken();
946
1054
  const collection = this.parsePropertyValue();
947
- this.tokenizer.nextToken();
1055
+ if (!collection.value && collection.value !== 0) {
1056
+ this.addError(ParseErrorCode.MISSING_FOR_COLLECTION, '"for" 缺少集合表达式', forLoc);
1057
+ }
1058
+ const semicolonToken = this.tokenizer.nextToken();
1059
+ if (!(semicolonToken.type & TokenType.Semicolon)) {
1060
+ this.addError(ParseErrorCode.MISSING_FOR_SEMICOLON, '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.tokenizer.emptyLoc());
1061
+ }
948
1062
  const itemToken = this.tokenizer.nextToken();
949
1063
  const isDestruct = itemToken.type === TokenType.InsertionExp;
950
1064
  if (isDestruct) {
951
1065
  itemToken.value = '{' + itemToken.value + '}';
952
1066
  }
953
1067
  const item = this.parsePropertyValue();
1068
+ if (!item.value && item.value !== 0) {
1069
+ this.addError(ParseErrorCode.MISSING_FOR_ITEM, '"for" 缺少 item 变量名', itemToken.loc ?? this.tokenizer.emptyLoc());
1070
+ }
954
1071
  let char = this.tokenizer.peekChar(),
955
1072
  key,
956
1073
  index;
@@ -1013,6 +1130,8 @@ class Compiler {
1013
1130
  this.tokenizer.nextToken();
1014
1131
  node.value = this.parsePropertyValue();
1015
1132
  this.tokenizer.nextToken();
1133
+ } else {
1134
+ this.addError(ParseErrorCode.MISSING_ASSIGN, `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.tokenizer.emptyLoc());
1016
1135
  }
1017
1136
  node.loc.start = node.key.loc.start;
1018
1137
  node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
@@ -2010,5 +2129,5 @@ function customRender(option) {
2010
2129
  };
2011
2130
  }
2012
2131
 
2013
- export { Compiler, NodeType, Tokenizer, bobe, customRender };
2132
+ export { Compiler, NodeType, ParseSyntaxError, Tokenizer, bobe, customRender };
2014
2133
  //# sourceMappingURL=bobe.esm.js.map