Skip to content

Commit fbd1d5b

Browse files
committed
add remaining directives
1 parent 2c0bb2e commit fbd1d5b

File tree

2 files changed

+136
-0
lines changed

2 files changed

+136
-0
lines changed

nattlua/definitions/lua/ffi/preprocessor/parser.lua

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,19 @@ function META.New(tokens, code, config)
3434
config.system_include_paths = config.system_include_paths or {}
3535

3636
if config.add_standard_defines ~= false then
37+
-- Generate __DATE__ and __TIME__ at preprocessing time
38+
local date_str = os.date("\"%b %d %Y\"")
39+
local time_str = os.date("\"%H:%M:%S\"")
40+
3741
local standard_defines = {
3842
__STDC__ = 1,
3943
__STDC_VERSION__ = "201710L",
4044
__STDC_HOSTED__ = 1,
4145
__GNUC__ = 4,
4246
__GNUC_MINOR__ = 2,
4347
__GNUC_PATCHLEVEL__ = 1,
48+
__DATE__ = date_str,
49+
__TIME__ = time_str,
4450
}
4551

4652
for name, value in pairs(standard_defines) do
@@ -54,6 +60,7 @@ function META.New(tokens, code, config)
5460
self.conditional_stack = {} -- Track nested #if/#ifdef/#ifndef states
5561
self.position_stack = {} -- Track positions for directive token removal
5662
self.include_depth = 0 -- Track current include depth
63+
self.current_line = 1 -- Track current line number for __LINE__ macro
5764
-- Add predefined macros
5865
for name, value in pairs(config.defines) do
5966
if type(value) == "boolean" then
@@ -838,6 +845,49 @@ do -- #include directive
838845
return nil, "Include file not found: " .. filename
839846
end
840847

848+
function META:ReadError()
849+
if not (self:IsToken("#") and self:IsTokenValueOffset("error", 1)) then
850+
return false
851+
end
852+
853+
self:PushPosition()
854+
local hashtag = self:ExpectToken("#")
855+
local directive = self:ExpectTokenValue("error")
856+
local message_tokens = self:CaptureTokens()
857+
local message = self:ToString(message_tokens, false):gsub("^%s+", ""):gsub("%s+$", "")
858+
self:RemoveDirectiveTokens()
859+
error("#error: " .. message)
860+
end
861+
862+
function META:ReadWarning()
863+
if not (self:IsToken("#") and self:IsTokenValueOffset("warning", 1)) then
864+
return false
865+
end
866+
867+
self:PushPosition()
868+
local hashtag = self:ExpectToken("#")
869+
local directive = self:ExpectTokenValue("warning")
870+
local message_tokens = self:CaptureTokens()
871+
local message = self:ToString(message_tokens, false):gsub("^%s+", ""):gsub("%s+$", "")
872+
self:RemoveDirectiveTokens()
873+
print("#warning: " .. message)
874+
return true
875+
end
876+
877+
function META:ReadPragma()
878+
if not (self:IsToken("#") and self:IsTokenValueOffset("pragma", 1)) then
879+
return false
880+
end
881+
882+
self:PushPosition()
883+
local hashtag = self:ExpectToken("#")
884+
local directive = self:ExpectTokenValue("pragma")
885+
-- Capture and ignore the pragma content (implementation-specific)
886+
self:CaptureTokens()
887+
self:RemoveDirectiveTokens()
888+
return true
889+
end
890+
841891
function META:ReadInclude()
842892
if not (self:IsToken("#") and self:IsTokenValueOffset("include", 1)) then
843893
return false
@@ -1292,6 +1342,24 @@ function META:ExpandMacro()
12921342
return self:HandleVAOPT()
12931343
end
12941344

1345+
-- Special handling for __LINE__ and __FILE__
1346+
if tk.type == "letter" and tk:ValueEquals("__LINE__") then
1347+
local line_token = self:NewToken("number", tostring(self.current_line))
1348+
transfer_token_whitespace(tk:Copy(), {line_token}, false)
1349+
self:RemoveToken(self:GetPosition())
1350+
self:AddTokens({line_token})
1351+
return true
1352+
end
1353+
1354+
if tk.type == "letter" and tk:ValueEquals("__FILE__") then
1355+
local filename = (self.Code and self.Code:GetName()) or "unknown"
1356+
local file_token = self:NewToken("string", "\"" .. filename .. "\"")
1357+
transfer_token_whitespace(tk:Copy(), {file_token}, false)
1358+
self:RemoveToken(self:GetPosition())
1359+
self:AddTokens({file_token})
1360+
return true
1361+
end
1362+
12951363
local def = self:GetDefinition(nil, 0)
12961364

