diff --git a/src/lym/lym/gsiDeclLymMacro.cc b/src/lym/lym/gsiDeclLymMacro.cc index a08d2c7f5..617a337a7 100644 --- a/src/lym/lym/gsiDeclLymMacro.cc +++ b/src/lym/lym/gsiDeclLymMacro.cc @@ -455,6 +455,24 @@ static lym::Macro *macro_by_path (const std::string &path) return lym::MacroCollection::root ().find_macro (path); } +static std::string real_path (const std::string &path, int line) +{ + if (! path.empty () && path[0] == '@') { + return tl::IncludeExpander::from_string (path).translate_to_original (line).first; + } else { + return path; + } +} + +static int real_line (const std::string &path, int line) +{ + if (! path.empty () && path[0] == '@') { + return tl::IncludeExpander::from_string (path).translate_to_original (line).second; + } else { + return line; + } +} + Class decl_Macro ("lay", "Macro", gsi::method ("path", &lym::Macro::path, "@brief Gets the path of the macro\n" @@ -551,6 +569,46 @@ Class decl_Macro ("lay", "Macro", gsi::method ("menu_path=", &lym::Macro::set_menu_path, gsi::arg ("string"), "@brief Sets the menu path\n" "See \\menu_path for details.\n" + ) + + gsi::method ("real_path", &real_path, + "@brief Gets the real path for an include-encoded path and line number\n" + "\n" + "When using KLayout's include scheme based on '# %include ...', __FILE__ and __LINE__ (Ruby) or __file__ and __line__ (Python) will " + "not have the proper values but encoded file names. This method allows retrieving the real file by using\n" + "\n" + "@code\n" + "# Ruby\n" + "real_file = RBA::Macro::real_path(__FILE__, __LINE__)\n" + "\n" + "# Python\n" + "real_file = pya::Macro::real_path(__file__, __line__)\n" + "@/code\n" + "\n" + "This substitution is not required for top-level macros as KLayout's interpreter will automatically use this " + "function instead of __FILE__ or __file__. Call this function when you need __FILE__ or __file__ from files " + "included through the languages mechanisms such as 'import', 'require' or 'load' where this substitution does not happen.\n" + "\n" + "This feature has been introduced in version 0.27." + ) + + gsi::method ("real_line", &real_line, + "@brief Gets the real line number for an include-encoded path and line number\n" + "\n" + "When using KLayout's include scheme based on '# %include ...', __FILE__ and __LINE__ (Ruby) or __file__ and __line__ (Python) will " + "not have the proper values but encoded file names. This method allows retrieving the real line number by using\n" + "\n" + "@code\n" + "# Ruby\n" + "real_line = RBA::Macro::real_line(__FILE__, __LINE__)\n" + "\n" + "# Python\n" + "real_line = pya::Macro::real_line(__file__, __line__)\n" + "@/code\n" + "\n" + "This substitution is not required for top-level macros as KLayout's interpreter will automatically use this " + "function instead of __LINE__ or __line__. Call this function when you need __LINE__ or __line__ from files " + "included through the languages mechanisms such as 'import', 'require' or 'load' where this substitution does not happen.\n" + "\n" + "This feature has been introduced in version 0.27." ), "@brief A macro class\n" "\n" diff --git a/src/lym/lym/lymMacro.cc b/src/lym/lym/lymMacro.cc index 888bc7445..b7a30f726 100644 --- a/src/lym/lym/lymMacro.cc +++ b/src/lym/lym/lymMacro.cc @@ -1025,13 +1025,14 @@ int Macro::run () const gsi::Interpreter *ip = script_interpreter (interpreter ()); if (ip) { + static lym::MacroInterpreter def_interpreter; + if (! prolog ().empty ()) { ip->eval_string (prolog ().c_str ()); } - std::string expanded_text; - tl::IncludeExpander ie = tl::IncludeExpander::expand (path (), text (), expanded_text); - ip->eval_string (expanded_text.c_str (), ie.to_string ().c_str (), 1); + std::pair ep = def_interpreter.include_expansion (this); + ip->eval_string (ep.second.c_str (), ep.first.c_str (), 1); if (! epilog ().empty ()) { ip->eval_string (epilog ().c_str ()); diff --git a/src/lym/lym/lymMacroInterpreter.cc b/src/lym/lym/lymMacroInterpreter.cc index 3e778bf0c..995eaa0a1 100644 --- a/src/lym/lym/lymMacroInterpreter.cc +++ b/src/lym/lym/lymMacroInterpreter.cc @@ -54,6 +54,32 @@ MacroInterpreter::include_expansion (const lym::Macro *macro) { std::pair res; res.first = tl::IncludeExpander::expand (macro->path (), macro->text (), res.second).to_string (); + + if (res.first != macro->path ()) { + + // Fix the macro's text such that include expansion does not spoil __FILE__ or __LINE__ variables + // NOTE: this will modify the column for syntax errors. Let's hope this tiny error is acceptable. + // TODO: this substitution may be somewhat naive ... + + Macro::Interpreter ip = macro->interpreter (); + if (macro->interpreter () == Macro::DSLInterpreter) { + if (syntax_scheme () == "ruby") { + ip = Macro::Ruby; + } else if (syntax_scheme () == "python") { + ip = Macro::Python; + } + } + + if (ip == Macro::Ruby) { + res.second = tl::replaced (res.second, "__FILE__", "RBA::Macro::real_path(__FILE__, __LINE__)"); + res.second = tl::replaced (res.second, "__LINE__", "RBA::Macro::real_line(__FILE__, __LINE__)"); + } else if (ip == Macro::Python) { + res.second = tl::replaced (res.second, "__file__", "pya.Macro.real_path(__file__, __line__)"); + res.second = tl::replaced (res.second, "__line__", "pya.Macro.real_line(__file__, __line__)"); + } + + } + return res; } diff --git a/src/rba/rba/rbaUtils.cc b/src/rba/rba/rbaUtils.cc index 5bdc35432..29f63d63e 100644 --- a/src/rba/rba/rbaUtils.cc +++ b/src/rba/rba/rbaUtils.cc @@ -26,6 +26,7 @@ #include "rba.h" #include "rbaUtils.h" #include "rbaInternal.h" +#include "tlInclude.h" #if HAVE_RUBY_VERSION_CODE >= 20200 # include