|
| 1 | +## This nimscript file is used to generate bindings (webui/bindings.nim) based on webui.h |
| 2 | +## pre-requisites: |
| 3 | +## 1. Nim: https://nim-lang.org/install.html |
| 4 | +## 2. c2nim: nimble install c2nim |
| 5 | +## Usage: |
| 6 | +## nim scripts/generate_bindings.nims |
| 7 | + |
| 8 | + |
| 9 | +import os, strformat, strutils |
| 10 | + |
| 11 | +# Step 1: use c2nim to generate bindings (initial output) |
| 12 | + |
| 13 | +let c2nim = findExe "c2nim" |
| 14 | +if c2nim.len == 0: |
| 15 | + raise newException(OSError, "c2nim not found; run `nimble install c2nim`") |
| 16 | + quit(1) |
| 17 | +else: |
| 18 | + echo "Using c2nim at : ", c2nim |
| 19 | + |
| 20 | +let root_path = currentSourcePath.parentDir.parentDir |
| 21 | +let bindings_path = root_path / "webui" / "bindings.nim" |
| 22 | + |
| 23 | +echo "Root dir: ", root_path |
| 24 | + |
| 25 | +var cmd = fmt"{c2nim} -o:{bindings_path} --reordercomments " |
| 26 | +cmd &= " --importc " |
| 27 | +cmd &= " --def:webui_browser=WebuiBrowser " |
| 28 | +cmd &= " --def:webui_runtime=WebuiRuntime " |
| 29 | +cmd &= " --def:webui_event=WebuiEvent " |
| 30 | +cmd &= " --def:webui_config=WebuiConfig " |
| 31 | +cmd &= " --def:webui_event_t=Event " |
| 32 | +cmd &= " --def:webui_logger_level=WebuiLoggerLevel " |
| 33 | +cmd &= fmt"{root_path}/webui/webui/include/webui.h " |
| 34 | +# cmd &= " --delete:_WIN32 " |
| 35 | +echo "Running command: ", cmd |
| 36 | +exec cmd |
| 37 | + |
| 38 | +## Step 2: add the following code to include webui/bindings_include.nim, which defines some necessary macros and types |
| 39 | +# fix the generated bindings file |
| 40 | +let code_prefix = """ |
| 41 | +## This file is automatically generated by command : "nim scripts/generate_bindings.nims" |
| 42 | +## Do not edit this file directly |
| 43 | +## |
| 44 | +
|
| 45 | +include ./bindings_prefix.nim |
| 46 | +
|
| 47 | +""" |
| 48 | + |
| 49 | +let code_suffix = """ |
| 50 | +
|
| 51 | +include ./bindings_suffix.nim |
| 52 | +""" |
| 53 | + |
| 54 | +### Step 3: remove compiler related "when defined" blocks; add webui pragma; fix function names; fix callback annotations |
| 55 | + |
| 56 | +## blocks like when defined(_WIN32) are commented out |
| 57 | +proc removeWhenDefined(code: string): string = |
| 58 | + var lines = code.splitLines() |
| 59 | + var blocks : seq[tuple[startRow, endRow: int]] |
| 60 | + |
| 61 | + var startRow, endRow = -1 |
| 62 | + for i in 0 ..< lines.len: |
| 63 | + let row = lines[i] |
| 64 | + if row.startsWith("when defined") or row.startsWith("when not defined"): |
| 65 | + if startRow >= 0: |
| 66 | + endRow = i |
| 67 | + blocks.add((startRow, endRow)) |
| 68 | + startRow = i |
| 69 | + endRow = -1 |
| 70 | + else: |
| 71 | + startRow = i |
| 72 | + elif startRow >= 0: |
| 73 | + if row.startsWith(' ') or row.startsWith('\t'): |
| 74 | + continue |
| 75 | + elif row.startsWith("else"): |
| 76 | + continue |
| 77 | + elif row.startswith("#"): |
| 78 | + continue |
| 79 | + elif row.len == 0: |
| 80 | + continue |
| 81 | + elif row.startsWith("type") or row.startsWith("const") or row.startsWith("var") or row.startsWith("proc"): |
| 82 | + endRow = i - 1 |
| 83 | + blocks.add((startRow, endRow)) |
| 84 | + startRow = -1 |
| 85 | + endRow = -1 |
| 86 | + else: |
| 87 | + endRow = i |
| 88 | + blocks.add((startRow, endRow)) |
| 89 | + startRow = -1 |
| 90 | + endRow = -1 |
| 91 | + |
| 92 | + for (startRow, endRow) in blocks: |
| 93 | + for lineIdx in startRow .. endRow: |
| 94 | + lines[lineIdx] = "# " & lines[lineIdx] |
| 95 | + |
| 96 | + result = lines.join("\n") |
| 97 | + |
| 98 | +## each proc definition is annotated with {.webui.} in addition to importc: |
| 99 | +proc addWebuiPragma(code: string): string = |
| 100 | + code.replace("importc:", "webui, importc:") |
| 101 | + |
| 102 | +## remove "webui_" prefix from proc names |
| 103 | +proc fixFunctionName(code: string): string = |
| 104 | + code.replace( |
| 105 | + "proc webui_", "proc " |
| 106 | + ).replace( |
| 107 | + "proc bind", "proc `bind`" |
| 108 | + ) |
| 109 | + |
| 110 | +## for the callback function type in arguments, annotate it with {.webui.}, as default call standard for function pointer is closure |
| 111 | +proc fixCallbackAnnotation(code: string): string = |
| 112 | + let callback_start = "proc (" |
| 113 | + var annotation_inject_pos : seq[int] |
| 114 | + var pos = 0 |
| 115 | + |
| 116 | + while true: |
| 117 | + pos = code.find(callback_start, start = pos) |
| 118 | + let start_pos = pos |
| 119 | + if pos == -1: |
| 120 | + break |
| 121 | + pos += callback_start.len |
| 122 | + while code[pos] != ')': |
| 123 | + pos += 1 |
| 124 | + pos += 1 |
| 125 | + if code[pos] == ':': |
| 126 | + pos += 1 |
| 127 | + while code[pos] == ' ' or code[pos] == '\t': |
| 128 | + pos += 1 |
| 129 | + while code[pos] in {'a'..'z','A'..'Z', '0'..'9'}: |
| 130 | + pos += 1 |
| 131 | + annotation_inject_pos.add(pos-1) |
| 132 | + echo "adding webui pragma to function type: ", code[start_pos ..< pos], "\n" |
| 133 | + |
| 134 | + var prev_end = 0 |
| 135 | + for injection_pos in annotation_inject_pos: |
| 136 | + result &= code[prev_end .. injection_pos] |
| 137 | + result &= " {.webui.}" |
| 138 | + prev_end = injection_pos + 1 |
| 139 | + if prev_end < code.len: |
| 140 | + result &= code[prev_end ..< code.len] |
| 141 | + |
| 142 | +## add enum pragma to generate legacy constants |
| 143 | +proc addEnumPragma(code: string): string = |
| 144 | + code.replace( |
| 145 | + "* = enum", "* {.renameEnumFields.} = enum" |
| 146 | + ).replace( |
| 147 | + "WEBUI_EVENT_", "WEBUI_EVENTS_" |
| 148 | + ) |
| 149 | + |
| 150 | + # code.replace( |
| 151 | + # "WEBUI_EVENT_", "WEBUI_EVENTS_" |
| 152 | + # ) |
| 153 | + |
| 154 | +## fix the generated code |
| 155 | +let modified_code = (code_prefix & readFile(bindings_path) & code_suffix).removeWhenDefined( |
| 156 | + ).addWebuiPragma( |
| 157 | + ).fixFunctionName( |
| 158 | + ).fixCallbackAnnotation( |
| 159 | + ).addEnumPragma( |
| 160 | + ) |
| 161 | + |
| 162 | + |
| 163 | +## output the final code |
| 164 | +writeFile( |
| 165 | + bindings_path, |
| 166 | + modified_code |
| 167 | +) |
| 168 | + |
0 commit comments