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