#!/usr/bin/env lua local function parse_pair(pair, value_first) if pair:sub(1, 1) == "#" then return end local idx = pair:find(" ") if idx then local first, second = pair:sub(1, idx - 1), pair:sub(idx + 1) if value_first and first:sub(1, 1) ~= "[" then return second, first else return first, second end else return pair end end local function parse_spec(name, value_first) local f = io.open("spec/" .. name, "r") local spec = {} local top for l in f:lines() do if l:sub(1, 1) == "\t" then local key, val = parse_pair(l:sub(2), value_first) if val then top[key] = val elseif key then table.insert(top, key) end else local key, val = parse_pair(l, value_first) if val then spec[key] = val elseif key then top = {} spec[key] = top end end end f:close() return spec end local casemap = parse_spec("casemap") local function camel_case(snake) if casemap[snake] then return casemap[snake] end local camel = "" while #snake > 0 do local idx = snake:find("_") or #snake + 1 camel = camel .. snake:sub(1, 1):upper() .. snake:sub(2, idx - 1) snake = snake:sub(idx + 1) end return camel end local funcs = "" for name, fields in pairs(parse_spec("client/enum")) do local camel = camel_case(name) funcs = funcs .. "func luaPush" .. camel .. "(l *lua.State, val mt." .. camel .. ") {\n\tswitch val {\n" for _, var in ipairs(fields) do funcs = funcs .. "\tcase mt." .. (fields.prefix or "") .. camel_case(var) .. (fields.postfix or "") .. ":\n\t\t" .. (var == "no" and "l.PushNil()" or "l.PushString(\"" .. var .. "\")") .. "\n" end funcs = funcs .. "\t}\n}\n\n" end for name, fields in pairs(parse_spec("client/flag")) do local camel = camel_case(name) funcs = funcs .. "func luaPush" .. camel .. "(l *lua.State, val mt." .. camel .. ") {\n\tl.NewTable()\n" for _, var in ipairs(fields) do funcs = funcs .. "\tif val&mt." .. (fields.prefix or "") .. camel_case(var) .. (fields.postfix or "") .. " != 0 {\n\t\tl.PushBoolean(true)\n\t\tl.SetField(-2, \"" .. var .. "\")\n\t}\n" end funcs = funcs .. "}\n\n" end local push_type = { string = "l.PushString(string(VAL))", fixed_string = "l.PushString(string(VAL[:]))", boolean = "l.PushBoolean(bool(VAL))", integer = "l.PushInteger(int(VAL))", number = "l.PushNumber(float64(VAL))", vec2 = "luaPushVec2(l, [2]float64{float64(VAL[0]), float64(VAL[1])})", vec3 = "luaPushVec3(l, [3]float64{float64(VAL[0]), float64(VAL[1]), float64(VAL[2])})", box1 = "luaPushBox1(l, [2]float64{float64(VAL[0]), float64(VAL[1])})", box2 = "luaPushBox2(l, [2][2]float64{{float64(VAL[0][0]), float64(VAL[0][1])}, {float64(VAL[1][0]), float64(VAL[1][1])}})", box3 = "luaPushBox3(l, [2][3]float64{{float64(VAL[0][0]), float64(VAL[0][1]), float64(VAL[0][2])}, {float64(VAL[1][0]), float64(VAL[1][1]), float64(VAL[1][2])}})", } local function push_fields(fields, indent) local impl = "" for name, type in pairs(fields) do if name:sub(1, 1) ~= "[" then local camel = "val." .. camel_case(name) local idt = indent local condition = fields["[" .. name .. "]"] if condition then impl = impl .. indent .. "if " .. condition .. " {\n" idt = idt .. "\t" end if push_type[type] then impl = impl .. idt .. push_type[type]:gsub("VAL", camel) .. "\n" else impl = impl .. idt .. "luaPush" .. camel_case(type) .. "(l, " .. camel .. ")\n" end impl = impl .. idt .. "l.SetField(-2, \"" .. name .. "\")\n" if condition then impl = impl .. indent .. "}\n" end end end return impl end for name, fields in pairs(parse_spec("client/struct", true)) do local camel = camel_case(name) funcs = funcs .. "func luaPush" .. camel .. "(l *lua.State, val mt." .. camel .. ") {\n\tl.NewTable()\n" .. push_fields(fields, "\t") .. "}\n\n" end local to_string_impl = "" local to_lua_impl = "" for name, fields in pairs(parse_spec("client/pkt", true)) do local case = "\tcase *mt.ToClt" .. camel_case(name) .. ":\n" to_string_impl = to_string_impl .. case .. "\t\treturn \"" .. name .. "\"\n" if next(fields) then to_lua_impl = to_lua_impl .. case .. push_fields(fields, "\t\t") end end local f = io.open("convert.go", "w") f:write([[ // generated by mkconvert.lua, DO NOT EDIT package main import ( "github.com/Shopify/go-lua" "github.com/anon55555/mt" ) ]] .. funcs .. [[ func pktToString(pkt *mt.Pkt) string { switch pkt.Cmd.(type) { ]] .. to_string_impl .. [[ } panic("impossible") return "" } func pktToLua(l *lua.State, pkt *mt.Pkt) { if pkt == nil { l.PushNil() return } l.NewTable() l.PushString(pktToString(pkt)) l.SetField(-2, "_type") switch val := pkt.Cmd.(type) { ]] .. to_lua_impl .. [[ } } ]]) f:close()