From 67ed068e76cac4112773617aa3c05cdaff46604f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Nov 2021 01:53:14 +0100 Subject: [PATCH 1/6] Provide more methods for the Macro class. --- src/lym/lym/gsiDeclLymMacro.cc | 213 +++++++++++++++++++++++++++------ 1 file changed, 177 insertions(+), 36 deletions(-) diff --git a/src/lym/lym/gsiDeclLymMacro.cc b/src/lym/lym/gsiDeclLymMacro.cc index 0b81454a4..2ab526368 100644 --- a/src/lym/lym/gsiDeclLymMacro.cc +++ b/src/lym/lym/gsiDeclLymMacro.cc @@ -24,6 +24,7 @@ #include "gsiDecl.h" #include "gsiDeclBasic.h" #include "gsiInterpreter.h" +#include "gsiEnums.h" #include "lymMacroInterpreter.h" #include "lymMacro.h" #include "rba.h" @@ -161,9 +162,9 @@ public: return m_supports_include_expansion; } - void set_storage_scheme (int scheme) + void set_storage_scheme (lym::Macro::Format scheme) { - m_storage_scheme = lym::Macro::Format (scheme); + m_storage_scheme = scheme; } virtual lym::Macro::Format storage_scheme () const @@ -171,9 +172,9 @@ public: return m_storage_scheme; } - void set_debugger_scheme (int scheme) + void set_debugger_scheme (lym::Macro::Interpreter scheme) { - m_debugger_scheme = lym::Macro::Interpreter (scheme); + m_debugger_scheme = scheme; } virtual lym::Macro::Interpreter debugger_scheme () const @@ -256,43 +257,51 @@ private: bool m_supports_include_expansion; }; -int const_PlainTextFormat () +gsi::EnumIn decl_FormatEnum ("lay", "MacroFormat", + gsi::enum_const ("PlainTextFormat", lym::Macro::PlainTextFormat, + "@brief The macro has plain text format" + ) + + gsi::enum_const ("PlainTextWithHashAnnotationsFormat", lym::Macro::PlainTextWithHashAnnotationsFormat, + "@brief The macro has plain text format with special pseudo-comment annotations" + ) + + gsi::enum_const ("MacroFormat", lym::Macro::MacroFormat, + "@brief The macro has macro (XML) format" + ), + "@brief Specifies the format of a macro\n" + "This enum has been introduced in version 0.27.5." +); + +gsi::EnumIn decl_InterpreterEnum ("lay", "MacroInterpreter", + gsi::enum_const ("Ruby", lym::Macro::Ruby, + "@brief The interpreter is Ruby" + ) + + gsi::enum_const ("Python", lym::Macro::Python, + "@brief The interpreter is Python" + ) + + gsi::enum_const ("Text", lym::Macro::Text, + "@brief Plain text" + ) + + gsi::enum_const ("DSLInterpreter", lym::Macro::DSLInterpreter, + "@brief A domain-specific interpreter (DSL)" + ) + + gsi::enum_const ("None", lym::Macro::None, + "@brief No specific interpreter" + ), + "@brief Specifies the interpreter used for executing a macro\n" + "This enum has been introduced in version 0.27.5." +); + +lym::Macro::Interpreter const_RubyDebugger () { - return int (lym::Macro::PlainTextFormat); + return lym::Macro::Ruby; } -int const_PlainTextWithHashAnnotationsFormat () +lym::Macro::Interpreter const_NoDebugger () { - return int (lym::Macro::PlainTextWithHashAnnotationsFormat); -} - -int const_MacroFormat () -{ - return int (lym::Macro::MacroFormat); -} - -int const_RubyDebugger () -{ - return int (lym::Macro::Ruby); -} - -int const_NoDebugger () -{ - return int (lym::Macro::None); + return lym::Macro::None; } Class decl_MacroInterpreter ("lay", "MacroInterpreter", - gsi::method ("PlainTextFormat", &const_PlainTextFormat, - "@brief Indicates plain text format for \\storage_scheme\n" - ) + - gsi::method ("PlainTextWithHashAnnotationsFormat", &const_PlainTextWithHashAnnotationsFormat, - "@brief Indicates plain text format for \\storage_scheme\n" - "This format is identical to \\PlainTextFormat but indicates that it is possible " - "to insert annotations (properties) into the text in a hash-commented header." - ) + - gsi::method ("MacroFormat", &const_MacroFormat, - "@brief Indicates macro (XML) format for \\storage_scheme\n" - ) + gsi::method ("RubyDebugger", &const_RubyDebugger, "@brief Indicates Ruby debugger for \\debugger_scheme\n" ) + @@ -469,6 +478,9 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", "This class has been introduced in version 0.23 and modified in 0.27.\n" ); +// Inject the Macro::Format declarations into MacroInterpreter: +gsi::ClassExt inject_Format_in_parent (decl_FormatEnum.defs ()); + static lym::Macro *macro_by_path (const std::string &path) { return lym::MacroCollection::root ().find_macro (path); @@ -492,7 +504,132 @@ static int real_line (const std::string &path, int line) } } +lym::Macro *new_from_path (const std::string &path) +{ + std::unique_ptr m (new lym::Macro ()); + m->set_is_file (); + m->set_file_path (path); + m->load_from (path); + return m.release (); +} + Class decl_Macro ("lay", "Macro", + gsi::constructor ("new", &new_from_path, + "@brief Loads the macro from the given file path\n" + "\n" + "This constructor has been introduced in version 0.27.5.\n" + ) + + gsi::method ("run", &lym::Macro::run, + "@brief Executes the macro\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("save_to", &lym::Macro::save_to, gsi::arg ("path"), + "@brief Saves the macro to the given file\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("interpreter_name", &lym::Macro::interpreter_name, + "@brief Gets the macro interpreter name\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("version", &lym::Macro::version, + "@brief Gets the macro's version\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("version=", &lym::Macro::set_version, gsi::arg ("version"), + "@brief Sets the macro's version\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("doc", &lym::Macro::doc, + "@brief Gets the macro's documentation string\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("doc=", &lym::Macro::set_version, gsi::arg ("doc"), + "@brief Sets the macro's documentation string\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("shortcut", &lym::Macro::shortcut, + "@brief Gets the macro's keyboard shortcut\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("shortcut=", &lym::Macro::set_shortcut, gsi::arg ("shortcut"), + "@brief Sets the macro's keyboard shortcut\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("is_autorun?", &lym::Macro::is_autorun, + "@brief Gets a flag indicating whether the macro is automatically executed on startup\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("is_autorun=", &lym::Macro::set_autorun, gsi::arg ("flag"), + "@brief Sets a flag indicating whether the macro is automatically executed on startup\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("is_autorun_early?", &lym::Macro::is_autorun_early, + "@brief Gets a flag indicating whether the macro is automatically executed early on startup\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("is_autorun_early=", &lym::Macro::set_autorun_early, gsi::arg ("flag"), + "@brief Sets a flag indicating whether the macro is automatically executed early on startup\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("format", &lym::Macro::format, + "@brief Gets the macro's storage format\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("format=", &lym::Macro::set_format, gsi::arg ("format"), + "@brief Sets the macro's storage format\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("interpreter", &lym::Macro::interpreter, + "@brief Gets the macro's interpreter\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("interpreter=", &lym::Macro::set_interpreter, gsi::arg ("interpreter"), + "@brief Sets the macro's interpreter\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("dsl_interpreter", &lym::Macro::dsl_interpreter, + "@brief Gets the macro's DSL interpreter name (if interpreter is DSLInterpreter)\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("dsl_interpreter=", &lym::Macro::set_dsl_interpreter, gsi::arg ("dsl_interpreter"), + "@brief Sets the macro's DSL interpreter name (if interpreter is DSLInterpreter)\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("sync_text_with_properties=", &lym::Macro::sync_text_with_properties, + "@brief Synchronizes the macro text with the properties\n" + "\n" + "This method applies to PlainTextWithHashAnnotationsFormat format. The macro text will " + "be enhanced with pseudo-comments reflecting the macro properties. This way, the macro " + "properties can be stored in plain files.\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + + gsi::method ("sync_properties_with_text=", &lym::Macro::sync_properties_with_text, + "@brief Synchronizes the macro properties with the text\n" + "\n" + "This method performs the reverse process of \\sync_text_with_properties.\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + gsi::method ("path", &lym::Macro::path, "@brief Gets the path of the macro\n" "\n" @@ -589,7 +726,7 @@ Class decl_Macro ("lay", "Macro", "@brief Sets the menu path\n" "See \\menu_path for details.\n" ) + - gsi::method ("real_path", &real_path, + gsi::method ("real_path", &real_path, gsi::arg ("path"), gsi::arg ("line"), "@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) will " @@ -614,7 +751,7 @@ Class decl_Macro ("lay", "Macro", "\n" "This feature has been introduced in version 0.27." ) + - gsi::method ("real_line", &real_line, + gsi::method ("real_line", &real_line, gsi::arg ("path"), gsi::arg ("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) will " @@ -649,5 +786,9 @@ Class decl_Macro ("lay", "Macro", "enhanced in future versions and provide access to macros stored inside KLayout's macro repository." ); +// Inject the Macro::Format declarations into MacroInterpreter: +gsi::ClassExt inject_Format_in_macro (decl_FormatEnum.defs ()); +gsi::ClassExt inject_Interprert_in_macro (decl_InterpreterEnum.defs ()); + } From ab350078a05d6a28c25098a1acd5ec83e15ae2b5 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Nov 2021 19:57:31 +0100 Subject: [PATCH 2/6] Cross-calling of Python from Ruby and much more ... --- src/gsi/gsi/gsiDeclBasic.cc | 85 +++++++++++++++++++++++++++++++++- src/gsi/gsi/gsiDeclBasic.h | 2 + src/gsi/gsi/gsiInterpreter.h | 3 +- src/lym/lym/gsiDeclLymMacro.cc | 67 +++++++++++++++++---------- src/pya/pya/pya.cc | 5 +- src/pya/pya/pya.h | 2 +- src/pyastub/pya.cc | 3 +- src/pyastub/pya.h | 2 +- src/rba/rba/rba.cc | 7 +-- src/rba/rba/rba.h | 2 +- src/rba/unit_tests/rbaTests.cc | 1 + src/rbastub/rba.cc | 3 +- src/rbastub/rba.h | 2 +- 13 files changed, 146 insertions(+), 38 deletions(-) diff --git a/src/gsi/gsi/gsiDeclBasic.cc b/src/gsi/gsi/gsiDeclBasic.cc index 78f857372..dab1ce035 100644 --- a/src/gsi/gsi/gsiDeclBasic.cc +++ b/src/gsi/gsi/gsiDeclBasic.cc @@ -22,12 +22,24 @@ #include "gsiDeclBasic.h" +#include "gsiInterpreter.h" #include "gsiDecl.h" +#include "tlTypeTraits.h" + +namespace tl +{ + template <> struct tl::type_traits + : tl::type_traits + { + typedef false_tag has_copy_constructor; + typedef false_tag has_default_constructor; + typedef false_tag has_public_destructor; + }; +} namespace gsi { - // --------------------------------------------------------------------------------- // A generic value wrapper that allows wrapping a plain data type into an object @@ -66,4 +78,75 @@ Class decl_Value ("tl", "Value", "This class has been introduced in version 0.22." ); +static void eval_string_impl (Interpreter *ip, const char *string, const char *filename, int line) +{ + ip->eval_string (string, filename, line); +} + +static tl::Variant eval_expr_impl (Interpreter *ip, const char *string, const char *filename, int line) +{ + return ip->eval_expr (string, filename, line); +} + +static void define_variable_impl (Interpreter *ip, const std::string &name, const tl::Variant &value) +{ + ip->define_variable (name, value); +} + +static gsi::Interpreter *interpreter_by_name (const std::string &name) +{ + for (tl::Registrar::iterator i = gsi::interpreters.begin (); i != gsi::interpreters.end (); ++i) { + if (i.current_name () == name) { + return i->available () ? i.operator-> () : 0; + } + } + return 0; +} + +static gsi::Interpreter *python_interpreter () +{ + return interpreter_by_name ("pya"); +} + +static gsi::Interpreter *ruby_interpreter () +{ + return interpreter_by_name ("rba"); +} + +Class decl_Macro ("tl", "Interpreter", + gsi::method ("load_file", &Interpreter::load_file, gsi::arg ("path"), + "@brief Loads the given file into the interpreter\n" + "This will execute the code inside the file.\n" + ) + + gsi::method_ext ("eval_string", &eval_string_impl, gsi::arg ("string"), gsi::arg ("filename", (const char *) 0, "nil"), gsi::arg ("line", 1), + "@brief Executes the code inside the given string\n" + "Use 'filename' and 'line' to indicate the original source for the error messages.\n" + ) + + gsi::method_ext ("eval_expr", &eval_expr_impl, gsi::arg ("string"), gsi::arg ("filename", (const char *) 0, "nil"), gsi::arg ("line", 1), + "@brief Executes the expression inside the given string and returns the result value\n" + "Use 'filename' and 'line' to indicate the original source for the error messages.\n" + ) + + gsi::method_ext ("define_variable", &define_variable_impl, gsi::arg ("name"), gsi::arg ("value"), + "@brief Defines A (global) variable with the given name and value\n" + "You can use the \\Value class to provide 'out' parameters which can be modified by code executed inside the interpreter." + ) + + gsi::method ("python_interpreter", &python_interpreter, + "@brief Gets the instance of the Python interpreter\n" + ) + + gsi::method ("ruby_interpreter", &ruby_interpreter, + "@brief Gets the instance of the Ruby interpreter\n" + ), + "@brief A generalization of script interpreters\n" + "The main purpose of this class is to provide cross-language call options. " + "Using the Python interpreter, it is possible to execute Python code from Ruby and vice versa.\n" + "\n" + "@code\n" + "pya = RBA::Interpreter::python_interpreter\n" + "out_param = RBA::Value::new(17)\n" + "pya.define_variable(\"out_param\", out_param)\n" + "pya.eval_string(\"print(\"This is Python now!\")\nout_param.value = out_param.value + 25\")\n" + "puts out_param.value # gives '42'" + "@/code\n" +); + } diff --git a/src/gsi/gsi/gsiDeclBasic.h b/src/gsi/gsi/gsiDeclBasic.h index cbd41cbca..43acb605a 100644 --- a/src/gsi/gsi/gsiDeclBasic.h +++ b/src/gsi/gsi/gsiDeclBasic.h @@ -30,6 +30,7 @@ #include #include "tlVariant.h" +#include "gsiObject.h" namespace gsi { @@ -38,6 +39,7 @@ namespace gsi * @brief Provides a basic implementation for a "boxed" plain value using a Variant as the basic type */ class GSI_PUBLIC Value + : public gsi::ObjectBase { public: /** diff --git a/src/gsi/gsi/gsiInterpreter.h b/src/gsi/gsi/gsiInterpreter.h index 36ddea1e1..818719001 100644 --- a/src/gsi/gsi/gsiInterpreter.h +++ b/src/gsi/gsi/gsiInterpreter.h @@ -25,6 +25,7 @@ #include "tlScriptError.h" #include "tlClassRegistry.h" +#include "tlVariant.h" #include "gsiCommon.h" namespace gsi @@ -267,7 +268,7 @@ public: /** * @brief Defines a global variable with the given name and value */ - virtual void define_variable (const std::string &name, const std::string &value) = 0; + virtual void define_variable (const std::string &name, const tl::Variant &value) = 0; /** * @brief Installs the given console for output diff --git a/src/lym/lym/gsiDeclLymMacro.cc b/src/lym/lym/gsiDeclLymMacro.cc index 2ab526368..d41d4ef80 100644 --- a/src/lym/lym/gsiDeclLymMacro.cc +++ b/src/lym/lym/gsiDeclLymMacro.cc @@ -97,11 +97,11 @@ Class decl_MacroExecutionContext ("lay", "MacroExecu "suppress exceptions when re-raising them." ); -class MacroInterpreter +class MacroInterpreterImpl : public lym::MacroInterpreter { public: - MacroInterpreter () + MacroInterpreterImpl () : lym::MacroInterpreter (), mp_registration (0), m_supports_include_expansion (true) { @@ -112,7 +112,7 @@ public: m_debugger_scheme = lym::MacroInterpreter::debugger_scheme (); } - ~MacroInterpreter () + ~MacroInterpreterImpl () { delete mp_registration; mp_registration = 0; @@ -257,7 +257,7 @@ private: bool m_supports_include_expansion; }; -gsi::EnumIn decl_FormatEnum ("lay", "MacroFormat", +gsi::EnumIn decl_FormatEnum ("lay", "Format", gsi::enum_const ("PlainTextFormat", lym::Macro::PlainTextFormat, "@brief The macro has plain text format" ) + @@ -271,7 +271,7 @@ gsi::EnumIn decl_FormatEnum ("lay", "MacroFormat "This enum has been introduced in version 0.27.5." ); -gsi::EnumIn decl_InterpreterEnum ("lay", "MacroInterpreter", +gsi::EnumIn decl_InterpreterEnum ("lay", "Interpreter", gsi::enum_const ("Ruby", lym::Macro::Ruby, "@brief The interpreter is Ruby" ) + @@ -301,14 +301,14 @@ lym::Macro::Interpreter const_NoDebugger () return lym::Macro::None; } -Class decl_MacroInterpreter ("lay", "MacroInterpreter", +Class decl_MacroInterpreter ("lay", "MacroInterpreter", gsi::method ("RubyDebugger", &const_RubyDebugger, "@brief Indicates Ruby debugger for \\debugger_scheme\n" ) + gsi::method ("NoDebugger", &const_NoDebugger, "@brief Indicates no debugging for \\debugger_scheme\n" ) + - gsi::method ("register", &MacroInterpreter::register_gsi, gsi::arg ("name"), + gsi::method ("register", &MacroInterpreterImpl::register_gsi, gsi::arg ("name"), "@brief Registers the macro interpreter\n" "@param name The interpreter name. This is an arbitrary string which should be unique.\n" "\n" @@ -316,7 +316,7 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", "is set to 'dsl' can use this object to run the script. For executing a script, the system will " "call the interpreter's \\execute method.\n" ) + - gsi::method ("create_template", &MacroInterpreter::create_template, gsi::arg ("url"), + gsi::method ("create_template", &MacroInterpreterImpl::create_template, gsi::arg ("url"), "@brief Creates a new macro template\n" "@param url The template will be initialized from that URL.\n" "\n" @@ -326,7 +326,7 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", "\n" "This method must be called after \\register has called.\n" ) + - gsi::method ("supports_include_expansion=", &MacroInterpreter::set_supports_include_expansion, gsi::arg ("flag"), + gsi::method ("supports_include_expansion=", &MacroInterpreterImpl::set_supports_include_expansion, gsi::arg ("flag"), "@brief Sets a value indicating whether this interpreter supports the default include file expansion scheme.\n" "If this value is set to true (the default), lines like '# %include ...' will be substituted by the " "content of the file following the '%include' keyword.\n" @@ -334,7 +334,7 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", "\n" "This attribute has been introduced in version 0.27.\n" ) + - gsi::method ("syntax_scheme=", &gsi::MacroInterpreter::set_syntax_scheme, gsi::arg ("scheme"), + gsi::method ("syntax_scheme=", &MacroInterpreterImpl::set_syntax_scheme, gsi::arg ("scheme"), "@brief Sets a string indicating the syntax highlighter scheme\n" "\n" "The scheme string can be empty (indicating no syntax highlighting), \"ruby\" for the Ruby syntax " @@ -346,7 +346,7 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", "Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for " "performance reasons in version 0.25.\n" ) + - gsi::method ("debugger_scheme=", &gsi::MacroInterpreter::set_debugger_scheme, gsi::arg ("scheme"), + gsi::method ("debugger_scheme=", &MacroInterpreterImpl::set_debugger_scheme, gsi::arg ("scheme"), "@brief Sets the debugger scheme (which debugger to use for the DSL macro)\n" "\n" "The value can be one of the constants \\RubyDebugger or \\NoDebugger.\n" @@ -356,7 +356,7 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", "Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for " "performance reasons in version 0.25.\n" ) + - gsi::method ("storage_scheme=", &gsi::MacroInterpreter::set_storage_scheme, gsi::arg ("scheme"), + gsi::method ("storage_scheme=", &MacroInterpreterImpl::set_storage_scheme, gsi::arg ("scheme"), "@brief Sets the storage scheme (the format as which the macro is stored)\n" "\n" "This value indicates how files for this DSL macro type shall be stored. " @@ -367,7 +367,7 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", "Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for " "performance reasons in version 0.25.\n" ) + - gsi::method ("description=", &gsi::MacroInterpreter::set_description, gsi::arg ("description"), + gsi::method ("description=", &MacroInterpreterImpl::set_description, gsi::arg ("description"), "@brief Sets a description string\n" "\n" "This string is used for showing the type of DSL macro in the file selection box together with the " @@ -378,7 +378,7 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", "Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for " "performance reasons in version 0.25.\n" ) + - gsi::method ("suffix=", &gsi::MacroInterpreter::set_suffix, gsi::arg ("suffix"), + gsi::method ("suffix=", &MacroInterpreterImpl::set_suffix, gsi::arg ("suffix"), "@brief Sets the file suffix\n" "\n" "This string defines which file suffix to associate with the DSL macro. If an empty string is given (the default) " @@ -389,7 +389,7 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", "Before version 0.25 this attribute was a re-implementable method. It has been turned into an attribute for " "performance reasons in version 0.25.\n" ) + - gsi::callback ("executable", &gsi::MacroInterpreter::executable, &gsi::MacroInterpreter::f_executable, gsi::arg ("macro"), + gsi::callback ("executable", &MacroInterpreterImpl::executable, &MacroInterpreterImpl::f_executable, gsi::arg ("macro"), "@brief Returns the executable object which implements the macro execution\n" "This method must be reimplemented to return an \\Executable object for the actual implementation. " "The system will use this function to execute the script when a macro with interpreter type 'dsl' and the " @@ -479,7 +479,7 @@ Class decl_MacroInterpreter ("lay", "MacroInterpreter", ); // Inject the Macro::Format declarations into MacroInterpreter: -gsi::ClassExt inject_Format_in_parent (decl_FormatEnum.defs ()); +gsi::ClassExt inject_Format_in_parent (decl_FormatEnum.defs ()); static lym::Macro *macro_by_path (const std::string &path) { @@ -529,11 +529,6 @@ Class decl_Macro ("lay", "Macro", "\n" "This method has been introduced in version 0.27.5.\n" ) + - gsi::method ("interpreter_name", &lym::Macro::interpreter_name, - "@brief Gets the macro interpreter name\n" - "\n" - "This method has been introduced in version 0.27.5.\n" - ) + gsi::method ("version", &lym::Macro::version, "@brief Gets the macro's version\n" "\n" @@ -549,7 +544,7 @@ Class decl_Macro ("lay", "Macro", "\n" "This method has been introduced in version 0.27.5.\n" ) + - gsi::method ("doc=", &lym::Macro::set_version, gsi::arg ("doc"), + gsi::method ("doc=", &lym::Macro::set_doc, gsi::arg ("doc"), "@brief Sets the macro's documentation string\n" "\n" "This method has been introduced in version 0.27.5.\n" @@ -604,6 +599,12 @@ Class decl_Macro ("lay", "Macro", "\n" "This method has been introduced in version 0.27.5.\n" ) + + gsi::method ("interpreter_name", &lym::Macro::interpreter_name, + "@brief Gets the macro interpreter name\n" + "This is the string version of \\interpreter.\n" + "\n" + "This method has been introduced in version 0.27.5.\n" + ) + gsi::method ("dsl_interpreter", &lym::Macro::dsl_interpreter, "@brief Gets the macro's DSL interpreter name (if interpreter is DSLInterpreter)\n" "\n" @@ -614,7 +615,7 @@ Class decl_Macro ("lay", "Macro", "\n" "This method has been introduced in version 0.27.5.\n" ) + - gsi::method ("sync_text_with_properties=", &lym::Macro::sync_text_with_properties, + gsi::method ("sync_text_with_properties", &lym::Macro::sync_text_with_properties, "@brief Synchronizes the macro text with the properties\n" "\n" "This method applies to PlainTextWithHashAnnotationsFormat format. The macro text will " @@ -623,7 +624,7 @@ Class decl_Macro ("lay", "Macro", "\n" "This method has been introduced in version 0.27.5.\n" ) + - gsi::method ("sync_properties_with_text=", &lym::Macro::sync_properties_with_text, + gsi::method ("sync_properties_with_text", &lym::Macro::sync_properties_with_text, "@brief Synchronizes the macro properties with the text\n" "\n" "This method performs the reverse process of \\sync_text_with_properties.\n" @@ -784,11 +785,27 @@ Class decl_Macro ("lay", "Macro", "This class is provided mainly to support generation of template macros in the " "DSL interpreter framework provided by \\MacroInterpreter. The implementation may be " "enhanced in future versions and provide access to macros stored inside KLayout's macro repository." + "\n" + "But it can be used to execute macro code in a consistent way:\n" + "\n" + "@code\n" + "path = \"path-to-macro.lym\"\n" + "RBA::Macro::new(path).run()\n" + "@/code\n" + "\n" + "Using the Macro class with \\run for executing code will chose the right interpreter and is " + "able to execute DRC and LVS scripts in the proper environment. This also provides an option to " + "execute Ruby code from Python and vice versa.\n" + "\n" + "In this scenario you can pass values to the script using \\Interpreter#define_variable. " + "The interpreter to choose for DRC and LVS scripts is \\Interpreter#ruby_interpreter. " + "For passing values back from the script, wrap the variable value into a \\Value object " + "which can be modified by the called script and read back by the caller." ); // Inject the Macro::Format declarations into MacroInterpreter: gsi::ClassExt inject_Format_in_macro (decl_FormatEnum.defs ()); -gsi::ClassExt inject_Interprert_in_macro (decl_InterpreterEnum.defs ()); +gsi::ClassExt inject_Interpreter_in_macro (decl_InterpreterEnum.defs ()); } diff --git a/src/pya/pya/pya.cc b/src/pya/pya/pya.cc index dee876b65..6266282f1 100644 --- a/src/pya/pya/pya.cc +++ b/src/pya/pya/pya.cc @@ -175,7 +175,8 @@ static void reset_interpreter () } PythonInterpreter::PythonInterpreter (bool embedded) - : mp_current_console (0), mp_current_exec_handler (0), m_current_exec_level (0), + : gsi::Interpreter (0, "pya"), + mp_current_console (0), mp_current_exec_handler (0), m_current_exec_level (0), m_in_trace (false), m_block_exceptions (false), m_ignore_next_exception (false), mp_current_frame (NULL), mp_py3_app_name (0), m_embedded (embedded) { @@ -572,7 +573,7 @@ PythonInterpreter::inspector (int context) } void -PythonInterpreter::define_variable (const std::string &name, const std::string &value) +PythonInterpreter::define_variable (const std::string &name, const tl::Variant &value) { PythonPtr main_module (PyImport_AddModule ("__main__")); PythonPtr dict (PyModule_GetDict (main_module.get ())); diff --git a/src/pya/pya/pya.h b/src/pya/pya/pya.h index 4688e64e8..ff69ab37c 100644 --- a/src/pya/pya/pya.h +++ b/src/pya/pya/pya.h @@ -182,7 +182,7 @@ public: /** * @brief Defines a global variable with the given name and value */ - void define_variable (const std::string &name, const std::string &value); + void define_variable (const std::string &name, const tl::Variant &value); /** * @brief Gets a value indicating whether the interpreter is available diff --git a/src/pyastub/pya.cc b/src/pyastub/pya.cc index f1dcdf1b3..d8b6643e9 100644 --- a/src/pyastub/pya.cc +++ b/src/pyastub/pya.cc @@ -35,6 +35,7 @@ static void fail (const char *file, int line) static PythonInterpreter *sp_pya_interpreter = 0; PythonInterpreter::PythonInterpreter () + : gsi::Interpreter (0, "pya") { tl_assert (! sp_pya_interpreter); sp_pya_interpreter = this; @@ -118,7 +119,7 @@ PythonInterpreter::inspector (int) } void -PythonInterpreter::define_variable (const std::string &, const std::string &) +PythonInterpreter::define_variable (const std::string &, const tl::Variant &) { // .. nothing .. } diff --git a/src/pyastub/pya.h b/src/pyastub/pya.h index 45826dc9d..e9ee98cc5 100644 --- a/src/pyastub/pya.h +++ b/src/pyastub/pya.h @@ -114,7 +114,7 @@ public: /** * @brief Defines a global variable with the given name and value */ - void define_variable (const std::string &name, const std::string &value); + void define_variable (const std::string &name, const tl::Variant &value); /** * @brief Gets a value indicating whether the interpreter is available diff --git a/src/rba/rba/rba.cc b/src/rba/rba/rba.cc index b0978762a..7aaa7899e 100644 --- a/src/rba/rba/rba.cc +++ b/src/rba/rba/rba.cc @@ -1758,7 +1758,8 @@ rba_init (RubyInterpreterPrivateData *d) } RubyInterpreter::RubyInterpreter () - : d (new RubyInterpreterPrivateData ()) + : gsi::Interpreter (0, "rba"), + d (new RubyInterpreterPrivateData ()) { tl::SelfTimer timer (tl::verbosity () >= 21, "Initializing Ruby"); @@ -2057,9 +2058,9 @@ RubyInterpreter::eval_string_and_print (const char *expr, const char *file, int } void -RubyInterpreter::define_variable (const std::string &name, const std::string &value) +RubyInterpreter::define_variable (const std::string &name, const tl::Variant &value) { - rb_gv_set (name.c_str (), rb_str_new (value.c_str (), long (value.size ()))); + rb_gv_set (name.c_str (), c2ruby (value)); } gsi::Inspector * diff --git a/src/rba/rba/rba.h b/src/rba/rba/rba.h index bd8442724..094e031d3 100644 --- a/src/rba/rba/rba.h +++ b/src/rba/rba/rba.h @@ -127,7 +127,7 @@ public: /** * @brief Defines a global variable with the given name and value */ - void define_variable (const std::string &name, const std::string &value); + void define_variable (const std::string &name, const tl::Variant &value); /** * @brief Gets a value indicating whether the interpreter is available diff --git a/src/rba/unit_tests/rbaTests.cc b/src/rba/unit_tests/rbaTests.cc index 7cdac9988..3d9a9f2c8 100644 --- a/src/rba/unit_tests/rbaTests.cc +++ b/src/rba/unit_tests/rbaTests.cc @@ -141,6 +141,7 @@ RUBYTEST (imgObject, "imgObject.rb") RUBYTEST (layLayers, "layLayers.rb") RUBYTEST (layLayoutView, "layLayoutView.rb") RUBYTEST (layMarkers, "layMarkers.rb") +RUBYTEST (layMacro, "layMacro.rb") RUBYTEST (layMenuTest, "layMenuTest.rb") RUBYTEST (laySession, "laySession.rb") RUBYTEST (layTechnologies, "layTechnologies.rb") diff --git a/src/rbastub/rba.cc b/src/rbastub/rba.cc index dda501be6..2c1c821d7 100644 --- a/src/rbastub/rba.cc +++ b/src/rbastub/rba.cc @@ -34,6 +34,7 @@ static void fail (const char *file, int line) static RubyInterpreter *sp_rba_interpreter = 0; RubyInterpreter::RubyInterpreter () + : gsi::Interpreter (0, "rba") { tl_assert (! sp_rba_interpreter); sp_rba_interpreter = this; @@ -110,7 +111,7 @@ RubyInterpreter::eval_string_and_print (const char *, const char *file, int line } void -RubyInterpreter::define_variable (const std::string &, const std::string &) +RubyInterpreter::define_variable (const std::string &, const tl::Variant &) { // .. nothing .. } diff --git a/src/rbastub/rba.h b/src/rbastub/rba.h index 2fc5a7ca2..a17799994 100644 --- a/src/rbastub/rba.h +++ b/src/rbastub/rba.h @@ -111,7 +111,7 @@ public: /** * @brief Defines a global variable with the given name and value */ - void define_variable (const std::string &name, const std::string &value); + void define_variable (const std::string &name, const tl::Variant &value); /** * @brief Gets a value indicating whether the interpreter is available From be191651402d14876ed4ec0c1abaa8a8e106ee80 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Nov 2021 20:34:56 +0100 Subject: [PATCH 3/6] Doc fixes. --- src/gsi/gsi/gsiDeclBasic.cc | 13 +++++++++---- src/lym/lym/gsiDeclLymMacro.cc | 2 +- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/gsi/gsi/gsiDeclBasic.cc b/src/gsi/gsi/gsiDeclBasic.cc index dab1ce035..bd10db3be 100644 --- a/src/gsi/gsi/gsiDeclBasic.cc +++ b/src/gsi/gsi/gsiDeclBasic.cc @@ -127,8 +127,8 @@ Class decl_Macro ("tl", "Interpreter", "Use 'filename' and 'line' to indicate the original source for the error messages.\n" ) + gsi::method_ext ("define_variable", &define_variable_impl, gsi::arg ("name"), gsi::arg ("value"), - "@brief Defines A (global) variable with the given name and value\n" - "You can use the \\Value class to provide 'out' parameters which can be modified by code executed inside the interpreter." + "@brief Defines a (global) variable with the given name and value\n" + "You can use the \\Value class to provide 'out' or 'inout' parameters which can be modified by code executed inside the interpreter and read back by the caller." ) + gsi::method ("python_interpreter", &python_interpreter, "@brief Gets the instance of the Python interpreter\n" @@ -138,15 +138,20 @@ Class decl_Macro ("tl", "Interpreter", ), "@brief A generalization of script interpreters\n" "The main purpose of this class is to provide cross-language call options. " - "Using the Python interpreter, it is possible to execute Python code from Ruby and vice versa.\n" + "Using the Python interpreter, it is possible to execute Python code from Ruby for example.\n" + "\n" + "The following example shows how to use the interpreter class to execute Python code from Ruby " + "and how to pass values from Ruby to Python and back using the \\Value wrapper object:\n" "\n" "@code\n" "pya = RBA::Interpreter::python_interpreter\n" "out_param = RBA::Value::new(17)\n" "pya.define_variable(\"out_param\", out_param)\n" - "pya.eval_string(\"print(\"This is Python now!\")\nout_param.value = out_param.value + 25\")\n" + "pya.eval_string(\"print(\\\"This is Python now!\\\")\\nout_param.value = out_param.value + 25\")\n" "puts out_param.value # gives '42'" "@/code\n" + "\n" + "This class was introduced in version 0.27.5.\n" ); } diff --git a/src/lym/lym/gsiDeclLymMacro.cc b/src/lym/lym/gsiDeclLymMacro.cc index d41d4ef80..91ba0da3e 100644 --- a/src/lym/lym/gsiDeclLymMacro.cc +++ b/src/lym/lym/gsiDeclLymMacro.cc @@ -514,7 +514,7 @@ lym::Macro *new_from_path (const std::string &path) } Class decl_Macro ("lay", "Macro", - gsi::constructor ("new", &new_from_path, + gsi::constructor ("new", &new_from_path, gsi::arg ("path"), "@brief Loads the macro from the given file path\n" "\n" "This constructor has been introduced in version 0.27.5.\n" From 2831dd7f2682a1176e581d879030464552b7cb93 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 13 Nov 2021 20:39:49 +0100 Subject: [PATCH 4/6] Fixed code sample --- src/gsi/gsi/gsiDeclBasic.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/gsi/gsi/gsiDeclBasic.cc b/src/gsi/gsi/gsiDeclBasic.cc index bd10db3be..d13aadf68 100644 --- a/src/gsi/gsi/gsiDeclBasic.cc +++ b/src/gsi/gsi/gsiDeclBasic.cc @@ -147,7 +147,10 @@ Class decl_Macro ("tl", "Interpreter", "pya = RBA::Interpreter::python_interpreter\n" "out_param = RBA::Value::new(17)\n" "pya.define_variable(\"out_param\", out_param)\n" - "pya.eval_string(\"print(\\\"This is Python now!\\\")\\nout_param.value = out_param.value + 25\")\n" + "pya.eval_string(< Date: Sun, 14 Nov 2021 11:56:40 +0100 Subject: [PATCH 5/6] Fixed build errors. --- src/gsi/gsi/gsiDeclBasic.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gsi/gsi/gsiDeclBasic.cc b/src/gsi/gsi/gsiDeclBasic.cc index d13aadf68..dc43160c0 100644 --- a/src/gsi/gsi/gsiDeclBasic.cc +++ b/src/gsi/gsi/gsiDeclBasic.cc @@ -28,7 +28,7 @@ namespace tl { - template <> struct tl::type_traits + template <> struct type_traits : tl::type_traits { typedef false_tag has_copy_constructor; From 7d316b2a2c2b21ac85b548acd53671109c8967f7 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 14 Nov 2021 14:39:42 +0100 Subject: [PATCH 6/6] Added missing file --- testdata/ruby/layMacro.rb | 184 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 testdata/ruby/layMacro.rb diff --git a/testdata/ruby/layMacro.rb b/testdata/ruby/layMacro.rb new file mode 100644 index 000000000..e246e4030 --- /dev/null +++ b/testdata/ruby/layMacro.rb @@ -0,0 +1,184 @@ +# encoding: UTF-8 + +# KLayout Layout Viewer +# Copyright (C) 2006-2021 Matthias Koefferlein +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +if !$:.member?(File::dirname($0)) + $:.push(File::dirname($0)) +end + +load("test_prologue.rb") + +class LAYMacro_TestClass < TestBase + + def test_1 + + macro = RBA::Macro::new + + macro.version = "1.7" + assert_equal(macro.version, "1.7") + + macro.doc = "%doc" + assert_equal(macro.doc, "%doc") + + macro.description = "%description" + assert_equal(macro.description, "%description") + + macro.prolog = "%prolog" + assert_equal(macro.prolog, "%prolog") + + macro.epilog = "%epilog" + assert_equal(macro.epilog, "%epilog") + + macro.category = "%category" + assert_equal(macro.category, "%category") + + macro.shortcut = "%shortcut" + assert_equal(macro.shortcut, "%shortcut") + + assert_equal(macro.is_autorun?, false) + macro.is_autorun = true + assert_equal(macro.is_autorun?, true) + + assert_equal(macro.is_autorun_early?, false) + macro.is_autorun_early = true + assert_equal(macro.is_autorun_early?, true) + + macro.format = RBA::Macro::PlainTextFormat + assert_equal(macro.format == RBA::Macro::PlainTextFormat, true) + macro.format = RBA::Macro::MacroFormat + assert_equal(macro.format == RBA::Macro::MacroFormat, true) + + macro.interpreter = RBA::Macro::Ruby + assert_equal(macro.interpreter == RBA::Macro::Ruby, true) + assert_equal(macro.interpreter_name, "Ruby") + macro.interpreter = RBA::Macro::Python + assert_equal(macro.interpreter == RBA::Macro::Python, true) + assert_equal(macro.interpreter_name, "Python") + + macro.dsl_interpreter = "%dsl" + assert_equal(macro.dsl_interpreter, "%dsl") + + macro.text = "%text" + macro.format = RBA::Macro::PlainTextWithHashAnnotationsFormat + assert_equal(macro.text, "%text") + macro.sync_text_with_properties + assert_equal(macro.text, "# $description: %description\n" + + "# $prolog: %prolog\n" + + "# $epilog: %epilog\n" + + "# $version: 1.7\n" + + "# $autorun\n" + + "# $autorun-early\n" + + "# $shortcut: %shortcut\n" + + "%text") + + macro.text = "# $description: %description\n" + + "# $prolog: %prolog\n" + + "# $epilog: %epilog\n" + + "# $version: 7.1\n" + + "# $autorun\n" + + "# $autorun-early\n" + + "# $shortcut: %shortcut\n" + + "%text" + macro.sync_properties_with_text + assert_equal(macro.version, "7.1") + + macro.group_name = "%group" + assert_equal(macro.group_name, "%group") + + assert_equal(macro.show_in_menu?, false) + macro.show_in_menu = true + assert_equal(macro.show_in_menu?, true) + + macro.menu_path = "menu.path" + assert_equal(macro.menu_path, "menu.path") + + end + + def test_2 + + macro_file = File.join($ut_testtmp, "test.lym") + File.open(macro_file, "w") do |file| + file.write(<<"END") + + + %description + 42 + ruby + +$test_output = "x" + $test_input + + +END + end + + macro = RBA::Macro::new(macro_file) + assert_equal(macro.description, "%description") + assert_equal(macro.path, macro_file) + + $test_input = "42" + $test_output = "" + macro.run + assert_equal($test_output, "x42") + + macro_file2 = File.join($ut_testtmp, "test2.lym") + macro.save_to(macro_file2) + + macro2 = RBA::Macro::new(macro_file2) + assert_equal(macro2.path, macro_file2) + + end + + def test_3 + + pya = RBA::Interpreter.python_interpreter + if !pya + return + end + + macro_file = File.join($ut_testtmp, "test.lym") + File.open(macro_file, "w") do |file| + file.write(<<"END") + + + %description + 42 + python + +print("Python calling!") +context.value = "x" + context.value + + +END + end + + macro = RBA::Macro::new(macro_file) + assert_equal(macro.description, "%description") + assert_equal(macro.path, macro_file) + + context = RBA::Value::new + context.value = "42" + pya.define_variable("context", context) + + macro.run + assert_equal(context.value, "x42") + + end + +end + +load("test_epilogue.rb")