-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathModelProvider.cs
331 lines (275 loc) · 10.6 KB
/
ModelProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using HtmlAgilityPack;
namespace DotGLFW.Generator;
public class ModelProvider : IModelProvider
{
private readonly IGLFWProvider _glfwProvider;
public ModelProvider(
IGLFWProvider glfwProvider)
{
_glfwProvider = glfwProvider;
}
public ParsedAPI GetAPI()
{
var glfw3_8h = _glfwProvider.ReadGLFWRepoFile("docs/html/glfw3_8h.html");
var doc = new HtmlDocument();
doc.LoadHtml(glfw3_8h);
var macroTable = doc.DocumentNode.Descendants("table")
.Where(table =>
table.HasClass("memberdecls") && table.Descendants("tr").First().InnerText.Contains("Macro"))
.First();
var excludedMacros = new[] { "GLAPIENTRY" };
var macros = ParseMacrosInTable(macroTable)
.Where(macro => !excludedMacros.Contains(macro.Name));
var typeDefTable = doc.DocumentNode.Descendants("table")
.Where(table =>
table.HasClass("memberdecls") && table.Descendants("tr").First().InnerText.Contains("Typedefs"))
.First();
var excludedTypedefs = new[] { "GLFWglproc", "GLFWvkproc" };
var typedefs = ParseTypeDefsInTable(typeDefTable)
.Where(typeDef => !excludedTypedefs.Contains(typeDef.Name))
.ToList();
typedefs.Add(new DelegateTypeDef
{
Name = "PFN_vkGetInstanceProcAddr",
ReturnType = new CppType { Name = "void*" },
SimpleDocumentation = "The function pointer type for the instance-level Vulkan function vkGetInstanceProcAddr.",
Parameters = [
new ("instance", new CppType { Name = "VkInstance" }),
new ("pName", new CppType { Name = "const char*" })
]
});
var functionsTable = doc.DocumentNode.Descendants("table")
.Where(table =>
table.HasClass("memberdecls") && table.Descendants("tr").First().InnerText.Contains("Functions"))
.First();
var functions = ParseFunctionsInTable(functionsTable);
var parsedAPI = new ParsedAPI
{
MacroCollections = GetMacroCollections().ToList(),
Macros = macros.ToList(),
TypeDefs = typedefs,
Functions = functions.ToList()
};
return parsedAPI;
}
private sealed class TableParser<T>
{
private readonly HtmlNode _table;
private readonly IGLFWProvider _glfwProvider;
private int _rowIndex = -1;
private readonly Func<HtmlNode, HtmlNode, string, T> _itemParser;
public TableParser(
HtmlNode table,
IGLFWProvider glfwProvider,
Func<HtmlNode, HtmlNode, string, T> itemParser)
{
_table = table;
_glfwProvider = glfwProvider;
_itemParser = itemParser;
}
public IEnumerable<T> Parse()
{
var rowsInTable = _table.Descendants("tr").Count();
while (_rowIndex + 1 < rowsInTable)
{
var item = ParseItem();
if (!EqualityComparer<T>.Default.Equals(item, default))
{
yield return item;
}
}
}
private HtmlNode ConsumeRow()
{
return _table.Descendants("tr").Skip(++_rowIndex).First();
}
private HtmlNode CurrentRow()
{
return _table.Descendants("tr").Skip(_rowIndex).First();
}
private bool TryConsumeRow(Predicate<HtmlNode> predicate, out HtmlNode row)
{
if (_rowIndex + 1 >= _table.Descendants("tr").Count())
{
row = null;
return false;
}
row = _table.Descendants("tr").Skip(_rowIndex + 1).First();
if (predicate(row))
{
_rowIndex++;
return true;
}
return false;
}
private bool IsRowSeparator(HtmlNode row)
{
return row.GetAttributeValue("class", "").StartsWith("separator");
}
private bool IsRowHeading(HtmlNode row)
{
return row.GetAttributeValue("class", "").StartsWith("heading");
}
private bool RowHasAttributes(HtmlNode row)
{
return row.Attributes.Any();
}
private T ParseItem()
{
var row = ConsumeRow();
if (IsRowSeparator(row) || IsRowHeading(row) || !RowHasAttributes(row))
return default;
var fullDocs = GetFullDocsForItem(row);
string simpleDocs = null;
if (TryConsumeRow(row => row.GetAttributeValue("class", "").StartsWith("memdesc"), out var simpleDocsRow))
simpleDocs = ReadSimpleDocsFromRow(simpleDocsRow);
return _itemParser(row, fullDocs, simpleDocs);
}
private string ReadSimpleDocsFromRow(HtmlNode row)
{
var cells = row.Descendants("td").ToArray();
return cells.Skip(1).First().InnerText.Trim()
.Remove("\n").Remove(" ").FixSpaces();
}
private HtmlNode GetFullDocsForItem(HtmlNode row)
{
var cellThatHasLinkToDocs = row.Descendants("td").Last();
var linkInDocs = cellThatHasLinkToDocs.Descendants("a").FirstOrDefault();
var linkToDocs = linkInDocs.GetAttributeValue("href", null);
if (linkToDocs == null)
return null;
var idInLink = linkToDocs.Split('#').Last();
var fileInLink = linkToDocs.Split('#').First();
var docsHtml = _glfwProvider.ReadGLFWRepoFile($"docs/html/{fileInLink}");
var doc = new HtmlDocument();
doc.LoadHtml(docsHtml);
var link = doc.DocumentNode.Descendants("a").First(a => a.GetAttributeValue("name", null) == idInLink);
var h2Sibling = link.NextSibling.NextSibling;
var linkInSibling = h2Sibling.Descendants("a").First();
var linkInLink = linkInSibling.GetAttributeValue("href", null);
if (linkInLink != $"#{idInLink}")
throw new Exception("Link in sibling is not the same as the id in link");
var memItemDiv = h2Sibling.NextSibling.NextSibling;
var memDoc = memItemDiv.Descendants("div").Where(div => div.HasClass("memdoc")).First();
return memDoc;
}
}
private IEnumerable<Macro> ParseMacrosInTable(HtmlNode table)
{
var macroParser = new TableParser<Macro>(table, _glfwProvider, ParseMacro);
return macroParser.Parse();
}
private Macro ParseMacro(HtmlNode row, HtmlNode fullDocs, string simpleDocs)
{
var rowInnerText = row.InnerText.Replace(" ", " ").Replace('\n', ' ').Trim();
var lexer = new Lexer(rowInnerText);
var tokens = lexer.Lex().ToArray();
var parser = new MacroParser(tokens);
var parsedMacro = parser.Parse();
parsedMacro.Documentation = simpleDocs;
parsedMacro.Type = new CppType { Name = "int" };
return parsedMacro;
}
private IEnumerable<MacroCollection> GetMacroCollections()
{
(string file, string name, string prefixToRemove, string suffixToRemove)[] collections = [
("buttons", "MouseButton", "GLFW_MOUSE_", ""),
("errors", "ErrorCode", "GLFW_", ""),
("gamepad__axes", "GamepadAxis", "GLFW_GAMEPAD_AXIS_", ""),
("gamepad__buttons", "GamepadButton", "GLFW_GAMEPAD_BUTTON_", ""),
("hat__state", "JoystickHat", "GLFW_HAT_", ""),
("joysticks", "Joystick", "GLFW_JOYSTICK_", ""),
("Keys", "Key", "GLFW_KEY_", ""),
("mods", "ModifierKey", "GLFW_MOD_", ""),
("shapes", "CursorShape", "GLFW_", "_CURSOR")
];
foreach (var (collection, name, prefixToRemove, suffixToRemove) in collections)
{
var html = _glfwProvider.ReadGLFWRepoFile($"docs/html/group__{collection}.html");
var doc = new HtmlDocument();
doc.LoadHtml(html);
var table = doc.DocumentNode.Descendants("table")
.Where(table =>
table.HasClass("memberdecls") && table.Descendants("tr").First().InnerText.Contains("Macros"))
.First();
var macros = ParseMacrosInTable(table).ToList();
yield return new MacroCollection
{
Name = name,
Macros = macros,
PrefixToRemove = prefixToRemove,
SuffixToRemove = suffixToRemove
};
}
}
private IEnumerable<ITypeDef> ParseTypeDefsInTable(HtmlNode table)
{
var typeDefParser = new TableParser<ITypeDef>(table, _glfwProvider, ParseTypeDef);
return typeDefParser.Parse();
}
private ITypeDef ParseTypeDef(HtmlNode row, HtmlNode fullDocs, string simpleDocs)
{
var rowInnerText = row.InnerText.Replace(" ", " ").Replace('\n', ' ').Trim();
var lexer = new Lexer(rowInnerText);
var tokens = lexer.Lex().ToArray();
var parser = new TypeDefParser(tokens);
var parsedTypeDef = parser.Parse();
parsedTypeDef.Documentation = fullDocs;
parsedTypeDef.SimpleDocumentation = simpleDocs;
if (parsedTypeDef is StructTypeDef std)
{
var cells = row.Descendants("td").ToArray();
var firstCell = cells.First();
var linkInCell = firstCell.Descendants("a").FirstOrDefault();
var link = linkInCell.GetAttributeValue("href", null);
if (link.StartsWith("struct"))
{
std.Fields = ParseFieldsInStructFile(link);
}
}
return parsedTypeDef;
}
private List<StructTypeField> ParseFieldsInStructFile(string structFile)
{
var structHtml = _glfwProvider.ReadGLFWRepoFile($"docs/html/{structFile}");
var doc = new HtmlDocument();
doc.LoadHtml(structHtml);
var fieldTable = doc.DocumentNode.Descendants("table")
.Where(table =>
table.HasClass("memberdecls") && table.Descendants("tr").First().InnerText.Contains("Data Fields"))
.First();
var fieldParser = new TableParser<StructTypeField>(fieldTable, _glfwProvider, ParseField);
return fieldParser.Parse().ToList();
}
private StructTypeField ParseField(HtmlNode row, HtmlNode fullDocs, string simpleDocs)
{
var innerText = row.InnerText.Replace(" ", " ").Replace('\n', ' ').Trim();
var lexer = new Lexer(innerText);
var tokens = lexer.Lex().ToArray();
var parser = new TypeDefParser.StructFieldParser(tokens);
var parsedField = parser.Parse();
parsedField.SimpleDocumentation = simpleDocs;
parsedField.Documentation = fullDocs;
return parsedField;
}
private IEnumerable<Function> ParseFunctionsInTable(HtmlNode table)
{
var functionParser = new TableParser<Function>(table, _glfwProvider, ParseFunction);
return functionParser.Parse();
}
private Function ParseFunction(HtmlNode row, HtmlNode fullDocs, string simpleDocs)
{
var rowInnerText = row.InnerText.Replace(" ", " ").Replace('\n', ' ').Trim();
var lexer = new Lexer(rowInnerText);
var tokens = lexer.Lex().ToArray();
var parser = new FunctionParser(tokens);
var parsedFunction = parser.Parse();
parsedFunction.Documentation = fullDocs;
parsedFunction.SimpleDocumentation = simpleDocs;
return parsedFunction;
}
}