12971365
if not def then return false end
@@ -1364,9 +1432,22 @@ end
13641432

13651433
function META:NextToken()
13661434
if not self:GetDefinition(nil, 0) then
1435+
local prev_tk = self:GetToken()
13671436
self:Advance(1)
13681437
local tk = self:GetToken()
13691438

1439+
-- Update line counter when we see newlines in whitespace
1440+
if prev_tk:HasWhitespace() then
1441+
for _, ws in ipairs(prev_tk:GetWhitespace()) do
1442+
local ws_str = ws:GetValueString()
1443+
for i = 1, #ws_str do
1444+
if ws_str:sub(i, i) == "\n" then
1445+
self.current_line = self.current_line + 1
1446+
end
1447+
end
1448+
end
1449+
end
1450+
13701451
if tk.type == "end_of_file" then return false end
13711452

13721453
return true
@@ -1387,6 +1468,9 @@ function META:Parse()
13871468
self:ReadElif() or
13881469
self:ReadElse() or
13891470
self:ReadEndif() or
1471+
self:ReadError() or
1472+
self:ReadWarning() or
1473+
self:ReadPragma() or
13901474
self:ReadInclude() or
13911475
self:ExpandMacroCall() or
13921476
self:ExpandMacroConcatenation() or

test/tests/nattlua/c_declarations/preprocessor.lua

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,3 +412,55 @@ do -- conditional compilation
412412
test("#if !(5 > 10)\n>x=1<\n#endif", "x=1")
413413
test("#define A 5\n#define B 10\n#if A + B > 10\n>x=1<\n#endif", "x=1")
414414
end
415+
416+
do -- #pragma directive
417+
test("#pragma once\n>x=1<", "x=1")
418+
test("#pragma pack(push, 1)\n>x=1<", "x=1")
419+
test("#define X 42\n#pragma GCC diagnostic ignored \"-Wunused\"\n>X<", "42")
420+
end
421+
422+
do -- #error directive
423+
test_error("#error This is an error message", "#error: This is an error message")
424+
test_error("#define ERR 1\n#if ERR\n#error Compilation stopped\n#endif", "#error: Compilation stopped")
425+
test_error("#error", "#error: ")
426+
end
427+
428+
do -- #warning directive (warnings should not stop preprocessing)
429+
-- Note: warnings print to stdout but don't throw errors
430+
test("#warning This is a warning\n>x=1<", "x=1")
431+
end
432+
433+
do -- __DATE__ and __TIME__ macros
434+
-- These are dynamic, so we just test they expand to quoted strings
435+
local result = preprocess(">__DATE__<")
436+
assert(result:match(">\".-\"<"), "Expected __DATE__ to expand to a quoted string, got: " .. result)
437+
438+
result = preprocess(">__TIME__<")
439+
assert(result:match(">\".-\"<"), "Expected __TIME__ to expand to a quoted string, got: " .. result)
440+
441+
-- Test that DATE and TIME expand in macro definitions
442+
local date_value = preprocess("__DATE__")
443+
local result2 = preprocess("#define BUILD_DATE __DATE__\n>BUILD_DATE<")
444+
assert(result2:match("\""), "Expected BUILD_DATE to expand to a date string")
445+
end
446+
447+
do -- __LINE__ and __FILE__ macros
448+
-- __LINE__ uses a simple counter that increments on newlines
449+
test(">__LINE__<", "1")
450+
test("\n>__LINE__<", "2")
451+
test("\n\n>__LINE__<", "3")
452+
test("#define X __LINE__\n>X<", "2") -- __LINE__ expands at use time, line 2
453+
454+
-- __FILE__ expands to the current filename
455+
local result = preprocess(">__FILE__<")
456+
assert(result:match(">\".-\"<"), "Expected __FILE__ to expand to a quoted string, got: " .. result)
457+
458+
-- Should contain "cpreprocessor" (the default filename from preprocessor.lua)
459+
assert(result:match("cpreprocessor"), "Expected __FILE__ to contain 'cpreprocessor', got: " .. result)
460+
end
461+
462+
do -- combined predefined macros
463+
test(">__STDC__<", "1")
464+
test("#if __STDC__\n>x=1<\n#endif", "x=1")
465+
test("#if __GNUC__ >= 4\n>x=1<\n#endif", "x=1")
466+
end

0 commit comments

Comments
 (0)