summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/generator.nim515
-rw-r--r--tools/raylib_wrap.nim46
2 files changed, 561 insertions, 0 deletions
diff --git a/tools/generator.nim b/tools/generator.nim
new file mode 100644
index 0000000..583f4e4
--- /dev/null
+++ b/tools/generator.nim
@@ -0,0 +1,515 @@
+import json
+import os
+import osproc
+import parseutils
+import sequtils
+import sets
+import strformat
+import strscans
+import strtabs
+import strutils
+import tables
+import unittest
+
+let nativeToNimType = newStringTable(
+ ("void*", "pointer"),
+ ("void**", "pointer"),
+
+ ("char", "cchar"),
+ ("signed char", "cchar"),
+ ("unsigned char", "cuchar"),
+ ("const char*", "cstring"),
+ ("const char *", "cstring"),
+
+ ("unsigned int", "cuint"),
+ ("signed int", "cint"),
+ ("int", "cint"),
+
+ ("float", "cfloat"),
+ ("double", "cdouble"),
+
+ ("short", "cshort"),
+ ("unsigned short", "cushort"),
+ ("signed short", "cshort"),
+
+ # ("unsigned char*", "ptr byte"),
+ # ("unsigned char**", "ptr ptr byte"),
+ # ("unsigned int*", "ptr cuint"),
+ # ("const char*", "cstring"),
+ # ("char*", "cstring"),
+
+ ("int64_t", "int64"),
+ ("uint64_t", "uint64"),
+
+ ("ImVector_ImWchar*", "ptr ImVector[ImWchar]"),
+
+ ("size_t", "csize"),
+
+ modeCaseSensitive
+)
+
+proc parseFunctionPointerDecl(decl: string): string
+
+proc parseTypeDecl(decl: string): string =
+ result = decl.strip()
+
+ if "(*)" in result:
+ return parseFunctionPointerDecl(result)
+
+ if result in nativeToNimType:
+ return nativeToNimType[result]
+
+ result = decl.replace("const", "").strip()
+
+ if result in nativeToNimType:
+ return nativeToNimType[result]
+
+ # simple array, e.g. float[3]
+ var n = ""
+ var s = ""
+ if scanf(decl, "$w[$*]", n, s):
+ n = parseTypeDecl(n)
+ return &"array[{s}, {n}]"
+
+ if result.endsWith("*") or result.endsWith("[]"):
+ var ptrs: seq[string] = @[]
+ result = result.replace("[]", "*")
+ while result.endsWith("*"):
+ ptrs.add("ptr")
+ result = result[0..^2]
+ return ptrs.join(" ") & " " & parseTypeDecl(result)
+
+when defined(test):
+ test "simple types":
+ check parseTypeDecl("float") == "cfloat"
+ check parseTypeDecl("double") == "cdouble"
+
+ test "simple types with spaces":
+ check parseTypeDecl("float ") == "cfloat"
+ check parseTypeDecl(" double") == "cdouble"
+
+ test "array":
+ check parseTypeDecl("float[3]") == "array[3, cfloat]"
+
+ test "pointer":
+ check parseTypeDecl("float*") == "ptr cfloat"
+ check parseTypeDecl("float *") == "ptr cfloat"
+ check parseTypeDecl("unsigned char*") == "ptr cuchar"
+ check parseTypeDecl("unsigned char *") == "ptr cuchar"
+ check parseTypeDecl("const char* const[]") == "ptr ptr cchar"
+ check parseTypeDecl("const void*") == "pointer"
+ check parseTypeDecl("unsigned char**") == "ptr ptr cuchar"
+
+proc processParamName(name: string): string =
+ result = name.strip(chars=Whitespace + {'_'})
+ if result in ["type", "in", "ptr", "ref"]:
+ result = "`" & result & "`"
+
+proc parseFunctionPointerDecl(decl: string): string =
+ var returnType: string
+
+ var pos = parseUntil(decl, returnType, '(')
+
+ returnType = parseTypeDecl(returnType)
+
+ pos = pos + skipUntil(decl, '(', pos+1) + 2
+
+ var params: string
+
+ discard parseUntil(decl, params, ')', pos)
+
+ proc parseParamDecl(paramDecl: string): (string, string) =
+ var i = len(paramDecl)
+ while paramDecl[i-1] in IdentChars:
+ dec(i)
+ return (paramDecl[0 ..< i], paramDecl[i .. ^1])
+
+ let paramsList =
+ if params.len == 0:
+ ""
+ else:
+ params.split(",")
+ .mapIt(it.strip())
+ .mapIt(parseParamDecl(it))
+ .mapIt((parseTypeDecl(it[0]), processParamName(it[1])))
+ .mapIt(it[1] & ": " & it[0])
+ .join(", ")
+
+ result = &"proc ({paramsList})"
+
+ if returnType != "void":
+ result &= ": " & returnType
+
+ result &= " {.cdecl.}"
+
+when defined(test):
+ test "function type decl":
+ let cdecl = "void(*)(const ImDrawList* parent_list,const ImDrawCmd* cmd);"
+ let ndecl = "proc (parent_list: ptr ImDrawList, cmd: ptr ImDrawCmd) {.cdecl.}"
+ check parseFunctionPointerDecl(cdecl) == ndecl
+
+ let cdecl2 = "void(*)(const ImDrawList * parent_list,const ImDrawCmd *cmd);"
+ let ndecl2 = "proc (parent_list: ptr ImDrawList, cmd: ptr ImDrawCmd) {.cdecl.}"
+ check parseFunctionPointerDecl(cdecl2) == ndecl2
+
+ let cdecl3 = "const char *(*)();"
+ let ndecl3 = "proc (): cstring {.cdecl.}"
+ check parseFunctionPointerDecl(cdecl3) == ndecl3
+
+template withDir(dir: string, body: untyped): untyped =
+ var curdir = getCurrentDir()
+ try:
+ setCurrentDir(dir)
+ body
+ finally:
+ setCurrentDir(curdir)
+
+if not existsDir("cimgui"):
+ assert execCmd("git clone git@github.com:cimgui/cimgui.git") == 0
+
+ withDir("cimgui"):
+ assert execCmd("git checkout 1.73") == 0
+ assert execCmd("git submodule update --init") == 0
+
+let structsAndEnums = parseJson(readFile("cimgui/generator/output/structs_and_enums.json"))
+let typedefsDict = parseJson(readFile("cimgui/generator/output/typedefs_dict.json"))
+let functions = parseJson(readFile("cimgui/generator/output/definitions.json"))
+
+let file = open("imgui.nim", fmWrite)
+
+file.writeLine("""
+import os
+
+{.passC:"-I" & currentSourcePath().parentDir() / "imgui".}
+{.passC:"-I" & currentSourcePath().parentDir() / "imgui" / "examples".}
+
+{.passC:"-DIMGUI_IMPL_OPENGL_LOADER_GLAD".}
+
+{.compile:"imgui/imgui.cpp".}
+{.compile:"imgui/imgui_demo.cpp".}
+{.compile:"imgui/imgui_draw.cpp".}
+{.compile:"imgui/imgui_widgets.cpp".}
+{.compile:"imgui/examples/imgui_impl_opengl3.cpp".}
+{.compile:"imgui/examples/imgui_impl_raylib.cpp".}
+
+const
+ imguiHeader = "<imgui.h>"
+
+""")
+
+for enumName, value in structsAndEnums["enums"]:
+ for item in value:
+ var itemName = item["name"].getStr()
+
+ if itemName.startsWith("_") or itemName.endsWith("_") or itemName.contains("__"):
+ continue
+
+ var itemValue =
+ if item["value"].kind == JString:
+ item["value"].getStr()
+ .replace("<<", " shl ")
+ .replace("|", " or ")
+ else:
+ $item["value"].getInt()
+
+ file.writeLine(&" {itemName}* = ({itemValue}).cint")
+
+ file.writeLine("")
+
+file.writeLine("type")
+
+
+let typeOverride = newStringTable()
+typeOverride["ImGuiStoragePair"] = """
+ ImGuiStoragePair* {.importcpp, header: imguiHeader.} = object
+"""
+
+file.writeLine("""
+ ImVector*[T] {.importcpp, header: imguiHeader.} = object
+ Size*: cint
+ Capacity*: cint
+ Data*: ptr T
+
+ ImDrawListSharedData* {.importcpp, header: imguiHeader.} = object
+
+ ImGuiContext* {.importcpp, header: imguiHeader.} = object
+
+ va_list* {.importc, header:"<stdarg.h>".} = object
+
+""")
+
+for alias, value in typedefsDict:
+ var value = value.getStr()
+
+ if value == &"struct {alias}":
+ continue
+
+ if alias in ["const_iterator", "iterator", "value_type"]:
+ continue
+
+ if "(" in value:
+ file.writeLine(&" {alias}* = {parseFunctionPointerDecl(value)}")
+ else:
+ file.writeLine(&" {alias}* = {parseTypeDecl(value)}")
+
+file.writeLine("")
+
+for structName, fields in structsAndEnums["structs"]:
+ if structName in typeOverride:
+ file.writeLine(typeOverride[structName])
+ continue
+
+ file.writeLine(&" {structName}* {{.importcpp, header: imguiHeader.}} = object")
+
+ for item in fields:
+ var itemName = item["name"].getStr()
+
+ if itemName.startsWith("_"):
+ continue
+
+ var itemType = item["type"].getStr()
+
+ if "template_type" in item:
+ let p = itemType.split("_")
+ let container = p[0]
+ itemType = item["template_type"].getStr()
+ itemType = parseTypeDecl(itemType)
+ itemType = &"{container}[{itemType}]"
+ else:
+ if "(" in itemType:
+ itemType = parseFunctionPointerDecl(itemType)
+ else:
+ itemType = parseTypeDecl(itemType)
+
+ var n = ""
+ var s = ""
+ if scanf(itemName, "$w[$*]", n, s):
+ itemName = n
+ s = s[0..^1]
+ itemType = &"array[{s}, {itemType}]"
+
+ file.writeLine(&" {itemName}*: {itemType}")
+
+ file.writeLine("")
+
+type
+ Function = ref object
+ isConstructor: bool
+ isDestructor: bool
+ structName: string
+ namespace: string
+ name: string
+ overloads: seq[Overload]
+
+ Overload = ref object
+ isVararg: bool
+ paramList: string
+ pragmas: string
+ isTemplated: bool
+ returnType: string
+ signature: string
+
+iterator items(t: OrderedTable[string, JsonNode]): (string, JsonNode) =
+ for k, v in t:
+ yield (k, v)
+
+proc newOverload(f: Function, j: JsonNode): Overload =
+ new(result)
+
+ result.isVararg = "isvararg" in j
+
+ result.isTemplated = "templated" in j
+
+ if not f.isConstructor:
+ result.returnType =
+ if j["ret"].getStr() == "void":
+ ""
+ else:
+ if "retref" in j:
+ parseTypeDecl(j["ret"].getStr()).replace("ptr", "var")
+ else:
+ parseTypeDecl(j["ret"].getStr())
+ else:
+ result.returnType = f.structName
+
+ var pragmaSeq = @[
+ # &"importcpp: \"{structName}(@)\"",
+ # "importcpp",
+ "header: imguiHeader",
+ ]
+
+ if f.isConstructor:
+ pragmaSeq.add(&"importcpp: \"{f.structName}(@)\"")
+ elif f.structName.len > 0:
+ pragmaSeq.add(&"importcpp: \"#.{f.name}(@)\"")
+ elif f.namespace.len > 0:
+ pragmaSeq.add(&"importcpp: \"{f.namespace}::{f.name}(@)\"")
+
+ if result.isVararg:
+ pragmaSeq.add("varargs")
+
+ result.pragmas = "{." & pragmaSeq.join(", ") & ".}"
+
+ proc processDefaultValue(s: string): string =
+ if s == "((void*)0)":
+ return "nil"
+ elif s == "FLT_MAX":
+ return "high(cfloat)"
+
+ # is the default an instantiation of a struct?
+ var name, args: string
+ if scanf(s, "$w($+)$.", name, args):
+ if name == "sizeof":
+ return &"sizeof({parseTypeDecl(args)}).cint"
+ return &"init{name}({args})"
+
+ return s.replace("<<", " shl ").replace("|", " or ")
+
+ proc formatParam(name, typ, def: string): string =
+ var def =
+ if def.len > 0:
+ " = " & def
+ else:
+ ""
+ var typeName: string
+ if scanf(typ, "$w&$.", typeName):
+ typeName = "var " & parseTypeDecl(typeName)
+ else:
+ typeName = typ
+ result = name & ": " & typeName & def
+
+ let defaults = newStringTable(j["defaults"].getFields()
+ .mapIt((it[0], processDefaultValue(it[1].getStr()))),
+ modeCaseSensitive)
+
+ result.paramList = j["argsT"]
+ .mapIt((it["name"].getStr(), it["type"].getStr()))
+ .filterIt(it[0] notin ["self", "..."])
+ .mapIt((processParamName(it[0]), parseTypeDecl(it[1]), defaults.getOrDefault(it[0], "")))
+ .mapIt(formatParam(it[0], it[1], it[2]))
+ .join(", ")
+
+ # Helps us to filter out overloads that differ in constness only
+ result.signature = j["signature"].getStr()
+ if result.signature.endsWith(")const"):
+ result.signature = result.signature[0..^6]
+
+proc newFunction(j: JsonNode): Function =
+ new(result)
+
+ result.isConstructor = "constructor" in j[0]
+
+ result.isDestructor = "destructor" in j[0]
+
+ result.structName = j[0]["stname"].getStr()
+
+ result.namespace =
+ if "namespace" in j[0]:
+ j[0]["namespace"].getStr()
+ else:
+ ""
+
+ result.name =
+ if result.isDestructor:
+ result.structName
+ else:
+ j[0]["funcname"].getStr()
+
+ result.overloads = @[]
+
+ # Don't load overloads for private functions
+ if result.name.startsWith("_"):
+ return
+
+ var signatures = initHashSet[string]()
+ for node in j:
+ if "nonUDT" in node:
+ continue
+
+ let overload = newOverload(result, node)
+
+ if overload.signature notin signatures:
+ result.overloads.add(overload)
+ signatures.incl(overload.signature)
+
+proc dump(f: Function) =
+ for overload in f.overloads:
+ if f.isConstructor:
+ file.writeLine(&"proc init{f.structName}*({overload.paramList}): {f.structName} {overload.pragmas}")
+ # elif isDestructor:
+ # discard
+ elif f.structName.len > 0:
+ let templ =
+ if overload.isTemplated:
+ "[T]"
+ else:
+ ""
+ let args =
+ if overload.paramList.len > 0:
+ &"self: var {f.structName}{templ}, {overload.paramList}"
+ else:
+ &"self: var {f.structName}{templ}"
+ let returnType =
+ if overload.returnType == "":
+ ""
+ else:
+ ": " & overload.returnType
+ let name =
+ if f.name in ["end"]:
+ "`" & f.name & "`"
+ else:
+ f.name
+ file.writeLine(&"proc {name}*{templ}({args}){returnType} {overload.pragmas}")
+ elif f.namespace.len > 0:
+ let returnType =
+ if overload.returnType == "":
+ ""
+ else:
+ ": " & overload.returnType
+ file.writeLine(&"proc {f.name}*({overload.paramList}){returnType} {overload.pragmas}")
+
+var processedFunctions = functions.getFields().mapIt(newFunction(it[1])).toSeq()
+
+let skipStructs = ["ImGuiStoragePair", "ImGuiTextBuffer", "ImGuiTextRange"]
+
+for function in processedFunctions:
+ if not function.isConstructor:
+ continue
+ if function.structName in skipStructs:
+ continue
+ function.dump()
+
+for function in processedFunctions:
+ # A little hack for the static function
+ if function.structName == "ImColor" and function.name == "HSV":
+ function.namespace = "ImColor"
+ function.structName = ""
+ if function.isConstructor or function.isDestructor:
+ continue
+ if function.structName in skipStructs:
+ continue
+ function.dump()
+
+
+file.writeLine("""
+
+const
+ ImGuiImplOpengl3Header = "<imgui_impl_opengl3.h>"
+ ImGuiImplRaylibHeader = "<imgui_impl_raylib.h>"
+
+proc ImGui_ImplOpenGL3_Init*(glsl_version: cstring = nil): bool {.importcpp:"ImGui_ImplOpenGL3_Init(@)", header:ImGuiImplOpengl3Header.}
+proc ImGui_ImplOpenGL3_Shutdown*() {.importcpp:"ImGui_ImplOpenGL3_Shutdown(@)", header:ImGuiImplOpengl3Header.}
+proc ImGui_ImplOpenGL3_NewFrame*() {.importcpp:"ImGui_ImplOpenGL3_NewFrame(@)", header:ImGuiImplOpengl3Header.}
+proc ImGui_ImplOpenGL3_RenderDrawData*(draw_data: ptr ImDrawData) {.importcpp:"ImGui_ImplOpenGL3_RenderDrawData(@)", header:ImGuiImplOpengl3Header.}
+
+proc ImGui_ImplOpenGL3_CreateFontsTexture*(): bool {.importcpp:"ImGui_ImplOpenGL3_CreateFontsTexture(@)", header:ImGuiImplOpengl3Header.}
+proc ImGui_ImplOpenGL3_DestroyFontsTexture*() {.importcpp:"ImGui_ImplOpenGL3_DestroyFontsTexture(@)", header:ImGuiImplOpengl3Header.}
+proc ImGui_ImplOpenGL3_CreateDeviceObjects*(): bool {.importcpp:"ImGui_ImplOpenGL3_CreateDeviceObjects(@)", header:ImGuiImplOpengl3Header.}
+proc ImGui_ImplOpenGL3_DestroyDeviceObjects*() {.importcpp:"ImGui_ImplOpenGL3_DestroyDeviceObjects(@)", header:ImGuiImplOpengl3Header.}
+
+proc ImGui_ImplRaylib_Init*(): bool {.importcpp:"ImGui_ImplRaylib_Init(@)", header:ImGuiImplRaylibHeader.}
+proc ImGui_ImplRaylib_Shutdown*() {.importcpp:"ImGui_ImplRaylib_Shutdown(@)", header:ImGuiImplRaylibHeader.}
+proc ImGui_ImplRaylib_NewFrame*() {.importcpp:"ImGui_ImplRaylib_NewFrame(@)", header:ImGuiImplRaylibHeader.}
+proc ImGui_ImplRaylib_ProcessEvent*(): bool {.importcpp:"ImGui_ImplRaylib_ProcessEvent(@)", header:ImGuiImplRaylibHeader.}
+
+""")
diff --git a/tools/raylib_wrap.nim b/tools/raylib_wrap.nim
new file mode 100644
index 0000000..d740b02
--- /dev/null
+++ b/tools/raylib_wrap.nim
@@ -0,0 +1,46 @@
+import os
+import strformat
+
+import nimterop/[cimport, build]
+
+static:
+ cDebug()
+ cDisableCaching()
+
+cOverride:
+ type
+ MusicData = object
+ Music = ptr MusicData
+
+cImport(currentSourcePath().parentDir().parentDir() / "src" / "foolib" / "raylib" / "src" / "raylib.h")
+
+proc initColor*(r, g, b, a: int): Color =
+ Color(r: r.cuchar, g: g.cuchar, b: b.cuchar, a: a.cuchar)
+
+const LIGHTGRAY* = initColor(200, 200, 200, 255) # Light Gray
+const GRAY* = initColor(130, 130, 130, 255) # Gray
+const DARKGRAY* = initColor(80, 80, 80, 255) # Dark Gray
+const YELLOW* = initColor(253, 249, 0, 255) # Yellow
+const GOLD* = initColor(255, 203, 0, 255) # Gold
+const ORANGE* = initColor(255, 161, 0, 255) # Orange
+const PINK* = initColor(255, 109, 194, 255) # Pink
+const RED* = initColor(230, 41, 55, 255) # Red
+const MAROON* = initColor(190, 33, 55, 255) # Maroon
+const GREEN* = initColor(0, 228, 48, 255) # Green
+const LIME* = initColor(0, 158, 47, 255) # Lime
+const DARKGREEN* = initColor(0, 117, 44, 255) # Dark Green
+const SKYBLUE* = initColor(102, 191, 255, 255) # Sky Blue
+const BLUE* = initColor(0, 121, 241, 255) # Blue
+const DARKBLUE* = initColor(0, 82, 172, 255) # Dark Blue
+const PURPLE* = initColor(200, 122, 255, 255) # Purple
+const VIOLET* = initColor(135, 60, 190, 255) # Violet
+const DARKPURPLE* = initColor(112, 31, 126, 255) # Dark Purple
+const BEIGE* = initColor(211, 176, 131, 255) # Beige
+const BROWN* = initColor(127, 106, 79, 255) # Brown
+const DARKBROWN* = initColor(76, 63, 47, 255) # Dark Brown
+
+const WHITE* = initColor(255, 255, 255, 255) # White
+const BLACK* = initColor(0, 0, 0, 255) # Black
+const BLANK* = initColor(0, 0, 0, 0) # Blank (Transparent)
+const MAGENTA* = initColor(255, 0, 255, 255) # Magenta
+const RAYWHITE* = initColor(245, 245, 245, 255) # My own White (raylib logo)