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.
@@ -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;
@@ -90,6 +97,25 @@ class Tokenizer {
90
97
  column: this.column
91
98
  };
92
99
  }
100
+ unclosedLoc(startOffset, startLine, startCol) {
101
+ const end = this.code.length - 1;
102
+ return {
103
+ start: {
104
+ offset: startOffset,
105
+ line: startLine,
106
+ column: startCol
107
+ },
108
+ end: {
109
+ offset: end,
110
+ line: this.line,
111
+ column: this.column
112
+ },
113
+ source: this.code.slice(startOffset, end)
114
+ };
115
+ }
116
+ throwUnclosed(code, message, startOffset, startLine, startCol) {
117
+ throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
118
+ }
93
119
  resume(_snapshot) {
94
120
  this.token = undefined;
95
121
  this.needIndent = false;
@@ -276,8 +302,7 @@ class Tokenizer {
276
302
  }
277
303
  return this.token;
278
304
  } catch (error) {
279
- console.error(error);
280
- return this.token;
305
+ throw error;
281
306
  } finally {
282
307
  this.handledTokens.push(this.token);
283
308
  }
@@ -356,6 +381,9 @@ class Tokenizer {
356
381
  this.setToken(TokenType.Pipe, '|');
357
382
  }
358
383
  staticIns() {
384
+ const startOffset = this.preI,
385
+ startLine = this.line,
386
+ startCol = this.preCol;
359
387
  let nextC = this.code[this.i + 1];
360
388
  if (nextC !== '{') {
361
389
  return false;
@@ -365,6 +393,9 @@ class Tokenizer {
365
393
  let innerBrace = 0;
366
394
  while (1) {
367
395
  nextC = this.code[this.i + 1];
396
+ if (nextC === undefined) {
397
+ this.throwUnclosed('UNCLOSED_STATIC_INS', '未闭合的 "${...}"', startOffset, startLine, startCol);
398
+ }
368
399
  value += nextC;
369
400
  this.next();
370
401
  if (nextC === '{') {
@@ -381,6 +412,9 @@ class Tokenizer {
381
412
  return true;
382
413
  }
383
414
  brace() {
415
+ const startOffset = this.preI,
416
+ startLine = this.line,
417
+ startCol = this.preCol;
384
418
  let inComment,
385
419
  inString,
386
420
  count = 0,
@@ -388,6 +422,9 @@ class Tokenizer {
388
422
  backslashCount = 0;
389
423
  while (1) {
390
424
  const char = this.code[this.i];
425
+ if (char === undefined) {
426
+ this.throwUnclosed('UNCLOSED_BRACE', '未闭合的 "{"', startOffset, startLine, startCol);
427
+ }
391
428
  const nextChar = this.code[this.i + 1];
392
429
  if (inComment === 'single' && char === '\n') {
393
430
  inComment = null;
@@ -570,11 +607,17 @@ class Tokenizer {
570
607
  this.setToken(TokenType.Identifier, realValue);
571
608
  }
572
609
  str(char) {
610
+ const startOffset = this.preI,
611
+ startLine = this.line,
612
+ startCol = this.preCol;
573
613
  let value = '';
574
614
  let nextC;
575
615
  let continuousBackslashCount = 0;
576
616
  while (1) {
577
617
  nextC = this.code[this.i + 1];
618
+ if (nextC === undefined) {
619
+ this.throwUnclosed('UNCLOSED_STRING', '未闭合的字符串字面量', startOffset, startLine, startCol);
620
+ }
578
621
  const memoCount = continuousBackslashCount;
579
622
  if (nextC === '\\') {
580
623
  continuousBackslashCount++;
@@ -882,18 +925,47 @@ class Compiler {
882
925
  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
926
  _initProto = _applyDecs$e[0];
884
927
  }
928
+ errors = (_initProto(this), []);
885
929
  constructor(tokenizer, hooks = {}) {
886
930
  this.tokenizer = tokenizer;
887
931
  this.hooks = hooks;
888
- _initProto(this);
932
+ }
933
+ addError(code, message, loc) {
934
+ this.errors.push({
935
+ code,
936
+ message,
937
+ loc
938
+ });
939
+ }
940
+ emptyLoc() {
941
+ const pos = this.tokenizer.getCurrentPos();
942
+ return {
943
+ start: pos,
944
+ end: {
945
+ offset: pos.offset + 1,
946
+ line: pos.line,
947
+ column: pos.column + 1
948
+ },
949
+ source: ' '
950
+ };
889
951
  }
890
952
  parseProgram() {
891
- this.tokenizer.nextToken();
892
953
  const body = [];
893
- while (!this.tokenizer.isEof()) {
894
- const node = this.templateNode();
895
- if (node) {
896
- body.push(node);
954
+ try {
955
+ this.tokenizer.nextToken();
956
+ while (!this.tokenizer.isEof()) {
957
+ const node = this.templateNode(body);
958
+ if (node) {
959
+ body.push(node);
960
+ }
961
+ }
962
+ } catch (error) {
963
+ if (error instanceof ParseSyntaxError) {
964
+ this.addError(error.code, error.message, error.loc);
965
+ } else if (error instanceof SyntaxError) {
966
+ const knownCodes = ['INCONSISTENT_INDENT', 'INDENT_MISMATCH'];
967
+ const code = knownCodes.includes(error.message) ? error.message : 'INCONSISTENT_INDENT';
968
+ this.addError(code, error.message, this.emptyLoc());
897
969
  }
898
970
  }
899
971
  return {
@@ -919,7 +991,7 @@ class Compiler {
919
991
  if (this.tokenizer.token.type & TokenType.Indent) {
920
992
  this.tokenizer.nextToken();
921
993
  while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
922
- const child = this.templateNode();
994
+ const child = this.templateNode(children);
923
995
  if (child) {
924
996
  children.push(child);
925
997
  }
@@ -930,13 +1002,25 @@ class Compiler {
930
1002
  }
931
1003
  return children;
932
1004
  }
933
- templateNode() {
934
- this.tokenizer.token;
1005
+ templateNode(siblings) {
1006
+ const token = this.tokenizer.token;
1007
+ if (token.type & TokenType.Pipe) {
1008
+ this.addError('PIPE_IN_WRONG_CONTEXT', '"|" 只能出现在元素属性扩展行中', token.loc ?? this.emptyLoc());
1009
+ this.tokenizer.nextToken();
1010
+ return null;
1011
+ }
935
1012
  const _this$tokenizer$_hook = this.tokenizer._hook({}),
936
1013
  _this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
937
1014
  hookType = _this$tokenizer$_hook2[0],
938
1015
  value = _this$tokenizer$_hook2[1];
939
1016
  if (value === 'if' || value === 'else' || value === 'fail') {
1017
+ if (value === 'else' || value === 'fail') {
1018
+ const lastSibling = siblings[siblings.length - 1];
1019
+ const lastType = lastSibling?.type;
1020
+ if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
1021
+ this.addError('ELSE_WITHOUT_IF', `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.emptyLoc());
1022
+ }
1023
+ }
940
1024
  return this.parseConditionalNode();
941
1025
  }
942
1026
  if (value === 'for') {
@@ -961,6 +1045,13 @@ class Compiler {
961
1045
  }
962
1046
  parseElementNode(node) {
963
1047
  const tagToken = this.tokenizer.token;
1048
+ if (!(tagToken.type & TokenType.Identifier)) {
1049
+ this.addError('INVALID_TAG_NAME', `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.emptyLoc());
1050
+ while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
1051
+ this.tokenizer.nextToken();
1052
+ }
1053
+ return null;
1054
+ }
964
1055
  const tagName = tagToken.value;
965
1056
  this.tokenizer.nextToken();
966
1057
  const props = this.headerLineAndExtensions();
@@ -986,15 +1077,25 @@ class Compiler {
986
1077
  return node;
987
1078
  }
988
1079
  parseLoopNode(node) {
1080
+ const forLoc = this.tokenizer.token.loc ?? this.emptyLoc();
989
1081
  this.tokenizer.nextToken();
990
1082
  const collection = this.parsePropertyValue();
991
- this.tokenizer.nextToken();
1083
+ if (!collection.value && collection.value !== 0) {
1084
+ this.addError('MISSING_FOR_COLLECTION', '"for" 缺少集合表达式', forLoc);
1085
+ }
1086
+ const semicolonToken = this.tokenizer.nextToken();
1087
+ if (!(semicolonToken.type & TokenType.Semicolon)) {
1088
+ this.addError('MISSING_FOR_SEMICOLON', '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.emptyLoc());
1089
+ }
992
1090
  const itemToken = this.tokenizer.nextToken();
993
1091
  const isDestruct = itemToken.type === TokenType.InsertionExp;
994
1092
  if (isDestruct) {
995
1093
  itemToken.value = '{' + itemToken.value + '}';
996
1094
  }
997
1095
  const item = this.parsePropertyValue();
1096
+ if (!item.value && item.value !== 0) {
1097
+ this.addError('MISSING_FOR_ITEM', '"for" 缺少 item 变量名', itemToken.loc ?? this.emptyLoc());
1098
+ }
998
1099
  let char = this.tokenizer.peekChar(),
999
1100
  key,
1000
1101
  index;
@@ -1057,6 +1158,8 @@ class Compiler {
1057
1158
  this.tokenizer.nextToken();
1058
1159
  node.value = this.parsePropertyValue();
1059
1160
  this.tokenizer.nextToken();
1161
+ } else {
1162
+ this.addError('MISSING_ASSIGN', `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.emptyLoc());
1060
1163
  }
1061
1164
  node.loc.start = node.key.loc.start;
1062
1165
  node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
@@ -2056,6 +2159,7 @@ function customRender(option) {
2056
2159
 
2057
2160
  exports.Compiler = Compiler;
2058
2161
  exports.NodeType = NodeType;
2162
+ exports.ParseSyntaxError = ParseSyntaxError;
2059
2163
  exports.Tokenizer = Tokenizer;
2060
2164
  exports.bobe = bobe;
2061
2165
  exports.customRender = customRender;