alternatives to C __FILE__ and friends
The C Preprocessor has special macros that expand to file names and source locations, for the ease of logging and debugging output. Nelua doesn't offer these macros, but every AST node comes with locational information, so the same outcome can be achieved.
- log1.nelua - a log macro using its parent's location
- log2.nelua - an
<alwayspoly>function reusing compiler error formatting - log3.nelua - a macro accepting a format string and runtime args
- log4.nelua -
srclocis too immediate for logging macros - log5.nelua - the alternative to
__func__ - log6.nelua - using the C Preprocessor instead?
log1.nelua - a log macro using its parent's location
## local function log(msg)
## local loc = context.scope.parent.node:location()
print(#[string.format('[%s:%d %s] ', loc.srcname, loc.lineno, loc.colno) .. msg]#)
## end
#[log]#('printing f')
print 'f'
log!'printed f (alt syntax)'
## log 'wrong syntax (always line 1!)'
Output:
[log1.nelua:6 8] printing f f [log1.nelua:8 5] printed f (alt syntax) [log1.nelua:1 1] wrong syntax (always line 1!)
Note that loc.colno isn't 1 as you might expect. Try running this example with a much longer name for the macro.
log2.nelua - an <alwayspoly> function reusing compiler error formatting
local function trace() <alwayspoly>
## local locmsg = context.state.inpolyeval.srcnode:format_message("tracing", "this")
local msg = #[locmsg]#
print(msg)
end
local function f()
trace()
end
f()Output (which can include ANSI color codes):
log2.nelua:8:8: tracing: this
trace()
^~
log3.nelua - a macro accepting a format string and runtime args
require 'string'
## local function log(msg, ...)
## local loc = context.scope.parent.node:location()
local msg = #[string.format('[%s:%d %s] ', loc.srcname, loc.lineno, loc.colno) .. msg]#
local msg <close> = string.format(msg, #[...]#)
print(msg)
## end
log!'printing f'
print 'f'
local s = 'f'
log!('printed %s', s)Output:
[log3.nelua:10 5] printing f f [log3.nelua:13 5] printed f
log4.nelua - srcloc is too immediate for logging macros
Consider:
print(#[srcloc.lineno]#) -- 1 print(#[srcloc.lineno]#) -- 2 print(#[srcloc.lineno]#) -- 3 ## local function f() print(#[srcloc.lineno]#) ## end ## f() -- 5 ## f() -- 5 ## f() -- 5
fprintf (stderr, "Internal error: "
"negative string length "
"%d at %s, line %d.",
length, __FILE__, __LINE__);In Nelua that'd look like
require 'string'
local notstring = @record{
data: cstring,
size: isize,
}
local function show(s: notstring)
if s.size < 0 then
panic(string.format(#['Internal error: '
.. 'negative string length '
.. '%d at %s, line %d.']#,
s.size, #[srcloc.srcname]#, #[srcloc.lineno]#))
end
print((@string){s.data, s.size})
end
local s: cstring = "hello world"
show({s, 5})
show({s, -5})Output:
hello Internal error: negative string length -5 at likegcc.nelua, line 12. Aborted
log5.nelua - the alternative to __func__
require 'string'
## local function log(msg, ...)
## local loc = context.scope.parent.node:location()
## local func = context.scope.parent.node.attr.name
local msg = #[string.format('[%s.%s:%d] ', loc.srcname, func, loc.lineno) .. msg]#
local msg <close> = string.format(msg, #[...]#)
print(msg)
## end
local function f1()
## log("hello, %s", 'nelua')
end
local function f2(n: integer)
## log('got: %d', n)
end
f1()
f2(3)Output:
[log5.nelua.f1:11] hello, nelua [log5.nelua.f2:15] got: 3
Note, the 'log5.nelua' in that output becomes the full path when this file is required instead of directly run.
log6.nelua - using the C Preprocessor instead?
local __FILE__: cstring <const, cimport, nodecl>
local __LINE__: cint <const, cimport, nodecl>
local __DATE__: cstring <const, cimport, nodecl>
require 'string'
## local function log(msg, ...)
local msg <close> = '[%s:%d @ %s] ' .. #[msg]#
local msg <close> = string.format(msg, __FILE__, __LINE__, __DATE__, #[...]#)
print(msg)
## end
## log 'printing f'
print 'f'
local s = 'f'
## log('printed %s', s)Usage:
$ nelua --cache-dir=. log6.nelua [./log6.c:2594 @ Nov 12 2023] printing f f [./log6.c:2598 @ Nov 12 2023] printed f