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