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.
@@ -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;
@@ -89,6 +113,25 @@ class Tokenizer {
89
113
  column: this.column
90
114
  };
91
115
  }
116
+ unclosedLoc(startOffset, startLine, startCol) {
117
+ const end = this.code.length - 1;
118
+ return {
119
+ start: {
120
+ offset: startOffset,
121
+ line: startLine,
122
+ column: startCol
123
+ },
124
+ end: {
125
+ offset: end,
126
+ line: this.line,
127
+ column: this.column
128
+ },
129
+ source: this.code.slice(startOffset, end)
130
+ };
131
+ }
132
+ throwUnclosed(code, message, startOffset, startLine, startCol) {
133
+ throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
134
+ }
92
135
  resume(_snapshot) {
93
136
  this.token = undefined;
94
137
  this.needIndent = false;
@@ -142,7 +185,7 @@ class Tokenizer {
142
185
  const expLen = this.dentStack[i];
143
186
  if (currLen === expLen) break;
144
187
  if (currLen > expLen) {
145
- throw SyntaxError(`缩进错误,缩进长度不匹配`);
188
+ throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
146
189
  }
147
190
  if (this.shorterThanBaseDentEof()) {
148
191
  break;
@@ -275,8 +318,7 @@ class Tokenizer {
275
318
  }
276
319
  return this.token;
277
320
  } catch (error) {
278
- console.error(error);
279
- return this.token;
321
+ throw error;
280
322
  } finally {
281
323
  this.handledTokens.push(this.token);
282
324
  }
@@ -355,6 +397,9 @@ class Tokenizer {
355
397
  this.setToken(TokenType.Pipe, '|');
356
398
  }
357
399
  staticIns() {
400
+ const startOffset = this.preI,
401
+ startLine = this.line,
402
+ startCol = this.preCol;
358
403
  let nextC = this.code[this.i + 1];
359
404
  if (nextC !== '{') {
360
405
  return false;
@@ -364,6 +409,9 @@ class Tokenizer {
364
409
  let innerBrace = 0;
365
410
  while (1) {
366
411
  nextC = this.code[this.i + 1];
412
+ if (nextC === undefined) {
413
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_STATIC_INS, '未闭合的 "${...}"', startOffset, startLine, startCol);
414
+ }
367
415
  value += nextC;
368
416
  this.next();
369
417
  if (nextC === '{') {
@@ -380,6 +428,9 @@ class Tokenizer {
380
428
  return true;
381
429
  }
382
430
  brace() {
431
+ const startOffset = this.preI,
432
+ startLine = this.line,
433
+ startCol = this.preCol;
383
434
  let inComment,
384
435
  inString,
385
436
  count = 0,
@@ -387,6 +438,9 @@ class Tokenizer {
387
438
  backslashCount = 0;
388
439
  while (1) {
389
440
  const char = this.code[this.i];
441
+ if (char === undefined) {
442
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_BRACE, '未闭合的 "{"', startOffset, startLine, startCol);
443
+ }
390
444
  const nextChar = this.code[this.i + 1];
391
445
  if (inComment === 'single' && char === '\n') {
392
446
  inComment = null;
@@ -474,6 +528,18 @@ class Tokenizer {
474
528
  isEmptyLine
475
529
  };
476
530
  }
531
+ emptyLoc() {
532
+ const pos = this.getCurrentPos();
533
+ return {
534
+ start: pos,
535
+ end: {
536
+ offset: pos.offset + 1,
537
+ line: pos.line,
538
+ column: pos.column + 1
539
+ },
540
+ source: ' '
541
+ };
542
+ }
477
543
  dent() {
478
544
  const _this$getDentValue2 = this.getDentValue(),
479
545
  value = _this$getDentValue2.value,
@@ -500,7 +566,7 @@ class Tokenizer {
500
566
  const expLen = this.dentStack[i];
501
567
  if (currLen === expLen) break;
502
568
  if (currLen > expLen) {
503
- throw SyntaxError('缩进大小不统一');
569
+ throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
504
570
  }
505
571
  if (this.shorterThanBaseDentEof()) {
506
572
  return;
@@ -569,11 +635,17 @@ class Tokenizer {
569
635
  this.setToken(TokenType.Identifier, realValue);
570
636
  }
571
637
  str(char) {
638
+ const startOffset = this.preI,
639
+ startLine = this.line,
640
+ startCol = this.preCol;
572
641
  let value = '';
573
642
  let nextC;
574
643
  let continuousBackslashCount = 0;
575
644
  while (1) {
576
645
  nextC = this.code[this.i + 1];
646
+ if (nextC === undefined) {
647
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_STRING, '未闭合的字符串字面量', startOffset, startLine, startCol);
648
+ }
577
649
  const memoCount = continuousBackslashCount;
578
650
  if (nextC === '\\') {
579
651
  continuousBackslashCount++;
@@ -881,18 +953,33 @@ class Compiler {
881
953
  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);
882
954
  _initProto = _applyDecs$e[0];
883
955
  }
956
+ errors = (_initProto(this), []);
884
957
  constructor(tokenizer, hooks = {}) {
885
958
  this.tokenizer = tokenizer;
886
959
  this.hooks = hooks;
887
- _initProto(this);
960
+ }
961
+ addError(code, message, loc) {
962
+ this.errors.push({
963
+ code,
964
+ message,
965
+ loc
966
+ });
888
967
  }
889
968
  parseProgram() {
890
- this.tokenizer.nextToken();
891
969
  const body = [];
892
- while (!this.tokenizer.isEof()) {
893
- const node = this.templateNode();
894
- if (node) {
895
- body.push(node);
970
+ try {
971
+ this.tokenizer.nextToken();
972
+ while (!this.tokenizer.isEof()) {
973
+ const node = this.templateNode(body);
974
+ if (node) {
975
+ body.push(node);
976
+ }
977
+ }
978
+ } catch (error) {
979
+ if (error instanceof ParseSyntaxError) {
980
+ this.addError(error.code, error.message, error.loc);
981
+ } else {
982
+ this.addError(error.toString(), '未知错误', this.tokenizer.emptyLoc());
896
983
  }
897
984
  }
898
985
  return {
@@ -918,7 +1005,7 @@ class Compiler {
918
1005
  if (this.tokenizer.token.type & TokenType.Indent) {
919
1006
  this.tokenizer.nextToken();
920
1007
  while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
921
- const child = this.templateNode();
1008
+ const child = this.templateNode(children);
922
1009
  if (child) {
923
1010
  children.push(child);
924
1011
  }
@@ -929,13 +1016,26 @@ class Compiler {
929
1016
  }
930
1017
  return children;
931
1018
  }
932
- templateNode() {
933
- this.tokenizer.token;
1019
+ templateNode(siblings) {
1020
+ const token = this.tokenizer.token;
1021
+ if (token.type & TokenType.Pipe) {
1022
+ this.addError(ParseErrorCode.PIPE_IN_WRONG_CONTEXT, '"|" 只能出现在元素属性扩展行中', token.loc ?? this.tokenizer.emptyLoc());
1023
+ this.tokenizer.nextToken();
1024
+ return null;
1025
+ }
934
1026
  const _this$tokenizer$_hook = this.tokenizer._hook({}),
935
1027
  _this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
936
1028
  hookType = _this$tokenizer$_hook2[0],
937
1029
  value = _this$tokenizer$_hook2[1];
938
- if (value === 'if' || value === 'else' || value === 'fail') {
1030
+ const isElseOrFail = value === 'else' || value === 'fail';
1031
+ if (value === 'if' || isElseOrFail) {
1032
+ if (isElseOrFail) {
1033
+ const lastSibling = siblings[siblings.length - 1];
1034
+ const lastType = lastSibling?.type;
1035
+ if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
1036
+ this.addError(ParseErrorCode.ELSE_WITHOUT_IF, `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.tokenizer.emptyLoc());
1037
+ }
1038
+ }
939
1039
  return this.parseConditionalNode();
940
1040
  }
941
1041
  if (value === 'for') {
@@ -960,6 +1060,13 @@ class Compiler {
960
1060
  }
961
1061
  parseElementNode(node) {
962
1062
  const tagToken = this.tokenizer.token;
1063
+ if (!(tagToken.type & TokenType.Identifier)) {
1064
+ this.addError(ParseErrorCode.INVALID_TAG_NAME, `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.tokenizer.emptyLoc());
1065
+ while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
1066
+ this.tokenizer.nextToken();
1067
+ }
1068
+ return null;
1069
+ }
963
1070
  const tagName = tagToken.value;
964
1071
  this.tokenizer.nextToken();
965
1072
  const props = this.headerLineAndExtensions();
@@ -985,15 +1092,25 @@ class Compiler {
985
1092
  return node;
986
1093
  }
987
1094
  parseLoopNode(node) {
1095
+ const forLoc = this.tokenizer.token.loc ?? this.tokenizer.emptyLoc();
988
1096
  this.tokenizer.nextToken();
989
1097
  const collection = this.parsePropertyValue();
990
- this.tokenizer.nextToken();
1098
+ if (!collection.value && collection.value !== 0) {
1099
+ this.addError(ParseErrorCode.MISSING_FOR_COLLECTION, '"for" 缺少集合表达式', forLoc);
1100
+ }
1101
+ const semicolonToken = this.tokenizer.nextToken();
1102
+ if (!(semicolonToken.type & TokenType.Semicolon)) {
1103
+ this.addError(ParseErrorCode.MISSING_FOR_SEMICOLON, '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.tokenizer.emptyLoc());
1104
+ }
991
1105
  const itemToken = this.tokenizer.nextToken();
992
1106
  const isDestruct = itemToken.type === TokenType.InsertionExp;
993
1107
  if (isDestruct) {
994
1108
  itemToken.value = '{' + itemToken.value + '}';
995
1109
  }
996
1110
  const item = this.parsePropertyValue();
1111
+ if (!item.value && item.value !== 0) {
1112
+ this.addError(ParseErrorCode.MISSING_FOR_ITEM, '"for" 缺少 item 变量名', itemToken.loc ?? this.tokenizer.emptyLoc());
1113
+ }
997
1114
  let char = this.tokenizer.peekChar(),
998
1115
  key,
999
1116
  index;
@@ -1056,6 +1173,8 @@ class Compiler {
1056
1173
  this.tokenizer.nextToken();
1057
1174
  node.value = this.parsePropertyValue();
1058
1175
  this.tokenizer.nextToken();
1176
+ } else {
1177
+ this.addError(ParseErrorCode.MISSING_ASSIGN, `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.tokenizer.emptyLoc());
1059
1178
  }
1060
1179
  node.loc.start = node.key.loc.start;
1061
1180
  node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
@@ -2053,5 +2172,5 @@ function customRender(option) {
2053
2172
  };
2054
2173
  }
2055
2174
 
2056
- export { Compiler, NodeType, Tokenizer, bobe, customRender };
2175
+ export { Compiler, NodeType, ParseSyntaxError, Tokenizer, bobe, customRender };
2057
2176
  //# sourceMappingURL=bobe.compiler.esm.js.map