Move LuaJIT compat shims into backends/imgui_impl_love.lua (COMPAT section); remove compat/

This commit is contained in:
4DBug
2026-10-08 19:31:22 -05:00
parent 66fa061089
commit 5dcda2b550
7 changed files with 288 additions and 145 deletions
+2 -2
View File
@@ -172,11 +172,11 @@ io.Fonts:AddFontFromFileTTF("fonts/Roboto-Medium.ttf", 16.0)
The same sources also build for [LÖVE](https://love2d.org) (LuaJIT), with a GPU renderer in [backends/imgui_impl_love.lua](backends/imgui_impl_love.lua):
```
python3 tools/bundle.py --target love # -> build/imgui_love.lua (LÖVE backend + compat/luajit.lua shims)
python3 tools/bundle.py --target love # -> build/imgui_love.lua (LÖVE backend + shims from the LÖVE backend)
love examples/example_love
```
Roblox stays the primary target: the Roblox build is untouched by this (no shims, `@native` kept); the LÖVE build strips `@native` and adds small `bit32`/`buffer`/`utf8`/`table` shims ([compat/luajit.lua](compat/luajit.lua)). `tools/test/love/run.sh` checks that LuaJIT produces exactly the same draw data as Luau.
Roblox stays the primary target: the Roblox build is untouched by this (no shims, `@native` kept); the LÖVE build strips `@native` and adds small `bit32`/`buffer`/`utf8`/`table` shims (the COMPAT section of [backends/imgui_impl_love.lua](backends/imgui_impl_love.lua)). `tools/test/love/run.sh` checks that LuaJIT produces exactly the same draw data as Luau.
[examples/example_shared/app.lua](examples/example_shared/app.lua) is one UI that runs unchanged on both: `roblox.client.luau` (Rojo: `examples/example_shared/default.project.json`) and `main.lua` (`love examples/example_shared`) are the only platform code. Write shared UI code in plain Lua 5.1 syntax (no `+=`, `//`, `continue`).
+140
View File
@@ -6,7 +6,147 @@
--- love.draw: ImGui_ImplLove.RenderDrawData(ImGui.GetDrawData())
--- forward love.mousemoved/mousepressed/mousereleased/wheelmoved/keypressed/keyreleased/textinput to ImGui_ImplLove.*
-- [SECTION] COMPAT (Luau stdlib for LuaJIT). tools/bundle.py --target love runs this section before the library.
--@compat-begin
local ffi = require("ffi")
local floor = math.floor
-- Luau standard library pieces the port uses, for LuaJIT (LÖVE). Prepended by `tools/bundle.py --target love` only:
-- the Roblox build uses Luau's native bit32/buffer/table/utf8 and never sees this file.
local bit = require("bit")
local TWO32 = 4294967296
-- bit32: unsigned 32-bit results like Luau/Lua 5.2 (LuaJIT's `bit` returns signed values)
if not bit32 then
local band, bor, bxor, bnot, lshift, rshift, arshift, tobit = bit.band, bit.bor, bit.bxor, bit.bnot, bit.lshift, bit.rshift, bit.arshift, bit.tobit
local function u(x) return x % TWO32 end
local function t(x) return tobit(x % TWO32) end -- any number -> int32 (bit.* wraps doubles >= 2^31 unreliably)
bit32 = {}
function bit32.band(a, b, ...)
if a == nil then return TWO32 - 1 end
if b == nil then return u(t(a)) end
local r = band(t(a), t(b))
if ... ~= nil then for i = 1, select("#", ...) do r = band(r, t((select(i, ...)))) end end
return u(r)
end
function bit32.bor(a, b, ...)
if a == nil then return 0 end
if b == nil then return u(t(a)) end
local r = bor(t(a), t(b))
if ... ~= nil then for i = 1, select("#", ...) do r = bor(r, t((select(i, ...)))) end end
return u(r)
end
function bit32.bxor(a, b, ...)
if a == nil then return 0 end
if b == nil then return u(t(a)) end
local r = bxor(t(a), t(b))
if ... ~= nil then for i = 1, select("#", ...) do r = bxor(r, t((select(i, ...)))) end end
return u(r)
end
function bit32.bnot(a) return u(bnot(t(a))) end
function bit32.lshift(a, n) if n >= 32 or n <= -32 then return 0 end if n < 0 then return u(rshift(t(a), -n)) end return u(lshift(t(a), n)) end
function bit32.rshift(a, n) if n >= 32 or n <= -32 then return 0 end if n < 0 then return u(lshift(t(a), -n)) end return u(rshift(t(a), n)) end
function bit32.arshift(a, n) if n >= 32 then n = 31 end return u(arshift(t(a), n)) end
function bit32.btest(...) return bit32.band(...) ~= 0 end
function bit32.extract(n, field, width) width = width or 1; return u(band(rshift(t(n), field), 2 ^ width - 1)) end
end
-- buffer: fixed-size byte buffers (subset used by the port), on FFI memory. Little-endian like Luau.
if not buffer then
ffi.cdef("void *memmove(void *dst, const void *src, size_t n); void *memset(void *s, int c, size_t n);")
local C = ffi.C
local u8p, u32p, f64p = ffi.typeof("uint8_t*"), ffi.typeof("uint32_t*"), ffi.typeof("double*")
buffer = {}
function buffer.create(n)
local mem = ffi.new("uint8_t[?]", math.max(n, 1)) -- zero-filled
return { p = ffi.cast(u8p, mem), n = n, mem = mem }
end
function buffer.len(b) return b.n end
function buffer.readu8(b, o) return b.p[o] end
function buffer.writeu8(b, o, v) b.p[o] = v end
function buffer.readu32(b, o) return ffi.cast(u32p, b.p + o)[0] end
function buffer.writeu32(b, o, v) ffi.cast(u32p, b.p + o)[0] = v % TWO32 end
function buffer.readf64(b, o) return ffi.cast(f64p, b.p + o)[0] end
function buffer.writef64(b, o, v) ffi.cast(f64p, b.p + o)[0] = v end
function buffer.readstring(b, o, n) return ffi.string(b.p + o, n) end
function buffer.writestring(b, o, s, n) ffi.copy(b.p + o, s, n or #s) end
function buffer.copy(dst, doff, src, soff, n)
soff = soff or 0
n = n or (src.n - soff)
if n > 0 then C.memmove(dst.p + doff, src.p + soff, n) end
end
function buffer.fill(b, o, v, n) n = n or (b.n - o); if n > 0 then C.memset(b.p + o, v, n) end end
function buffer.tostring(b) return ffi.string(b.p, b.n) end
function buffer.fromstring(s) local b = buffer.create(#s); ffi.copy(b.p, s, #s); return b end
end
-- table / math / utf8 additions from Luau / Lua 5.3
do
local ok, clear = pcall(require, "table.clear")
table.clear = table.clear or (ok and clear) or function(t) for k in pairs(t) do t[k] = nil end end
table.unpack = table.unpack or unpack
table.find = table.find or function(t, v, init) for i = init or 1, #t do if t[i] == v then return i end end end
table.move = table.move or function(a1, f, e, t, a2)
a2 = a2 or a1
if e >= f then
if t > f or t > e or a1 ~= a2 then for i = 0, e - f do a2[t + i] = a1[f + i] end
else for i = e - f, 0, -1 do a2[t + i] = a1[f + i] end end
end
return a2
end
math.pow = math.pow or function(a, b) return a ^ b end
end
if not utf8 then
utf8 = { charpattern = "[\0-\x7F\xC2-\xFD][\x80-\xBF]*" }
function utf8.char(...)
local out = {}
for i = 1, select("#", ...) do
local c = select(i, ...)
if c < 0x80 then out[#out + 1] = string.char(c)
elseif c < 0x800 then out[#out + 1] = string.char(0xC0 + floor(c / 64), 0x80 + c % 64)
elseif c < 0x10000 then out[#out + 1] = string.char(0xE0 + floor(c / 4096), 0x80 + floor(c / 64) % 64, 0x80 + c % 64)
else out[#out + 1] = string.char(0xF0 + floor(c / 262144), 0x80 + floor(c / 4096) % 64, 0x80 + floor(c / 64) % 64, 0x80 + c % 64) end
end
return table.concat(out)
end
local function decode(s, i)
local c = s:byte(i)
if not c then return nil end
if c < 0x80 then return c, 1 end
if c < 0xE0 then return (c % 32) * 64 + s:byte(i + 1) % 64, 2 end
if c < 0xF0 then return (c % 16) * 4096 + (s:byte(i + 1) % 64) * 64 + s:byte(i + 2) % 64, 3 end
return (c % 8) * 262144 + (s:byte(i + 1) % 64) * 4096 + (s:byte(i + 2) % 64) * 64 + s:byte(i + 3) % 64, 4
end
function utf8.codes(s)
local i = 1
return function()
if i > #s then return nil end
local c, n = decode(s, i)
local p = i; i = i + n
return p, c
end
end
function utf8.codepoint(s, i, j)
i = i or 1; j = j or i
local out = {}
while i <= j do local c, n = decode(s, i); out[#out + 1] = c; i = i + n end
return unpack(out)
end
function utf8.len(s) local n = 0; for _ in utf8.codes(s) do n = n + 1 end return n end
function utf8.offset(s, n, i)
i = i or (n >= 0 and 1 or #s + 1)
if n > 0 then
n = n - 1
while n > 0 and i <= #s do i = i + 1; while i <= #s and s:byte(i) >= 0x80 and s:byte(i) < 0xC0 do i = i + 1 end; n = n - 1 end
if n > 0 then return nil end
return i
end
return i
end
end
--@compat-end
local floor, min = math.floor, math.min
local ImGui_ImplLOVE_GetBackendData
+1 -1
View File
@@ -1 +1 @@
[[149, "imgui.lua"], [18609, "imgui_demo.lua"], [25894, "imgui_draw.lua"], [31132, "imgui_h.lua"], [33330, "imgui_impl_love.lua"], [33730, "imgui_internal.lua"], [36902, "imgui_tables.lua"], [41249, "imgui_widgets.lua"], [51004, "imstb_rectpack.lua"], [51450, "imstb_textedit.lua"], [52691, "imstb_truetype.lua"], [55159, "imstd_minstdio.lua"]]
[[149, "imgui.lua"], [18609, "imgui_demo.lua"], [25894, "imgui_draw.lua"], [31132, "imgui_h.lua"], [33330, "imgui_impl_love.lua"], [33870, "imgui_internal.lua"], [37042, "imgui_tables.lua"], [41389, "imgui_widgets.lua"], [51144, "imstb_rectpack.lua"], [51590, "imstb_textedit.lua"], [52831, "imstb_truetype.lua"], [55299, "imstd_minstdio.lua"]]
+142 -2
View File
@@ -1,10 +1,10 @@
-- GENERATED by tools/bundle.py --target love - do not edit
local ffi = require("ffi")
local floor = math.floor
-- Luau standard library pieces the port uses, for LuaJIT (LÖVE). Prepended by `tools/bundle.py --target love` only:
-- the Roblox build uses Luau's native bit32/buffer/table/utf8 and never sees this file.
local ffi = require("ffi")
local bit = require("bit")
local floor = math.floor
local TWO32 = 4294967296
-- bit32: unsigned 32-bit results like Luau/Lua 5.2 (LuaJIT's `bit` returns signed values)
@@ -33335,7 +33335,147 @@ __FILES['imgui_impl_love.lua'] = function()
--- love.draw: ImGui_ImplLove.RenderDrawData(ImGui.GetDrawData())
--- forward love.mousemoved/mousepressed/mousereleased/wheelmoved/keypressed/keyreleased/textinput to ImGui_ImplLove.*
-- [SECTION] COMPAT (Luau stdlib for LuaJIT). tools/bundle.py --target love runs this section before the library.
--@compat-begin
local ffi = require("ffi")
local floor = math.floor
-- Luau standard library pieces the port uses, for LuaJIT (LÖVE). Prepended by `tools/bundle.py --target love` only:
-- the Roblox build uses Luau's native bit32/buffer/table/utf8 and never sees this file.
local bit = require("bit")
local TWO32 = 4294967296
-- bit32: unsigned 32-bit results like Luau/Lua 5.2 (LuaJIT's `bit` returns signed values)
if not bit32 then
local band, bor, bxor, bnot, lshift, rshift, arshift, tobit = bit.band, bit.bor, bit.bxor, bit.bnot, bit.lshift, bit.rshift, bit.arshift, bit.tobit
local function u(x) return x % TWO32 end
local function t(x) return tobit(x % TWO32) end -- any number -> int32 (bit.* wraps doubles >= 2^31 unreliably)
bit32 = {}
function bit32.band(a, b, ...)
if a == nil then return TWO32 - 1 end
if b == nil then return u(t(a)) end
local r = band(t(a), t(b))
if ... ~= nil then for i = 1, select("#", ...) do r = band(r, t((select(i, ...)))) end end
return u(r)
end
function bit32.bor(a, b, ...)
if a == nil then return 0 end
if b == nil then return u(t(a)) end
local r = bor(t(a), t(b))
if ... ~= nil then for i = 1, select("#", ...) do r = bor(r, t((select(i, ...)))) end end
return u(r)
end
function bit32.bxor(a, b, ...)
if a == nil then return 0 end
if b == nil then return u(t(a)) end
local r = bxor(t(a), t(b))
if ... ~= nil then for i = 1, select("#", ...) do r = bxor(r, t((select(i, ...)))) end end
return u(r)
end
function bit32.bnot(a) return u(bnot(t(a))) end
function bit32.lshift(a, n) if n >= 32 or n <= -32 then return 0 end if n < 0 then return u(rshift(t(a), -n)) end return u(lshift(t(a), n)) end
function bit32.rshift(a, n) if n >= 32 or n <= -32 then return 0 end if n < 0 then return u(lshift(t(a), -n)) end return u(rshift(t(a), n)) end
function bit32.arshift(a, n) if n >= 32 then n = 31 end return u(arshift(t(a), n)) end
function bit32.btest(...) return bit32.band(...) ~= 0 end
function bit32.extract(n, field, width) width = width or 1; return u(band(rshift(t(n), field), 2 ^ width - 1)) end
end
-- buffer: fixed-size byte buffers (subset used by the port), on FFI memory. Little-endian like Luau.
if not buffer then
ffi.cdef("void *memmove(void *dst, const void *src, size_t n); void *memset(void *s, int c, size_t n);")
local C = ffi.C
local u8p, u32p, f64p = ffi.typeof("uint8_t*"), ffi.typeof("uint32_t*"), ffi.typeof("double*")
buffer = {}
function buffer.create(n)
local mem = ffi.new("uint8_t[?]", math.max(n, 1)) -- zero-filled
return { p = ffi.cast(u8p, mem), n = n, mem = mem }
end
function buffer.len(b) return b.n end
function buffer.readu8(b, o) return b.p[o] end
function buffer.writeu8(b, o, v) b.p[o] = v end
function buffer.readu32(b, o) return ffi.cast(u32p, b.p + o)[0] end
function buffer.writeu32(b, o, v) ffi.cast(u32p, b.p + o)[0] = v % TWO32 end
function buffer.readf64(b, o) return ffi.cast(f64p, b.p + o)[0] end
function buffer.writef64(b, o, v) ffi.cast(f64p, b.p + o)[0] = v end
function buffer.readstring(b, o, n) return ffi.string(b.p + o, n) end
function buffer.writestring(b, o, s, n) ffi.copy(b.p + o, s, n or #s) end
function buffer.copy(dst, doff, src, soff, n)
soff = soff or 0
n = n or (src.n - soff)
if n > 0 then C.memmove(dst.p + doff, src.p + soff, n) end
end
function buffer.fill(b, o, v, n) n = n or (b.n - o); if n > 0 then C.memset(b.p + o, v, n) end end
function buffer.tostring(b) return ffi.string(b.p, b.n) end
function buffer.fromstring(s) local b = buffer.create(#s); ffi.copy(b.p, s, #s); return b end
end
-- table / math / utf8 additions from Luau / Lua 5.3
do
local ok, clear = pcall(require, "table.clear")
table.clear = table.clear or (ok and clear) or function(t) for k in pairs(t) do t[k] = nil end end
table.unpack = table.unpack or unpack
table.find = table.find or function(t, v, init) for i = init or 1, #t do if t[i] == v then return i end end end
table.move = table.move or function(a1, f, e, t, a2)
a2 = a2 or a1
if e >= f then
if t > f or t > e or a1 ~= a2 then for i = 0, e - f do a2[t + i] = a1[f + i] end
else for i = e - f, 0, -1 do a2[t + i] = a1[f + i] end end
end
return a2
end
math.pow = math.pow or function(a, b) return a ^ b end
end
if not utf8 then
utf8 = { charpattern = "[\0-\x7F\xC2-\xFD][\x80-\xBF]*" }
function utf8.char(...)
local out = {}
for i = 1, select("#", ...) do
local c = select(i, ...)
if c < 0x80 then out[#out + 1] = string.char(c)
elseif c < 0x800 then out[#out + 1] = string.char(0xC0 + floor(c / 64), 0x80 + c % 64)
elseif c < 0x10000 then out[#out + 1] = string.char(0xE0 + floor(c / 4096), 0x80 + floor(c / 64) % 64, 0x80 + c % 64)
else out[#out + 1] = string.char(0xF0 + floor(c / 262144), 0x80 + floor(c / 4096) % 64, 0x80 + floor(c / 64) % 64, 0x80 + c % 64) end
end
return table.concat(out)
end
local function decode(s, i)
local c = s:byte(i)
if not c then return nil end
if c < 0x80 then return c, 1 end
if c < 0xE0 then return (c % 32) * 64 + s:byte(i + 1) % 64, 2 end
if c < 0xF0 then return (c % 16) * 4096 + (s:byte(i + 1) % 64) * 64 + s:byte(i + 2) % 64, 3 end
return (c % 8) * 262144 + (s:byte(i + 1) % 64) * 4096 + (s:byte(i + 2) % 64) * 64 + s:byte(i + 3) % 64, 4
end
function utf8.codes(s)
local i = 1
return function()
if i > #s then return nil end
local c, n = decode(s, i)
local p = i; i = i + n
return p, c
end
end
function utf8.codepoint(s, i, j)
i = i or 1; j = j or i
local out = {}
while i <= j do local c, n = decode(s, i); out[#out + 1] = c; i = i + n end
return unpack(out)
end
function utf8.len(s) local n = 0; for _ in utf8.codes(s) do n = n + 1 end return n end
function utf8.offset(s, n, i)
i = i or (n >= 0 and 1 or #s + 1)
if n > 0 then
n = n - 1
while n > 0 and i <= #s do i = i + 1; while i <= #s and s:byte(i) >= 0x80 and s:byte(i) < 0xC0 do i = i + 1 end; n = n - 1 end
if n > 0 then return nil end
return i
end
return i
end
end
--@compat-end
local floor, min = math.floor, math.min
local ImGui_ImplLOVE_GetBackendData
-137
View File
@@ -1,137 +0,0 @@
-- Luau standard library pieces the port uses, for LuaJIT (LÖVE). Prepended by `tools/bundle.py --target love` only:
-- the Roblox build uses Luau's native bit32/buffer/table/utf8 and never sees this file.
local ffi = require("ffi")
local bit = require("bit")
local floor = math.floor
local TWO32 = 4294967296
-- bit32: unsigned 32-bit results like Luau/Lua 5.2 (LuaJIT's `bit` returns signed values)
if not bit32 then
local band, bor, bxor, bnot, lshift, rshift, arshift, tobit = bit.band, bit.bor, bit.bxor, bit.bnot, bit.lshift, bit.rshift, bit.arshift, bit.tobit
local function u(x) return x % TWO32 end
local function t(x) return tobit(x % TWO32) end -- any number -> int32 (bit.* wraps doubles >= 2^31 unreliably)
bit32 = {}
function bit32.band(a, b, ...)
if a == nil then return TWO32 - 1 end
if b == nil then return u(t(a)) end
local r = band(t(a), t(b))
if ... ~= nil then for i = 1, select("#", ...) do r = band(r, t((select(i, ...)))) end end
return u(r)
end
function bit32.bor(a, b, ...)
if a == nil then return 0 end
if b == nil then return u(t(a)) end
local r = bor(t(a), t(b))
if ... ~= nil then for i = 1, select("#", ...) do r = bor(r, t((select(i, ...)))) end end
return u(r)
end
function bit32.bxor(a, b, ...)
if a == nil then return 0 end
if b == nil then return u(t(a)) end
local r = bxor(t(a), t(b))
if ... ~= nil then for i = 1, select("#", ...) do r = bxor(r, t((select(i, ...)))) end end
return u(r)
end
function bit32.bnot(a) return u(bnot(t(a))) end
function bit32.lshift(a, n) if n >= 32 or n <= -32 then return 0 end if n < 0 then return u(rshift(t(a), -n)) end return u(lshift(t(a), n)) end
function bit32.rshift(a, n) if n >= 32 or n <= -32 then return 0 end if n < 0 then return u(lshift(t(a), -n)) end return u(rshift(t(a), n)) end
function bit32.arshift(a, n) if n >= 32 then n = 31 end return u(arshift(t(a), n)) end
function bit32.btest(...) return bit32.band(...) ~= 0 end
function bit32.extract(n, field, width) width = width or 1; return u(band(rshift(t(n), field), 2 ^ width - 1)) end
end
-- buffer: fixed-size byte buffers (subset used by the port), on FFI memory. Little-endian like Luau.
if not buffer then
ffi.cdef("void *memmove(void *dst, const void *src, size_t n); void *memset(void *s, int c, size_t n);")
local C = ffi.C
local u8p, u32p, f64p = ffi.typeof("uint8_t*"), ffi.typeof("uint32_t*"), ffi.typeof("double*")
buffer = {}
function buffer.create(n)
local mem = ffi.new("uint8_t[?]", math.max(n, 1)) -- zero-filled
return { p = ffi.cast(u8p, mem), n = n, mem = mem }
end
function buffer.len(b) return b.n end
function buffer.readu8(b, o) return b.p[o] end
function buffer.writeu8(b, o, v) b.p[o] = v end
function buffer.readu32(b, o) return ffi.cast(u32p, b.p + o)[0] end
function buffer.writeu32(b, o, v) ffi.cast(u32p, b.p + o)[0] = v % TWO32 end
function buffer.readf64(b, o) return ffi.cast(f64p, b.p + o)[0] end
function buffer.writef64(b, o, v) ffi.cast(f64p, b.p + o)[0] = v end
function buffer.readstring(b, o, n) return ffi.string(b.p + o, n) end
function buffer.writestring(b, o, s, n) ffi.copy(b.p + o, s, n or #s) end
function buffer.copy(dst, doff, src, soff, n)
soff = soff or 0
n = n or (src.n - soff)
if n > 0 then C.memmove(dst.p + doff, src.p + soff, n) end
end
function buffer.fill(b, o, v, n) n = n or (b.n - o); if n > 0 then C.memset(b.p + o, v, n) end end
function buffer.tostring(b) return ffi.string(b.p, b.n) end
function buffer.fromstring(s) local b = buffer.create(#s); ffi.copy(b.p, s, #s); return b end
end
-- table / math / utf8 additions from Luau / Lua 5.3
do
local ok, clear = pcall(require, "table.clear")
table.clear = table.clear or (ok and clear) or function(t) for k in pairs(t) do t[k] = nil end end
table.unpack = table.unpack or unpack
table.find = table.find or function(t, v, init) for i = init or 1, #t do if t[i] == v then return i end end end
table.move = table.move or function(a1, f, e, t, a2)
a2 = a2 or a1
if e >= f then
if t > f or t > e or a1 ~= a2 then for i = 0, e - f do a2[t + i] = a1[f + i] end
else for i = e - f, 0, -1 do a2[t + i] = a1[f + i] end end
end
return a2
end
math.pow = math.pow or function(a, b) return a ^ b end
end
if not utf8 then
utf8 = { charpattern = "[\0-\x7F\xC2-\xFD][\x80-\xBF]*" }
function utf8.char(...)
local out = {}
for i = 1, select("#", ...) do
local c = select(i, ...)
if c < 0x80 then out[#out + 1] = string.char(c)
elseif c < 0x800 then out[#out + 1] = string.char(0xC0 + floor(c / 64), 0x80 + c % 64)
elseif c < 0x10000 then out[#out + 1] = string.char(0xE0 + floor(c / 4096), 0x80 + floor(c / 64) % 64, 0x80 + c % 64)
else out[#out + 1] = string.char(0xF0 + floor(c / 262144), 0x80 + floor(c / 4096) % 64, 0x80 + floor(c / 64) % 64, 0x80 + c % 64) end
end
return table.concat(out)
end
local function decode(s, i)
local c = s:byte(i)
if not c then return nil end
if c < 0x80 then return c, 1 end
if c < 0xE0 then return (c % 32) * 64 + s:byte(i + 1) % 64, 2 end
if c < 0xF0 then return (c % 16) * 4096 + (s:byte(i + 1) % 64) * 64 + s:byte(i + 2) % 64, 3 end
return (c % 8) * 262144 + (s:byte(i + 1) % 64) * 4096 + (s:byte(i + 2) % 64) * 64 + s:byte(i + 3) % 64, 4
end
function utf8.codes(s)
local i = 1
return function()
if i > #s then return nil end
local c, n = decode(s, i)
local p = i; i = i + n
return p, c
end
end
function utf8.codepoint(s, i, j)
i = i or 1; j = j or i
local out = {}
while i <= j do local c, n = decode(s, i); out[#out + 1] = c; i = i + n end
return unpack(out)
end
function utf8.len(s) local n = 0; for _ in utf8.codes(s) do n = n + 1 end return n end
function utf8.offset(s, n, i)
i = i or (n >= 0 and 1 or #s + 1)
if n > 0 then
n = n - 1
while n > 0 and i <= #s do i = i + 1; while i <= #s and s:byte(i) >= 0x80 and s:byte(i) < 0xC0 do i = i + 1 end; n = n - 1 end
if n > 0 then return nil end
return i
end
return i
end
end
+1 -1
View File
@@ -21,7 +21,7 @@ there, but keep this port's existing data structures and conventions. Read the s
## Portability (LÖVE build)
- Library code must stay valid **Lua 5.1/LuaJIT syntax** apart from `@native` lines (stripped for LÖVE): no `+=`, `//`,
`continue`, type annotations, `if`-expressions or string interpolation. Luau library calls are fine only if
`compat/luajit.lua` shims them (`bit32`, `buffer`, `table.clear/find/move/unpack`, `utf8`).
the COMPAT section of `backends/imgui_impl_love.lua` shims them (`bit32`, `buffer`, `table.clear/find/move/unpack`, `utf8`).
- Check with `tools/test/love/run.sh` (LuaJIT draw checksums must equal the Luau ones).
## Conventions (follow what the port already does)
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# Bundles the root *.lua (library + imgui_demo.lua), backends/ and fonts/ into ONE ModuleScript: build/ImGui.luau
# --no-demo: library only (imgui_demo.lua left out, like not compiling imgui_demo.cpp)
# --target love: build/imgui_love.lua for LÖVE/LuaJIT instead (LÖVE backend, compat/luajit.lua shims, @native stripped).
# --target love: build/imgui_love.lua for LÖVE/LuaJIT instead (LÖVE backend, shims from the LÖVE backend's COMPAT section, @native stripped).
# The default (roblox) build is unaffected by it.
# Why: ModuleScripts don't share globals, and the setfenv() alternative turns off Luau's fast paths / native codegen.
# Each source file becomes a function in __FILES; IM_INCLUDE(name) runs it once and caches the result.
@@ -34,7 +34,7 @@ for src in FILES.values():
out = ["--!optimize 2", "-- GENERATED by tools/bundle.py from *.lua, backends/, fonts/ - do not edit", ""]
if LOVE:
out = ["-- GENERATED by tools/bundle.py --target love - do not edit", open(os.path.join(ROOT, "compat", "luajit.lua")).read(), ""]
out = ["-- GENERATED by tools/bundle.py --target love - do not edit", re.search(r"--@compat-begin\n(.*?)--@compat-end", open(os.path.join(ROOT, "backends", "imgui_impl_love.lua")).read(), re.S).group(1), ""]
out += [
"local __FILES, __LOADED = {}, {}",
"function IM_INCLUDE(name)",