Did you ever had a problem that you simply couldn't solve? I had one such problem 8 months ago, I wanted some way to skip the somewhat tedious log4net logging approach:
if (logger.IsDebugEnabled)
logger.Debug("message with "+ "expansive" +" " + "string concentration");
This is the recommended approach from the log4net documentation, and it makes sense, this way, if you don't enable logging, you don't pay for any string concentration. But it's very tedious to do so over and over again. I really wanted a clean way to do it, but at the time Boo's macros were far beyond my capabilities. Even for such a simple task.
Fast forward to the present....
Now I have no difficulities writing this, in fact, the entire utility was developed in one session in Notepad (the way real programmers do it :-) ). What is interesting is that in the meantime I had no education in compilers, AST, etc. I just came back to the subject and it suddenly made sense. It happened to me before in many situations, but this is the first that I can actually find a documented proof of that.
I remember in high school I was unabled to grasp the idea of dynamic memory (on my defense, I didn't try very hard), that was on Pascal with ^ going all over the place. Six months later, I was a triple star programmer (and proud of it :-) ) in C. Again, nothing much has changed, except maybe that this time I was more willing to learn.
Anyway, here is the code, less than 60 lines of code, and I found it useful in reducing common repetitive statements.
| |
""" |
This macros allows a convenient way to log properly, without getting tired by writing logger.IsXXXEnabled all the time. The macros depend on a ILog field/local named logger being present, this you've to define yourself. |
""" |
namespace log4net |
|
import System |
import Boo.Lang.Compiler |
import Boo.Lang.Compiler.Ast |
|
class LogdbgMacro(AbstractAstMacro): |
""" |
Logs a debug message if debug messages logging are enabled. |
Usage: logdbg "message" [exception] |
""" |
override def Expand(macro as MacroStatement): |
return GenerateStatement("Debug", macro) |
|
class LogwarnMacro(AbstractAstMacro): |
""" |
Logs a warning message if warning messages are enabled enabled. |
Usage: logwarn "warning" [exception] |
""" |
override def Expand(macro as MacroStatement): |
return GenerateStatement("Warn", macro) |
|
class LogerrorMacro(AbstractAstMacro): |
""" |
Logs an error message if error logging is enabled |
Usage: logerror "error" [exception] |
""" |
override def Expand(macro as MacroStatement): |
return GenerateStatement("Error", macro) |
|
class LogfatalMacro(AbstractAstMacro): |
""" |
Logs a fatal error message if fatal error logging is enabled |
Usage: logfatal "fatal error" [exception] |
""" |
override def Expand(macro as MacroStatement): |
return GenerateStatement("Fatal", macro) |
|
class LogInfoMacro(AbstractAstMacro): |
""" |
Logs an informative message if informative messages logging is enabled. |
Usage: loginfo "didja know?" [exception] |
""" |
override def Expand(macro as MacroStatement): |
return GenerateStatement("Info", macro) |
|
internal def GenerateStatement(logOption as string, macro as MacroStatement): |
if len(macro.Arguments) < 1 or len(macro.Arguments) > 2 : |
raise CompilerError(macro.LexicalInfo, "The log can be called with either one or two parameters only!") |
isEnabledProp = MethodInvocationExpression( Target: AstUtil.CreateReferenceExpression("logger.get_Is${logOption}Enabled") ) |
ifEnabled = IfStatement(Condition: isEnabledProp, TrueBlock: Block() ) |
mie = MethodInvocationExpression(Target: AstUtil.CreateReferenceExpression("logger.${logOption}") ) |
mie.Arguments.Add(macro.Arguments[0]) |
mie.Arguments.Add(macro.Arguments[1]) if len(macro.Arguments)==2 |
ifEnabled.TrueBlock.Add(mie) return ifEnabled |