1 /* Copyright (c) 1997-2021
2    Ewgenij Gawrilow, Michael Joswig, and the polymake team
3    Technische Universität Berlin, Germany
4    https://polymake.org
5 
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version: http://www.gnu.org/licenses/gpl.txt.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 --------------------------------------------------------------------------------
16 */
17 
18 #pragma once
19 
20 #ifdef POLYMAKE_APPNAME
21 #error polymake::Main cannot be used in clients
22 #endif
23 
24 #ifndef POLYMAKE_DEBUG
25 #define POLYMAKE_DEBUG 0
26 #endif
27 
28 // for classes defined in external application code
29 #define POLYMAKE_APPNAME unknown
30 
31 #include "polymake/client.h"
32 #include "polymake/vector"
33 #include <string>
34 
35 namespace pm { namespace perl {
36 
37 class Scope;
38 
39 class Main {
40 public:
41    //! Initialize polymake for use in the current thread
42    //!
43    //! Only the first invocation in the program evaluates its arguments,
44    //! all subsequent invocations are only needed to register already initialized polymake system with other threads.
45    //! The calling application is responsible to prevent simultaneous execution of any polymake functions in several threads,
46    //! which includes all methods of this class, Scope, BigObject, BigObjectType, as well as call_function, prepare_call_function and
47    //! any other functions requiring interaction with polymake perl interpreter.
48    //!
49    //! @param user_opts configuration path, corresponds to --config-path option of the standalone main script
50    //!                  several path elements should be separated with ;
51    //! @param install_top override the location of polymake installation top (shared) directory specified during initial pre-build configuration
52    //! @param install_arch override the location of polymake installation binary directory specified during initial pre-build configuration
53    explicit Main(const std::string& user_opts = "user",
54                  std::string install_top = "",
55                  std::string install_arch = "");
56 
57    //! Get a greeting message
58    //! @param verbose integer between 0 and 2, larger value means longer text
59    std::string greeting(int verbose = 2);
60 
61    //! Select the current application by name
62    void set_application(const AnyString& appname);
63 
64    //! Select the current application by a big object belonging to it
65    void set_application_of(const BigObject& x);
66 
67    //! Load an extension installed at the given path
68    //! shell_enable() must be called prior to this
69    void add_extension(const AnyString& path);
70 
71    //! Load a rulefile specified by given path
72    void include(const AnyString& path);
73 
74    //! Set a preference list
75    //! @param label_exp same as in the interactive shell command
76    void set_preference(const AnyString& label_exp);
77 
78    //! Reset a preference list to default value as defined in the rules
79    //! @param label_exp same as in the interactive shell command
80    void reset_preference(const AnyString& label_exp);
81 
82    //! Set a custom variable to a given value
83    template <typename T>
set_custom(const AnyString & name,T && value)84    void set_custom(const AnyString& name, T&& value)
85    {
86       Value x;
87       x << std::forward<T>(value);
88       set_custom_var(name, AnyString(), x);
89    }
90 
91    //! Set a custom hash entry to a given value
92    template <typename T>
set_custom(const AnyString & name,const AnyString & key,T && value)93    void set_custom(const AnyString& name, const AnyString& key, T&& value)
94    {
95       Value x;
96       x << std::forward<T>(value);
97       set_custom_var(name, key, x);
98    }
99 
100    //! Reset a custom variable or a custom hash entry to its default value as defined in the rules
101    void reset_custom(const AnyString& name, const AnyString& key = AnyString());
102 
103    //! Construct a new Scope object
104    Scope newScope();
105 
106    friend class Scope;
107 
108    //! Load additional modules providing shell functionality.
109    //! This should be called prior to loading any application, otherwise methods querying
110    //! command completion and context help won't work.
111    void shell_enable();
112 
113    //! Execute a piece of polymake/perl code in the context of the current application as if it has been entered in an interactive shell
114    //! The input must be encoded in UTF-8.
115    //! @return a tuple of four elements:
116    //!         <0> boolean indicating whether the input string could be parsed and executed
117    //!         <1> the entire stdout output produced during the execution, in particular, results of print statements;
118    //!             empty when <0> is false
119    //!         <2> the entire stderr output produced during the execution, this may include various harmless warnings and credit notes;
120    //!             empty when <0> is false
121    //!         <3> the message of an exception raised during the execution (when <0> is true) or input parse error message (when <0> is false)
122    //! Note that the result for a syntactically correct but incomplete input (unfinished statement, open block, etc.)
123    //! will be {false, "", "", ""}.
124    using shell_execute_t = std::tuple<bool, std::string, std::string, std::string>;
125    shell_execute_t shell_execute(const std::string& input);
126 
127    //! get all possible completions for the partial input string, as if the TAB key has been pressed at the end of the string
128    //! @return a tuple of three elements:
129    //!         <0> offset from the end of the input string to the position where all completion proposals can be applied;
130    //!             in other words, length of the intersection of the input string and the proposals
131    //!         <1> optional delimiter character which can be appended after every completion proposal
132    //!         <2> array of completion proposals
133    using shell_complete_t = std::tuple<int, char, std::vector<std::string>>;
134    shell_complete_t shell_complete(const std::string& input);
135 
136    //! get documentation strings describing an item in the input string near given position
137    //! multiple results are possible when the input can't be parsed unambiguously,
138    //! e.g. when the item in question refers to an overloaded function
139    //! @param input a piece of polymake/perl code
140    //! @param pos cursor position within the input string; results will describe the item
141    //!            under the cursor or at the closest location towards the begin
142    //!            By default, end of line is assumed
143    //! @param full provide full documentation texts including parameter descriptions, examples, etc.
144    //! @param html documentation texts may include HTML markup
145    std::vector<std::string> shell_context_help(const std::string& input, size_t pos=std::string::npos, bool full=false, bool html=false);
146 
147    //! Install a signal handler for the given signal.
148    //!
149    //! The signal can be SIGINT or a custom interceptible signal like SUGUSR1.
150    //!
151    //! If the application handles signals in a thread other than where polymake functions are executed,
152    //! it must deliver the signal agreed upon here to polymake thread using pthread_kill().
153    //! It is in the caller's responsibility to ensure that the signal is not blocked by polymake's thread signal mask.
154    //!
155    //! If the signal arrives while polymake is busy e.g. computing object properties or executing a user function,
156    //! it will eventually stop and throw an exception, returning to the caller.  If the signal arrives during shell_execute,
157    //! the exception will be reported as part of the result tuple.
158    //! Please be aware that the break can happen with arbitrarily large delay, because inner loops in pure C/C++ code not communicating
159    //! with polymake server are not interruptible yet.
160    void set_interrupt_signal(int signum);
161 
162    //! Uninstall the interrupt signal handler installed earlier via set_interrupt_signal().
163    void reset_interrupt_signal();
164 
165 private:
166    SV* lookup_extension(const AnyString& path); // currently unused
167    void call_app_method(const char* method, const AnyString& arg);
168 
169    void set_custom_var(const AnyString& name, const AnyString& key, Value& x);
170 };
171 
172 class Scope {
173    friend class Main;
174 public:
Scope(Scope && s)175    Scope(Scope&& s)
176       : pm_main(s.pm_main)
177       , saved(s.saved)
178       , id(s.id)
179    {
180       s.saved = nullptr;
181    }
182 
183    ~Scope();
184 
185    //! Set a temporary preference list
186    //! It is reverted when this Scope object is destroyed
187    //! @param labels same as in the interactive shell command
188    void prefer_now(const AnyString& labels) const;
189 
190    //! Assign a temporary value to a custom variable
191    //! It is reverted when this Scope object is destroyed
192    template <typename T>
set_custom(const AnyString & name,T && value)193    void set_custom(const AnyString& name, T&& value)
194    {
195       Value x;
196       x << std::forward<T>(value);
197       set_custom_var(name, AnyString(), x);
198    }
199 
200    //! Assign a temporary value to a custom hash entry
201    //! It is reverted when this Scope object is destroyed
202    template <typename T>
set_custom(const AnyString & name,const AnyString & key,T && value)203    void set_custom(const AnyString& name, const AnyString& key, T&& value)
204    {
205       Value x;
206       x << std::forward<T>(value);
207       set_custom_var(name, key, x);
208    }
209 
210 private:
Scope(Main * main_arg,SV * sv)211    Scope(Main* main_arg, SV* sv)
212       : pm_main(main_arg)
213       , saved(sv)
214       , id(++depth) {}
215 
216    Scope(const Scope& s) = delete;
217    Scope& operator= (const Scope&) = delete;
218 
219    void set_custom_var(const AnyString& name, const AnyString& key, Value& value) const;
220 
221    static unsigned int depth;
222    Main* pm_main;
223    SV* saved;
224    unsigned int id;
225 };
226 
227 } }
228 
229 namespace polymake {
230 
231 using pm::perl::Main;
232 using pm::perl::Scope;
233 
234 }
235 
236 
237 // Local Variables:
238 // mode:C++
239 // c-basic-offset:3
240 // indent-tabs-mode:nil
241 // End:
242