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