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