2
0
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:
ChrisDill 2019-04-15 16:12:29 +01:00 committed by Chris Dill
parent 7871e2ea1a
commit 1236e4979a

View File

@ -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.IO;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Globalization; using System.Globalization;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using System.Diagnostics; using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Generator namespace Generator
{ {
@ -16,6 +24,7 @@ namespace Generator
public string Name; public string Name;
public Type Type; public Type Type;
public int Offset; public int Offset;
public TypeMember(string name, Type type, int offset) public TypeMember(string name, Type type, int offset)
{ {
Name = name; Name = name;
@ -28,6 +37,7 @@ namespace Generator
{ {
public string Name; public string Name;
public List<KeyValuePair<string, int>> Enums; public List<KeyValuePair<string, int>> Enums;
public UserDefinedEnumData(string name) public UserDefinedEnumData(string name)
{ {
Name = name; Name = name;
@ -47,7 +57,6 @@ namespace Generator
} }
} }
public struct Function public struct Function
{ {
public string Name; public string Name;
@ -57,13 +66,11 @@ namespace Generator
public Function(string name, string returnType, params FunctionParam[] parameters) public Function(string name, string returnType, params FunctionParam[] parameters)
{ {
var Isreturnpointer = name[0] == '*'; var Isreturnpointer = name[0] == '*';
Name = Isreturnpointer ? name.Replace("*","") : name; Name = Isreturnpointer ? name.Replace("*", "") : name;
ReturnType = new Type(returnType, Isreturnpointer); ReturnType = new Type(returnType, Isreturnpointer);
Params = new List<FunctionParam>(parameters); Params = new List<FunctionParam>(parameters);
} }
public override string ToString() public override string ToString()
{ {
var str = $"{ReturnType} {Name}("; var str = $"{ReturnType} {Name}(";
@ -88,7 +95,6 @@ namespace Generator
IsPointer = isPointer; IsPointer = isPointer;
} }
public override string ToString() public override string ToString()
{ {
return Name + (IsPointer == true ? "*" : ""); return Name + (IsPointer == true ? "*" : "");
@ -108,6 +114,13 @@ namespace Generator
public FunctionParam(string FullParam) public FunctionParam(string FullParam)
{ {
if (FullParam == "")
{
Name = "";
Type = new Type();
return;
}
FullParam = FullParam.Trim(); FullParam = FullParam.Trim();
if (FullParam.Split(' ').Length == 2) if (FullParam.Split(' ').Length == 2)
{ {
@ -118,7 +131,7 @@ namespace Generator
else else
{ {
var Isreturnpointer = FullParam.Split(' ')[2][0] == '*'; 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); 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 class Program
{ {
static void Main(string[] args) static void Main(string[] args)
{ {
var process = Process.Start(new ProcessStartInfo("Dia2Dump.exe", " -t raylib.pdb") { RedirectStandardOutput = true, UseShellExecute = false }); var process = Process.Start(new ProcessStartInfo("Dia2Dump.exe", " -t raylib.pdb") { RedirectStandardOutput = true, UseShellExecute = false });
string typedata = ""; string typedata = "";
var ischkout = process.StandardOutput; var ischkout = process.StandardOutput;
while (process.HasExited == false) while (process.HasExited == false)
{ {
typedata += ischkout.ReadToEnd(); typedata += ischkout.ReadToEnd();
} }
File.WriteAllText("types.txt", typedata); File.WriteAllText("types.txt", typedata);
var typesfile = File.ReadAllLines("types.txt"); var typesfile = File.ReadAllLines("types.txt");
var TypeMap = new Dictionary<string, string>(); var TypeMap = new Dictionary<string, string>();
typesfile = typesfile.Where(x => x != "").ToArray(); 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"), var sources = new KeyValuePair<string, string>[] {
new KeyValuePair<string, string>( "easings.h","EASEDEF"),new KeyValuePair<string, string>( "raygui.h","RAYGUIDEF")}; 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) foreach (var sourcefilenameandexporttag in sources)
{ {
var functions = new List<string>(); var functions = new List<string>();
var types = new HashSet<string>(); var types = new HashSet<string>();
var enums = new HashSet<string>(); var enums = new HashSet<string>();
var Funcs = new List<Function>(); var Funcs = new List<Function>();
var tps = new Dictionary<string, UserDefinedTypeData>();
var eps = new Dictionary<string, UserDefinedEnumData>();
var sourcefilename = sourcefilenameandexporttag.Key; var sourcefilename = sourcefilenameandexporttag.Key;
var FileName = new CultureInfo("en-us", false).TextInfo.ToTitleCase(sourcefilename.Replace(".h", "")); var FileName = new CultureInfo("en-us", false).TextInfo.ToTitleCase(sourcefilename.Replace(".h", ""));
var ExportTag = sourcefilenameandexporttag.Value; var ExportTag = sourcefilenameandexporttag.Value;
var sourcefile = File.ReadAllLines(sourcefilename); 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++) for (int i = 0; i < sourcefile.Length; i++)
{ {
var source = sourcefile[i].Split("//".ToCharArray())[0].Trim(); 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(ExportTag))
{ {
if (!source.Contains("#define")) if (!source.Contains("#define"))
{ {
source = source.TrimStart(ExportTag.ToCharArray()).Trim(); 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()); types.Add(src.Trim('}', ';').Trim());
} }
} }
} }
} }
// Break down function string
for (int i = 0; i < functions.Count; i++) for (int i = 0; i < functions.Count; i++)
{ {
var func = functions[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]; 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); types.Add(returntype);
func = func.Substring(returntype.Length).Trim(); func = func.Substring(returntype.Length).Trim();
//@TODO extra comma in param list
var funcname = func.Split('(')[0].Trim(); var funcname = func.Split('(')[0].Trim();
var Func = new Function(funcname, returntype); var Func = new Function(funcname, returntype);
if (func.Contains(',')) if (func.Contains(','))
@ -217,7 +303,6 @@ namespace Generator
} }
else else
{ {
var TypeAndVar = Param.Trim().Split(' '); var TypeAndVar = Param.Trim().Split(' ');
if (TypeAndVar.Length == 2) if (TypeAndVar.Length == 2)
{ {
@ -233,7 +318,6 @@ namespace Generator
} }
Func.Params.Add(new FunctionParam(Param)); Func.Params.Add(new FunctionParam(Param));
} }
} }
} }
@ -243,6 +327,7 @@ namespace Generator
var TypeAndVar = Param.Trim().Split(' '); var TypeAndVar = Param.Trim().Split(' ');
var type = TypeAndVar[0]; var type = TypeAndVar[0];
var Var = TypeAndVar[1]; var Var = TypeAndVar[1];
types.Add(type); types.Add(type);
Func.Params.Add(new FunctionParam(Param)); Func.Params.Add(new FunctionParam(Param));
} }
@ -290,7 +375,6 @@ namespace Generator
} }
} }
for (int i = 0; i < Funcs.Count; i++) for (int i = 0; i < Funcs.Count; i++)
{ {
var Func = Funcs[i]; var Func = Funcs[i];
@ -298,6 +382,7 @@ namespace Generator
{ {
Func.ReturnType.Name = TypeMap[Func.ReturnType.Name]; Func.ReturnType.Name = TypeMap[Func.ReturnType.Name];
} }
for (int t = 0; t < Func.Params.Count; t++) for (int t = 0; t < Func.Params.Count; t++)
{ {
if (TypeMap.ContainsKey(Func.Params[t].Type.Name)) if (TypeMap.ContainsKey(Func.Params[t].Type.Name))
@ -308,6 +393,7 @@ namespace Generator
} }
Funcs[i] = Func; Funcs[i] = Func;
} }
for (int i = 0; i < typesfile.Length; i++) for (int i = 0; i < typesfile.Length; i++)
{ {
var typestr = typesfile[i]; var typestr = typesfile[i];
@ -319,6 +405,7 @@ namespace Generator
var usertype = new UserDefinedTypeData(type); var usertype = new UserDefinedTypeData(type);
int t = 1; int t = 1;
typestr = typesfile[i + t]; typestr = typesfile[i + t];
while (!(typestr.Contains("UserDefinedType: ") && typestr["UserDefinedType: ".Length - 1] == ' ')) while (!(typestr.Contains("UserDefinedType: ") && typestr["UserDefinedType: ".Length - 1] == ' '))
{ {
if (typestr.Contains("Member")) if (typestr.Contains("Member"))
@ -327,17 +414,21 @@ namespace Generator
var MemberNameandType = tpsrc.Substring(tpsrc.IndexOf("Type:") + "Type:".Length).Trim().Split(','); var MemberNameandType = tpsrc.Substring(tpsrc.IndexOf("Type:") + "Type:".Length).Trim().Split(',');
var MemberName = MemberNameandType[1].Trim(); var MemberName = MemberNameandType[1].Trim();
var MemberType = MemberNameandType[0].Trim(); var MemberType = MemberNameandType[0].Trim();
if (MemberType.Contains("<unnamed-enum-false>")) if (MemberType.Contains("<unnamed-enum-false>"))
{ {
MemberType = "bool"; MemberType = "bool";
} }
var isptr = MemberType[MemberType.Length - 1] == '*'; var isptr = MemberType[MemberType.Length - 1] == '*';
var offset = int.Parse(tpsrc.Split(',')[0].Split(':')[1].Trim().Substring("this+0x".Length), System.Globalization.NumberStyles.HexNumber); var offset = int.Parse(tpsrc.Split(',')[0].Split(':')[1].Trim().Substring("this+0x".Length), System.Globalization.NumberStyles.HexNumber);
if (isptr) if (isptr)
{ {
MemberType = MemberType.Remove(MemberType.Length - 1); MemberType = MemberType.Remove(MemberType.Length - 1);
} }
usertype.Members.Add(new TypeMember(MemberName, new Type(MemberType, isptr), offset)); usertype.Members.Add(new TypeMember(MemberName, new Type(MemberType, isptr), offset));
if (typesfile[i + t + 1].Contains(MemberType.Trim("struct".ToCharArray())) && if (typesfile[i + t + 1].Contains(MemberType.Trim("struct".ToCharArray())) &&
typesfile[i + t + 1].Contains("UserDefinedType: ")) typesfile[i + t + 1].Contains("UserDefinedType: "))
{ {
@ -350,6 +441,7 @@ namespace Generator
tps.Add(usertype.Name, usertype); tps.Add(usertype.Name, usertype);
} }
} }
if (typestr.Contains("Enum : ")) if (typestr.Contains("Enum : "))
{ {
var Enum = typestr.Substring("Enum : ".Length).Split(',')[0].Trim(); var Enum = typestr.Substring("Enum : ".Length).Split(',')[0].Trim();
@ -358,6 +450,7 @@ namespace Generator
var UserDefineEnum = new UserDefinedEnumData(Enum); var UserDefineEnum = new UserDefinedEnumData(Enum);
int t = 1; int t = 1;
typestr = typesfile[i + t]; typestr = typesfile[i + t];
while (typestr.Contains("Constant")) while (typestr.Contains("Constant"))
{ {
var intstr = typestr.Split(",".ToCharArray(), 2)[0].Split(':')[1].Split(' ')[4].Substring("0x".Length).Trim(); 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)); 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"))) var codetree = CompilationUnit().AddUsings(UsingDirective(ParseName("System")))
.AddUsings(UsingDirective(ParseName("System.IO"))) .AddUsings(UsingDirective(ParseName("System.IO")))
.AddUsings(UsingDirective(ParseName("System.Collections.Generic"))) .AddUsings(UsingDirective(ParseName("System.Collections.Generic")))
.AddUsings(UsingDirective(ParseName("System.Security"))) .AddUsings(UsingDirective(ParseName("System.Security")))
.AddUsings(UsingDirective(ParseName("System.Runtime.InteropServices"))) .AddUsings(UsingDirective(ParseName("System.Runtime.InteropServices")));
;
var Raylibnamespace = NamespaceDeclaration(ParseName("Raylib")).NormalizeWhitespace(); var Raylibnamespace = NamespaceDeclaration(ParseName("Raylib")).NormalizeWhitespace();
foreach (var Enumtype in eps.Values) foreach (var Enumtype in eps.Values)
{ {
var Enum = EnumDeclaration(Enumtype.Name) var Enum = EnumDeclaration(Enumtype.Name).WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)));
.WithModifiers(
TokenList(
Token(SyntaxKind.PublicKeyword)));
foreach (var Member in Enumtype.Enums) foreach (var Member in Enumtype.Enums)
{ {
var enummember = EnumMemberDeclaration( var enummember = EnumMemberDeclaration(
Identifier(Member.Key)) Identifier(Member.Key))
.WithEqualsValue( .WithEqualsValue(
@ -402,6 +509,7 @@ namespace Generator
} }
Raylibnamespace = Raylibnamespace.AddMembers(Enum); Raylibnamespace = Raylibnamespace.AddMembers(Enum);
} }
foreach (var Type in tps.Values) foreach (var Type in tps.Values)
{ {
var Struct = StructDeclaration(Type.Name).WithAttributeLists( var Struct = StructDeclaration(Type.Name).WithAttributeLists(
@ -431,8 +539,11 @@ namespace Generator
.WithModifiers( .WithModifiers(
TokenList( TokenList(
Token(SyntaxKind.PublicKeyword))); Token(SyntaxKind.PublicKeyword)));
bool IsUnsafe = false; bool IsUnsafe = false;
var FixedStructTypes = new List<string>(); var FixedStructTypes = new List<string>();
foreach (var Member in Type.Members) foreach (var Member in Type.Members)
{ {
var IsStruct = false; var IsStruct = false;
@ -458,7 +569,6 @@ namespace Generator
TypeName = TypeName.Replace("unsigned char", "byte"); TypeName = TypeName.Replace("unsigned char", "byte");
} }
var IsFixed = false; var IsFixed = false;
var VariableDec = VariableDeclarator(Member.Name); var VariableDec = VariableDeclarator(Member.Name);
if (TypeName.Contains("[")) if (TypeName.Contains("["))
@ -554,17 +664,19 @@ namespace Generator
IsUnsafe = true; IsUnsafe = true;
} }
} }
var variable = VariableDeclaration(ParseTypeName(TypeName)).AddVariables(VariableDec); var variable = VariableDeclaration(ParseTypeName(TypeName)).AddVariables(VariableDec);
var field = FieldDeclaration(variable).AddModifiers(Token(SyntaxKind.PublicKeyword)); var field = FieldDeclaration(variable).AddModifiers(Token(SyntaxKind.PublicKeyword));
if (IsFixed) field = field.AddModifiers(Token(SyntaxKind.FixedKeyword)); if (IsFixed) field = field.AddModifiers(Token(SyntaxKind.FixedKeyword));
Struct = Struct.AddMembers(field); Struct = Struct.AddMembers(field);
} }
if (IsUnsafe) Struct = Struct.AddModifiers(Token(SyntaxKind.UnsafeKeyword)); if (IsUnsafe) Struct = Struct.AddModifiers(Token(SyntaxKind.UnsafeKeyword));
Raylibnamespace = Raylibnamespace.AddMembers(Struct); Raylibnamespace = Raylibnamespace.AddMembers(Struct);
} }
{ {
var RaylibClass = ClassDeclaration(FileName) var RaylibClass = ClassDeclaration(fileName)
.WithAttributeLists( .WithAttributeLists(
SingletonList<AttributeListSyntax>( SingletonList<AttributeListSyntax>(
AttributeList( AttributeList(
@ -631,7 +743,7 @@ namespace Generator
Token(SyntaxKind.ExternKeyword) Token(SyntaxKind.ExternKeyword)
})).WithSemicolonToken(Token(SyntaxKind.SemicolonToken)); })).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 Param = Func.Params[i];
var TypeName = (Param.Type.IsPointer ? "IntPtr" : Param.Type.Name).Trim(); var TypeName = (Param.Type.IsPointer ? "IntPtr" : Param.Type.Name).Trim();
@ -668,12 +780,10 @@ namespace Generator
} }
codetree = codetree.AddMembers(Raylibnamespace); codetree = codetree.AddMembers(Raylibnamespace);
Console.WriteLine(codetree.NormalizeWhitespace().ToFullString()); 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(); Console.ReadLine();
} }
} }
} }