rokojori-godot/rokojori-cpp-generator/source/cpp-creation/CppCreator.ts

222 lines
6.6 KiB
TypeScript

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<CppDefinition>( 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 );
}
}