├── .gitignore
├── Makefile
├── README.md
├── config.ld
├── htdocs
├── lcpp-scm-1.rockspec
├── lcpp.lua
└── resources
├── lua-logo-label.ps
├── lua-logo-lcpp.png
└── plugin.h
/.gitignore:
--------------------------------------------------------------------------------
1 | # Object files
2 | *.o
3 |
4 | # Libraries
5 | *.lib
6 | *.a
7 |
8 | # Shared objects (inc. Windows DLLs)
9 | *.dll
10 | *.so
11 | *.so.*
12 | *.dylib
13 |
14 | # Executables
15 | *.exe
16 | *.out
17 | *.app
18 |
19 | # Lua doc
20 | /luadoc
21 |
22 | # vim
23 | .*.swp
24 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | default: all
2 | all: test doc
3 |
4 |
5 | prepare:
6 | mkdir -p luadoc
7 | clean:
8 | rm -rf luadoc
9 | test:
10 | lua -e 'local lcpp = require("lcpp"); lcpp.test();'
11 | test_file:
12 | lua -e 'lcpp = require("lcpp"); local out = lcpp.compileFile("resources/plugin.h", {UNICODE=1}); print(out);'
13 |
14 | run:
15 | lua -e 'lcpp = require("lcpp"); print("### entered lcpp interactive mode ###")' -i
16 | file:
17 | lua -e 'lcpp = require("lcpp"); local out = lcpp.compileFile("${FILE}"); print(out);'
18 |
19 | # DOC
20 | doc: luadoc
21 | ldoc: luadoc
22 | luadoc: prepare
23 | ldoc.lua ${PWD}
24 | cp resources/lua-logo-lcpp.png luadoc
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |

