WIP: LVS rerun feature

This commit is contained in:
Matthias Koefferlein 2019-08-25 21:55:48 +02:00
parent 515b68b76f
commit 441f946f43
16 changed files with 239 additions and 68 deletions

View File

@ -20,6 +20,9 @@ module DRC
cv = RBA::CellView::active cv = RBA::CellView::active
@generator = ""
@rdb_index = nil
@l2ndb_index = nil
@def_layout = cv && cv.layout @def_layout = cv && cv.layout
@def_cell = cv && cv.cell @def_cell = cv && cv.cell
@def_path = cv && cv.filename @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'") 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_cell = @output_rdb.create_cell(cn)
@output_rdb.generator = $0 @output_rdb.generator = self._generator
@output_rdb.top_cell_name = cn @output_rdb.top_cell_name = cn
@output_rdb.description = description @output_rdb.description = description
@ -1466,7 +1469,11 @@ CODE
# NOTE: to prevent the netter destroying the database, we need to take it # NOTE: to prevent the netter destroying the database, we need to take it
l2ndb = _take_data 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) view.show_l2ndb(l2ndb_index, view.active_cellview_index)
end end
@ -1551,6 +1558,30 @@ CODE
end end
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 private
def _make_string(v) def _make_string(v)
@ -1717,7 +1748,7 @@ CODE
@layout_sources[name] = src @layout_sources[name] = src
src src
end end
end end
end end

View File

@ -407,7 +407,7 @@ module DRC
@l2n = RBA::LayoutToNetlist::new(layout.top_cell.name, layout.dbu) @l2n = RBA::LayoutToNetlist::new(layout.top_cell.name, layout.dbu)
end end
@l2n.generator = $0 @l2n.generator = @engine._generator
end end

View File

