mirror of
https://github.com/raylib-cs/raylib-cs
synced 2025-04-05 11:19:39 -04:00
- Reviewing generator contribution. Work in progress.
This commit is contained in:
parent
7871e2ea1a
commit
1236e4979a
@ -1,13 +1,21 @@
|
||||
using System;
|
||||
/* Raylib-cs
|
||||
* Program.cs - Generator for creating pinvoke bindings from raylib headers.
|
||||
* Copyright 2019
|
||||
*
|
||||
* Release under zLib License.
|
||||
* See LICENSE for details.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.Diagnostics;
|
||||
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
||||
|
||||
namespace Generator
|
||||
{
|
||||
@ -16,6 +24,7 @@ namespace Generator
|
||||
public string Name;
|
||||
public Type Type;
|
||||
public int Offset;
|
||||
|
||||
public TypeMember(string name, Type type, int offset)
|
||||
{
|
||||
Name = name;
|
||||
@ -28,6 +37,7 @@ namespace Generator
|
||||
{
|
||||
public string Name;
|
||||
public List<KeyValuePair<string, int>> Enums;
|
||||
|
||||
public UserDefinedEnumData(string name)
|
||||
{
|
||||
Name = name;
|
||||
@ -47,7 +57,6 @@ namespace Generator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct Function
|
||||
{
|
||||
public string Name;
|
||||
@ -57,13 +66,11 @@ namespace Generator
|
||||
public Function(string name, string returnType, params FunctionParam[] parameters)
|
||||
{
|
||||
var Isreturnpointer = name[0] == '*';
|
||||
Name = Isreturnpointer ? name.Replace("*","") : name;
|
||||
Name = Isreturnpointer ? name.Replace("*", "") : name;
|
||||
ReturnType = new Type(returnType, Isreturnpointer);
|
||||
Params = new List<FunctionParam>(parameters);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var str = $"{ReturnType} {Name}(";
|
||||
@ -88,7 +95,6 @@ namespace Generator
|
||||
IsPointer = isPointer;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name + (IsPointer == true ? "*" : "");
|
||||
@ -108,6 +114,13 @@ namespace Generator
|
||||
|
||||
public FunctionParam(string FullParam)
|
||||
{
|
||||
if (FullParam == "")
|
||||
{
|
||||
Name = "";
|
||||
Type = new Type();
|
||||
return;
|
||||
}
|
||||
|
||||
FullParam = FullParam.Trim();
|
||||
if (FullParam.Split(' ').Length == 2)
|
||||
{
|
||||
@ -118,7 +131,7 @@ namespace Generator
|
||||
else
|
||||
{
|
||||
var Isreturnpointer = FullParam.Split(' ')[2][0] == '*';
|
||||
Name = Isreturnpointer ? FullParam.Replace("*","").Split(' ')[2] : FullParam.Split(' ')[2];
|
||||
Name = Isreturnpointer ? FullParam.Replace("*", "").Split(' ')[2] : FullParam.Split(' ')[2];
|
||||
Type = new Type(FullParam.Replace("*", "").Split(' ')[0] + " " + FullParam.Replace("*", "").Split(' ')[1], Isreturnpointer);
|
||||
}
|
||||
}
|
||||
@ -129,46 +142,116 @@ namespace Generator
|
||||
}
|
||||
}
|
||||
|
||||
struct SyntaxData
|
||||
{
|
||||
public string tag;
|
||||
public List<string> functions;
|
||||
public HashSet<string> types;
|
||||
public HashSet<string> enums;
|
||||
public List<Function> funcs;
|
||||
public List<string> comments;
|
||||
public Dictionary<string, UserDefinedTypeData> tps;
|
||||
public Dictionary<string, UserDefinedEnumData> eps;
|
||||
|
||||
public SyntaxData(string _tag)
|
||||
{
|
||||
tag = _tag;
|
||||
functions = new List<string>();
|
||||
types = new HashSet<string>();
|
||||
enums = new HashSet<string>();
|
||||
funcs = new List<Function>();
|
||||
comments = new List<string>();
|
||||
tps = new Dictionary<string, UserDefinedTypeData>();
|
||||
eps = new Dictionary<string, UserDefinedEnumData>();
|
||||
}
|
||||
}
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var process = Process.Start(new ProcessStartInfo("Dia2Dump.exe", " -t raylib.pdb") { RedirectStandardOutput = true, UseShellExecute = false });
|
||||
|
||||
string typedata = "";
|
||||
var ischkout = process.StandardOutput;
|
||||
|
||||
while (process.HasExited == false)
|
||||
{
|
||||
typedata += ischkout.ReadToEnd();
|
||||
}
|
||||
File.WriteAllText("types.txt", typedata);
|
||||
var typesfile = File.ReadAllLines("types.txt");
|
||||
|
||||
var TypeMap = new Dictionary<string, string>();
|
||||
typesfile = typesfile.Where(x => x != "").ToArray();
|
||||
|
||||
var sources = new KeyValuePair<string, string>[] { new KeyValuePair<string, string>( "raylib.h","RLAPI") , new KeyValuePair<string, string>("physac.h", "PHYSACDEF"),
|
||||
new KeyValuePair<string, string>( "easings.h","EASEDEF"),new KeyValuePair<string, string>( "raygui.h","RAYGUIDEF")};
|
||||
var sources = new KeyValuePair<string, string>[] {
|
||||
new KeyValuePair<string, string>( "raylib.h","RLAPI"),
|
||||
// new KeyValuePair<string, string>("physac.h", "PHYSACDEF"),
|
||||
// new KeyValuePair<string, string>( "easings.h","EASEDEF"),
|
||||
// new KeyValuePair<string, string>( "raygui.h","RAYGUIDEF")
|
||||
};
|
||||
|
||||
foreach (var sourcefilenameandexporttag in sources)
|
||||
{
|
||||
var functions = new List<string>();
|
||||
var types = new HashSet<string>();
|
||||
var enums = new HashSet<string>();
|
||||
var Funcs = new List<Function>();
|
||||
var tps = new Dictionary<string, UserDefinedTypeData>();
|
||||
var eps = new Dictionary<string, UserDefinedEnumData>();
|
||||
|
||||
var sourcefilename = sourcefilenameandexporttag.Key;
|
||||
var FileName = new CultureInfo("en-us", false).TextInfo.ToTitleCase(sourcefilename.Replace(".h", ""));
|
||||
var ExportTag = sourcefilenameandexporttag.Value;
|
||||
var sourcefile = File.ReadAllLines(sourcefilename);
|
||||
|
||||
var syntax = GetSyntax(sourcefile, ExportTag, TypeMap, typesfile);
|
||||
GenerateBinding(syntax, FileName);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Takes the source from a raylib module and stores the syntax data
|
||||
static SyntaxData GetSyntax(string[] sourcefile, string tag, Dictionary<string, string> TypeMap, string[] typesfile)
|
||||
{
|
||||
var syntax = new SyntaxData(tag);
|
||||
|
||||
// local references
|
||||
var ExportTag = syntax.tag;
|
||||
var comments = syntax.comments;
|
||||
var functions = syntax.functions;
|
||||
var enums = syntax.enums;
|
||||
var Funcs = syntax.funcs;
|
||||
var types = syntax.types;
|
||||
var tps = syntax.tps;
|
||||
var eps = syntax.eps;
|
||||
|
||||
for (int i = 0; i < sourcefile.Length; i++)
|
||||
{
|
||||
var source = sourcefile[i].Split("//".ToCharArray())[0].Trim();
|
||||
|
||||
// Comments test
|
||||
if (source.StartsWith("*") || source.StartsWith("//"))
|
||||
{
|
||||
comments.Add(source);
|
||||
}
|
||||
|
||||
if (source.Contains(ExportTag))
|
||||
{
|
||||
if (!source.Contains("#define"))
|
||||
{
|
||||
source = source.TrimStart(ExportTag.ToCharArray()).Trim();
|
||||
if (!source.Contains("{")) functions.Add(source);
|
||||
if (!source.Contains("{"))
|
||||
{
|
||||
// Some functions go across multiple lines
|
||||
if (source[source.Length - 1] == ',')
|
||||
{
|
||||
i++;
|
||||
var nextLine = sourcefile[i].Split("//".ToCharArray())[0].Trim();
|
||||
source += " " + nextLine;
|
||||
}
|
||||
|
||||
functions.Add(source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,17 +274,20 @@ namespace Generator
|
||||
}
|
||||
types.Add(src.Trim('}', ';').Trim());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Break down function string
|
||||
for (int i = 0; i < functions.Count; i++)
|
||||
{
|
||||
var func = functions[i];
|
||||
var returntype = func.Split('(')[0].Split(' ').Length == 3 ? func.Split('(')[0].Split(' ')[0] + " " + func.Split('(')[0].Split(' ')[1] : func.Split('(')[0].Split(' ')[0];
|
||||
|
||||
types.Add(returntype);
|
||||
func = func.Substring(returntype.Length).Trim();
|
||||
|
||||
//@TODO extra comma in param list
|
||||
var funcname = func.Split('(')[0].Trim();
|
||||
var Func = new Function(funcname, returntype);
|
||||
if (func.Contains(','))
|
||||
@ -217,7 +303,6 @@ namespace Generator
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
var TypeAndVar = Param.Trim().Split(' ');
|
||||
if (TypeAndVar.Length == 2)
|
||||
{
|
||||
@ -233,7 +318,6 @@ namespace Generator
|
||||
}
|
||||
|
||||
Func.Params.Add(new FunctionParam(Param));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -243,6 +327,7 @@ namespace Generator
|
||||
var TypeAndVar = Param.Trim().Split(' ');
|
||||
var type = TypeAndVar[0];
|
||||
var Var = TypeAndVar[1];
|
||||
|
||||
types.Add(type);
|
||||
Func.Params.Add(new FunctionParam(Param));
|
||||
}
|
||||
@ -290,7 +375,6 @@ namespace Generator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < Funcs.Count; i++)
|
||||
{
|
||||
var Func = Funcs[i];
|
||||
@ -298,6 +382,7 @@ namespace Generator
|
||||
{
|
||||
Func.ReturnType.Name = TypeMap[Func.ReturnType.Name];
|
||||
}
|
||||
|
||||
for (int t = 0; t < Func.Params.Count; t++)
|
||||
{
|
||||
if (TypeMap.ContainsKey(Func.Params[t].Type.Name))
|
||||
@ -308,6 +393,7 @@ namespace Generator
|
||||
}
|
||||
Funcs[i] = Func;
|
||||
}
|
||||
|
||||
for (int i = 0; i < typesfile.Length; i++)
|
||||
{
|
||||
var typestr = typesfile[i];
|
||||
@ -319,6 +405,7 @@ namespace Generator
|
||||
var usertype = new UserDefinedTypeData(type);
|
||||
int t = 1;
|
||||
typestr = typesfile[i + t];
|
||||
|
||||
while (!(typestr.Contains("UserDefinedType: ") && typestr["UserDefinedType: ".Length - 1] == ' '))
|
||||
{
|
||||
if (typestr.Contains("Member"))
|
||||
@ -327,17 +414,21 @@ namespace Generator
|
||||
var MemberNameandType = tpsrc.Substring(tpsrc.IndexOf("Type:") + "Type:".Length).Trim().Split(',');
|
||||
var MemberName = MemberNameandType[1].Trim();
|
||||
var MemberType = MemberNameandType[0].Trim();
|
||||
|
||||
if (MemberType.Contains("<unnamed-enum-false>"))
|
||||
{
|
||||
MemberType = "bool";
|
||||
}
|
||||
|
||||
var isptr = MemberType[MemberType.Length - 1] == '*';
|
||||
var offset = int.Parse(tpsrc.Split(',')[0].Split(':')[1].Trim().Substring("this+0x".Length), System.Globalization.NumberStyles.HexNumber);
|
||||
|
||||
if (isptr)
|
||||
{
|
||||
MemberType = MemberType.Remove(MemberType.Length - 1);
|
||||
}
|
||||
usertype.Members.Add(new TypeMember(MemberName, new Type(MemberType, isptr), offset));
|
||||
|
||||
if (typesfile[i + t + 1].Contains(MemberType.Trim("struct".ToCharArray())) &&
|
||||
typesfile[i + t + 1].Contains("UserDefinedType: "))
|
||||
{
|
||||
@ -350,6 +441,7 @@ namespace Generator
|
||||
tps.Add(usertype.Name, usertype);
|
||||
}
|
||||
}
|
||||
|
||||
if (typestr.Contains("Enum : "))
|
||||
{
|
||||
var Enum = typestr.Substring("Enum : ".Length).Split(',')[0].Trim();
|
||||
@ -358,6 +450,7 @@ namespace Generator
|
||||
var UserDefineEnum = new UserDefinedEnumData(Enum);
|
||||
int t = 1;
|
||||
typestr = typesfile[i + t];
|
||||
|
||||
while (typestr.Contains("Constant"))
|
||||
{
|
||||
var intstr = typestr.Split(",".ToCharArray(), 2)[0].Split(':')[1].Split(' ')[4].Substring("0x".Length).Trim();
|
||||
@ -373,24 +466,38 @@ namespace Generator
|
||||
}
|
||||
types.IntersectWith(tps.Select(x => x.Key));
|
||||
|
||||
return syntax;
|
||||
}
|
||||
|
||||
// Takes in SyntaxData and creates a .cs binding file
|
||||
static void GenerateBinding(SyntaxData syntax, string fileName)
|
||||
{
|
||||
// local references
|
||||
var ExportTag = syntax.tag;
|
||||
var comments = syntax.comments;
|
||||
var functions = syntax.functions;
|
||||
var enums = syntax.enums;
|
||||
var Funcs = syntax.funcs;
|
||||
var types = syntax.types;
|
||||
var tps = syntax.tps;
|
||||
var eps = syntax.eps;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Begin generating bindings here using SyntaxTree
|
||||
// ----------------------------------------------------------------------------
|
||||
var codetree = CompilationUnit().AddUsings(UsingDirective(ParseName("System")))
|
||||
.AddUsings(UsingDirective(ParseName("System.IO")))
|
||||
.AddUsings(UsingDirective(ParseName("System.Collections.Generic")))
|
||||
.AddUsings(UsingDirective(ParseName("System.Security")))
|
||||
.AddUsings(UsingDirective(ParseName("System.Runtime.InteropServices")))
|
||||
;
|
||||
.AddUsings(UsingDirective(ParseName("System.Runtime.InteropServices")));
|
||||
|
||||
var Raylibnamespace = NamespaceDeclaration(ParseName("Raylib")).NormalizeWhitespace();
|
||||
|
||||
foreach (var Enumtype in eps.Values)
|
||||
{
|
||||
var Enum = EnumDeclaration(Enumtype.Name)
|
||||
.WithModifiers(
|
||||
TokenList(
|
||||
Token(SyntaxKind.PublicKeyword)));
|
||||
var Enum = EnumDeclaration(Enumtype.Name).WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)));
|
||||
foreach (var Member in Enumtype.Enums)
|
||||
{
|
||||
|
||||
var enummember = EnumMemberDeclaration(
|
||||
Identifier(Member.Key))
|
||||
.WithEqualsValue(
|
||||
@ -402,6 +509,7 @@ namespace Generator
|
||||
}
|
||||
Raylibnamespace = Raylibnamespace.AddMembers(Enum);
|
||||
}
|
||||
|
||||
foreach (var Type in tps.Values)
|
||||
{
|
||||
var Struct = StructDeclaration(Type.Name).WithAttributeLists(
|
||||
@ -431,8 +539,11 @@ namespace Generator
|
||||
.WithModifiers(
|
||||
TokenList(
|
||||
Token(SyntaxKind.PublicKeyword)));
|
||||
|
||||
|
||||
bool IsUnsafe = false;
|
||||
var FixedStructTypes = new List<string>();
|
||||
|
||||
foreach (var Member in Type.Members)
|
||||
{
|
||||
var IsStruct = false;
|
||||
@ -458,7 +569,6 @@ namespace Generator
|
||||
TypeName = TypeName.Replace("unsigned char", "byte");
|
||||
}
|
||||
|
||||
|
||||
var IsFixed = false;
|
||||
var VariableDec = VariableDeclarator(Member.Name);
|
||||
if (TypeName.Contains("["))
|
||||
@ -554,17 +664,19 @@ namespace Generator
|
||||
IsUnsafe = true;
|
||||
}
|
||||
}
|
||||
|
||||
var variable = VariableDeclaration(ParseTypeName(TypeName)).AddVariables(VariableDec);
|
||||
var field = FieldDeclaration(variable).AddModifiers(Token(SyntaxKind.PublicKeyword));
|
||||
if (IsFixed) field = field.AddModifiers(Token(SyntaxKind.FixedKeyword));
|
||||
Struct = Struct.AddMembers(field);
|
||||
}
|
||||
|
||||
if (IsUnsafe) Struct = Struct.AddModifiers(Token(SyntaxKind.UnsafeKeyword));
|
||||
Raylibnamespace = Raylibnamespace.AddMembers(Struct);
|
||||
}
|
||||
|
||||
{
|
||||
var RaylibClass = ClassDeclaration(FileName)
|
||||
var RaylibClass = ClassDeclaration(fileName)
|
||||
.WithAttributeLists(
|
||||
SingletonList<AttributeListSyntax>(
|
||||
AttributeList(
|
||||
@ -631,7 +743,7 @@ namespace Generator
|
||||
Token(SyntaxKind.ExternKeyword)
|
||||
})).WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
|
||||
|
||||
for (int i=0; i < Func.Params.Count;i++)
|
||||
for (int i = 0; i < Func.Params.Count; i++)
|
||||
{
|
||||
var Param = Func.Params[i];
|
||||
var TypeName = (Param.Type.IsPointer ? "IntPtr" : Param.Type.Name).Trim();
|
||||
@ -668,12 +780,10 @@ namespace Generator
|
||||
}
|
||||
codetree = codetree.AddMembers(Raylibnamespace);
|
||||
Console.WriteLine(codetree.NormalizeWhitespace().ToFullString());
|
||||
File.WriteAllText($"{FileName}.cs", codetree.NormalizeWhitespace().ToFullString());
|
||||
File.WriteAllText($"{fileName}.cs", codetree.NormalizeWhitespace().ToFullString());
|
||||
|
||||
}
|
||||
Console.WriteLine("Finished generating bindings for file ");
|
||||
Console.ReadLine();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user