Created
August 7, 2010 02:38
-
-
Save kevinmcmahon/512356 to your computer and use it in GitHub Desktop.
An Objective C brute force parser from #monotouch irc
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// no claim or warranty is made on this code | |
// it is very brute force | |
using System; | |
using System.Collections; | |
using System.IO; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Collections.Generic; | |
namespace parseObjectiveC | |
{ | |
public class SourceStream : Stream | |
{ | |
Stream source; | |
public SourceStream(Stream source) | |
{ | |
this.source = source; | |
} | |
public override bool CanRead { get { return true; } } | |
public override bool CanWrite { get { return false; } } | |
public override bool CanSeek { get { return false; } } | |
public override long Length { get { return source.Length; } } | |
public override long Position { get { throw new Exception(); } set { throw new Exception(); } } | |
public override void Flush() { throw new Exception(); } | |
public override int Read(byte[] buf, int offset, int count) | |
{ | |
int n = 0; | |
for (int i = 0; i < count; i++) | |
{ | |
int c = ReadByte(); | |
if (c == -1) | |
return n; | |
buf[offset + n] = (byte)c; | |
n++; | |
} | |
return n; | |
} | |
public override long Seek(long p, SeekOrigin o) | |
{ | |
throw new Exception(); | |
} | |
public override void SetLength(long l) | |
{ | |
throw new Exception(); | |
} | |
public override void Write(byte[] b, int a, int c) | |
{ | |
throw new Exception(); | |
} | |
public override int ReadByte() | |
{ | |
restart: | |
int n = source.ReadByte(); | |
if (n == -1) | |
return -1; | |
if (n == '/') | |
{ | |
int p = source.ReadByte(); | |
if (p == '/') | |
{ | |
while (true) | |
{ | |
n = source.ReadByte(); | |
if (n == -1) | |
return -1; | |
if (n == '\n') | |
return n; | |
} | |
} | |
else if (p == '*') | |
{ | |
while (true) | |
{ | |
n = source.ReadByte(); | |
if (n == -1) | |
return -1; | |
while (n == '*') | |
{ | |
n = source.ReadByte(); | |
if (n == -1) | |
return -1; | |
if (n == '/') | |
goto restart; | |
} | |
} | |
} | |
source.Position = source.Position - 1; | |
return '/'; | |
} | |
return n; | |
} | |
} | |
class TrivialParser | |
{ | |
StreamWriter gencs; | |
StreamWriter enumcs; | |
StreamReader r; | |
ArrayList types = new ArrayList(); | |
string currentInterface = ""; | |
void ProcessProperty(string line) | |
{ | |
bool ro = false; | |
string getter = null; | |
string assign = ""; | |
line = CleanDeclaration(line); | |
if (line.Length == 0) | |
return; | |
int p = line.IndexOf(')'); | |
var sub = line.Substring(0, p + 1); | |
if (sub.IndexOf("readonly") != -1) | |
{ | |
ro = true; | |
} | |
int j = sub.IndexOf("getter="); | |
if (j != -1) | |
{ | |
int k = sub.IndexOfAny(new char[] { ',', ')' }, j + 1); | |
//Console.WriteLine("j={0} k={1} str={2}", j, k, sub); | |
getter = sub.Substring(j + 7, k - (j + 7)); | |
} | |
if (sub.IndexOf("assign") != -1) | |
assign = ", ArgumentSemantic.Assign"; | |
if (sub.IndexOf("retain") != -1) | |
assign += ", ArgumentSemantic.Retain"; | |
if (sub.IndexOf("copy") != -1) | |
assign += ", ArgumentSemantic.Copy"; | |
if (sub.IndexOf("readonly") != -1) | |
assign += ", ArgumentSemantic.ReadOnly"; | |
var type = new StringBuilder(); | |
int i = p + 1; | |
for (; i < line.Length; i++) | |
{ | |
char c = line[i]; | |
if (!Char.IsWhiteSpace(c)) | |
break; | |
} | |
for (; i < line.Length; i++) | |
{ | |
char c = line[i]; | |
if (Char.IsWhiteSpace(c)) | |
break; | |
type.Append(c); | |
} | |
for (; i < line.Length; i++) | |
{ | |
char c = line[i]; | |
if (Char.IsWhiteSpace(c) || c == '*') | |
continue; | |
else | |
break; | |
} | |
var selector = new StringBuilder(); | |
for (; i < line.Length; i++) | |
{ | |
char c = line[i]; | |
if (Char.IsWhiteSpace(c) || c == ';') | |
break; | |
selector.Append(c); | |
} | |
string retType = TranslateType(type.ToString()).Trim(); | |
if (retType == "id") | |
retType = "IntPtr"; | |
gencs.WriteLine("\t//{0}", line); | |
gencs.WriteLine("\t[Export (\"{0}\"{1})]", selector, assign); | |
gencs.WriteLine("\t{0} {1} {{ {2} {3} }}", | |
retType, selector.ToString().Substring(0, 1).ToUpper() + selector.ToString().Substring(1, selector.Length - 1), | |
getter != null ? "[Bind (\"" + getter + "\")] get;" : "get;", | |
ro ? "" : "set; "); | |
gencs.WriteLine(); | |
} | |
string MakeSelector(string sig) | |
{ | |
StringBuilder sb = new StringBuilder(); | |
string[] split1 = sig.Split(':'); | |
int numArguments = split1.Length - 1; | |
if (numArguments > 0) | |
{ | |
string[] args = new string[numArguments]; | |
//split[0] is the function name | |
//split[1] is the first argument plus the name of the 2nd argument if present | |
for (int a = 1; a < split1.Length; a++) | |
{ | |
//(@"(?<=\().*?[nNsS].*?(?=\))", | |
string r = Regex.Replace(split1[a], "\\(.+\\)", "").Trim(); | |
r = r.Replace(" ", " ").Replace("\t\t", " ").Replace(" ", " ").Replace(" ", " "); | |
string[] split11 = r.Split(new char[2] { ' ', '\t' }); | |
if (split11.Length == 2) | |
{ | |
if (string.IsNullOrEmpty(args[a - 1]) == true) | |
args[a - 1] = split11[0]; | |
if (a >= numArguments) | |
continue; | |
args[a] = split11[1] + ":"; | |
} | |
else if (split11.Length == 1 && a == 1) | |
args[a - 1] = split11[0] + ":"; | |
} | |
string signature = split1[0] + ":"; | |
for (int b = 1; b < args.Length; b++) | |
signature += args[b]; | |
return signature.Replace(";", "").Trim(); | |
} | |
else | |
return sig.Replace(";", "").Trim(); | |
for (int i = 0; i < sig.Length; i++) | |
{ | |
char c = sig[i]; | |
if (c == ' ') | |
continue; | |
if (c == ';') | |
break; | |
else if (c == ':') | |
{ | |
bool bSkipFirst = true; | |
sb.Append(c); | |
i++; | |
for (; i < sig.Length; i++) | |
{ | |
c = sig[i]; | |
if (c == ')') | |
{ | |
if (bSkipFirst) | |
{ | |
bSkipFirst = false; | |
continue; | |
} | |
for (++i; i < sig.Length; i++) | |
{ | |
if (!Char.IsLetterOrDigit(sig[i])) | |
break; | |
} | |
break; | |
} | |
} | |
} | |
else | |
sb.Append(c); | |
} | |
return sb.ToString(); | |
} | |
enum State | |
{ | |
SkipToType, | |
EndOfType, | |
Parameter, | |
} | |
string MakeParameters(string sig) | |
{ | |
//Console.WriteLine("[{0}]", sig); | |
int colon = sig.IndexOf(':'); | |
if (colon == -1) | |
return ""; | |
string line = ""; | |
string[] s = sig.Replace(";", "").Split(':'); | |
for (int x = 1; x < s.Length; x++) | |
{ | |
int stringTextReplacement = 0; | |
string[] b = s[x].Split(')'); | |
if (string.IsNullOrEmpty(line) == false) | |
line += ", "; | |
if (b.Length == 2) | |
{ | |
string t = TranslateType(b[0].Replace("(", "").Replace(")", "")); | |
if (t == "id") | |
t = "IntPtr"; | |
if (t == "void") | |
t = "IntPtr"; | |
string[] c = b[1].Trim().Split(new char[2] { ' ', '\t' }); | |
if (c[0] == "string" || c[0] == "event" || c[0] == "delegate" || c[0] == "object" || c[0] == "base") | |
c[0] = c[0] + (++stringTextReplacement).ToString(); | |
line += t + " " + c[0]; | |
} | |
else | |
line += b[0]; | |
} | |
return line; | |
var sb = new StringBuilder(); | |
State state = State.SkipToType; | |
for (int i = 0; i < sig.Length; i++) | |
{ | |
char c = sig[i]; | |
switch (state) | |
{ | |
case State.SkipToType: | |
if (Char.IsWhiteSpace(c)) | |
continue; | |
if (c == '(') | |
state = State.EndOfType; | |
break; | |
case State.EndOfType: | |
if (c == ')') | |
{ | |
state = State.Parameter; | |
sb.Append(' '); | |
} | |
else | |
{ | |
if (c != '*') | |
sb.Append(c); | |
} | |
break; | |
case State.Parameter: | |
if (Char.IsWhiteSpace(c)) | |
{ | |
state = State.SkipToType; | |
sb.Append(", "); | |
} | |
else | |
{ | |
if (c != ';') | |
sb.Append(c); | |
} | |
break; | |
} | |
} | |
//Console.WriteLine ("{0}", sb); | |
return sb.ToString(); | |
} | |
Regex rx = new Regex("__OSX_AVAILABLE_STARTING\\(.*\\)"); | |
string CleanDeclaration(string line) | |
{ | |
return rx.Replace(line, ""); | |
} | |
string TranslateType(string retval) | |
{ | |
bool bNeedsOut = false; | |
if (retval.Contains("**") == true) | |
bNeedsOut = true; | |
retval = retval.Replace("*", ""); | |
retval = retval.Trim(); | |
if (retval.Contains("<")) | |
{ | |
string[] a = retval.Split('<'); | |
retval = a[0].Trim(); | |
} | |
switch (retval.Trim()) | |
{ | |
case "NSTextAlignment": | |
return "uint"; | |
case "NSSize": | |
return "SizeF"; | |
case "NSWindowDepth": | |
return "int"; | |
case "IBAction": | |
return "void"; | |
case "NSGlyph": | |
return "uint"; | |
case "void *": | |
case "void*": | |
retval = "IntPtr"; | |
break; | |
case "NSMutableArray*": | |
case "NSMutableArray *": | |
case "NSMutableArray": | |
case "NSArray*": | |
case "NSArray *": | |
case "NSArray": | |
retval = retval.Replace("*", "").Trim(); | |
break; | |
case "NSMutableSet": | |
retval = "NSSet"; | |
break; | |
case "NSMutableDictionary": | |
retval = "NSDictionary"; | |
break; | |
case "NSString": | |
case "NSString*": | |
case "NSString *": | |
retval = "string"; | |
break; | |
case "NSRect": | |
case "NSRect*": | |
case "NSRect *": | |
retval = "RectangleF"; | |
break; | |
// case "id": | |
// retval = "NSObject"; | |
// break; | |
case "BOOL": | |
retval = "bool"; | |
break; | |
case "NSPoint": | |
case "NSPoint*": | |
case "NSPoint *": | |
retval = "PointF"; | |
break; | |
case "NSURL": | |
retval = "NSUrl"; | |
break; | |
case "CFTimeInterval": | |
retval = "double"; | |
break; | |
case "CGRect": | |
retval = "System.Drawing.RectangleF"; | |
break; | |
case "CGPoint": | |
retval = "System.Drawing.PointF"; | |
break; | |
case "CGSize": | |
retval = "System.Drawing.SizeF"; | |
break; | |
case "CGFloat": | |
case "GLfloat": | |
retval = "float"; | |
break; | |
case "GLubyte": | |
retval = "byte"; | |
break; | |
case "ccTime": | |
retval = "float"; | |
break; | |
case "GLshort": | |
retval = "short"; | |
break; | |
case "unsigned int": | |
case "NSUInteger": | |
case "GLenum": | |
case "GLuint": | |
retval = "uint"; | |
break; | |
case "NSInteger": | |
retval = "int"; | |
break; | |
case "unsigned char": | |
retval = "byte"; | |
break; | |
case "SEL": | |
retval = "Selector"; | |
break; | |
case "NSTimeInterval": | |
retval = "double"; | |
break; | |
} | |
string typedefValue = ""; | |
if (retval == "NSAnimationProgress") | |
Console.WriteLine("break"); | |
if (typedefName.TryGetValue(retval.Replace("*", "").Trim(), out typedefValue)) | |
return typedefValue; | |
if (bNeedsOut) | |
retval = "out " + retval; | |
return retval.Replace("*", ""); | |
} | |
Dictionary<string, string> methodDefs = new Dictionary<string, string>(); | |
Dictionary<string, string> csharpMethods = new Dictionary<string, string>(); | |
int ifdefCount = 0; | |
void ProcessDeclaration(bool isProtocol, string line, bool is_optional) | |
{ | |
//StringBuilder sb = interfaceDictionary[currentInterface]; | |
StringBuilder sb = new StringBuilder(); | |
line = CleanDeclaration(line); | |
if (line.Length == 0) | |
return; | |
if (line.Contains("deprecated") || line.Contains("NS_REQUIRES_NIL_TERMINATION")) | |
{ | |
sb.AppendFormat("\t\t//{0}", line); | |
sb.AppendLine(); | |
return; | |
} | |
if (isProtocol && !is_optional) | |
{ | |
//sb.AppendFormat("\t\t[Abstract]"); | |
//sb.AppendLine(); | |
} | |
if (line.StartsWith("@property")) | |
{ | |
ProcessProperty(line); | |
return; | |
} | |
if (line.StartsWith("typedef enum")) | |
{ | |
ProcessEnum(line); | |
return; | |
} | |
//if (line.StartsWith("enum")) | |
//{ | |
// ProcessEnum2(line); | |
// return; | |
//} | |
if (line.StartsWith("typedef struct")) | |
{ | |
ProcessStruct(line); | |
return; | |
} | |
//Console.WriteLine("PROCESSING: {0}", line); | |
string addStatic = ""; | |
if (line.StartsWith("+")) | |
addStatic = "Static, "; | |
int p, q; | |
p = line.IndexOf('('); | |
if (p == -1) | |
return; | |
q = line.IndexOf(')'); | |
if (line.Contains("NSTableColumnNoResizing") == true) | |
q = q; | |
line = line.Replace(Environment.NewLine, " "); | |
//Console.WriteLine ("->{0}\np={1} q-p={2}", line, p, q-p); | |
string retval = line.Substring(p + 1, q - p - 1); | |
retval = TranslateType(retval); | |
string constructor = ""; | |
if (retval == "id" || retval == currentInterface) | |
if (line.Contains("init")) | |
{ | |
retval = "IntPtr"; | |
constructor = "Constructor"; | |
} | |
else | |
retval = currentInterface; | |
p = line.IndexOf(';'); | |
string signature = line.Substring(q + 1, p - q); | |
string selector = MakeSelector(signature); | |
string parameters = MakeParameters(signature); | |
string unsupported = ""; | |
if (parameters.Contains("const") || retval.Contains("const") || signature.Contains("...") || | |
retval.Contains("unsigned short") || parameters.Contains("NSInteger (*") || | |
parameters.Contains("NSComparisonResult (*") || | |
retval.Contains("unsigned long long") || | |
parameters.Contains("unsigned short") || | |
currentCategory.Contains("Deprecated") || | |
parameters.Contains("[")) | |
unsupported = "//"; | |
//Console.WriteLine("signature: {0}", signature); | |
//Console.WriteLine("selector: {0}", selector); | |
sb.AppendFormat("\t\t//{1}{2}{0}", line, unsupported, | |
(currentTarget != "" && currentInterface != currentTarget ? " from " + currentCategory : "")); | |
if (currentCategory.Contains("Deprecated")) | |
unsupported = "//"; | |
sb.AppendLine(); | |
selector = selector.Replace("DEPRECATED_IN_MAC_OS_X_VERSION_10_4_AND_LATER", "").Replace("AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER_BUT_DEPRECATED", "").Replace("AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER", "").Trim(); | |
sb.AppendFormat("\t\t{2}[{1}{3}Export (\"{0}\")]", selector, addStatic, unsupported, | |
(isProtocol && !is_optional ? "Abstract, " : "")); | |
sb.AppendLine(); | |
int f = selector.IndexOf(':'); | |
string func = ""; | |
if (f != -1) | |
func = selector.Substring(0, 1).ToUpper() + selector.Substring(1, f - 1); | |
else | |
func = selector.Substring(0, 1).ToUpper() + selector.Substring(1, selector.Length - 1); | |
if (func.Contains("URL")) | |
func = func.Replace("URL", "Url"); | |
if (bInCategory) | |
{ | |
//if (currentTarget != currentInterface) | |
{ | |
string[] s1 = selector.Split(':'); | |
if (s1.Length > 2) | |
func = s1[0].Substring(0, 1).ToUpper() + s1[0].Substring(1) + | |
s1[1].Substring(0, 1).ToUpper() + s1[1].Substring(1); | |
} | |
} | |
func = func.Replace("DEPRECATED_IN_MAC_OS_X_VERSION_10_4_AND_LATER", "").Replace("AVAILABLE_MAC_OS_X_VERSION_10_5_AND_LATER","").Trim(); | |
string[] p1 = parameters.Split(' '); | |
string p3 = ""; | |
if (parameters != "") | |
{ | |
for (int p2 = 0; p2 < p1.Length; p2 = p2 + 2) | |
p3 += p1[p2]; | |
} | |
string csharpSig = currentInterface + func + p3; | |
if (csharpMethods.ContainsKey(csharpSig)) | |
{ | |
string[] s1 = selector.Split(':'); | |
if (s1.Length > 2) | |
func = s1[0].Substring(0, 1).ToUpper() + s1[0].Substring(1) + | |
s1[1].Substring(0, 1).ToUpper() + s1[1].Substring(1); | |
else | |
Console.WriteLine("conflict"); | |
} | |
else | |
csharpMethods.Add(csharpSig, "hi"); | |
if (currentTarget != "") | |
{ | |
string targetObj = currentCategory.Replace("NS", ""); | |
targetObj = targetObj.Substring(0, 1).ToLower() + targetObj.Substring(1); | |
parameters = string.Format("[Target] {0} {2}{1}", currentTarget, parameters == "" ? "" : ", " + parameters, | |
targetObj); | |
} | |
if (parameters != "" || retval == "void") | |
sb.AppendFormat("\t\t{3}{0} {1} ({2});", retval, constructor == "" ? func : constructor, parameters, unsupported); | |
else | |
sb.AppendFormat("\t\t{4}{0} {1} {2} get; {3}", retval, func, "{", "}", unsupported); | |
sb.AppendLine(); | |
sb.AppendLine(); | |
string uniqueNmae = currentInterface + "-" + selector.ToLower() + "%$" + addStatic + | |
(string.IsNullOrEmpty(p3) ? "" : p3) + "$%" + retval; | |
if (methodDefs.ContainsKey(uniqueNmae) == false) | |
methodDefs.Add(uniqueNmae, sb.ToString()); | |
else | |
Console.WriteLine("dup"); | |
} | |
private struct ttt | |
{ | |
int x; | |
} | |
private void ProcessStruct(string line) | |
{ | |
string firstLine = "\tpublic struct {0}\n"; | |
StringBuilder sb = new StringBuilder(); | |
sb.Append("\t{\n"); | |
while (true) | |
{ | |
line = r.ReadLine(); | |
if (line.Contains("}")) | |
{ | |
line = line.Replace("}", "").Replace(";", "").Trim(); | |
firstLine = string.Format(firstLine, line); | |
sb.Insert(0, firstLine); | |
sb.Append("\t}\n"); | |
break; | |
} | |
else | |
{ | |
line = line.Trim(); | |
if (line == "" || line == "{") | |
continue; | |
sb.Append("\t\t" + line + "\n"); | |
} | |
} | |
//enumcs.Write(sb); | |
} | |
int enumNotFoundCounter = 0; | |
private void ProcessEnum2(string line) | |
{ | |
StringBuilder sb = new StringBuilder(); | |
string enumName = ""; | |
sb.AppendLine("\tpublic enum REPLACEME"); | |
sb.AppendLine("\t{"); | |
int endFound = 0; | |
List<string> enumArgs = new List<string>(); | |
while (true) | |
{ | |
if (line == null) | |
break; | |
if (line.Contains(";")) | |
endFound++; | |
if (endFound <= 1) | |
{ | |
line = line.Replace("enum", "").Replace("{", "").Replace("}", "").Replace("}", "").Replace(";", ""); | |
string[] args = line.Split(','); | |
foreach (string a in args) | |
{ | |
string b = a.Trim(); | |
if (b == "") | |
continue; | |
enumArgs.Add(b); | |
} | |
} | |
if (endFound == 1) | |
{ | |
int peek = r.Peek(); | |
if (peek == '@' || peek == 'e') | |
break; | |
if (peek == '#' || peek == '/') | |
{ | |
line = r.ReadLine(); | |
continue; | |
} | |
if (peek == 't') | |
{ | |
line = r.ReadLine(); | |
string[] args = line.Split(' '); | |
enumName = args[2].Replace(";", "").Trim(); | |
break; | |
} | |
} | |
if (endFound > 1) | |
break; | |
line = r.ReadLine(); | |
} | |
for (int i = 0; i < enumArgs.Count; i++) | |
{ | |
string ar = enumArgs[i]; | |
if (ar.StartsWith("#")) | |
sb.AppendLine("//" + ar); | |
else | |
sb.AppendLine("\t\t" + ar + (i < enumArgs.Count -1 ? "," : "")); | |
} | |
sb.AppendLine("\t}"); | |
if (enumName == "") | |
enumName = string.Format("EnumNameNotFound{0}", enumNotFoundCounter++); | |
enumName += string.Format(" // in {0}", currentFileName); | |
sb = sb.Replace("REPLACEME", enumName); | |
enumcs.Write(sb); | |
} | |
private void ProcessEnum(string line) | |
{ | |
string firstLine = "public enum {0}\n"; | |
StringBuilder sb = new StringBuilder(); | |
sb.Append("{\n"); | |
while (true) | |
{ | |
line = r.ReadLine(); | |
if (line.Contains(";")) | |
{ | |
line = line.Replace("}", "").Replace(";", ""); | |
firstLine = string.Format(firstLine, line); | |
sb.Insert(0, firstLine); | |
sb.Append("}\n"); | |
break; | |
} | |
else | |
{ | |
line = line.Trim(); | |
if (line == "") | |
continue; | |
sb.Append("\t" + line + "\n"); | |
} | |
} | |
enumcs.Write(sb); | |
} | |
Dictionary<string, StringBuilder> interfaceDictionary = new Dictionary<string, StringBuilder>(); | |
Dictionary<string, string> interfaceParent = new Dictionary<string, string>(); | |
string lastClassName = ""; | |
string currentCategory = ""; | |
string currentTarget = ""; | |
bool bInCategory = false; | |
void ProcessInterface(string iface) | |
{ | |
var cols = iface.Split(':'); | |
string line; | |
bInCategory = false; | |
currentCategory = ""; | |
if (iface.Contains("NSWindow")) | |
Console.WriteLine("stop here"); | |
string className = cols[0].Replace("@interface", "").Trim(); | |
currentTarget = ""; | |
if (className.Contains("(")) | |
{ | |
string[] t = className.Split('('); | |
//if (types.Contains(t[0].Trim())) | |
string maybeNewClassName = t[0]; | |
// this code fixes delegate callbacks | |
//if (maybeNewClassName == "NSObject") | |
//{ | |
// maybeNewClassName = t[1].Replace(")", ""); | |
bInCategory = true; | |
//} | |
//else | |
{ | |
className = maybeNewClassName; | |
currentTarget = maybeNewClassName.Trim(); | |
currentCategory = t[1].Replace(")", "").Trim(); | |
} | |
if (className == "") | |
className = currentFileName; | |
} | |
lastClassName = className; | |
types.Add(className); | |
currentInterface = className.Trim(); | |
StringBuilder sb; | |
bool bNewInterface = false; | |
if (interfaceDictionary.TryGetValue(currentInterface, out sb) == false) | |
{ | |
sb = new StringBuilder(); | |
interfaceDictionary.Add(currentInterface, sb); | |
bNewInterface = true; | |
} | |
//Console.WriteLine("**** {0} ", iface); | |
if (cols.Length == 2) | |
{ | |
string[] typeLine = cols[1].Trim().Split(new char[] { ' ', ',', '\t', '<' }); | |
//sb.AppendFormat("\n\t[BaseType (typeof ({0}))]", typeLine[0]); | |
//sb.AppendLine(); | |
if (string.IsNullOrEmpty(typeLine[0]) == false) | |
{ | |
string curType = ""; | |
if (interfaceParent.TryGetValue(currentInterface, out curType)) | |
{ | |
sb.AppendFormat("// {0} has multiple base types defined, '{1}' and '{2}'", | |
currentInterface, curType, typeLine[0]); | |
} | |
else | |
interfaceParent.Add(currentInterface, typeLine[0]); | |
} | |
} | |
//if (bNewInterface) | |
//{ | |
// sb.AppendFormat("\tinterface {0} {{", className); | |
// sb.AppendLine(); | |
// sb.AppendLine("APPKIT_EXTERN_ME"); | |
//} | |
while ((line = r.ReadLine()) != null && !line.StartsWith("@end")) | |
{ | |
string full = ""; | |
//while ((line = r.ReadLine()) != null && !line.StartsWith("@end")) | |
{ | |
if (line.Contains("#if") || line.Contains("#endif")) | |
{ | |
methodDefs.Add(currentInterface + "-" + ifdefCount.ToString(), "//" + line + Environment.NewLine); | |
ifdefCount++; | |
continue; | |
} | |
full += line; | |
if (full.IndexOf(';') != -1 || full.IndexOf('}') != -1) | |
{ | |
full = full.Replace('\n', ' '); | |
ProcessDeclaration(false, full, false); | |
full = ""; | |
} | |
} | |
//break; | |
} | |
//sb.AppendLine("\t}"); | |
} | |
void ProcessProtocol(string proto) | |
{ | |
string[] d = proto.Split(new char[] { ' ', '<', '>' }); | |
string line; | |
StringBuilder sb = new StringBuilder(); | |
interfaceDictionary.Add(d[1], sb); | |
currentInterface = d[1]; | |
types.Add(d[1]); | |
string curType = ""; | |
if (interfaceParent.TryGetValue(currentInterface, out curType)) | |
{ | |
sb.AppendFormat("// {0} has multiple base types defined, '{1}' and '{2}'", | |
currentInterface, curType, d[3]); | |
} | |
else | |
interfaceParent.Add(currentInterface, d[3]); | |
//sb.AppendFormat("\t[BaseType (typeof ({0}))]", d[3]); | |
//sb.AppendLine(); | |
sb.AppendLine("\t[Model]"); | |
//sb.AppendFormat("\tinterface {0}", d[1]); | |
//sb.AppendLine(); | |
//sb.AppendLine("\t{"); | |
bool optional = false; | |
//while ((line = r.ReadLine()) != null && !line.StartsWith("@end")) | |
{ | |
line = r.ReadLine(); | |
if (line.StartsWith("@optional")) | |
optional = true; | |
string full = ""; | |
while ((line = r.ReadLine()) != null && !line.StartsWith("@end")) | |
{ | |
full += line; | |
if (full.IndexOf(';') != -1) | |
{ | |
full = full.Replace('\n', ' '); | |
ProcessDeclaration(true, full, optional); | |
full = ""; | |
} | |
} | |
} | |
} | |
internal TrivialParser() { } | |
internal string directoryName = ""; | |
internal string currentFileName = ""; | |
Dictionary<string, string> typedefName = new Dictionary<string, string>(); | |
internal void Run(string[] args) | |
{ | |
gencs = File.CreateText("gen.cs"); | |
enumcs = File.CreateText("enum.cs"); | |
gencs.WriteLine("using System;"); | |
gencs.WriteLine("using MonoMac.ObjCRuntime;"); | |
gencs.WriteLine("using MonoMac.CoreFoundation;"); | |
gencs.WriteLine("\nnamespace {0}", "MonoMac.Foundation"); | |
gencs.WriteLine("{"); | |
enumcs.WriteLine("\nnamespace {0}", "MonoMac.Foundation"); | |
enumcs.WriteLine("{"); | |
if (args.Length == 1) | |
{ | |
if (Directory.Exists(args[0])) | |
{ | |
directoryName = args[0]; | |
DirectoryInfo di = new DirectoryInfo(args[0]); | |
FileInfo[] files = di.GetFiles(); | |
List<string> newArgs = new List<string>(); | |
foreach (FileInfo f1 in files) | |
newArgs.Add(args[0] + System.IO.Path.DirectorySeparatorChar + f1.Name); | |
args = newArgs.ToArray(); | |
} | |
} | |
foreach (string f in args) | |
{ | |
using (var fs = File.OpenRead(f)) | |
{ | |
FileInfo fi = new FileInfo(f); | |
currentFileName = fi.Name.Replace("*.h", ""); | |
r = new StreamReader(new SourceStream(fs)); | |
interfaceDictionary.Add(f + "_externs", new StringBuilder()); | |
string line; | |
while ((line = r.ReadLine()) != null) | |
{ | |
if (line.StartsWith("#")) | |
continue; | |
if (line.Length == 0) | |
continue; | |
if (line.StartsWith("@class")) | |
continue; | |
if (line.StartsWith("@interface")) | |
ProcessInterface(line); | |
if (line.StartsWith("@protocol") && line.IndexOf("<") != -1) | |
ProcessProtocol(line); | |
if (line.StartsWith("typedef enum")) | |
{ | |
ProcessEnum(line); | |
continue; | |
} | |
if (line.StartsWith("enum")) | |
{ | |
ProcessEnum2(line); | |
continue; | |
} | |
if (line.StartsWith("typedef struct")) | |
{ | |
if (line.Contains(";")) | |
{ | |
string[] typeDefLine = line.Split(new char[2] { ' ', '\t' }); | |
string t2 = typeDefLine[2].Trim(); | |
string t3 = typeDefLine[3].Replace(";", "").Trim(); | |
if (t3.Contains("*")) | |
t2 = "IntPtr"; | |
t3 = t3.Replace("*", "").Trim(); | |
typedefName.Add(t3, t2); | |
continue; | |
} | |
ProcessStruct(line); | |
continue; | |
} | |
if (line.StartsWith("typedef float")) | |
{ | |
//not sure this should be done as most of these are enums | |
if (line.Contains(";")) | |
{ | |
string[] typeDefLine = line.Split(new char[2] { ' ', '\t' }); | |
typedefName.Add(typeDefLine[2].Replace(";","").Trim(), typeDefLine[1].Trim()); | |
continue; | |
} | |
continue; | |
} | |
if (line.StartsWith("APPKIT_EXTERN") || line.StartsWith("FOUNDATION_EXPORT")) | |
{ | |
ProcessAppKitExtern(line, f); | |
continue; | |
} | |
} | |
} | |
} | |
//foreach (string s in types) | |
//{ | |
// Console.WriteLine("\t\ttypeof ({0}),", s); | |
//} | |
gencs.WriteLine("}"); | |
enumcs.WriteLine("}"); | |
gencs.Close(); | |
enumcs.Close(); | |
string fileName = "MonoMac"; | |
if (directoryName != "") | |
fileName = directoryName + System.IO.Path.DirectorySeparatorChar + "MonoMac"; | |
StreamWriter writeCS = File.CreateText(fileName + ".cs"); | |
writeCS.WriteLine("using System;"); | |
writeCS.WriteLine("using System.Drawing;"); | |
writeCS.WriteLine("using MonoMac.Foundation;"); | |
writeCS.WriteLine("using MonoMac.ObjCRuntime;"); | |
writeCS.WriteLine("\nnamespace {0}", "MonoMac.AppKit"); | |
writeCS.WriteLine("{"); | |
foreach (string key in interfaceDictionary.Keys) | |
{ | |
if (key.Contains("_externs")) | |
continue; | |
StringBuilder appKitExterns; | |
interfaceDictionary.TryGetValue(key + ".h_externs", out appKitExterns); | |
StringBuilder sb = interfaceDictionary[key]; | |
string[] keys = new string[methodDefs.Keys.Count]; | |
methodDefs.Keys.CopyTo(keys, 0); | |
for (int i = 0; i < keys.Length; i++) | |
{ | |
string methodName = keys[i]; | |
if (methodName.StartsWith(key + "-")) | |
{ | |
string setMethod; | |
int pos = methodName.IndexOf("%$"); | |
if (pos > 0) | |
{ | |
string getMethodName = methodName.Substring(0, pos).Replace(key + "-", ""); | |
getMethodName = "set" + getMethodName + ":"; | |
int pos1 = methodName.IndexOf("$%") + 2; // start of retval | |
string retval = methodName.Substring(pos1); | |
getMethodName = key + "-" + getMethodName + "%$" + retval + "$%void"; | |
if (methodDefs.TryGetValue(getMethodName, out setMethod)) | |
{ | |
string getMethod = methodDefs[methodName]; | |
getMethod = getMethod.Replace("{ get; }", "{ get; set; }"); | |
methodDefs[methodName] = getMethod; | |
methodDefs.Remove(getMethodName); | |
} | |
else | |
{ | |
getMethodName = methodName.Substring(0, pos).Replace(key + "-", ""); | |
string isMethodName = getMethodName; | |
getMethodName = "set" + getMethodName.Replace("is", "") + ":"; | |
pos1 = methodName.IndexOf("$%") + 2; // start of retval | |
retval = methodName.Substring(pos1); | |
getMethodName = key + "-" + getMethodName + "%$" + retval + "$%void"; | |
if (methodDefs.TryGetValue(getMethodName, out setMethod)) | |
{ | |
isMethodName = isMethodName.Replace("is", ""); | |
isMethodName = isMethodName.Substring(0, 1).ToUpper() + isMethodName.Substring(1); | |
string getMethod = methodDefs[methodName]; | |
getMethod = getMethod.Replace("{ get; }", | |
string.Format("{{ [Bind(\"{0}\")] get; set; }}", "is" + isMethodName)); | |
getMethod = getMethod.Replace("Is" + isMethodName, isMethodName); | |
methodDefs[methodName] = getMethod; | |
methodDefs.Remove(getMethodName); | |
} | |
} | |
} | |
} | |
} | |
string parent = ""; | |
if (key == "NSWindow") | |
Console.WriteLine("debug here"); | |
if (interfaceParent.TryGetValue(key, out parent) == true) | |
{ | |
sb.AppendFormat("\n\t[BaseType (typeof ({0}))]", parent); | |
sb.AppendLine(); | |
} | |
sb.AppendFormat("\tinterface {0} {{", key.Replace(".h", "")); | |
sb.AppendLine(); | |
sb.AppendLine("APPKIT_EXTERN_ME"); | |
foreach (string methodName in methodDefs.Keys) | |
{ | |
if (methodName.StartsWith(key + "-")) | |
{ | |
sb.Append(methodDefs[methodName]); | |
} | |
} | |
sb.AppendLine("\t}"); | |
//string fileName = key; | |
//if (key == "NSEntityMapping") | |
//{ | |
// writeCS.WriteLine("using NSEntityMappingType = System.UInt32;"); | |
//} | |
if (appKitExterns != null) | |
sb.Replace("APPKIT_EXTERN_ME", appKitExterns.ToString()); | |
else | |
sb.Replace("APPKIT_EXTERN_ME", ""); | |
writeCS.Write(sb.ToString()); | |
} | |
writeCS.WriteLine("}"); | |
writeCS.Close(); | |
} | |
StringBuilder appKitExterns; | |
private void ProcessAppKitExtern(string line, string fileName) | |
{ | |
StringBuilder appKitExterns = interfaceDictionary[fileName + "_externs"]; | |
string[] words = line.Replace("*", "").Replace(";", "").Replace("\t", "").Replace(" ", " ").Replace(" ", " ").Replace(" ", " ").Split(' '); | |
if ((words[0] != "APPKIT_EXTERN" || words[0] != "FOUNDATION_EXPORT") && words[1] != "NSString") | |
{ | |
appKitExterns.AppendFormat("\t\tTODO: {0}", line); | |
appKitExterns.AppendLine(); | |
return; | |
} | |
string word = ""; | |
if (words[0] == "APPKIT_EXTERN") | |
word = words[2]; | |
else | |
word = words[3]; | |
// format is [Field('objectCName')] TYPE NAME { get; } | |
string cSharpName = word.Substring(0, 1).ToUpper() + word.Substring(1); | |
appKitExterns.AppendFormat("\t\t[Field(\"{0}\")]", word); | |
appKitExterns.AppendLine(); | |
appKitExterns.AppendFormat("\t\t{0} {1} {2} get; {3}", "NSString", cSharpName, "{", "}"); | |
appKitExterns.AppendLine(); | |
appKitExterns.AppendLine(); | |
} | |
public static void Main(string[] args) | |
{ | |
var tp = new TrivialParser(); | |
tp.Run(args); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment