262 lines
6.7 KiB
C#
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 );
|
|
}
|
|
}
|
|
}
|
|
} |