bobe 0.0.36 → 0.0.38

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
@@ -14,8 +14,15 @@ let TokenType = function (TokenType) {
14
14
  TokenType[TokenType["InsertionExp"] = 128] = "InsertionExp";
15
15
  TokenType[TokenType["Semicolon"] = 256] = "Semicolon";
16
16
  TokenType[TokenType["StaticInsExp"] = 512] = "StaticInsExp";
17
+ TokenType[TokenType["String"] = 1024] = "String";
18
+ TokenType[TokenType["Number"] = 2048] = "Number";
19
+ TokenType[TokenType["Boolean"] = 4096] = "Boolean";
20
+ TokenType[TokenType["Null"] = 8192] = "Null";
21
+ TokenType[TokenType["Undefined"] = 16384] = "Undefined";
17
22
  return TokenType;
18
23
  }({});
24
+ const BaseTokenType = TokenType.String | TokenType.Number | TokenType.Boolean | TokenType.Null | TokenType.Undefined;
25
+ const ValueTokenType = BaseTokenType | TokenType.InsertionExp | TokenType.StaticInsExp;
19
26
  let FakeType = function (FakeType) {
20
27
  FakeType[FakeType["If"] = 1] = "If";
21
28
  FakeType[FakeType["Fail"] = 2] = "Fail";
@@ -43,6 +50,25 @@ let NodeSort = function (NodeSort) {
43
50
  TerpEvt["HandledComponentNode"] = "handled-component-node";
44
51
  return TerpEvt;
45
52
  })({});
