@gent-js/gent
Version:
template-based data generator.
289 lines (288 loc) • 11.3 kB
JavaScript
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);
}