UNPKG

@gent-js/gent

Version:

template-based data generator.

160 lines (159 loc) 5.62 kB
import { pickFirst, pickMany } from "../../utils.js"; import { commandParser, lex } from "./commandParser.js"; const BaseVisitor = commandParser.getBaseCstVisitorConstructor(); class CommandVisitor extends BaseVisitor { constructor() { super(); this.validateVisitor(); } commandExpression(children, param) { const commandNameNode = children.commandName[0]; if (commandNameNode === undefined) { return undefined; } const commandNameFragment = pickFirst(this.visit(commandNameNode)); if (commandNameFragment?.type !== "name") { return undefined; } let hasOption = false; const commandOptions = {}; const commandOptionNodes = children.commandOption; pickMany(commandOptionNodes) .flatMap((commandOptionNode) => this.visit(commandOptionNode)) .forEach((commandOptionFragment) => { if (commandOptionFragment === undefined) { return; } if (commandOptionFragment.type !== "option") { return; } commandOptions[commandOptionFragment.key] = commandOptionFragment.value; hasOption = true; }); let commandExpressionFragment; if (hasOption) { commandExpressionFragment = { type: "command", name: commandNameFragment.content, options: commandOptions, }; } else { commandExpressionFragment = { type: "command", name: commandNameFragment.content, }; } return commandExpressionFragment; } commandName(children, param) { const commandNameToken = children.Identifier[0]; if (commandNameToken === undefined) { return undefined; } const commandNameFragment = { type: "name", content: commandNameToken.image, }; return commandNameFragment; } commandOption(children, param) { const commandOptionKeyNode = children.commandOptionKey[0]; if (commandOptionKeyNode === undefined) { return undefined; } const commandOptionKeyFragment = pickFirst(this.visit(commandOptionKeyNode)); if (commandOptionKeyFragment?.type !== "option-key") { return undefined; } let commandOptionValueFragment; const commandOptionValueNode = children.commandOptionValue?.[0]; if (commandOptionValueNode !== undefined) { const possibleCommandOptionValue = pickFirst(this.visit(commandOptionValueNode)); if (possibleCommandOptionValue?.type === "option-value") { commandOptionValueFragment = possibleCommandOptionValue; } } let commandOptionFragment; if (commandOptionValueFragment === undefined) { commandOptionFragment = { type: "option", key: commandOptionKeyFragment.content, value: true, }; } else { commandOptionFragment = { type: "option", key: commandOptionKeyFragment.content, value: commandOptionValueFragment.content, }; } return commandOptionFragment; } commandOptionKey(children, param) { const commandOptionKeyToken = children.CommandOptionKey[0]; if (commandOptionKeyToken === undefined) { return undefined; } const key = commandOptionKeyToken.image; let actualKey; if (key.startsWith("--")) { actualKey = key.substring(2); } else if (key.startsWith("-")) { actualKey = key.substring(1); } else { actualKey = key; } const commandOptionKeyFragment = { type: "option-key", content: actualKey, }; return commandOptionKeyFragment; } commandOptionValue(children, param) { const numberToken = children.NumberLiteral?.[0]; if (numberToken !== undefined) { const numberValue = Number.parseInt(numberToken.image); if (!Number.isNaN(numberValue)) { return { type: "option-value", content: numberValue, }; } } const identifierToken = children.Identifier?.[0]; const literalToken = identifierToken ?? children.Literal?.[0]; if (literalToken !== undefined) { return { type: "option-value", content: literalToken.image, }; } const quotedLiteralToken = children.QuotedLiteral?.[0]; if (quotedLiteralToken !== undefined) { const quotedValue = quotedLiteralToken.image; return { type: "option-value", content: quotedValue.substring(1, quotedValue.length - 1), }; } return undefined; } } const visitor = new CommandVisitor(); export function toAstVisitor(input) { const lexResult = lex(input); commandParser.input = lexResult.tokens; const cst = commandParser.commandExpression(); if (commandParser.errors.length > 0) { throw Error("parsing errors detected!\n" + commandParser.errors[0]?.message); } const out = pickFirst(visitor.visit(cst)); if (out?.type !== "command") { return undefined; } return out; }