diff --git a/stage_3_generation/build/decorators/CloneStatement.ts b/stage_3_generation/build/decorators/CloneStatement.ts new file mode 100644 index 0000000..8fe351e --- /dev/null +++ b/stage_3_generation/build/decorators/CloneStatement.ts @@ -0,0 +1,83 @@ +//#region preamble +import { + CodeBlockWriter, +} from "ts-morph"; + +import { + ClassFieldStatementsMap, + LiteralTypeStructureImpl, + MemberedStatementsKey, + MethodSignatureImpl, + ParameterDeclarationImpl, + UnionTypeStructureImpl, + type stringWriterOrStatementImpl, +} from "#stage_two/snapshot/source/exports.js"; + +import GetterFilter from "../fieldStatements/GetterFilter.js"; + +import BlockStatementImpl from "../../pseudoStatements/BlockStatement.js"; +import CallExpressionStatementImpl from "#stage_three/generation/pseudoStatements/CallExpression.js"; +//#endregion preamble + +export default class CloneStatement_Statements extends GetterFilter +{ + getMethodSignature(): MethodSignatureImpl + { + this.module.addImports("ts-morph", [], ["StatementStructures"]); + this.module.addImports("public", [], ["stringOrWriterFunction", "StatementStructureImpls"]); + const method = new MethodSignatureImpl("#cloneStatement"); + + const param = new ParameterDeclarationImpl("source"); + param.typeStructure = new UnionTypeStructureImpl([ + LiteralTypeStructureImpl.get("stringOrWriterFunction"), + LiteralTypeStructureImpl.get("StatementStructures") + ]); + method.parameters.push(param); + + method.returnTypeStructure = new UnionTypeStructureImpl([ + LiteralTypeStructureImpl.get("stringOrWriterFunction"), + LiteralTypeStructureImpl.get("StatementStructureImpls") + ]); + + return method; + } + + accept( + key: MemberedStatementsKey + ): boolean + { + return ( + (key.statementGroupKey === "static #cloneStatement") && + (key.fieldKey === ClassFieldStatementsMap.FIELD_TAIL_FINAL_RETURN) + ); + } + + getStatements( + key: MemberedStatementsKey + ): readonly stringWriterOrStatementImpl[] + { + void(key); + this.module.addImports("internal", ["StructureClassesMap"], []); + + return [ + new BlockStatementImpl( + `if (typeof source !== "object")`, + [ + `return source;` + ] + ).writerFunction, + + (writer: CodeBlockWriter): void => { + writer.write("return "); + (new CallExpressionStatementImpl({ + name: "StructureClassesMap.clone", + typeParameters: [ + LiteralTypeStructureImpl.get("StatementStructures"), + LiteralTypeStructureImpl.get("StatementStructureImpls"), + ], + parameters: ["source"] + })).writerFunction(writer); + } + ] + } +} diff --git a/stage_3_generation/build/decorators/createDecorators.ts b/stage_3_generation/build/decorators/createDecorators.ts index fb7733f..9fc6a6c 100644 --- a/stage_3_generation/build/decorators/createDecorators.ts +++ b/stage_3_generation/build/decorators/createDecorators.ts @@ -22,6 +22,7 @@ import { import modifyTypeMembersForTypeStructures from "../classTools/modifyTypeMembersForTypeStructures.js"; import StatementsRouter from "../fieldStatements/StatementsRouter.js"; +import CloneStatement_Statements from "./CloneStatement.js"; import { addImportsToModule, @@ -60,6 +61,13 @@ async function buildDecorator( typeToClass.addTypeMember(true, module.createCopyFieldsMethod()) + if (name.startsWith("StatementedNode")) { + const cloneStatementFilter = new CloneStatement_Statements(module); + router.filters.unshift(cloneStatementFilter); + typeToClass.addTypeMember(true, cloneStatementFilter.getMethodSignature()); + } + + typeToClass.defineStatementsByPurpose("body", false); module.classMembersMap = typeToClass.buildClassMembersMap(); diff --git a/stage_3_generation/build/fieldStatements/CopyFields.ts b/stage_3_generation/build/fieldStatements/CopyFields.ts index 53c80ff..5fd3ddf 100644 --- a/stage_3_generation/build/fieldStatements/CopyFields.ts +++ b/stage_3_generation/build/fieldStatements/CopyFields.ts @@ -17,6 +17,7 @@ import { LiteralTypeStructureImpl, MemberedStatementsKey, ParenthesesTypeStructureImpl, + QualifiedNameTypeStructureImpl, type PropertySignatureImpl, TypeStructureKind, type TypeStructures, @@ -30,6 +31,7 @@ import { } from "#stage_two/snapshot/source/exports.js"; import { + getClassInterfaceName, getStructureNameFromModified, } from "#utilities/source/StructureNameTransforms.js"; @@ -41,6 +43,7 @@ import GetterFilter from "../fieldStatements/GetterFilter.js"; import BlockStatementImpl from "../../pseudoStatements/BlockStatement.js"; import CallExpressionStatementImpl from "#stage_three/generation/pseudoStatements/CallExpression.js"; +import InterfaceModule from "#stage_three/generation/moduleClasses/InterfaceModule.js"; //#endregion preamble const booleanType = LiteralTypeStructureImpl.get("boolean"); @@ -202,12 +205,21 @@ export default class CopyFieldsStatements extends GetterFilter { const { name } = fieldType; - if (name === "statements") { + if ((this.module.baseName === "StatementedNodeStructure") && (name === "statements")) { return this.#getStatementsForCloneStatements(); } - const originalField = this.#getOriginalField(name); - void(originalField); + if ((this.module.baseName === "Structure") && ( + (name === "leadingTrivia") || (name === "trailingTrivia") + )) + { + return this.#getStatementsForTrivia(name); + } + + // DecoratableNodeStructureMixin + // JSDocableNodeStructureMixin + // ParameteredNodeStructureMixin + // TypeParameteredNodeStructureMixin if (objectType.kind === TypeStructureKind.Parentheses) objectType = objectType.childTypes[0]; @@ -217,50 +229,73 @@ export default class CopyFieldsStatements extends GetterFilter const childTypes = types as readonly ReadonlyDeep[]; - let useStructureClassesMap = false; - let includesString = false; + let generatedClassName = ""; + for (const childType of childTypes) { - if (childType === stringType) { - includesString = true; - } - else if (childType === LiteralTypeStructureImpl.get("stringOrWriterFunction")) { + if (childType === LiteralTypeStructureImpl.get("stringOrWriterFunction")) { this.module.addImports("public", [], ["stringOrWriterFunction"]); } else if (getStructureNameFromModified(childType.stringValue) !== childType.stringValue) { - this.module.addImports( - "public", - ["StructureClassesMap"], - [childType.stringValue] - ); - useStructureClassesMap = true; - } - } + assert(generatedClassName === ""); - void(includesString); - // DecoratableNodeStructureMixin - // JSDocableNodeStructureMixin - // ParameteredNodeStructureMixin - // TypeParameteredNodeStructureMixin + this.module.addImports("public", [], [childType.stringValue]); + this.module.addImports("internal", ["StructureClassesMap"], []); - if (useStructureClassesMap) { - /* - if (source.decorators) { - target.decorators.push( - ...StructureClassesMap.cloneArrayWithKind< - DecoratorStructure, - StructureKind.Decorator, - DecoratorImpl - >( - StructureKind.Decorator, - StructureClassesMap.forceArray(source.decorators), - ), - ); + generatedClassName = childType.stringValue; } - */ - return []; } + assert(generatedClassName, `we should be ready to clone structures now, ${this.module.decoratorName}:${name}`); + + const generatedStructureName = getStructureNameFromModified(generatedClassName); + const generatedInterfaceName = getClassInterfaceName(generatedStructureName); + const generatedStructureKind = InterfaceModule.structuresMap.get(generatedInterfaceName)!.structureKindName!; + + this.module.addImports( + "ts-morph", [], ["StructureKind", generatedStructureName] + ); + + const callClone = new CallExpressionStatementImpl({ + name: "...StructureClassesMap.cloneArrayWithKind", + typeParameters: [ + LiteralTypeStructureImpl.get(generatedStructureName), + new QualifiedNameTypeStructureImpl([ + "StructureKind", + generatedStructureKind + ]), + objectType as TypeStructures + ], + parameters: [ + `StructureKind.${generatedStructureKind}`, + new CallExpressionStatementImpl({ + name: `StructureClassesMap.forceArray`, + parameters: [ + `source.${name}` + ] + }) + ] + }); - assert(name === "leadingTrivia" || name === "trailingTrivia"); + return [ + new BlockStatementImpl( + `if (source.${name})`, + [ + new CallExpressionStatementImpl({ + name: `target.${name}.push`, + parameters: [ + callClone + ] + }).writerFunction + ] + ).writerFunction + ]; + + return []; + } + + #getStatementsForTrivia( + name: string + ): readonly stringWriterOrStatementImpl[] + { return [ new BlockStatementImpl( `if (Array.isArray(source.${name}))`, @@ -334,7 +369,7 @@ export default class CopyFieldsStatements extends GetterFilter } #getOriginalField( - propertyName: string + propertyName: string, ): PropertySignatureImpl { return getTypeAugmentedStructure( diff --git a/stage_3_generation/pseudoStatements/CallExpression.ts b/stage_3_generation/pseudoStatements/CallExpression.ts index b41ecf8..3eea7f1 100644 --- a/stage_3_generation/pseudoStatements/CallExpression.ts +++ b/stage_3_generation/pseudoStatements/CallExpression.ts @@ -3,14 +3,15 @@ import type { } from "ts-morph"; import type { + TypeStructures, stringOrWriterFunction } from "#stage_two/snapshot/source/exports.js"; import StatementBase from "./StatementBase.js"; export interface CallExpressionStatementContext { name: string; - readonly typeParameters: string[]; - readonly parameters: stringOrWriterFunction[]; + readonly typeParameters: TypeStructures[]; + readonly parameters: (stringOrWriterFunction | CallExpressionStatementImpl)[]; } export default @@ -18,8 +19,8 @@ class CallExpressionStatementImpl extends StatementBase implements CallExpressionStatementContext { name: string; - readonly typeParameters: string[] = []; - readonly parameters: stringOrWriterFunction[] = []; + readonly typeParameters: TypeStructures[] = []; + readonly parameters: (stringOrWriterFunction | CallExpressionStatementImpl)[] = []; constructor( context: Pick & Partial @@ -38,7 +39,7 @@ implements CallExpressionStatementContext writer.write(this.name); if (this.typeParameters.length) { this.pairedWrite(writer, "<", ">", () => { - this.writeArray(writer, this.typeParameters); + this.writeArray(writer, this.typeParameters.map(typeParam => typeParam.writerFunction)); }) }