2 | ===
3 |
4 | ## lcpp - a C-PreProcessor for Lua 5.1 and LuaJIT ffi integration
5 |
6 | This module offers a standard preprocessor for C code in pure Lua.
7 | The primary usecase is to enable LuaJIT ffi preprocessing.
8 | But you can also preprocess any other stuff (even Lua code itself)
9 |
10 | ### Links
11 | * Project page http://lcpp.schmoock.net
12 | * GitHub Page https://github.com/m-schmoock/lcpp
13 | * Lua http://www.lua.org/
14 | * LuaJIT http://luajit.org
15 |
16 | ### Usage
17 | -- load lcpp (ffi.cdef wrapper turned on per default)
18 | local lcpp = require("lcpp")
19 |
20 | -- just use LuaJIT ffi and lcpp together
21 | ffi.cdef("#include ")
22 |
23 | -- or compile some code by hand
24 | local result = lcpp.compile("...")
25 | local out = lcpp.compile([[
26 | #define MAXPATH 260
27 | typedef struct somestruct_t {
28 | void* base;
29 | size_t size;
30 | wchar_t path[MAXPATH];
31 | } t_exe;
32 | ]])
33 |
34 | ### Supports
35 |
36 | features:
37 | transparent LuaJIT ffi extension
38 | replacement macros
39 | macro chaining
40 | functional macros
41 | multiline macros
42 | expressions (defined(xyz) && ...)
43 | concat operator ("##")
44 | screen C single- and multiline comments
45 |
46 | directives:
47 | #include
48 | #define
49 | #undef
50 | #pragma
51 | #error
52 | #if
53 | #ifdef
54 | #ifndef
55 | #else
56 | #else if
57 | #elif
58 | #endif
59 |
60 | macros:
61 | __LINE__
62 | __FILE__
63 | __DATE__
64 | __TIME__
65 | __LCPP_INDENT__
66 |
67 | ### Make targets
68 | make test # run the included test cases
69 | make doc # ldoc must be installed
70 |
71 | ### License
72 | MIT licencse included in lua modue.
73 |
74 | 2012-2014 Michael Schmoock
75 |
--------------------------------------------------------------------------------
/config.ld:
--------------------------------------------------------------------------------
1 | title = "lcpp"
2 | project = "API Doc"..[[
3 |
4 |
5 |
6 |
14 |
17 |
18 | ]]
19 |
20 | description = [[
21 | -- single module has no description
22 | ]]
23 |
24 | readme = {
25 | "README.md",
26 | }
27 |
28 | file = {
29 | ".",
30 | }
31 | dir = "luadoc"
32 | ext = "html"
33 | format = "discount"
34 |
35 |
--------------------------------------------------------------------------------
/htdocs:
--------------------------------------------------------------------------------
1 | luadoc
--------------------------------------------------------------------------------
/lcpp-scm-1.rockspec:
--------------------------------------------------------------------------------
1 | package = "lcpp"
2 | version = "scm-1"
3 | rockspec_format = "1.0"
4 |
5 | source = {
6 | url = "git://github.com/willsteel/lcpp.git",
7 | }
8 |
9 | description = {
10 | summary = "a C PreProcessor in Lua 5.1 for LuaJIT ffi",
11 | detailed = [[
12 | This module offers a standard preprocessor for C code in pure Lua.
13 | The primary usecase is to enable LuaJIT ffi preprocessing.
14 | But you can also preprocess any other data (even Lua code itself).
15 | github site: (https://github.com/willsteel/lcpp)
16 | ]],
17 | homepage = "http://lcpp.schmoock.net/",
18 | license = "MIT/X11",
19 | }
20 |
21 | dependencies = {
22 | "lua >= 5.1", -- luajit >= 2.0.0 is recommended but not required
23 | }
24 |
25 | build = {
26 | type = "none",
27 | install = {
28 | lua = {
29 | lcpp = "lcpp.lua",
30 | },
31 | },
32 | copy_directories = {},
33 | }
34 |
--------------------------------------------------------------------------------
/lcpp.lua:
--------------------------------------------------------------------------------
1 | ----------------------------------------------------------------------------
2 | --## lcpp - a C-PreProcessor for Lua 5.1 and LuaJIT ffi integration
3 | --
4 | -- Copyright (C) 2012-2014 Michael Schmoock
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 | -- local ffi = require("ffi")
24 | -- ffi.cdef("#include ")
25 | --
26 | -- -- use lcpp manually but add some predefines
27 | -- local lcpp = require("lcpp");
28 | -- local out = lcpp.compileFile("your_header.h", {UNICODE=1});
29 | -- print(out);
30 | --
31 | -- -- compile some input manually
32 | -- local out = lcpp.compile([[
33 | -- #include "myheader.h"
34 | -- #define MAXPATH 260
35 | -- typedef struct somestruct_t {
36 | -- void* base;
37 | -- size_t size;
38 | -- wchar_t path[MAXPATH];
39 | -- } t_exe;
40 | -- ]])
41 | -- -- the result should be like this
42 | -- out == [[
43 | -- //
44 | -- typedef struct somestruct_t {
45 | -- void* base;
46 | -- size_t size;
47 | -- wchar_t path[260];
48 | -- } t_exe;
49 | -- ]]
50 | --
51 | -- -- access lcpp defines dynamically (i.e. if used with ffi)
52 | -- local ffi = require("ffi")
53 | -- local lcpp = require("lcpp")
54 | -- ffi.cdef("#include ")
55 | -- =ffi.lcpp_defs.YOUR_DEFINE
56 | --
57 | --
58 | --## This CPPs BNF:
59 | -- RULES:
60 | -- CODE := {LINE}
61 | -- LINE := {STUFF NEWML} STUFF NEWL
62 | -- STUFF := DIRECTIVE | IGNORED_CONTENT
63 | -- DIRECTIVE := OPTSPACES CMD OPTSPACES DIRECTIVE_NAME WHITESPACES DIRECTIVE_CONTENT WHITESPACES NEWL
64 | --
65 | -- LEAVES:
66 | -- NEWL := "\n"
67 | -- NEWL_ESC := "\\n"
68 | -- WHITESPACES := "[ \t]+"
69 | -- OPTSPACES := "[ \t]*"
70 | -- COMMENT := "//(.-)$"
71 | -- MLCOMMENT := "/[*](.-)[*]/"
72 | -- IGNORED_CONTENT := "[^#].*"
73 | -- CMD := "#"
74 | -- DIRECTIVE_NAME := "include"|"define"|"undef"|"if"|"else"|"elif"|"else if"|"endif"|"ifdef"|"ifndef"|"pragma"
75 | -- DIRECTIVE_CONTENT := ".*?"
76 | --
77 | --## TODOs:
78 | -- - lcpp.LCPP_LUA for: load, loadfile
79 | --
80 | --## License (MIT)
81 | -- -----------------------------------------------------------------------------
82 | -- Permission is hereby granted, free of charge, to any person obtaining a copy
83 | -- of this software and associated documentation files (the "Software"), to deal
84 | -- in the Software without restriction, including without limitation the rights
85 | -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
86 | -- copies of the Software, and to permit persons to whom the Software is
87 | -- furnished to do so, subject to the following conditions:
88 | --
89 | -- The above copyright notice and this permission notice shall be included in
90 | -- all copies or substantial portions of the Software.
91 | --
92 | -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
93 | -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
94 | -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
95 | -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
96 | -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
97 | -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
98 | -- THE SOFTWARE.
99 | --
100 | -- MIT license: http://www.opensource.org/licenses/mit-license.php
101 | -- -----------------------------------------------------------------------------
102 | --
103 | -- @module lcpp
104 | local lcpp = {}
105 | -- check bit is avail or not
106 | local ok, bit = pcall(require, 'bit')
107 | if not ok then
108 | bit = {
109 | lshift = function (x, y)
110 | if y < 0 then return bit.rshift(x,-y) end
111 | return (x * 2^y) % (2^32)
112 | end,
113 | rshift = function (x, y)
114 | if y < 0 then return bit.lshift(x,-y) end
115 | return math.floor(x % (2^32) / (2^y))
116 | end,
117 | bxor = function (x, y)
118 | -- from http://lua-users.org/wiki/BitUtils
119 | local z = 0
120 | for i = 0, 31 do
121 | if (x % 2 == 0) then -- x had a '0' in bit i
122 | if ( y % 2 == 1) then -- y had a '1' in bit i
123 | y = y - 1
124 | z = z + 2 ^ i -- set bit i of z to '1'
125 | end
126 | else -- x had a '1' in bit i
127 | x = x - 1
128 | if (y % 2 == 0) then -- y had a '0' in bit i
129 | z = z + 2 ^ i -- set bit i of z to '1'
130 | else
131 | y = y - 1
132 | end
133 | end
134 | y = y / 2
135 | x = x / 2
136 | end
137 | return z
138 | end,
139 | bnot = function (x)
140 | -- if word size is not defined, I think it better than 0xFFFFFFFF - x.
141 | return -1 - x
142 | end,
143 | band = function (x, y)
144 | return ((x + y) - bit.bxor(x, y)) / 2
145 | end,
146 | bor = function (x, y)
147 | return bit.bnot(bit.band(bit.bnot(x), bit.bnot(y)))
148 | end,
149 | }
150 | end
151 |
152 | -- CONFIG
153 | lcpp.LCPP_LUA = false -- whether to use lcpp to preprocess Lua code (load, loadfile, loadstring...)
154 | lcpp.LCPP_FFI = true -- whether to use lcpp as LuaJIT ffi PreProcessor (if used in luaJIT)
155 | lcpp.LCPP_TEST = false -- whether to run lcpp unit tests when loading lcpp module
156 | lcpp.ENV = {} -- static predefines (env-like)
157 | lcpp.FAST = false -- perf. tweaks when enabled. con: breaks minor stuff like __LINE__ macros
158 | lcpp.DEBUG = false
159 |
160 | -- PREDEFINES
161 | local __FILE__ = "__FILE__"
162 | local __LINE__ = "__LINE__"
163 | local __DATE__ = "__DATE__"
164 | local __TIME__ = "__TIME__"
165 | local __LCPP_INDENT__ = "__LCPP_INDENT__"
166 |
167 | -- BNF LEAVES
168 | local ENDL = "$"
169 | local STARTL = "^"
170 | local NEWL = "\n"
171 | local NEWL_BYTE = NEWL:byte(1)
172 | local NEWL_ESC = "\\"
173 | local NEWML = "\\\n"
174 | local CMD = "#"
175 | local CMD_BYTE = CMD:byte(1)
176 | local COMMENT = "^(.-)//.-$"
177 | local MLCOMMENT = "/[*].-[*]/"
178 | local WHITESPACES = "%s+"
179 | local OPTSPACES = "%s*"
180 | local IDENTIFIER = "[_%a][_%w]*"
181 | local NOIDENTIFIER = "[^%w_]+"
182 | local FILENAME = "[0-9a-zA-Z.%-_/\\]+"
183 | local TEXT = ".+"
184 | local STRINGIFY = "#"
185 | local STRINGIFY_BYTE = STRINGIFY:byte(1)
186 | local STRING_LITERAL = ".*"
187 |
188 | -- BNF WORDS
189 | local _INCLUDE = "include"
190 | local _INCLUDE_NEXT = "include_next"
191 | local _DEFINE = "define"
192 | local _IFDEF = "ifdef"
193 | local _IFNDEF = "ifndef"
194 | local _ENDIF = "endif"
195 | local _UNDEF = "undef"
196 | local _IF = "if"
197 | local _ELSE = "else"
198 | local _ELIF = "elif"
199 | local _NOT = "!"
200 | local _ERROR = "error"
201 | local _WARNING = "warning"
202 | local _PRAGMA = "pragma"
203 |
204 | -- BNF RULES
205 | local INCLUDE = STARTL.._INCLUDE..WHITESPACES.."[<]("..FILENAME..")[>]"..OPTSPACES..ENDL
206 | local LOCAL_INCLUDE = STARTL.._INCLUDE..WHITESPACES.."[\"]("..FILENAME..")[\"]"..OPTSPACES..ENDL
207 | local INCLUDE_NEXT = STARTL.._INCLUDE_NEXT..WHITESPACES.."[\"<]("..FILENAME..")[\">]"..OPTSPACES..ENDL
208 | local DEFINE = STARTL.._DEFINE
209 | local IFDEF = STARTL.._IFDEF..WHITESPACES.."("..IDENTIFIER..")"..OPTSPACES..ENDL
210 | local IFNDEF = STARTL.._IFNDEF..WHITESPACES.."("..IDENTIFIER..")"..OPTSPACES..ENDL
211 | local ENDIF = STARTL.._ENDIF..OPTSPACES..ENDL
212 | local UNDEF = STARTL.._UNDEF..WHITESPACES.."("..IDENTIFIER..")"..OPTSPACES..ENDL
213 | local IF = STARTL.._IF..WHITESPACES.."(.*)"..ENDL
214 | local ELSE = STARTL.._ELSE..OPTSPACES..ENDL
215 | local ELIF = STARTL.._ELIF..WHITESPACES.."(.*)"..ENDL
216 | local ELSEIF = STARTL.._ELSE..WHITESPACES.._IF..WHITESPACES.."(.*)"..ENDL
217 | local ERROR = STARTL.._ERROR..WHITESPACES.."("..TEXT..")"..OPTSPACES..ENDL
218 | local WARNING = STARTL.._WARNING..WHITESPACES.."("..TEXT..")"..OPTSPACES..ENDL
219 | local ERROR_NOTEXT = STARTL.._ERROR..OPTSPACES..ENDL --> not required when we have POSIX regex
220 | local PRAGMA = STARTL.._PRAGMA
221 |
222 | -- speedups
223 | local TRUEMACRO = STARTL.."("..IDENTIFIER..")%s*$"
224 | local REPLMACRO = STARTL.."("..IDENTIFIER..")"..WHITESPACES.."(.+)$"
225 | local FUNCMACRO = STARTL.."("..IDENTIFIER..")%(([_%s%w,]*)%)%s*(.*)"
226 |
227 |
228 | -- ------------
229 | -- LOCAL UTILS
230 | -- ------------
231 | lcpp.STATE = {lineno = 0} -- current state for debugging the last operation
232 | local function error(msg) _G.print(debug.traceback()); _G.error(string.format("lcpp ERR [%04i] %s", lcpp.STATE.lineno, msg)) end
233 | local function print(msg) _G.print(string.format("lcpp INF [%04i] %s", lcpp.STATE.lineno, msg)) end
234 |
235 | -- splits a string using a pattern into a table of substrings
236 | local function gsplit(str, pat)
237 | local function _split(str, pat)
238 | local t = {} -- NOTE: use {n = 0} in Lua-5.0
239 | local fpat = "(.-)"..pat
240 | local last_end = 1
241 | local s, e, cap = str:find(fpat, 1)
242 | while s do
243 | if s ~= 1 or cap ~= "" then
244 | coroutine.yield(cap)
245 | end
246 | last_end = e + 1
247 | s, e, cap = str:find(fpat, last_end)
248 | end
249 | if last_end <= #str then
250 | cap = str:sub(last_end)
251 | coroutine.yield(cap)
252 | end
253 | end
254 | return coroutine.wrap(function() _split(str, pat) end)
255 | end
256 | local function split(str, pat)
257 | local t = {}
258 | for str in gsplit(str, pat) do table.insert(t, str) end
259 | return t
260 | end
261 |
262 | -- Checks whether a string starts with a given substring
263 | -- offset is optional
264 | local function strsw(str, pat, offset)
265 | if not str then return false end
266 | if not offset then offset = 0 end
267 | return string.sub(str, 1+offset, string.len(pat)+offset) == pat
268 | end
269 |
270 | -- Checks whether a string ends with a given substring
271 | local function strew(str, pat)
272 | if not str then return false end
273 | return pat=='' or string.sub(str,-string.len(pat)) == pat
274 | end
275 |
276 | -- string trim12 from lua wiki
277 | local function trim(str)
278 | local from = str:match"^%s*()"
279 | return from > #str and "" or str:match(".*%S", from)
280 | end
281 |
282 | -- returns the number of string occurrences
283 | local function findn(input, what)
284 | local count = 0
285 | local offset = 0
286 | local _
287 | while true do
288 | _, offset = string.find(input, what, offset+1, true)
289 | if not offset then return count end
290 | count = count + 1
291 | end
292 | end
293 |
294 | -- C literal string concatenation
295 | local function concatStringLiteral(input)
296 | -- screener does remove multiline definition, so just check ".*"%s*".*" pattern
297 | return input:gsub("\"("..STRING_LITERAL..")\""..OPTSPACES.."\"("..STRING_LITERAL..")\"", "\"%1%2\"")
298 | end
299 |
300 | -- c style boolean check (thus, 0 will be false)
301 | local function CBoolean(value)
302 | return value and (value ~= 0)
303 | end
304 |
305 | -- eval with c style number parse (UL, LL, L)
306 | local function CEval(expr)
307 | local ok, r = pcall(loadstring, "return " .. parseCInteger(expr))
308 | if ok and r then
309 | return r()
310 | else
311 | error(r)
312 | end
313 | end
314 |
315 | -- a lightweight and flexible tokenizer
316 | local function _tokenizer(str, setup)
317 | local defsetup = {
318 | -- EXAMPLE patterns have to be pretended with "^" for the tokenizer
319 | ["identifier"] = '^[_%a][_%w]*',
320 | ["number"] = '^[%+%-]?%d+[%.]?%d*[UL]*',
321 | ["ignore"] = '^%s+',
322 | ["string"] = true,
323 | ["keywords"] = {
324 | -- ["NAME"] = '^pattern',
325 | -- ...
326 | },
327 | }
328 | if not setup then
329 | setup = defsetup
330 | end
331 | setup.identifier = setup.identifier or defsetup.identifier
332 | setup.number = setup.number or defsetup.number
333 | setup.ignore = setup.ignore or defsetup.ignore
334 | if nil == setup.string then setup.string = true end
335 | setup.keywords = setup.keywords or {}
336 |
337 | local strlen = #str
338 | local i = 1
339 | local i1, i2
340 | local keyword
341 |
342 | local function find(pat)
343 | i1, i2 = str:find(pat,i)
344 | return i1 ~= nil
345 | end
346 |
347 | local function cut()
348 | return str:sub(i, i2)
349 | end
350 |
351 | local findKeyword
352 | if setup.keywords_order then
353 | findKeyword = function ()
354 | for _, name in ipairs(setup.keywords_order) do
355 | assert(setup.keywords[name])
356 | local pat = setup.keywords[name]
357 | local result = find(pat)
358 | if result then
359 | keyword = name
360 | return true
361 | end
362 | end
363 | end
364 | else
365 | findKeyword = function ()
366 | for name, pat in pairs(setup.keywords) do
367 | local result = find(pat)
368 | if result then
369 | keyword = name
370 | return true
371 | end
372 | end
373 | end
374 | end
375 |
376 | while true do
377 | if i > strlen then return 'eof', nil, strlen, strlen end
378 | if findKeyword() then
379 | coroutine.yield(keyword, cut(), i1, i2)
380 | elseif find(setup.ignore) then
381 | coroutine.yield("ignore", cut(), i1, i2)
382 | elseif find(setup.number) then
383 | coroutine.yield('number', tonumber(cut()), i1, i2)
384 | elseif find(setup.identifier) then
385 | coroutine.yield('identifier', cut(), i1, i2)
386 | elseif setup.string and (find('^"[^"]*"') or find("^'[^']*'")) then
387 | -- strip the quotes
388 | coroutine.yield('string', cut():sub(2,-2), i1, i2)
389 | else -- any other unknown character
390 | i1 = i
391 | i2 = i
392 | coroutine.yield('unknown', cut(), i1, i2)
393 | end
394 | i = i2+1
395 | end
396 | end
397 | local function tokenizer(str, setup)
398 | return coroutine.wrap(function() _tokenizer(str, setup) end)
399 | end
400 |
401 |
402 | -- ------------
403 | -- PARSER
404 | -- ------------
405 |
406 | local LCPP_TOKENIZE_COMMENT = {
407 | string = false,
408 | keywords = {
409 | MLCOMMENT = "^/%*.-%*/",
410 | SLCOMMENT = "^//.-\n",
411 | STRING_LITERAL = '^"[^"]*"',
412 | },
413 | }
414 | -- hint: LuaJIT ffi does not rely on us to remove the comments, but maybe other usecases
415 | local function removeComments(input)
416 | local out = {}
417 | for k, v, start, end_ in tokenizer(input, LCPP_TOKENIZE_COMMENT) do
418 | if k == "MLCOMMENT" then
419 | local newlineCount = findn(input:sub(start, end_), "\n")
420 | local newlines = string.rep("\n", newlineCount)
421 | table.insert(out, newlines)
422 | elseif k == "SLCOMMENT" then
423 | table.insert(out, "\n")
424 | else
425 | table.insert(out, input:sub(start, end_))
426 | end
427 | end
428 | return table.concat(out)
429 | end
430 |
431 | -- C style number parse (UL, LL, L) and (octet, hex, binary)
432 | local LCPP_TOKENIZE_INTEGER = {
433 | string = false,
434 | keywords_order = {
435 | "STRING_LITERAL",
436 | "CHAR_LITERAL",
437 | "HEX_LITERAL",
438 | "BIN_LITERAL",
439 | "OCT_LITERAL",
440 | "FPNUM_LITERAL",
441 | "NUMBER_LITERAL",
442 | },
443 | keywords = {
444 | STRING_LITERAL = '^"[^"]*"',
445 | CHAR_LITERAL = "^L'.*'",
446 | HEX_LITERAL = '^[%+%-]?%s*0x[a-fA-F%d]+[UL]*',
447 | BIN_LITERAL = '^[%+%-]?%s*0b%d+[UL]*',
448 | OCT_LITERAL = '^[%+%-]?%s*0%d+[UL]*',
449 | FPNUM_LITERAL = '^[%+%-]?%s*%d+[%.]?%d*e[%+%-]%d*',
450 | NUMBER_LITERAL = '^[%+%-]?%s*%d+[%.]?%d*[UL]+',
451 | },
452 | }
453 | local function parseCInteger(input)
454 | -- print('parseCInteger:input:' .. input)
455 | local out = {}
456 | local unary
457 | for k, v, start, end_ in tokenizer(input, LCPP_TOKENIZE_INTEGER) do
458 | -- print('parseCInteger:' .. k .. "|" .. v)
459 | if k == "CHAR_LITERAL" then
460 | table.insert(out, tostring(string.byte(loadstring("return \"" .. v:gsub("^L%'(.+)%'", "%1") .. "\"")())))
461 | elseif k == "HEX_LITERAL" then
462 | unary, v = v:match('([%+%-]?)0x([a-fA-F%d]+)[UL]*')
463 | local n = tonumber(v, 16)
464 | table.insert(out, unary..tostring(n))
465 | elseif k == "NUMBER_LITERAL" then
466 | v = v:match('([^UL]+)[UL]+')
467 | table.insert(out, v)
468 | elseif k == "BIN_LITERAL" then
469 | unary, v = v:match('([%+%-]?)0b([01]+)[UL]*')
470 | local n = tonumber(v, 2)
471 | table.insert(out, unary..tostring(n))
472 | elseif k == "OCT_LITERAL" then
473 | unary, v = v:match('([%+%-]?)(0%d+)[UL]*')
474 | local n = tonumber(v, 8)
475 | table.insert(out, unary..tostring(n))
476 | else
477 | table.insert(out, input:sub(start, end_))
478 | end
479 | end
480 | local str = table.concat(out)
481 | -- print('parseCInteger:result:'..str)
482 | return str
483 | end
484 |
485 | -- screener: revmoce comments, trim, ml concat...
486 | -- it only splits to cpp input lines and removes comments. it does not tokenize.
487 | local function screener(input)
488 | local function _screener(input)
489 | input = removeComments(input)
490 |
491 | -- concat mulit-line input.
492 | local count = 1
493 | while count > 0 do input, count = string.gsub(input, "^(.-)\\\n(.-)$", "%1 %2\n") end
494 |
495 | -- trim and join blocks not starting with "#"
496 | local buffer = {}
497 | for line in gsplit(input, NEWL) do
498 | --print('newline:'..line)
499 | line = trim(line)
500 | if #line > 0 then
501 | if line:byte(1) == CMD_BYTE then
502 | line = line:gsub("#%s*(.*)", "#%1") -- remove optinal whitespaces after "#". reduce triming later.
503 | if #buffer > 0 then
504 | coroutine.yield(table.concat(buffer, NEWL))
505 | buffer = {}
506 | end
507 | coroutine.yield(line)
508 | else
509 | if lcpp.FAST then
510 | table.insert(buffer, line)
511 | else
512 | coroutine.yield(line)
513 | end
514 | end
515 | elseif not lcpp.FAST then
516 | coroutine.yield(line)
517 | end
518 | end
519 | if #buffer > 0 then
520 | coroutine.yield(table.concat(buffer, NEWL))
521 | end
522 | end
523 |
524 | return coroutine.wrap(function() _screener(input) end)
525 | end
526 |
527 | -- apply currently known macros to input (and returns it)
528 | local LCPP_TOKENIZE_APPLY_MACRO = {
529 | keywords = {
530 | DEFINED = "^defined%s*%(%s*"..IDENTIFIER.."%s*%)" ,
531 | },
532 | }
533 | local function apply(state, input)
534 | while true do
535 | local out = {}
536 | local functions = {}
537 | local expand
538 |
539 | for k, v, start, end_ in tokenizer(input, LCPP_TOKENIZE_APPLY_MACRO) do
540 | -- print('tokenize:'..tostring(k).."|"..tostring(v))
541 | if k == "identifier" then
542 | local repl = v
543 | local macro = state.defines[v]
544 | if macro then
545 | if type(macro) == "boolean" then
546 | repl = ""
547 | expand = true
548 | elseif type(macro) == "string" then
549 | repl = macro
550 | expand = (repl ~= v)
551 | elseif type(macro) == "number" then
552 | repl = tostring(macro)
553 | expand = (repl ~= v)
554 | elseif type(macro) == "function" then
555 | local decl,cnt = input:sub(start):gsub("^[_%a][_%w]*%s*%b()", "%1")
556 | -- print('matching:'..input.."|"..decl.."|"..cnt)
557 | if cnt > 0 then
558 | repl = macro(decl)
559 | -- print("d&r:"..decl.."|"..repl)
560 | expand = true
561 | table.insert(out, repl)
562 | table.insert(out, input:sub(end_ + #decl))
563 | break
564 | else
565 | if input:sub(start):find("^[_%a][_%w]*%s*%(") then
566 | -- that is part of functional macro declaration.
567 | -- print(v ..': cannot replace:<'..input..'> read more line')
568 | return input,true
569 | else
570 | -- on macro name is also used as the symbol of some C declaration
571 | -- (e.g. /usr/include/spawn.h, /usr/include/sys/select.h on centos 6.4)
572 | -- no need to preprocess.
573 | print(v .. ': macro name but used as C declaration in:' .. input)
574 | end
575 | end
576 | end
577 | end
578 | table.insert(out, repl)
579 | elseif k == "DEFINED" then
580 | table.insert(out, input:sub(start, end_))
581 | else
582 | table.insert(out, input:sub(start, end_))
583 | end
584 | end
585 | input = table.concat(out)
586 | if not expand then
587 | break
588 | end
589 | end
590 |
591 | -- C liberal string concatenation, processing U,L,UL,LL
592 | return parseCInteger(concatStringLiteral(input)),false
593 | end
594 |
595 | -- processes an input line. called from lcpp doWork loop
596 | local function processLine(state, line)
597 | if not line or #line == 0 then return line end
598 | local cmd = nil
599 | if line:byte(1) == CMD_BYTE then cmd = line:sub(2) end
600 | -- print("process:"..line)--.."|"..tostring(state:skip()))
601 |
602 | --[[ IF/THEN/ELSE STRUCTURAL BLOCKS ]]--
603 | if cmd then
604 | local ifdef = cmd:match(IFDEF)
605 | local ifexp = cmd:match(IF)
606 | local ifndef = cmd:match(IFNDEF)
607 | local elif = cmd:match(ELIF)
608 | local elseif_ = cmd:match(ELSEIF)
609 | local else_ = cmd:match(ELSE)
610 | local endif = cmd:match(ENDIF)
611 | local struct = ifdef or ifexp or ifndef or elif or elseif_ or else_ or endif
612 |
613 | if struct then
614 | local skip = state:skip()
615 | if ifdef then state:openBlock(state:defined(ifdef)) end
616 | -- if skipped, it may have undefined expression. so not parse them
617 | if ifexp then state:openBlock(skip and true or CBoolean(state:parseExpr(ifexp))) end
618 | if ifndef then state:openBlock(not state:defined(ifndef)) end
619 | if elif then state:elseBlock((skip and skip < #state.stack) and true or CBoolean(state:parseExpr(elif))) end
620 | if elseif_ then state:elseBlock((skip and skip < #state.stack) and true or CBoolean(state:parseExpr(elseif_))) end
621 | if else_ then state:elseBlock(true) end
622 | if endif then state:closeBlock() end
623 | return -- remove structural directives
624 | end
625 | end
626 |
627 |
628 | --[[ SKIPPING ]]--
629 | if state:skip() then
630 | -- print('skip:' .. line)
631 | return
632 | end
633 |
634 |
635 | --[[ READ NEW DIRECTIVES ]]--
636 | if cmd then
637 | -- handle #undef ...
638 | local key = cmd:match(UNDEF)
639 | if type(key) == "string" then
640 | state:undefine(key)
641 | return
642 | end
643 |
644 | -- read "#define >FooBar...<" directives
645 | if cmd:match(DEFINE) then
646 | local define = trim(cmd:sub(DEFINE:len()+1))
647 | local macroname, replacement
648 |
649 | -- simple "true" defines
650 | macroname = define:match(TRUEMACRO)
651 | if macroname then
652 | state:define(macroname, true)
653 | else
654 |
655 | -- replace macro defines
656 | macroname, replacement = define:match(REPLMACRO)
657 | if macroname and replacement then
658 | state:define(macroname, replacement)
659 | else
660 |
661 | -- read functional macros
662 | macroname, replacement, source = state:parseFunction(define)
663 | if macroname and replacement then
664 | -- add original text for definition to check identify
665 | state:define(macroname, replacement, false, source)
666 | end
667 |
668 | end
669 | end
670 |
671 | return
672 | end
673 |
674 | -- handle #include ...
675 | local filename = cmd:match(INCLUDE)
676 | if filename then
677 | return state:includeFile(filename)
678 | end
679 | local filename = cmd:match(LOCAL_INCLUDE)
680 | if filename then
681 | return state:includeFile(filename, false, true)
682 | end
683 | local filename = cmd:match(INCLUDE_NEXT)
684 | if filename then
685 | --print("include_next:"..filename)
686 | return state:includeFile(filename, true)
687 | end
688 |
689 | -- ignore, because we dont have any pragma directives yet
690 | if cmd:match(PRAGMA) then return end
691 |
692 | -- handle #error
693 | local errMsg = cmd:match(ERROR)
694 | local errNoTxt = cmd:match(ERROR_NOTEXT)
695 | local warnMsg = cmd:match(WARNING)
696 | if errMsg then error(errMsg) end
697 | if errNoTxt then error("") end
698 | if warnMsg then
699 | print(warnMsg)
700 | return
701 | end
702 |
703 | -- abort on unknown keywords
704 | error("unknown directive: "..line)
705 | end
706 |
707 | if state.incompleteLine then
708 | --print('merge with incompleteLine:'..state.incompleteLine)
709 | line = (state.incompleteLine .. line)
710 | state.incompleteLine = nil
711 | end
712 |
713 |
714 | --[[ APPLY MACROS ]]--
715 | -- print(line)
716 | local _line,more = state:apply(line);
717 | -- print('endprocess:'.._line)
718 | if more then
719 | state.incompleteLine = line
720 | return ""
721 | else
722 | return _line
723 | end
724 |
725 | return line
726 | end
727 |
728 | local function doWork(state)
729 | local function _doWork(state)
730 | if not state:defined(__FILE__) then state:define(__FILE__, "", true) end
731 | local oldIndent = state:getIndent()
732 | while true do
733 | local input = state:getLine()
734 | if not input then break end
735 | local output = processLine(state, input)
736 | if not lcpp.FAST and not output then output = "" end -- output empty skipped lines
737 | if lcpp.DEBUG then output = output.." -- "..input end -- input as comment when DEBUG
738 | if output then coroutine.yield(output) end
739 | end
740 | if (oldIndent ~= state:getIndent()) then error("indentation level must be balanced within a file. was:"..oldIndent.." is:"..state:getIndent()) end
741 | end
742 | return coroutine.wrap(function() _doWork(state) end)
743 | end
744 |
745 | local function includeFile(state, filename, next, _local)
746 | local result, result_state = lcpp.compileFile(filename, state.defines, state.macro_sources, next, _local)
747 | -- now, we take the define table of the sub file for further processing
748 | state.defines = result_state.defines
749 | -- and return the compiled result
750 | return result
751 | end
752 |
753 | -- sets a global define
754 | local function define(state, key, value, override, macro_source)
755 | --print("define:"..key.." type:"..tostring(value).." value:"..tostring(pval))
756 | if value and not override then
757 | if type(value) == 'function' then
758 | assert(macro_source, "macro source should specify to check identity")
759 | local pval = state.macro_sources[key]
760 | if pval and (pval ~= macro_source) then error("already defined: "..key) end
761 | state.macro_sources[key] = macro_source
762 | else
763 | local pval = state.defines[key]
764 | if pval and (pval ~= value) then error("already defined: "..key) end
765 | end
766 | end
767 | state.defines[key] = state:prepareMacro(value)
768 | end
769 |
770 | -- parses CPP exressions
771 | -- i.e.: #if !defined(_UNICODE) && !defined(UNICODE)
772 | --
773 | --BNF:
774 | -- EXPR -> (BRACKET_OPEN)(EXPR)(BRACKET_CLOSE)
775 | -- EXPR -> (EXPR)(OR)(EXPR)
776 | -- EXPR -> (EXPR)(AND)(EXPR)
777 | -- EXPR -> (NOT)(EXPR)
778 | -- EXPR -> (FUNCTION)
779 | -- FUNCTION -> (IDENTIFIER)(BRACKET_OPEN)(ARGS)(BRACKET_CLOSE)
780 | -- ARGS -> ((IDENTIFIER)[(COMMA)(IDENTIFIER)])?
781 | --LEAVES:
782 | -- IGNORE -> " \t"
783 | -- BRACKET_OPEN -> "("
784 | -- BRACKET_CLOSE -> ")"
785 | -- OR -> "||"
786 | -- AND -> "&&"
787 | -- NOT -> "!"
788 | -- IDENTIFIER -> "[0-9a-zA-Z_]"
789 | --
790 |
791 | local LCPP_TOKENIZE_MACRO = {
792 | string = true,
793 | keywords_order = {
794 | "CONCAT",
795 | "SPACE",
796 | },
797 | keywords = {
798 | CONCAT = "^%s*##%s*",
799 | SPACE = "^%s",
800 | },
801 | }
802 | local LCPP_TOKENIZE_MACRO_ARGS = {
803 | string = true,
804 | keywords_order = {
805 | "STRING_LITERAL",
806 | "PARENTHESE",
807 | "FUNCTIONAL",
808 | "ARGS",
809 | "SINGLE_CHARACTER_ARGS",
810 | "COMMA",
811 | },
812 | keywords = {
813 | PARENTHESE = "^%s*%b()",
814 | FUNCTIONAL = "^".. IDENTIFIER .. "%s*%b()",
815 | STRING_LITERAL = '^"[^"]*"',
816 | ARGS = "^[^,%s][^,]*[^,%s]",
817 | SINGLE_CHARACTER_ARGS = "^[^,%s]",
818 | COMMA = "^,",
819 | },
820 | }
821 | local LCPP_TOKENIZE_EXPR = {
822 | string = false,
823 | keywords_order = {
824 | "DEFINED",
825 | "FUNCTIONAL_MACRO",
826 | "BROPEN",
827 | "BRCLOSE",
828 |
829 | "TENARY_START",
830 | "TENARY_MIDDLE",
831 | -- binary operators
832 | "EQUAL",
833 | "NOT_EQUAL",
834 | "AND",
835 | "OR",
836 | "BAND",
837 | "BOR",
838 | "BXOR",
839 | "PLUS",
840 | "MINUS",
841 | "MULTIPLY",
842 | "DIV",
843 | "MOD",
844 | "LTE",
845 | "MTE",
846 | "LSHIFT",
847 | "RSHIFT",
848 | "LT",
849 | "MT",
850 | -- unary operator
851 | "NOT",
852 | "BNOT",
853 | -- literal
854 | "STRING_LITERAL",
855 | "CHAR_LITERAL",
856 | "HEX_LITERAL",
857 | "FPNUM_LITERAL",
858 | "NUMBER_LITERAL",
859 | },
860 | keywords = {
861 | DEFINED = '^defined',
862 | FUNCTIONAL_MACRO = '^' .. IDENTIFIER .. "%s*%b()",
863 | BROPEN = '^[(]',
864 | BRCLOSE = '^[)]',
865 |
866 | TENARY_START = '^%?',
867 | TENARY_MIDDLE = '^%:',
868 |
869 | EQUAL = '^==',
870 | NOT_EQUAL = '^!=',
871 | AND = '^&&',
872 | OR = '^||',
873 | BAND = '^&',
874 | BOR = '^|',
875 | BXOR = '^%^',
876 | PLUS = '^%+',
877 | MINUS = '^%-',
878 | MULTIPLY = '^%*',
879 | DIV = '^%/',
880 | MOD = '^%%',
881 | LTE = '^<=',
882 | MTE = '^>=',
883 | LSHIFT = '^<<',
884 | RSHIFT = '^>>',
885 | LT = '^<',
886 | MT = '^>',
887 |
888 | NOT = '^!',
889 | BNOT = '^~',
890 |
891 | STRING_LITERAL = '^L?"[^"]*"',
892 | CHAR_LITERAL = "^L?'.*'",
893 | HEX_LITERAL = '^[%+%-]?0?x[a-fA-F%d]+[UL]*',
894 | FPNUM_LITERAL = '^[%+%-]?%d+[%.]?%d*e[%+%-]%d*',
895 | NUMBER_LITERAL = '^[%+%-]?0?b?%d+[%.]?%d*[UL]*',
896 | },
897 | }
898 |
899 | local function parseDefined(state, input)
900 | local result = false
901 | local bropen = false
902 | local brclose = false
903 | local ident = nil
904 |
905 | for key, value in input do
906 | if key == "BROPEN" then
907 | bropen = true
908 | end
909 | if key == "identifier" then
910 | ident = value
911 | if not bropen then break end
912 | end
913 | if key == "BRCLOSE" and ident then
914 | brclose = true
915 | break
916 | end
917 | end
918 |
919 | -- wiht and w/o brackets allowed
920 | if ident and ((bropen and brclose) or (not bropen and not brclose)) then
921 | return state:defined(ident)
922 | end
923 |
924 | error("expression parse error: defined(ident)")
925 | end
926 |
927 |
928 | --[[
929 | order : smaller is higher priority
930 | 1 () [] -> .
931 | 2 ! ~ - + * & sizeof type cast ++ --
932 | 3 * / %
933 | 4 + -
934 | 5 << >>
935 | 6 < <= > >=
936 | 7 == !=
937 | 8 &
938 | 9 ^
939 | 10 |
940 | 11 &&
941 | 12 ||
942 | 13 ?: = += -= *= /= %= &= |= ^= <<= >>=
943 | 14 ,
944 | ]]
945 | local combination_order = function (op, unary)
946 | if unary then
947 | if op == '-' or op == '!' or op == '~' then
948 | return 2
949 | else
950 | assert(false, 'unsupported unary operator:' .. op)
951 | end
952 | else
953 | if op == '*' or op == '/' or op == '%' then
954 | return 3
955 | elseif op == '+' or op == '-' then
956 | return 4
957 | elseif op == '>>' or op == '<<' then
958 | return 5
959 | elseif op == '<' or op == '>' or op == '<=' or op == '>=' then
960 | return 6
961 | elseif op == '==' or op == '!=' then
962 | return 7
963 | elseif op == '&' then
964 | return 8
965 | elseif op == '^' then
966 | return 9
967 | elseif op == '|' then
968 | return 10
969 | elseif op == '&&' then
970 | return 11
971 | elseif op == '||' then
972 | return 12
973 | elseif op == '?' or op == ':' then
974 | return 13
975 | else
976 | assert(false, 'unsupported operator:' .. op)
977 | end
978 | end
979 | end
980 |
981 | local evaluate
982 | evaluate = function (node)
983 | if not node.op then -- leaf node or leaf node with unary operators
984 | local v = node.v
985 | if node.uops then
986 | for _, uop in ipairs(node.uops) do
987 | -- print('apply uop:'..uop.."|"..tostring(v))
988 | if uop == '-' then
989 | v = -v
990 | elseif uop == '!' then
991 | v = (not v)
992 | elseif uop == '~' then
993 | v = bit.bnot(v)
994 | else
995 | assert(false, 'invalid uop:' .. tostring(uop))
996 | end
997 | end
998 | end
999 | -- print('after apply:'..tostring(v))
1000 | return v
1001 | end
1002 | -- print(node.op..':'..tostring(node.l.v or node.l.op).."("..type(node.l.v)..")|"..tostring(node.r.v or node.r.op).."("..type(node.r.v)..")")
1003 | if node.op == '+' then -- binary operators
1004 | return (evaluate(node.l) + evaluate(node.r))
1005 | elseif node.op == '-' then
1006 | return (evaluate(node.l) - evaluate(node.r))
1007 | elseif node.op == '*' then
1008 | return (evaluate(node.l) * evaluate(node.r))
1009 | elseif node.op == '/' then
1010 | return (evaluate(node.l) / evaluate(node.r))
1011 | elseif node.op == '%' then
1012 | return (evaluate(node.l) % evaluate(node.r))
1013 | elseif node.op == '==' then
1014 | return (evaluate(node.l) == evaluate(node.r))
1015 | elseif node.op == '!=' then
1016 | return (evaluate(node.l) ~= evaluate(node.r))
1017 | elseif node.op == '<<' then
1018 | return bit.lshift(evaluate(node.l), evaluate(node.r))
1019 | elseif node.op == '>>' then
1020 | return bit.rshift(evaluate(node.l), evaluate(node.r))
1021 | elseif node.op == '&&' then
1022 | return (CBoolean(evaluate(node.l)) and CBoolean(evaluate(node.r)))
1023 | elseif node.op == '||' then
1024 | return (CBoolean(evaluate(node.l)) or CBoolean(evaluate(node.r)))
1025 | elseif node.op == '&' then
1026 | return bit.band(evaluate(node.l), evaluate(node.r))
1027 | elseif node.op == '|' then
1028 | return bit.bor(evaluate(node.l), evaluate(node.r))
1029 | elseif node.op == '^' then
1030 | return bit.bxor(evaluate(node.l), evaluate(node.r))
1031 | elseif node.op == '<=' then
1032 | return (evaluate(node.l) <= evaluate(node.r))
1033 | elseif node.op == '>=' then
1034 | return (evaluate(node.l) >= evaluate(node.r))
1035 | elseif node.op == '<' then
1036 | return (evaluate(node.l) < evaluate(node.r))
1037 | elseif node.op == '>' then
1038 | return (evaluate(node.l) > evaluate(node.r))
1039 | else
1040 | assert(false, 'invalid op:' .. tostring(node.op))
1041 | end
1042 | end
1043 |
1044 | local function setValue(node, v)
1045 | -- print('setValue:' .. tostring(v).."|"..tostring(node.uops))-- .. "\t" .. debug.traceback())
1046 | if not node.op then
1047 | assert(not node.v, debug.traceback())
1048 | node.v = v
1049 | else
1050 | assert(node.l and (not node.r))
1051 | node.r = {v = v, uops = node.uops}
1052 | end
1053 | end
1054 |
1055 | local function setUnaryOp(node, uop)
1056 | -- print('setUnaryOp:' .. tostring(uop))-- .. "\t" .. debug.traceback())
1057 | if not node.uops then node.uops = {} end
1058 | table.insert(node.uops, 1, uop)
1059 | end
1060 |
1061 | local function parseExpr(state, input)
1062 | local node = {}
1063 | local root = node
1064 | -- first call gets string input. rest uses tokenizer
1065 | if type(input) == "string" then
1066 | -- print('parse:' .. input)
1067 | input = tokenizer(input, LCPP_TOKENIZE_EXPR)
1068 | end
1069 |
1070 | for type, value in input do
1071 | -- print("type:"..type.." value:"..value)
1072 | -- unary operator
1073 | if type == "NOT" or
1074 | type == "BNOT" then
1075 | setUnaryOp(node, value)
1076 | end
1077 | if type == "BROPEN" then
1078 | setValue(node, state:parseExpr(input))
1079 | end
1080 | if type == "BRCLOSE" then
1081 | --print('BRCLOSE:' .. tostring(result))
1082 | break
1083 | end
1084 | if type == "STRING_LITERAL" then
1085 | setValue(node, value:sub(value[1] == 'L' and 3 or 2,-2))
1086 | end
1087 | if type == "NUMBER_LITERAL" or type == "HEX_LITERAL" or type == "FPNUM_LITERAL" or type == "CHAR_LITERAL" then
1088 | setValue(node, tonumber(parseCInteger(value)))
1089 | end
1090 | -- tenary operator
1091 | -- tenary has lowest priority, so any other operation can be calculate now.
1092 | if type == "TENARY_START" then
1093 | local l = state:parseExpr(input)
1094 | local r = state:parseExpr(input)
1095 | if evaluate(root) then
1096 | return l
1097 | else
1098 | return r
1099 | end
1100 | end
1101 | if type == "TENARY_MIDDLE" then
1102 | break
1103 | end
1104 | -- binary operator
1105 | if type == "EQUAL" or
1106 | type == "NOT_EQUAL" or
1107 | type == "AND" or
1108 | type == "OR" or
1109 | type == "BAND" or
1110 | type == "BOR" or
1111 | type == "BXOR" or
1112 | type == "PLUS" or
1113 | type == "MINUS" or
1114 | type == "MULTIPLY" or
1115 | type == "DIV" or
1116 | type == "MOD" or
1117 | type == "LTE" or
1118 | type == "MTE" or
1119 | type == "LSHIFT" or
1120 | type == "RSHIFT" or
1121 | type == "LT" or
1122 | type == "MT" then
1123 | if node.op then
1124 | if not node.r then -- during parse right operand : uop1 uop2 ... uopN operand1 op1 uop(N+1) uop(N+2) ... [uop(N+K)]
1125 | assert(type == "MINUS", "error: operators come consequently: " .. tostring(node.op) .. " and " .. tostring(value))
1126 | -- unary operater after binary operator
1127 | setUnaryOp(node, value)
1128 | else -- uop1 uop2 ... uopN operand1 op1 uop(N+1) uop(N+2) ... uop(N+M) operand2 [op2]
1129 | -- print("operator processing:" .. tostring(node.op) .. "|" .. value .. "|" .. tostring(node.l) .. "|" .. tostring(node.r))
1130 | local tmp = node
1131 | while tmp do
1132 | -- print('compare ' .. value..' and ' .. tmp.op)
1133 | if combination_order(tmp.op) > combination_order(value) then
1134 | -- print(value..' is stronger than ' .. tmp.op)
1135 | break
1136 | end
1137 | tmp = tmp.parent
1138 | end
1139 | if tmp then
1140 | node = {
1141 | op = value,
1142 | l = tmp.r,
1143 | parent = tmp
1144 | }
1145 | tmp.r.parent = node
1146 | tmp.r = node
1147 | else
1148 | node = {
1149 | op = value,
1150 | l = root,
1151 | }
1152 | root.parent = node
1153 | root = node
1154 | end
1155 | end
1156 | elseif node.v ~= nil then -- uop1 uop2 ... uopN operand1 [op]
1157 | local devided
1158 | if node.uops then
1159 | for _, uop in ipairs(node.uops) do
1160 | if combination_order(uop, true) > combination_order(value) then
1161 | -- there is a binary operator which has stronger than any of the unary
1162 | devided = uop
1163 | end
1164 | end
1165 | end
1166 | if devided then
1167 | assert(false, "TODO: can we do something about this case??:"..value.." is stronger than "..devided)
1168 | else
1169 | node.l = { v = node.v, uops = node.uops }
1170 | node.v = nil
1171 | node.uops = nil
1172 | node.op = value
1173 | end
1174 | else -- unary operator : uop1 uop2 ... [uopN]
1175 | assert(type == "MINUS", "error: invalid unary operator:" .. value)
1176 | setUnaryOp(node, value)
1177 | end
1178 | end
1179 | if type == "DEFINED" then
1180 | setValue(node, parseDefined(state, input))
1181 | elseif type == "identifier" or type == "FUNCTIONAL_MACRO" then
1182 | -- print('ident:' .. value)
1183 | local eval = state:apply(value)
1184 | -- print('apply result ' .. eval .. "|" .. tostring(unprocessed))
1185 | if eval ~= value then
1186 | eval = state:parseExpr(eval)
1187 | -- print('re-evaluate expr ' .. tostring(eval))
1188 | setValue(node, eval)
1189 | else
1190 | -- undefined macro symbol is always treated as 0.
1191 | -- http://gcc.gnu.org/onlinedocs/cpp/If.html#If
1192 | setValue(node, 0)
1193 | end
1194 | end
1195 | end
1196 |
1197 | local r = evaluate(root)
1198 | -- print('evaluate:' .. tostring(r))
1199 | return r
1200 | end
1201 |
1202 | -- apply string ops "##"
1203 | local function prepareMacro(state, input)
1204 | if type(input) ~= "string" then return input end
1205 | repeat
1206 | local out = {}
1207 | local concat
1208 | for k, v, start, end_ in tokenizer(input, LCPP_TOKENIZE_MACRO) do
1209 | if k == "CONCAT" then
1210 | -- remove concat op "##"
1211 | concat = true
1212 | else
1213 | table.insert(out, input:sub(start, end_))
1214 | end
1215 | end
1216 | input = table.concat(out)
1217 | until not concat
1218 | return input
1219 | end
1220 |
1221 | -- macro args replacement function slower but more torelant for pathological case
1222 | local function replaceArgs(argsstr, repl)
1223 | local args = {}
1224 | argsstr = argsstr:sub(2,-2)
1225 | -- print('argsstr:'..argsstr)
1226 | local comma
1227 | for k, v, start, end_ in tokenizer(argsstr, LCPP_TOKENIZE_MACRO_ARGS) do
1228 | -- print("replaceArgs:" .. k .. "|" .. v)
1229 | if k == "ARGS" or k == "PARENTHESE" or k == "STRING_LITERAL" or
1230 | k == "FUNCTIONAL" or k == "SINGLE_CHARACTER_ARGS" then
1231 | table.insert(args, v)
1232 | comma = false
1233 | elseif k == "COMMA" then
1234 | if comma then
1235 | -- continued comma means empty parameter
1236 | table.insert(args, "")
1237 | end
1238 | comma = true
1239 | end
1240 | end
1241 | local v = repl:gsub("%$(%d+)", function (m) return args[tonumber(m)] or "" end)
1242 | -- print("replaceArgs:" .. repl .. "|" .. tostring(#args) .. "|" .. v)
1243 | return v
1244 | end
1245 |
1246 | -- i.e.: "MAX(x, y) (((x) > (y)) ? (x) : (y))"
1247 | local function parseFunction(state, input)
1248 | if not input then return end
1249 | local concat
1250 | local name, argsstr, repl = input:match(FUNCMACRO)
1251 | if not name or not argsstr or not repl then return end
1252 |
1253 | -- rename args to $1,$2... for later gsub
1254 | local noargs = 0
1255 | for argname in argsstr:gmatch(IDENTIFIER) do
1256 | noargs = noargs + 1
1257 | -- avoid matching substring of another identifier (eg. attrib matches __attribute__ and replace it)
1258 | repl = repl:gsub("(#*)(%s*)("..argname..")([_%w]?)", function (s1, s2, s3, s4)
1259 | if #s4 <= 0 then
1260 | return (#s1 == 1) and ("\"$"..noargs.."\"") or (s1..s2.."$"..noargs)
1261 | else
1262 | return s1..s2..s3..s4
1263 | end
1264 | end)
1265 | end
1266 | -- remove concat (after replace matching argument name to $1, $2, ...)
1267 | repl = repl:gsub("%s*##%s*", "")
1268 |
1269 | -- build macro funcion
1270 | local func = function(input)
1271 | return input:gsub(name.."%s*(%b())", function (match)
1272 | return replaceArgs(match, repl)
1273 | end)
1274 | end
1275 |
1276 | return name, func, repl
1277 | end
1278 |
1279 |
1280 | -- ------------
1281 | -- LCPP INTERFACE
1282 | -- ------------
1283 |
1284 | --- initialies a lcpp state. not needed manually. handy for testing
1285 | function lcpp.init(input, predefines, macro_sources)
1286 | -- create sate var
1287 | local state = {} -- init the state object
1288 | state.defines = {} -- the table of known defines and replacements
1289 | state.screener = screener(input)
1290 | state.lineno = 0 -- the current line number
1291 | state.stack = {} -- stores wether the current stack level is to be included
1292 | state.once = {} -- stack level was once true (first if that evals to true)
1293 | state.macro_sources = macro_sources or {} -- original replacement text for functional macro
1294 |
1295 | -- funcs
1296 | state.define = define
1297 | state.undefine = function(state, key)
1298 | state:define(key, nil)
1299 | state.macro_sources[key] = nil
1300 | end
1301 | state.defined = function(state, key)
1302 | return state.defines[key] ~= nil
1303 | end
1304 | state.apply = apply
1305 | state.includeFile = includeFile
1306 | state.doWork = doWork
1307 | state.getIndent = function(state)
1308 | return #state.stack
1309 | end
1310 | state.openBlock = function(state, bool)
1311 | state.stack[#state.stack+1] = bool
1312 | state.once [#state.once+1] = bool
1313 | state:define(__LCPP_INDENT__, state:getIndent(), true)
1314 | end
1315 | state.elseBlock = function(state, bool)
1316 | if state.once[#state.once] then
1317 | state.stack[#state.stack] = false
1318 | else
1319 | state.stack[#state.stack] = bool
1320 | if bool then state.once[#state.once] = true end
1321 | end
1322 | end
1323 | state.closeBlock = function(state)
1324 | state.stack[#state.stack] = nil
1325 | state.once [#state.once] = nil
1326 | state:define(__LCPP_INDENT__, state:getIndent(), true)
1327 | if state:getIndent() < 0 then error("Unopened block detected. Indentaion problem.") end
1328 | end
1329 | state.skip = function(state)
1330 | for i = 1, #state.stack do
1331 | if not state.stack[i] then return i end
1332 | end
1333 | return false
1334 | end
1335 | state.getLine = function(state)
1336 | state.lineno = state.lineno + 1
1337 | state:define(__LINE__, state.lineno, true)
1338 | return state.screener()
1339 | end
1340 | state.prepareMacro = prepareMacro
1341 | state.parseExpr = parseExpr
1342 | state.parseFunction = parseFunction
1343 |
1344 | -- predefines
1345 | state:define(__DATE__, os.date("%B %d %Y"), true)
1346 | state:define(__TIME__, os.date("%H:%M:%S"), true)
1347 | state:define(__LINE__, state.lineno, true)
1348 | state:define(__LCPP_INDENT__, state:getIndent(), true)
1349 | predefines = predefines or {}
1350 | for k,v in pairs(lcpp.ENV) do state:define(k, v, true) end -- static ones
1351 | for k,v in pairs(predefines) do state:define(k, v, true) end
1352 |
1353 | if lcpp.LCPP_TEST then lcpp.STATE = state end -- activate static state debugging
1354 |
1355 | return state
1356 | end
1357 |
1358 | --- the preprocessors main function.
1359 | -- returns the preprocessed output as a string.
1360 | -- @param code data as string
1361 | -- @param predefines OPTIONAL a table of predefined variables
1362 | -- @usage lcpp.compile("#define bar 0x1337\nstatic const int foo = bar;")
1363 | -- @usage lcpp.compile("#define bar 0x1337\nstatic const int foo = bar;", {["bar"] = "0x1338"})
1364 | function lcpp.compile(code, predefines, macro_sources)
1365 | local state = lcpp.init(code, predefines, macro_sources)
1366 | local buf = {}
1367 | for output in state:doWork() do
1368 | table.insert(buf, output)
1369 | end
1370 | local output = table.concat(buf, NEWL)
1371 | if lcpp.DEBUG then print(output) end
1372 | return output, state
1373 | end
1374 |
1375 | --- preprocesses a file
1376 | -- @param filename the file to read
1377 | -- @param predefines OPTIONAL a table of predefined variables
1378 | -- @usage out, state = lcpp.compileFile("../odbg/plugin.h", {["MAX_PAH"]=260, ["UNICODE"]=true})
1379 | function lcpp.compileFile(filename, predefines, macro_sources, next, _local)
1380 | if not filename then error("processFile() arg1 has to be a string") end
1381 | local file = io.open(filename, 'r')
1382 | if not file then error("file not found: "..filename) end
1383 | local code = file:read('*a')
1384 | predefines = predefines or {}
1385 | predefines[__FILE__] = filename
1386 | return lcpp.compile(code, predefines, macro_sources)
1387 | end
1388 |
1389 |
1390 | -- ------------
1391 | -- SATIC UNIT TESTS
1392 | -- ------------
1393 | function lcpp.test(suppressMsg)
1394 | local testLabelCount = 0
1395 | local function getTestLabel()
1396 | testLabelCount = testLabelCount + 1
1397 | return " lcpp_assert_"..testLabelCount
1398 | end
1399 |
1400 | -- this ugly global is required so our testcode can find it
1401 | _G.lcpp_test = {
1402 | assertTrueCalls = 0;
1403 | assertTrueCount = 0;
1404 | assertTrue = function()
1405 | lcpp_test.assertTrueCount = lcpp_test.assertTrueCount + 1;
1406 | end
1407 | }
1408 |
1409 | local testlcpp = [[
1410 | assert(__LINE__ == 1, "_LINE_ macro test 1: __LINE__")
1411 | // This test uses LCPP with lua code (uncommon but possible)
1412 | assert(__LINE__ == 3, "_LINE_ macro test 3: __LINE__")
1413 | /*
1414 | * It therefore asserts any if/else/macro functions and various syntaxes
1415 | * (including this comment, that would cause errors if not filtered)
1416 | */
1417 | assert(__LINE__ == 8, "_LINE_ macro test 8: __LINE__")
1418 | /*
1419 | assert(false, "multi-line comment not removed")
1420 | */
1421 | /* pathological case which contains single line comment start in multiline comments.
1422 | * e.g. this multiline comment should be finish next line.
1423 | * http://foobar.com */ // comment
1424 | /* if singleline comment processes first, sometimes indicator of end of multiline loss */ #define THIS_SHOULD_ENABLE 111 /*
1425 | continuous multiline comment after macro definition
1426 | //*/
1427 | ///* this removed.
1428 | assert(THIS_SHOULD_ENABLE == 111, "pathological multiline comment test")
1429 |
1430 |
1431 | #define TRUE
1432 | #define ONE (1)
1433 | #define TWO (2)
1434 | #define THREE_FUNC(x) (3)
1435 | #define LEET 0x1337
1436 | #define CLONG 123456789L
1437 | #define CLONGLONG 123456789123456789LL
1438 | #define CULONG 12345678UL
1439 | #define CUINT 123456U
1440 | #define BINARY -0b1001 /* -9 */
1441 | #define OCTET 075 /* 61 */
1442 | #define NON_OCTET 75
1443 | #define HEX 0xffffU
1444 | #define __P(x) x
1445 | #define WCHAR_ZERO L'\0'
1446 | # define NCURSES_IMPEXP
1447 | # define NCURSES_API
1448 | # define NCURSES_EXPORT(type) NCURSES_IMPEXP type NCURSES_API
1449 | #define MACRO_TO_ITSELF MACRO_TO_ITSELF
1450 | local MACRO_TO_ITSELF = 111
1451 | assert(MACRO_TO_ITSELF == 111, "can process macro to itself")
1452 | assert(WCHAR_ZERO == 0, "wchar evaluate fails")
1453 | assert(CLONG == 123456789, "read *L fails")
1454 | assert(CLONGLONG == 123456789123456789, "read *LL fails")
1455 | assert(CULONG == 12345678, "read *UL fails")
1456 | assert(HEX == 65535, "read hex fails")
1457 | #if CUINT != 123456U
1458 | assert(false, "cannot evaluate number which contains Unsinged postfix correctly")
1459 | #else
1460 | assert(CUINT == 123456, "read *U fails")
1461 | #endif
1462 | #pragma ignored
1463 | assert __P((BINARY == -9, "parse, binary literal fails"))
1464 | assert(OCTET == 61 and NON_OCTET == 75, "parse octet literal fails")
1465 |
1466 | lcpp_test.assertTrue()
1467 | assert(LEET == 0x1337, "simple #define replacement")
1468 | local msg
1469 | /* function to check macro expand to empty */
1470 | local function check_argnum(...)
1471 | return select('#', ...)
1472 | end
1473 | NCURSES_EXPORT(function) test_export(a)
1474 | return a + 1
1475 | end
1476 | assert(test_export(2) == 3, "EXPORT style macro")
1477 | local macrofunc = function __P((
1478 | a, b))
1479 | return a + b
1480 | end
1481 | assert(macrofunc(1, 2) == 3, "macro arg contains parenthese")
1482 |
1483 | msg = "tenary operator test"
1484 | #if (HEX % 2 == 1 ? CUINT : CULONG) == 123456
1485 | lcpp_test.assertTrue()
1486 | #else
1487 | assert(false, msg.."1")
1488 | #endif
1489 | #if (OCTET % 2 == 0 ? CUINT : CULONG) == 123456
1490 | assert(false, msg.."1")
1491 | #else
1492 | lcpp_test.assertTrue()
1493 | #endif
1494 |
1495 |
1496 |
1497 |
1498 | # if defined TRUE
1499 | lcpp_test.assertTrue() -- valid strange syntax test (spaces and missing brackets)
1500 | # endif
1501 |
1502 |
1503 | msg = "#define if/else test"
1504 | #ifdef TRUE
1505 | lcpp_test.assertTrue()
1506 | #else
1507 | assert(false, msg.."1")
1508 | #endif
1509 | #ifdef NOTDEFINED
1510 | assert(false, msg.."2")
1511 | #else
1512 | lcpp_test.assertTrue()
1513 | #endif
1514 | #ifndef NOTDEFINED
1515 | lcpp_test.assertTrue()
1516 | #else
1517 | assert(false, msg.."3")
1518 | #endif
1519 |
1520 |
1521 | msg = "#if defined statement test"
1522 | #if defined(TRUE)
1523 | lcpp_test.assertTrue()
1524 | #else
1525 | assert(false, msg.."1")
1526 | #endif
1527 | #if !defined(LEET) && !defined(TRUE)
1528 | assert(false, msg.."2")
1529 | #endif
1530 | #if !defined(NOTLEET) && !defined(NOTDEFINED)
1531 | lcpp_test.assertTrue()
1532 | #else
1533 | assert(false, msg.."3")
1534 | #endif
1535 | #if !(defined(LEET) && defined(TRUE))
1536 | assert(false, msg.."4")
1537 | #else
1538 | lcpp_test.assertTrue()
1539 | #endif
1540 | #if !defined(LEET) && !defined(TRUE)
1541 | assert(false, msg.."5")
1542 | #endif
1543 | #if defined(LEET) && defined(TRUE) && defined(NOTDEFINED)
1544 | assert(false, msg.."6")
1545 | #endif
1546 | #if ONE + TWO * TWO == 5
1547 | lcpp_test.assertTrue()
1548 | #else
1549 | assert(false, msg.."7")
1550 | #endif
1551 | #if (ONE + TWO) * TWO == 0x6
1552 | lcpp_test.assertTrue()
1553 | #else
1554 | assert(false, msg.."8")
1555 | #endif
1556 | #if ONE * TWO + ONE / TWO == 2.5
1557 | lcpp_test.assertTrue()
1558 | #else
1559 | assert(false, msg.."9")
1560 | #endif
1561 | #if ONE + ONE * TWO / TWO == 2
1562 | lcpp_test.assertTrue()
1563 | #else
1564 | assert(false, msg.."10")
1565 | #endif
1566 | #if TWO - - TWO == 4
1567 | lcpp_test.assertTrue()
1568 | #else
1569 | assert(false, msg.."11")
1570 | #endif
1571 | #if (TWO - - TWO) % (ONE + TWO) == 1
1572 | lcpp_test.assertTrue()
1573 | #else
1574 | assert(false, msg.."12")
1575 | #endif
1576 | #if ONE << TWO + TWO >> ONE == 8
1577 | lcpp_test.assertTrue()
1578 | #else
1579 | assert(false, msg.."13")
1580 | #endif
1581 | #if (ONE << TWO) + (TWO >> ONE) == 5
1582 | lcpp_test.assertTrue()
1583 | #else
1584 | assert(false, msg.."14")
1585 | #endif
1586 | #if (ONE << TWO) + TWO >> ONE == 3
1587 | lcpp_test.assertTrue()
1588 | #else
1589 | assert(false, msg.."15")
1590 | #endif
1591 | #if (THREE_FUNC(0xfffffU) & 4) == 0
1592 | lcpp_test.assertTrue()
1593 | #else
1594 | assert(false, msg.."16")
1595 | #endif
1596 | #if (0x3 & THREE_FUNC("foobar")) == 0b11
1597 | lcpp_test.assertTrue()
1598 | #else
1599 | assert(false, msg.."17")
1600 | #endif
1601 | #if defined(TWO) && ((TWO-0) < 3)
1602 | lcpp_test.assertTrue()
1603 | #else
1604 | assert(false, msg.."17")
1605 | #endif
1606 | #if TWO == 1--1
1607 | lcpp_test.assertTrue()
1608 | #else
1609 | assert(false, msg.."18")
1610 | #endif
1611 | #if HEX > 0xfFfFU
1612 | assert(false, msg.."18")
1613 | #else
1614 | lcpp_test.assertTrue()
1615 | #endif
1616 | #define TRUE_DEFINED defined(TRUE)
1617 | #if TRUE_DEFINED
1618 | lcpp_test.assertTrue()
1619 | #else
1620 | assert(false, msg.."19")
1621 | #endif
1622 | #define NOTDEFINED_DEFINED defined(TRUE) && defined(NOTDEFINED)
1623 | #if NOTDEFINED_DEFINED
1624 | assert(false, msg.."20")
1625 | #else
1626 | lcpp_test.assertTrue()
1627 | #endif
1628 | #if LEET && LEET > 0x1336
1629 | lcpp_test.assertTrue()
1630 | #else
1631 | assert(false, msg.."20")
1632 | #endif
1633 | #if NOTLEET && NOTLEET > 0x1336
1634 | assert(false, msg.."21")
1635 | #else
1636 | lcpp_test.assertTrue()
1637 | #endif
1638 | #if defined(NOTLEET) || BINARY + 0 >= 10L || !defined(TRUE)
1639 | assert(false, msg.."22")
1640 | #else
1641 | lcpp_test.assertTrue()
1642 | #endif
1643 |
1644 |
1645 |
1646 | msg = "macro chaining"
1647 | #define FOO 0x7B
1648 | #define BAR (FOO+0x7B)
1649 | assert(-BAR == -0x7B*2, msg)
1650 | #define BAZ 456
1651 | #define BLUR BA##Z
1652 | assert(BLUR == 456, msg)
1653 | local testfunc = function (x) return "["..tostring(x).."]" end
1654 | #define FOOBAR(x) testfunc(x)
1655 | assert(FOOBAR(1) == "[1]", msg)
1656 | #define testfunc(x)
1657 | #define FOOBAZ(x) testfunc(x)
1658 | assert(check_argnum(FOOBAR(1)) == 0, msg)
1659 | assert(check_argnum(FOOBAZ(1)) == 0, msg)
1660 | #undef testfunc
1661 | assert(FOOBAR(1) == "[1]", msg)
1662 | assert(FOOBAZ(1) == "[1]", msg)
1663 |
1664 |
1665 | msg = "indentation test"
1666 | assert(__LCPP_INDENT__ == 0, msg.."1")
1667 | #if defined(TRUE)
1668 | assert(__LCPP_INDENT__ == 1, msg.."2")
1669 | #endif
1670 | assert(__LCPP_INDENT__ == 0, msg.."3")
1671 |
1672 |
1673 | #define LCPP_FUNCTION_1(x, y) (x and not y)
1674 | assert(LCPP_FUNCTION_1(true, false), "function macro")
1675 | #define LCPP_FUNCTION_2(x, y) \
1676 | (not x \
1677 | and y)
1678 | assert(LCPP_FUNCTION_2(false, true), "multiline function macro")
1679 | #define LCPP_FUNCTION_3(_x, _y) LCPP_FUNCTION_2(_x, _y)
1680 | assert(LCPP_FUNCTION_3(false, true), "function macro with argname contains _")
1681 |
1682 | #define LCPP_FUNCTION_4_CHILD() false
1683 | #define LCPP_FUNCTION_4(_x)
1684 |
1685 | assert(check_argnum(LCPP_FUNCTION_4(LCPP_FUNCTION_4_CHILD())) == 0, "functional macro which receives functional macro as argument")
1686 | assert(check_argnum(LCPP_FUNCTION_4(LCPP_FUNCTION_3(true, true))) == 0, "functional macro which receives functional macro as argument2")
1687 |
1688 | #define LCPP_FUNCTION_5(x, y) (x) + (x) + (y) + (y)
1689 | assert(LCPP_FUNCTION_5(10, 20) == 60, "macro argument multiple usage")
1690 |
1691 | #define LCPP_NOT_FUNCTION (BLUR)
1692 | assert(LCPP_NOT_FUNCTION == 456, "if space between macro name and argument definition exists, it is regarded as replacement macro")
1693 |
1694 | #define __CONCAT(x, y, z, w) x ## y ## z (w)
1695 | local __fncall = function (x) return x + 111 end
1696 | assert(111 == __CONCAT( __ , fnc , all, 0 ), "funcall fails")
1697 | assert(222 == __CONCAT( __ ,, fncall, 111 ), "funcall fails2")
1698 | #define __ATTRIB_CALL(x, y, attrib) __attribute__(x, y, attrib)
1699 | local __attribute__ = function (x, y, attr)
1700 | return attr * (x + y)
1701 | end
1702 | assert(__ATTRIB_CALL( 1, 2 , 100 ) == 300, "funcall fails3")
1703 |
1704 |
1705 | msg = "#elif test"
1706 | #if defined(NOTDEFINED)
1707 | -- it should not be processed
1708 | #if NOTDEFINED
1709 | #else
1710 | assert(false, msg.."1")
1711 | #endif
1712 | #elif defined(NOTDEFINED)
1713 | assert(false, msg.."2")
1714 | #elif defined(TRUE)
1715 | lcpp_test.assertTrue()
1716 | #else
1717 | assert(false, msg.."3")
1718 | #endif
1719 |
1720 |
1721 | msg = "#else if test"
1722 | #if defined(NOTDEFINED)
1723 | assert(false, msg.."1")
1724 | #else if defined(NOTDEFINED)
1725 | assert(false, msg.."2")
1726 | #else if defined(TRUE)
1727 | lcpp_test.assertTrue()
1728 | #else
1729 | assert(false, msg.."3")
1730 | #endif
1731 |
1732 |
1733 | msg = "bock stack tree test"
1734 | #ifdef TRUE
1735 | #ifdef NOTDEFINED
1736 | assert(false, msg.."1")
1737 | #elif defined(TRUE)
1738 | lcpp_test.assertTrue()
1739 | #else
1740 | assert(false, msg.."2")
1741 | #endif
1742 | #else
1743 | assert(false, msg.."3")
1744 | #endif
1745 | #ifdef NOTDEFINED
1746 | #ifdef TRUE
1747 | assert(false, msg.."4")
1748 | #endif
1749 | #endif
1750 |
1751 | msg = "test concat ## operator"
1752 | #define CONCAT_TEST1 foo##bar
1753 | local CONCAT_TEST1 = "blubb"
1754 | assert(foobar == "blubb", msg)
1755 | #define CONCAT_TEST2() bar##foo
1756 | local CONCAT_TEST2() = "blubb"
1757 | assert(barfoo == "blubb", msg)
1758 | -- dont process ## within strings
1759 | #define CONCAT_TEST3 "foo##bar"
1760 | assert(CONCAT_TEST3 == "foo##bar", msg)
1761 | msg = "test concat inside func type macro"
1762 | #define CONCAT_TEST4(baz) CONCAT_TEST##baz
1763 | local CONCAT_TEST4(1) = "bazbaz"
1764 | assert(foobar == "bazbaz", msg)
1765 |
1766 | msg = "#undef test"
1767 | #define UNDEF_TEST
1768 | #undef UNDEF_TEST
1769 | #ifdef UNDEF_TEST
1770 | assert(false, msg)
1771 | #endif
1772 |
1773 | msg = "stringify operator(#) test"
1774 | #define STRINGIFY(str) #str
1775 | assert(STRINGIFY(abcde) == "abcde", msg)
1776 | #define STRINGIFY_AND_CONCAT(str1, str2) #str1 ## \
1777 | #str2
1778 | assert(STRINGIFY_AND_CONCAT(fgh, ij) == "fghij", msg)
1779 |
1780 | #define msg_concat(msg1, msg2) msg1 ## msg2
1781 | assert("I, am, lcpp" == msg_concat("I, am", ", lcpp"), "processing macro argument which includes ,")
1782 |
1783 | #define FUNC__ARG 500
1784 | #define __ARG 100
1785 | #define FUNC(x) FUNC##x
1786 | assert(FUNC(__ARG) == 500, "create new macro symbol by concat")
1787 |
1788 | msg = "same macro definition which has exactly same value allows. (checked with gcc 4.4.7 __WORDSIZE)"
1789 | #define DUP_MACRO_DEF (111)
1790 | #define DUP_MACRO_DEF (111)
1791 | #define DUP_FUNC_MACRO(x, y) x + y
1792 | #define DUP_FUNC_MACRO(x, y) x + y
1793 |
1794 |
1795 | msg = "check #if conditional check"
1796 | #define VALUE1 (123)
1797 | #if VALUE1 != 123
1798 | assert(false, msg .." #if " .. tostring(VALUE1) .. " != 123")
1799 | #endif
1800 | #if 123 != VALUE1
1801 | assert(false, msg .." #if " .. tostring(VALUE1) .. " != 123 (2)")
1802 | #endif
1803 |
1804 | #define VALUE2 ("hoge")
1805 | #if VALUE2 == "hoge"
1806 | #define VALUE3 (true)
1807 | #endif
1808 | assert(VALUE3 == true, msg .. " #if " .. tostring(VALUE3) .. " == true")
1809 |
1810 | #define VALUE4 (VALUE1 + VALUE1)
1811 | #if VALUE4 != 246
1812 | assert(false, msg .." #if check for nested definition:" .. tostring(VALUE4))
1813 | #endif
1814 |
1815 | msg = "+-*/<> in #if expression:"
1816 | #define CALC_VALUE_A (1)
1817 | #define CALC_VALUE_B (2)
1818 | #if (CALC_VALUE_A + CALC_VALUE_B) != 3
1819 | assert(false, msg .. " + not work:")
1820 | #endif
1821 | #if (CALC_VALUE_A * CALC_VALUE_B) != 2
1822 | assert(false, msg .. " * not work:")
1823 | #endif
1824 | #if (CALC_VALUE_A - CALC_VALUE_B) != -1
1825 | assert(false, msg .. " - not work:")
1826 | #endif
1827 | #if (CALC_VALUE_A / CALC_VALUE_B) != 0.5
1828 | assert(false, msg .. " / not work:")
1829 | #endif
1830 |
1831 | #if (CALC_VALUE_A >= CALC_VALUE_A)
1832 | #else
1833 | assert(false, msg .. " >= not work1")
1834 | #endif
1835 | #if (CALC_VALUE_B >= CALC_VALUE_A)
1836 | #else
1837 | assert(false, msg .. " >= not work2")
1838 | #endif
1839 |
1840 | #if (CALC_VALUE_B <= CALC_VALUE_B)
1841 | #else
1842 | assert(false, msg .. " <= not work1")
1843 | #endif
1844 | #if (CALC_VALUE_A <= CALC_VALUE_B)
1845 | #else
1846 | assert(false, msg .. " <= not work2")
1847 | #endif
1848 |
1849 | #if (CALC_VALUE_B > CALC_VALUE_A)
1850 | #else
1851 | assert(false, msg .. " > not work1")
1852 | #endif
1853 | #if (CALC_VALUE_A > CALC_VALUE_A)
1854 | assert(false, msg .. " > not work2")
1855 | #endif
1856 |
1857 | #if (CALC_VALUE_A < CALC_VALUE_B)
1858 | #else
1859 | assert(false, msg .. " < not work1")
1860 | #endif
1861 | #if (CALC_VALUE_B < CALC_VALUE_B)
1862 | assert(false, msg .. " < not work2")
1863 | #endif
1864 | #if (CALC_VALUE_B | CALC_VALUE_A) != 3
1865 | assert(false, msg .. " | not work")
1866 | #endif
1867 | #if (CALC_VALUE_B & CALC_VALUE_A) != 0
1868 | assert(false, msg .. " & not work")
1869 | #endif
1870 | #if (CALC_VALUE_B ^ CALC_VALUE_A) != 3
1871 | assert(false, msg .. " ^ not work")
1872 | #endif
1873 | #if -CALC_VALUE_B != -2
1874 | assert(false, msg .. " unary - not work")
1875 | #endif
1876 | #if ~CALC_VALUE_B != -3
1877 | assert(false, msg .. " unary ~ not work")
1878 | #endif
1879 | ]]
1880 | lcpp.FAST = false -- enable full valid output for testing
1881 | lcpp.SELF_TEST = true
1882 | local testlua = lcpp.compile(testlcpp)
1883 | lcpp.SELF_TEST = nil
1884 | -- print(testlua)
1885 | assert(loadstring(testlua, "testlua"))()
1886 | lcpp_test.assertTrueCalls = findn(testlcpp, "lcpp_test.assertTrue()")
1887 | assert(lcpp_test.assertTrueCount == lcpp_test.assertTrueCalls, "assertTrue calls:"..lcpp_test.assertTrueCalls.." count:"..lcpp_test.assertTrueCount)
1888 | _G.lcpp_test = nil -- delete ugly global hack
1889 | if not suppressMsg then print("Test run suscessully") end
1890 | end
1891 | if lcpp.LCPP_TEST then lcpp.test(true) end
1892 |
1893 |
1894 | -- ------------
1895 | -- REGISTER LCPP
1896 | -- ------------
1897 |
1898 | --- disable lcpp processing for ffi, loadstring and such
1899 | lcpp.disable = function()
1900 | if lcpp.LCPP_LUA then
1901 | -- activate LCPP_LUA actually does anything useful
1902 | -- _G.loadstring = _G.loadstring_lcpp_backup
1903 | end
1904 |
1905 | if lcpp.LCPP_FFI and pcall(require, "ffi") then
1906 | ffi = require("ffi")
1907 | if ffi.lcpp_cdef_backup then
1908 | ffi.cdef = ffi.lcpp_cdef_backup
1909 | ffi.lcpp_cdef_backup = nil
1910 | end
1911 | end
1912 | end
1913 |
1914 | --- (re)enable lcpp processing for ffi, loadstring and such
1915 | lcpp.enable = function()
1916 | -- Use LCPP to process Lua code (load, loadfile, loadstring...)
1917 | if lcpp.LCPP_LUA then
1918 | -- TODO: make it properly work on all functions
1919 | error("lcpp.LCPP_LUA = true -- not properly implemented yet");
1920 | _G.loadstring_lcpp_backup = _G.loadstring
1921 | _G.loadstring = function(str, chunk)
1922 | return loadstring_lcpp_backup(lcpp.compile(str), chunk)
1923 | end
1924 | end
1925 | -- Use LCPP as LuaJIT PreProcessor if used inside LuaJIT. i.e. Hook ffi.cdef
1926 | if lcpp.LCPP_FFI and pcall(require, "ffi") then
1927 | ffi = require("ffi")
1928 | if not ffi.lcpp_cdef_backup then
1929 | if not ffi.lcpp_defs then ffi.lcpp_defs = {} end -- defs are stored and reused
1930 | ffi.lcpp = function(input)
1931 | local output, state = lcpp.compile(input, ffi.lcpp_defs, ffi.lcpp_macro_sources)
1932 | ffi.lcpp_defs = state.defines
1933 | ffi.lcpp_macro_sources = state.macro_sources
1934 | return output
1935 | end
1936 | ffi.lcpp_cdef_backup = ffi.cdef
1937 | ffi.cdef = function(input)
1938 | if true then
1939 | return ffi.lcpp_cdef_backup(ffi.lcpp(input))
1940 | else
1941 | local fn,cnt = input:gsub('#include ["<].-([^/]+%.h)[">]', '%1')
1942 | input = ffi.lcpp(input)
1943 | if cnt > 0 then
1944 | local f = io.open("./tmp/"..fn, 'w')
1945 | if f then
1946 | f:write(input)
1947 | f:close()
1948 | else
1949 | assert(fn:find('/'), 'cannot open: ./tmp/'..fn)
1950 | end
1951 | end
1952 | return ffi.lcpp_cdef_backup(input)
1953 | end
1954 | end
1955 | end
1956 | end
1957 | end
1958 |
1959 | lcpp.enable()
1960 | return lcpp
1961 |
1962 |
--------------------------------------------------------------------------------
/resources/lua-logo-label.ps:
--------------------------------------------------------------------------------
1 | %!PS-Adobe-2.0 EPSF-2.0
2 | %%Title: Lua logo
3 | %%Creator: lua@tecgraf.puc-rio.br
4 | %%CreationDate: Wed Nov 29 19:04:04 EDT 2000
5 | %%BoundingBox: -45 0 1035 1080
6 | %%Pages: 1
7 | %%EndComments
8 | %%EndProlog
9 |
10 | %------------------------------------------------------------------------------
11 | %
12 | % Graphic design by Alexandre Nakonechnyj.
13 | % PostScript programming by the Lua team.
14 | % This code is hereby placed in the public domain.
15 | %
16 | % Permission is hereby granted, without written agreement and without license
17 | % or royalty fees, to use, copy, and distribute this logo for any purpose,
18 | % including commercial applications, subject to the following conditions:
19 | %
20 | % * The origin of this logo must not be misrepresented; you must not
21 | % claim that you drew the original logo. We recommend that you give credit
22 | % to the graphics designer in all printed matter that includes the logo.
23 | %
24 | % * The only modification you can make is to adapt the orbiting text to
25 | % your product name.
26 | %
27 | % * The logo can be used in any scale as long as the relative proportions
28 | % of its elements are maintained.
29 | %
30 | %------------------------------------------------------------------------------
31 |
32 | /LABEL (lcpp) def
33 |
34 | %-- DO NOT CHANGE ANYTHING BELOW THIS LINE ------------------------------------
35 |
36 | /PLANETCOLOR {0 0 0.5 setrgbcolor} bind def
37 | /HOLECOLOR {1.0 setgray} bind def
38 | /ORBITCOLOR {0.5 setgray} bind def
39 | /LOGOFONT {/Helvetica 0.90} def
40 | /LABELFONT {/Helvetica 0.36} def
41 |
42 | %------------------------------------------------------------------------------
43 |
44 | /MOONCOLOR {PLANETCOLOR} bind def
45 | /LOGOCOLOR {HOLECOLOR} bind def
46 | /LABELCOLOR {ORBITCOLOR} bind def
47 |
48 | /LABELANGLE 125 def
49 | /LOGO (Lua) def
50 |
51 | /DASHANGLE 10 def
52 | /HALFDASHANGLE DASHANGLE 2 div def
53 |
54 | % moon radius. planet radius is 1.
55 | /r 1 2 sqrt 2 div sub def
56 |
57 | /D {0 360 arc fill} bind def
58 | /F {exch findfont exch scalefont setfont} bind def
59 |
60 | % place it nicely on the paper
61 | /RESOLUTION 1024 def
62 | RESOLUTION 2 div dup translate
63 | RESOLUTION 2 div 2 sqrt div dup scale
64 |
65 | %-------------------------------------------------------------------- planet --
66 | PLANETCOLOR
67 | 0 0 1 D
68 |
69 | %---------------------------------------------------------------------- hole --
70 | HOLECOLOR
71 | 1 2 r mul sub dup r D
72 |
73 | %---------------------------------------------------------------------- moon --
74 | MOONCOLOR
75 | 1 1 r D
76 |
77 | %---------------------------------------------------------------------- logo --
78 | LOGOCOLOR
79 | LOGOFONT
80 | F
81 | LOGO stringwidth pop 2 div neg
82 | -0.5 moveto
83 | LOGO show
84 |
85 | %------------------------------------------------------------------------------
86 | % based on code from Blue Book Program 10, on pages 167--169
87 | % available at ftp://ftp.adobe.com/pub/adobe/displaypostscript/bluebook.shar
88 |
89 | % str ptsize centerangle radius outsidecircletext --
90 | /outsidecircletext {
91 | circtextdict begin
92 | /radius exch def
93 | /centerangle exch def
94 | /ptsize exch def
95 | /str exch def
96 |
97 | gsave
98 | str radius ptsize findhalfangle
99 | centerangle
100 | add rotate
101 | str
102 | { /charcode exch def
103 | ( ) dup 0 charcode put outsideplacechar
104 | } forall
105 |
106 | grestore
107 | end
108 | } def
109 |
110 | % string radius ptsize findhalfangle halfangle
111 | /findhalfangle {
112 | 4 div add
113 | exch
114 | stringwidth pop 2 div
115 | exch
116 | 2 mul 3.1415926535 mul div 360 mul
117 | } def
118 |
119 | /circtextdict 16 dict def
120 | circtextdict begin
121 |
122 | /outsideplacechar {
123 | /char exch def
124 | /halfangle char radius ptsize findhalfangle def
125 | gsave
126 | halfangle neg rotate
127 | radius 0 translate
128 | -90 rotate
129 | char stringwidth pop 2 div neg 0 moveto
130 | char show
131 | grestore
132 | halfangle 2 mul neg rotate
133 | } def
134 |
135 | end
136 |
137 | %--------------------------------------------------------------------- label --
138 | LABELFONT
139 | F
140 |
141 | /LABELSIZE LABELFONT exch pop def
142 | /LABELRADIUS LABELSIZE 3 div 1 r add sub neg 1.02 mul def
143 |
144 |
145 | /HALFANGLE
146 | LABEL LABELRADIUS LABELSIZE findhalfangle
147 | HALFDASHANGLE div ceiling HALFDASHANGLE mul
148 | def
149 |
150 | /LABELANGLE
151 | 60 LABELANGLE HALFANGLE sub
152 | lt
153 | {
154 | HALFANGLE
155 | HALFANGLE DASHANGLE div floor DASHANGLE mul
156 | eq
157 | {LABELANGLE DASHANGLE div ceiling DASHANGLE mul}
158 | {LABELANGLE HALFDASHANGLE sub DASHANGLE div round DASHANGLE mul HALFDASHANGLE add}
159 | ifelse
160 | }
161 | {HALFANGLE 60 add}
162 | ifelse
163 | def
164 |
165 | LABELCOLOR
166 | LABEL
167 | LABELSIZE
168 | LABELANGLE
169 | LABELRADIUS
170 | outsidecircletext
171 |
172 | %--------------------------------------------------------------------- orbit --
173 | ORBITCOLOR
174 | 0.03 setlinewidth
175 | [1 r add 3.1415926535 180 div HALFDASHANGLE mul mul] 0 setdash
176 | newpath
177 | 0 0
178 | 1 r add
179 | 3 copy
180 | 30
181 | LABELANGLE HALFANGLE add
182 | arcn
183 | stroke
184 | 60
185 | LABELANGLE HALFANGLE sub
186 | 2 copy
187 | lt {arc stroke} {4 {pop} repeat} ifelse
188 |
189 | %------------------------------------------------------------------ copyright --
190 | /COPYRIGHT
191 | (Graphic design by A. Nakonechnyj. Copyright (c) 1998, All rights reserved.)
192 | def
193 |
194 | LABELCOLOR
195 | LOGOFONT
196 | 32 div
197 | F
198 | 2 sqrt 0.99 mul
199 | dup
200 | neg
201 | moveto
202 | COPYRIGHT
203 | 90 rotate
204 | %show
205 |
206 | %---------------------------------------------------------------------- done --
207 | showpage
208 |
209 | %%Trailer
210 | %%EOF
211 |
--------------------------------------------------------------------------------
/resources/lua-logo-lcpp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/m-schmoock/lcpp/4a6987071ec74b083a9e88954d0e66c8d6ba2ba7/resources/lua-logo-lcpp.png
--------------------------------------------------------------------------------