Mercurial > games > semicongine
comparison svk/generate.nim @ 1484:a2af327f19df
add: final raw wrapper
| author | sam <sam@basx.dev> |
|---|---|
| date | Thu, 01 May 2025 00:59:40 +0700 |
| parents | 55911f736a5a |
| children | 6e062a84c157 |
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 |
