diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb index ab1373625..5cd9fec55 100644 --- a/src/drc/drc/built-in-macros/_drc_engine.rb +++ b/src/drc/drc/built-in-macros/_drc_engine.rb @@ -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 diff --git a/src/drc/drc/built-in-macros/_drc_netter.rb b/src/drc/drc/built-in-macros/_drc_netter.rb index 0763d6e54..a449ba5b1 100644 --- a/src/drc/drc/built-in-macros/_drc_netter.rb +++ b/src/drc/drc/built-in-macros/_drc_netter.rb @@ -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 diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index 27f98a315..2874f388f 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -642,13 +642,15 @@ Class 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 ¶ms) const @@ -688,9 +690,11 @@ Class 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 ()), "@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 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" diff --git a/src/laybasic/laybasic/NetlistBrowserPage.ui b/src/laybasic/laybasic/NetlistBrowserPage.ui index e341345cd..19d7438c1 100644 --- a/src/laybasic/laybasic/NetlistBrowserPage.ui +++ b/src/laybasic/laybasic/NetlistBrowserPage.ui @@ -141,6 +141,9 @@ :/run.png:/run.png + + F5 + true diff --git a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc index 129ca2851..9dc052808 100644 --- a/src/laybasic/laybasic/gsiDeclLayLayoutView.cc +++ b/src/laybasic/laybasic/gsiDeclLayLayoutView.cc @@ -218,11 +218,17 @@ static db::LayoutVsSchematic *get_lvsdb (lay::LayoutView *view, unsigned int ind return dynamic_cast (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 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 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" diff --git a/src/laybasic/laybasic/layLayoutView.cc b/src/laybasic/laybasic/layLayoutView.cc index 3930c678c..3a2b0dbf2 100644 --- a/src/laybasic/laybasic/layLayoutView.cc +++ b/src/laybasic/laybasic/layLayoutView.cc @@ -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) { diff --git a/src/laybasic/laybasic/layLayoutView.h b/src/laybasic/laybasic/layLayoutView.h index cf5cbc8b9..548dd499d 100644 --- a/src/laybasic/laybasic/layLayoutView.h +++ b/src/laybasic/laybasic/layLayoutView.h @@ -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 * diff --git a/src/laybasic/laybasic/layNetlistBrowserPage.cc b/src/laybasic/laybasic/layNetlistBrowserPage.cc index c078a9e38..22d8e06db 100644 --- a/src/laybasic/laybasic/layNetlistBrowserPage.cc +++ b/src/laybasic/laybasic/layNetlistBrowserPage.cc @@ -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 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 ()); } diff --git a/src/lvs/lvs/built-in-macros/_lvs_netter.rb b/src/lvs/lvs/built-in-macros/_lvs_netter.rb index a0b0b2cf8..9649cb49e 100644 --- a/src/lvs/lvs/built-in-macros/_lvs_netter.rb +++ b/src/lvs/lvs/built-in-macros/_lvs_netter.rb @@ -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 diff --git a/src/lvs/lvs/built-in-macros/lvs_interpreters.lym b/src/lvs/lvs/built-in-macros/lvs_interpreters.lym index b72af993b..5c6e36eca 100644 --- a/src/lvs/lvs/built-in-macros/lvs_interpreters.lym +++ b/src/lvs/lvs/built-in-macros/lvs_interpreters.lym @@ -17,37 +17,39 @@ 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 diff --git a/src/lym/lym/gsiDeclLymMacro.cc b/src/lym/lym/gsiDeclLymMacro.cc index bf2bd56b7..6416bba2a 100644 --- a/src/lym/lym/gsiDeclLymMacro.cc +++ b/src/lym/lym/gsiDeclLymMacro.cc @@ -73,9 +73,8 @@ public: }; Class 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 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 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 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 decl_Macro ("lay", "Macro", gsi::method ("path", &lym::Macro::path, "@brief Gets the path of the macro\n" @@ -431,6 +433,13 @@ Class 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 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 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 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 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 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 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 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" diff --git a/src/rba/rba/rbaMarshal.cc b/src/rba/rba/rbaMarshal.cc index 7b0b0d9ad..df03434fe 100644 --- a/src/rba/rba/rbaMarshal.cc +++ b/src/rba/rba/rbaMarshal.cc @@ -350,8 +350,14 @@ struct writer aa->write ((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 *)new RubyBasedVectorAdaptor (arg, atype.inner ())); + } } }; @@ -370,10 +376,17 @@ struct writer } else { aa->write ((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 *)new RubyBasedMapAdaptor (arg, atype.inner (), atype.inner_k ())); + } } }; diff --git a/src/tl/tl/tlRecipe.cc b/src/tl/tl/tlRecipe.cc index 3d02bb8ac..157280a32 100644 --- a/src/tl/tl/tlRecipe.cc +++ b/src/tl/tl/tlRecipe.cc @@ -51,7 +51,7 @@ std::string Recipe::generator (const std::map ¶ms) return g; } -tl::Variant Recipe::make (const std::string &generator) +tl::Variant Recipe::make (const std::string &generator, const std::map &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::const_iterator p = padd.begin (); p != padd.end (); ++p) { + params.insert (*p); + } + tl::Recipe *recipe_obj = 0; for (tl::Registrar::iterator r = tl::Registrar::begin (); r != tl::Registrar::end (); ++r) { if (r->name () == recipe) { diff --git a/src/tl/tl/tlRecipe.h b/src/tl/tl/tlRecipe.h index 700df550e..900b48ed1 100644 --- a/src/tl/tl/tlRecipe.h +++ b/src/tl/tl/tlRecipe.h @@ -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 ¶ms = std::map ()); /** * @brief Recipe interface: executes the recipe with the given parameters diff --git a/src/tl/unit_tests/tlRecipeTests.cc b/src/tl/unit_tests/tlRecipeTests.cc index bf16eba78..21bbc3f50 100644 --- a/src/tl/unit_tests/tlRecipeTests.cc +++ b/src/tl/unit_tests/tlRecipeTests.cc @@ -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 padd; + padd["C"] = tl::Variant(1.5); + res = tl::Recipe::make (g, padd); + EXPECT_EQ (res.to_double (), 63.0); } diff --git a/testdata/ruby/tlTest.rb b/testdata/ruby/tlTest.rb index e0369a353..bb55b9ea5 100644 --- a/testdata/ruby/tlTest.rb +++ b/testdata/ruby/tlTest.rb @@ -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