import { Member } from "./members/Member"; import { Header } from "./Header"; import { Implementation } from "./Implementation"; import { Files } from "../library/Files"; import { MatchResult } from "../library/MatchResult"; import { Texts } from "../library/Texts"; import { MemberType } from "./MemberType"; import { Field } from "./members/Field"; import { RJLog } from "../library/RJLog"; import { CppDefinition } from "./CppDefinition"; import { Objects } from "../library/Objects"; import { Variations } from "./Variations"; import { VariationDefinitions, VariationTemplates } from "./VariationTemplates"; import { Method } from "./members/Method"; import { MemberFactory } from "./factories/MemberFactory"; export class CppCreator { static definitionsPath = __dirname + "/../../../rokojori-action-library-definitions"; static outputPath = __dirname + "/../../../rokojori-action-library"; static rawPath = CppCreator.definitionsPath + "/_raw"; static registerTypesPath = CppCreator.rawPath + "/register_types.cpp"; static registerTypesPathOutput = CppCreator.outputPath + "/register_types.cpp"; static createIncludes( className:string ) { // RJLog.log( "Create include", className ); let godotClasses:any = { "Node" : "scene/main/node.h", "Node2D" : "scene/2d/node_2d.h", "Node3D" : "scene/3d/node_3d.h", "Resource" : "core/io/resource.h" } if ( godotClasses[ className ] ) { return `#include "${godotClasses[ className ]}"`; } return `#include "./${className}.h"`; } static getHeaderDefine( className:string ) { className = className.replace( /^RJ/, "" ); let output = [ className[ 0 ] ]; for ( let i = 1; i < className.length; i++ ) { let character = className[ i ]; let upperCaseCharacter = character.toUpperCase(); if ( character === upperCaseCharacter ) { output.push( "_" ); } output.push( upperCaseCharacter ); } className = output.join( "" ); return "ROKOJORI__" + className + "_H"; } static async createCppFiles( definitionPath:string ) { let jsonPath = definitionPath; // path.join( definitionsPath, definitionPath ); let isDir = await Files.isDirectory( jsonPath ); if ( isDir ) { let files = await Files.getFiles( jsonPath ); for ( let file of files ) { if ( file.startsWith( "_" ) ) { continue; } await CppCreator.createCppFiles( Texts.joinPaths( jsonPath, file ) ); } return Promise.resolve(); } return this.createCppFile( jsonPath ); } static async createCppFile( jsonPath:string ) { let data = await Files.loadJSON( jsonPath ); if ( data.variations ) { let variationsData = data.variations; if ( typeof variationsData == "string" ) { variationsData = (VariationTemplates as any as {[index:string]:VariationDefinitions})[ variationsData ]; } let variations = new Variations( variationsData ); console.log( variations.items, variations.counter, variations.hasItems ); while ( variations.hasItems ) { let newData = CppDefinition.createVariation( data, variations.currentMap ); await this.createCppFileWith( newData ); variations.nextItem(); } } else { await CppCreator.createCppFileWith( data ); } return Promise.resolve(); } static async createCppFileWith( data:CppDefinition ) { let classNameResult = /(\w+)(?:\:(\w+))?/.exec( data.class ); let className = classNameResult[ 1 ]; let extendingClass = classNameResult[ 2 ]; let headerDefine = CppCreator.getHeaderDefine( className ); let fromProtectedMembers = MemberFactory.grabMembers( data.protected, "protected" ); let fromPublicMembers = MemberFactory.grabMembers( data.public, "public" ); let protectedMembers:Member[] = []; let publicMembers:Member[] = []; let insertMember = ( m:Member ) => { let container = m.accessModifier === "public" ? publicMembers : protectedMembers; container.push( m ); }; fromProtectedMembers.forEach( m => insertMember( m ) ); fromPublicMembers.forEach( m => insertMember( m ) ); let allMembers:Member[] = [].concat( protectedMembers ).concat( publicMembers ); let protectedHeader = protectedMembers.map( m => m.getHeaderDefinition() ).join( "\n " ); let publicHeader = publicMembers.map( m => m.getHeaderDefinition() ).join( "\n " ); let fields:Field[] = allMembers.filter( m => m.memberType === MemberType.Field ) as Field[]; let methods:Method[] = allMembers.filter( m => m.memberType === MemberType.Method ) as Method[]; let fieldDeclarations = fields.map( f => f.getFieldDeclaration() ).join( "\n " ); protectedHeader += "\n\n " + fieldDeclarations; let methodBindings = allMembers.map( m => m.getBindings( className ) ).join( "\n " ); let initializers = fields.filter( m => m.hasInitializer ).map( m => m.getInitializer() ).join( "\n "); let includes = `#include "./RJGodotHeaders.h"`; if ( data.includes ) { let mappedIncludes = data.includes.map( ( inc:string ) => `#include "${inc}"` ); includes += "\n" + mappedIncludes.join( "\n" ); } let extendingClassInclude = CppCreator.createIncludes( extendingClass ); includes += "\n" + extendingClassInclude; let forwards = ""; if ( data.forwards ) { let forwardDeclarations = data.forwards as string[]; forwards = forwardDeclarations.join( "\n" ); } let header = Header.create( className, extendingClass, headerDefine, protectedHeader, publicHeader, includes, forwards ); let constructorExpressions = initializers; let destructorExpressions = ""; let implementations = ""; let fieldImplementations = fields.map( f => f.getFieldImplementation( className ) ); implementations += fieldImplementations.join( "\n" ) + "\n"; let methodsWithImplementation = methods.filter( m => m.hasMethodImplementation ); let methodImplementations = methodsWithImplementation.map( m => m.getMethodImplementation( className ) ); implementations += methodImplementations.join( "\n" ); let implementation = Implementation.craete( className, methodBindings, constructorExpressions, destructorExpressions, implementations ); let rawFilePath = Texts.joinPaths( CppCreator.outputPath, className ); await Files.saveUTF8( rawFilePath + ".h", header ); await Files.saveUTF8( rawFilePath + ".cpp", implementation ); } }