mirror of https://github.com/KLayout/klayout.git
WIP: LVS rerun feature
This commit is contained in:
parent
515b68b76f
commit
441f946f43
|
|
@ -20,6 +20,9 @@ module DRC
|
|||
|
||||
cv = RBA::CellView::active
|
||||
|
||||
@generator = ""
|
||||
@rdb_index = nil
|
||||
@l2ndb_index = nil
|
||||
@def_layout = cv && cv.layout
|
||||
@def_cell = cv && cv.cell
|
||||
@def_path = cv && cv.filename
|
||||
|
|
@ -780,7 +783,7 @@ module DRC
|
|||
cn || raise("No cell name specified - either the source was not specified before 'report' or there is no default source. In the latter case, specify a cell name as the third parameter of 'report'")
|
||||
|
||||
@output_rdb_cell = @output_rdb.create_cell(cn)
|
||||
@output_rdb.generator = $0
|
||||
@output_rdb.generator = self._generator
|
||||
@output_rdb.top_cell_name = cn
|
||||
@output_rdb.description = description
|
||||
|
||||
|
|
@ -1466,7 +1469,11 @@ CODE
|
|||
|
||||
# NOTE: to prevent the netter destroying the database, we need to take it
|
||||
l2ndb = _take_data
|
||||
l2ndb_index = view.add_l2ndb(l2ndb)
|
||||
if self._l2ndb_index
|
||||
l2ndb_index = view.replace_l2ndb(self._l2ndb_index, l2ndb)
|
||||
else
|
||||
l2ndb_index = view.add_l2ndb(l2ndb)
|
||||
end
|
||||
view.show_l2ndb(l2ndb_index, view.active_cellview_index)
|
||||
|
||||
end
|
||||
|
|
@ -1551,6 +1558,30 @@ CODE
|
|||
end
|
||||
end
|
||||
|
||||
def _generator
|
||||
@generator
|
||||
end
|
||||
|
||||
def _generator=(g)
|
||||
@generator = g
|
||||
end
|
||||
|
||||
def _rdb_index
|
||||
@rdb_index
|
||||
end
|
||||
|
||||
def _rdb_index=(i)
|
||||
@rdb_index = i
|
||||
end
|
||||
|
||||
def _l2ndb_index
|
||||
@l2ndb_index
|
||||
end
|
||||
|
||||
def _l2ndb_index=(i)
|
||||
@l2ndb_index = i
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def _make_string(v)
|
||||
|
|
@ -1717,7 +1748,7 @@ CODE
|
|||
@layout_sources[name] = src
|
||||
src
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -407,7 +407,7 @@ module DRC
|
|||
@l2n = RBA::LayoutToNetlist::new(layout.top_cell.name, layout.dbu)
|
||||
end
|
||||
|
||||
@l2n.generator = $0
|
||||
@l2n.generator = @engine._generator
|
||||
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -642,13 +642,15 @@ Class<tl::GlobPattern> decl_GlobPattern ("tl", "GlobPattern",
|
|||
);
|
||||
|
||||
class Recipe_Impl
|
||||
: public tl::Recipe
|
||||
: public tl::Recipe, public gsi::ObjectBase
|
||||
{
|
||||
public:
|
||||
Recipe_Impl (const std::string &name, const std::string &description)
|
||||
: tl::Recipe (name, description)
|
||||
{
|
||||
// .. nothing yet ..
|
||||
// makes the object owned by the C++ side (registrar). This way we don't need to keep a
|
||||
// singleton instance.
|
||||
keep ();
|
||||
}
|
||||
|
||||
virtual tl::Variant execute (const std::map<std::string, tl::Variant> ¶ms) const
|
||||
|
|
@ -688,9 +690,11 @@ Class<Recipe_Impl> decl_Recipe_Impl ("tl", "Recipe",
|
|||
gsi::method ("description", &Recipe_Impl::description,
|
||||
"@brief Gets the description of the recipe."
|
||||
) +
|
||||
gsi::method ("make", &Recipe_Impl::make, gsi::arg ("generator"),
|
||||
gsi::method ("make", &Recipe_Impl::make, gsi::arg ("generator"), gsi::arg ("add_params", std::map<std::string, tl::Variant> ()),
|
||||
"@brief Executes the recipe given by the generator string.\n"
|
||||
"The generator string is the one delivered with \\generator."
|
||||
"The generator string is the one delivered with \\generator.\n"
|
||||
"Additional parameters can be passed in \"add_params\". They have lower priority than the parameters "
|
||||
"kept inside the generator string."
|
||||
) +
|
||||
gsi::method ("generator", &Recipe_Impl::generator, gsi::arg ("params"),
|
||||
"@brief Delivers the generator string from the given parameters.\n"
|
||||
|
|
@ -711,9 +715,9 @@ Class<Recipe_Impl> decl_Recipe_Impl ("tl", "Recipe",
|
|||
"user requests a re-run of the DRC, the recipe will be called and \n"
|
||||
"the implementation is supposed to deliver a new database.\n"
|
||||
"\n"
|
||||
"To register a recipe, reimplement tl::Recipe and create a singleton\n"
|
||||
"To register a recipe, reimplement the Recipe class and create an\n"
|
||||
"instance. To serialize a recipe, use \"generator\", to execute the\n"
|
||||
"recipe, use \"make\". \n"
|
||||
"recipe, use \"make\".\n"
|
||||
"\n"
|
||||
"Parameters are kept as a generic key/value map.\n"
|
||||
"\n"
|
||||
|
|
|
|||
|
|
@ -141,6 +141,9 @@
|
|||
<iconset resource="../../lay/lay/layResources.qrc">
|
||||
<normaloff>:/run.png</normaloff>:/run.png</iconset>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F5</string>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -218,11 +218,17 @@ static db::LayoutVsSchematic *get_lvsdb (lay::LayoutView *view, unsigned int ind
|
|||
return dynamic_cast<db::LayoutVsSchematic *> (db);
|
||||
}
|
||||
|
||||
static void add_lvsdb (lay::LayoutView *view, db::LayoutVsSchematic *lvsdb)
|
||||
static unsigned int add_lvsdb (lay::LayoutView *view, db::LayoutVsSchematic *lvsdb)
|
||||
{
|
||||
view->add_l2ndb (lvsdb);
|
||||
return view->add_l2ndb (lvsdb);
|
||||
}
|
||||
|
||||
static unsigned int replace_lvsdb (lay::LayoutView *view, unsigned int db_index, db::LayoutVsSchematic *lvsdb)
|
||||
{
|
||||
return view->replace_l2ndb (db_index, lvsdb);
|
||||
}
|
||||
|
||||
|
||||
// this binding returns a const pointer which is not converted into a copy by RBA
|
||||
static lay::LayerPropertiesNodeRef insert_layer1 (lay::LayoutView *view, const lay::LayerPropertiesConstIterator &iter, const lay::LayerProperties &props)
|
||||
{
|
||||
|
|
@ -1499,6 +1505,15 @@ Class<lay::LayoutView> decl_LayoutView (QT_EXTERNAL_BASE (QWidget) "lay", "Layou
|
|||
"\n"
|
||||
"This method has been added in version 0.26."
|
||||
) +
|
||||
gsi::method ("replace_l2ndb", &lay::LayoutView::replace_l2ndb, gsi::arg ("db_index"), gsi::arg ("db"),
|
||||
"@brief Replaces the database with the given index\n"
|
||||
"\n"
|
||||
"If the index is not valid, the database will be added to the view (see \\add_lvsdb).\n"
|
||||
"\n"
|
||||
"@return The index of the database within the view (see \\lvsdb)\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.26."
|
||||
) +
|
||||
gsi::method_ext ("create_l2ndb", &create_l2ndb, gsi::arg ("name"),
|
||||
"@brief Creates a new netlist database and returns the index of the new database\n"
|
||||
"@param name The name of the new netlist database\n"
|
||||
|
|
@ -1532,6 +1547,15 @@ Class<lay::LayoutView> decl_LayoutView (QT_EXTERNAL_BASE (QWidget) "lay", "Layou
|
|||
"\n"
|
||||
"This method has been added in version 0.26."
|
||||
) +
|
||||
gsi::method_ext ("replace_lvsdb", &replace_lvsdb, gsi::arg ("db_index"), gsi::arg ("db"),
|
||||
"@brief Replaces the database with the given index\n"
|
||||
"\n"
|
||||
"If the index is not valid, the database will be added to the view (see \\add_lvsdb).\n"
|
||||
"\n"
|
||||
"@return The index of the database within the view (see \\lvsdb)\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.26."
|
||||
) +
|
||||
gsi::method_ext ("create_lvsdb", &create_lvsdb, gsi::arg ("name"),
|
||||
"@brief Creates a new netlist database and returns the index of the new database\n"
|
||||
"@param name The name of the new netlist database\n"
|
||||
|
|
|
|||
|
|
@ -7207,6 +7207,37 @@ LayoutView::add_l2ndb (db::LayoutToNetlist *l2ndb)
|
|||
return (unsigned int)(m_l2ndbs.size () - 1);
|
||||
}
|
||||
|
||||
unsigned int
|
||||
LayoutView::replace_l2ndb (unsigned int db_index, db::LayoutToNetlist *l2ndb)
|
||||
{
|
||||
if (db_index < m_l2ndbs.size ()) {
|
||||
|
||||
std::string n = m_l2ndbs [db_index]->name ();
|
||||
|
||||
delete m_l2ndbs [db_index];
|
||||
m_l2ndbs.erase (m_l2ndbs.begin () + db_index);
|
||||
|
||||
// keep old name if possible
|
||||
if (l2ndb->name ().empty ()) {
|
||||
l2ndb->set_name (n);
|
||||
} else {
|
||||
make_unique_name (l2ndb, m_l2ndbs.begin (), m_l2ndbs.end ());
|
||||
}
|
||||
|
||||
m_l2ndbs.insert (m_l2ndbs.begin () + db_index, l2ndb);
|
||||
|
||||
// Mark this object as owned by us (for GSI)
|
||||
l2ndb->keep ();
|
||||
|
||||
l2ndb_list_changed_event ();
|
||||
|
||||
return db_index;
|
||||
|
||||
} else {
|
||||
return add_l2ndb (l2ndb);
|
||||
}
|
||||
}
|
||||
|
||||
db::LayoutToNetlist *
|
||||
LayoutView::get_l2ndb (int index)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2340,6 +2340,17 @@ public:
|
|||
*/
|
||||
unsigned int add_l2ndb (db::LayoutToNetlist *l2ndb);
|
||||
|
||||
/**
|
||||
* @brief Replaces a Netlist database
|
||||
*
|
||||
* The layout view will become owner of the database.
|
||||
* If the index is not valid, the database will be added and the new index will be returned.
|
||||
*
|
||||
* @param db_index The index of the database to replace
|
||||
* @param l2ndb The database to add
|
||||
*/
|
||||
unsigned int replace_l2ndb (unsigned int db_index, db::LayoutToNetlist *l2ndb);
|
||||
|
||||
/**
|
||||
* @brief Get the netlist database by index
|
||||
*
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@
|
|||
#include "layMarker.h"
|
||||
#include "layNetInfoDialog.h"
|
||||
#include "layNetExportDialog.h"
|
||||
#include "lymMacro.h"
|
||||
#include "tlProgress.h"
|
||||
#include "tlExceptions.h"
|
||||
#include "tlRecipe.h"
|
||||
#include "dbLayoutToNetlist.h"
|
||||
#include "dbNetlistDeviceClasses.h"
|
||||
#include "dbCellMapping.h"
|
||||
|
|
@ -600,13 +600,17 @@ NetlistBrowserPage::rerun_macro ()
|
|||
{
|
||||
if (! mp_database->generator ().empty ()) {
|
||||
|
||||
lym::Macro *generator = lym::MacroCollection::root ().find_macro (mp_database->generator ());
|
||||
if (! generator) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot find generator script: %s")), mp_database->generator()));
|
||||
} else {
|
||||
generator->run ();
|
||||
std::map<std::string, tl::Variant> add_pars;
|
||||
|
||||
for (unsigned int i = 0; i < mp_view->num_l2ndbs (); ++i) {
|
||||
if (mp_view->get_l2ndb (i) == mp_database.get ()) {
|
||||
add_pars["l2ndb_index"] = tl::Variant (int (i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tl::Recipe::make (mp_database->generator (), add_pars);
|
||||
|
||||
}
|
||||
}
|
||||
void
|
||||
|
|
@ -767,7 +771,11 @@ NetlistBrowserPage::set_db (db::LayoutToNetlist *l2ndb)
|
|||
|
||||
rerun_button->setEnabled (mp_database.get () && ! mp_database->generator ().empty ());
|
||||
if (rerun_button->isEnabled ()) {
|
||||
rerun_button->setToolTip (tl::to_qstring (tl::to_string (tr ("Run ")) + mp_database->generator ()));
|
||||
QString shortcut;
|
||||
if (! rerun_button->shortcut ().isEmpty ()) {
|
||||
shortcut = QString::fromUtf8 (" (%1)").arg (rerun_button->shortcut ().toString ());
|
||||
}
|
||||
rerun_button->setToolTip (tl::to_qstring (tl::to_string (tr ("Run ")) + mp_database->generator ()) + shortcut);
|
||||
} else {
|
||||
rerun_button->setToolTip (QString ());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ module LVS
|
|||
@lvs = RBA::LayoutVsSchematic::new(cell.name, layout.dbu)
|
||||
end
|
||||
|
||||
@lvs.generator = $0
|
||||
@lvs.generator = @engine._generator
|
||||
|
||||
@l2n = @lvs
|
||||
@comparer = RBA::NetlistComparer::new
|
||||
|
|
|
|||
|
|
@ -17,37 +17,39 @@
|
|||
<text>
|
||||
module LVS
|
||||
|
||||
def LVS.execute_lvs(_macro)
|
||||
def self.execute_lvs(macro, generator, l2ndb_index = nil)
|
||||
|
||||
_timer = RBA::Timer::new
|
||||
_timer.start
|
||||
_lvs = LVSEngine::new
|
||||
timer = RBA::Timer::new
|
||||
timer.start
|
||||
lvs = LVSEngine::new
|
||||
lvs._l2ndb_index = l2ndb_index
|
||||
lvs._generator = generator
|
||||
|
||||
begin
|
||||
|
||||
# Set a debugger scope so that our errors end up with the debugger set to the LVS's line
|
||||
RBA::MacroExecutionContext::set_debugger_scope(_macro.path)
|
||||
RBA::MacroExecutionContext::set_debugger_scope(macro.path)
|
||||
# No verbosity set in lvs engine - we cannot use the engine's logger
|
||||
RBA::Logger::verbosity >= 10 && RBA::Logger::info("Running #{_macro.path}")
|
||||
_lvs.instance_eval(_macro.text, _macro.path)
|
||||
RBA::Logger::verbosity >= 10 && RBA::Logger::info("Running #{macro.path}")
|
||||
lvs.instance_eval(macro.text, macro.path)
|
||||
# Remove the debugger scope
|
||||
RBA::MacroExecutionContext::remove_debugger_scope
|
||||
|
||||
rescue => ex
|
||||
|
||||
_lvs.error("In #{_macro.path}: #{ex.to_s}")
|
||||
lvs.error("In #{macro.path}: #{ex.to_s}")
|
||||
RBA::MacroExecutionContext::ignore_next_exception
|
||||
raise ex
|
||||
|
||||
ensure
|
||||
|
||||
# cleans up and creates layout and report views
|
||||
_lvs._finish
|
||||
lvs._finish
|
||||
|
||||
end
|
||||
|
||||
_timer.stop
|
||||
_lvs.info("Total run time: #{'%.3f'%(_timer.sys+_timer.user)}s")
|
||||
timer.stop
|
||||
lvs.info("Total run time: #{'%.3f'%(timer.sys+timer.user)}s")
|
||||
|
||||
end
|
||||
|
||||
|
|
@ -55,7 +57,9 @@ module LVS
|
|||
class LVSInterpreter < RBA::MacroInterpreter
|
||||
|
||||
# Constructor
|
||||
def initialize
|
||||
def initialize(recipe)
|
||||
|
||||
@recipe = recipe
|
||||
|
||||
# Make the DSL use ruby syntax highlighting
|
||||
self.syntax_scheme = "ruby"
|
||||
|
|
@ -80,8 +84,7 @@ module LVS
|
|||
|
||||
# Implements the execute method
|
||||
def execute(macro)
|
||||
$0 = macro.path
|
||||
LVS::execute_lvs(macro)
|
||||
LVS::execute_lvs(macro, @recipe.generator("script" => macro.path))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -90,7 +93,9 @@ module LVS
|
|||
class LVSPlainTextInterpreter < RBA::MacroInterpreter
|
||||
|
||||
# Constructor
|
||||
def initialize
|
||||
def initialize(recipe)
|
||||
|
||||
@recipe = recipe
|
||||
|
||||
# Make the DSL use ruby syntax highlighting
|
||||
self.syntax_scheme = "ruby"
|
||||
|
|
@ -106,15 +111,41 @@ module LVS
|
|||
|
||||
# Implements the execute method
|
||||
def execute(macro)
|
||||
LVS::execute_lvs(macro)
|
||||
LVS::execute_lvs(macro, @recipe.generator("script" => macro.path))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# A recipe implementation allowing the LVS run to be redone
|
||||
class LVSRecipe < RBA::Recipe
|
||||
|
||||
def initialize
|
||||
super("lvs", "LVS recipe")
|
||||
end
|
||||
|
||||
def execute(params)
|
||||
|
||||
script = params["script"]
|
||||
if ! script
|
||||
return
|
||||
end
|
||||
|
||||
macro = RBA::Macro::macro_by_path(script)
|
||||
macro || raise("Can't find LVS script #{script} - unable to re-run")
|
||||
|
||||
LVS::execute_lvs(macro, self.generator("script" => script), params["l2ndb_index"])
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Register the new interpreters
|
||||
LVSInterpreter::new
|
||||
LVSPlainTextInterpreter::new
|
||||
# Register the recipe
|
||||
lvs_recipe = LVSRecipe::new
|
||||
|
||||
# Register the new interpreters
|
||||
LVSInterpreter::new(lvs_recipe)
|
||||
LVSPlainTextInterpreter::new(lvs_recipe)
|
||||
|
||||
end
|
||||
</text>
|
||||
</klayout-macro>
|
||||
|
|
|
|||
|
|
@ -73,9 +73,8 @@ public:
|
|||
};
|
||||
|
||||
Class<gsi::MacroExecutionContext> decl_MacroExecutionContext ("lay", "MacroExecutionContext",
|
||||
gsi::method ("set_debugger_scope", &gsi::MacroExecutionContext::set_debugger_scope,
|
||||
gsi::method ("set_debugger_scope", &gsi::MacroExecutionContext::set_debugger_scope, gsi::arg ("filename"),
|
||||
"@brief Sets a debugger scope (file level which shall appear in the debugger)\n"
|
||||
"@args filename\n"
|
||||
"If a debugger scope is set, back traces will be produced starting from that scope. "
|
||||
"Setting a scope is useful for implementing DSL interpreters and giving a proper hint about "
|
||||
"the original location of an error."
|
||||
|
|
@ -277,9 +276,8 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
|
|||
gsi::method ("NoDebugger", &const_NoDebugger,
|
||||
"@brief Indicates no debugging for \\debugger_scheme\n"
|
||||
) +
|
||||
gsi::method ("register", &MacroInterpreter::register_gsi,
|
||||
gsi::method ("register", &MacroInterpreter::register_gsi, gsi::arg ("name"),
|
||||
"@brief Registers the macro interpreter\n"
|
||||
"@args name\n"
|
||||
"@param name The interpreter name. This is an arbitrary string which should be unique.\n"
|
||||
"\n"
|
||||
"Registration of the interpreter makes the object known to the system. After registration, macros whose interpreter "
|
||||
|
|
@ -351,9 +349,8 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
|
|||
"Before version 0.25 this attribute was a reimplementable method. It has been turned into an attribute for "
|
||||
"performance reasons in version 0.25.\n"
|
||||
) +
|
||||
gsi::callback ("execute", &gsi::MacroInterpreter::execute, &gsi::MacroInterpreter::f_execute,
|
||||
gsi::callback ("execute", &gsi::MacroInterpreter::execute, &gsi::MacroInterpreter::f_execute, gsi::arg ("macro"),
|
||||
"@brief Gets called to execute a macro\n"
|
||||
"@args macro\n"
|
||||
"This method must be reimplemented to execute the macro. "
|
||||
"The system will call this script when a macro with interpreter type 'dsl' and the "
|
||||
"name of this interpreter is run."
|
||||
|
|
@ -424,6 +421,11 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
|
|||
"This class has been introduced in version 0.23.\n"
|
||||
);
|
||||
|
||||
static lym::Macro *macro_by_path (const std::string &path)
|
||||
{
|
||||
return lym::MacroCollection::root ().find_macro (path);
|
||||
}
|
||||
|
||||
Class<lym::Macro> decl_Macro ("lay", "Macro",
|
||||
gsi::method ("path", &lym::Macro::path,
|
||||
"@brief Gets the path of the macro\n"
|
||||
|
|
@ -431,6 +433,13 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
"The path is the path where the macro is stored, starting with an abstract group identifier. "
|
||||
"The path is used to identify the macro in the debugger for example."
|
||||
) +
|
||||
gsi::method ("macro_by_path", ¯o_by_path, gsi::arg ("path"),
|
||||
"@brief Finds the macro by installation path\n"
|
||||
"\n"
|
||||
"Returns nil if no macro with this path can be found.\n"
|
||||
"\n"
|
||||
"This method has been added in version 0.26."
|
||||
) +
|
||||
gsi::method ("name", &lym::Macro::name,
|
||||
"@brief Gets the name of the macro\n"
|
||||
"\n"
|
||||
|
|
@ -443,9 +452,8 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
"the description text can have the format \"Group;;Description\". In that case, the macro "
|
||||
"will appear in a group with title \"Group\"."
|
||||
) +
|
||||
gsi::method ("description=", &lym::Macro::set_description,
|
||||
gsi::method ("description=", &lym::Macro::set_description, gsi::arg ("description"),
|
||||
"@brief Sets the description text\n"
|
||||
"@args description\n"
|
||||
"@param description The description text.\n"
|
||||
"See \\description for details.\n"
|
||||
) +
|
||||
|
|
@ -455,9 +463,8 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
"The prolog is executed before the actual code is executed. Interpretation depends on the "
|
||||
"implementation of the DSL interpreter for DSL macros."
|
||||
) +
|
||||
gsi::method ("prolog=", &lym::Macro::set_prolog,
|
||||
gsi::method ("prolog=", &lym::Macro::set_prolog, gsi::arg ("string"),
|
||||
"@brief Sets the prolog\n"
|
||||
"@args string\n"
|
||||
"See \\prolog for details.\n"
|
||||
) +
|
||||
gsi::method ("epilog", &lym::Macro::epilog,
|
||||
|
|
@ -466,9 +473,8 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
"The epilog is executed after the actual code is executed. Interpretation depends on the "
|
||||
"implementation of the DSL interpreter for DSL macros."
|
||||
) +
|
||||
gsi::method ("epilog=", &lym::Macro::set_epilog,
|
||||
gsi::method ("epilog=", &lym::Macro::set_epilog, gsi::arg ("string"),
|
||||
"@brief Sets the epilog\n"
|
||||
"@args string\n"
|
||||
"See \\epilog for details.\n"
|
||||
) +
|
||||
gsi::method ("category", &lym::Macro::category,
|
||||
|
|
@ -477,9 +483,8 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
"The category tags string indicates to which categories a macro will belong to. This string "
|
||||
"is only used for templates currently and is a comma-separated list of category names."
|
||||
) +
|
||||
gsi::method ("category=", &lym::Macro::set_category,
|
||||
gsi::method ("category=", &lym::Macro::set_category, gsi::arg ("string"),
|
||||
"@brief Sets the category tags string\n"
|
||||
"@args string\n"
|
||||
"See \\category for details.\n"
|
||||
) +
|
||||
gsi::method ("text", &lym::Macro::text,
|
||||
|
|
@ -488,17 +493,15 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
"The text is the code executed by the macro interpreter. "
|
||||
"Depending on the DSL interpreter, the text can be any kind of code."
|
||||
) +
|
||||
gsi::method ("text=", &lym::Macro::set_text,
|
||||
gsi::method ("text=", &lym::Macro::set_text, gsi::arg ("string"),
|
||||
"@brief Sets the macro text\n"
|
||||
"@args string\n"
|
||||
"See \\text for details.\n"
|
||||
) +
|
||||
gsi::method ("show_in_menu?", &lym::Macro::show_in_menu,
|
||||
"@brief Gets a value indicating whether the macro shall be shown in the menu\n"
|
||||
) +
|
||||
gsi::method ("show_in_menu=", &lym::Macro::set_show_in_menu,
|
||||
gsi::method ("show_in_menu=", &lym::Macro::set_show_in_menu, gsi::arg ("flag"),
|
||||
"@brief Sets a value indicating whether the macro shall be shown in the menu\n"
|
||||
"@args flag\n"
|
||||
) +
|
||||
gsi::method ("group_name", &lym::Macro::group_name,
|
||||
"@brief Gets the menu group name\n"
|
||||
|
|
@ -506,9 +509,8 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
"If a group name is specified and \\show_in_menu? is true, the macro will appear in "
|
||||
"a separate group (separated by a separator) together with other macros sharing the same group."
|
||||
) +
|
||||
gsi::method ("group_name=", &lym::Macro::set_group_name,
|
||||
gsi::method ("group_name=", &lym::Macro::set_group_name, gsi::arg ("string"),
|
||||
"@brief Sets the menu group name\n"
|
||||
"@args string\n"
|
||||
"See \\group_name for details.\n"
|
||||
) +
|
||||
gsi::method ("menu_path", &lym::Macro::menu_path,
|
||||
|
|
@ -517,9 +519,8 @@ Class<lym::Macro> decl_Macro ("lay", "Macro",
|
|||
"If a menu path is specified and \\show_in_menu? is true, the macro will appear in "
|
||||
"the menu at the specified position."
|
||||
) +
|
||||
gsi::method ("menu_path=", &lym::Macro::set_menu_path,
|
||||
gsi::method ("menu_path=", &lym::Macro::set_menu_path, gsi::arg ("string"),
|
||||
"@brief Sets the menu path\n"
|
||||
"@args string\n"
|
||||
"See \\menu_path for details.\n"
|
||||
),
|
||||
"@brief A macro class\n"
|
||||
|
|
|
|||
|
|
@ -350,8 +350,14 @@ struct writer<gsi::VectorType>
|
|||
aa->write<void *> ((void *)0);
|
||||
}
|
||||
} else {
|
||||
|
||||
if (TYPE (arg) != T_ARRAY) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected array, got %s)")), rba_class_name (arg).c_str ()));
|
||||
}
|
||||
|
||||
tl_assert (atype.inner () != 0);
|
||||
aa->write<void *> ((void *)new RubyBasedVectorAdaptor (arg, atype.inner ()));
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -370,10 +376,17 @@ struct writer<gsi::MapType>
|
|||
} else {
|
||||
aa->write<void *> ((void *)0);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (TYPE (arg) != T_HASH) {
|
||||
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Unexpected object type (expected hash, got %s)")), rba_class_name (arg).c_str ()));
|
||||
}
|
||||
|
||||
tl_assert (atype.inner () != 0);
|
||||
tl_assert (atype.inner_k () != 0);
|
||||
aa->write<void *> ((void *)new RubyBasedMapAdaptor (arg, atype.inner (), atype.inner_k ()));
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ std::string Recipe::generator (const std::map<std::string, tl::Variant> ¶ms)
|
|||
return g;
|
||||
}
|
||||
|
||||
tl::Variant Recipe::make (const std::string &generator)
|
||||
tl::Variant Recipe::make (const std::string &generator, const std::map<std::string, tl::Variant> &padd)
|
||||
{
|
||||
tl::Extractor ex (generator.c_str ());
|
||||
|
||||
|
|
@ -70,6 +70,10 @@ tl::Variant Recipe::make (const std::string &generator)
|
|||
params.insert (std::make_pair (key, v));
|
||||
}
|
||||
|
||||
for (std::map<std::string, tl::Variant>::const_iterator p = padd.begin (); p != padd.end (); ++p) {
|
||||
params.insert (*p);
|
||||
}
|
||||
|
||||
tl::Recipe *recipe_obj = 0;
|
||||
for (tl::Registrar<tl::Recipe>::iterator r = tl::Registrar<tl::Recipe>::begin (); r != tl::Registrar<tl::Recipe>::end (); ++r) {
|
||||
if (r->name () == recipe) {
|
||||
|
|
|
|||
|
|
@ -104,8 +104,10 @@ public:
|
|||
* @brief Executes the recipe from the generator
|
||||
*
|
||||
* Returns nil if the recipe can't be executed, e.g. because the recipe isn't known.
|
||||
* Additional parameters can be passed in the second argument.
|
||||
* They have lower priority than the parameters kept in the generator argument.
|
||||
*/
|
||||
static tl::Variant make (const std::string &generator);
|
||||
static tl::Variant make (const std::string &generator, const std::map<std::string, tl::Variant> ¶ms = std::map<std::string, tl::Variant> ());
|
||||
|
||||
/**
|
||||
* @brief Recipe interface: executes the recipe with the given parameters
|
||||
|
|
|
|||
|
|
@ -36,7 +36,8 @@ namespace {
|
|||
{
|
||||
int a = get_value (params, "A", 0);
|
||||
double b = get_value (params, "B", 0.0);
|
||||
return tl::Variant (b * a);
|
||||
double c = get_value (params, "C", 1.0);
|
||||
return tl::Variant (b * a * c);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -55,4 +56,9 @@ TEST(1)
|
|||
|
||||
tl::Variant res = tl::Recipe::make (g);
|
||||
EXPECT_EQ (res.to_double (), 42.0);
|
||||
|
||||
std::map<std::string, tl::Variant> padd;
|
||||
padd["C"] = tl::Variant(1.5);
|
||||
res = tl::Recipe::make (g, padd);
|
||||
EXPECT_EQ (res.to_double (), 63.0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,13 +265,14 @@ class Tl_TestClass < TestBase
|
|||
class MyRecipe < RBA::Recipe
|
||||
|
||||
def initialize
|
||||
super("test_recipe", "description")
|
||||
super("rba_test_recipe", "description")
|
||||
end
|
||||
|
||||
def execute(params)
|
||||
a = params["A"] || 0
|
||||
b = params["B"] || 0.0
|
||||
b * a
|
||||
c = params["C"] || 1.0
|
||||
b * a * c
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -285,12 +286,13 @@ class Tl_TestClass < TestBase
|
|||
my_recipe = MyRecipe::new
|
||||
my_recipe._create # makes debugging easier
|
||||
|
||||
assert_equal(my_recipe.name, "test_recipe")
|
||||
assert_equal(my_recipe.name, "rba_test_recipe")
|
||||
assert_equal(my_recipe.description, "description")
|
||||
|
||||
g = my_recipe.generator("A" => 6, "B" => 7.0)
|
||||
assert_equal(g, "test_recipe: A=#6,B=##7")
|
||||
assert_equal(g, "rba_test_recipe: A=#6,B=##7")
|
||||
assert_equal("%g" % RBA::Recipe::make(g).to_s, "42")
|
||||
assert_equal("%g" % RBA::Recipe::make(g, "C" => 1.5).to_s, "63")
|
||||
|
||||
my_recipe._destroy
|
||||
my_recipe = nil
|
||||
|
|
|
|||
Loading…
Reference in New Issue