53
+ let ParseErrorCode = function (ParseErrorCode) {
54
+ ParseErrorCode[ParseErrorCode["UNCLOSED_BRACE"] = 9001] = "UNCLOSED_BRACE";
55
+ ParseErrorCode[ParseErrorCode["UNCLOSED_STRING"] = 9002] = "UNCLOSED_STRING";
56
+ ParseErrorCode[ParseErrorCode["UNCLOSED_STATIC_INS"] = 9003] = "UNCLOSED_STATIC_INS";
57
+ ParseErrorCode[ParseErrorCode["INCONSISTENT_INDENT"] = 9004] = "INCONSISTENT_INDENT";
58
+ ParseErrorCode[ParseErrorCode["INDENT_MISMATCH"] = 9005] = "INDENT_MISMATCH";
59
+ ParseErrorCode[ParseErrorCode["MISSING_ASSIGN"] = 9006] = "MISSING_ASSIGN";
60
+ ParseErrorCode[ParseErrorCode["INVALID_TAG_NAME"] = 9007] = "INVALID_TAG_NAME";
61
+ ParseErrorCode[ParseErrorCode["INVALID_PROP_KEY"] = 9008] = "INVALID_PROP_KEY";
62
+ ParseErrorCode[ParseErrorCode["ELSE_WITHOUT_IF"] = 9009] = "ELSE_WITHOUT_IF";
63
+ ParseErrorCode[ParseErrorCode["EMPTY_IF_BODY"] = 9010] = "EMPTY_IF_BODY";
64
+ ParseErrorCode[ParseErrorCode["EMPTY_FOR_BODY"] = 9011] = "EMPTY_FOR_BODY";
65
+ ParseErrorCode[ParseErrorCode["MISSING_FOR_COLLECTION"] = 9012] = "MISSING_FOR_COLLECTION";
66
+ ParseErrorCode[ParseErrorCode["MISSING_FOR_SEMICOLON"] = 9013] = "MISSING_FOR_SEMICOLON";
67
+ ParseErrorCode[ParseErrorCode["MISSING_FOR_ITEM"] = 9014] = "MISSING_FOR_ITEM";
68
+ ParseErrorCode[ParseErrorCode["MISSING_PROP_ASSIGNMENT"] = 9015] = "MISSING_PROP_ASSIGNMENT";
69
+ ParseErrorCode[ParseErrorCode["PIPE_IN_WRONG_CONTEXT"] = 9016] = "PIPE_IN_WRONG_CONTEXT";
70
+ return ParseErrorCode;
71
+ }({});
46
72
  class ParseSyntaxError extends SyntaxError {
47
73
  constructor(code, message, loc) {
48
74
  super(message);
@@ -160,7 +186,7 @@ class Tokenizer {
160
186
  const expLen = this.dentStack[i];
161
187
  if (currLen === expLen) break;
162
188
  if (currLen > expLen) {
163
- throw SyntaxError(`缩进错误,缩进长度不匹配`);
189
+ throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
164
190
  }
165
191
  if (this.shorterThanBaseDentEof()) {
166
192
  break;
@@ -351,7 +377,7 @@ class Tokenizer {
351
377
  while (1) {
352
378
  nextC = this.code[this.i + 1];
353
379
  if (nextC === undefined) {
354
- this.throwUnclosed('UNCLOSED_STATIC_INS', '未闭合的 "${...}"', startOffset, startLine, startCol);
380
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_STATIC_INS, '未闭合的 "${...}"', startOffset, startLine, startCol);
355
381
  }
356
382
  value += nextC;
357
383
  this.next();
@@ -380,7 +406,7 @@ class Tokenizer {
380
406
  while (1) {
381
407
  const char = this.code[this.i];
382
408
  if (char === undefined) {
383
- this.throwUnclosed('UNCLOSED_BRACE', '未闭合的 "{"', startOffset, startLine, startCol);
409
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_BRACE, '未闭合的 "{"', startOffset, startLine, startCol);
384
410
  }
385
411
  const nextChar = this.code[this.i + 1];
386
412
  if (inComment === 'single' && char === '\n') {
@@ -469,6 +495,18 @@ class Tokenizer {
469
495
  isEmptyLine
470
496
  };
471
497
  }
498
+ emptyLoc() {
499
+ const pos = this.getCurrentPos();
500
+ return {
501
+ start: pos,
502
+ end: {
503
+ offset: pos.offset + 1,
504
+ line: pos.line,
505
+ column: pos.column + 1
506
+ },
507
+ source: ' '
508
+ };
509
+ }
472
510
  dent() {
473
511
  const _this$getDentValue2 = this.getDentValue(),
474
512
  value = _this$getDentValue2.value,
@@ -495,7 +533,7 @@ class Tokenizer {
495
533
  const expLen = this.dentStack[i];
496
534
  if (currLen === expLen) break;
497
535
  if (currLen > expLen) {
498
- throw SyntaxError('缩进大小不统一');
536
+ throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
499
537
  }
500
538
  if (this.shorterThanBaseDentEof()) {
501
539
  return;
@@ -560,8 +598,30 @@ class Tokenizer {
560
598
  this.setToken(TokenType.Dedent, '');
561
599
  return;
562
600
  }
563
- let realValue = value === 'null' ? null : value === 'undefined' ? undefined : value === 'false' ? false : value === 'true' ? true : value;
564
- this.setToken(TokenType.Identifier, realValue);
601
+ let realValue, tokenType;
602
+ switch (value) {
603
+ case 'null':
604
+ realValue = null;
605
+ tokenType = TokenType.Null;
606
+ break;
607
+ case 'undefined':
608
+ realValue = undefined;
609
+ tokenType = TokenType.Undefined;
610
+ break;
611
+ case 'false':
612
+ realValue = false;
613
+ tokenType = TokenType.Boolean;
614
+ break;
615
+ case 'true':
616
+ realValue = true;
617
+ tokenType = TokenType.Boolean;
618
+ break;
619
+ default:
620
+ realValue = value;
621
+ tokenType = TokenType.Identifier;
622
+ break;
623
+ }
624
+ this.setToken(tokenType, realValue);
565
625
  }
566
626
  str(char) {
567
627
  const startOffset = this.preI,
@@ -573,7 +633,7 @@ class Tokenizer {
573
633
  while (1) {
574
634
  nextC = this.code[this.i + 1];
575
635
  if (nextC === undefined) {
576
- this.throwUnclosed('UNCLOSED_STRING', '未闭合的字符串字面量', startOffset, startLine, startCol);
636
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_STRING, '未闭合的字符串字面量', startOffset, startLine, startCol);
577
637
  }
578
638
  const memoCount = continuousBackslashCount;
579
639
  if (nextC === '\\') {
@@ -587,7 +647,7 @@ class Tokenizer {
587
647
  }
588
648
  value += nextC;
589
649
  }
590
- this.setToken(TokenType.Identifier, value);
650
+ this.setToken(TokenType.String, value);
591
651
  }
592
652
  number(char) {
593
653
  let value = char;
@@ -600,7 +660,7 @@ class Tokenizer {
600
660
  value += nextC;
601
661
  this.next();
602
662
  }
603
- this.setToken(TokenType.Identifier, Number(value));
663
+ this.setToken(TokenType.Number, Number(value));
604
664
  }
605
665
  eof() {
606
666
  this.setToken(TokenType.Eof, 'End Of File');
@@ -879,7 +939,7 @@ var NodeType = function (NodeType) {
879
939
  let _initProto;
880
940
  class Compiler {
881
941
  static {
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);
942
+ 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, "parseJsExp"], [[NodeHook, TokenLoc], 2, "parsePropertyValue"], [[NodeHook, TokenLoc], 2, "parseName"]]).e, 1);
883
943
  _initProto = _applyDecs$e[0];
884
944
  }
885
945
  errors = (_initProto(this), []);
@@ -887,25 +947,16 @@ class Compiler {
887
947
  this.tokenizer = tokenizer;
888
948
  this.hooks = hooks;
889
949
  }
890
- addError(code, message, loc) {
950
+ addError(code, message, loc, node) {
951
+ if (node) {
952
+ node.hasError = true;
953
+ }
891
954
  this.errors.push({
892
955
  code,
893
956
  message,
894
957
  loc
895
958
  });
896
959
  }
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
- };
908
- }
909
960
  parseProgram() {
910
961
  const body = [];
911
962
  try {
@@ -919,10 +970,8 @@ class Compiler {
919
970
  } catch (error) {
920
971
  if (error instanceof ParseSyntaxError) {
921
972
  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());
973
+ } else {
974
+ this.addError(error.toString(), '未知错误', this.tokenizer.emptyLoc());
926
975
  }
927
976
  }
928
977
  return {
@@ -962,7 +1011,7 @@ class Compiler {
962
1011
  templateNode(siblings) {
963
1012
  const token = this.tokenizer.token;
964
1013
  if (token.type & TokenType.Pipe) {
965
- this.addError('PIPE_IN_WRONG_CONTEXT', '"|" 只能出现在元素属性扩展行中', token.loc ?? this.emptyLoc());
1014
+ this.addError(ParseErrorCode.PIPE_IN_WRONG_CONTEXT, '"|" 只能出现在元素属性扩展行中', token.loc ?? this.tokenizer.emptyLoc());
966
1015
  this.tokenizer.nextToken();
967
1016
  return null;
968
1017
  }
@@ -970,12 +1019,13 @@ class Compiler {
970
1019
  _this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
971
1020
  hookType = _this$tokenizer$_hook2[0],
972
1021
  value = _this$tokenizer$_hook2[1];
973
- if (value === 'if' || value === 'else' || value === 'fail') {
974
- if (value === 'else' || value === 'fail') {
1022
+ const isElseOrFail = value === 'else' || value === 'fail';
1023
+ if (value === 'if' || isElseOrFail) {
1024
+ if (isElseOrFail) {
975
1025
  const lastSibling = siblings[siblings.length - 1];
976
1026
  const lastType = lastSibling?.type;
977
1027
  if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
978
- this.addError('ELSE_WITHOUT_IF', `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.emptyLoc());
1028
+ this.addError(ParseErrorCode.ELSE_WITHOUT_IF, `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.tokenizer.emptyLoc());
979
1029
  }
980
1030
  }
981
1031
  return this.parseConditionalNode();
@@ -1003,7 +1053,7 @@ class Compiler {
1003
1053
  parseElementNode(node) {
1004
1054
  const tagToken = this.tokenizer.token;
1005
1055
  if (!(tagToken.type & TokenType.Identifier)) {
1006
- this.addError('INVALID_TAG_NAME', `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.emptyLoc());
1056
+ this.addError(ParseErrorCode.INVALID_TAG_NAME, `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.tokenizer.emptyLoc(), node);
1007
1057
  while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
1008
1058
  this.tokenizer.nextToken();
1009
1059
  }
@@ -1023,7 +1073,7 @@ class Compiler {
1023
1073
  parseConditionalNode(node) {
1024
1074
  const keyword = this.tokenizer.token.value;
1025
1075
  this.tokenizer.condExp();
1026
- const condition = this.parsePropertyValue();
1076
+ const condition = this.parseJsExp();
1027
1077
  this.tokenizer.nextToken();
1028
1078
  this.tokenizer.nextToken();
1029
1079
  node.type = keyword === 'if' ? NodeType.If : keyword === 'else' ? NodeType.Else : NodeType.Fail;
@@ -1034,24 +1084,24 @@ class Compiler {
1034
1084
  return node;
1035
1085
  }
1036
1086
  parseLoopNode(node) {
1037
- const forLoc = this.tokenizer.token.loc ?? this.emptyLoc();
1087
+ const forLoc = this.tokenizer.token.loc ?? this.tokenizer.emptyLoc();
1038
1088
  this.tokenizer.nextToken();
1039
- const collection = this.parsePropertyValue();
1089
+ const collection = this.parseJsExp();
1040
1090
  if (!collection.value && collection.value !== 0) {
1041
- this.addError('MISSING_FOR_COLLECTION', '"for" 缺少集合表达式', forLoc);
1091
+ this.addError(ParseErrorCode.MISSING_FOR_COLLECTION, '"for" 缺少集合表达式', forLoc, node);
1042
1092
  }
1043
1093
  const semicolonToken = this.tokenizer.nextToken();
1044
1094
  if (!(semicolonToken.type & TokenType.Semicolon)) {
1045
- this.addError('MISSING_FOR_SEMICOLON', '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.emptyLoc());
1095
+ this.addError(ParseErrorCode.MISSING_FOR_SEMICOLON, '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.tokenizer.emptyLoc(), node);
1046
1096
  }
1047
1097
  const itemToken = this.tokenizer.nextToken();
1048
1098
  const isDestruct = itemToken.type === TokenType.InsertionExp;
1049
1099
  if (isDestruct) {
1050
1100
  itemToken.value = '{' + itemToken.value + '}';
1051
1101
  }
1052
- const item = this.parsePropertyValue();
1102
+ const item = this.parseJsExp();
1053
1103
  if (!item.value && item.value !== 0) {
1054
- this.addError('MISSING_FOR_ITEM', '"for" 缺少 item 变量名', itemToken.loc ?? this.emptyLoc());
1104
+ this.addError(ParseErrorCode.MISSING_FOR_ITEM, '"for" 缺少 item 变量名', itemToken.loc ?? this.tokenizer.emptyLoc(), node);
1055
1105
  }
1056
1106
  let char = this.tokenizer.peekChar(),
1057
1107
  key,
@@ -1060,16 +1110,16 @@ class Compiler {
1060
1110
  this.tokenizer.nextToken();
1061
1111
  if (this.tokenizer.peekChar() !== '\n') {
1062
1112
  this.tokenizer.jsExp();
1063
- key = this.parsePropertyValue();
1113
+ key = this.parseJsExp();
1064
1114
  }
1065
1115
  } else if (char === '\n') ; else {
1066
1116
  this.tokenizer.nextToken();
1067
- index = this.parsePropertyValue();
1117
+ index = this.parseJsExp();
1068
1118
  if (this.tokenizer.peekChar() === ';') {
1069
1119
  this.tokenizer.nextToken();
1070
1120
  if (this.tokenizer.peekChar() !== '\n') {
1071
1121
  this.tokenizer.jsExp();
1072
- key = this.parsePropertyValue();
1122
+ key = this.parseJsExp();
1073
1123
  }
1074
1124
  }
1075
1125
  }
@@ -1103,23 +1153,41 @@ class Compiler {
1103
1153
  attributeList() {
1104
1154
  const props = [];
1105
1155
  while (!(this.tokenizer.token.type & TokenType.NewLine) && !(this.tokenizer.token.type & TokenType.Pipe) && !this.tokenizer.isEof()) {
1106
- props.push(this.parseProperty());
1156
+ const prop = this.parseProperty();
1157
+ if (prop) {
1158
+ props.push(prop);
1159
+ }
1107
1160
  }
1108
1161
  return props;
1109
1162
  }
1110
1163
  parseProperty(node) {
1111
1164
  node.type = NodeType.Property;
1112
- node.key = this.parsePropertyKey();
1113
- const token = this.tokenizer.nextToken();
1114
- if (token.value === '=') {
1115
- this.tokenizer.nextToken();
1116
- node.value = this.parsePropertyValue();
1165
+ if (this.tokenizer.token.type !== TokenType.Identifier) {
1166
+ this.addError(ParseErrorCode.INVALID_PROP_KEY, `属性名 "${this.tokenizer.token.value}" 不合法`, this.tokenizer.token.loc ?? this.tokenizer.emptyLoc(), node);
1117
1167
  this.tokenizer.nextToken();
1118
- } else {
1119
- this.addError('MISSING_ASSIGN', `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.emptyLoc());
1168
+ return null;
1120
1169
  }
1170
+ node.key = this.parsePropertyKey();
1171
+ const token = this.tokenizer.nextToken();
1172
+ if (token.value !== '=') {
1173
+ this.addError(ParseErrorCode.MISSING_ASSIGN, `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.tokenizer.emptyLoc(), node);
1174
+ node.loc.start = node.key.loc.start;
1175
+ node.loc.end = node.key.loc.end;
1176
+ node.loc.source = this.tokenizer.code.slice(node.loc.start.offset, node.loc.end.offset);
1177
+ return node;
1178
+ }
1179
+ const valueToken = this.tokenizer.nextToken();
1180
+ if ((valueToken.type & ValueTokenType) === 0) {
1181
+ this.addError(ParseErrorCode.MISSING_PROP_ASSIGNMENT, `属性值不合法, "${valueToken.value}" 不合法`, valueToken.loc ?? this.tokenizer.emptyLoc(), node);
1182
+ node.loc.start = node.key.loc.start;
1183
+ node.loc.end = node.key.loc.end;
1184
+ node.loc.source = this.tokenizer.code.slice(node.loc.start.offset, node.loc.end.offset);
1185
+ return node;
1186
+ }
1187
+ node.value = this.parsePropertyValue();
1188
+ this.tokenizer.nextToken();
1121
1189
  node.loc.start = node.key.loc.start;
1122
- node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
1190
+ node.loc.end = node.value.loc.end;
1123
1191
  node.loc.source = this.tokenizer.code.slice(node.loc.start.offset, node.loc.end.offset);
1124
1192
  return node;
1125
1193
  }
@@ -1128,7 +1196,7 @@ class Compiler {
1128
1196
  node.key = this.tokenizer.token.value;
1129
1197
  return node;
1130
1198
  }
1131
- parsePropertyValue(node) {
1199
+ parseJsExp(node) {
1132
1200
  const _this$tokenizer$_hook3 = this.tokenizer._hook({}),
1133
1201
  _this$tokenizer$_hook4 = _slicedToArray(_this$tokenizer$_hook3, 2),
1134
1202
  hookType = _this$tokenizer$_hook4[0],
@@ -1137,7 +1205,7 @@ class Compiler {
1137
1205
  node.value = value;
1138
1206
  return node;
1139
1207
  }
1140
- parseName(node) {
1208
+ parsePropertyValue(node) {
1141
1209
  const _this$tokenizer$_hook5 = this.tokenizer._hook({}),
1142
1210
  _this$tokenizer$_hook6 = _slicedToArray(_this$tokenizer$_hook5, 2),
1143
1211
  hookType = _this$tokenizer$_hook6[0],
@@ -1146,6 +1214,15 @@ class Compiler {
1146
1214
  node.value = value;
1147
1215
  return node;
1148
1216
  }
1217
+ parseName(node) {
1218
+ const _this$tokenizer$_hook7 = this.tokenizer._hook({}),
1219
+ _this$tokenizer$_hook8 = _slicedToArray(_this$tokenizer$_hook7, 2),
1220
+ hookType = _this$tokenizer$_hook8[0],
1221
+ value = _this$tokenizer$_hook8[1];
1222
+ node.type = hookType === 'dynamic' ? NodeType.DynamicValue : NodeType.StaticValue;
1223
+ node.value = value;
1224
+ return node;
1225
+ }
1149
1226
  }
1150
1227
  function NodeLoc(target, context) {
1151
1228
  return function (_node) {