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
package/dist/bobe.cjs.js
CHANGED
|
@@ -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;
|
|
@@ -81,6 +105,25 @@ class Tokenizer {
|
|
|
81
105
|
column: this.column
|
|
82
106
|
};
|
|
83
107
|
}
|
|
108
|
+
unclosedLoc(startOffset, startLine, startCol) {
|
|
109
|
+
const end = this.code.length - 1;
|
|
110
|
+
return {
|
|
111
|
+
start: {
|
|
112
|
+
offset: startOffset,
|
|
113
|
+
line: startLine,
|
|
114
|
+
column: startCol
|
|
115
|
+
},
|
|
116
|
+
end: {
|
|
117
|
+
offset: end,
|
|
118
|
+
line: this.line,
|
|
119
|
+
column: this.column
|
|
120
|
+
},
|
|
121
|
+
source: this.code.slice(startOffset, end)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
throwUnclosed(code, message, startOffset, startLine, startCol) {
|
|
125
|
+
throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
|
|
126
|
+
}
|
|
84
127
|
resume(_snapshot) {
|
|
85
128
|
this.token = undefined;
|
|
86
129
|
this.needIndent = false;
|
|
@@ -134,7 +177,7 @@ class Tokenizer {
|
|
|
134
177
|
const expLen = this.dentStack[i];
|
|
135
178
|
if (currLen === expLen) break;
|
|
136
179
|
if (currLen > expLen) {
|
|
137
|
-
throw
|
|
180
|
+
throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
|
|
138
181
|
}
|
|
139
182
|
if (this.shorterThanBaseDentEof()) {
|
|
140
183
|
break;
|
|
@@ -249,8 +292,7 @@ class Tokenizer {
|
|
|
249
292
|
}
|
|
250
293
|
return this.token;
|
|
251
294
|
} catch (error) {
|
|
252
|
-
|
|
253
|
-
return this.token;
|
|
295
|
+
throw error;
|
|
254
296
|
} finally {
|
|
255
297
|
this.handledTokens.push(this.token);
|
|
256
298
|
}
|
|
@@ -313,6 +355,9 @@ class Tokenizer {
|
|
|
313
355
|
this.setToken(TokenType.Pipe, '|');
|
|
314
356
|
}
|
|
315
357
|
staticIns() {
|
|
358
|
+
const startOffset = this.preI,
|
|
359
|
+
startLine = this.line,
|
|
360
|
+
startCol = this.preCol;
|
|
316
361
|
let nextC = this.code[this.i + 1];
|
|
317
362
|
if (nextC !== '{') {
|
|
318
363
|
return false;
|
|
@@ -322,6 +367,9 @@ class Tokenizer {
|
|
|
322
367
|
let innerBrace = 0;
|
|
323
368
|
while (1) {
|
|
324
369
|
nextC = this.code[this.i + 1];
|
|
370
|
+
if (nextC === undefined) {
|
|
371
|
+
this.throwUnclosed(ParseErrorCode.UNCLOSED_STATIC_INS, '未闭合的 "${...}"', startOffset, startLine, startCol);
|
|
372
|
+
}
|
|
325
373
|
value += nextC;
|
|
326
374
|
this.next();
|
|
327
375
|
if (nextC === '{') {
|
|
@@ -338,6 +386,9 @@ class Tokenizer {
|
|
|
338
386
|
return true;
|
|
339
387
|
}
|
|
340
388
|
brace() {
|
|
389
|
+
const startOffset = this.preI,
|
|
390
|
+
startLine = this.line,
|
|
391
|
+
startCol = this.preCol;
|
|
341
392
|
let inComment,
|
|
342
393
|
inString,
|
|
343
394
|
count = 0,
|
|
@@ -345,6 +396,9 @@ class Tokenizer {
|
|
|
345
396
|
backslashCount = 0;
|
|
346
397
|
while (1) {
|
|
347
398
|
const char = this.code[this.i];
|
|
399
|
+
if (char === undefined) {
|
|
400
|
+
this.throwUnclosed(ParseErrorCode.UNCLOSED_BRACE, '未闭合的 "{"', startOffset, startLine, startCol);
|
|
401
|
+
}
|
|
348
402
|
const nextChar = this.code[this.i + 1];
|
|
349
403
|
if (inComment === 'single' && char === '\n') {
|
|
350
404
|
inComment = null;
|
|
@@ -432,6 +486,18 @@ class Tokenizer {
|
|
|
432
486
|
isEmptyLine
|
|
433
487
|
};
|
|
434
488
|
}
|
|
489
|
+
emptyLoc() {
|
|
490
|
+
const pos = this.getCurrentPos();
|
|
491
|
+
return {
|
|
492
|
+
start: pos,
|
|
493
|
+
end: {
|
|
494
|
+
offset: pos.offset + 1,
|
|
495
|
+
line: pos.line,
|
|
496
|
+
column: pos.column + 1
|
|
497
|
+
},
|
|
498
|
+
source: ' '
|
|
499
|
+
};
|
|
500
|
+
}
|
|
435
501
|
dent() {
|
|
436
502
|
const _this$getDentValue2 = this.getDentValue(),
|
|
437
503
|
value = _this$getDentValue2.value,
|
|
@@ -458,7 +524,7 @@ class Tokenizer {
|
|
|
458
524
|
const expLen = this.dentStack[i];
|
|
459
525
|
if (currLen === expLen) break;
|
|
460
526
|
if (currLen > expLen) {
|
|
461
|
-
throw
|
|
527
|
+
throw new ParseSyntaxError(ParseErrorCode.INCONSISTENT_INDENT, '缩进大小不统一', this.emptyLoc());
|
|
462
528
|
}
|
|
463
529
|
if (this.shorterThanBaseDentEof()) {
|
|
464
530
|
return;
|
|
@@ -527,11 +593,17 @@ class Tokenizer {
|
|
|
527
593
|
this.setToken(TokenType.Identifier, realValue);
|
|
528
594
|
}
|
|
529
595
|
str(char) {
|
|
596
|
+
const startOffset = this.preI,
|
|
597
|
+
startLine = this.line,
|
|
598
|
+
startCol = this.preCol;
|
|
530
599
|
let value = '';
|
|
531
600
|
let nextC;
|
|
532
601
|
let continuousBackslashCount = 0;
|
|
533
602
|
while (1) {
|
|
534
603
|
nextC = this.code[this.i + 1];
|
|
604
|
+
if (nextC === undefined) {
|
|
605
|
+
this.throwUnclosed(ParseErrorCode.UNCLOSED_STRING, '未闭合的字符串字面量', startOffset, startLine, startCol);
|
|
606
|
+
}
|
|
535
607
|
const memoCount = continuousBackslashCount;
|
|
536
608
|
if (nextC === '\\') {
|
|
537
609
|
continuousBackslashCount++;
|
|
@@ -839,18 +911,33 @@ class Compiler {
|
|
|
839
911
|
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);
|
|
840
912
|
_initProto = _applyDecs$e[0];
|
|
841
913
|
}
|
|
914
|
+
errors = (_initProto(this), []);
|
|
842
915
|
constructor(tokenizer, hooks = {}) {
|
|
843
916
|
this.tokenizer = tokenizer;
|
|
844
917
|
this.hooks = hooks;
|
|
845
|
-
|
|
918
|
+
}
|
|
919
|
+
addError(code, message, loc) {
|
|
920
|
+
this.errors.push({
|
|
921
|
+
code,
|
|
922
|
+
message,
|
|
923
|
+
loc
|
|
924
|
+
});
|
|
846
925
|
}
|
|
847
926
|
parseProgram() {
|
|
848
|
-
this.tokenizer.nextToken();
|
|
849
927
|
const body = [];
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
928
|
+
try {
|
|
929
|
+
this.tokenizer.nextToken();
|
|
930
|
+
while (!this.tokenizer.isEof()) {
|
|
931
|
+
const node = this.templateNode(body);
|
|
932
|
+
if (node) {
|
|
933
|
+
body.push(node);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
} catch (error) {
|
|
937
|
+
if (error instanceof ParseSyntaxError) {
|
|
938
|
+
this.addError(error.code, error.message, error.loc);
|
|
939
|
+
} else {
|
|
940
|
+
this.addError(error.toString(), '未知错误', this.tokenizer.emptyLoc());
|
|
854
941
|
}
|
|
855
942
|
}
|
|
856
943
|
return {
|
|
@@ -876,7 +963,7 @@ class Compiler {
|
|
|
876
963
|
if (this.tokenizer.token.type & TokenType.Indent) {
|
|
877
964
|
this.tokenizer.nextToken();
|
|
878
965
|
while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
|
|
879
|
-
const child = this.templateNode();
|
|
966
|
+
const child = this.templateNode(children);
|
|
880
967
|
if (child) {
|
|
881
968
|
children.push(child);
|
|
882
969
|
}
|
|
@@ -887,13 +974,26 @@ class Compiler {
|
|
|
887
974
|
}
|
|
888
975
|
return children;
|
|
889
976
|
}
|
|
890
|
-
templateNode() {
|
|
891
|
-
this.tokenizer.token;
|
|
977
|
+
templateNode(siblings) {
|
|
978
|
+
const token = this.tokenizer.token;
|
|
979
|
+
if (token.type & TokenType.Pipe) {
|
|
980
|
+
this.addError(ParseErrorCode.PIPE_IN_WRONG_CONTEXT, '"|" 只能出现在元素属性扩展行中', token.loc ?? this.tokenizer.emptyLoc());
|
|
981
|
+
this.tokenizer.nextToken();
|
|
982
|
+
return null;
|
|
983
|
+
}
|
|
892
984
|
const _this$tokenizer$_hook = this.tokenizer._hook({}),
|
|
893
985
|
_this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
|
|
894
986
|
hookType = _this$tokenizer$_hook2[0],
|
|
895
987
|
value = _this$tokenizer$_hook2[1];
|
|
896
|
-
|
|
988
|
+
const isElseOrFail = value === 'else' || value === 'fail';
|
|
989
|
+
if (value === 'if' || isElseOrFail) {
|
|
990
|
+
if (isElseOrFail) {
|
|
991
|
+
const lastSibling = siblings[siblings.length - 1];
|
|
992
|
+
const lastType = lastSibling?.type;
|
|
993
|
+
if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
|
|
994
|
+
this.addError(ParseErrorCode.ELSE_WITHOUT_IF, `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.tokenizer.emptyLoc());
|
|
995
|
+
}
|
|
996
|
+
}
|
|
897
997
|
return this.parseConditionalNode();
|
|
898
998
|
}
|
|
899
999
|
if (value === 'for') {
|
|
@@ -918,6 +1018,13 @@ class Compiler {
|
|
|
918
1018
|
}
|
|
919
1019
|
parseElementNode(node) {
|
|
920
1020
|
const tagToken = this.tokenizer.token;
|
|
1021
|
+
if (!(tagToken.type & TokenType.Identifier)) {
|
|
1022
|
+
this.addError(ParseErrorCode.INVALID_TAG_NAME, `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.tokenizer.emptyLoc());
|
|
1023
|
+
while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
|
|
1024
|
+
this.tokenizer.nextToken();
|
|
1025
|
+
}
|
|
1026
|
+
return null;
|
|
1027
|
+
}
|
|
921
1028
|
const tagName = tagToken.value;
|
|
922
1029
|
this.tokenizer.nextToken();
|
|
923
1030
|
const props = this.headerLineAndExtensions();
|
|
@@ -943,15 +1050,25 @@ class Compiler {
|
|
|
943
1050
|
return node;
|
|
944
1051
|
}
|
|
945
1052
|
parseLoopNode(node) {
|
|
1053
|
+
const forLoc = this.tokenizer.token.loc ?? this.tokenizer.emptyLoc();
|
|
946
1054
|
this.tokenizer.nextToken();
|
|
947
1055
|
const collection = this.parsePropertyValue();
|
|
948
|
-
|
|
1056
|
+
if (!collection.value && collection.value !== 0) {
|
|
1057
|
+
this.addError(ParseErrorCode.MISSING_FOR_COLLECTION, '"for" 缺少集合表达式', forLoc);
|
|
1058
|
+
}
|
|
1059
|
+
const semicolonToken = this.tokenizer.nextToken();
|
|
1060
|
+
if (!(semicolonToken.type & TokenType.Semicolon)) {
|
|
1061
|
+
this.addError(ParseErrorCode.MISSING_FOR_SEMICOLON, '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.tokenizer.emptyLoc());
|
|
1062
|
+
}
|
|
949
1063
|
const itemToken = this.tokenizer.nextToken();
|
|
950
1064
|
const isDestruct = itemToken.type === TokenType.InsertionExp;
|
|
951
1065
|
if (isDestruct) {
|
|
952
1066
|
itemToken.value = '{' + itemToken.value + '}';
|
|
953
1067
|
}
|
|
954
1068
|
const item = this.parsePropertyValue();
|
|
1069
|
+
if (!item.value && item.value !== 0) {
|
|
1070
|
+
this.addError(ParseErrorCode.MISSING_FOR_ITEM, '"for" 缺少 item 变量名', itemToken.loc ?? this.tokenizer.emptyLoc());
|
|
1071
|
+
}
|
|
955
1072
|
let char = this.tokenizer.peekChar(),
|
|
956
1073
|
key,
|
|
957
1074
|
index;
|
|
@@ -1014,6 +1131,8 @@ class Compiler {
|
|
|
1014
1131
|
this.tokenizer.nextToken();
|
|
1015
1132
|
node.value = this.parsePropertyValue();
|
|
1016
1133
|
this.tokenizer.nextToken();
|
|
1134
|
+
} else {
|
|
1135
|
+
this.addError(ParseErrorCode.MISSING_ASSIGN, `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.tokenizer.emptyLoc());
|
|
1017
1136
|
}
|
|
1018
1137
|
node.loc.start = node.key.loc.start;
|
|
1019
1138
|
node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
|
|
@@ -2013,6 +2132,7 @@ function customRender(option) {
|
|
|
2013
2132
|
|
|
2014
2133
|
exports.Compiler = Compiler;
|
|
2015
2134
|
exports.NodeType = NodeType;
|
|
2135
|
+
exports.ParseSyntaxError = ParseSyntaxError;
|
|
2016
2136
|
exports.Tokenizer = Tokenizer;
|
|
2017
2137
|
exports.bobe = bobe;
|
|
2018
2138
|
exports.customRender = customRender;
|