Skip to content

Instantly share code, notes, and snippets.

@leafi
Last active August 29, 2015 14:18
Show Gist options
  • Save leafi/9cac45df526be3625e8a to your computer and use it in GitHub Desktop.
Save leafi/9cac45df526be3625e8a to your computer and use it in GitHub Desktop.
to_variants.lua
--Assertion: all 'functions =' lines are either:
-- functions = {
--or:
-- functions = {
--...EXCEPT those representing module functions, where it's:
-- functions = {
--or:
-- functions = {
-- The first 'special' module functions line should only happen once per file.
-- If we see a 'types = {' line, we need to exit special mode. (for love_api.lua which has mod funs at top)
-- Yep, we're really doing this based on indentation.
-- This program operates in three stages:
-- #1: Make sure all files match our parser's expectations.
-- #2: Perform the rewrite.
-- #3: require("love_api") and check everything looks OK lua-side.
-- Given this is a bit risky, let's first ensure we're actually correct.
local allFiles = {"love_api.lua", "modules/Audio.lua", "modules/Event.lua",
"modules/Filesystem.lua", "modules/Graphics.lua", "modules/Image.lua",
"modules/Joystick.lua", "modules/Keyboard.lua", "modules/Math.lua",
"modules/Mouse.lua", "modules/Physics.lua", "modules/Sound.lua",
"modules/System.lua", "modules/Thread.lua", "modules/Timer.lua",
"modules/Window.lua"}
local fspecial1 = ' functions = {'
local cspecial1 = ' callbacks = {'
local fspecial2 = ' functions = {'
local f1 = ' functions = {'
local f2 = ' functions = {'
--
-- ASSERT that in all files, any line containing string 'functions' that
-- also contains an = sign and NOT containing 'description', is a line that
-- matches the rules at the top of the file.
--
local failed = false
for _, fname in ipairs(allFiles) do
local lspecial1 = 0
local lspecial2 = 0
local l1 = 0
local l2 = 0
local bad = 0
local special = false
local doneSpecial = false
print(fname .. ":")
local linenum = 1
for line in io.lines(fname) do
if line:find("types = {") then
special = false
end
if (line:find("functions") and line:find("=") and not line:find("description")) or
(line:find("callbacks") and line:find("=") and not line:find("description")) then
if special then
-- Expect fspecial2.
if line:find(fspecial2) == 1 then
lspecial2 = lspecial2 + 1
doneSpecial = true
else
bad = bad + 1
print(" ! bad functions line. expected fspecial2. line: " .. tostring(linenum) .. " s: " .. line)
end
else
-- Usual functions lines - but watch out for fspecial1...
if line:find(f1) == 1 then
l1 = l1 + 1
elseif line:find(f2) == 1 then
if l1 > 0 then
l2 = l2 + 1
else
bad = bad + 1
print(" ! bad (f2 before f1) line: " .. tostring(linenum) .. " s: " .. line)
end
elseif line:find(fspecial1) == 1 or line:find(cspecial1) == 1 then
if (fname ~= "love_api.lua") and doneSpecial then error(" ! line: " .. tostring(linenum) .. ": already seen fspecial1/cspecial1!") end
lspecial1 = lspecial1 + 1
special = true
else
bad = bad + 1
print(" ! bad (just bad) f1/f2 line: " .. tostring(linenum) .. " s: " .. line)
end
end
end
linenum = linenum + 1
end
if bad > 0 then
print(" ! Non-zero number of unrecognized 'functions =' lines.")
failed = true
end
if (lspecial1 ~= 1) or (lspecial2 < 1) then
if not (fname == "love_api.lua" and lspecial1 == 2) then
print(" ! Failed to find fspecial1 (i.e. module functions definition) in this file.")
failed = true
end
end
print(" Found f1: " .. tostring(l1) .. " f2: " .. tostring(l2) .. " fspecial1: " .. tostring(lspecial1) .. " fspecial2: " .. tostring(lspecial2) .. " BAD: " .. tostring(bad))
end
if failed then
error("Assertion failed. :(")
end
print("")
print("*****")
print(" Yeah, format looks good - I *think* we can do this!")
print("*****")
--
-- Go through all the files again, but this time, rewrite inner 'functions' lines to be 'variants'.
--
for _, fname in ipairs(allFiles) do
local special = false
print("Rewriting " .. fname .. ":")
local oldLines = {}
for line in io.lines(fname) do
table.insert(oldLines, line)
end
local f = io.open(fname, "w")
for _, line in ipairs(oldLines) do
if line:find("types = {") then
special = false
end
if (line:find("functions") and line:find("=") and not line:find("description")) or
(line:find("callbacks") and line:find("=") and not line:find("description")) then
if special then
-- Expect fspecial2.
if line:find(fspecial2) == 1 then
f:write(' variants = {' .. '\n')
else
error(" ! bad functions line. expected fspecial2.")
end
else
-- Usual functions lines - but watch out for fspecial1...
if line:find(f1) == 1 then
f:write(line .. '\n')
elseif line:find(f2) == 1 then
f:write(' variants = {' .. '\n')
elseif line:find(fspecial1) == 1 or line:find(cspecial1) == 1 then
special = true
f:write(line .. '\n')
else
error(" ! bad (just bad) f1/f2.")
end
end
else
f:write(line .. '\n')
end
end
f:close()
end
print("*** Rewriting done, I think.")
--
-- Now we load love_api as Lua and make sure it matches our expectations.
--
print("")
print("OK - let's load love_api into our namespace, trawl through the modules, and make sure everything makes sense.")
local love_api = require("love_api")
local mods = {}
table.insert(mods, love_api)
for _, v in ipairs(love_api.modules) do
table.insert(mods, v)
end
print("found #" .. #mods .. "modules")
if #mods ~= #allFiles then
error("! On verify, different number of found logical modules vs number of .lua files! (" .. tostring(#mods) .. " vs " .. tostring(#allFiles) .. ")")
end
for _, mod in ipairs(mods) do
print(" Verifying " .. (mod.name or "love_api") .. "...")
-- First, check module-level functions.
if mod.variants then error((mod.name or "love_api") .. " now has .variants. We failed horribly.") end
for _, fun in ipairs(mod.functions or {}) do
if fun.functions then
error((mod.name or "love_api") .. "'s module-level function '" .. fun.name .. "' contained 'functions' field.")
elseif not fun.variants then
error((mod.name or "love_api") .. "'s module-level function '" .. fun.name .. "' did not contain a 'variants' field!")
end
print(" im " .. fun.name)
end
-- Check module-level callbacks, if any.
for _, fun in ipairs(mod.callbacks or {}) do
if fun.functions then
error((mod.name or "love_api") .. "'s module-level callback '" .. fun.name .. "' contained 'functions' field.")
elseif not fun.variants then
error((mod.name or "love_api") .. "'s module-level callback '" .. fun.name .. "' did not contain a 'variants' field!")
end
print(" ic " .. fun.name)
end
-- Now check type-level functions.
for _, t in ipairs(mod.types or {}) do
if t.variants then error((mod.name or "love_api") .. "'s type " .. t.name .. " now has .variants. We failed utterly.") end
for _, fun in ipairs(t.functions or {}) do
if fun.functions then
error((mod.name or "love_api") .. "'s type " .. t.name .. " type-level function '" .. fun.name .. "' contained 'functions' field.")
elseif not fun.variants then
error((mod.name or "love_api") .. "'s type " .. t.name .. " type-level function '" .. fun.name .. "' did not contain a 'variants' field!")
end
print(" i " .. t.name .. " " .. fun.name)
end
end
end
print("")
print("*** Looks OK to me!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment