bobe 0.0.35 → 0.0.36

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,13 @@ let NodeSort = function (NodeSort) {
43
43
  TerpEvt["HandledComponentNode"] = "handled-component-node";
44
44
  return TerpEvt;
45
45
  })({});
46
+ class ParseSyntaxError extends SyntaxError {
47
+ constructor(code, message, loc) {
48
+ super(message);
49
+ this.code = code;
50
+ this.loc = loc;
51
+ }
52
+ }
46
53
 
47
54
  class Tokenizer {
48
55
  TabSize = 2;
@@ -81,6 +88,25 @@ class Tokenizer {
81
88
  column: this.column
82
89
  };
83
90
  }
91
+ unclosedLoc(startOffset, startLine, startCol) {
92
+ const end = this.code.length - 1;
93
+ return {
94
+ start: {
95
+ offset: startOffset,
96
+ line: startLine,
97
+ column: startCol
98
+ },
99
+ end: {
100
+ offset: end,
101
+ line: this.line,
102
+ column: this.column
103
+ },
104
+ source: this.code.slice(startOffset, end)
105
+ };
106
+ }
107
+ throwUnclosed(code, message, startOffset, startLine, startCol) {
108
+ throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
109
+ }
84
110
  resume(_snapshot) {
85
111
  this.token = undefined;
86
112
  this.needIndent = false;
@@ -249,8 +275,7 @@ class Tokenizer {
249
275
  }
250
276
  return this.token;
251
277
  } catch (error) {
252
- console.error(error);
253
- return this.token;
278
+ throw error;
254
279
  } finally {
255
280
  this.handledTokens.push(this.token);
256
281
  }
@@ -313,6 +338,9 @@ class Tokenizer {
313
338
  this.setToken(TokenType.Pipe, '|');
314
339
  }
