diff --git a/lcpp.lua b/lcpp.lua index f38f828..b1c0ccf 100755 --- a/lcpp.lua +++ b/lcpp.lua @@ -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") @@ -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) @@ -727,7 +728,7 @@ end local function doWork(state) local function _doWork(state) - if not state:defined(__FILE__) then state:define(__FILE__, "", true) end + if not state:defined(__FILE__) then state:define(__FILE__, '"'..""..'"', true) end local oldIndent = state:getIndent() while true do local input = state:getLine() @@ -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 @@ -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 @@ -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 @@ -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 @@ -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") */ @@ -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"