mirror of https://github.com/KLayout/klayout.git
Added tests for include feature, some bug fixes and enhancements.
This commit is contained in:
parent
a69d65daa3
commit
a01eb70891
|
|
@ -124,7 +124,7 @@ public:
|
|||
std::pair<std::string, std::string> include_expansion (const lym::Macro *macro)
|
||||
{
|
||||
if (m_supports_include_expansion) {
|
||||
return MacroInterpreter::include_expansion (macro);
|
||||
return lym::MacroInterpreter::include_expansion (macro);
|
||||
} else {
|
||||
return std::pair<std::string, std::string> (macro->path (), macro->text ());
|
||||
}
|
||||
|
|
@ -573,27 +573,32 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
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 "
|
||||
"When using KLayout's include scheme based on '# %include ...', __FILE__ and __LINE__ (Ruby) 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"
|
||||
"function instead of __FILE__. Call this function when you need __FILE__ from files "
|
||||
"included through the languages mechanisms such as 'require' or 'load' where this substitution does not happen.\n"
|
||||
"\n"
|
||||
"For Python there is no equivalent for __LINE__, so you always have to use:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"# Python"
|
||||
"import inspect\n"
|
||||
"real_file = pya.Macro.real_path(__file__, inspect.currentframe().f_back.f_lineno)\n"
|
||||
"@/code\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 "
|
||||
"When using KLayout's include scheme based on '# %include ...', __FILE__ and __LINE__ (Ruby) will "
|
||||
"not have the proper values but encoded file names. This method allows retrieving the real line number by using\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
|
|
@ -605,8 +610,16 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
"@/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"
|
||||
"function instead of __FILE__. Call this function when you need __FILE__ from files "
|
||||
"included through the languages mechanisms such as 'require' or 'load' where this substitution does not happen.\n"
|
||||
"\n"
|
||||
"For Python there is no equivalent for __LINE__, so you always have to use:\n"
|
||||
"\n"
|
||||
"@code\n"
|
||||
"# Python"
|
||||
"import inspect\n"
|
||||
"real_line = pya.Macro.real_line(__file__, inspect.currentframe().f_back.f_lineno)\n"
|
||||
"@/code\n"
|
||||
"\n"
|
||||
"This feature has been introduced in version 0.27."
|
||||
),
|
||||
|
|
|
|||
|
|
@ -71,11 +71,25 @@ MacroInterpreter::include_expansion (const lym::Macro *macro)
|
|||
}
|
||||
|
||||
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__)");
|
||||
|
||||
std::string subst;
|
||||
const std::string file_const ("__FILE__");
|
||||
const std::string line_const ("__LINE__");
|
||||
|
||||
for (const char *cp = res.second.c_str (); *cp; ) {
|
||||
if (strncmp (cp, file_const.c_str (), file_const.size ()) == 0 && !isalnum (cp[file_const.size ()]) && cp[file_const.size ()] != '_') {
|
||||
subst += "RBA::Macro::real_path(__FILE__, __LINE__)";
|
||||
cp += file_const.size ();
|
||||
} else if (strncmp (cp, line_const.c_str (), line_const.size ()) == 0 && !isalnum (cp[line_const.size ()]) && cp[line_const.size ()] != '_') {
|
||||
subst += "RBA::Macro::real_line(__FILE__, __LINE__)";
|
||||
cp += line_const.size ();
|
||||
} else {
|
||||
subst += *cp++;
|
||||
}
|
||||
}
|
||||
|
||||
res.second = subst;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,179 @@
|
|||
|
||||
#include "tlUnitTest.h"
|
||||
|
||||
TEST(1)
|
||||
#include "lymMacro.h"
|
||||
#include "lymMacroInterpreter.h"
|
||||
#include "gsiInterpreter.h"
|
||||
#include "rba.h"
|
||||
#include "pya.h"
|
||||
|
||||
class TestCollectorConsole
|
||||
: public gsi::Console
|
||||
{
|
||||
// TODO: add tests for lym specific things
|
||||
throw tl::CancelException (); // skip this test to indicate that there is nothing yet
|
||||
public:
|
||||
TestCollectorConsole () { }
|
||||
~TestCollectorConsole () { }
|
||||
|
||||
virtual void write_str (const char *text, output_stream)
|
||||
{
|
||||
m_text += text;
|
||||
}
|
||||
|
||||
virtual void flush () { }
|
||||
virtual bool is_tty () { return false; }
|
||||
virtual int columns () { return 80; }
|
||||
virtual int rows () { return 50; }
|
||||
|
||||
const std::string &text () const { return m_text; }
|
||||
|
||||
private:
|
||||
std::string m_text;
|
||||
};
|
||||
|
||||
#if defined(HAVE_RUBY)
|
||||
|
||||
TEST(1_BasicRuby)
|
||||
{
|
||||
tl_assert (rba::RubyInterpreter::instance () != 0);
|
||||
|
||||
lym::Macro macro;
|
||||
|
||||
macro.set_file_path (tl::testsrc () + "/testdata/lym/m1.rb");
|
||||
macro.set_interpreter (lym::Macro::Ruby);
|
||||
macro.load ();
|
||||
|
||||
TestCollectorConsole console;
|
||||
rba::RubyInterpreter::instance ()->push_console (&console);
|
||||
try {
|
||||
EXPECT_EQ (macro.run (), 0);
|
||||
rba::RubyInterpreter::instance ()->remove_console (&console);
|
||||
} catch (...) {
|
||||
rba::RubyInterpreter::instance ()->remove_console (&console);
|
||||
throw;
|
||||
}
|
||||
|
||||
EXPECT_EQ (console.text (), "Hello, world!\n");
|
||||
}
|
||||
|
||||
TEST(2_RubyInclude)
|
||||
{
|
||||
tl_assert (rba::RubyInterpreter::instance () != 0);
|
||||
|
||||
lym::Macro macro;
|
||||
|
||||
macro.set_file_path (tl::testsrc () + "/testdata/lym/m2.rb");
|
||||
macro.set_interpreter (lym::Macro::Ruby);
|
||||
macro.load ();
|
||||
|
||||
TestCollectorConsole console;
|
||||
rba::RubyInterpreter::instance ()->push_console (&console);
|
||||
try {
|
||||
EXPECT_EQ (macro.run (), 0);
|
||||
rba::RubyInterpreter::instance ()->remove_console (&console);
|
||||
} catch (...) {
|
||||
rba::RubyInterpreter::instance ()->remove_console (&console);
|
||||
throw;
|
||||
}
|
||||
|
||||
EXPECT_EQ (console.text (), "Stop 1: m2.rb:2\nf: a_inc.rb:3\nStop 2: m2.rb:8\n");
|
||||
}
|
||||
|
||||
TEST(11_DRCBasic)
|
||||
{
|
||||
tl_assert (rba::RubyInterpreter::instance () != 0);
|
||||
|
||||
lym::Macro macro;
|
||||
|
||||
macro.set_file_path (tl::testsrc () + "/testdata/lym/m1.drc");
|
||||
macro.set_interpreter (lym::Macro::DSLInterpreter);
|
||||
macro.set_dsl_interpreter ("drc");
|
||||
macro.load ();
|
||||
|
||||
TestCollectorConsole console;
|
||||
rba::RubyInterpreter::instance ()->push_console (&console);
|
||||
try {
|
||||
EXPECT_EQ (macro.run (), 0);
|
||||
rba::RubyInterpreter::instance ()->remove_console (&console);
|
||||
} catch (...) {
|
||||
rba::RubyInterpreter::instance ()->remove_console (&console);
|
||||
throw;
|
||||
}
|
||||
|
||||
EXPECT_EQ (console.text (), "Result: (500,500;500,2000;1000,2000;1000,500) in m1.drc:20\n");
|
||||
}
|
||||
|
||||
TEST(12_DRCBasic)
|
||||
{
|
||||
tl_assert (rba::RubyInterpreter::instance () != 0);
|
||||
|
||||
lym::Macro macro;
|
||||
|
||||
macro.set_file_path (tl::testsrc () + "/testdata/lym/m2.drc");
|
||||
macro.set_interpreter (lym::Macro::DSLInterpreter);
|
||||
macro.set_dsl_interpreter ("drc");
|
||||
macro.load ();
|
||||
|
||||
TestCollectorConsole console;
|
||||
rba::RubyInterpreter::instance ()->push_console (&console);
|
||||
try {
|
||||
EXPECT_EQ (macro.run (), 0);
|
||||
rba::RubyInterpreter::instance ()->remove_console (&console);
|
||||
} catch (...) {
|
||||
rba::RubyInterpreter::instance ()->remove_console (&console);
|
||||
throw;
|
||||
}
|
||||
|
||||
EXPECT_EQ (console.text (), "Result: (500,500;500,2000;1000,2000;1000,500) in m2.drc:14\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PYTHON)
|
||||
|
||||
TEST(101_BasicPython)
|
||||
{
|
||||
tl_assert (pya::PythonInterpreter::instance () != 0);
|
||||
|
||||
lym::Macro macro;
|
||||
|
||||
macro.set_file_path (tl::testsrc () + "/testdata/lym/m1.py");
|
||||
macro.set_interpreter (lym::Macro::Python);
|
||||
macro.load ();
|
||||
|
||||
TestCollectorConsole console;
|
||||
pya::PythonInterpreter::instance ()->push_console (&console);
|
||||
try {
|
||||
EXPECT_EQ (macro.run (), 0);
|
||||
pya::PythonInterpreter::instance ()->remove_console (&console);
|
||||
} catch (...) {
|
||||
pya::PythonInterpreter::instance ()->remove_console (&console);
|
||||
throw;
|
||||
}
|
||||
|
||||
EXPECT_EQ (console.text (), "Hello, world!\n");
|
||||
}
|
||||
|
||||
TEST(102_PythonInclude)
|
||||
{
|
||||
tl_assert (pya::PythonInterpreter::instance () != 0);
|
||||
|
||||
lym::Macro macro;
|
||||
|
||||
macro.set_file_path (tl::testsrc () + "/testdata/lym/m2.py");
|
||||
macro.set_interpreter (lym::Macro::Python);
|
||||
macro.load ();
|
||||
|
||||
TestCollectorConsole console;
|
||||
pya::PythonInterpreter::instance ()->push_console (&console);
|
||||
try {
|
||||
EXPECT_EQ (macro.run (), 0);
|
||||
pya::PythonInterpreter::instance ()->remove_console (&console);
|
||||
} catch (...) {
|
||||
pya::PythonInterpreter::instance ()->remove_console (&console);
|
||||
throw;
|
||||
}
|
||||
|
||||
EXPECT_EQ (console.text (), "Stop 1: m2.py:8\nf: a_inc.py:5\nStop 2: m2.py:14\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ include($$PWD/../../lib_ut.pri)
|
|||
SOURCES = \
|
||||
lymBasicTests.cc
|
||||
|
||||
INCLUDEPATH += $$LYM_INC $$TL_INC $$GSI_INC
|
||||
DEPENDPATH += $$LYM_INC $$TL_INC $$GSI_INC
|
||||
INCLUDEPATH += $$RBA_INC $$PYA_INC $$LYM_INC $$TL_INC $$GSI_INC
|
||||
DEPENDPATH += $$RBA_INC $$PYA_INC $$LYM_INC $$TL_INC $$GSI_INC
|
||||
|
||||
LIBS += -L$$DESTDIR_UT -lklayout_lym -lklayout_tl -lklayout_gsi
|
||||
LIBS += -L$$DESTDIR_UT -lklayout_rba -lklayout_pya -lklayout_lym -lklayout_tl -lklayout_gsi
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,13 @@ IncludeExpander::read (const std::string &path, tl::InputStream &is, std::string
|
|||
tl::Extractor ex (l.c_str ());
|
||||
if (ex.test ("#") && ex.test ("%include")) {
|
||||
|
||||
std::string include_path = tl::trim (ex.skip ());
|
||||
std::string include_path;
|
||||
if (*ex.skip () == '"' || *ex.skip () == '\'') {
|
||||
ex.read_quoted (include_path);
|
||||
ex.expect_end ();
|
||||
} else {
|
||||
include_path = tl::trim (ex.skip ());
|
||||
}
|
||||
|
||||
// allow some customization by employing expression interpolation
|
||||
include_path = tl::Eval ().interpolate (include_path);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
# some prep steps
|
||||
ly = RBA::Layout::new
|
||||
ly.create_cell("TOP")
|
||||
l1 = ly.layer(1, 0)
|
||||
ly.top_cell.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 2000))
|
||||
l2 = ly.layer(2, 0)
|
||||
ly.top_cell.shapes(l2).insert(RBA::Box::new(500, 500, 1500, 2500))
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
import os
|
||||
|
||||
def f():
|
||||
return("f: " + os.path.basename(pya.Macro.real_path(__file__, lineno())) + ":" + str(pya.Macro.real_line(__file__, lineno())))
|
||||
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
|
||||
def f
|
||||
return "f: " + File.basename(__FILE__) + ":" + __LINE__.to_s
|
||||
end
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
# some prep steps
|
||||
ly = RBA::Layout::new
|
||||
ly.create_cell("TOP")
|
||||
l1 = ly.layer(1, 0)
|
||||
ly.top_cell.shapes(l1).insert(RBA::Box::new(0, 0, 1000, 2000))
|
||||
l2 = ly.layer(2, 0)
|
||||
ly.top_cell.shapes(l2).insert(RBA::Box::new(500, 500, 1500, 2500))
|
||||
|
||||
# actual "DRC"
|
||||
|
||||
source(ly.top_cell)
|
||||
|
||||
l1_drc = input(1, 0)
|
||||
l2_drc = input(2, 0)
|
||||
(l1_drc & l2_drc).output(100, 0)
|
||||
|
||||
l100 = ly.layer(100, 0)
|
||||
|
||||
puts "Result: " + RBA::Region::new(ly.top_cell.begin_shapes_rec(l100)).to_s + " in " + File.basename(__FILE__) + ":" + __LINE__.to_s
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
print("Hello, world!")
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
puts "Hello, world!"
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
# %include "a_inc.drc"
|
||||
|
||||
# actual "DRC"
|
||||
|
||||
source(ly.top_cell)
|
||||
|
||||
l1_drc = input(1, 0)
|
||||
l2_drc = input(2, 0)
|
||||
(l1_drc & l2_drc).output(100, 0)
|
||||
|
||||
l100 = ly.layer(100, 0)
|
||||
|
||||
puts "Result: " + RBA::Region::new(ly.top_cell.begin_shapes_rec(l100)).to_s + " in " + File.basename(__FILE__) + ":" + __LINE__.to_s
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
|
||||
import os
|
||||
import inspect
|
||||
|
||||
def lineno():
|
||||
return inspect.currentframe().f_back.f_lineno
|
||||
|
||||
print("Stop 1: " + os.path.basename(pya.Macro.real_path(__file__, lineno())) + ":" + str(pya.Macro.real_line(__file__, lineno())))
|
||||
|
||||
# %include a_inc.py
|
||||
|
||||
print(f())
|
||||
|
||||
print("Stop 2: " + os.path.basename(pya.Macro.real_path(__file__, lineno())) + ":" + str(pya.Macro.real_line(__file__, lineno())))
|
||||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
puts "Stop 1: " + File.basename(__FILE__) + ":" + __LINE__.to_s
|
||||
|
||||
# %include a_inc.rb
|
||||
|
||||
puts f
|
||||
|
||||
puts "Stop 2: " + File.basename(__FILE__) + ":" + __LINE__.to_s
|
||||
|
||||
Loading…
Reference in New Issue