315
340
  staticIns() {
341
+ const startOffset = this.preI,
342
+ startLine = this.line,
343
+ startCol = this.preCol;
316
344
  let nextC = this.code[this.i + 1];
317
345
  if (nextC !== '{') {
318
346
  return false;
@@ -322,6 +350,9 @@ class Tokenizer {
322
350
  let innerBrace = 0;
323
351
  while (1) {
324
352
  nextC = this.code[this.i + 1];
353
+ if (nextC === undefined) {
354
+ this.throwUnclosed('UNCLOSED_STATIC_INS', '未闭合的 "${...}"', startOffset, startLine, startCol);
355
+ }
325
356
  value += nextC;
326
357
  this.next();
327
358
  if (nextC === '{') {
@@ -338,6 +369,9 @@ class Tokenizer {
338
369
  return true;
339
370
  }
340
371
  brace() {
372
+ const startOffset = this.preI,
373
+ startLine = this.line,
374
+ startCol = this.preCol;
341
375
  let inComment,
342
376
  inString,
343
377
  count = 0,
@@ -345,6 +379,9 @@ class Tokenizer {
345
379
  backslashCount = 0;
346
380
  while (1) {
347
381
  const char = this.code[this.i];
382
+ if (char === undefined) {
383
+ this.throwUnclosed('UNCLOSED_BRACE', '未闭合的 "{"', startOffset, startLine, startCol);
384
+ }
348
385
  const nextChar = this.code[this.i + 1];
349
386
  if (inComment === 'single' && char === '\n') {
350
387
  inComment = null;
@@ -527,11 +564,17 @@ class Tokenizer {
527
564
  this.setToken(TokenType.Identifier, realValue);
528
565
  }
529
566
  str(char) {
567
+ const startOffset = this.preI,
568
+ startLine = this.line,
569
+ startCol = this.preCol;
530
570
  let value = '';
531
571
  let nextC;
532
572
  let continuousBackslashCount = 0;
533
573
  while (1) {
534
574
  nextC = this.code[this.i + 1];
575
+ if (nextC === undefined) {
576
+ this.throwUnclosed('UNCLOSED_STRING', '未闭合的字符串字面量', startOffset, startLine, startCol);
577
+ }
535
578
  const memoCount = continuousBackslashCount;
536
579
  if (nextC === '\\') {
537
580
  continuousBackslashCount++;
@@ -839,18 +882,47 @@ class Compiler {
839
882
  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
883
  _initProto = _applyDecs$e[0];
841
884
  }
885
+ errors = (_initProto(this), []);
842
886
  constructor(tokenizer, hooks = {}) {
843
887
  this.tokenizer = tokenizer;
844
888
  this.hooks = hooks;
845
- _initProto(this);
889
+ }
890
+ addError(code, message, loc) {
891
+ this.errors.push({
892
+ code,
893
+ message,
894
+ loc
895
+ });
896
+ }
897
+ emptyLoc() {
898
+ const pos = this.tokenizer.getCurrentPos();
899
+ return {
900
+ start: pos,
901
+ end: {
902
+ offset: pos.offset + 1,
903
+ line: pos.line,
904
+ column: pos.column + 1
905
+ },
906
+ source: ' '
907
+ };
846
908
  }
847
909
  parseProgram() {
848
- this.tokenizer.nextToken();
849
910
  const body = [];
850
- while (!this.tokenizer.isEof()) {
851
- const node = this.templateNode();
852
- if (node) {
853
- body.push(node);
911
+ try {
912
+ this.tokenizer.nextToken();
913
+ while (!this.tokenizer.isEof()) {
914
+ const node = this.templateNode(body);
915
+ if (node) {
916
+ body.push(node);
917
+ }
918
+ }
919
+ } catch (error) {
920
+ if (error instanceof ParseSyntaxError) {
921
+ this.addError(error.code, error.message, error.loc);
922
+ } else if (error instanceof SyntaxError) {
923
+ const knownCodes = ['INCONSISTENT_INDENT', 'INDENT_MISMATCH'];
924
+ const code = knownCodes.includes(error.message) ? error.message : 'INCONSISTENT_INDENT';
925
+ this.addError(code, error.message, this.emptyLoc());
854
926
  }
855
927
  }
856
928
  return {
@@ -876,7 +948,7 @@ class Compiler {
876
948
  if (this.tokenizer.token.type & TokenType.Indent) {
877
949
  this.tokenizer.nextToken();
878
950
  while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
879
- const child = this.templateNode();
951
+ const child = this.templateNode(children);
880
952
  if (child) {
881
953
  children.push(child);
882
954
  }
@@ -887,13 +959,25 @@ class Compiler {
887
959
  }
888
960
  return children;
889
961
  }
890
- templateNode() {
891
- this.tokenizer.token;
962
+ templateNode(siblings) {
963
+ const token = this.tokenizer.token;
964
+ if (token.type & TokenType.Pipe) {
965
+ this.addError('PIPE_IN_WRONG_CONTEXT', '"|" 只能出现在元素属性扩展行中', token.loc ?? this.emptyLoc());
966
+ this.tokenizer.nextToken();
967
+ return null;
968
+ }
892
969
  const _this$tokenizer$_hook = this.tokenizer._hook({}),
893
970
  _this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
894
971
  hookType = _this$tokenizer$_hook2[0],
895
972
  value = _this$tokenizer$_hook2[1];
896
973
  if (value === 'if' || value === 'else' || value === 'fail') {
974
+ if (value === 'else' || value === 'fail') {
975
+ const lastSibling = siblings[siblings.length - 1];
976
+ const lastType = lastSibling?.type;
977
+ if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
978
+ this.addError('ELSE_WITHOUT_IF', `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.emptyLoc());
979
+ }
980
+ }
897
981
  return this.parseConditionalNode();
898
982
  }
899
983
  if (value === 'for') {
@@ -918,6 +1002,13 @@ class Compiler {
918
1002
  }
919
1003
  parseElementNode(node) {
920
1004
  const tagToken = this.tokenizer.token;
1005
+ if (!(tagToken.type & TokenType.Identifier)) {
1006
+ this.addError('INVALID_TAG_NAME', `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.emptyLoc());
1007
+ while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
1008
+ this.tokenizer.nextToken();
1009
+ }
1010
+ return null;
1011
+ }
921
1012
  const tagName = tagToken.value;
922
1013
  this.tokenizer.nextToken();
923
1014
  const props = this.headerLineAndExtensions();
@@ -943,15 +1034,25 @@ class Compiler {
943
1034
  return node;
944
1035
  }
945
1036
  parseLoopNode(node) {
1037
+ const forLoc = this.tokenizer.token.loc ?? this.emptyLoc();
946
1038
  this.tokenizer.nextToken();
947
1039
  const collection = this.parsePropertyValue();
948
- this.tokenizer.nextToken();
1040
+ if (!collection.value && collection.value !== 0) {
1041
+ this.addError('MISSING_FOR_COLLECTION', '"for" 缺少集合表达式', forLoc);
1042
+ }
1043
+ const semicolonToken = this.tokenizer.nextToken();
1044
+ if (!(semicolonToken.type & TokenType.Semicolon)) {
1045
+ this.addError('MISSING_FOR_SEMICOLON', '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.emptyLoc());
1046
+ }
949
1047
  const itemToken = this.tokenizer.nextToken();
950
1048
  const isDestruct = itemToken.type === TokenType.InsertionExp;
951
1049
  if (isDestruct) {
952
1050
  itemToken.value = '{' + itemToken.value + '}';
953
1051
  }
954
1052
  const item = this.parsePropertyValue();
1053
+ if (!item.value && item.value !== 0) {
1054
+ this.addError('MISSING_FOR_ITEM', '"for" 缺少 item 变量名', itemToken.loc ?? this.emptyLoc());
1055
+ }
955
1056
  let char = this.tokenizer.peekChar(),
956
1057
  key,
957
1058
  index;
@@ -1014,6 +1115,8 @@ class Compiler {
1014
1115
  this.tokenizer.nextToken();
1015
1116
  node.value = this.parsePropertyValue();
1016
1117
  this.tokenizer.nextToken();
1118
+ } else {
1119
+ this.addError('MISSING_ASSIGN', `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.emptyLoc());
1017
1120
  }
1018
1121
  node.loc.start = node.key.loc.start;
1019
1122
  node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
@@ -2013,6 +2116,7 @@ function customRender(option) {
2013
2116
 
2014
2117
  exports.Compiler = Compiler;
2015
2118
  exports.NodeType = NodeType;
2119
+ exports.ParseSyntaxError = ParseSyntaxError;
2016
2120
  exports.Tokenizer = Tokenizer;
2017
2121
  exports.bobe = bobe;
2018
2122
  exports.customRender = customRender;