Mercurial > games > semicongine
comparison svk/generate.nim @ 1484:a2af327f19df default tip
add: final raw wrapper
author | sam <sam@basx.dev> |
---|---|
date | Thu, 01 May 2025 00:59:40 +0700 |
parents | 55911f736a5a |
children |
comparison
equal
deleted
inserted
replaced
1483:55911f736a5a | 1484:a2af327f19df |
---|---|
84 value = $(v) | 84 value = $(v) |
85 | 85 |
86 if value notin edef.values.mapIt(it.value): | 86 if value notin edef.values.mapIt(it.value): |
87 edef.values.add EnumEntry(name: n.attr("name"), value: value) | 87 edef.values.add EnumEntry(name: n.attr("name"), value: value) |
88 | 88 |
89 func doTypename(typename: string, isPointer: bool): string = | 89 func doTypename(typename: string, pointerType: int): string = |
90 ## pointerType == 0: no pointer | |
91 ## pointerType == 1: normal pointer (e.g. char *) | |
92 ## pointerType == 2: double pointer (e.g. void **) | |
93 assert pointerType in [0, 1, 2] | |
90 result = TYPEMAP.getOrDefault(typename.strip(), typename.strip()).strip(chars = {'_'}) | 94 result = TYPEMAP.getOrDefault(typename.strip(), typename.strip()).strip(chars = {'_'}) |
91 | 95 |
92 if typename == "void": | 96 if typename == "void": |
93 assert isPointer | 97 assert pointerType > 0 |
94 | 98 |
95 if isPointer: | 99 if pointerType > 0: |
96 if typename == "void": | 100 if typename == "void": |
97 result = "pointer" | 101 result = "pointer" |
98 elif typename == "char": | 102 elif typename == "char": |
99 result = "cstring" | 103 result = "cstring" |
100 else: | 104 else: |
101 result = "ptr " & result | 105 result = "ptr " & result |
106 if pointerType == 2: | |
107 result = "ptr " & result | |
102 | 108 |
103 func doIdentifier(typename: string): string = | 109 func doIdentifier(typename: string): string = |
104 if typename in ["type", "object"]: | 110 if typename in ["type", "object"]: |
105 return &"`{typename}`" | 111 return &"`{typename}`" |
106 return typename.strip() | 112 return typename.strip() |
107 | 113 |
108 func doMember(typename, theType: string, isPointer: bool, value: string): string = | 114 func doMember(typename, theType: string, pointerType: int, value: string): string = |
109 if value == "": | 115 if value == "": |
110 &"{doIdentifier(typename)}: {doTypename(theType, isPointer)}" | 116 &"{doIdentifier(typename)}: {doTypename(theType, pointerType)}" |
111 else: | 117 else: |
112 &"{doIdentifier(typename)}: {doTypename(theType, isPointer)} = {value}" | 118 &"{doIdentifier(typename)}: {doTypename(theType, pointerType)} = {value}" |
113 | 119 |
114 func memberDecl(n: XmlNode): string = | 120 func memberDecl(n: XmlNode): string = |
115 for i in 0 ..< n.len: | 121 for i in 0 ..< n.len: |
116 if n[i].kind == xnElement and n[i].tag == "comment": | 122 if n[i].kind == xnElement and n[i].tag == "comment": |
117 n.delete(i) | 123 n.delete(i) |
118 break | 124 break |
119 assert n.tag == "member" | 125 assert n.tag == "member" |
120 if n.len == 2: | 126 if n.len == 2: |
121 return doMember(n[1][0].text, n[0][0].text, false, n.attr("values")) | 127 return doMember(n[1][0].text, n[0][0].text, 0, n.attr("values")) |
122 elif n.len == 3: | 128 elif n.len == 3: |
123 assert "*" notin n[0][0].text.strip() | 129 assert "*" notin n[0][0].text.strip() |
124 if n[1].kind == xnElement and n[1].tag == "name": | 130 if n[1].kind == xnElement and n[1].tag == "name": |
125 # bitfield | 131 # bitfield |
126 if n[2].text.strip().startsWith(":"): | 132 if n[2].text.strip().startsWith(":"): |
127 return | 133 return |
128 &"{doIdentifier(n[1][0].text)} {{.bitsize:{n[2].text.strip()[1 .. ^1]}.}}: {doTypename(n[0][0].text, false)}" | 134 &"{doIdentifier(n[1][0].text)} {{.bitsize:{n[2].text.strip()[1 .. ^1]}.}}: {doTypename(n[0][0].text, 0)}" |
129 # array definition | 135 # array definition |
130 elif n[2].text.strip().startsWith("["): | 136 elif n[2].text.strip().startsWith("["): |
131 let arrayDim = n[2].text[1 ..< ^1] | 137 let arrayDim = n[2].text[1 ..< ^1] |
132 if "][" in arrayDim: | 138 if "][" in arrayDim: |
133 let dims = arrayDim.split("][", 1) | 139 let dims = arrayDim.split("][", 1) |
134 let (dim1, dim2) = (dims[0], dims[1]) | 140 let (dim1, dim2) = (dims[0], dims[1]) |
135 return doMember( | 141 return doMember( |
136 n[1][0].text, | 142 n[1][0].text, |
137 &"array[{dim1}, array[{dim2}, {doTypename(n[0][0].text, false)}]]", | 143 &"array[{dim1}, array[{dim2}, {doTypename(n[0][0].text, 0)}]]", |
138 false, | 144 0, |
139 n.attr("values"), | 145 n.attr("values"), |
140 ) | 146 ) |
141 else: | 147 else: |
142 return doMember( | 148 return doMember( |
143 n[1][0].text, | 149 n[1][0].text, |
144 &"array[{arrayDim}, {doTypename(n[0][0].text, false)}]", | 150 &"array[{arrayDim}, {doTypename(n[0][0].text, 0)}]", |
145 false, | 151 0, |
146 n.attr("values"), | 152 n.attr("values"), |
147 ) | 153 ) |
148 else: | 154 else: |
149 debugecho n.toSeq | 155 debugecho n.toSeq |
150 doAssert false, "This should not happen" | 156 doAssert false, "This should not happen" |
151 else: | 157 else: |
152 # pointer definition | 158 # pointer definition |
153 assert n[1].text.strip() == "*" | 159 assert n[1].text.strip() == "*" |
154 return doMember(n[2][0].text, n[0][0].text, true, n.attr("values")) | 160 return doMember(n[2][0].text, n[0][0].text, 1, n.attr("values")) |
155 elif n.len == 4: | 161 elif n.len == 4: |
156 if n[0].text.strip() in ["struct", "const struct"]: | 162 if n[0].text.strip() in ["struct", "const struct"]: |
157 return doMember(n[3][0].text, n[1][0].text, true, n.attr("values")) | 163 return doMember(n[3][0].text, n[1][0].text, 1, n.attr("values")) |
158 else: | 164 else: |
159 assert n[0].text.strip() == "const" # can be ignored | 165 assert n[0].text.strip() == "const" # can be ignored |
160 assert n[1].tag == "type" | 166 assert n[1].tag == "type" |
161 assert n[2].text.strip() in ["*", "* const *", "* const*"] | 167 assert n[2].text.strip() in ["*", "* const *", "* const*"] |
162 # can be ignored, basically every type is a pointer | 168 # can be ignored, basically every type is a pointer |
163 assert n[3].tag == "name" | 169 assert n[3].tag == "name" |
164 assert n[1].len == 1 | 170 assert n[1].len == 1 |
165 assert n[3].len == 1 | 171 assert n[3].len == 1 |
166 return doMember(n[3][0].text, n[1][0].text, true, n.attr("values")) | 172 return doMember(n[3][0].text, n[1][0].text, 1, n.attr("values")) |
167 elif n.len in [5, 6]: | 173 elif n.len in [5, 6]: |
168 # array definition, using const-value for array length | 174 # array definition, using const-value for array length |
169 # <type>uint8_t</type>,<name>pipelineCacheUUID</name>[<enum>VK_UUID_SIZE</enum>] | 175 # <type>uint8_t</type>,<name>pipelineCacheUUID</name>[<enum>VK_UUID_SIZE</enum>] |
170 assert n[2].text.strip() == "[" | 176 assert n[2].text.strip() == "[" |
171 assert n[4].text.strip() == "]" | 177 assert n[4].text.strip() == "]" |
172 return doMember( | 178 return doMember( |
173 n[1][0].text, | 179 n[1][0].text, |
174 &"array[{n[3][0].text}, {doTypename(n[0][0].text, false)}]", | 180 &"array[{n[3][0].text}, {doTypename(n[0][0].text, 0)}]", |
175 false, | 181 0, |
176 n.attr("values"), | 182 n.attr("values"), |
177 ) | 183 ) |
178 assert false | 184 assert false |
179 | 185 |
180 for e in xmlenums: | 186 for e in xmlenums: |
213 | 219 |
214 # generate core types =============================================================================== | 220 # generate core types =============================================================================== |
215 # preamble, much easier to hardcode than to generate from xml | 221 # preamble, much easier to hardcode than to generate from xml |
216 outFile.writeLine """ | 222 outFile.writeLine """ |
217 | 223 |
224 import std/dynlib | |
225 | |
218 import ../semicongine/thirdparty/winim/winim/inc/winbase | 226 import ../semicongine/thirdparty/winim/winim/inc/winbase |
219 import ../semicongine/thirdparty/winim/winim/inc/windef | 227 import ../semicongine/thirdparty/winim/winim/inc/windef |
220 import ../semicongine/thirdparty/x11/xlib | 228 import ../semicongine/thirdparty/x11/xlib |
221 import ../semicongine/thirdparty/x11/x | 229 import ../semicongine/thirdparty/x11/x |
222 import ../semicongine/thirdparty/x11/xrandr | 230 import ../semicongine/thirdparty/x11/xrandr |
346 elif category == "union": | 354 elif category == "union": |
347 outFile.writeLine &" {tName}* {{.union.}} = object" | 355 outFile.writeLine &" {tName}* {{.union.}} = object" |
348 for member in t.findAll("member"): | 356 for member in t.findAll("member"): |
349 outFile.writeLine &" {member.memberDecl()}" | 357 outFile.writeLine &" {member.memberDecl()}" |
350 elif category == "handle": | 358 elif category == "handle": |
351 outFile.writeLine &" {t[2][0].text}* = distinct pointer" | 359 outFile.writeLine &" {t[2][0].text} = distinct pointer" |
352 elif category == "struct": | 360 elif category == "struct": |
353 outFile.writeLine &" {tName}* = object" | 361 outFile.writeLine &" {tName}* = object" |
354 for member in t.findAll("member"): | 362 for member in t.findAll("member"): |
355 if member.attr("api") == "vulkansc": | 363 if member.attr("api") == "vulkansc": |
356 continue | 364 continue |
357 outFile.writeLine &" {member.memberDecl()}" | 365 outFile.writeLine &" {member.memberDecl()}" |
358 elif category == "funcpointer": | 366 elif category == "funcpointer": |
359 #[ | |
360 <type category="funcpointer">typedef void* (VKAPI_PTR *<name>PFN_vkAllocationFunction</name>)( | |
361 <type>void</type>* pUserData, | |
362 <type>size_t</type> size, | |
363 <type>size_t</type> alignment, | |
364 <type>VkSystemAllocationScope</type> allocationScope); | |
365 </type> | |
366 ]# | |
367 assert t[0].text.startsWith("typedef ") | 367 assert t[0].text.startsWith("typedef ") |
368 let retName = t[0].text[8 ..< ^13].strip() | 368 let retName = t[0].text[8 ..< ^13].strip() |
369 let funcName = t.findAll("name")[0][0].text | 369 let funcName = t.findAll("name")[0][0].text |
370 | 370 |
371 outFile.write &" {funcname}* = proc(" | 371 outFile.write &" {funcname}* = proc(" |
372 for i in 3 ..< t.len: | 372 let nParams = (t.len - 3) div 2 |
373 # TODO: params | 373 for i in 0 ..< nParams: |
374 | 374 assert t[i * 2 + 3].tag == "type" |
375 if retName == "void" | 375 let typename = t[i * 2 + 3][0].text.strip() |
376 var identifier = t[i * 2 + 4].text.strip(chars = {' ', ')', ';', ',', '\n'}) | |
377 var pointerType = if identifier.startsWith("*"): 1 else: 0 | |
378 if pointerType > 0: | |
379 identifier = identifier[1 .. ^1].strip(chars = {' ', ')', ';', ',', '\n'}) | |
380 if identifier.endsWith("const"): | |
381 identifier = identifier[0 .. ^6].strip(chars = {' ', ')', ';', ',', '\n'}) | |
382 identifier = identifier.strip(chars = {','}) | |
383 outFile.write &"{doIdentifier(identifier)}: {doTypename(typename, pointerType)}, " | |
384 | |
385 if retName == "void": | |
376 outFile.writeLine &") {{.cdecl.}}" | 386 outFile.writeLine &") {{.cdecl.}}" |
377 elif retName == "void*" | 387 elif retName == "void*": |
378 outFile.writeLine &"): pointer {{.cdecl.}}" | 388 outFile.writeLine &"): pointer {{.cdecl.}}" |
379 else: | 389 else: |
380 outFile.writeLine &"): {doTypename(retName, false)} {{.cdecl.}}" | 390 outFile.writeLine &"): {doTypename(retName, 0)} {{.cdecl.}}" |
381 | |
382 else: | 391 else: |
383 doAssert category in ["", "basetype", "enum"], "unknown type category: " & category | 392 doAssert category in ["", "basetype", "enum"], "unknown type category: " & category |
384 outFile.writeLine "" | 393 outFile.writeLine "" |
385 | 394 |
395 outFile.write &"var\n" | |
386 for command in commands: | 396 for command in commands: |
387 #[ | |
388 <command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_INITIALIZATION_FAILED,VK_ERROR_LAYER_NOT_PRESENT,VK_ERROR_EXTENSION_NOT_PRESENT,VK_ERROR_INCOMPATIBLE_DRIVER"> | |
389 <proto> | |
390 <type>VkResult</type> | |
391 <name>vkCreateInstance</name> | |
392 </proto> | |
393 <param>const <type>VkInstanceCreateInfo</type>* <name>pCreateInfo</name></param> | |
394 <param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param> | |
395 <param><type>VkInstance</type>* <name>pInstance</name></param> | |
396 </command> | |
397 ]# | |
398 if command.attr("api") == "vulkansc": | 397 if command.attr("api") == "vulkansc": |
399 continue | 398 continue |
400 if command.attr("alias") != "": | 399 if command.attr("alias") != "": |
401 let funcName = command.attr("name") | 400 let funcName = command.attr("name") |
402 let funcAlias = command.attr("alias") | 401 let funcAlias = command.attr("alias") |
403 outFile.write &"var {funcName}* = {funcAlias}\n" | 402 outFile.write &" {funcName}* = {funcAlias}\n" |
404 continue | 403 continue |
405 | 404 |
406 let proto = command.findAll("proto")[0] | 405 let proto = command.findAll("proto")[0] |
407 let retType = proto.findAll("type")[0][0].text.strip() | 406 let retType = proto.findAll("type")[0][0].text.strip() |
408 let funcName = proto.findAll("name")[0][0].text.strip() | 407 let funcName = proto.findAll("name")[0][0].text.strip() |
409 | 408 |
410 if "Video" in funcName: # Video API not supported at this time | 409 if "Video" in funcName: # Video API not supported at this time |
411 continue | 410 continue |
412 | 411 |
413 outFile.write &"var {funcName}*: proc(" | 412 outFile.write &" {funcName}*: proc(" |
414 for param in command: | 413 for param in command: |
415 if param.tag != "param": | 414 if param.tag != "param": |
416 continue | 415 continue |
417 if param.attr("api") == "vulkansc": | 416 if param.attr("api") == "vulkansc": |
418 continue | 417 continue |
419 assert param.len in [2, 3, 4] | 418 assert param.len in [2, 3, 4] |
420 let paramType = param.findAll("type")[0][0].text | 419 let paramType = param.findAll("type")[0][0].text |
421 let paramName = param.findAll("name")[0][0].text | 420 let paramName = param.findAll("name")[0][0].text |
422 assert "*" notin paramType, $param | 421 assert "*" notin paramType, $param |
423 if paramType == "void": | 422 |
424 outFile.write &"{doIdentifier(paramName)}: {doTypename(paramType, true)}, " | 423 if param.len == 4: |
425 else: | 424 param.delete(0) |
426 outFile.write &"{doIdentifier(paramName)}: {doTypename(paramType, false)}, " | 425 |
426 var pointerType = 0 | |
427 | |
428 if param.len == 3: | |
429 if param[param.len - 1].kind == xnText: | |
430 assert param[param.len - 1].text[^1] == ']' | |
431 else: | |
432 assert param[0].tag == "type" | |
433 assert param[param.len - 1].tag == "name" | |
434 if param[1].text.strip() == "*": | |
435 pointerType = 1 | |
436 elif param[1].text.strip() == "**": | |
437 pointerType = 2 | |
438 # echo "3: ", param[1].text, " ", paramType, " ", paramName | |
439 outFile.write &"{doIdentifier(paramName)}: {doTypename(paramType, pointerType)}, " | |
427 | 440 |
428 outFile.write &")" | 441 outFile.write &")" |
429 if retType != "void": | 442 if retType != "void": |
430 assert "*" notin retType | 443 assert "*" notin retType |
431 outFile.write &": {doTypename(retType, false)}" | 444 outFile.write &": {doTypename(retType, 0)}" |
432 outFile.write " {.stdcall.}\n" | 445 outFile.write " {.stdcall.}\n" |
433 | 446 |
447 outFile.write """ | |
448 when defined(linux): | |
449 let vulkanLib = loadLib("libvulkan.so.1") | |
450 when defined(windows): | |
451 let vulkanLib = loadLib("vulkan-1.dll") | |
452 if vulkanLib == nil: | |
453 raise newException(Exception, "Unable to load vulkan library") | |
454 | |
455 vkGetInstanceProcAddr = cast[proc(instance: VkInstance, pName: cstring, ): PFN_vkVoidFunction {.stdcall.}](checkedSymAddr(vulkanLib, "vkGetInstanceProcAddr")) | |
456 """ | |
457 | |
434 outFile.close() | 458 outFile.close() |
435 | 459 |
436 assert execCmd("nim c " & outPath) == 0 | 460 assert execCmd("nim c " & outPath) == 0 |