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