@flowscripter/mpeg-sdl-parser
Version:
ISO/IEC 14496-34 Syntactic Description Language (MPEG SDL) parser implemented in TypeScript
128 lines (113 loc) • 3.15 kB
text/typescript
import type { Doc } from "prettier";
import { doc } from "prettier";
import type { Token } from "../ast/token/Token";
import type { Trivia } from "../ast/token/Trivia";
const { hardline, indent, join } = doc.builders;
function getCommentString(trivia: Trivia): string {
if (!trivia.text.startsWith("//")) {
throw new Error(
"Logic Error: Expected comment to start with // : " +
JSON.stringify(trivia),
);
}
// Remove leading "//" from the comment text
const commentText = trivia.text.replace(/^\s*\/\/\s*/, "").trim();
return "// " + commentText;
}
/**
* Returns a doc for the leading trivia of a token.
* @param token The token to get the leading trivia for.
* @param indentLeadingTriviaAsIndentIsCompleting Whether to indent the leading trivia as if it is completing a line.
*
* Note that if indentLeadingTriviaAsIndentIsCompleting is true, the leading trivia will include a hardline as this
* is required to perform the indent.
*/
function getLeadingTriviaDoc(
token: Token,
indentLeadingTriviaAsIndentIsCompleting: boolean,
): Doc[] {
if (!token.leadingTrivia.length) {
if (indentLeadingTriviaAsIndentIsCompleting) {
return [hardline];
}
return [];
}
const elements = join(
hardline,
token.leadingTrivia.map((trivia) => getCommentString(trivia)),
);
if (indentLeadingTriviaAsIndentIsCompleting) {
return [
indent(
[hardline, elements],
),
hardline,
];
}
return [
elements,
hardline,
];
}
function getTrailingTriviaDoc(token: Token): Doc[] {
if (!token.trailingTrivia.length) {
return [];
}
return [
" ",
join(
hardline,
token.trailingTrivia.map((trivia) => getCommentString(trivia)),
),
hardline,
];
}
export function getDocWithTrivia(
token: Token,
indentLeadingTriviaAsIndentIsCompleting = false,
): Doc[] {
const leadingTriviaDoc = getLeadingTriviaDoc(
token,
indentLeadingTriviaAsIndentIsCompleting,
);
const trailingTriviaDoc = getTrailingTriviaDoc(token);
const doc = [];
if (leadingTriviaDoc.length > 0) {
doc.push(leadingTriviaDoc);
}
doc.push(token.text);
if (trailingTriviaDoc.length > 0) {
doc.push(trailingTriviaDoc);
}
return doc;
}
export function addCommaSeparatorsToDoc(
valuesDoc: Doc[],
commaSeparatorTokens: Token[] | undefined,
): Doc[] {
if (commaSeparatorTokens === undefined) {
if (valuesDoc.length > 1) {
throw new Error(
`Logic Error: Number of values: ${valuesDoc.length} and no comma separators provided`,
);
}
return valuesDoc;
}
if (valuesDoc.length !== commaSeparatorTokens.length + 1) {
throw new Error(
`Logic Error: Number of values: ${valuesDoc.length} and comma separators: ${commaSeparatorTokens.length} are not as expected`,
);
}
const result: Doc[] = [];
for (let i = 0; i < valuesDoc.length; i++) {
if (i < commaSeparatorTokens.length) {
result.push([
valuesDoc[i],
getDocWithTrivia(commaSeparatorTokens[i]),
]);
} else {
result.push(valuesDoc[i]);
}
}
return result;
}