Skip to content

Commit 3c61fba

Browse files
authored
Merge pull request #48 from YesDrX/auto_gen_nim_bindings
Auto gen nim bindings
2 parents a97eff8 + 13a905c commit 3c61fba

File tree

9 files changed

+1817
-503
lines changed

9 files changed

+1817
-503
lines changed

.github/workflows/test.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ jobs:
4343
- name: Init submodules
4444
run: git submodule update --init
4545

46+
- name: Generate bindings
47+
run: |
48+
nimble install c2nim
49+
nim scripts/generate_bindings.nims
50+
4651
- name: Compile examples
4752
run: |
4853
nim c examples/minimal.nim
@@ -54,4 +59,4 @@ jobs:
5459
5560
nim c examples/serve_folder/serve_folder.nim
5661
nim c examples/qxexample/qxexample.nim
57-
# nim c examples/text_editor/src/text_editor.nim
62+
# nim c examples/text_editor/src/text_editor.nim # osfiles/dialog not available in macos

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ git clone --recursive https://github.com/webui-dev/nim-webui.git
7676

7777
The Nim library exposes two files: `webui.nim` and `webui/bindings.nim`.
7878
`webui/bindings.nim` are low-level bindings for WebUI, generated by
79-
[c2nim](https://github.com/nim-lang/c2nim). `webui.nim` is a high-level wrapper for
79+
[c2nim](https://github.com/nim-lang/c2nim) using script [generate_bindings.nims](./scripts/generate_bindings.nims). `webui.nim` is a high-level wrapper for
8080
WebUI, using native Nim types and avoiding pointers.
8181

8282
The wrapper and bindings also allow to to control whether or not to statically

scripts/generate_bindings.nims

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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+

webui.nim

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ proc browserExist*(browser: bindings.WebuiBrowser): bool =
119119
##
120120
## Returns `true` if the specified browser is available.
121121

122-
bindings.browserExist(browser)
122+
bindings.browserExist(browser.csize_t)
123123

124124
proc setConfig*(option: bindings.WebuiConfig; status: bool) =
125125
## Control WebUI's behaviour via setting configuration option `option` to either
@@ -480,7 +480,7 @@ proc `port=`*(window: Window, port: int) =
480480
## :window: The window
481481
## :port: The web-server network port WebUI should use
482482

483-
bindings.setPort(csize_t window, csize_t port)
483+
discard bindings.setPort(csize_t window, csize_t port)
484484

485485
proc setIcon*(window: Window; icon, mime: string) =
486486
## Set the default embedded HTML favicon.
@@ -713,7 +713,7 @@ proc `bind`*(window: Window; element: string; `func`: proc (e: Event)) =
713713
## An empty element means `func` will be bound to all events.
714714
## :func: The function to bind to `element`.
715715

716-
let bid = int bindings.bind(csize_t window, cstring element, bindHandler)
716+
let bid = int bindings.`bind`(csize_t window, cstring element, bindHandler)
717717
let wid = int bindings.interfaceGetWindowId(csize_t window)
718718

719719
cbs[wid][bid] = `func`

webui.nimble

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Package
22

3-
version = "2.5.0.0"
3+
version = "2.5.0.1"
44
author = "Jasmine"
55
description = "Wrapper for WebUI"
66
license = "MIT"

0 commit comments

Comments
 (0)