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