@gent-js/gent
Version:
template-based data generator.
87 lines (86 loc) • 2.58 kB
JavaScript
import { createToken, CstParser, Lexer } from "chevrotain";
import { EXPRESSION_BLOCK_END, EXPRESSION_BLOCK_START } from "../consts.js";
// region token
const LiteralBlock = createToken({
name: "LiteralBlock",
pattern: matchLiteralBlock,
line_breaks: true,
});
const ExpressionBlock = createToken({
name: "ExpressionBlock",
pattern: matchExpressionBlock,
line_breaks: false,
});
const allTokens = [ExpressionBlock, LiteralBlock];
// endregion
// region matcher
function matchLiteralBlock(text, startOffset) {
const endOffset = text.indexOf(EXPRESSION_BLOCK_START, startOffset);
if (endOffset === -1) {
return [text.substring(startOffset)];
}
else if (endOffset === startOffset) {
return null;
}
else {
let matchedString = text.substring(startOffset, endOffset);
return [matchedString];
}
}
function matchExpressionBlock(text, startOffset) {
for (let i = 0; i < EXPRESSION_BLOCK_START.length; i++) {
if (text.at(i + startOffset) !== EXPRESSION_BLOCK_START.at(i)) {
return null;
}
}
const startOfBlockEnd = text.indexOf(EXPRESSION_BLOCK_END, startOffset);
if (startOfBlockEnd === -1) {
return null;
}
const endOffset = startOfBlockEnd + EXPRESSION_BLOCK_END.length;
if (endOffset === startOffset) {
return null;
}
else {
let matchedString = text.substring(startOffset, endOffset);
return [matchedString];
}
}
// endregion
// region lexer
const lexer = new Lexer(allTokens);
export function lex(input) {
const lexingResult = lexer.tokenize(input);
if (lexingResult.errors.length > 0) {
throw Error(`lexing errors detected
${lexingResult.errors[0]?.message}`);
}
return lexingResult;
}
// endregion
// region parser
class TemplateParser extends CstParser {
constructor() {
super(allTokens);
this.performSelfAnalysis();
}
template = this.RULE("template", () => {
this.MANY(() => {
this.SUBRULE(this.templateFragment);
});
});
templateFragment = this.RULE("templateFragment", () => {
this.OR([
{ ALT: () => this.SUBRULE(this.literalBlock) },
{ ALT: () => this.SUBRULE(this.expressionBlock) },
]);
});
literalBlock = this.RULE("literalBlock", () => {
this.CONSUME(LiteralBlock);
});
expressionBlock = this.RULE("expressionBlock", () => {
this.CONSUME(ExpressionBlock);
});
}
export const templateParser = new TemplateParser();
// endregion