UNPKG

@gent-js/gent

Version:

template-based data generator.

289 lines (288 loc) 11.3 kB
import { parseCommand } from "../command/index.js"; import { buildCommandDocumentFragments } from "../commandDocument/index.js"; import { parseAndEmbedCommandExpression, } from "../commandTemplate/index.js"; import { isReadonlyArray } from "../common/utils.js"; import { normalizeWeight } from "../common/weightedItemFeeder.js"; import { EXPRESSION_BLOCK_END, EXPRESSION_BLOCK_START, isStringOnlyTemplateFragments, parseTemplate, } from "../template/index.js"; import { isNonNullObject, parseNonNaNFloat, parseNonNaNInteger, } from "../utils.js"; import { createJsonable } from "./createJsonable.js"; import { JsonableContentParameterName, JsonableLengthParameterName, JsonableProbabilityParameterName, JsonableTypeParameterName, JsonableWeightParameterName, MAX_PROBABILITY, MIN_PROBABILITY, } from "./jsonableParametersConsts.js"; import { isJsonValueType } from "./utils.js"; export function createJsonableTransformer(commandManager, documentOptions) { const commandDocumentFragmentsBuilder = function (parsedTemplateFragments) { return buildCommandDocumentFragments(parsedTemplateFragments, commandManager, documentOptions); }; const jsonableTransformer = function (value) { if (value === null) { // null return value; } else if (typeof value === "boolean") { // boolean return value; } else if (typeof value === "number") { // number return value; } else if (typeof value === "string") { // string const documentContent = parseDocumentContent(value, commandDocumentFragmentsBuilder); if (typeof documentContent === "string") { return documentContent; } else { return createJsonable({ type: "string", subType: "string", content: documentContent, probability: undefined, weight: undefined, }); } } else if (isReadonlyArray(value)) { // array const arrayJsonableParameters = tryParseArrayJsonableParameters(value, commandDocumentFragmentsBuilder, jsonableTransformer); if (arrayJsonableParameters !== undefined) { return createJsonable(arrayJsonableParameters); } return transformIntoJsonableArray(value, jsonableTransformer); } else { // object const jsonableValueParameters = tryParseJsonableParameters(value, commandDocumentFragmentsBuilder, jsonableTransformer); if (jsonableValueParameters !== undefined) { return createJsonable(jsonableValueParameters); } return transformIntoJsonableObject(value, jsonableTransformer); } }; return jsonableTransformer; } function transformIntoJsonableArray(content, jsonableTransformer) { return content .map((item) => jsonableTransformer(item)) .filter((jsonableValue) => jsonableValue !== undefined); } function transformIntoJsonableObject(jsonObject, jsonableTransformer) { let jsonableObject = {}; Object.keys(jsonObject).forEach((memberKey) => { const memberValue = jsonObject[memberKey]; if (memberValue === undefined) { return; } const jsonableValue = jsonableTransformer(memberValue); if (jsonableValue === undefined) { return; } jsonableObject[memberKey] = jsonableValue; }); return jsonableObject; } function tryParseArrayJsonableParameters(array, commandDocumentFragmentsBuilder, jsonableTransformer) { const items = []; let lengthContent; array.forEach((item) => { const possibleExpression = tryExtractJsonableParameterExpression(item); if (possibleExpression !== undefined) { const parsedCommand = parseCommand(possibleExpression); if (parsedCommand.name === JsonableLengthParameterName) { lengthContent = commandDocumentFragmentsBuilder([parsedCommand]); return; } } items.push(item); }); if (lengthContent === undefined) { return undefined; } return { type: "array", content: transformIntoJsonableArray(items, jsonableTransformer), length: lengthContent, probability: undefined, weight: undefined, }; } function tryParseJsonableParameters(jsonObject, commandDocumentFragmentsBuilder, jsonableTransformer) { let jsonValueType; let contentValue; let lengthValue; let probabilityValue; let weightValue; let hasShorthandObjectJsonableTrigger = false; const otherMembers = {}; Object.keys(jsonObject).forEach((memberKey) => { const memberValue = jsonObject[memberKey]; const possibleParameterName = tryExtractJsonableParameterExpression(memberKey); if (possibleParameterName === JsonableTypeParameterName) { // explicitly specified "type" if (!isJsonValueType(memberValue)) { return; } jsonValueType = memberValue; } else if (possibleParameterName === JsonableContentParameterName) { // specify "content" and also implicitly indicate "object" type contentValue = memberValue; hasShorthandObjectJsonableTrigger = true; } else if (possibleParameterName === JsonableProbabilityParameterName) { // specify "probability" and also implicitly indicate "object" type probabilityValue = memberValue; hasShorthandObjectJsonableTrigger = true; } else if (possibleParameterName === JsonableLengthParameterName) { lengthValue = memberValue; } else if (possibleParameterName === JsonableWeightParameterName) { // specify "weight" and also implicitly indicate "object" type weightValue = memberValue; hasShorthandObjectJsonableTrigger = true; } else if (memberValue !== undefined) { otherMembers[memberKey] = memberValue; } }); let actualJsonValueType; if (jsonValueType !== undefined) { // explicit type actualJsonValueType = jsonValueType; } else if (hasShorthandObjectJsonableTrigger) { // shorthand object (implicit type) actualJsonValueType = "object"; contentValue = contentValue ?? otherMembers; } else { // => return undefined return undefined; } if (actualJsonValueType === "object") { // ## object case let jsonableObject; if (isNonNullObject(contentValue)) { jsonableObject = transformIntoJsonableObject(contentValue, jsonableTransformer); } else { jsonableObject = {}; } // => return JsonableValueParameters return { type: "object", content: jsonableObject, ...createCommonJsonableParameters(probabilityValue, weightValue), }; } else if (actualJsonValueType === "array") { // ## array case if (typeof lengthValue !== "string") { // => return undefined return undefined; } const lengthContent = parseDocumentContent(lengthValue, commandDocumentFragmentsBuilder); let jsonableArray; if (Array.isArray(contentValue)) { jsonableArray = transformIntoJsonableArray(contentValue, jsonableTransformer); } else { jsonableArray = []; } // => return JsonableValueParameters return { type: actualJsonValueType, content: jsonableArray, length: lengthContent, ...createCommonJsonableParameters(probabilityValue, weightValue), }; } else if (actualJsonValueType === "string") { // ## string case if (typeof contentValue !== "string") { if (contentValue === undefined) { // => return undefined return undefined; } // string-json let jsonableValue; jsonableValue = jsonableTransformer(contentValue); if (jsonableValue === undefined) { // => return undefined return undefined; } // string-json return { type: actualJsonValueType, subType: "json", content: jsonableValue, ...createCommonJsonableParameters(probabilityValue, weightValue), }; } // string-string const content = parseDocumentContent(contentValue, commandDocumentFragmentsBuilder); // => return JsonableValueParameters return { type: actualJsonValueType, subType: "string", content: content, ...createCommonJsonableParameters(probabilityValue, weightValue), }; } else if (actualJsonValueType === "number" || actualJsonValueType === "boolean") { // ## number | boolean case if (typeof contentValue !== "string") { // => return undefined return undefined; } const content = parseDocumentContent(contentValue, commandDocumentFragmentsBuilder); // => return JsonableValueParameters return { type: actualJsonValueType, content: content, ...createCommonJsonableParameters(probabilityValue, weightValue), }; } else { // ## null case // => return JsonableValueParameters return { type: "null", ...createCommonJsonableParameters(probabilityValue, weightValue), }; } } function parseDocumentContent(value, commandDocumentFragmentsBuilder) { const templateFragments = parseTemplate(value); if (isStringOnlyTemplateFragments(templateFragments)) { return value; } else { const documentFragments = commandDocumentFragmentsBuilder(parseAndEmbedCommandExpression(templateFragments)); return documentFragments; } } function tryExtractJsonableParameterExpression(value) { if (typeof value !== "string") { return undefined; } if (!value.startsWith(EXPRESSION_BLOCK_START) || !value.endsWith(EXPRESSION_BLOCK_END)) { return undefined; } return value.substring(EXPRESSION_BLOCK_START.length, value.length - EXPRESSION_BLOCK_END.length); } function createCommonJsonableParameters(probabilityValue, weightValue) { const probability = parseNonNaNFloat(probabilityValue); const normalizedProbability = probability !== undefined ? normalizeProbability(probability) : undefined; const weight = parseNonNaNInteger(weightValue); const normalizedWeight = weight !== undefined ? normalizeWeight(weight) : undefined; return { probability: normalizedProbability, weight: normalizedWeight, }; } function normalizeProbability(value) { return Math.min(Math.max(value, MIN_PROBABILITY), MAX_PROBABILITY); }