rj-action-library/Tools/docs/ShaderDocGenerator.cs

262 lines
6.7 KiB
C#

using Godot;
using Rokojori;
using System.Collections.Generic;
using System;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Linq;
namespace Rokojori.DocGenerator
{
public class ShaderDocGenerator
{
public static readonly string GDShaderExtension = ".gdshader";
public static readonly string GDShaderIncludeExtension = ".gdshaderinc";
ClassDocInfo info = new ClassDocInfo();
List<LexerEvent> _lexerEvents;
LexerList tokens;
public ClassDocInfo Create( string filePath )
{
var fileContent = FilesSync.LoadUTF8( filePath );
_lexerEvents = GDShaderLexer.Lex( fileContent );
tokens = LexerList.Create( _lexerEvents );
if ( filePath.EndsWith( GDShaderIncludeExtension ) )
{
info.definitionType = "GDShaderInclude";
}
else
{
info.definitionType = "GDShader";
}
info.name = RegexUtility.TrimToLastPathFragment( filePath ).ReplaceEnd( GDShaderIncludeExtension, "" ).ReplaceEnd( GDShaderExtension, "" );
GetIncludes();
GetUniforms();
GetBlocks();
return info;
}
void GetIncludes()
{
var includePrexix = "#include ";
RJLog.Log( "Processing includes:", tokens.GetAll( LexerMatcherLibrary.CInstructionMatcher ).events.Count );
tokens.ForEach( LexerMatcherLibrary.CInstructionMatcher,
( t )=>
{
var match = t.match;
RJLog.Log( "Processing include:", match );
if ( ! match.StartsWith( includePrexix ) )
{
RJLog.Log( "Processing include:", match );
return;
}
var path = match.ReplaceStart( includePrexix, "" );
path = path.Substring( 1, path.Length - 2 );
var name = RegexUtility.TrimToLastPathFragment( path ).TrimSuffix( ".gdshaderinc");
var memberInfo = MemberInfo.Create( "ShaderInclude" );
memberInfo.name = name;
memberInfo.path = path;
memberInfo.dataType = "GDShaderInclude";
info.memberInfos.Add( memberInfo );
}
);
}
void GetUniforms()
{
var sequences = tokens.FindSequences(
( int tokenIndex, bool inSequence ) =>
{
var token = tokens.events[ tokenIndex ];
if ( ! inSequence )
{
if ( token.Is( GDShaderLexer.UsageMatcher ) )
{
return Trillean.True;
}
return Trillean.Any;
}
else
{
return TrilleanLogic.FromBool( ! token.Is( LexerMatcherLibrary.OperatorMatcher, ";" ) );
}
}
);
// RJLog.Log( "Uniforms/Varyings:", sequences.Count );
sequences.ForEach(
( s )=>
{
if ( s == null )
{
return;
}
var filtered = s.Filter( s => ! s.IsAnyOf( GDShaderLexer.ignore ) );
// RJLog.Log( "'" + filtered.Map( s => s.match ).Join( "', '") + "'" );
if ( filtered.Count < 3 )
{
return;
}
var first = filtered[ 0 ];
var isUniform = first.MatchIs( "uniform" );
var memberInfo = isUniform ? MemberInfo.CreateUniform() : MemberInfo.CreateVarying();
memberInfo.dataType = filtered[ 1 ].match;
memberInfo.name = filtered[ 2 ].match;
// LINE INFO
info.memberInfos.Add( memberInfo );
}
);
}
void GetBlocks()
{
var blocks = tokens.GetBlocks();
if ( blocks == null )
{
return;
}
// RJLog.Log( "Num blocks:", blocks.Count );
blocks.ForEach( b => ProcessBlock( b ) );
}
void ProcessBlock( RangeI blockRange )
{
var blockStart = blockRange.min;
var firstTokenBeforeResult = tokens.ReverseFind( blockStart - 1,
( t ) =>
{
// RJLog.Log( "Token type:", t );
if ( t.IsAnyOf( GDShaderLexer.ignore ) )
{
return LexerEvent.FindResultType.KeepSearching;
}
if ( !
(
t.Is( LexerMatcherLibrary.BracketMatcher, ")" ) ||
t.Is( LexerMatcherLibrary.CwordMatcher )
)
)
{
// RJLog.Log( "Invalid token type:", t );
return LexerEvent.FindResultType.Error;
}
return LexerEvent.FindResultType.Found;
}
);
if ( ! firstTokenBeforeResult.found )
{
// RJLog.Log( "No first token before block found!" );
return;
}
var firstToken = _lexerEvents[ firstTokenBeforeResult.index ];
var isMethod = firstToken.Is( LexerMatcherLibrary.BracketMatcher, ")" );
if ( isMethod )
{
var closeBracketResult = firstTokenBeforeResult;
var openBracketResult = tokens.ReverseFindOpeningBracket( closeBracketResult.index );
if ( ! openBracketResult.found )
{
return;
}
var nameToken = tokens.ReverseFind( openBracketResult.index,
( t ) =>
{
return t.Is( LexerMatcherLibrary.CFunctionMatcher ) ? LexerEvent.FindResultType.Found : LexerEvent.FindResultType.KeepSearching;
}
);
if ( ! nameToken.found )
{
return;
}
var typeToken = tokens.ReverseFind( nameToken.index - 1,
( t ) =>
{
return t.Is( LexerMatcherLibrary.CwordMatcher ) ? LexerEvent.FindResultType.Found : LexerEvent.FindResultType.KeepSearching;
}
);
var memberInfo = MemberInfo.CreateMethod();
memberInfo.name = tokens.events[ nameToken.index ].match;
memberInfo.dataType = tokens.events[ typeToken.index ].match;
var parameters = tokens.Seperate( openBracketResult.index + 1, closeBracketResult.index - 1, GDShaderLexer.ignore );
parameters.ForEach(
( p )=>
{
var pValues = tokens.Range( p ).Filter( t => ! t.IsAnyOf( GDShaderLexer.ignore ) );
var parameterType = new ParameterType();
parameterType.name = pValues.events.ReverseAt( 0 ).match;
parameterType.type = pValues.events.ReverseAt( 1 ).match;
if ( pValues.events.Count > 2 )
{
parameterType.modifiers.Add( pValues.events.ReverseAt( 2 ).match );
}
memberInfo.parameters.Add( parameterType );
}
);
info.memberInfos.Add( memberInfo );
// RJLog.Log( "Method found!", memberInfo.memberType, memberInfo.name );
}
else
{
// RJLog.Log( "No method found!", firstToken );
}
}
}
}