@@ -163,6 +163,7 @@ local __LINE__ = "__LINE__"
163163local __DATE__ = " __DATE__"
164164local __TIME__ = " __TIME__"
165165local __LCPP_INDENT__ = " __LCPP_INDENT__"
166+ local _Pragma = " _Pragma"
166167
167168-- BNF LEAVES
168169local ENDL = " $"
@@ -290,12 +291,6 @@ local function findn(input, what)
290291 end
291292end
292293
293- -- C literal string concatenation
294- local function concatStringLiteral (input )
295- -- screener does remove multiline definition, so just check ".*"%s*".*" pattern
296- return input :gsub (" \" (" .. STRING_LITERAL .. " )\" " .. OPTSPACES .. " \" (" .. STRING_LITERAL .. " )\" " , " \" %1%2\" " )
297- end
298-
299294-- c style boolean check (thus, 0 will be false)
300295local function CBoolean (value )
301296 return value and (value ~= 0 )
@@ -311,6 +306,12 @@ local function CEval(expr)
311306 end
312307end
313308
309+ local function process_Pragma (value )
310+ return value :gsub (" \\\\ " , " \\ " ):gsub (" \\\" " , " \" " ):gsub (" _Pragma%s*(%b())" , function (match )
311+ return " #pragma " .. match :sub (3 , - 3 )
312+ end )
313+ end
314+
314315-- a lightweight and flexible tokenizer
315316local function _tokenizer (str , setup )
316317 local defsetup = {
401402-- ------------
402403-- PARSER
403404-- ------------
405+ -- C literal string concatenation
406+ local LCPP_TOKENIZE_LITERAL = {
407+ string = true ,
408+ keywords = {
409+ },
410+ }
411+
412+ local function concatStringLiteral (input )
413+ -- print("input = "..input)
414+ local out = {}
415+ local literal_appears = {}
416+ local last_ignore
417+ local quote
418+ for k , v , start , end_ in tokenizer (input , LCPP_TOKENIZE_LITERAL ) do
419+ if k == " string" then
420+ -- print("string:["..input:sub(start + 1, end_ - 1).."]")
421+ table.insert (literal_appears , input :sub (start + 1 , end_ - 1 ))
422+ quote = input :sub (start ,start )
423+ elseif k == " ignore" then
424+ if # literal_appears == 0 then
425+ table.insert (out , input :sub (start , end_ ))
426+ else
427+ last_ignore = input :sub (start , end_ )
428+ end
429+ else
430+ if # literal_appears > 0 then
431+ local concat = quote .. table.concat (literal_appears ).. quote
432+ table.insert (out , concat )
433+ if last_ignore then
434+ table.insert (out , last_ignore )
435+ end
436+ end
437+ table.insert (out , input :sub (start , end_ ))
438+ literal_appears = {}
439+ last_ignore = nil
440+ end
441+ end
442+ if # literal_appears > 0 then
443+ table.insert (out , quote .. table.concat (literal_appears ).. quote )
444+ end
445+ local rinput = table.concat (out )
446+ -- if rinput ~= input then
447+ -- print("result:["..rinput.."]["..input.."]")
448+ -- end
449+ return rinput
450+ end
404451
405452local LCPP_TOKENIZE_COMMENT = {
406453 string = false ,
@@ -533,7 +580,6 @@ local function apply(state, input)
533580 local out = {}
534581 local functions = {}
535582 local expand
536-
537583 for k , v , start , end_ in tokenizer (input , LCPP_TOKENIZE_APPLY_MACRO ) do
538584 -- print('tokenize:'..tostring(k).."|"..tostring(v))
539585 if k == " identifier" then
@@ -550,14 +596,14 @@ local function apply(state, input)
550596 repl = tostring (macro )
551597 expand = (repl ~= v )
552598 elseif type (macro ) == " function" then
553- local decl , cnt = input :sub (start ):gsub (" ^[_%a][_%w]*%s*%b()" , " %1 " )
599+ local decl = input :sub (start ):match (" ^[_%a][_%w]*%s*%b()" )
554600 -- print('matching:'..input.."|"..decl.."|"..cnt)
555- if cnt > 0 then
601+ if decl then
556602 repl = macro (decl )
557603 -- print("d&r:"..decl.."|"..repl)
558604 expand = true
559605 table.insert (out , repl )
560- table.insert (out , input :sub (end_ + # decl ))
606+ table.insert (out , input :sub (start + # decl ))
561607 break
562608 else
563609 if input :sub (start ):find (" ^[_%a][_%w]*%s*%(" ) then
@@ -1217,7 +1263,7 @@ local function prepareMacro(state, input)
12171263end
12181264
12191265-- macro args replacement function slower but more torelant for pathological case
1220- local function replaceArgs (argsstr , repl )
1266+ local function replaceArgs (argsstr , repl , state )
12211267 local args = {}
12221268 argsstr = argsstr :sub (2 ,- 2 )
12231269 -- print('argsstr:'..argsstr)
@@ -1236,7 +1282,13 @@ local function replaceArgs(argsstr, repl)
12361282 comma = true
12371283 end
12381284 end
1239- local v = repl :gsub (" %$(%d+)" , function (m ) return args [tonumber (m )] or " " end )
1285+ local v = repl :gsub (" %$(#?)(%d+)" , function (stringify , m )
1286+ if # stringify > 0 then
1287+ return args [tonumber (m )] and state :apply (args [tonumber (m )]) or " "
1288+ else
1289+ return args [tonumber (m )] or " "
1290+ end
1291+ end )
12401292 -- print("replaceArgs:" .. repl .. "|" .. tostring(#args) .. "|" .. v)
12411293 return v
12421294end
@@ -1255,7 +1307,7 @@ local function parseFunction(state, input)
12551307 -- avoid matching substring of another identifier (eg. attrib matches __attribute__ and replace it)
12561308 repl = repl :gsub (" (#*)(%s*)(" .. argname .. " )([_%w]?)" , function (s1 , s2 , s3 , s4 )
12571309 if # s4 <= 0 then
1258- return (# s1 == 1 ) and (" \" $" .. noargs .. " \" " ) or (s1 .. s2 .. " $" .. noargs )
1310+ return (# s1 == 1 ) and (" \" $# " .. noargs .. " \" " ) or (s1 .. s2 .. " $" .. noargs )
12591311 else
12601312 return s1 .. s2 .. s3 .. s4
12611313 end
@@ -1267,7 +1319,7 @@ local function parseFunction(state, input)
12671319 -- build macro funcion
12681320 local func = function (input )
12691321 return input :gsub (name .. " %s*(%b())" , function (match )
1270- return replaceArgs (match , repl )
1322+ return replaceArgs (match , repl , state )
12711323 end )
12721324 end
12731325
@@ -1344,6 +1396,7 @@ function lcpp.init(input, predefines, macro_sources)
13441396 state :define (__TIME__ , os.date (" %H:%M:%S" ), true )
13451397 state :define (__LINE__ , state .lineno , true )
13461398 state :define (__LCPP_INDENT__ , state :getIndent (), true )
1399+ state :define (_Pragma , process_Pragma , true )
13471400 predefines = predefines or {}
13481401 for k ,v in pairs (lcpp .ENV ) do state :define (k , v , true ) end -- static ones
13491402 for k ,v in pairs (predefines ) do state :define (k , v , true ) end
@@ -1416,6 +1469,11 @@ function lcpp.test(suppressMsg)
14161469 /*
14171470 assert(false, "multi-line comment not removed")
14181471 */
1472+ #define PLUS1(x) x + 1
1473+ assert(PLUS1(__LINE__) == 14, "__LINE__ should evaluate before PLUS1 evaluated")
1474+ #define TOSTR(x) # x
1475+ assert(TOSTR(__LINE__) == "15", "__LINE__ should evaluate before TOSTR evaluated")
1476+ assert(TOSTR() == "", "empty stringify should not cause error")
14191477 /* pathological case which contains single line comment start in multiline comments.
14201478 * e.g. this multiline comment should be finish next line.
14211479 * http://foobar.com */ // comment
@@ -1883,6 +1941,23 @@ function lcpp.test(suppressMsg)
18831941 assert (loadstring (testlua , " testlua" ))()
18841942 lcpp_test .assertTrueCalls = findn (testlcpp , " lcpp_test.assertTrue()" )
18851943 assert (lcpp_test .assertTrueCount == lcpp_test .assertTrueCalls , " assertTrue calls:" .. lcpp_test .assertTrueCalls .. " count:" .. lcpp_test .assertTrueCount )
1944+
1945+ -- pragma test because it causes error if appeared in above testlua code
1946+ local src = [[
1947+ #ifdef DEBUG
1948+ #define DO_PRAGMA(x) _Pragma (#x)
1949+ #else
1950+ #define DO_PRAGMA(x)
1951+ #endif
1952+ DO_PRAGMA (CUSTOM_COMPILER debug_msg "random message")
1953+ ]]
1954+ local pragma_statement = ' #pragma CUSTOM_COMPILER debug_msg "random message"'
1955+ local r1 , r2 = lcpp .compile (src ), lcpp .compile (src , { DEBUG = true })
1956+ -- print('r1=[['..r1..']]')
1957+ -- print('r2=[['..r2..']]')
1958+ assert (not r1 :match (pragma_statement ))
1959+ assert (r2 :match (pragma_statement ))
1960+
18861961 _G .lcpp_test = nil -- delete ugly global hack
18871962 if not suppressMsg then print (" Test run suscessully" ) end
18881963end
0 commit comments