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/index.d.ts CHANGED
@@ -133,7 +133,12 @@ declare enum TokenType {
133
133
  InsertionExp = 128,
134
134
  Semicolon = 256,
135
135
  /** 仅编译时可解析 */
136
- StaticInsExp = 512
136
+ StaticInsExp = 512,
137
+ String = 1024,
138
+ Number = 2048,
139
+ Boolean = 4096,
140
+ Null = 8192,
141
+ Undefined = 16384
137
142
  }
138
143
  declare enum FakeType {
139
144
  If = 1,
@@ -174,7 +179,24 @@ type TerpConf = Partial<Pick<Interpreter, 'createNode' | 'setProp' | 'insertAfte
174
179
  type CustomRenderConf = Pick<TerpConf, 'createNode' | 'setProp' | 'insertAfter' | 'remove' | 'createAnchor' | 'firstChild' | 'nextSib'>;
175
180
  type Hook = (props: HookProps) => any;
176
181
  type HookType = 'dynamic' | 'static';
177
- type ParseErrorCode = 'UNCLOSED_BRACE' | 'UNCLOSED_STRING' | 'UNCLOSED_STATIC_INS' | 'INCONSISTENT_INDENT' | 'INDENT_MISMATCH' | 'MISSING_ASSIGN' | 'INVALID_TAG_NAME' | 'ELSE_WITHOUT_IF' | 'EMPTY_IF_BODY' | 'EMPTY_FOR_BODY' | 'MISSING_FOR_COLLECTION' | 'MISSING_FOR_SEMICOLON' | 'MISSING_FOR_ITEM' | 'PIPE_IN_WRONG_CONTEXT';
182
+ declare enum ParseErrorCode {
183
+ UNCLOSED_BRACE = 9001,
184
+ UNCLOSED_STRING = 9002,
185
+ UNCLOSED_STATIC_INS = 9003,
186
+ INCONSISTENT_INDENT = 9004,
187
+ INDENT_MISMATCH = 9005,
188
+ MISSING_ASSIGN = 9006,
189
+ INVALID_TAG_NAME = 9007,
190
+ INVALID_PROP_KEY = 9008,
191
+ ELSE_WITHOUT_IF = 9009,
192
+ EMPTY_IF_BODY = 9010,
193
+ EMPTY_FOR_BODY = 9011,
194
+ MISSING_FOR_COLLECTION = 9012,
195
+ MISSING_FOR_SEMICOLON = 9013,
196
+ MISSING_FOR_ITEM = 9014,
197
+ MISSING_PROP_ASSIGNMENT = 9015,
198
+ PIPE_IN_WRONG_CONTEXT = 9016
199
+ }
178
200
  type ParseError = {
179
201
  code: ParseErrorCode;
180
202
  message: string;
@@ -310,6 +332,7 @@ declare class Tokenizer {
310
332
  private brace;
311
333
  private newLine;
312
334
  private getDentValue;
335
+ emptyLoc(): SourceLocation;
313
336
  private dent;
314
337
  private shorterThanBaseDentEof;
315
338
  private identifier;
@@ -344,6 +367,7 @@ type ASTNodeType = NodeType;
344
367
  interface BaseNode {
345
368
  type: ASTNodeType;
346
369
  loc?: SourceLocation;
370
+ hasError?: boolean;
347
371
  }
348
372
  interface Program extends BaseNode {
349
373
  type: NodeType.Program;
@@ -413,7 +437,6 @@ declare class Compiler {
413
437
  errors: ParseError[];
414
438
  constructor(tokenizer: Tokenizer, hooks?: ParseHooks);
415
439
  private addError;
416
- private emptyLoc;
417
440
  /**
418
441
  * 编译程序入口,生成AST
419
442
  */
@@ -452,6 +475,10 @@ declare class Compiler {
452
475
  * 根据值类型创建属性 key 节点
453
476
  */
454
477
  parsePropertyKey(node?: PropertyKeyNode): PropertyKeyNode;
478
+ /**
479
+ * 根据值类型创建属性值节点
480
+ */
481
+ parseJsExp(node?: PropertyValue): PropertyValue;
455
482
  /**
456
483
  * 根据值类型创建属性值节点
457
484
  */
@@ -476,5 +503,5 @@ type ParseHooks = Partial<{
476
503
  declare function bobe(fragments: TemplateStringsArray, ...values: any[]): BobeUI;
477
504
  declare function customRender(option: CustomRenderConf): <T>(Ctor: typeof Store, root: any) => (ComponentNode$1 | Store)[];
478
505
 
479
- export { Compiler, NodeType, ParseSyntaxError, Tokenizer, bobe, customRender };
480
- export type { ASTNodeType, BaseNode, ComponentNode, ConditionalNode, DynamicValue, ElementNode, FragmentNode, InterpolationNode, LoopNode, ParseError, ParseErrorCode, Program, Property, PropertyKeyNode, PropertyValue, SourceLocation, StaticValue, TemplateNode, TextNode };
506
+ export { Compiler, NodeType, ParseErrorCode, ParseSyntaxError, Tokenizer, bobe, customRender };
507
+ export type { ASTNodeType, BaseNode, ComponentNode, ConditionalNode, DynamicValue, ElementNode, FragmentNode, InterpolationNode, LoopNode, ParseError, Program, Property, PropertyKeyNode, PropertyValue, SourceLocation, StaticValue, TemplateNode, TextNode };
package/dist/index.umd.js CHANGED
@@ -15,8 +15,15 @@
15
15
  TokenType[TokenType["InsertionExp"] = 128] = "InsertionExp";
16
16
  TokenType[TokenType["Semicolon"] = 256] = "Semicolon";
17
17
  TokenType[TokenType["StaticInsExp"] = 512] = "StaticInsExp";
18
+ TokenType[TokenType["String"] = 1024] = "String";
19
+ TokenType[TokenType["Number"] = 2048] = "Number";
20
+ TokenType[TokenType["Boolean"] = 4096] = "Boolean";
21
+ TokenType[TokenType["Null"] = 8192] = "Null";
22
+ TokenType[TokenType["Undefined"] = 16384] = "Undefined";
18
23
  return TokenType;
19
24
  }({});
25
+ const BaseTokenType = TokenType.String | TokenType.Number | TokenType.Boolean | TokenType.Null | TokenType.Undefined;
26
+ const ValueTokenType = BaseTokenType | TokenType.InsertionExp | TokenType.StaticInsExp;
20
27
  let FakeType = function (FakeType) {
21
28
  FakeType[FakeType["If"] = 1] = "If";
22
29
  FakeType[FakeType["Fail"] = 2] = "Fail";
@@ -44,6 +51,25 @@
44
51
  TerpEvt["HandledComponentNode"] = "handled-component-node";
45
52
  return TerpEvt;
46
53
  })({});
54
+ let ParseErrorCode = function (ParseErrorCode) {
55
+ ParseErrorCode[ParseErrorCode["UNCLOSED_BRACE"] = 9001] = "UNCLOSED_BRACE";
56
+ ParseErrorCode[ParseErrorCode["UNCLOSED_STRING"] = 9002] = "UNCLOSED_STRING";
57
+ ParseErrorCode[ParseErrorCode["UNCLOSED_STATIC_INS"] = 9003] = "UNCLOSED_STATIC_INS";
58
+ ParseErrorCode[ParseErrorCode["INCONSISTENT_INDENT"] = 9004] = "INCONSISTENT_INDENT";
59
+ ParseErrorCode[ParseErrorCode["INDENT_MISMATCH"] = 9005] = "INDENT_MISMATCH";
60
+ ParseErrorCode[ParseErrorCode["MISSING_ASSIGN"] = 9006] = "MISSING_ASSIGN";
61
+ ParseErrorCode[ParseErrorCode["INVALID_TAG_NAME"] = 9007] = "INVALID_TAG_NAME";
62
+ ParseErrorCode[ParseErrorCode["INVALID_PROP_KEY"] = 9008] = "INVALID_PROP_KEY";
63
+ ParseErrorCode[ParseErrorCode["ELSE_WITHOUT_IF"] = 9009] = "ELSE_WITHOUT_IF";
64
+ ParseErrorCode[ParseErrorCode["EMPTY_IF_BODY"] = 9010] = "EMPTY_IF_BODY";
65
+ ParseErrorCode[ParseErrorCode["EMPTY_FOR_BODY"] = 9011] = "EMPTY_FOR_BODY";
66
+ ParseErrorCode[ParseErrorCode["MISSING_FOR_COLLECTION"] = 9012] = "MISSING_FOR_COLLECTION";
67
+ ParseErrorCode[ParseErrorCode["MISSING_FOR_SEMICOLON"] = 9013] = "MISSING_FOR_SEMICOLON";
68
+ ParseErrorCode[ParseErrorCode["MISSING_FOR_ITEM"] = 9014] = "MISSING_FOR_ITEM";
69
+ ParseErrorCode[ParseErrorCode["MISSING_PROP_ASSIGNMENT"] = 9015] = "MISSING_PROP_ASSIGNMENT";
70
+ ParseErrorCode[ParseErrorCode["PIPE_IN_WRONG_CONTEXT"] = 9016] = "PIPE_IN_WRONG_CONTEXT";
71
+ return ParseErrorCode;
72
+ }({});
47
73
  class ParseSyntaxError extends SyntaxError {
48
74
  constructor(code, message, loc) {
49
75
  super(message);
@@ -161,7 +187,7 @@
161
187
  const expLen = this.dentStack[i];
162
188
  if (currLen === expLen) break;
163
189
  if (currLen > expLen) {
164
- throw SyntaxError(`缩进错误,缩进长度不匹配`);
190
+ throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
165
191
  }
166
192
  if (this.shorterThanBaseDentEof()) {
167
193
  break;
@@ -352,7 +378,7 @@
352
378
  while (1) {
353
379
  nextC = this.code[this.i + 1];
354
380
  if (nextC === undefined) {
355
- this.throwUnclosed('UNCLOSED_STATIC_INS', '未闭合的 "${...}"', startOffset, startLine, startCol);
381
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_STATIC_INS, '未闭合的 "${...}"', startOffset, startLine, startCol);
356
382
  }
357
383
  value += nextC;
358
384
  this.next();
@@ -381,7 +407,7 @@
381
407
  while (1) {
382
408
  const char = this.code[this.i];
383
409
  if (char === undefined) {
384
- this.throwUnclosed('UNCLOSED_BRACE', '未闭合的 "{"', startOffset, startLine, startCol);
410
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_BRACE, '未闭合的 "{"', startOffset, startLine, startCol);
385
411
  }
386
412
  const nextChar = this.code[this.i + 1];
387
413
  if (inComment === 'single' && char === '\n') {
@@ -470,6 +496,18 @@
470
496
  isEmptyLine
471
497
  };
472
498
  }
499
+ emptyLoc() {
500
+ const pos = this.getCurrentPos();
501
+ return {
502
+ start: pos,
503
+ end: {
504
+ offset: pos.offset + 1,
505
+ line: pos.line,
506
+ column: pos.column + 1
507
+ },
508
+ source: ' '
509
+ };
510
+ }
473
511
  dent() {
474
512
  const _this$getDentValue2 = this.getDentValue(),
475
513
  value = _this$getDentValue2.value,
@@ -496,7 +534,7 @@
496
534
  const expLen = this.dentStack[i];
497
535
  if (currLen === expLen) break;
498
536
  if (currLen > expLen) {
499
- throw SyntaxError('缩进大小不统一');
537
+ throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
500
538
  }
501
539
  if (this.shorterThanBaseDentEof()) {
502
540
  return;
@@ -561,8 +599,30 @@
561
599
  this.setToken(TokenType.Dedent, '');
562
600
  return;
563
601
  }
564
- let realValue = value === 'null' ? null : value === 'undefined' ? undefined : value === 'false' ? false : value === 'true' ? true : value;
565
- this.setToken(TokenType.Identifier, realValue);
602
+ let realValue, tokenType;
603
+ switch (value) {
604
+ case 'null':
605
+ realValue = null;
606
+ tokenType = TokenType.Null;
607
+ break;
608
+ case 'undefined':
609
+ realValue = undefined;
610
+ tokenType = TokenType.Undefined;
611
+ break;
612
+ case 'false':
613
+ realValue = false;
614
+ tokenType = TokenType.Boolean;
615
+ break;
616
+ case 'true':
617
+ realValue = true;
618
+ tokenType = TokenType.Boolean;
619
+ break;
620
+ default:
621
+ realValue = value;
622
+ tokenType = TokenType.Identifier;
623
+ break;
624
+ }
625
+ this.setToken(tokenType, realValue);
566
626
  }
567
627
  str(char) {
568
628
  const startOffset = this.preI,
@@ -574,7 +634,7 @@
574
634
  while (1) {
575
635
  nextC = this.code[this.i + 1];
576
636
  if (nextC === undefined) {
577
- this.throwUnclosed('UNCLOSED_STRING', '未闭合的字符串字面量', startOffset, startLine, startCol);
637
+ this.throwUnclosed(ParseErrorCode.UNCLOSED_STRING, '未闭合的字符串字面量', startOffset, startLine, startCol);
578
638
  }
579
639
  const memoCount = continuousBackslashCount;
580
640
  if (nextC === '\\') {
@@ -588,7 +648,7 @@
588
648
  }
589
649
  value += nextC;
590
650
  }
591
- this.setToken(TokenType.Identifier, value);
651
+ this.setToken(TokenType.String, value);
592
652
  }
593
653
  number(char) {
594
654
  let value = char;
@@ -601,7 +661,7 @@
601
661
  value += nextC;
602
662
  this.next();
603
663
  }
604
- this.setToken(TokenType.Identifier, Number(value));
664
+ this.setToken(TokenType.Number, Number(value));
605
665
  }
606
666
  eof() {
607
667
  this.setToken(TokenType.Eof, 'End Of File');
@@ -880,7 +940,7 @@
880
940
  let _initProto;
881
941
  class Compiler {
882
942
  static {
883
- 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);
943
+ 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);
884
944
  _initProto = _applyDecs$e[0];
885
945
  }
886
946
  errors = (_initProto(this), []);
@@ -888,25 +948,16 @@
888
948
  this.tokenizer = tokenizer;
889
949
  this.hooks = hooks;
890
950
  }
891
- addError(code, message, loc) {
951
+ addError(code, message, loc, node) {
952
+ if (node) {
953
+ node.hasError = true;
954
+ }
892
955
  this.errors.push({
893
956
  code,
894
957
  message,
895
958
  loc
896
959
  });
897
960
  }
898
- emptyLoc() {
899
- const pos = this.tokenizer.getCurrentPos();
900
- return {
901
- start: pos,
902
- end: {
903
- offset: pos.offset + 1,
904
- line: pos.line,
905
- column: pos.column + 1
906
- },
907
- source: ' '
908
- };
909
- }
910
961
  parseProgram() {
911
962
  const body = [];
912
963
  try {
@@ -920,10 +971,8 @@
920
971
  } catch (error) {
921
972
  if (error instanceof ParseSyntaxError) {
922
973
  this.addError(error.code, error.message, error.loc);
923
- } else if (error instanceof SyntaxError) {
924
- const knownCodes = ['INCONSISTENT_INDENT', 'INDENT_MISMATCH'];
925
- const code = knownCodes.includes(error.message) ? error.message : 'INCONSISTENT_INDENT';
926
- this.addError(code, error.message, this.emptyLoc());
974
+ } else {
975
+ this.addError(error.toString(), '未知错误', this.tokenizer.emptyLoc());
927
976
  }
928
977
  }
929
978
  return {
@@ -963,7 +1012,7 @@
963
1012
  templateNode(siblings) {
964
1013
  const token = this.tokenizer.token;
965
1014
  if (token.type & TokenType.Pipe) {
966
- this.addError('PIPE_IN_WRONG_CONTEXT', '"|" 只能出现在元素属性扩展行中', token.loc ?? this.emptyLoc());
1015
+ this.addError(ParseErrorCode.PIPE_IN_WRONG_CONTEXT, '"|" 只能出现在元素属性扩展行中', token.loc ?? this.tokenizer.emptyLoc());
967
1016
  this.tokenizer.nextToken();
968
1017
  return null;
969
1018
  }
@@ -971,12 +1020,13 @@
971
1020
  _this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
972
1021
  hookType = _this$tokenizer$_hook2[0],
973
1022
  value = _this$tokenizer$_hook2[1];
974
- if (value === 'if' || value === 'else' || value === 'fail') {
975
- if (value === 'else' || value === 'fail') {
1023
+ const isElseOrFail = value === 'else' || value === 'fail';
1024
+ if (value === 'if' || isElseOrFail) {
1025
+ if (isElseOrFail) {
976
1026
  const lastSibling = siblings[siblings.length - 1];
977
1027
  const lastType = lastSibling?.type;
978
1028
  if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
979
- this.addError('ELSE_WITHOUT_IF', `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.emptyLoc());
1029
+ this.addError(ParseErrorCode.ELSE_WITHOUT_IF, `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.tokenizer.emptyLoc());
980
1030
  }
981
1031
  }
982
1032
  return this.parseConditionalNode();
@@ -1004,7 +1054,7 @@
1004
1054
  parseElementNode(node) {
1005
1055
  const tagToken = this.tokenizer.token;
1006
1056
  if (!(tagToken.type & TokenType.Identifier)) {
1007
- this.addError('INVALID_TAG_NAME', `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.emptyLoc());
1057
+ this.addError(ParseErrorCode.INVALID_TAG_NAME, `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.tokenizer.emptyLoc(), node);
1008
1058
  while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
1009
1059
  this.tokenizer.nextToken();
1010
1060
  }
@@ -1024,7 +1074,7 @@
1024
1074
  parseConditionalNode(node) {
1025
1075
  const keyword = this.tokenizer.token.value;
1026
1076
  this.tokenizer.condExp();
1027
- const condition = this.parsePropertyValue();
1077
+ const condition = this.parseJsExp();
1028
1078
  this.tokenizer.nextToken();
1029
1079
  this.tokenizer.nextToken();
1030
1080
  node.type = keyword === 'if' ? NodeType.If : keyword === 'else' ? NodeType.Else : NodeType.Fail;
@@ -1035,24 +1085,24 @@
1035
1085
  return node;
1036
1086
  }
1037
1087
  parseLoopNode(node) {
1038
- const forLoc = this.tokenizer.token.loc ?? this.emptyLoc();
1088
+ const forLoc = this.tokenizer.token.loc ?? this.tokenizer.emptyLoc();
1039
1089
  this.tokenizer.nextToken();
1040
- const collection = this.parsePropertyValue();
1090
+ const collection = this.parseJsExp();
1041
1091
  if (!collection.value && collection.value !== 0) {
1042
- this.addError('MISSING_FOR_COLLECTION', '"for" 缺少集合表达式', forLoc);
1092
+ this.addError(ParseErrorCode.MISSING_FOR_COLLECTION, '"for" 缺少集合表达式', forLoc, node);
1043
1093
  }
1044
1094
  const semicolonToken = this.tokenizer.nextToken();
1045
1095
  if (!(semicolonToken.type & TokenType.Semicolon)) {
1046
- this.addError('MISSING_FOR_SEMICOLON', '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.emptyLoc());
1096
+ this.addError(ParseErrorCode.MISSING_FOR_SEMICOLON, '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.tokenizer.emptyLoc(), node);
1047
1097
  }
1048
1098
  const itemToken = this.tokenizer.nextToken();
1049
1099
  const isDestruct = itemToken.type === TokenType.InsertionExp;
1050
1100
  if (isDestruct) {
1051
1101
  itemToken.value = '{' + itemToken.value + '}';
1052
1102
  }
1053
- const item = this.parsePropertyValue();
1103
+ const item = this.parseJsExp();
1054
1104
  if (!item.value && item.value !== 0) {
1055
- this.addError('MISSING_FOR_ITEM', '"for" 缺少 item 变量名', itemToken.loc ?? this.emptyLoc());
1105
+ this.addError(ParseErrorCode.MISSING_FOR_ITEM, '"for" 缺少 item 变量名', itemToken.loc ?? this.tokenizer.emptyLoc(), node);
1056
1106
  }
1057
1107
  let char = this.tokenizer.peekChar(),
1058
1108
  key,
@@ -1061,16 +1111,16 @@
1061
1111
  this.tokenizer.nextToken();
1062
1112
  if (this.tokenizer.peekChar() !== '\n') {
1063
1113
  this.tokenizer.jsExp();
1064
- key = this.parsePropertyValue();
1114
+ key = this.parseJsExp();
1065
1115
  }
1066
1116
  } else if (char === '\n') ; else {
1067
1117
  this.tokenizer.nextToken();
1068
- index = this.parsePropertyValue();
1118
+ index = this.parseJsExp();
1069
1119
  if (this.tokenizer.peekChar() === ';') {
1070
1120
  this.tokenizer.nextToken();
1071
1121
  if (this.tokenizer.peekChar() !== '\n') {
1072
1122
  this.tokenizer.jsExp();
1073
- key = this.parsePropertyValue();
1123
+ key = this.parseJsExp();
1074
1124
  }
1075
1125
  }
1076
1126
  }
@@ -1104,23 +1154,41 @@
1104
1154
  attributeList() {
1105
1155
  const props = [];
1106
1156
  while (!(this.tokenizer.token.type & TokenType.NewLine) && !(this.tokenizer.token.type & TokenType.Pipe) && !this.tokenizer.isEof()) {
1107
- props.push(this.parseProperty());
1157
+ const prop = this.parseProperty();
1158
+ if (prop) {
1159
+ props.push(prop);
1160
+ }
1108
1161
  }
1109
1162
  return props;
1110
1163
  }
1111
1164
  parseProperty(node) {
1112
1165
  node.type = NodeType.Property;
1113
- node.key = this.parsePropertyKey();
1114
- const token = this.tokenizer.nextToken();
1115
- if (token.value === '=') {
1116
- this.tokenizer.nextToken();
1117
- node.value = this.parsePropertyValue();
1166
+ if (this.tokenizer.token.type !== TokenType.Identifier) {
1167
+ this.addError(ParseErrorCode.INVALID_PROP_KEY, `属性名 "${this.tokenizer.token.value}" 不合法`, this.tokenizer.token.loc ?? this.tokenizer.emptyLoc(), node);
1118
1168
  this.tokenizer.nextToken();
1119
- } else {
1120
- this.addError('MISSING_ASSIGN', `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.emptyLoc());
1169
+ return null;
1121
1170
  }
1171
+ node.key = this.parsePropertyKey();
1172
+ const token = this.tokenizer.nextToken();
1173
+ if (token.value !== '=') {
1174
+ this.addError(ParseErrorCode.MISSING_ASSIGN, `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.tokenizer.emptyLoc(), node);
1175
+ node.loc.start = node.key.loc.start;
1176
+ node.loc.end = node.key.loc.end;
1177
+ node.loc.source = this.tokenizer.code.slice(node.loc.start.offset, node.loc.end.offset);
1178
+ return node;
1179
+ }
1180
+ const valueToken = this.tokenizer.nextToken();
1181
+ if ((valueToken.type & ValueTokenType) === 0) {
1182
+ this.addError(ParseErrorCode.MISSING_PROP_ASSIGNMENT, `属性值不合法, "${valueToken.value}" 不合法`, valueToken.loc ?? this.tokenizer.emptyLoc(), node);
1183
+ node.loc.start = node.key.loc.start;
1184
+ node.loc.end = node.key.loc.end;
1185
+ node.loc.source = this.tokenizer.code.slice(node.loc.start.offset, node.loc.end.offset);
1186
+ return node;
1187
+ }
1188
+ node.value = this.parsePropertyValue();
1189
+ this.tokenizer.nextToken();
1122
1190
  node.loc.start = node.key.loc.start;
1123
- node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
1191
+ node.loc.end = node.value.loc.end;
1124
1192
  node.loc.source = this.tokenizer.code.slice(node.loc.start.offset, node.loc.end.offset);
1125
1193
  return node;
1126
1194
  }
@@ -1129,7 +1197,7 @@
1129
1197
  node.key = this.tokenizer.token.value;
1130
1198
  return node;
1131
1199
  }
1132
- parsePropertyValue(node) {
1200
+ parseJsExp(node) {
1133
1201
  const _this$tokenizer$_hook3 = this.tokenizer._hook({}),
1134
1202
  _this$tokenizer$_hook4 = _slicedToArray(_this$tokenizer$_hook3, 2),
1135
1203
  hookType = _this$tokenizer$_hook4[0],
@@ -1138,7 +1206,7 @@
1138
1206
  node.value = value;
1139
1207
  return node;
1140
1208
  }
1141
- parseName(node) {
1209
+ parsePropertyValue(node) {
1142
1210
  const _this$tokenizer$_hook5 = this.tokenizer._hook({}),
1143
1211
  _this$tokenizer$_hook6 = _slicedToArray(_this$tokenizer$_hook5, 2),
1144
1212
  hookType = _this$tokenizer$_hook6[0],
@@ -1147,6 +1215,15 @@
1147
1215
  node.value = value;
1148
1216
  return node;
1149
1217
  }
1218
+ parseName(node) {
1219
+ const _this$tokenizer$_hook7 = this.tokenizer._hook({}),
1220
+ _this$tokenizer$_hook8 = _slicedToArray(_this$tokenizer$_hook7, 2),
1221
+ hookType = _this$tokenizer$_hook8[0],
1222
+ value = _this$tokenizer$_hook8[1];
1223
+ node.type = hookType === 'dynamic' ? NodeType.DynamicValue : NodeType.StaticValue;
1224
+ node.value = value;
1225
+ return node;
1226
+ }
1150
1227
  }
1151
1228
  function NodeLoc(target, context) {
1152
1229
  return function (_node) {