Mercurial > games > semicongine
view svk/generate.nim @ 1480:0b302531f5c5 default tip
did: continue on vulkan nim api generator
author | sam <sam@basx.dev> |
---|---|
date | Mon, 21 Apr 2025 01:31:55 +0700 |
parents | 172ac338f820 |
children |
line wrap: on
line source
import std/strtabs import std/syncio import std/xmltree import std/tables import std/options import std/sequtils import std/strutils import std/strformat import std/xmlparser import std/os import std/osproc import std/paths # helpers func smartParseInt(value: string): int = if value.startsWith("0x"): parseHexInt(value) else: parseInt(value) const TYPEMAP = { "void": "void", "char": "char", "float": "float32", "double": "float64", "int8_t": "int8", "uint8_t": "uint8", "int16_t": "int16", "uint16_t": "uint16", "int32_t": "int32", "uint32_t": "uint32", "uint64_t": "uint64", "int64_t": "int64", "size_t": "csize_t", "int": "cint", "void*": "pointer", "char*": "cstring", "ptr char": "cstring", "ptr void": "pointer", # "VK_DEFINE_HANDLE": "VkHandle", # not required, can directly defined as a distinct pointer, (in C is a pointer to an empty struct type) # "VK_DEFINE_NON_DISPATCHABLE_HANDLE": "VkNonDispatchableHandle", # same here }.toTable # load xml let xml = (system.currentSourcePath.parentDir() / "vk.xml").loadXml() let platforms = xml.findAll("platforms")[0] let types = xml.findAll("types")[0] let xmlenums = xml.findAll("enums") let commands = xml.findAll("commands")[0] let features = xml.findAll("feature") # features has extends="<ENUM>" let extensions = xml.findAll("extensions")[0] # extensions has extends="<ENUM>" let formats = xml.findAll("formats")[0] # gather all enums type EnumEntry = object name: string value: string EnumDef = object name: string values: seq[EnumEntry] isBitmask: bool ConstantDef = object name: string datatype: string value: string var consts: seq[ConstantDef] var enums: Table[string, EnumDef] func addValue(edef: var EnumDef, n: XmlNode) = if n.attr("deprecated") != "aliased" and n.attr("alias") == "": if n.attr("name") in edef.values.mapIt(it.name): return if n.attr("name").endsWith("_EXT") and n.attr("name")[0 ..< ^4] in edef.values.mapIt(it.name): return var value = "" if n.attr("value") != "": value = n.attr("value") elif n.attr("bitpos") != "": value = $(1 shl parseInt(n.attr("bitpos"))) elif n.attr("offset") != "": var enumBase = 1000000000 if n.attr("extnumber") != "": enumBase += (smartParseInt(n.attr("extnumber")) - 1) * 1000 var v = smartParseInt(n.attr("offset")) + enumBase if n.attr("dir") == "-": v = -v value = $(v) edef.values.add EnumEntry(name: n.attr("name"), value: value) func memberDecl(n: XmlNode): string = for i in 0 ..< n.len: if n[i].kind == xnElement and n[i].tag == "comment": n.delete(i) break assert n.tag == "member" debugecho n.toSeq, " ", n.len if n.len == 2: return &"{n[1][0].text}: {n[0][0]}" elif n.len == 3: if n[1].kind == xnElement and n[1].tag == "name": return &"{n[1][0].text}: array[{n[2].text[1 ..< ^1]}, {TYPEMAP.getOrDefault(n[0][0].text, n[0][0].text)}]]" else: assert n[1].text.strip() == "*" return &"{n[2][0].text}: ptr {n[0][0].text}" elif n.len == 4: if n[0].text.strip() in ["struct", "const struct"]: return &"{n[3][0].text}: ptr {n[1][0].text}" else: assert n[2].text.strip() in ["*", "* const *", "* const*"] return &"?" elif n.len in [5, 6]: return &"{n[1][0].text}: array[{n[3][0].text}, {n[0][0].text}]" assert false for e in xmlenums: if e.attr("type") == "constants": for c in e.findAll("enum"): var value = c.attr("value").strip(chars = {'(', ')'}) consts.add ConstantDef( name: c.attr("name"), datatype: TYPEMAP[c.attr("type")], value: value ) elif e.attr("type") == "enum": var edef = EnumDef(name: e.attr("name"), isBitmask: false) for ee in e.findAll("enum"): edef.addValue(ee) enums[edef.name] = edef elif e.attr("type") == "bitmask": var edef = EnumDef(name: e.attr("name"), isBitmask: true) for ee in e.findAll("enum"): edef.addValue(ee) enums[edef.name] = edef for f in features: for extendenum in f.findAll("enum"): if extendenum.attr("extends") != "": enums[extendenum.attr("extends")].addValue(extendenum) for extension in extensions.findAll("extension"): let extNum = extension.attr("number") for extendenum in extension.findAll("enum"): if extendenum.attr("extends") != "": if extendenum.attr("extnumber") == "": extendenum.attrs["extnumber"] = extNum enums[extendenum.attr("extends")].addValue(extendenum) let outPath = (system.currentSourcePath.parentDir() / "api.nim") let outFile = open(outPath, fmWrite) # generate core types =============================================================================== # preamble, much easier to hardcode than to generate from xml outFile.writeLine """ func VK_MAKE_API_VERSION*( variant: uint32, major: uint32, minor: uint32, patch: uint32 ): uint32 {.compileTime.} = (variant shl 29) or (major shl 22) or (minor shl 12) or patch """ outFile.writeLine "type" outFile.writeLine """ VkSampleMask = distinct uint32 VkBool32 = distinct uint32 VkFlags = distinct uint32 VkFlags64 = distinct uint64 VkDeviceSize = distinct uint64 VkDeviceAddress = distinct uint64 VkRemoteAddressNV = pointer """ for t in types: if t.attr("api") == "vulkansc": continue if t.attr("alias") != "": continue if t.attr("deprecated") == "true": continue if t.attr("category") == "include": continue if t.attr("category") == "define": continue if t.attr("category") == "bitmask": if t.len > 0 and t[0].text.startsWith("typedef"): outFile.writeLine &" {t[2][0].text} = distinct {t[1][0].text}" elif t.attr("category") == "union": let n = t.attr("name") outFile.writeLine &" {n}* {{.union.}} = object" for member in t.findAll("member"): outFile.writeLine &" {member.memberDecl()}" elif t.attr("category") == "handle": outFile.writeLine &" {t[2][0].text} = distinct pointer" elif t.attr("category") == "struct": let n = t.attr("name") outFile.writeLine &" {n}* = object" for member in t.findAll("member"): outFile.writeLine &" {member.memberDecl()}" # TODO: funcpointer outFile.writeLine "" # generate consts =============================================================================== outFile.writeLine "const" for c in consts: var value = c.value if value.endsWith("U"): value = value[0 ..^ 2] & "'u32" elif value.endsWith("ULL"): value = value[0 ..^ 4] & "'u64" if value[0] == '~': value = "not " & value[1 ..^ 1] outFile.writeLine &" {c.name}*: {c.datatype} = {value}" outFile.writeLine "" # generate enums =============================================================================== outFile.writeLine "type" for edef in enums.values(): if edef.values.len > 0: outFile.writeLine &" {edef.name}* {{.size: 4.}} = enum" for ee in edef.values: outFile.writeLine &" {ee.name} = {ee.value}" outFile.writeLine "" outFile.writeLine """ when defined(linux): include ../semicongine/rendering/vulkan/platform/xlib when defined(windows): include ../semicongine/rendering/vulkan/platform/win32 """ outFile.close() assert execCmd("nim c " & outPath) == 0