@ -642,13 +642,15 @@ Class<tl::GlobPattern> decl_GlobPattern ("tl", "GlobPattern",
); );
class Recipe_Impl class Recipe_Impl
: public tl::Recipe : public tl::Recipe, public gsi::ObjectBase
{ {
public: public:
Recipe_Impl (const std::string &name, const std::string &description) Recipe_Impl (const std::string &name, const std::string &description)
: tl::Recipe (name, 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> &params) const virtual tl::Variant execute (const std::map<std::string, tl::Variant> &params) const
@ -688,9 +690,11 @@ Class<Recipe_Impl> decl_Recipe_Impl ("tl", "Recipe",
gsi::method ("description", &Recipe_Impl::description, gsi::method ("description", &Recipe_Impl::description,
"@brief Gets the description of the recipe." "@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" "@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"), gsi::method ("generator", &Recipe_Impl::generator, gsi::arg ("params"),
"@brief Delivers the generator string from the given parameters.\n" "@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" "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" "the implementation is supposed to deliver a new database.\n"
"\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" "instance. To serialize a recipe, use \"generator\", to execute the\n"
"recipe, use \"make\". \n" "recipe, use \"make\".\n"
"\n" "\n"
"Parameters are kept as a generic key/value map.\n" "Parameters are kept as a generic key/value map.\n"
"\n" "\n"

View File

@ -141,6 +141,9 @@
<iconset resource="../../lay/lay/layResources.qrc"> <iconset resource="../../lay/lay/layResources.qrc">
<normaloff>:/run.png</normaloff>:/run.png</iconset> <normaloff>:/run.png</normaloff>:/run.png</iconset>
</property> </property>
<property name="shortcut">
<string>F5</string>
</property>
<property name="autoRaise"> <property name="autoRaise">
<bool>true</bool> <bool>true</bool>
</property> </property>

View File

@ -218,11 +218,17 @@ static db::LayoutVsSchematic *get_lvsdb (lay::LayoutView *view, unsigned int ind
return dynamic_cast<db::LayoutVsSchematic *> (db); 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 // 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) 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" "\n"
"This method has been added in version 0.26." "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"), 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" "@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" "@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" "\n"
"This method has been added in version 0.26." "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"), 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" "@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" "@param name The name of the new netlist database\n"

View File

@ -7207,6 +7207,37 @@ LayoutView::add_l2ndb (db::LayoutToNetlist *l2ndb)
return (unsigned int)(m_l2ndbs.size () - 1); 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 * db::LayoutToNetlist *
LayoutView::get_l2ndb (int index) LayoutView::get_l2ndb (int index)
{ {

View File

@ -2340,6 +2340,17 @@ public:
*/ */
unsigned int add_l2ndb (db::LayoutToNetlist *l2ndb); 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 * @brief Get the netlist database by index
* *

View File

@ -30,9 +30,9 @@
#include "layMarker.h" #include "layMarker.h"
#include "layNetInfoDialog.h" #include "layNetInfoDialog.h"
#include "layNetExportDialog.h" #include "layNetExportDialog.h"
#include "lymMacro.h"
#include "tlProgress.h" #include "tlProgress.h"
#include "tlExceptions.h" #include "tlExceptions.h"
#include "tlRecipe.h"
#include "dbLayoutToNetlist.h" #include "dbLayoutToNetlist.h"
#include "dbNetlistDeviceClasses.h" #include "dbNetlistDeviceClasses.h"
#include "dbCellMapping.h" #include "dbCellMapping.h"
@ -600,13 +600,17 @@ NetlistBrowserPage::rerun_macro ()
{ {
if (! mp_database->generator ().empty ()) { if (! mp_database->generator ().empty ()) {
lym::Macro *generator = lym::MacroCollection::root ().find_macro (mp_database->generator ()); std::map<std::string, tl::Variant> add_pars;
if (! generator) {
throw tl::Exception (tl::sprintf (tl::to_string (tr ("Cannot find generator script: %s")), mp_database->generator())); for (unsigned int i = 0; i < mp_view->num_l2ndbs (); ++i) {
} else { if (mp_view->get_l2ndb (i) == mp_database.get ()) {
generator->run (); add_pars["l2ndb_index"] = tl::Variant (int (i));
break;
}
} }
tl::Recipe::make (mp_database->generator (), add_pars);
} }
} }
void void
@ -767,7 +771,11 @@ NetlistBrowserPage::set_db (db::LayoutToNetlist *l2ndb)
rerun_button->setEnabled (mp_database.get () && ! mp_database->generator ().empty ()); rerun_button->setEnabled (mp_database.get () && ! mp_database->generator ().empty ());
if (rerun_button->isEnabled ()) { 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 { } else {
rerun_button->setToolTip (QString ()); rerun_button->setToolTip (QString ());
} }

View File

@ -61,7 +61,7 @@ module LVS
@lvs = RBA::LayoutVsSchematic::new(cell.name, layout.dbu) @lvs = RBA::LayoutVsSchematic::new(cell.name, layout.dbu)
end end
@lvs.generator = $0 @lvs.generator = @engine._generator
@l2n = @lvs @l2n = @lvs
@comparer = RBA::NetlistComparer::new @comparer = RBA::NetlistComparer::new

View File

@ -17,37 +17,39 @@
<text> <text>
module LVS module LVS
def LVS.execute_lvs(_macro) def self.execute_lvs(macro, generator, l2ndb_index = nil)
_timer = RBA::Timer::new timer = RBA::Timer::new
_timer.start timer.start
_lvs = LVSEngine::new lvs = LVSEngine::new
lvs._l2ndb_index = l2ndb_index
lvs._generator = generator
begin begin
# Set a debugger scope so that our errors end up with the debugger set to the LVS's line # 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 # No verbosity set in lvs engine - we cannot use the engine's logger
RBA::Logger::verbosity &gt;= 10 &amp;&amp; RBA::Logger::info("Running #{_macro.path}") RBA::Logger::verbosity &gt;= 10 &amp;&amp; RBA::Logger::info("Running #{macro.path}")
_lvs.instance_eval(_macro.text, _macro.path) lvs.instance_eval(macro.text, macro.path)
# Remove the debugger scope # Remove the debugger scope
RBA::MacroExecutionContext::remove_debugger_scope RBA::MacroExecutionContext::remove_debugger_scope
rescue =&gt; ex rescue =&gt; ex
_lvs.error("In #{_macro.path}: #{ex.to_s}") lvs.error("In #{macro.path}: #{ex.to_s}")
RBA::MacroExecutionContext::ignore_next_exception RBA::MacroExecutionContext::ignore_next_exception
raise ex raise ex
ensure ensure
# cleans up and creates layout and report views # cleans up and creates layout and report views
_lvs._finish lvs._finish
end end
_timer.stop timer.stop
_lvs.info("Total run time: #{'%.3f'%(_timer.sys+_timer.user)}s") lvs.info("Total run time: #{'%.3f'%(timer.sys+timer.user)}s")
end end
@ -55,7 +57,9 @@ module LVS
class LVSInterpreter &lt; RBA::MacroInterpreter class LVSInterpreter &lt; RBA::MacroInterpreter
# Constructor # Constructor
def initialize def initialize(recipe)
@recipe = recipe
# Make the DSL use ruby syntax highlighting # Make the DSL use ruby syntax highlighting
self.syntax_scheme = "ruby" self.syntax_scheme = "ruby"
@ -80,8 +84,7 @@ module LVS
# Implements the execute method # Implements the execute method
def execute(macro) def execute(macro)
$0 = macro.path LVS::execute_lvs(macro, @recipe.generator("script" => macro.path))
LVS::execute_lvs(macro)
end end
end end
@ -90,7 +93,9 @@ module LVS
class LVSPlainTextInterpreter &lt; RBA::MacroInterpreter class LVSPlainTextInterpreter &lt; RBA::MacroInterpreter
# Constructor # Constructor
def initialize def initialize(recipe)
@recipe = recipe
# Make the DSL use ruby syntax highlighting # Make the DSL use ruby syntax highlighting
self.syntax_scheme = "ruby" self.syntax_scheme = "ruby"
@ -106,15 +111,41 @@ module LVS
# Implements the execute method # Implements the execute method
def execute(macro) def execute(macro)
LVS::execute_lvs(macro) LVS::execute_lvs(macro, @recipe.generator("script" => macro.path))
end end
end end
# A recipe implementation allowing the LVS run to be redone
class LVSRecipe &lt; 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 # Register the recipe
LVSInterpreter::new lvs_recipe = LVSRecipe::new
LVSPlainTextInterpreter::new
# Register the new interpreters
LVSInterpreter::new(lvs_recipe)
LVSPlainTextInterpreter::new(lvs_recipe)
end end
</text> </text>
</klayout-macro> </klayout-macro>

View File

@ -73,9 +73,8 @@ public:
}; };
Class<gsi::MacroExecutionContext> decl_MacroExecutionContext ("lay", "MacroExecutionContext", 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" "@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. " "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 " "Setting a scope is useful for implementing DSL interpreters and giving a proper hint about "
"the original location of an error." "the original location of an error."
@ -277,9 +276,8 @@ Class<gsi::MacroInterpreter> decl_MacroInterpreter ("lay", "MacroInterpreter",
gsi::method ("NoDebugger", &const_NoDebugger, gsi::method ("NoDebugger", &const_NoDebugger,
"@brief Indicates no debugging for \\debugger_scheme\n" "@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" "@brief Registers the macro interpreter\n"
"@args name\n"
"@param name The interpreter name. This is an arbitrary string which should be unique.\n" "@param name The interpreter name. This is an arbitrary string which should be unique.\n"
"\n" "\n"
"Registration of the interpreter makes the object known to the system. After registration, macros whose interpreter " "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 " "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" "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" "@brief Gets called to execute a macro\n"
"@args macro\n"
"This method must be reimplemented to execute the macro. " "This method must be reimplemented to execute the macro. "
"The system will call this script when a macro with interpreter type 'dsl' and the " "The system will call this script when a macro with interpreter type 'dsl' and the "
"name of this interpreter is run." "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" "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", Class<lym::Macro> decl_Macro ("lay", "Macro",
gsi::method ("path", &lym::Macro::path, gsi::method ("path", &lym::Macro::path,
"@brief Gets the path of the macro\n" "@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 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." "The path is used to identify the macro in the debugger for example."
) + ) +
gsi::method ("macro_by_path", &macro_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, gsi::method ("name", &lym::Macro::name,
"@brief Gets the name of the macro\n" "@brief Gets the name of the macro\n"
"\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 " "the description text can have the format \"Group;;Description\". In that case, the macro "
"will appear in a group with title \"Group\"." "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" "@brief Sets the description text\n"
"@args description\n"
"@param description The description text.\n" "@param description The description text.\n"
"See \\description for details.\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 " "The prolog is executed before the actual code is executed. Interpretation depends on the "
"implementation of the DSL interpreter for DSL macros." "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" "@brief Sets the prolog\n"
"@args string\n"
"See \\prolog for details.\n" "See \\prolog for details.\n"
) + ) +
gsi::method ("epilog", &lym::Macro::epilog, 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 " "The epilog is executed after the actual code is executed. Interpretation depends on the "
"implementation of the DSL interpreter for DSL macros." "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" "@brief Sets the epilog\n"
"@args string\n"
"See \\epilog for details.\n" "See \\epilog for details.\n"
) + ) +
gsi::method ("category", &lym::Macro::category, 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 " "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." "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" "@brief Sets the category tags string\n"
"@args string\n"
"See \\category for details.\n" "See \\category for details.\n"
) + ) +
gsi::method ("text", &lym::Macro::text, 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. " "The text is the code executed by the macro interpreter. "
"Depending on the DSL interpreter, the text can be any kind of code." "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" "@brief Sets the macro text\n"
"@args string\n"
"See \\text for details.\n" "See \\text for details.\n"
) + ) +
gsi::method ("show_in_menu?", &lym::Macro::show_in_menu, 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" "@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" "@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, gsi::method ("group_name", &lym::Macro::group_name,
"@brief Gets the menu group name\n" "@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 " "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." "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" "@brief Sets the menu group name\n"
"@args string\n"
"See \\group_name for details.\n" "See \\group_name for details.\n"
) + ) +
gsi::method ("menu_path", &lym::Macro::menu_path, 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 " "If a menu path is specified and \\show_in_menu? is true, the macro will appear in "
"the menu at the specified position." "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" "@brief Sets the menu path\n"
"@args string\n"
"See \\menu_path for details.\n" "See \\menu_path for details.\n"
), ),
"@brief A macro class\n" "@brief A macro class\n"

View File

@ -350,8 +350,14 @@ struct writer<gsi::VectorType>
aa->write<void *> ((void *)0); aa->write<void *> ((void *)0);
} }
} else { } 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); tl_assert (atype.inner () != 0);
aa->write<void *> ((void *)new RubyBasedVectorAdaptor (arg, atype.inner ())); aa->write<void *> ((void *)new RubyBasedVectorAdaptor (arg, atype.inner ()));
} }
} }
}; };
@ -370,10 +376,17 @@ struct writer<gsi::MapType>
} else { } else {
aa->write<void *> ((void *)0); aa->write<void *> ((void *)0);
} }
} else { } 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 () != 0);
tl_assert (atype.inner_k () != 0); tl_assert (atype.inner_k () != 0);
aa->write<void *> ((void *)new RubyBasedMapAdaptor (arg, atype.inner (), atype.inner_k ())); aa->write<void *> ((void *)new RubyBasedMapAdaptor (arg, atype.inner (), atype.inner_k ()));
} }
} }
}; };

