bobe 0.0.35 → 0.0.37
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 +135 -15
- package/dist/bobe.cjs.js.map +1 -1
- package/dist/bobe.compiler.cjs.js +135 -15
- package/dist/bobe.compiler.cjs.js.map +1 -1
- package/dist/bobe.compiler.esm.js +135 -16
- package/dist/bobe.compiler.esm.js.map +1 -1
- package/dist/bobe.esm.js +135 -16
- package/dist/bobe.esm.js.map +1 -1
- package/dist/index.d.ts +35 -2
- package/dist/index.umd.js +135 -15
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -3
|
@@ -42,6 +42,30 @@ let NodeSort = function (NodeSort) {
|
|
|
42
42
|
TerpEvt["HandledComponentNode"] = "handled-component-node";
|
|
43
43
|
return TerpEvt;
|
|
44
44
|
})({});
|
|
45
|
+
let ParseErrorCode = function (ParseErrorCode) {
|
|
46
|
+
ParseErrorCode[ParseErrorCode["UNCLOSED_BRACE"] = 9001] = "UNCLOSED_BRACE";
|
|
47
|
+
ParseErrorCode[ParseErrorCode["UNCLOSED_STRING"] = 9002] = "UNCLOSED_STRING";
|
|
48
|
+
ParseErrorCode[ParseErrorCode["UNCLOSED_STATIC_INS"] = 9003] = "UNCLOSED_STATIC_INS";
|
|
49
|
+
ParseErrorCode[ParseErrorCode["INCONSISTENT_INDENT"] = 9004] = "INCONSISTENT_INDENT";
|
|
50
|
+
ParseErrorCode[ParseErrorCode["INDENT_MISMATCH"] = 9005] = "INDENT_MISMATCH";
|
|
51
|
+
ParseErrorCode[ParseErrorCode["MISSING_ASSIGN"] = 9006] = "MISSING_ASSIGN";
|
|
52
|
+
ParseErrorCode[ParseErrorCode["INVALID_TAG_NAME"] = 9007] = "INVALID_TAG_NAME";
|
|
53
|
+
ParseErrorCode[ParseErrorCode["ELSE_WITHOUT_IF"] = 9008] = "ELSE_WITHOUT_IF";
|
|
54
|
+
ParseErrorCode[ParseErrorCode["EMPTY_IF_BODY"] = 9009] = "EMPTY_IF_BODY";
|
|
55
|
+
ParseErrorCode[ParseErrorCode["EMPTY_FOR_BODY"] = 9010] = "EMPTY_FOR_BODY";
|
|
56
|
+
ParseErrorCode[ParseErrorCode["MISSING_FOR_COLLECTION"] = 9011] = "MISSING_FOR_COLLECTION";
|
|
57
|
+
ParseErrorCode[ParseErrorCode["MISSING_FOR_SEMICOLON"] = 9012] = "MISSING_FOR_SEMICOLON";
|
|
58
|
+
ParseErrorCode[ParseErrorCode["MISSING_FOR_ITEM"] = 9013] = "MISSING_FOR_ITEM";
|
|
59
|
+
ParseErrorCode[ParseErrorCode["PIPE_IN_WRONG_CONTEXT"] = 9014] = "PIPE_IN_WRONG_CONTEXT";
|
|
60
|
+
return ParseErrorCode;
|
|
61
|
+
}({});
|
|
62
|
+
class ParseSyntaxError extends SyntaxError {
|
|
63
|
+
constructor(code, message, loc) {
|
|
64
|
+
super(message);
|
|
65
|
+
this.code = code;
|
|
66
|
+
this.loc = loc;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
45
69
|
|
|
46
70
|
class Tokenizer {
|
|
47
71
|
TabSize = 2;
|
|
@@ -89,6 +113,25 @@ class Tokenizer {
|
|
|
89
113
|
column: this.column
|
|
90
114
|
};
|
|
91
115
|
}
|
|
116
|
+
unclosedLoc(startOffset, startLine, startCol) {
|
|
117
|
+
const end = this.code.length - 1;
|
|
118
|
+
return {
|
|
119
|
+
start: {
|
|
120
|
+
offset: startOffset,
|
|
121
|
+
line: startLine,
|
|
122
|
+
column: startCol
|
|
123
|
+
},
|
|
124
|
+
end: {
|
|
125
|
+
offset: end,
|
|
126
|
+
line: this.line,
|
|
127
|
+
column: this.column
|
|
128
|
+
},
|
|
129
|
+
source: this.code.slice(startOffset, end)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
throwUnclosed(code, message, startOffset, startLine, startCol) {
|
|
133
|
+
throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
|
|
134
|
+
}
|
|
92
135
|
resume(_snapshot) {
|
|
93
136
|
this.token = undefined;
|
|
94
137
|
this.needIndent = false;
|
|
@@ -142,7 +185,7 @@ class Tokenizer {
|
|
|
142
185
|
const expLen = this.dentStack[i];
|
|
143
186
|
if (currLen === expLen) break;
|
|
144
187
|
if (currLen > expLen) {
|
|
145
|
-
throw
|
|
188
|
+
throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
|
|
146
189
|
}
|
|
147
190
|
if (this.shorterThanBaseDentEof()) {
|
|
148
191
|
break;
|
|
@@ -275,8 +318,7 @@ class Tokenizer {
|
|
|
275
318
|
}
|
|
276
319
|
return this.token;
|
|
277
320
|
} catch (error) {
|
|
278
|
-
|
|
279
|
-
return this.token;
|
|
321
|
+
throw error;
|
|
280
322
|
} finally {
|
|
281
323
|
this.handledTokens.push(this.token);
|
|
282
324
|
}
|
|
@@ -355,6 +397,9 @@ class Tokenizer {
|
|
|
355
397
|
this.setToken(TokenType.Pipe, '|');
|
|
356
398
|
}
|
|
357
399
|
staticIns() {
|
|
400
|
+
const startOffset = this.preI,
|
|
401
|
+
startLine = this.line,
|
|
402
|
+
startCol = this.preCol;
|
|
358
403
|
let nextC = this.code[this.i + 1];
|
|
359
404
|
if (nextC !== '{') {
|
|
360
405
|
return false;
|
|
@@ -364,6 +409,9 @@ class Tokenizer {
|
|
|
364
409
|
let innerBrace = 0;
|
|
365
410
|
while (1) {
|
|
366
411
|
nextC = this.code[this.i + 1];
|
|
412
|
+
if (nextC === undefined) {
|
|
413
|
+
this.throwUnclosed(ParseErrorCode.UNCLOSED_STATIC_INS, '未闭合的 "${...}"', startOffset, startLine, startCol);
|
|
414
|
+
}
|
|
367
415
|
value += nextC;
|
|
368
416
|
this.next();
|
|
369
417
|
if (nextC === '{') {
|
|
@@ -380,6 +428,9 @@ class Tokenizer {
|
|
|
380
428
|
return true;
|
|
381
429
|
}
|
|
382
430
|
brace() {
|
|
431
|
+
const startOffset = this.preI,
|
|
432
|
+
startLine = this.line,
|
|
433
|
+
startCol = this.preCol;
|
|
383
434
|
let inComment,
|
|
384
435
|
inString,
|
|
385
436
|
count = 0,
|
|
@@ -387,6 +438,9 @@ class Tokenizer {
|
|
|
387
438
|
backslashCount = 0;
|
|
388
439
|
while (1) {
|
|
389
440
|
const char = this.code[this.i];
|
|
441
|
+
if (char === undefined) {
|
|
442
|
+
this.throwUnclosed(ParseErrorCode.UNCLOSED_BRACE, '未闭合的 "{"', startOffset, startLine, startCol);
|
|
443
|
+
}
|
|
390
444
|
const nextChar = this.code[this.i + 1];
|
|
391
445
|
if (inComment === 'single' && char === '\n') {
|
|
392
446
|
inComment = null;
|
|
@@ -474,6 +528,18 @@ class Tokenizer {
|
|
|
474
528
|
isEmptyLine
|
|
475
529
|
};
|
|
476
530
|
}
|
|
531
|
+
emptyLoc() {
|
|
532
|
+
const pos = this.getCurrentPos();
|
|
533
|
+
return {
|
|
534
|
+
start: pos,
|
|
535
|
+
end: {
|
|
536
|
+
offset: pos.offset + 1,
|
|
537
|
+
line: pos.line,
|
|
538
|
+
column: pos.column + 1
|
|
539
|
+
},
|
|
540
|
+
source: ' '
|
|
541
|
+
};
|
|
542
|
+
}
|
|
477
543
|
dent() {
|
|
478
544
|
const _this$getDentValue2 = this.getDentValue(),
|
|
479
545
|
value = _this$getDentValue2.value,
|
|
@@ -500,7 +566,7 @@ class Tokenizer {
|
|
|
500
566
|
const expLen = this.dentStack[i];
|
|
501
567
|
if (currLen === expLen) break;
|
|
502
568
|
if (currLen > expLen) {
|
|
503
|
-
throw
|
|
569
|
+
throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
|
|
504
570
|
}
|
|
505
571
|
if (this.shorterThanBaseDentEof()) {
|
|
506
572
|
return;
|
|
@@ -569,11 +635,17 @@ class Tokenizer {
|
|
|
569
635
|
this.setToken(TokenType.Identifier, realValue);
|
|
570
636
|
}
|
|
571
637
|
str(char) {
|
|
638
|
+
const startOffset = this.preI,
|
|
639
|
+
startLine = this.line,
|
|
640
|
+
startCol = this.preCol;
|
|
572
641
|
let value = '';
|
|
573
642
|
let nextC;
|
|
574
643
|
let continuousBackslashCount = 0;
|
|
575
644
|
while (1) {
|
|
576
645
|
nextC = this.code[this.i + 1];
|
|
646
|
+
if (nextC === undefined) {
|
|
647
|
+
this.throwUnclosed(ParseErrorCode.UNCLOSED_STRING, '未闭合的字符串字面量', startOffset, startLine, startCol);
|
|
648
|
+
}
|
|
577
649
|
const memoCount = continuousBackslashCount;
|
|
578
650
|
if (nextC === '\\') {
|
|
579
651
|
continuousBackslashCount++;
|
|
@@ -881,18 +953,33 @@ class Compiler {
|
|
|
881
953
|
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
954
|
_initProto = _applyDecs$e[0];
|
|
883
955
|
}
|
|
956
|
+
errors = (_initProto(this), []);
|
|
884
957
|
constructor(tokenizer, hooks = {}) {
|
|
885
958
|
this.tokenizer = tokenizer;
|
|
886
959
|
this.hooks = hooks;
|
|
887
|
-
|
|
960
|
+
}
|
|
961
|
+
addError(code, message, loc) {
|
|
962
|
+
this.errors.push({
|
|
963
|
+
code,
|
|
964
|
+
message,
|
|
965
|
+
loc
|
|
966
|
+
});
|
|
888
967
|
}
|
|
889
968
|
parseProgram() {
|
|
890
|
-
this.tokenizer.nextToken();
|
|
891
969
|
const body = [];
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
970
|
+
try {
|
|
971
|
+
this.tokenizer.nextToken();
|
|
972
|
+
while (!this.tokenizer.isEof()) {
|
|
973
|
+
const node = this.templateNode(body);
|
|
974
|
+
if (node) {
|
|
975
|
+
body.push(node);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
} catch (error) {
|
|
979
|
+
if (error instanceof ParseSyntaxError) {
|
|
980
|
+
this.addError(error.code, error.message, error.loc);
|
|
981
|
+
} else {
|
|
982
|
+
this.addError(error.toString(), '未知错误', this.tokenizer.emptyLoc());
|
|
896
983
|
}
|
|
897
984
|
}
|
|
898
985
|
return {
|
|
@@ -918,7 +1005,7 @@ class Compiler {
|
|
|
918
1005
|
if (this.tokenizer.token.type & TokenType.Indent) {
|
|
919
1006
|
this.tokenizer.nextToken();
|
|
920
1007
|
while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
|
|
921
|
-
const child = this.templateNode();
|
|
1008
|
+
const child = this.templateNode(children);
|
|
922
1009
|
if (child) {
|
|
923
1010
|
children.push(child);
|
|
924
1011
|
}
|
|
@@ -929,13 +1016,26 @@ class Compiler {
|
|
|
929
1016
|
}
|
|
930
1017
|
return children;
|
|
931
1018
|
}
|
|
932
|
-
templateNode() {
|
|
933
|
-
this.tokenizer.token;
|
|
1019
|
+
templateNode(siblings) {
|
|
1020
|
+
const token = this.tokenizer.token;
|
|
1021
|
+
if (token.type & TokenType.Pipe) {
|
|
1022
|
+
this.addError(ParseErrorCode.PIPE_IN_WRONG_CONTEXT, '"|" 只能出现在元素属性扩展行中', token.loc ?? this.tokenizer.emptyLoc());
|
|
1023
|
+
this.tokenizer.nextToken();
|
|
1024
|
+
return null;
|
|
1025
|
+
}
|
|
934
1026
|
const _this$tokenizer$_hook = this.tokenizer._hook({}),
|
|
935
1027
|
_this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
|
|
936
1028
|
hookType = _this$tokenizer$_hook2[0],
|
|
937
1029
|
value = _this$tokenizer$_hook2[1];
|
|
938
|
-
|
|
1030
|
+
const isElseOrFail = value === 'else' || value === 'fail';
|
|
1031
|
+
if (value === 'if' || isElseOrFail) {
|
|
1032
|
+
if (isElseOrFail) {
|
|
1033
|
+
const lastSibling = siblings[siblings.length - 1];
|
|
1034
|
+
const lastType = lastSibling?.type;
|
|
1035
|
+
if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
|
|
1036
|
+
this.addError(ParseErrorCode.ELSE_WITHOUT_IF, `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.tokenizer.emptyLoc());
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
939
1039
|
return this.parseConditionalNode();
|
|
940
1040
|
}
|
|
941
1041
|
if (value === 'for') {
|
|
@@ -960,6 +1060,13 @@ class Compiler {
|
|
|
960
1060
|
}
|
|
961
1061
|
parseElementNode(node) {
|
|
962
1062
|
const tagToken = this.tokenizer.token;
|
|
1063
|
+
if (!(tagToken.type & TokenType.Identifier)) {
|
|
1064
|
+
this.addError(ParseErrorCode.INVALID_TAG_NAME, `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.tokenizer.emptyLoc());
|
|
1065
|
+
while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
|
|
1066
|
+
this.tokenizer.nextToken();
|
|
1067
|
+
}
|
|
1068
|
+
return null;
|
|
1069
|
+
}
|
|
963
1070
|
const tagName = tagToken.value;
|
|
964
1071
|
this.tokenizer.nextToken();
|
|
965
1072
|
const props = this.headerLineAndExtensions();
|
|
@@ -985,15 +1092,25 @@ class Compiler {
|
|
|
985
1092
|
return node;
|
|
986
1093
|
}
|
|
987
1094
|
parseLoopNode(node) {
|
|
1095
|
+
const forLoc = this.tokenizer.token.loc ?? this.tokenizer.emptyLoc();
|
|
988
1096
|
this.tokenizer.nextToken();
|
|
989
1097
|
const collection = this.parsePropertyValue();
|
|
990
|
-
|
|
1098
|
+
if (!collection.value && collection.value !== 0) {
|
|
1099
|
+
this.addError(ParseErrorCode.MISSING_FOR_COLLECTION, '"for" 缺少集合表达式', forLoc);
|
|
1100
|
+
}
|
|
1101
|
+
const semicolonToken = this.tokenizer.nextToken();
|
|
1102
|
+
if (!(semicolonToken.type & TokenType.Semicolon)) {
|
|
1103
|
+
this.addError(ParseErrorCode.MISSING_FOR_SEMICOLON, '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.tokenizer.emptyLoc());
|
|
1104
|
+
}
|
|
991
1105
|
const itemToken = this.tokenizer.nextToken();
|
|
992
1106
|
const isDestruct = itemToken.type === TokenType.InsertionExp;
|
|
993
1107
|
if (isDestruct) {
|
|
994
1108
|
itemToken.value = '{' + itemToken.value + '}';
|
|
995
1109
|
}
|
|
996
1110
|
const item = this.parsePropertyValue();
|
|
1111
|
+
if (!item.value && item.value !== 0) {
|
|
1112
|
+
this.addError(ParseErrorCode.MISSING_FOR_ITEM, '"for" 缺少 item 变量名', itemToken.loc ?? this.tokenizer.emptyLoc());
|
|
1113
|
+
}
|
|
997
1114
|
let char = this.tokenizer.peekChar(),
|
|
998
1115
|
key,
|
|
999
1116
|
index;
|
|
@@ -1056,6 +1173,8 @@ class Compiler {
|
|
|
1056
1173
|
this.tokenizer.nextToken();
|
|
1057
1174
|
node.value = this.parsePropertyValue();
|
|
1058
1175
|
this.tokenizer.nextToken();
|
|
1176
|
+
} else {
|
|
1177
|
+
this.addError(ParseErrorCode.MISSING_ASSIGN, `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.tokenizer.emptyLoc());
|
|
1059
1178
|
}
|
|
1060
1179
|
node.loc.start = node.key.loc.start;
|
|
1061
1180
|
node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
|
|
@@ -2053,5 +2172,5 @@ function customRender(option) {
|
|
|
2053
2172
|
};
|
|
2054
2173
|
}
|
|
2055
2174
|
|
|
2056
|
-
export { Compiler, NodeType, Tokenizer, bobe, customRender };
|
|
2175
|
+
export { Compiler, NodeType, ParseSyntaxError, Tokenizer, bobe, customRender };
|
|
2057
2176
|
//# sourceMappingURL=bobe.compiler.esm.js.map
|