355 lines
8.3 KiB
JavaScript
355 lines
8.3 KiB
JavaScript
const fs = require( "node:fs/promises" );
|
|
const path = require( "path" );
|
|
const { loadJSON, getFiles, saveUTF8, getMatches, loadUTF8, insertText, isDirectory } = require( "./library.js" );
|
|
const { CppClassMember } = require( "./cpp-class-member.js" );
|
|
|
|
function createCppHeader( className, extendingClassName, headerDefine, protectedMembers,
|
|
publicMembers, includes )
|
|
{
|
|
|
|
let cppHeader =
|
|
|
|
`
|
|
/* ${className}.h */
|
|
|
|
#ifndef ${headerDefine}
|
|
#define ${headerDefine}
|
|
|
|
${includes}
|
|
|
|
|
|
class ${className} : public ${extendingClassName}
|
|
{
|
|
GDCLASS(${className}, ${extendingClassName});
|
|
|
|
protected:
|
|
static void _bind_methods();
|
|
${protectedMembers}
|
|
|
|
public:
|
|
|
|
${publicMembers}
|
|
|
|
${className}();
|
|
|
|
~${className}();
|
|
};
|
|
|
|
|
|
#endif // ${headerDefine}
|
|
`
|
|
|
|
return cppHeader;
|
|
|
|
|
|
}
|
|
|
|
function createCppImplementation( className, boundMethods, constructorExpressions, destructorExpressions, methodImplementations )
|
|
{
|
|
|
|
let cppImplementation =
|
|
`
|
|
/* ${className}.cpp */
|
|
|
|
#include "${className}.h"
|
|
|
|
|
|
void ${className}::_bind_methods()
|
|
{
|
|
${boundMethods}
|
|
}
|
|
|
|
${className}::${className}()
|
|
{
|
|
${constructorExpressions}
|
|
}
|
|
|
|
${className}::~${className}()
|
|
{
|
|
${destructorExpressions}
|
|
}
|
|
|
|
${methodImplementations}
|
|
|
|
`
|
|
|
|
return cppImplementation;
|
|
|
|
}
|
|
|
|
let definitionsPath = __dirname + "/rokojori-action-library-definitions";
|
|
let outputPath = __dirname + "/rokojori-action-library";
|
|
let registerTypesPath = outputPath + "/register_types.cpp";
|
|
let classRegistrationRegex = /ClassDB\:\:register_class\<(\w+)\>\(\)\;/;
|
|
|
|
function createTypeRegistration( className )
|
|
{
|
|
return `ClassDB::register_class<${className}>();`;
|
|
}
|
|
|
|
function createIncludes( className )
|
|
{
|
|
console.log( "Create include", className );
|
|
|
|
let godotClasses =
|
|
{
|
|
"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"`;
|
|
}
|
|
|
|
async function main()
|
|
{
|
|
let files = await getFiles( definitionsPath );
|
|
files = files.filter( f => ! f.startsWith( "_" ) );
|
|
|
|
let typesFile = await loadUTF8( registerTypesPath );
|
|
|
|
let types = getRegistratedTypes( typesFile );
|
|
|
|
let missingTypes = [];
|
|
|
|
for ( let file of files )
|
|
{
|
|
await createCppFiles( path.join( definitionsPath, file ), types, missingTypes );
|
|
}
|
|
|
|
if ( missingTypes.length === 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
console.log( "Registrating missing types:", missingTypes );
|
|
|
|
let additions = missingTypes.map( mt => createTypeRegistration( mt ) );
|
|
let additionsBlock = additions.join( "\n " );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let lastType = types[ types.length -1 ];
|
|
let insertPosition = lastType.index + lastType.match.length;
|
|
let updatedTypesFile = insertText( typesFile, "\n " + additionsBlock, insertPosition );
|
|
|
|
|
|
let includeMatches = getMatches( updatedTypesFile, /#include "(\w|\.|\/|\\)+.h"/ );
|
|
let lastInclude = includeMatches[ includeMatches.length - 1 ];
|
|
let includes = missingTypes.map( mt => createIncludes( mt ) ).join( "\n" );
|
|
insertPosition = lastInclude.index + lastInclude.match.length;
|
|
updatedTypesFile = insertText( updatedTypesFile, "\n" + includes, insertPosition );
|
|
|
|
saveUTF8( registerTypesPath, updatedTypesFile );
|
|
|
|
}
|
|
|
|
|
|
|
|
function grabMembers( membersData, accessModifier )
|
|
{
|
|
let members = [];
|
|
|
|
if ( ! membersData )
|
|
{
|
|
return members;
|
|
}
|
|
|
|
for ( let it in membersData )
|
|
{
|
|
if ( ! membersData.hasOwnProperty( it ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
let nameDefinition = it;
|
|
let memberData = membersData[ it ];
|
|
|
|
let actionRegex = /^\s*action\s+(\w+)\s*$/;
|
|
|
|
if ( actionRegex.test( nameDefinition ) )
|
|
{
|
|
let name = actionRegex.exec( nameDefinition )[ 1 ];
|
|
let upperCaseName = name[ 0 ].toUpperCase() + name.substring( 1 );
|
|
let actionName = `on${upperCaseName}`;
|
|
|
|
let signal = `signal ${name}()`;
|
|
let actionGetter = `virtual get_${actionName}():Ref<RJAction>`;
|
|
let actionSetter = `virtual set_${actionName}()`;
|
|
|
|
members.push( new CppClassMember( accessModifier, signal, {} ) );
|
|
members.push( new CppClassMember( "protected", "m_" + name, {} ) );
|
|
members.push( new CppClassMember( accessModifier, actionGetter, {} ) );
|
|
members.push( new CppClassMember( accessModifier, actionSetter, { "action":"Ref<RJAction>"} ) );
|
|
}
|
|
else
|
|
{
|
|
let classMember = new CppClassMember( accessModifier, nameDefinition, memberData );
|
|
members.push( classMember );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
return members;
|
|
}
|
|
|
|
function getHeaderDefine( className )
|
|
{
|
|
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";
|
|
}
|
|
|
|
function getRegistratedTypes( text )
|
|
{
|
|
let matches = getMatches( text, classRegistrationRegex );
|
|
|
|
let types = matches.map(
|
|
m =>
|
|
{
|
|
return { match: m.match, index: m.index, type: classRegistrationRegex.exec( m.match )[ 1 ] }
|
|
}
|
|
);
|
|
|
|
return types;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function createCppFiles( definitionPath, types, missingTypes )
|
|
{
|
|
console.log( "Creating: ", definitionPath );
|
|
|
|
let jsonPath = definitionPath;
|
|
// path.join( definitionsPath, definitionPath );
|
|
|
|
let isDir = await isDirectory( jsonPath );
|
|
|
|
if ( isDir )
|
|
{
|
|
let files = await getFiles( jsonPath );
|
|
|
|
for ( let file of files )
|
|
{
|
|
await createCppFiles( path.join( jsonPath, file ), types, missingTypes );
|
|
}
|
|
|
|
return Promise.resolve();
|
|
}
|
|
|
|
let data = await loadJSON( jsonPath );
|
|
|
|
//console.log( "data:", jsonPath, data );
|
|
|
|
let classNameResult = /(\w+)(?:\:(\w+))?/.exec( data.class );
|
|
|
|
let className = classNameResult[ 1 ];
|
|
let extendingClass = classNameResult[ 2 ];
|
|
|
|
let headerDefine = getHeaderDefine( className );
|
|
|
|
let fromProtectedMembers = grabMembers( data.protected, "protected" );
|
|
let fromPublicMembers = grabMembers( data.public, "public" );
|
|
|
|
let protectedMembers = [];
|
|
let publicMembers = [];
|
|
|
|
let insertMember = m =>
|
|
{
|
|
let container = m.accessModifier === "public" ? publicMembers : protectedMembers;
|
|
container.push( m );
|
|
};
|
|
|
|
fromProtectedMembers.forEach( m => insertMember( m ) );
|
|
fromPublicMembers.forEach( m => insertMember( m ) );
|
|
|
|
|
|
let allMembers = [].concat( protectedMembers ).concat( publicMembers );
|
|
|
|
|
|
let protectedHeader = protectedMembers.map( m => m.getHeaderDefinition() ).join( "\n " );
|
|
let publicHeader = publicMembers.map( m => m.getHeaderDefinition() ).join( "\n " );
|
|
|
|
let properties = publicMembers.filter( m => ! m.isMethod );
|
|
|
|
let propertyDefinitions = properties.map( p => p.getPropertyHeaderDefinition() ).join( "\n " );
|
|
|
|
protectedHeader += "\n\n " + propertyDefinitions;
|
|
|
|
let methodBindings = allMembers.map( m => m.getMethodBinding( className ) ).join( "\n " );
|
|
|
|
let initializers = allMembers.filter( m => m.isMemberWithInitialValue ).map( m => m.getMemberInitializer() ).join( "\n ");
|
|
|
|
let includes = "";
|
|
|
|
if ( data.includes )
|
|
{
|
|
includes = data.includes.map( inc => `#include "${inc}"` );
|
|
}
|
|
|
|
let extendingClassInclude = createIncludes( extendingClass );
|
|
|
|
includes += "\n" + extendingClassInclude;
|
|
let header = createCppHeader( className, extendingClass, headerDefine, protectedHeader, publicHeader, includes );
|
|
|
|
let constructorExpressions = initializers;
|
|
let destructorExpressions = "";
|
|
let methodImplementations = "";
|
|
|
|
let propertyImplementations = properties.map( p => p.getPropertyImplementation( className ) ).join( "\n" );
|
|
|
|
methodImplementations += propertyImplementations;
|
|
|
|
let implementation = createCppImplementation( className, methodBindings, constructorExpressions, destructorExpressions, methodImplementations );
|
|
|
|
//console.log( header );
|
|
//console.log( implementation );
|
|
|
|
let hasType = types.find( t => t.type === className );
|
|
|
|
if ( ! hasType )
|
|
{
|
|
missingTypes.push( className );
|
|
}
|
|
|
|
let rawFilePath = path.join( outputPath, className );
|
|
await saveUTF8( rawFilePath + ".h", header );
|
|
await saveUTF8( rawFilePath + ".cpp", implementation );
|
|
|
|
return Promise.resolve();
|
|
}
|
|
|
|
|
|
|
|
main(); |