View File

@ -51,7 +51,7 @@ std::string Recipe::generator (const std::map<std::string, tl::Variant> &params)
return g; 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 ()); 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)); 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; tl::Recipe *recipe_obj = 0;
for (tl::Registrar<tl::Recipe>::iterator r = tl::Registrar<tl::Recipe>::begin (); r != tl::Registrar<tl::Recipe>::end (); ++r) { for (tl::Registrar<tl::Recipe>::iterator r = tl::Registrar<tl::Recipe>::begin (); r != tl::Registrar<tl::Recipe>::end (); ++r) {
if (r->name () == recipe) { if (r->name () == recipe) {

View File

@ -104,8 +104,10 @@ public:
* @brief Executes the recipe from the generator * @brief Executes the recipe from the generator
* *
* Returns nil if the recipe can't be executed, e.g. because the recipe isn't known. * 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> &params = std::map<std::string, tl::Variant> ());
/** /**
* @brief Recipe interface: executes the recipe with the given parameters * @brief Recipe interface: executes the recipe with the given parameters

View File

@ -36,7 +36,8 @@ namespace {
{ {
int a = get_value (params, "A", 0); int a = get_value (params, "A", 0);
double b = get_value (params, "B", 0.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); tl::Variant res = tl::Recipe::make (g);
EXPECT_EQ (res.to_double (), 42.0); 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);
} }

View File

@ -265,13 +265,14 @@ class Tl_TestClass < TestBase
class MyRecipe < RBA::Recipe class MyRecipe < RBA::Recipe
def initialize def initialize
super("test_recipe", "description") super("rba_test_recipe", "description")
end end
def execute(params) def execute(params)
a = params["A"] || 0 a = params["A"] || 0
b = params["B"] || 0.0 b = params["B"] || 0.0
b * a c = params["C"] || 1.0
b * a * c
end end
end end
@ -285,12 +286,13 @@ class Tl_TestClass < TestBase
my_recipe = MyRecipe::new my_recipe = MyRecipe::new
my_recipe._create # makes debugging easier 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") assert_equal(my_recipe.description, "description")
g = my_recipe.generator("A" => 6, "B" => 7.0) 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).to_s, "42")
assert_equal("%g" % RBA::Recipe::make(g, "C" => 1.5).to_s, "63")
my_recipe._destroy my_recipe._destroy
my_recipe = nil my_recipe = nil