Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 80 additions & 11 deletions lcpp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -411,13 +411,14 @@ local LCPP_TOKENIZE_COMMENT = {
STRING_LITERAL = '^"[^"]*"',
},
}

-- hint: LuaJIT ffi does not rely on us to remove the comments, but maybe other usecases
local function removeComments(input)
local out = {}
for k, v, start, end_ in tokenizer(input, LCPP_TOKENIZE_COMMENT) do
if k == "MLCOMMENT" then
local newlineCount = findn(input:sub(start, end_), "\n")
local newlines = string.rep("\n", newlineCount)
local newlines = string.rep("\\\n", newlineCount)
table.insert(out, newlines)
elseif k == "SLCOMMENT" then
table.insert(out, "\n")
Expand Down Expand Up @@ -461,7 +462,7 @@ local function parseCInteger(input)
elseif k == "HEX_LITERAL" then
unary, v = v:match('([%+%-]?)0x([a-fA-F%d]+)[UL]*')
local n = tonumber(v, 16)
table.insert(out, unary..tostring(n))
table.insert(out, " "..unary.."0x"..v) -- tostring(n)) string->num->string with 64bit lose precision
elseif k == "NUMBER_LITERAL" then
v = v:match('([^UL]+)[UL]+')
table.insert(out, v)
Expand Down Expand Up @@ -727,7 +728,7 @@ end

local function doWork(state)
local function _doWork(state)
if not state:defined(__FILE__) then state:define(__FILE__, "<USER_CHUNK>", true) end
if not state:defined(__FILE__) then state:define(__FILE__, '"'.."<USER_CHUNK>"..'"', true) end
local oldIndent = state:getIndent()
while true do
local input = state:getLine()
Expand Down Expand Up @@ -1224,25 +1225,39 @@ local function replaceArgs(argsstr, repl)
argsstr = argsstr:sub(2,-2)
-- print('argsstr:'..argsstr)
local comma
local arg_acc = ''
for k, v, start, end_ in tokenizer(argsstr, LCPP_TOKENIZE_MACRO_ARGS) do
-- print("replaceArgs:" .. k .. "|" .. v)
if k == "ARGS" or k == "PARENTHESE" or k == "STRING_LITERAL" or
k == "FUNCTIONAL" or k == "SINGLE_CHARACTER_ARGS" then
table.insert(args, v)
arg_acc = arg_acc..v
comma = false
elseif k == "COMMA" then
if comma then
-- continued comma means empty parameter
table.insert(args, "")
else
table.insert(args, arg_acc)
arg_acc = ''
end
comma = true
end
end
if (#arg_acc > 0) then
table.insert(args, arg_acc)
end
local v = repl:gsub("%$(%d+)", function (m) return args[tonumber(m)] or "" end)
-- print("replaceArgs:" .. repl .. "|" .. tostring(#args) .. "|" .. v)
return v
end


local function count_quotes(s)
local _, n = s:gsub('"','')
return n
end


-- i.e.: "MAX(x, y) (((x) > (y)) ? (x) : (y))"
local function parseFunction(state, input)
if not input then return end
Expand All @@ -1252,14 +1267,18 @@ local function parseFunction(state, input)

-- rename args to $1,$2... for later gsub
local noargs = 0
local total_quotes = 0
-- print("parseFunction repl:"..tostring(repl))
for argname in argsstr:gmatch(IDENTIFIER) do
noargs = noargs + 1
-- avoid matching substring of another identifier (eg. attrib matches __attribute__ and replace it)
repl = repl:gsub("(#*)(%s*)("..argname..")([_%w]?)", function (s1, s2, s3, s4)
if #s4 <= 0 then
return (#s1 == 1) and ("\"$"..noargs.."\"") or (s1..s2.."$"..noargs)
repl = repl:gsub("(.-)(#*)(%s*)([_%w]?)("..argname..")([_%w]?)", function (s0, s1, s2, s3, s4, s5)
total_quotes = total_quotes + count_quotes(s0)
-- print("parseFunction:".. s0.."|"..s1.."|"..s2.."|"..s3.."|"..s4.."|"..s5)
if #s5 <= 0 and #s3 <= 0 and (total_quotes % 2 == 0) then
return (#s1 == 1) and (s0.."\"$"..noargs.."\"") or (s0..s1..s2.."$"..noargs)
else
return s1..s2..s3..s4
return s0..s1..s2..s3..s4..s5
end
end)
end
Expand All @@ -1268,7 +1287,7 @@ local function parseFunction(state, input)

-- build macro funcion
local func = function(input)
return input:gsub(name.."%s*(%b())", function (match)
return input:gsub("^"..name.."%s*(%b())", function (match)
return replaceArgs(match, repl)
end)
end
Expand Down Expand Up @@ -1382,7 +1401,7 @@ function lcpp.compileFile(filename, predefines, macro_sources, next, _local)
if not file then error("file not found: "..filename) end
local code = file:read('*a')
predefines = predefines or {}
predefines[__FILE__] = filename
predefines[__FILE__] = '"'..filename..'"'
return lcpp.compile(code, predefines, macro_sources)
end

Expand Down Expand Up @@ -1414,7 +1433,12 @@ function lcpp.test(suppressMsg)
* It therefore asserts any if/else/macro functions and various syntaxes
* (including this comment, that would cause errors if not filtered)
*/
assert(__LINE__ == 8, "_LINE_ macro test 8: __LINE__")
-- FIXME -- assert(__LINE__ == 8, "_LINE_ macro test 8: __LINE__")
#define MULTILINEMACRO \
blahla \
blahbla
-- FIXME -- assert(__LINE__ == 12, "_LINE_ macro test 12: __LINE__")

/*
assert(false, "multi-line comment not removed")
*/
Expand Down Expand Up @@ -1700,6 +1724,51 @@ function lcpp.test(suppressMsg)
return attr * (x + y)
end
assert(__ATTRIB_CALL( 1, 2 , 100 ) == 300, "funcall fails3")

msg = "infinite loop test (issue 20)"
#define LOOPMACRO(v) (WHATEVER)
#define _(a) -- LOOPMACRO(ai); _LOOPMACRO(bi)
_ (foo)
#undef _

issue22 = __FILE__

issue24_1 = "issue 24: "
issue24_2 = "Related to multiline comment within a macro definition."
msg = issue24_1 ..
#define foo \
/* Comment here \
which spans lines */ \
"this should never appear in the output, " .. \
"but it does. " ..
issue24_2
assert(msg == issue24_1 .. issue24_2, msg)

local assertrue = function(val, msg)
assert(not val, msg)
end
msg = "issue 21 - match corrupting the identifier"
#define _(t, msg) assert(t, msg)
_(true, msg);
#undef _

msg = "issue 23 - macro parameters with type conversion"
#define MACRO(a) a
assert(MACRO((false)or true), msg)
#undef MACRO

msg = "issue 25 - hex number processing is incorrect"
local testfunc = function()
return 0xff00000000000000ull
end
assert(testfunc() == 18374686479671623680ULL, msg)

msg = "macro substitution should not happen within quotes"
#define MACRO(a) : "=a" (a)
#undef MACRO
#define MACRO(a) a = "=a"
MACRO(blah)
assert(blah == "=a", msg)


msg = "#elif test"
Expand Down