links-notation 0.13.0 → 0.16.0
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/README.md +55 -0
- package/bun.lock +74 -94
- package/dist/index.js +238 -166
- package/eslint.config.js +34 -0
- package/examples/empty_reference.js +43 -0
- package/examples/typescript-example.ts +95 -0
- package/index.d.ts +312 -0
- package/package.json +19 -7
- package/src/Link.js +7 -1
- package/src/Parser.js +36 -3
- package/src/grammar.pegjs +86 -12
- package/src/parser-generated.js +221 -147
- package/tests/EmptyReference.test.js +91 -0
- package/tests/Link.test.js +2 -2
- package/tests/MultilineQuotedString.test.js +4 -2
- package/tests/NestedIndentation.test.js +114 -0
- package/tests/SingleLineParser.test.js +7 -7
- package/tsconfig.json +15 -0
package/dist/index.js
CHANGED
|
@@ -36,9 +36,12 @@ class Link {
|
|
|
36
36
|
return value && typeof value.toLinkOrIdString === "function" ? value.toLinkOrIdString() : String(value);
|
|
37
37
|
}
|
|
38
38
|
static escapeReference(reference) {
|
|
39
|
-
if (
|
|
39
|
+
if (reference === null || reference === undefined) {
|
|
40
40
|
return "";
|
|
41
41
|
}
|
|
42
|
+
if (reference === "") {
|
|
43
|
+
return '""';
|
|
44
|
+
}
|
|
42
45
|
const hasSingleQuote = reference.includes("'");
|
|
43
46
|
const hasDoubleQuote = reference.includes('"');
|
|
44
47
|
const needsQuoting = reference.includes(":") || reference.includes("(") || reference.includes(")") || reference.includes(" ") || reference.includes("\t") || reference.includes(`
|
|
@@ -378,9 +381,9 @@ function peg$parse(input, options) {
|
|
|
378
381
|
document: peg$parsedocument
|
|
379
382
|
};
|
|
380
383
|
let peg$startRuleFunction = peg$parsedocument;
|
|
381
|
-
const peg$c0 = "
|
|
382
|
-
const peg$c1 = "
|
|
383
|
-
const peg$c2 = "
|
|
384
|
+
const peg$c0 = "(";
|
|
385
|
+
const peg$c1 = ")";
|
|
386
|
+
const peg$c2 = ":";
|
|
384
387
|
const peg$c3 = '"';
|
|
385
388
|
const peg$c4 = "'";
|
|
386
389
|
const peg$c5 = "`";
|
|
@@ -392,9 +395,9 @@ function peg$parse(input, options) {
|
|
|
392
395
|
const peg$e0 = peg$classExpectation([" ", "\t"], false, false, false);
|
|
393
396
|
const peg$e1 = peg$classExpectation(["\r", `
|
|
394
397
|
`], false, false, false);
|
|
395
|
-
const peg$e2 = peg$literalExpectation("
|
|
396
|
-
const peg$e3 = peg$literalExpectation("
|
|
397
|
-
const peg$e4 = peg$literalExpectation("
|
|
398
|
+
const peg$e2 = peg$literalExpectation("(", false);
|
|
399
|
+
const peg$e3 = peg$literalExpectation(")", false);
|
|
400
|
+
const peg$e4 = peg$literalExpectation(":", false);
|
|
398
401
|
const peg$e5 = peg$literalExpectation('"', false);
|
|
399
402
|
const peg$e6 = peg$anyExpectation();
|
|
400
403
|
const peg$e7 = peg$literalExpectation("'", false);
|
|
@@ -405,17 +408,13 @@ function peg$parse(input, options) {
|
|
|
405
408
|
const peg$e11 = peg$classExpectation([" ", "\t", `
|
|
406
409
|
`, "\r", "(", ":", ")"], true, false, false);
|
|
407
410
|
function peg$f0() {
|
|
408
|
-
|
|
409
|
-
baseIndentation = null;
|
|
410
|
-
return true;
|
|
411
|
+
return resetState();
|
|
411
412
|
}
|
|
412
413
|
function peg$f1(links) {
|
|
413
414
|
return links;
|
|
414
415
|
}
|
|
415
416
|
function peg$f2() {
|
|
416
|
-
|
|
417
|
-
baseIndentation = null;
|
|
418
|
-
return true;
|
|
417
|
+
return resetState();
|
|
419
418
|
}
|
|
420
419
|
function peg$f3() {
|
|
421
420
|
return [];
|
|
@@ -431,7 +430,7 @@ function peg$parse(input, options) {
|
|
|
431
430
|
return l;
|
|
432
431
|
}
|
|
433
432
|
function peg$f7(e, l) {
|
|
434
|
-
return {
|
|
433
|
+
return Object.assign({}, e, { children: l });
|
|
435
434
|
}
|
|
436
435
|
function peg$f8(e) {
|
|
437
436
|
return e;
|
|
@@ -457,27 +456,28 @@ function peg$parse(input, options) {
|
|
|
457
456
|
function peg$f15(vl) {
|
|
458
457
|
return vl;
|
|
459
458
|
}
|
|
460
|
-
function peg$f16(
|
|
461
|
-
|
|
459
|
+
function peg$f16(body) {
|
|
460
|
+
exitNestedContext();
|
|
461
|
+
return body;
|
|
462
462
|
}
|
|
463
|
-
function peg$f17(
|
|
464
|
-
return
|
|
463
|
+
function peg$f17(l) {
|
|
464
|
+
return { nested: l };
|
|
465
|
+
}
|
|
466
|
+
function peg$f18() {
|
|
467
|
+
return { nested: [] };
|
|
468
|
+
}
|
|
469
|
+
function peg$f19() {
|
|
470
|
+
return enterNestedContext();
|
|
465
471
|
}
|
|
466
|
-
function peg$
|
|
472
|
+
function peg$f20(value) {
|
|
467
473
|
return value;
|
|
468
474
|
}
|
|
469
|
-
function peg$
|
|
475
|
+
function peg$f21(list) {
|
|
470
476
|
return list;
|
|
471
477
|
}
|
|
472
|
-
function peg$
|
|
478
|
+
function peg$f22(id, v) {
|
|
473
479
|
return { id, values: v };
|
|
474
480
|
}
|
|
475
|
-
function peg$f21(id, v) {
|
|
476
|
-
return { id, values: v };
|
|
477
|
-
}
|
|
478
|
-
function peg$f22(v) {
|
|
479
|
-
return { values: v };
|
|
480
|
-
}
|
|
481
481
|
function peg$f23(v) {
|
|
482
482
|
return { values: v };
|
|
483
483
|
}
|
|
@@ -565,6 +565,9 @@ function peg$parse(input, options) {
|
|
|
565
565
|
function peg$f44(spaces) {
|
|
566
566
|
return checkIndentation(spaces);
|
|
567
567
|
}
|
|
568
|
+
function peg$f45() {
|
|
569
|
+
return isInsideNestedContext();
|
|
570
|
+
}
|
|
568
571
|
let peg$currPos = options.peg$currPos | 0;
|
|
569
572
|
let peg$savedPos = peg$currPos;
|
|
570
573
|
const peg$posDetailsCache = [{ line: 1, column: 1 }];
|
|
@@ -924,7 +927,7 @@ function peg$parse(input, options) {
|
|
|
924
927
|
function peg$parsereferenceOrLink() {
|
|
925
928
|
let s0, s1;
|
|
926
929
|
s0 = peg$currPos;
|
|
927
|
-
s1 = peg$
|
|
930
|
+
s1 = peg$parsenestedGroup();
|
|
928
931
|
if (s1 !== peg$FAILED) {
|
|
929
932
|
peg$savedPos = s0;
|
|
930
933
|
s1 = peg$f9(s1);
|
|
@@ -944,7 +947,7 @@ function peg$parse(input, options) {
|
|
|
944
947
|
function peg$parseanyLink() {
|
|
945
948
|
let s0, s1, s2;
|
|
946
949
|
s0 = peg$currPos;
|
|
947
|
-
s1 = peg$
|
|
950
|
+
s1 = peg$parsenestedGroup();
|
|
948
951
|
if (s1 !== peg$FAILED) {
|
|
949
952
|
s2 = peg$parseeol();
|
|
950
953
|
if (s2 !== peg$FAILED) {
|
|
@@ -978,14 +981,6 @@ function peg$parse(input, options) {
|
|
|
978
981
|
}
|
|
979
982
|
return s0;
|
|
980
983
|
}
|
|
981
|
-
function peg$parsemultiLineAnyLink() {
|
|
982
|
-
let s0;
|
|
983
|
-
s0 = peg$parsemultiLineValueLink();
|
|
984
|
-
if (s0 === peg$FAILED) {
|
|
985
|
-
s0 = peg$parsemultiLineLink();
|
|
986
|
-
}
|
|
987
|
-
return s0;
|
|
988
|
-
}
|
|
989
984
|
function peg$parsesingleLineAnyLink() {
|
|
990
985
|
let s0, s1, s2;
|
|
991
986
|
s0 = peg$currPos;
|
|
@@ -1022,32 +1017,97 @@ function peg$parse(input, options) {
|
|
|
1022
1017
|
}
|
|
1023
1018
|
return s0;
|
|
1024
1019
|
}
|
|
1025
|
-
function peg$
|
|
1026
|
-
let s0, s1, s2;
|
|
1020
|
+
function peg$parsenestedGroup() {
|
|
1021
|
+
let s0, s1, s2, s3;
|
|
1027
1022
|
s0 = peg$currPos;
|
|
1028
|
-
|
|
1023
|
+
if (input.charCodeAt(peg$currPos) === 40) {
|
|
1024
|
+
s1 = peg$c0;
|
|
1025
|
+
peg$currPos++;
|
|
1026
|
+
} else {
|
|
1027
|
+
s1 = peg$FAILED;
|
|
1028
|
+
if (peg$silentFails === 0) {
|
|
1029
|
+
peg$fail(peg$e2);
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1029
1032
|
if (s1 !== peg$FAILED) {
|
|
1030
|
-
s2 = peg$
|
|
1031
|
-
peg$
|
|
1032
|
-
|
|
1033
|
+
s2 = peg$parseENTER_NESTED_CONTEXT();
|
|
1034
|
+
if (s2 !== peg$FAILED) {
|
|
1035
|
+
s3 = peg$parsenestedGroupBody();
|
|
1036
|
+
if (s3 !== peg$FAILED) {
|
|
1037
|
+
peg$savedPos = s0;
|
|
1038
|
+
s0 = peg$f16(s3);
|
|
1039
|
+
} else {
|
|
1040
|
+
peg$currPos = s0;
|
|
1041
|
+
s0 = peg$FAILED;
|
|
1042
|
+
}
|
|
1043
|
+
} else {
|
|
1044
|
+
peg$currPos = s0;
|
|
1045
|
+
s0 = peg$FAILED;
|
|
1046
|
+
}
|
|
1033
1047
|
} else {
|
|
1034
1048
|
peg$currPos = s0;
|
|
1035
1049
|
s0 = peg$FAILED;
|
|
1036
1050
|
}
|
|
1037
1051
|
return s0;
|
|
1038
1052
|
}
|
|
1039
|
-
function peg$
|
|
1040
|
-
let s0, s1, s2, s3;
|
|
1053
|
+
function peg$parsenestedGroupBody() {
|
|
1054
|
+
let s0, s1, s2, s3, s4;
|
|
1041
1055
|
s0 = peg$currPos;
|
|
1042
|
-
s1 = peg$
|
|
1043
|
-
s2 =
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1056
|
+
s1 = peg$parseskipEmptyLines();
|
|
1057
|
+
s2 = peg$parselinks();
|
|
1058
|
+
if (s2 !== peg$FAILED) {
|
|
1059
|
+
s3 = peg$parse_();
|
|
1060
|
+
if (input.charCodeAt(peg$currPos) === 41) {
|
|
1061
|
+
s4 = peg$c1;
|
|
1062
|
+
peg$currPos++;
|
|
1063
|
+
} else {
|
|
1064
|
+
s4 = peg$FAILED;
|
|
1065
|
+
if (peg$silentFails === 0) {
|
|
1066
|
+
peg$fail(peg$e3);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
if (s4 !== peg$FAILED) {
|
|
1070
|
+
peg$savedPos = s0;
|
|
1071
|
+
s0 = peg$f17(s2);
|
|
1072
|
+
} else {
|
|
1073
|
+
peg$currPos = s0;
|
|
1074
|
+
s0 = peg$FAILED;
|
|
1075
|
+
}
|
|
1076
|
+
} else {
|
|
1077
|
+
peg$currPos = s0;
|
|
1078
|
+
s0 = peg$FAILED;
|
|
1079
|
+
}
|
|
1080
|
+
if (s0 === peg$FAILED) {
|
|
1081
|
+
s0 = peg$currPos;
|
|
1082
|
+
s1 = peg$parse_();
|
|
1083
|
+
if (input.charCodeAt(peg$currPos) === 41) {
|
|
1084
|
+
s2 = peg$c1;
|
|
1085
|
+
peg$currPos++;
|
|
1086
|
+
} else {
|
|
1087
|
+
s2 = peg$FAILED;
|
|
1088
|
+
if (peg$silentFails === 0) {
|
|
1089
|
+
peg$fail(peg$e3);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (s2 !== peg$FAILED) {
|
|
1093
|
+
peg$savedPos = s0;
|
|
1094
|
+
s0 = peg$f18();
|
|
1095
|
+
} else {
|
|
1096
|
+
peg$currPos = s0;
|
|
1097
|
+
s0 = peg$FAILED;
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
return s0;
|
|
1101
|
+
}
|
|
1102
|
+
function peg$parseENTER_NESTED_CONTEXT() {
|
|
1103
|
+
let s0;
|
|
1104
|
+
peg$savedPos = peg$currPos;
|
|
1105
|
+
s0 = peg$f19();
|
|
1106
|
+
if (s0) {
|
|
1107
|
+
s0 = undefined;
|
|
1108
|
+
} else {
|
|
1109
|
+
s0 = peg$FAILED;
|
|
1048
1110
|
}
|
|
1049
|
-
peg$savedPos = s0;
|
|
1050
|
-
s0 = peg$f17(s2);
|
|
1051
1111
|
return s0;
|
|
1052
1112
|
}
|
|
1053
1113
|
function peg$parsesingleLineValueAndWhitespace() {
|
|
@@ -1057,7 +1117,7 @@ function peg$parse(input, options) {
|
|
|
1057
1117
|
s2 = peg$parsereferenceOrLink();
|
|
1058
1118
|
if (s2 !== peg$FAILED) {
|
|
1059
1119
|
peg$savedPos = s0;
|
|
1060
|
-
s0 = peg$
|
|
1120
|
+
s0 = peg$f20(s2);
|
|
1061
1121
|
} else {
|
|
1062
1122
|
peg$currPos = s0;
|
|
1063
1123
|
s0 = peg$FAILED;
|
|
@@ -1079,7 +1139,7 @@ function peg$parse(input, options) {
|
|
|
1079
1139
|
}
|
|
1080
1140
|
if (s1 !== peg$FAILED) {
|
|
1081
1141
|
peg$savedPos = s0;
|
|
1082
|
-
s1 = peg$
|
|
1142
|
+
s1 = peg$f21(s1);
|
|
1083
1143
|
}
|
|
1084
1144
|
s0 = s1;
|
|
1085
1145
|
return s0;
|
|
@@ -1092,78 +1152,19 @@ function peg$parse(input, options) {
|
|
|
1092
1152
|
if (s2 !== peg$FAILED) {
|
|
1093
1153
|
s3 = peg$parse__();
|
|
1094
1154
|
if (input.charCodeAt(peg$currPos) === 58) {
|
|
1095
|
-
s4 = peg$
|
|
1155
|
+
s4 = peg$c2;
|
|
1096
1156
|
peg$currPos++;
|
|
1097
1157
|
} else {
|
|
1098
1158
|
s4 = peg$FAILED;
|
|
1099
1159
|
if (peg$silentFails === 0) {
|
|
1100
|
-
peg$fail(peg$
|
|
1160
|
+
peg$fail(peg$e4);
|
|
1101
1161
|
}
|
|
1102
1162
|
}
|
|
1103
1163
|
if (s4 !== peg$FAILED) {
|
|
1104
1164
|
s5 = peg$parsesingleLineValues();
|
|
1105
1165
|
if (s5 !== peg$FAILED) {
|
|
1106
1166
|
peg$savedPos = s0;
|
|
1107
|
-
s0 = peg$
|
|
1108
|
-
} else {
|
|
1109
|
-
peg$currPos = s0;
|
|
1110
|
-
s0 = peg$FAILED;
|
|
1111
|
-
}
|
|
1112
|
-
} else {
|
|
1113
|
-
peg$currPos = s0;
|
|
1114
|
-
s0 = peg$FAILED;
|
|
1115
|
-
}
|
|
1116
|
-
} else {
|
|
1117
|
-
peg$currPos = s0;
|
|
1118
|
-
s0 = peg$FAILED;
|
|
1119
|
-
}
|
|
1120
|
-
return s0;
|
|
1121
|
-
}
|
|
1122
|
-
function peg$parsemultiLineLink() {
|
|
1123
|
-
let s0, s1, s2, s3, s4, s5, s6, s7, s8;
|
|
1124
|
-
s0 = peg$currPos;
|
|
1125
|
-
if (input.charCodeAt(peg$currPos) === 40) {
|
|
1126
|
-
s1 = peg$c1;
|
|
1127
|
-
peg$currPos++;
|
|
1128
|
-
} else {
|
|
1129
|
-
s1 = peg$FAILED;
|
|
1130
|
-
if (peg$silentFails === 0) {
|
|
1131
|
-
peg$fail(peg$e3);
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
if (s1 !== peg$FAILED) {
|
|
1135
|
-
s2 = peg$parse_();
|
|
1136
|
-
s3 = peg$parsereference();
|
|
1137
|
-
if (s3 !== peg$FAILED) {
|
|
1138
|
-
s4 = peg$parse_();
|
|
1139
|
-
if (input.charCodeAt(peg$currPos) === 58) {
|
|
1140
|
-
s5 = peg$c0;
|
|
1141
|
-
peg$currPos++;
|
|
1142
|
-
} else {
|
|
1143
|
-
s5 = peg$FAILED;
|
|
1144
|
-
if (peg$silentFails === 0) {
|
|
1145
|
-
peg$fail(peg$e2);
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
if (s5 !== peg$FAILED) {
|
|
1149
|
-
s6 = peg$parsemultiLineValues();
|
|
1150
|
-
s7 = peg$parse_();
|
|
1151
|
-
if (input.charCodeAt(peg$currPos) === 41) {
|
|
1152
|
-
s8 = peg$c2;
|
|
1153
|
-
peg$currPos++;
|
|
1154
|
-
} else {
|
|
1155
|
-
s8 = peg$FAILED;
|
|
1156
|
-
if (peg$silentFails === 0) {
|
|
1157
|
-
peg$fail(peg$e4);
|
|
1158
|
-
}
|
|
1159
|
-
}
|
|
1160
|
-
if (s8 !== peg$FAILED) {
|
|
1161
|
-
peg$savedPos = s0;
|
|
1162
|
-
s0 = peg$f21(s3, s6);
|
|
1163
|
-
} else {
|
|
1164
|
-
peg$currPos = s0;
|
|
1165
|
-
s0 = peg$FAILED;
|
|
1166
|
-
}
|
|
1167
|
+
s0 = peg$f22(s2, s5);
|
|
1167
1168
|
} else {
|
|
1168
1169
|
peg$currPos = s0;
|
|
1169
1170
|
s0 = peg$FAILED;
|
|
@@ -1184,48 +1185,11 @@ function peg$parse(input, options) {
|
|
|
1184
1185
|
s1 = peg$parsesingleLineValues();
|
|
1185
1186
|
if (s1 !== peg$FAILED) {
|
|
1186
1187
|
peg$savedPos = s0;
|
|
1187
|
-
s1 = peg$
|
|
1188
|
+
s1 = peg$f23(s1);
|
|
1188
1189
|
}
|
|
1189
1190
|
s0 = s1;
|
|
1190
1191
|
return s0;
|
|
1191
1192
|
}
|
|
1192
|
-
function peg$parsemultiLineValueLink() {
|
|
1193
|
-
let s0, s1, s2, s3, s4;
|
|
1194
|
-
s0 = peg$currPos;
|
|
1195
|
-
if (input.charCodeAt(peg$currPos) === 40) {
|
|
1196
|
-
s1 = peg$c1;
|
|
1197
|
-
peg$currPos++;
|
|
1198
|
-
} else {
|
|
1199
|
-
s1 = peg$FAILED;
|
|
1200
|
-
if (peg$silentFails === 0) {
|
|
1201
|
-
peg$fail(peg$e3);
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
if (s1 !== peg$FAILED) {
|
|
1205
|
-
s2 = peg$parsemultiLineValues();
|
|
1206
|
-
s3 = peg$parse_();
|
|
1207
|
-
if (input.charCodeAt(peg$currPos) === 41) {
|
|
1208
|
-
s4 = peg$c2;
|
|
1209
|
-
peg$currPos++;
|
|
1210
|
-
} else {
|
|
1211
|
-
s4 = peg$FAILED;
|
|
1212
|
-
if (peg$silentFails === 0) {
|
|
1213
|
-
peg$fail(peg$e4);
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
if (s4 !== peg$FAILED) {
|
|
1217
|
-
peg$savedPos = s0;
|
|
1218
|
-
s0 = peg$f23(s2);
|
|
1219
|
-
} else {
|
|
1220
|
-
peg$currPos = s0;
|
|
1221
|
-
s0 = peg$FAILED;
|
|
1222
|
-
}
|
|
1223
|
-
} else {
|
|
1224
|
-
peg$currPos = s0;
|
|
1225
|
-
s0 = peg$FAILED;
|
|
1226
|
-
}
|
|
1227
|
-
return s0;
|
|
1228
|
-
}
|
|
1229
1193
|
function peg$parseindentedIdLink() {
|
|
1230
1194
|
let s0, s1, s2, s3, s4;
|
|
1231
1195
|
s0 = peg$currPos;
|
|
@@ -1233,12 +1197,12 @@ function peg$parse(input, options) {
|
|
|
1233
1197
|
if (s1 !== peg$FAILED) {
|
|
1234
1198
|
s2 = peg$parse__();
|
|
1235
1199
|
if (input.charCodeAt(peg$currPos) === 58) {
|
|
1236
|
-
s3 = peg$
|
|
1200
|
+
s3 = peg$c2;
|
|
1237
1201
|
peg$currPos++;
|
|
1238
1202
|
} else {
|
|
1239
1203
|
s3 = peg$FAILED;
|
|
1240
1204
|
if (peg$silentFails === 0) {
|
|
1241
|
-
peg$fail(peg$
|
|
1205
|
+
peg$fail(peg$e4);
|
|
1242
1206
|
}
|
|
1243
1207
|
}
|
|
1244
1208
|
if (s3 !== peg$FAILED) {
|
|
@@ -1767,6 +1731,9 @@ function peg$parse(input, options) {
|
|
|
1767
1731
|
}
|
|
1768
1732
|
if (s2 === peg$FAILED) {
|
|
1769
1733
|
s2 = peg$parseeof();
|
|
1734
|
+
if (s2 === peg$FAILED) {
|
|
1735
|
+
s2 = peg$parsenestedGroupEnd();
|
|
1736
|
+
}
|
|
1770
1737
|
}
|
|
1771
1738
|
if (s2 !== peg$FAILED) {
|
|
1772
1739
|
s1 = [s1, s2];
|
|
@@ -1777,6 +1744,48 @@ function peg$parse(input, options) {
|
|
|
1777
1744
|
}
|
|
1778
1745
|
return s0;
|
|
1779
1746
|
}
|
|
1747
|
+
function peg$parsenestedGroupEnd() {
|
|
1748
|
+
let s0, s1, s2, s3;
|
|
1749
|
+
s0 = peg$currPos;
|
|
1750
|
+
peg$savedPos = peg$currPos;
|
|
1751
|
+
s1 = peg$f45();
|
|
1752
|
+
if (s1) {
|
|
1753
|
+
s1 = undefined;
|
|
1754
|
+
} else {
|
|
1755
|
+
s1 = peg$FAILED;
|
|
1756
|
+
}
|
|
1757
|
+
if (s1 !== peg$FAILED) {
|
|
1758
|
+
s2 = peg$currPos;
|
|
1759
|
+
peg$silentFails++;
|
|
1760
|
+
if (input.charCodeAt(peg$currPos) === 41) {
|
|
1761
|
+
s3 = peg$c1;
|
|
1762
|
+
peg$currPos++;
|
|
1763
|
+
} else {
|
|
1764
|
+
s3 = peg$FAILED;
|
|
1765
|
+
if (peg$silentFails === 0) {
|
|
1766
|
+
peg$fail(peg$e3);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
peg$silentFails--;
|
|
1770
|
+
if (s3 !== peg$FAILED) {
|
|
1771
|
+
peg$currPos = s2;
|
|
1772
|
+
s2 = undefined;
|
|
1773
|
+
} else {
|
|
1774
|
+
s2 = peg$FAILED;
|
|
1775
|
+
}
|
|
1776
|
+
if (s2 !== peg$FAILED) {
|
|
1777
|
+
s1 = [s1, s2];
|
|
1778
|
+
s0 = s1;
|
|
1779
|
+
} else {
|
|
1780
|
+
peg$currPos = s0;
|
|
1781
|
+
s0 = peg$FAILED;
|
|
1782
|
+
}
|
|
1783
|
+
} else {
|
|
1784
|
+
peg$currPos = s0;
|
|
1785
|
+
s0 = peg$FAILED;
|
|
1786
|
+
}
|
|
1787
|
+
return s0;
|
|
1788
|
+
}
|
|
1780
1789
|
function peg$parseeof() {
|
|
1781
1790
|
let s0, s1;
|
|
1782
1791
|
s0 = peg$currPos;
|
|
@@ -1863,6 +1872,30 @@ function peg$parse(input, options) {
|
|
|
1863
1872
|
}
|
|
1864
1873
|
let indentationStack = [0];
|
|
1865
1874
|
let baseIndentation = null;
|
|
1875
|
+
let contextStack = [];
|
|
1876
|
+
function resetState() {
|
|
1877
|
+
indentationStack = [0];
|
|
1878
|
+
baseIndentation = null;
|
|
1879
|
+
contextStack = [];
|
|
1880
|
+
return true;
|
|
1881
|
+
}
|
|
1882
|
+
function enterNestedContext() {
|
|
1883
|
+
contextStack.push({ indentationStack, baseIndentation });
|
|
1884
|
+
indentationStack = [0];
|
|
1885
|
+
baseIndentation = null;
|
|
1886
|
+
return true;
|
|
1887
|
+
}
|
|
1888
|
+
function exitNestedContext() {
|
|
1889
|
+
const saved = contextStack.pop();
|
|
1890
|
+
if (saved) {
|
|
1891
|
+
indentationStack = saved.indentationStack;
|
|
1892
|
+
baseIndentation = saved.baseIndentation;
|
|
1893
|
+
}
|
|
1894
|
+
return true;
|
|
1895
|
+
}
|
|
1896
|
+
function isInsideNestedContext() {
|
|
1897
|
+
return contextStack.length > 0;
|
|
1898
|
+
}
|
|
1866
1899
|
function setBaseIndentation(spaces) {
|
|
1867
1900
|
if (baseIndentation === null) {
|
|
1868
1901
|
baseIndentation = spaces.length;
|
|
@@ -1890,6 +1923,24 @@ function peg$parse(input, options) {
|
|
|
1890
1923
|
function getCurrentIndentation() {
|
|
1891
1924
|
return indentationStack[indentationStack.length - 1];
|
|
1892
1925
|
}
|
|
1926
|
+
function isSubstantiveBody(content) {
|
|
1927
|
+
let depth = 0;
|
|
1928
|
+
let hasVisible = false;
|
|
1929
|
+
for (const c of content) {
|
|
1930
|
+
if (c === "(") {
|
|
1931
|
+
depth++;
|
|
1932
|
+
} else if (c === ")") {
|
|
1933
|
+
depth--;
|
|
1934
|
+
if (depth < 0) {
|
|
1935
|
+
return false;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
if (!/\s/.test(c)) {
|
|
1939
|
+
hasVisible = true;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
return hasVisible && depth === 0;
|
|
1943
|
+
}
|
|
1893
1944
|
function parseQuotedStringAt(inputStr, startPos, quoteChar) {
|
|
1894
1945
|
if (startPos >= inputStr.length || inputStr[startPos] !== quoteChar) {
|
|
1895
1946
|
return null;
|
|
@@ -1900,6 +1951,8 @@ function peg$parse(input, options) {
|
|
|
1900
1951
|
quoteCount++;
|
|
1901
1952
|
pos++;
|
|
1902
1953
|
}
|
|
1954
|
+
const isEvenRun = quoteCount % 2 === 0;
|
|
1955
|
+
const emptyReference = isEvenRun ? { value: "", length: quoteCount } : null;
|
|
1903
1956
|
const closeSeq = quoteChar.repeat(quoteCount);
|
|
1904
1957
|
const escapeSeq = quoteChar.repeat(quoteCount * 2);
|
|
1905
1958
|
let content = "";
|
|
@@ -1912,6 +1965,9 @@ function peg$parse(input, options) {
|
|
|
1912
1965
|
if (inputStr.substr(pos, quoteCount) === closeSeq) {
|
|
1913
1966
|
const afterClose = pos + quoteCount;
|
|
1914
1967
|
if (afterClose >= inputStr.length || inputStr[afterClose] !== quoteChar) {
|
|
1968
|
+
if (isEvenRun && !isSubstantiveBody(content)) {
|
|
1969
|
+
return emptyReference;
|
|
1970
|
+
}
|
|
1915
1971
|
return {
|
|
1916
1972
|
value: content,
|
|
1917
1973
|
length: afterClose - startPos
|
|
@@ -1921,7 +1977,7 @@ function peg$parse(input, options) {
|
|
|
1921
1977
|
content += inputStr[pos];
|
|
1922
1978
|
pos++;
|
|
1923
1979
|
}
|
|
1924
|
-
return
|
|
1980
|
+
return emptyReference;
|
|
1925
1981
|
}
|
|
1926
1982
|
let parsedValue = null;
|
|
1927
1983
|
let parsedLength = 0;
|
|
@@ -1988,7 +2044,7 @@ class Parser {
|
|
|
1988
2044
|
if (item === null || item === undefined)
|
|
1989
2045
|
return;
|
|
1990
2046
|
if (item.children && item.children.length > 0) {
|
|
1991
|
-
if (item.id && (!item.values || item.values.length === 0)) {
|
|
2047
|
+
if (item.id !== undefined && item.id !== null && (!item.values || item.values.length === 0)) {
|
|
1992
2048
|
const childValues = item.children.map((child) => {
|
|
1993
2049
|
if (child.values && child.values.length === 1) {
|
|
1994
2050
|
return this.transformLink(child.values[0]);
|
|
@@ -2041,21 +2097,37 @@ class Parser {
|
|
|
2041
2097
|
combined._isFromPathCombination = true;
|
|
2042
2098
|
return combined;
|
|
2043
2099
|
}
|
|
2100
|
+
transformNested(nested) {
|
|
2101
|
+
const nestedLinks = [];
|
|
2102
|
+
for (const item of nested) {
|
|
2103
|
+
if (item !== null && item !== undefined) {
|
|
2104
|
+
this.collectLinks(item, [], nestedLinks);
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
const wrapsSingleGroup = nested.length === 1 && nested[0] && nested[0].nested !== undefined;
|
|
2108
|
+
if (nestedLinks.length === 1 && !wrapsSingleGroup) {
|
|
2109
|
+
return nestedLinks[0];
|
|
2110
|
+
}
|
|
2111
|
+
return new Link(null, nestedLinks);
|
|
2112
|
+
}
|
|
2044
2113
|
transformLink(item) {
|
|
2045
2114
|
if (item === null || item === undefined)
|
|
2046
2115
|
return null;
|
|
2047
2116
|
if (item instanceof Link) {
|
|
2048
2117
|
return item;
|
|
2049
2118
|
}
|
|
2119
|
+
if (item.nested !== undefined) {
|
|
2120
|
+
return this.transformNested(item.nested);
|
|
2121
|
+
}
|
|
2050
2122
|
if (item.id !== undefined && !item.values && !item.children) {
|
|
2051
2123
|
return new Link(item.id);
|
|
2052
2124
|
}
|
|
2053
2125
|
if (item.values && Array.isArray(item.values)) {
|
|
2054
|
-
const link = new Link(item.id
|
|
2126
|
+
const link = new Link(item.id ?? null, []);
|
|
2055
2127
|
link.values = item.values.map((v) => this.transformLink(v));
|
|
2056
2128
|
return link;
|
|
2057
2129
|
}
|
|
2058
|
-
return new Link(item.id
|
|
2130
|
+
return new Link(item.id ?? null, []);
|
|
2059
2131
|
}
|
|
2060
2132
|
}
|
|
2061
2133
|
// src/FormatOptions.js
|
|
@@ -2087,10 +2159,10 @@ class FormatOptions {
|
|
|
2087
2159
|
class FormatConfig extends FormatOptions {
|
|
2088
2160
|
}
|
|
2089
2161
|
export {
|
|
2090
|
-
|
|
2091
|
-
Parser,
|
|
2092
|
-
LinksGroup,
|
|
2093
|
-
Link,
|
|
2162
|
+
FormatConfig,
|
|
2094
2163
|
FormatOptions,
|
|
2095
|
-
|
|
2164
|
+
Link,
|
|
2165
|
+
LinksGroup,
|
|
2166
|
+
Parser,
|
|
2167
|
+
formatLinks
|
|
2096
2168
|
};
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export default [
|
|
2
|
+
{
|
|
3
|
+
ignores: ['dist/', 'node_modules/', 'src/parser-generated.js'],
|
|
4
|
+
},
|
|
5
|
+
{
|
|
6
|
+
languageOptions: {
|
|
7
|
+
ecmaVersion: 'latest',
|
|
8
|
+
sourceType: 'module',
|
|
9
|
+
globals: {
|
|
10
|
+
// Node.js globals
|
|
11
|
+
process: 'readonly',
|
|
12
|
+
Buffer: 'readonly',
|
|
13
|
+
__dirname: 'readonly',
|
|
14
|
+
__filename: 'readonly',
|
|
15
|
+
global: 'readonly',
|
|
16
|
+
// Test framework globals
|
|
17
|
+
describe: 'readonly',
|
|
18
|
+
test: 'readonly',
|
|
19
|
+
it: 'readonly',
|
|
20
|
+
expect: 'readonly',
|
|
21
|
+
beforeEach: 'readonly',
|
|
22
|
+
afterEach: 'readonly',
|
|
23
|
+
beforeAll: 'readonly',
|
|
24
|
+
afterAll: 'readonly',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
rules: {
|
|
28
|
+
'no-unused-vars': 'warn',
|
|
29
|
+
'no-undef': 'error',
|
|
30
|
+
semi: ['error', 'always'],
|
|
31
|
+
quotes: ['warn', 'single', { avoidEscape: true }],
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
];
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// A document written for an object with empty keys, the case that motivated
|
|
2
|
+
// https://github.com/link-foundation/links-notation/issues/288
|
|
3
|
+
//
|
|
4
|
+
// `{"": {"": 1}}` is written as `(o: ("" (o: ("" 1))))`. Every reference in it
|
|
5
|
+
// reads back as itself, so encoders that produce empty keys round trip.
|
|
6
|
+
//
|
|
7
|
+
// Usage: node js/examples/empty_reference.js
|
|
8
|
+
|
|
9
|
+
import { Parser, formatLinks } from '../src/index.js';
|
|
10
|
+
|
|
11
|
+
const parser = new Parser();
|
|
12
|
+
|
|
13
|
+
const source = '(o: ("" (o: ("" 1))))';
|
|
14
|
+
|
|
15
|
+
console.log('Source: ', source);
|
|
16
|
+
|
|
17
|
+
const links = parser.parse(source);
|
|
18
|
+
|
|
19
|
+
// Show every reference between angle brackets, so an empty one stays visible.
|
|
20
|
+
function render(node) {
|
|
21
|
+
if (!node.values || node.values.length === 0) {
|
|
22
|
+
return `<${node.id ?? ''}>`;
|
|
23
|
+
}
|
|
24
|
+
const head =
|
|
25
|
+
node.id === null || node.id === undefined ? '' : `<${node.id}>: `;
|
|
26
|
+
return `(${head}${node.values.map(render).join(' ')})`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log('Parsed as: ', links.map(render).join('\n'));
|
|
30
|
+
|
|
31
|
+
const formatted = formatLinks(links);
|
|
32
|
+
console.log('Formatted: ', formatted);
|
|
33
|
+
console.log(
|
|
34
|
+
'Round trips: ',
|
|
35
|
+
formatLinks(parser.parse(formatted)) === formatted
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// A bare delimiter pair is one empty reference, and two in a row stay separate.
|
|
39
|
+
for (const example of ['(a "" b)', '(a "" "" b)', '(a " " b)', '(a ""x"" b)']) {
|
|
40
|
+
console.log(
|
|
41
|
+
`${example.padEnd(14)} => ${parser.parse(example).map(render).join(' ')}`
|
|
42
|
+
);
|
|
43
|
+
}
|