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