lcpp.lua 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939
  1. ----------------------------------------------------------------------------
  2. --## lcpp - a C-PreProcessor in Lua 5.1 for LuaJIT ffi
  3. --
  4. -- Copyright (C) 2012-2013 Michael Schmoock <[email protected]>
  5. --
  6. --### Links
  7. -- * GitHub page: [http://github.com/willsteel/lcpp](http://github.com/willsteel/lcpp)
  8. -- * Project page: [http://lcpp.schmoock.net](http://lcpp.schmoock.net)
  9. -- * Lua: [http://www.lua.org](http://www.lua.org)
  10. -- * LuaJIT: [http://luajit.org](http://luajit.org)
  11. -- * Sponsored by: [http://mmbbq.org](http://mmbbq.org)
  12. --
  13. -- It can be used to pre-process LuaJIT ffi C header file input.
  14. -- It can also be used to preprocess any other code (i.e. Lua itself)
  15. --
  16. -- git clone https://github.com/willsteel/lcpp.git
  17. ----------------------------------------------------------------------------
  18. --## USAGE
  19. -- -- load lcpp
  20. -- local lcpp = require("lcpp")
  21. --
  22. -- -- use LuaJIT ffi and lcpp to parse cpp code
  23. -- ffi.cdef("#include <your_header.h>")
  24. --
  25. -- -- compile some input
  26. -- local out = lcpp.compile([[
  27. -- #include "myheader.h"
  28. -- #define MAXPATH 260
  29. -- typedef struct somestruct_t {
  30. -- void* base;
  31. -- size_t size;
  32. -- wchar_t path[MAXPATH];
  33. -- } t_exe;
  34. -- ]])
  35. --
  36. -- -- the result should be
  37. -- out = [[
  38. -- // <preprocessed content of file "myheader.h">
  39. -- typedef struct somestruct_t {
  40. -- void* base;
  41. -- size_t size;
  42. -- wchar_t path[260];
  43. -- } t_exe;
  44. -- ]]
  45. --
  46. --## This CPPs BNF:
  47. -- RULES:
  48. -- CODE := {LINE}
  49. -- LINE := {STUFF NEWML} STUFF NEWL
  50. -- STUFF := DIRECTIVE | IGNORED_CONTENT
  51. -- DIRECTIVE := OPTSPACES CMD OPTSPACES DIRECTIVE_NAME WHITESPACES DIRECTIVE_CONTENT WHITESPACES NEWL
  52. --
  53. -- LEAVES:
  54. -- NEWL := "\n"
  55. -- NEWL_ESC := "\\n"
  56. -- WHITESPACES := "[ \t]+"
  57. -- OPTSPACES := "[ \t]*"
  58. -- COMMENT := "//(.-)$"
  59. -- MLCOMMENT := "/[*](.-)[*]/"
  60. -- IGNORED_CONTENT := "[^#].*"
  61. -- CMD := "#"
  62. -- DIRECTIVE_NAME := "include"|"define"|"undef"|"if"|"else"|"elif"|"else if"|"endif"|"ifdef"|"ifndef"|"pragma"|"version"
  63. -- DIRECTIVE_CONTENT := ".*?"
  64. --
  65. --## TODOs:
  66. -- - lcpp.LCPP_LUA for: load, loadfile
  67. -- - "#" operator for stringification
  68. -- - literal concatenation: "foo" "bar" -> "foobar"
  69. --
  70. --## License (MIT)
  71. -- -----------------------------------------------------------------------------
  72. -- Permission is hereby granted, free of charge, to any person obtaining a copy
  73. -- of this software and associated documentation files (the "Software"), to deal
  74. -- in the Software without restriction, including without limitation the rights
  75. -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  76. -- copies of the Software, and to permit persons to whom the Software is
  77. -- furnished to do so, subject to the following conditions:
  78. --
  79. -- The above copyright notice and this permission notice shall be included in
  80. -- all copies or substantial portions of the Software.
  81. --
  82. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  83. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  84. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  85. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  86. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  87. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  88. -- THE SOFTWARE.
  89. --
  90. -- MIT license: http://www.opensource.org/licenses/mit-license.php
  91. -- -----------------------------------------------------------------------------
  92. --
  93. -- @module lcpp
  94. local lcpp = {}
  95. -- CONFIG
  96. lcpp.LCPP_LUA = false -- whether to use lcpp to preprocess Lua code (load, loadfile, loadstring...)
  97. lcpp.LCPP_FFI = true -- whether to use lcpp as LuaJIT ffi PreProcessor (if used in luaJIT)
  98. lcpp.LCPP_TEST = false -- whether to run lcpp unit tests when loading lcpp module
  99. lcpp.ENV = {} -- static predefines (env-like)
  100. lcpp.FAST = true -- perf. tweaks when enabled. con: breaks minor stuff like __LINE__ macros
  101. lcpp.DEBUG = false
  102. -- PREDEFINES
  103. local __FILE__ = "__FILE__"
  104. local __LINE__ = "__LINE__"
  105. local __DATE__ = "__DATE__"
  106. local __TIME__ = "__TIME__"
  107. local __LCPP_INDENT__ = "__LCPP_INDENT__"
  108. local __LCPP_INSIDE_HEADERFILE__ = "__LCPP_INSIDE_HEADERFILE__"
  109. -- BNF LEAVES
  110. local ENDL = "$"
  111. local STARTL = "^"
  112. local NEWL = "\n"
  113. local NEWL_BYTE = NEWL:byte(1)
  114. local NEWL_ESC = "\\"
  115. local NEWML = "\\\n"
  116. local CMD = "#"
  117. local CMD_BYTE = CMD:byte(1)
  118. local COMMENT = "^(.-)//.-$"
  119. local MLCOMMENT = "/[*].-[*]/"
  120. local WHITESPACES = "%s+"
  121. local OPTSPACES = "%s*"
  122. local IDENTIFIER = "[_%a][_%w]*"
  123. local NOIDENTIFIER = "[^%w_]+"
  124. local FILENAME = "[0-9a-zA-Z.-_/\\]+"
  125. local TEXT = ".+"
  126. -- BNF WORDS
  127. local _INCLUDE = "include"
  128. local _DEFINE = "define"
  129. local _IFDEF = "ifdef"
  130. local _IFNDEF = "ifndef"
  131. local _ENDIF = "endif"
  132. local _UNDEF = "undef"
  133. local _IF = "if"
  134. local _ELSE = "else"
  135. local _ELIF = "elif"
  136. local _NOT = "!"
  137. local _ERROR = "error"
  138. local _PRAGMA = "pragma"
  139. local _VERSION = "version"
  140. -- BNF RULES
  141. local INCLUDE = STARTL.._INCLUDE..WHITESPACES.."[\"<]("..FILENAME..")[\">]"..OPTSPACES..ENDL
  142. local DEFINE = STARTL.._DEFINE
  143. local IFDEF = STARTL.._IFDEF..WHITESPACES.."("..IDENTIFIER..")"..OPTSPACES..ENDL
  144. local IFNDEF = STARTL.._IFNDEF..WHITESPACES.."("..IDENTIFIER..")"..OPTSPACES..ENDL
  145. local ENDIF = STARTL.._ENDIF..OPTSPACES.."(.*)"..ENDL
  146. local UNDEF = STARTL.._UNDEF..WHITESPACES.."("..IDENTIFIER..")"..OPTSPACES..ENDL
  147. local IF = STARTL.._IF..WHITESPACES.."(.*)"..ENDL
  148. local ELSE = STARTL.._ELSE..OPTSPACES.."(.*)"..ENDL
  149. local ELIF = STARTL.._ELIF..WHITESPACES.."(.*)"..ENDL
  150. local ELSEIF = STARTL.._ELSE..WHITESPACES.._IF..WHITESPACES.."(.*)"..ENDL
  151. local ERROR = STARTL.._ERROR..WHITESPACES.."("..TEXT..")"..OPTSPACES..ENDL
  152. local ERROR_NOTEXT = STARTL.._ERROR..OPTSPACES..ENDL --> not required when we have POSIX regex
  153. local PRAGMA = STARTL.._PRAGMA
  154. local VERSION = STARTL.._VERSION
  155. -- speedups
  156. local TRUEMACRO = STARTL.."("..IDENTIFIER..")%s*$"
  157. local REPLMACRO = STARTL.."("..IDENTIFIER..")"..WHITESPACES.."(.+)$"
  158. local FUNCMACRO = STARTL.."("..IDENTIFIER..")%s*%(([%s%w,]*)%)%s*(.*)"
  159. -- ------------
  160. -- LOCAL UTILS
  161. -- ------------
  162. lcpp.STATE = {lineno = 0} -- current state for debugging the last operation
  163. local function error(msg) _G.print(debug.traceback()); _G.error(string.format("lcpp ERR [%04i] %s", lcpp.STATE.lineno, msg)) end
  164. local function print(msg) _G.print(string.format("lcpp INF [%04i] %s", lcpp.STATE.lineno, msg)) end
  165. -- splits a string using a pattern into a table of substrings
  166. local function gsplit(str, pat)
  167. local function _split(str, pat)
  168. local t = {} -- NOTE: use {n = 0} in Lua-5.0
  169. local fpat = "(.-)"..pat
  170. local last_end = 1
  171. local s, e, cap = str:find(fpat, 1)
  172. while s do
  173. if s ~= 1 or cap ~= "" then
  174. coroutine.yield(cap)
  175. end
  176. last_end = e + 1
  177. s, e, cap = str:find(fpat, last_end)
  178. end
  179. if last_end <= #str then
  180. cap = str:sub(last_end)
  181. coroutine.yield(cap)
  182. end
  183. end
  184. return coroutine.wrap(function() _split(str, pat) end)
  185. end
  186. local function split(str, pat)
  187. local t = {}
  188. for str in gsplit(str, pat) do table.insert(t, str) end
  189. return t
  190. end
  191. -- Checks whether a string starts with a given substring
  192. -- offset is optional
  193. local function strsw(str, pat, offset)
  194. if not str then return false end
  195. if not offset then offset = 0 end
  196. return string.sub(str, 1+offset, string.len(pat)+offset) == pat
  197. end
  198. -- Checks whether a string ends with a given substring
  199. local function strew(str, pat)
  200. if not str then return false end
  201. return pat=='' or string.sub(str,-string.len(pat)) == pat
  202. end
  203. -- string trim12 from lua wiki
  204. local function trim(str)
  205. local from = str:match"^%s*()"
  206. return from > #str and "" or str:match(".*%S", from)
  207. end
  208. -- returns the number of string occurrences
  209. local function findn(input, what)
  210. local count = 0
  211. local offset = 0
  212. while true do
  213. _, offset = string.find(input, what, offset+1, true)
  214. if not offset then return count end
  215. count = count + 1
  216. end
  217. end
  218. -- a lightweight and flexible tokenizer
  219. local function _tokenizer(str, setup)
  220. local defsetup = {
  221. -- EXAMPLE patterns have to be pretended with "^" for the tokenizer
  222. ["identifier"] = '^[_%a][_%w]*',
  223. ["number"] = '^[%+%-]?%d+[%.]?%d*',
  224. ["ignore"] = '^%s+',
  225. ["string"] = true,
  226. ["keywords"] = {
  227. -- ["NAME"] = '^pattern',
  228. -- ...
  229. },
  230. }
  231. if not setup then
  232. setup = defsetup
  233. end
  234. setup.identifier = setup.identifier or defsetup.identifier
  235. setup.number = setup.number or defsetup.number
  236. setup.ignore = setup.number or defsetup.ignore
  237. if nil == setup.string then setup.string = true end
  238. setup.keywords = setup.keywords or {}
  239. local strlen = #str
  240. local i = 1
  241. local i1, i2
  242. local keyword
  243. local function find(pat)
  244. i1, i2 = str:find(pat,i)
  245. return i1 ~= nil
  246. end
  247. local function cut()
  248. return str:sub(i, i2)
  249. end
  250. local function findKeyword()
  251. for name, pat in pairs(setup.keywords) do
  252. local result = find(pat)
  253. if result then
  254. keyword = name
  255. return true
  256. end
  257. end
  258. end
  259. while true do
  260. if i > strlen then return 'eof', nil, strlen, strlen end
  261. if find(setup.ignore) then
  262. coroutine.yield("ignore", cut(), i1, i2)
  263. elseif findKeyword() then
  264. coroutine.yield(keyword, cut(), i1, i2)
  265. elseif find(setup.number) then
  266. coroutine.yield('number', tonumber(cut()), i1, i2)
  267. elseif find(setup.identifier) then
  268. coroutine.yield('identifier', cut(), i1, i2)
  269. elseif setup.string and (find('^"[^"]*"') or find("^'[^']*'")) then
  270. -- strip the quotes
  271. coroutine.yield('string', cut():sub(2,-2), i1, i2)
  272. else -- any other unknown character
  273. i1 = i
  274. i2 = i
  275. coroutine.yield('unknown', cut(), i1, i2)
  276. end
  277. i = i2+1
  278. end
  279. end
  280. local function tokenizer(str, setup)
  281. return coroutine.wrap(function() _tokenizer(str, setup) end)
  282. end
  283. -- ------------
  284. -- PARSER
  285. -- ------------
  286. -- hint: LuaJIT ffi does not rely on us to remove the comments, but maybe other usecases
  287. local function removeComments(input)
  288. input = string.gsub(input, "//.-\n", "\n") -- remove sl comments
  289. -- remove multiline comments in a way that it does not break __LINE__ macro
  290. if lcpp.FAST then
  291. input = string.gsub(input, "/%*.-%*/", "") -- remove ml comments (stupid method)
  292. else
  293. local offset = 0
  294. local output = {}
  295. local starti, endi, match, lastendi
  296. while offset do
  297. starti, endi, match = input:find("/%*(.-)%*/", offset, false)
  298. if starti then
  299. lastendi = endi
  300. local newlineCount = findn(match, "\n")
  301. local newlines = string.rep("\n", newlineCount)
  302. table.insert(output, input:sub(offset+1, starti-1))
  303. table.insert(output, newlines)
  304. offset = endi
  305. else
  306. offset = nil
  307. table.insert(output, input:sub((lastendi or 0) + 1))
  308. end
  309. end
  310. input = table.concat(output)
  311. --error(input)
  312. end
  313. return input
  314. end
  315. -- screener: revmoce comments, trim, ml concat...
  316. -- it only splits to cpp input lines and removes comments. it does not tokenize.
  317. local function screener(input)
  318. local function _screener(input)
  319. --input = removeComments(input)
  320. -- concat mulit-line input.
  321. local count = 1
  322. while count > 0 do input, count = string.gsub(input, "^(.-)\\\n(.-)$", "%1 %2\n") end
  323. -- trim and join blocks not starting with "#"
  324. local buffer = {}
  325. for line in gsplit(input, NEWL) do
  326. --line = trim(line)
  327. if #line > 0 then
  328. if line:byte(1) == CMD_BYTE then
  329. --line = line:gsub("#%s*(.*)", "#%1") -- remove optinal whitespaces after "#". reduce triming later.
  330. if #buffer > 0 then
  331. coroutine.yield(table.concat(buffer, NEWL))
  332. buffer = {}
  333. end
  334. coroutine.yield(line)
  335. else
  336. if lcpp.FAST then
  337. table.insert(buffer, line)
  338. else
  339. coroutine.yield(line)
  340. end
  341. end
  342. elseif not lcpp.FAST then
  343. coroutine.yield(line)
  344. end
  345. end
  346. if #buffer > 0 then
  347. coroutine.yield(table.concat(buffer, NEWL))
  348. end
  349. end
  350. return coroutine.wrap(function() _screener(input) end)
  351. end
  352. -- apply currently known macros to input (and returns it)
  353. local function apply(state, input)
  354. local out = {}
  355. local functions = {}
  356. for k, v, start, end_ in tokenizer(input) do
  357. if k == "identifier" then
  358. local repl = v
  359. local macro = state.defines[v]
  360. if macro then
  361. if type(macro) == "boolean" then
  362. repl = ""
  363. elseif type(macro) == "string" then
  364. repl = macro
  365. elseif type(macro) == "number" then
  366. repl = tostring(macro)
  367. elseif type(macro) == "function" then
  368. table.insert(functions, macro) -- we apply functions in a later step
  369. end
  370. end
  371. table.insert(out, repl)
  372. else
  373. table.insert(out, input:sub(start, end_))
  374. end
  375. end
  376. input = table.concat(out)
  377. for _, func in pairs(functions) do -- TODO: looks sucky (but works quite nice)
  378. input = func(input)
  379. end
  380. return input
  381. end
  382. -- processes an input line. called from lcpp doWork loop
  383. local function processLine(state, line)
  384. if not line or #line == 0 then return line end
  385. local cmd = nil
  386. if line:byte(1) == CMD_BYTE then cmd = line:sub(2) end
  387. --print("processLine(): "..line)
  388. --[[ SKIPPING ]]--
  389. if state:skip() then return end
  390. --[[ READ NEW DIRECTIVES ]]--
  391. if cmd then
  392. -- handle #include ...
  393. local filename = cmd:match(INCLUDE)
  394. if filename then
  395. print("processing header " .. filename)
  396. return state:includeFile(filename)
  397. end
  398. return line
  399. end
  400. --[[ APPLY MACROS ]]--
  401. --line = state:apply(line);
  402. return line
  403. end
  404. local function processLine2(state, line)
  405. if not line or #line == 0 then return line end
  406. local cmd = nil
  407. if line:byte(1) == CMD_BYTE then cmd = line:sub(2) end
  408. --print("processLine(): "..line)
  409. if state:defined(__LCPP_INSIDE_HEADERFILE__) then
  410. --[[ IF/THEN/ELSE STRUCTURAL BLOCKS ]]--
  411. if cmd then
  412. local ifdef = cmd:match(IFDEF)
  413. local ifexp = cmd:match(IF)
  414. local ifndef = cmd:match(IFNDEF)
  415. local elif = cmd:match(ELIF)
  416. local elseif_ = cmd:match(ELSEIF)
  417. local else_ = cmd:match(ELSE)
  418. local endif = cmd:match(ENDIF)
  419. local struct = ifdef or ifexp or ifndef or elif or elseif_ or else_ or endif
  420. if struct then
  421. if ifdef then state:openBlock(state:defined(ifdef)) end
  422. if ifexp then state:openBlock(state:parseExpr(ifexp)) end
  423. if ifndef then state:openBlock(not state:defined(ifndef)) end
  424. if elif then state:elseBlock(state:parseExpr(elif)) end
  425. if elseif_ then state:elseBlock(state:parseExpr(elseif_)) end
  426. if else_ then state:elseBlock(true) end
  427. if endif then state:closeBlock() end
  428. return line
  429. end
  430. end
  431. end
  432. --[[ SKIPPING ]]--
  433. if state:skip() then return end
  434. --[[ READ NEW DIRECTIVES ]]--
  435. if cmd then
  436. -- handle #include ...
  437. local filename = cmd:match(INCLUDE)
  438. if filename then
  439. print("processing header " .. filename)
  440. return state:includeFile(filename)
  441. end
  442. if state:defined(__LCPP_INSIDE_HEADERFILE__) then
  443. -- handle #undef ...
  444. local key = cmd:match(UNDEF)
  445. if type(key) == "string" then
  446. state:undefine(key)
  447. return
  448. end
  449. -- read "#define >FooBar...<" directives
  450. if cmd:match(DEFINE) then
  451. local define = trim(cmd:sub(DEFINE:len()+1))
  452. local macroname, replacement
  453. -- simple "true" defines
  454. macroname = define:match(TRUEMACRO)
  455. if macroname then
  456. state:define(macroname, true)
  457. end
  458. -- replace macro defines
  459. macroname, replacement = define:match(REPLMACRO)
  460. if macroname and replacement then
  461. state:define(macroname, replacement)
  462. end
  463. -- read functional macros
  464. macroname, replacement = state:parseFunction(define)
  465. if macroname and replacement then
  466. state:define(macroname, replacement)
  467. end
  468. return line
  469. end
  470. -- ignore, because we dont have any pragma directives yet
  471. if cmd:match(PRAGMA) then
  472. return line
  473. end
  474. -- abort on unknown keywords
  475. error("unknown directive: "..line)
  476. else
  477. return line
  478. end
  479. end
  480. --[[ APPLY MACROS ]]--
  481. --line = state:apply(line);
  482. return line
  483. end
  484. local function doWork(state)
  485. local function _doWork(state)
  486. if not state:defined(__FILE__) then state:define(__FILE__, "<USER_CHUNK>", true) end
  487. local oldIndent = state:getIndent()
  488. while true do
  489. local input = state:getLine()
  490. if not input then break end
  491. local output = processLine(state, input)
  492. if not lcpp.FAST and not output then
  493. output = "" end -- output empty skipped lines
  494. if lcpp.DEBUG then output = output.." -- "..input end -- input as comment when DEBUG
  495. if output then coroutine.yield(output) end
  496. end
  497. if (oldIndent ~= state:getIndent()) then error("indentation level must be balanced within a file. was:"..oldIndent.." is:"..state:getIndent()) end
  498. end
  499. return coroutine.wrap(function() _doWork(state) end)
  500. end
  501. local function doWork2(state)
  502. local function _doWork2(state)
  503. if not state:defined(__FILE__) then state:define(__FILE__, "<USER_CHUNK>", true) end
  504. local oldIndent = state:getIndent()
  505. while true do
  506. local input = state:getLine()
  507. if not input then break end
  508. local output = processLine2(state, input)
  509. if not lcpp.FAST and not output then output = "" end -- output empty skipped lines
  510. if lcpp.DEBUG then output = output.." -- "..input end -- input as comment when DEBUG
  511. if output then coroutine.yield(output) end
  512. end
  513. if (oldIndent ~= state:getIndent()) then error("indentation level must be balanced within a file. was:"..oldIndent.." is:"..state:getIndent()) end
  514. end
  515. return coroutine.wrap(function() _doWork2(state) end)
  516. end
  517. local function includeFile(state, filename)
  518. local result, result_state = lcpp.compileHeaderFile("../src/" .. filename, state.defines)
  519. -- now, we take the define table of the sub file for further processing
  520. state.defines = result_state.defines
  521. -- and return the compiled result
  522. return result
  523. end
  524. -- sets a global define
  525. local function define(state, key, value, override)
  526. --print("define:"..key.." type:"..type(value))
  527. --if value and not override and state:defined(key) then error("already defined: "..key) end
  528. value = state:prepareMacro(value)
  529. state.defines[key] = value
  530. end
  531. -- parses CPP exressions
  532. -- i.e.: #if !defined(_UNICODE) && !defined(UNICODE)
  533. --
  534. --BNF:
  535. -- EXPR -> (BRACKET_OPEN)(EXPR)(BRACKET_CLOSE)
  536. -- EXPR -> (EXPR)(OR)(EXPR)
  537. -- EXPR -> (EXPR)(AND)(EXPR)
  538. -- EXPR -> (NOT)(EXPR)
  539. -- EXPR -> (FUNCTION)
  540. -- FUNCTION -> (IDENTIFIER)(BRACKET_OPEN)(ARGS)(BRACKET_CLOSE)
  541. -- ARGS -> ((IDENTIFIER)[(COMMA)(IDENTIFIER)])?
  542. --LEAVES:
  543. -- IGNORE -> " \t"
  544. -- BRACKET_OPEN -> "("
  545. -- BRACKET_CLOSE -> ")"
  546. -- OR -> "||"
  547. -- AND -> "&&"
  548. -- NOT -> "!"
  549. -- IDENTIFIER -> "[0-9a-zA-Z_]"
  550. --
  551. local LCPP_TOKENIZE_MACRO = {
  552. string = true,
  553. keywords = {
  554. CONCAT = "^##",
  555. },
  556. }
  557. local LCPP_TOKENIZE_EXPR = {
  558. string = false,
  559. keywords = {
  560. NOT = '^!',
  561. DEFINED = '^defined',
  562. BROPEN = '^[(]',
  563. BRCLOSE = '^[)]',
  564. AND = '^&&',
  565. OR = '^||',
  566. },
  567. }
  568. local function parseDefined(state, input)
  569. local result = false
  570. local bropen = false
  571. local brclose = false
  572. local ident = nil
  573. for key, value in input do
  574. if key == "BROPEN" then
  575. bropen = true
  576. end
  577. if key == "identifier" then
  578. ident = value
  579. if not bropen then break end
  580. end
  581. if key == "BRCLOSE" and ident then
  582. brclose = true
  583. break
  584. end
  585. end
  586. -- wiht and w/o brackets allowed
  587. if ident and ((bropen and brclose) or (not bropen and not brclose)) then
  588. return state:defined(ident)
  589. end
  590. error("expression parse error: defined(ident)")
  591. end
  592. local function parseExpr(state, input)
  593. -- first call gets string input. rest uses tokenizer
  594. if type(input) == "string" then input = tokenizer(input, LCPP_TOKENIZE_EXPR) end
  595. local result = false
  596. local _not = false
  597. for type, value in input do
  598. -- print("type:"..type.." value:"..value)
  599. if type == "NOT" then
  600. _not = true
  601. end
  602. if type == "BROPEN" then
  603. return state:parseExpr(input)
  604. end
  605. if type == "BRCLOSE" then
  606. return result
  607. end
  608. if type == "AND" then
  609. return result and state:parseExpr(input)
  610. end
  611. if type == "OR" then
  612. return result or state:parseExpr(input)
  613. end
  614. if type == "DEFINED" then
  615. if _not then
  616. result = not parseDefined(state, input)
  617. else
  618. result = parseDefined(state, input)
  619. end
  620. end
  621. end
  622. return result
  623. end
  624. -- apply macros chaining and string ops "##" and "#"
  625. local function prepareMacro(state, input)
  626. if type(input) ~= "string" then return input end
  627. input = state:apply(input)
  628. local out = {}
  629. for k, v, start, end_ in tokenizer(input, LCPP_TOKENIZE_MACRO) do
  630. if k == "CONCAT" then
  631. -- remove concat op "##"
  632. else
  633. table.insert(out, input:sub(start, end_))
  634. end
  635. end
  636. return table.concat(out)
  637. end
  638. -- i.e.: "MAX(x, y) (((x) > (y)) ? (x) : (y))"
  639. local function parseFunction(state, input)
  640. if not input then return end
  641. local name, argsstr, repl = input:match(FUNCMACRO)
  642. if not name or not argsstr or not repl then return end
  643. repl = state:prepareMacro(repl)
  644. -- rename args to %1,%2... for later gsub
  645. local noargs = 0
  646. for argname in argsstr:gmatch(IDENTIFIER) do
  647. noargs = noargs + 1
  648. repl = repl:gsub(argname, "%%"..noargs)
  649. end
  650. -- build pattern string: name(arg, arg, ...)
  651. local pattern
  652. if noargs == 0 then pattern = name.."%s*%(%s*%)" -- quick 0 arg version
  653. elseif noargs == 1 then pattern = name.."%s*%(%s*([^,%)]*)%s*%)" -- quick 1 arg version
  654. elseif noargs == 2 then pattern = name.."%s*%(%s*([^,%)]*)%s*,%s*([^,%)]*)%s*%)" -- quick 2 arg version
  655. else -- arbitrary arg version
  656. local buf = {}
  657. table.insert(buf, name)
  658. table.insert(buf, "%s*%(%s*")
  659. for i = 1, noargs do
  660. table.insert(buf, "([^,%)]*)%s*")
  661. if i < noargs then
  662. table.insert(buf, ",%s*")
  663. end
  664. end
  665. table.insert(buf, "%)")
  666. pattern = table.concat(buf)
  667. end
  668. -- build macro funcion
  669. local func = function(input)
  670. return input:gsub(pattern, repl)
  671. end
  672. return name, func
  673. end
  674. -- ------------
  675. -- LCPP INTERFACE
  676. -- ------------
  677. --- initialies a lcpp state. not needed manually. handy for testing
  678. function lcpp.init(input, predefines)
  679. -- create sate var
  680. local state = {} -- init the state object
  681. state.defines = {} -- the table of known defines and replacements
  682. state.screener = screener(input)
  683. state.lineno = 0 -- the current line number
  684. state.stack = {} -- stores wether the current stack level is to be included
  685. state.once = {} -- stack level was once true (first if that evals to true)
  686. -- funcs
  687. state.define = define
  688. state.undefine = function(state, key)
  689. state:define(key, nil)
  690. end
  691. state.defined = function(state, key)
  692. return state.defines[key] ~= nil
  693. end
  694. state.apply = apply
  695. state.includeFile = includeFile
  696. state.doWork = doWork
  697. state.doWork2 = doWork2
  698. state.getIndent = function(state)
  699. return #state.stack
  700. end
  701. state.openBlock = function(state, bool)
  702. state.stack[#state.stack+1] = bool
  703. state.once [#state.once+1] = bool
  704. state:define(__LCPP_INDENT__, state:getIndent(), true)
  705. end
  706. state.elseBlock = function(state, bool)
  707. if state.once[#state.once] then
  708. state.stack[#state.stack] = false
  709. else
  710. state.stack[#state.stack] = bool
  711. if bool then state.once[#state.once] = true end
  712. end
  713. end
  714. state.closeBlock = function(state)
  715. state.stack[#state.stack] = nil
  716. state.once [#state.once] = nil
  717. state:define(__LCPP_INDENT__, state:getIndent(), true)
  718. if state:getIndent() < 0 then error("Unopened block detected. Indentaion problem.") end
  719. end
  720. state.skip = function(state)
  721. for i = 1, #state.stack do
  722. if not state.stack[i] then return true end
  723. end
  724. return false
  725. end
  726. state.getLine = function(state)
  727. state.lineno = state.lineno + 1
  728. state:define(__LINE__, state.lineno, true)
  729. return state.screener()
  730. end
  731. state.prepareMacro = prepareMacro
  732. state.parseExpr = parseExpr
  733. state.parseFunction = parseFunction
  734. -- predefines
  735. state:define(__DATE__, os.date("%B %d %Y"), true)
  736. state:define(__TIME__, os.date("%H:%M:%S"), true)
  737. state:define(__LINE__, state.lineno, true)
  738. state:define(__LCPP_INDENT__, state:getIndent(), true)
  739. predefines = predefines or {}
  740. for k,v in pairs(lcpp.ENV) do state:define(k, v, true) end -- static ones
  741. for k,v in pairs(predefines) do state:define(k, v, true) end
  742. if lcpp.LCPP_TEST then lcpp.STATE = state end -- activate static state debugging
  743. return state
  744. end
  745. --- the preprocessors main function.
  746. -- returns the preprocessed output as a string.
  747. -- @param code data as string
  748. -- @param predefines OPTIONAL a table of predefined variables
  749. -- @usage lcpp.compile("#define bar 0x1337\nstatic const int foo = bar;")
  750. -- @usage lcpp.compile("#define bar 0x1337\nstatic const int foo = bar;", {["bar"] = "0x1338"})
  751. function lcpp.compile(code, predefines)
  752. local state = lcpp.init(code, predefines)
  753. local buf = {}
  754. for output in state:doWork() do
  755. table.insert(buf, output)
  756. end
  757. local output = table.concat(buf, NEWL)
  758. if lcpp.DEBUG then print(output) end
  759. return output, state
  760. end
  761. function lcpp.compile2(code, predefines)
  762. local state = lcpp.init(code, predefines)
  763. state:define(__LCPP_INSIDE_HEADERFILE__,true)
  764. local buf = {}
  765. for output in state:doWork2() do
  766. table.insert(buf, output)
  767. end
  768. local output = table.concat(buf, NEWL)
  769. if lcpp.DEBUG then print(output) end
  770. return output, state
  771. end
  772. --- preprocesses a file
  773. -- @param filename the file to read
  774. -- @param predefines OPTIONAL a table of predefined variables
  775. -- @usage out, state = lcpp.compileFile("../odbg/plugin.h", {["MAXPATH"]=260, ["UNICODE"]=true})
  776. function lcpp.compileFile(filename, predefines)
  777. if not filename then error("processFile() arg1 has to be a string") end
  778. local file = io.open(filename, 'r')
  779. if not file then error("file not found: "..filename) end
  780. local code = file:read('*a')
  781. predefines = predefines or {}
  782. predefines[__FILE__] = filename
  783. return lcpp.compile(code, predefines)
  784. end
  785. function lcpp.compileHeaderFile(filename, predefines)
  786. if not filename then error("processFile() arg1 has to be a string") end
  787. local file = io.open(filename, 'r')
  788. if not file then error("file not found: "..filename) end
  789. local code = file:read('*a')
  790. predefines = predefines or {}
  791. predefines[__FILE__] = filename
  792. return lcpp.compile2(code, predefines)
  793. end
  794. -- ------------
  795. -- REGISTER LCPP
  796. -- ------------
  797. --- disable lcpp processing for ffi, loadstring and such
  798. lcpp.disable = function()
  799. if lcpp.LCPP_LUA then
  800. -- activate LCPP_LUA actually does anything useful
  801. -- _G.loadstring = _G.loadstring_lcpp_backup
  802. end
  803. if lcpp.LCPP_FFI and pcall(require, "ffi") then
  804. ffi = require("ffi")
  805. if ffi.lcpp_cdef_backup then
  806. ffi.cdef = ffi.lcpp_cdef_backup
  807. ffi.lcpp_cdef_backup = nil
  808. end
  809. end
  810. end
  811. --- (re)enable lcpp processing for ffi, loadstring and such
  812. lcpp.enable = function()
  813. -- Use LCPP to process Lua code (load, loadfile, loadstring...)
  814. if lcpp.LCPP_LUA then
  815. -- TODO: make it properly work on all functions
  816. error("lcpp.LCPP_LUA = true -- not properly implemented yet");
  817. _G.loadstring_lcpp_backup = _G.loadstring
  818. _G.loadstring = function(str, chunk)
  819. return loadstring_lcpp_backup(lcpp.compile(str), chunk)
  820. end
  821. end
  822. -- Use LCPP as LuaJIT PreProcessor if used inside LuaJIT. i.e. Hook ffi.cdef
  823. if lcpp.LCPP_FFI and pcall(require, "ffi") then
  824. ffi = require("ffi")
  825. if not ffi.lcpp_cdef_backup then
  826. if not ffi.lcpp_defs then ffi.lcpp_defs = {} end -- defs are stored and reused
  827. ffi.lcpp = function(input)
  828. local output, state = lcpp.compile(input, ffi.lcpp_defs)
  829. ffi.lcpp_defs = state.defines
  830. return output
  831. end
  832. ffi.lcpp_cdef_backup = ffi.cdef
  833. ffi.cdef = function(input) return ffi.lcpp_cdef_backup(ffi.lcpp(input)) end
  834. end
  835. end
  836. end
  837. lcpp.enable()
  838. return lcpp