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
package/dist/bobe.cjs.js
CHANGED
|
@@ -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;
|
|
@@ -81,6 +88,25 @@ class Tokenizer {
|
|
|
81
88
|
column: this.column
|
|
82
89
|
};
|
|
83
90
|
}
|
|
91
|
+
unclosedLoc(startOffset, startLine, startCol) {
|
|
92
|
+
const end = this.code.length - 1;
|
|
93
|
+
return {
|
|
94
|
+
start: {
|
|
95
|
+
offset: startOffset,
|
|
96
|
+
line: startLine,
|
|
97
|
+
column: startCol
|
|
98
|
+
},
|
|
99
|
+
end: {
|
|
100
|
+
offset: end,
|
|
101
|
+
line: this.line,
|
|
102
|
+
column: this.column
|
|
103
|
+
},
|
|
104
|
+
source: this.code.slice(startOffset, end)
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
throwUnclosed(code, message, startOffset, startLine, startCol) {
|
|
108
|
+
throw new ParseSyntaxError(code, message, this.unclosedLoc(startOffset, startLine, startCol));
|
|
109
|
+
}
|
|
84
110
|
resume(_snapshot) {
|
|
85
111
|
this.token = undefined;
|
|
86
112
|
this.needIndent = false;
|
|
@@ -249,8 +275,7 @@ class Tokenizer {
|
|
|
249
275
|
}
|
|
250
276
|
return this.token;
|
|
251
277
|
} catch (error) {
|
|
252
|
-
|
|
253
|
-
return this.token;
|
|
278
|
+
throw error;
|
|
254
279
|
} finally {
|
|
255
280
|
this.handledTokens.push(this.token);
|
|
256
281
|
}
|
|
@@ -313,6 +338,9 @@ class Tokenizer {
|
|
|
313
338
|
this.setToken(TokenType.Pipe, '|');
|
|
314
339
|
}
|
|
315
340
|
staticIns() {
|
|
341
|
+
const startOffset = this.preI,
|
|
342
|
+
startLine = this.line,
|
|
343
|
+
startCol = this.preCol;
|
|
316
344
|
let nextC = this.code[this.i + 1];
|
|
317
345
|
if (nextC !== '{') {
|
|
318
346
|
return false;
|
|
@@ -322,6 +350,9 @@ class Tokenizer {
|
|
|
322
350
|
let innerBrace = 0;
|
|
323
351
|
while (1) {
|
|
324
352
|
nextC = this.code[this.i + 1];
|
|
353
|
+
if (nextC === undefined) {
|
|
354
|
+
this.throwUnclosed('UNCLOSED_STATIC_INS', '未闭合的 "${...}"', startOffset, startLine, startCol);
|
|
355
|
+
}
|
|
325
356
|
value += nextC;
|
|
326
357
|
this.next();
|
|
327
358
|
if (nextC === '{') {
|
|
@@ -338,6 +369,9 @@ class Tokenizer {
|
|
|
338
369
|
return true;
|
|
339
370
|
}
|
|
340
371
|
brace() {
|
|
372
|
+
const startOffset = this.preI,
|
|
373
|
+
startLine = this.line,
|
|
374
|
+
startCol = this.preCol;
|
|
341
375
|
let inComment,
|
|
342
376
|
inString,
|
|
343
377
|
count = 0,
|
|
@@ -345,6 +379,9 @@ class Tokenizer {
|
|
|
345
379
|
backslashCount = 0;
|
|
346
380
|
while (1) {
|
|
347
381
|
const char = this.code[this.i];
|
|
382
|
+
if (char === undefined) {
|
|
383
|
+
this.throwUnclosed('UNCLOSED_BRACE', '未闭合的 "{"', startOffset, startLine, startCol);
|
|
384
|
+
}
|
|
348
385
|
const nextChar = this.code[this.i + 1];
|
|
349
386
|
if (inComment === 'single' && char === '\n') {
|
|
350
387
|
inComment = null;
|
|
@@ -527,11 +564,17 @@ class Tokenizer {
|
|
|
527
564
|
this.setToken(TokenType.Identifier, realValue);
|
|
528
565
|
}
|
|
529
566
|
str(char) {
|
|
567
|
+
const startOffset = this.preI,
|
|
568
|
+
startLine = this.line,
|
|
569
|
+
startCol = this.preCol;
|
|
530
570
|
let value = '';
|
|
531
571
|
let nextC;
|
|
532
572
|
let continuousBackslashCount = 0;
|
|
533
573
|
while (1) {
|
|
534
574
|
nextC = this.code[this.i + 1];
|
|
575
|
+
if (nextC === undefined) {
|
|
576
|
+
this.throwUnclosed('UNCLOSED_STRING', '未闭合的字符串字面量', startOffset, startLine, startCol);
|
|
577
|
+
}
|
|
535
578
|
const memoCount = continuousBackslashCount;
|
|
536
579
|
if (nextC === '\\') {
|
|
537
580
|
continuousBackslashCount++;
|
|
@@ -839,18 +882,47 @@ class Compiler {
|
|
|
839
882
|
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
883
|
_initProto = _applyDecs$e[0];
|
|
841
884
|
}
|
|
885
|
+
errors = (_initProto(this), []);
|
|
842
886
|
constructor(tokenizer, hooks = {}) {
|
|
843
887
|
this.tokenizer = tokenizer;
|
|
844
888
|
this.hooks = hooks;
|
|
845
|
-
|
|
889
|
+
}
|
|
890
|
+
addError(code, message, loc) {
|
|
891
|
+
this.errors.push({
|
|
892
|
+
code,
|
|
893
|
+
message,
|
|
894
|
+
loc
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
emptyLoc() {
|
|
898
|
+
const pos = this.tokenizer.getCurrentPos();
|
|
899
|
+
return {
|
|
900
|
+
start: pos,
|
|
901
|
+
end: {
|
|
902
|
+
offset: pos.offset + 1,
|
|
903
|
+
line: pos.line,
|
|
904
|
+
column: pos.column + 1
|
|
905
|
+
},
|
|
906
|
+
source: ' '
|
|
907
|
+
};
|
|
846
908
|
}
|
|
847
909
|
parseProgram() {
|
|
848
|
-
this.tokenizer.nextToken();
|
|
849
910
|
const body = [];
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
911
|
+
try {
|
|
912
|
+
this.tokenizer.nextToken();
|
|
913
|
+
while (!this.tokenizer.isEof()) {
|
|
914
|
+
const node = this.templateNode(body);
|
|
915
|
+
if (node) {
|
|
916
|
+
body.push(node);
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
} catch (error) {
|
|
920
|
+
if (error instanceof ParseSyntaxError) {
|
|
921
|
+
this.addError(error.code, error.message, error.loc);
|
|
922
|
+
} else if (error instanceof SyntaxError) {
|
|
923
|
+
const knownCodes = ['INCONSISTENT_INDENT', 'INDENT_MISMATCH'];
|
|
924
|
+
const code = knownCodes.includes(error.message) ? error.message : 'INCONSISTENT_INDENT';
|
|
925
|
+
this.addError(code, error.message, this.emptyLoc());
|
|
854
926
|
}
|
|
855
927
|
}
|
|
856
928
|
return {
|
|
@@ -876,7 +948,7 @@ class Compiler {
|
|
|
876
948
|
if (this.tokenizer.token.type & TokenType.Indent) {
|
|
877
949
|
this.tokenizer.nextToken();
|
|
878
950
|
while (!(this.tokenizer.token.type & TokenType.Dedent) && !this.tokenizer.isEof()) {
|
|
879
|
-
const child = this.templateNode();
|
|
951
|
+
const child = this.templateNode(children);
|
|
880
952
|
if (child) {
|
|
881
953
|
children.push(child);
|
|
882
954
|
}
|
|
@@ -887,13 +959,25 @@ class Compiler {
|
|
|
887
959
|
}
|
|
888
960
|
return children;
|
|
889
961
|
}
|
|
890
|
-
templateNode() {
|
|
891
|
-
this.tokenizer.token;
|
|
962
|
+
templateNode(siblings) {
|
|
963
|
+
const token = this.tokenizer.token;
|
|
964
|
+
if (token.type & TokenType.Pipe) {
|
|
965
|
+
this.addError('PIPE_IN_WRONG_CONTEXT', '"|" 只能出现在元素属性扩展行中', token.loc ?? this.emptyLoc());
|
|
966
|
+
this.tokenizer.nextToken();
|
|
967
|
+
return null;
|
|
968
|
+
}
|
|
892
969
|
const _this$tokenizer$_hook = this.tokenizer._hook({}),
|
|
893
970
|
_this$tokenizer$_hook2 = _slicedToArray(_this$tokenizer$_hook, 2),
|
|
894
971
|
hookType = _this$tokenizer$_hook2[0],
|
|
895
972
|
value = _this$tokenizer$_hook2[1];
|
|
896
973
|
if (value === 'if' || value === 'else' || value === 'fail') {
|
|
974
|
+
if (value === 'else' || value === 'fail') {
|
|
975
|
+
const lastSibling = siblings[siblings.length - 1];
|
|
976
|
+
const lastType = lastSibling?.type;
|
|
977
|
+
if (lastType !== NodeType.If && lastType !== NodeType.Else && lastType !== NodeType.Fail) {
|
|
978
|
+
this.addError('ELSE_WITHOUT_IF', `"${value}" 前必须有 "if" 或 "else" 节点`, token.loc ?? this.emptyLoc());
|
|
979
|
+
}
|
|
980
|
+
}
|
|
897
981
|
return this.parseConditionalNode();
|
|
898
982
|
}
|
|
899
983
|
if (value === 'for') {
|
|
@@ -918,6 +1002,13 @@ class Compiler {
|
|
|
918
1002
|
}
|
|
919
1003
|
parseElementNode(node) {
|
|
920
1004
|
const tagToken = this.tokenizer.token;
|
|
1005
|
+
if (!(tagToken.type & TokenType.Identifier)) {
|
|
1006
|
+
this.addError('INVALID_TAG_NAME', `无效的标签名,期望标识符但得到 "${tagToken.value}"`, tagToken.loc ?? this.emptyLoc());
|
|
1007
|
+
while (!(this.tokenizer.token.type & TokenType.NewLine) && !this.tokenizer.isEof()) {
|
|
1008
|
+
this.tokenizer.nextToken();
|
|
1009
|
+
}
|
|
1010
|
+
return null;
|
|
1011
|
+
}
|
|
921
1012
|
const tagName = tagToken.value;
|
|
922
1013
|
this.tokenizer.nextToken();
|
|
923
1014
|
const props = this.headerLineAndExtensions();
|
|
@@ -943,15 +1034,25 @@ class Compiler {
|
|
|
943
1034
|
return node;
|
|
944
1035
|
}
|
|
945
1036
|
parseLoopNode(node) {
|
|
1037
|
+
const forLoc = this.tokenizer.token.loc ?? this.emptyLoc();
|
|
946
1038
|
this.tokenizer.nextToken();
|
|
947
1039
|
const collection = this.parsePropertyValue();
|
|
948
|
-
|
|
1040
|
+
if (!collection.value && collection.value !== 0) {
|
|
1041
|
+
this.addError('MISSING_FOR_COLLECTION', '"for" 缺少集合表达式', forLoc);
|
|
1042
|
+
}
|
|
1043
|
+
const semicolonToken = this.tokenizer.nextToken();
|
|
1044
|
+
if (!(semicolonToken.type & TokenType.Semicolon)) {
|
|
1045
|
+
this.addError('MISSING_FOR_SEMICOLON', '"for" 语法:for <集合>; <item> [index][; key],缺少第一个 ";"', semicolonToken.loc ?? this.emptyLoc());
|
|
1046
|
+
}
|
|
949
1047
|
const itemToken = this.tokenizer.nextToken();
|
|
950
1048
|
const isDestruct = itemToken.type === TokenType.InsertionExp;
|
|
951
1049
|
if (isDestruct) {
|
|
952
1050
|
itemToken.value = '{' + itemToken.value + '}';
|
|
953
1051
|
}
|
|
954
1052
|
const item = this.parsePropertyValue();
|
|
1053
|
+
if (!item.value && item.value !== 0) {
|
|
1054
|
+
this.addError('MISSING_FOR_ITEM', '"for" 缺少 item 变量名', itemToken.loc ?? this.emptyLoc());
|
|
1055
|
+
}
|
|
955
1056
|
let char = this.tokenizer.peekChar(),
|
|
956
1057
|
key,
|
|
957
1058
|
index;
|
|
@@ -1014,6 +1115,8 @@ class Compiler {
|
|
|
1014
1115
|
this.tokenizer.nextToken();
|
|
1015
1116
|
node.value = this.parsePropertyValue();
|
|
1016
1117
|
this.tokenizer.nextToken();
|
|
1118
|
+
} else {
|
|
1119
|
+
this.addError('MISSING_ASSIGN', `属性 "${node.key.key}" 缺少 "=" 赋值符号`, node.key.loc ?? this.emptyLoc());
|
|
1017
1120
|
}
|
|
1018
1121
|
node.loc.start = node.key.loc.start;
|
|
1019
1122
|
node.loc.end = node.value ? node.value.loc.end : node.key.loc.end;
|
|
@@ -2013,6 +2116,7 @@ function customRender(option) {
|
|
|
2013
2116
|
|
|
2014
2117
|
exports.Compiler = Compiler;
|
|
2015
2118
|
exports.NodeType = NodeType;
|
|
2119
|
+
exports.ParseSyntaxError = ParseSyntaxError;
|
|
2016
2120
|
exports.Tokenizer = Tokenizer;
|
|
2017
2121
|
exports.bobe = bobe;
|
|
2018
2122
|
exports.customRender = customRender;
|