UNPKG

5.31 kBJavaScriptView Raw
1import { DocExcerpt } from '../../nodes';
2import { TokenSequence } from '../TokenSequence';
3import { TokenKind } from '../Token';
4/**
5 * The TokenCoverageChecker performs two diagnostics to detect parser bugs:
6 * 1. It checks for two DocNode objects whose excerpt contains overlapping tokens.
7 * By design, a single character from the input stream should be associated with
8 * at most one TokenSequence.
9 * 2. It checks for gaps, i.e. input tokens that were not associated with any DocNode
10 * (that is reachable from the final DocCommon node tree). In some cases this is
11 * okay. For example, if `@public` appears twice inside a comment, the second
12 * redundant instance is ignored. But in general we want to track the gaps in the
13 * unit test snapshots to ensure in general that every input character is associated
14 * with an excerpt for a DocNode.
15 */
16var TokenCoverageChecker = /** @class */ (function () {
17 function TokenCoverageChecker(parserContext) {
18 this._parserContext = parserContext;
19 this._tokenAssociations = [];
20 this._tokenAssociations.length = parserContext.tokens.length;
21 }
22 TokenCoverageChecker.prototype.getGaps = function (rootNode) {
23 this._addNodeTree(rootNode);
24 return this._checkForGaps(false);
25 };
26 TokenCoverageChecker.prototype.reportGaps = function (rootNode) {
27 this._addNodeTree(rootNode);
28 this._checkForGaps(true);
29 };
30 TokenCoverageChecker.prototype._addNodeTree = function (node) {
31 if (node instanceof DocExcerpt) {
32 this._addSequence(node.content, node);
33 }
34 for (var _i = 0, _a = node.getChildNodes(); _i < _a.length; _i++) {
35 var childNode = _a[_i];
36 this._addNodeTree(childNode);
37 }
38 };
39 TokenCoverageChecker.prototype._addSequence = function (tokenSequence, docNode) {
40 var newTokenAssociation = { docNode: docNode, tokenSequence: tokenSequence };
41 for (var i = tokenSequence.startIndex; i < tokenSequence.endIndex; ++i) {
42 var tokenAssociation = this._tokenAssociations[i];
43 if (tokenAssociation) {
44 throw new Error("Overlapping content encountered between" +
45 (" " + this._formatTokenAssociation(tokenAssociation) + " and") +
46 (" " + this._formatTokenAssociation(newTokenAssociation)));
47 }
48 this._tokenAssociations[i] = newTokenAssociation;
49 }
50 };
51 TokenCoverageChecker.prototype._checkForGaps = function (reportGaps) {
52 var gaps = [];
53 var gapStartIndex = undefined;
54 var tokenAssociationBeforeGap = undefined;
55 var tokens = this._parserContext.tokens;
56 if (tokens[tokens.length - 1].kind !== TokenKind.EndOfInput) {
57 throw new Error('Missing EndOfInput token');
58 }
59 for (var i = 0; i < this._parserContext.tokens.length - 1; ++i) {
60 var tokenAssociation = this._tokenAssociations[i];
61 if (gapStartIndex === undefined) {
62 // No gap found yet
63 if (tokenAssociation) {
64 tokenAssociationBeforeGap = tokenAssociation;
65 }
66 else {
67 // We found the start of a gap
68 gapStartIndex = i;
69 }
70 }
71 else {
72 // Is this the end of the gap?
73 if (tokenAssociation) {
74 var gap = new TokenSequence({
75 parserContext: this._parserContext,
76 startIndex: gapStartIndex,
77 endIndex: i
78 });
79 if (reportGaps) {
80 this._reportGap(gap, tokenAssociationBeforeGap, tokenAssociation);
81 }
82 gaps.push(gap);
83 gapStartIndex = undefined;
84 tokenAssociationBeforeGap = undefined;
85 }
86 }
87 }
88 if (gapStartIndex) {
89 var gap = new TokenSequence({
90 parserContext: this._parserContext,
91 startIndex: gapStartIndex,
92 endIndex: this._parserContext.tokens.length
93 });
94 if (reportGaps) {
95 this._reportGap(gap, tokenAssociationBeforeGap, undefined);
96 }
97 gaps.push(gap);
98 }
99 return gaps;
100 };
101 TokenCoverageChecker.prototype._reportGap = function (gap, tokenAssociationBeforeGap, tokenAssociationAfterGap) {
102 var message = 'Gap encountered';
103 if (tokenAssociationBeforeGap) {
104 message += ' before ' + this._formatTokenAssociation(tokenAssociationBeforeGap);
105 }
106 if (tokenAssociationAfterGap) {
107 message += ' after ' + this._formatTokenAssociation(tokenAssociationAfterGap);
108 }
109 message += ': ' + JSON.stringify(gap.toString());
110 throw new Error(message);
111 };
112 TokenCoverageChecker.prototype._formatTokenAssociation = function (tokenAssociation) {
113 return tokenAssociation.docNode.kind + " (" + JSON.stringify(tokenAssociation.tokenSequence.toString()) + ")";
114 };
115 return TokenCoverageChecker;
116}());
117export { TokenCoverageChecker };
118//# sourceMappingURL=TokenCoverageChecker.js.map
\No newline at end of file