bobe 0.0.35 → 0.0.36
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 +116 -12
- package/dist/bobe.cjs.js.map +1 -1
- package/dist/bobe.compiler.cjs.js +116 -12
- package/dist/bobe.compiler.cjs.js.map +1 -1
- package/dist/bobe.compiler.esm.js +116 -13
- package/dist/bobe.compiler.esm.js.map +1 -1
- package/dist/bobe.esm.js +116 -13
- package/dist/bobe.esm.js.map +1 -1
- package/dist/index.d.ts +20 -2
- package/dist/index.umd.js +116 -12
- package/dist/index.umd.js.map +1 -1
- package/package.json +3 -3
|
@@ -42,6 +42,13 @@ let NodeSort = function (NodeSort) {
|
|
|
42
42
|
TerpEvt["HandledComponentNode"] = "handled-component-node";
|
|
43
43
|
return TerpEvt;
|
|
44
44
|
})({});
|
|
45
|
+
class ParseSyntaxError extends SyntaxError {
|
|
46
|
+
constructor(code, message, loc) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.code = code;
|
|
49
|
+
this.loc = loc;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
45
52
|
|
|
46
53
|
class Tokenizer {
|
|
47
54
|
TabSize = 2;
|
|
@@ -89,6 +96,25 @@ class Tokenizer {
|
|
|
89
96
|
column: this.column
|
|
90
97
|
};
|
|
91
98
|
}
|
|
99
|
+
unclosedLoc(startOffset, startLine, startCol) {
|
|
100
|
+
const end = this.code.length - 1;
|
|
101
|
+
return {
|
|
102
|
+
start: {
|
|
103
|
+
offset: startOffset,
|
|
104
|
+
line: startLine,
|
|
105
|
+
column: startCol
|
|
106
|
+
},
|
|
107
|
+
end: {
|
|
108
|
+
offset: end,
|
|
109
|
+
line: this.line,
|
|
110
|
+
column: this.column
|
|
111
|
+
},
|
|
112
|
+
source: this.code.slice(startOffset, end)
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
throwUnclosed(code, message, startOffset, startLine, startCol) {
|
|
116
|
+
throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
|
|
117
|
+
}
|
|
92
118
|
resume(_snapshot) {
|
|
93
119
|
this.token = undefined;
|
|
94
120
|
this.needIndent = false;
|
|
@@ -275,8 +301,7 @@ class Tokenizer {
|
|
|
275
301
|
}
|
|
276
302
|
return this.token;
|
|
277
303
|
} catch (error) {
|
|
278
|
-
|
|
279
|
-
return this.token;
|
|
304
|
+
throw error;
|
|
280
305
|
} finally {
|
|
281
306
|
this.handledTokens.push(this.token);
|
|
282
307
|
}
|
|
@@ -355,6 +380,9 @@ class Tokenizer {
|
|
|
355
380
|
this.setToken(TokenType.Pipe, '|');
|
|
356
381
|
}
|
|
357
382
|
staticIns() {
|
|
383
|
+
const startOffset = this.preI,
|
|
384
|
+
startLine = this.line,
|
|
385
|
+
startCol = this.preCol;
|
|
358
386
|
let nextC = this.code[this.i + 1];
|
|
359
387
|
if (nextC !== '{') {
|
|
360
388
|
return false;
|
|
@@ -364,6 +392,9 @@ class Tokenizer {
|
|
|
364
392
|
let innerBrace = 0;
|
|
365
393
|
while (1) {
|
|
366
394
|
nextC = this.code[this.i + 1];
|
|
395
|
+
if (nextC === undefined) {
|
|
396
|
+
this.throwUnclosed('UNCLOSED_STATIC_INS', '未闭合的 "${...}"', startOffset, startLine, startCol);
|
|
397
|
+
}
|
|
367
398
|
value += nextC;
|
|
368
399
|
this.next();
|
|
369
400
|
if (nextC === '{') {
|
|
@@ -380,6 +411,9 @@ class Tokenizer {
|
|
|
380
411
|
return true;
|
|
381
412
|
}
|
|
382
413
|
brace() {
|
|
414
|
+
const startOffset = this.preI,
|
|
415
|
+
startLine = this.line,
|
|
416
|
+
startCol = this.preCol;
|
|
383
417
|
let inComment,
|
|
384
418
|
inString,
|
|
385
419
|
count = 0,
|
|
@@ -387,6 +421,9 @@ class Tokenizer {
|
|
|
387
421
|
backslashCount = 0;
|
|
388
422
|
while (1) {
|
|
389
423
|
const char = this.code[this.i];
|
|
424
|
+
if (char === undefined) {
|
|
425
|
+
this.throwUnclosed('UNCLOSED_BRACE', '未闭合的 "{"', startOffset, startLine, startCol);
|
|
426
|
+
}
|
|
390
427
|
const nextChar = this.code[this.i + 1];
|
|
391
428
|
if (inComment === 'single' && char === '\n') {
|
|
392
429
|
inComment = null;
|
|
@@ -569,11 +606,17 @@ class Tokenizer {
|
|
|
569
606
|
this.setToken(TokenType.Identifier, realValue);
|
|
570
607
|
}
|
|
571
608
|
str(char) {
|
|
609
|
+
const startOffset = this.preI,
|
|
610
|
+
startLine = this.line,
|
|
611
|
+
startCol = this.preCol;
|
|
572
612
|
let value = '';
|
|
573
613
|
let nextC;
|
|
574
614
|
let continuousBackslashCount = 0;
|
|
575
615
|
while (1) {
|
|
576
616
|
nextC = this.code[this.i + 1];
|
|
617
|
+
if (nextC === undefined) {
|
|
618
|
+
this.throwUnclosed('UNCLOSED_STRING', '未闭合的字符串字面量', startOffset, startLine, startCol);
|
|
619
|
+
}
|
|
577
620
|
const memoCount = continuousBackslashCount;
|
|
578
621
|
if (nextC === '\\') {
|
|
579
622
|
continuousBackslashCount++;
|
|
@@ -881,18 +924,47 @@ class Compiler {
|
|
|
881
924
|
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
925
|
_initProto = _applyDecs$e[0];
|
|
883
926
|
}
|
|
927
|
+
errors = (_initProto(this), []);
|
|
884
928
|
constructor(tokenizer, hooks = {}) {
|
|
885
929
|
this.tokenizer = tokenizer;
|
|
886
930
|
this.hooks = hooks;
|
|
887
|
-
|
|
931
|
+
}
|
|
932
|
+
addError(code, message, loc) {
|
|
933
|
+
this.errors.push({
|
|
934
|
+
code,
|
|
935
|
+
message,
|
|
936
|
+
loc
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
emptyLoc() {
|
|
940
|
+
const pos = this.tokenizer.getCurrentPos();
|
|
941
|
+
return {
|
|
942
|
+
start: pos,
|
|
943
|
+
end: {
|
|
944
|
+
offset: pos.offset + 1,
|
|
945
|
+
line: pos.line,
|
|
946
|
+
column: pos.column + 1
|
|
947
|
+
},
|
|
948
|
+
source: ' '
|
|
949
|
+
};
|
|
888
950
|
}
|
|
889
951
|
parseProgram() {
|
|
890
|
-
this.tokenizer.nextToken();
|
|
891
952
|
const body = [];
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
953
|
+
try {
|
|
954
|
+
this.tokenizer.nextToken();
|
|
955
|
+
while (!this.tokenizer.isEof()) {
|
|
956
|
+
const node = this.templateNode(body);
|
|
957
|
+
if (node) {
|
|
958
|
+
body.push(node);
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
} catch (error) {
|
|
962
|
+
if (error instanceof ParseSyntaxError) {
|
|
963
|
+
this.addError(error.code, error.message, error.loc);
|
|
964
|
+
} else if (error instanceof SyntaxError) {
|
|
965
|
+
const knownCodes = ['INCONSISTENT_INDENT', 'INDENT_MISMATCH'];
|
|
966
|
+
const code = knownCodes.includes(error.message) ? error.message : 'INCONSISTENT_INDENT';
|
|
967
|
+
this.addError(code, error.message, this.emptyLoc());
|
|
896
968
|
}
|
|
897
969
|
}
|
|
898
970
|
return {
|
|
@@ -918,7 +990,7 @@ class Compiler {
|
|
|
918
990
|
if (this.tokenizer.token.type & TokenType.Indent) {
|
|
919
991
|
this.tokenizer.nextToken();
|
|
920
992
|
while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
|
|
921
|
-
const child = this.templateNode();
|
|
993
|
+
const child = this.templateNode(children);
|
|
922
994
|
if (child) {
|
|
923
995
|
children.push(child);
|
|
924
996
|
}
|
|
@@ -929,13 +1001,25 @@ class Compiler {
|
|
|
929
1001
|
}
|
|
930
1002
|
return children;
|
|
931
1003
|
}
|
|
932
|
-
templateNode() {
|
|
933
|
-
this.tokenizer.token;
|
|
1004
|
+
templateNode(siblings) {
|
|
1005
|
+
const token = this.tokenizer.token;
|
|
1006
|
+
if (token.type & TokenType.Pipe) {
|
|
1007
|
+
this.addError('PIPE_IN_WRONG_CONTEXT', '"|" 只能出现在元素属性扩展行中', token.loc ?? this.emptyLoc());
|
|
1008
|
+
this.tokenizer.nextToken();
|
|
1009
|
+
return null;
|
|
1010
|
+
}
|
|
934
1011
|
const _this$tokenizer$_hook = this.tokenizer._hook({}),
|
|
935
1012
|
_this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
|
|
936
1013
|
hookType = _this$tokenizer$_hook2[0],
|
|
937
1014
|
value = _this$tokenizer$_hook2[1];
|
|
938
1015
|
if (value === 'if' || value === 'else' || value === 'fail') {
|
|
1016
|
+
if (value === 'else' || value === 'fail') {
|
|
1017
|
+
const lastSibling = siblings[siblings.length - 1];
|
|
1018
|
+
const lastType = lastSibling?.type;
|
|
1019
|
+
if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
|
|
1020
|
+
this.addError('ELSE_WITHOUT_IF', `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.emptyLoc());
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
939
1023
|
return this.parseConditionalNode();
|
|
940
1024
|
}
|
|
941
1025
|
if (value === 'for') {
|
|
@@ -960,6 +1044,13 @@ class Compiler {
|
|
|
960
1044
|
}
|
|
961
1045
|
parseElementNode(node) {
|
|
962
1046
|
const tagToken = this.tokenizer.token;
|
|
1047
|
+
if (!(tagToken.type & TokenType.Identifier)) {
|
|
1048
|
+
this.addError('INVALID_TAG_NAME', `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.emptyLoc());
|
|
1049
|
+
while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
|
|
1050
|
+
this.tokenizer.nextToken();
|
|
1051
|
+
}
|
|
1052
|
+
return null;
|
|
1053
|
+
}
|
|
963
1054
|
const tagName = tagToken.value;
|
|
964
1055
|
this.tokenizer.nextToken();
|
|
965
1056
|
const props = this.headerLineAndExtensions();
|
|
@@ -985,15 +1076,25 @@ class Compiler {
|
|
|
985
1076
|
return node;
|
|
986
1077
|
}
|
|
987
1078
|
parseLoopNode(node) {
|
|
1079
|
+
const forLoc = this.tokenizer.token.loc ?? this.emptyLoc();
|
|
988
1080
|
this.tokenizer.nextToken();
|
|
989
1081
|
const collection = this.parsePropertyValue();
|
|
990
|
-
|
|
1082
|
+
if (!collection.value && collection.value !== 0) {
|
|
1083
|
+
this.addError('MISSING_FOR_COLLECTION', '"for" 缺少集合表达式', forLoc);
|
|
1084
|
+
}
|
|
1085
|
+
const semicolonToken = this.tokenizer.nextToken();
|
|
1086
|
+
if (!(semicolonToken.type & TokenType.Semicolon)) {
|
|
1087
|
+
this.addError('MISSING_FOR_SEMICOLON', '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.emptyLoc());
|
|
1088
|
+
}
|
|
991
1089
|
const itemToken = this.tokenizer.nextToken();
|
|
992
1090
|
const isDestruct = itemToken.type === TokenType.InsertionExp;
|
|
993
1091
|
if (isDestruct) {
|
|
994
1092
|
itemToken.value = '{' + itemToken.value + '}';
|
|
995
1093
|
}
|
|
996
1094
|
const item = this.parsePropertyValue();
|
|
1095
|
+
if (!item.value && item.value !== 0) {
|
|
1096
|
+
this.addError('MISSING_FOR_ITEM', '"for" 缺少 item 变量名', itemToken.loc ?? this.emptyLoc());
|
|
1097
|
+
}
|
|
997
1098
|
let char = this.tokenizer.peekChar(),
|
|
998
1099
|
key,
|
|
999
1100
|
index;
|
|
@@ -1056,6 +1157,8 @@ class Compiler {
|
|
|
1056
1157
|
this.tokenizer.nextToken();
|
|
1057
1158
|
node.value = this.parsePropertyValue();
|
|
1058
1159
|
this.tokenizer.nextToken();
|
|
1160
|
+
} else {
|
|
1161
|
+
this.addError('MISSING_ASSIGN', `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.emptyLoc());
|
|
1059
1162
|
}
|
|
1060
1163
|
node.loc.start = node.key.loc.start;
|
|
1061
1164
|
node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
|
|
@@ -2053,5 +2156,5 @@ function customRender(option) {
|
|
|
2053
2156
|
};
|
|
2054
2157
|
}
|
|
2055
2158
|
|
|
2056
|
-
export { Compiler, NodeType, Tokenizer, bobe, customRender };
|
|
2159
|
+
export { Compiler, NodeType, ParseSyntaxError, Tokenizer, bobe, customRender };
|
|
2057
2160
|
//# sourceMappingURL=bobe.compiler.esm.js.map
|