1 // This file is distributed under the BSD License.
2 // See "license.txt" for details.
3 // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
4 // Copyright 2009-2017, Jason Turner (jason@emptycrate.com)
5 // http://www.chaiscript.com
6 
7 // This is an open source non-commercial project. Dear PVS-Studio, please check it.
8 // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
9 
10 
11 #include <iostream>
12 #include <list>
13 #include <regex>
14 
15 #ifdef _MSC_VER
16 #define _CRT_SECURE_NO_WARNINGS
17 #endif
18 
19 #include <chaiscript/chaiscript_basic.hpp>
20 #include "../static_libs/chaiscript_parser.hpp"
21 #include "../static_libs/chaiscript_stdlib.hpp"
22 
23 
24 #ifdef READLINE_AVAILABLE
25 #include <readline/readline.h>
26 #include <readline/history.h>
27 #else
28 
mystrdup(const char * s)29 char *mystrdup (const char *s) {
30   size_t len = strlen(s); // Space for length plus nul
31   char *d = static_cast<char*>(malloc (len+1));
32   if (d == nullptr) { return nullptr; }         // No memory
33 #ifdef CHAISCRIPT_MSVC
34   strcpy_s(d, len+1, s);                        // Copy the characters
35 #else
36   strncpy(d,s,len);                        // Copy the characters
37 #endif
38   d[len] = '\0';
39   return d;                            // Return the new string
40 }
41 
readline(const char * p)42 char* readline(const char* p)
43 {
44   std::string retval;
45   std::cout << p ;
46   std::getline(std::cin, retval);
47   return std::cin.eof() ? nullptr : mystrdup(retval.c_str());
48 }
49 
50 
add_history(const char *)51 void add_history(const char* /*unused*/){}
using_history()52 void using_history(){}
53 #endif
54 
55 
56 
cast_module_symbol(std::vector<std::string> (* t_path)())57 void *cast_module_symbol(std::vector<std::string> (*t_path)())
58 {
59   union cast_union
60   {
61     std::vector<std::string> (*in_ptr)();
62     void *out_ptr;
63   };
64 
65   cast_union c;
66   c.in_ptr = t_path;
67   return c.out_ptr;
68 }
69 
default_search_paths()70 std::vector<std::string> default_search_paths()
71 {
72   std::vector<std::string> paths;
73 
74 #ifndef CHAISCRIPT_NO_DYNLOAD
75 #ifdef CHAISCRIPT_WINDOWS  // force no unicode
76   CHAR path[4096];
77   int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1);
78 
79   std::string exepath(path, size);
80 
81   size_t lastslash = exepath.rfind('\\');
82   size_t secondtolastslash = exepath.rfind('\\', lastslash - 1);
83   if (lastslash != std::string::npos)
84   {
85     paths.push_back(exepath.substr(0, lastslash));
86   }
87 
88   if (secondtolastslash != std::string::npos)
89   {
90     return {exepath.substr(0, secondtolastslash) + "\\lib\\chaiscript\\"};
91   }
92 #else
93 
94   std::string exepath;
95 
96   std::vector<char> buf(2048);
97   ssize_t size = -1;
98 
99   if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) >= 0)
100   {
101     exepath = std::string(&buf.front(), static_cast<size_t>(size));
102   }
103 
104   if (exepath.empty())
105   {
106     if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) >= 0)
107     {
108       exepath = std::string(&buf.front(), static_cast<size_t>(size));
109     }
110   }
111 
112   if (exepath.empty())
113   {
114     if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) >= 0)
115     {
116       exepath = std::string(&buf.front(), static_cast<size_t>(size));
117     }
118   }
119 
120   if (exepath.empty())
121   {
122     Dl_info rInfo;
123     memset( &rInfo, 0, sizeof(rInfo) );
124     if ( dladdr(cast_module_symbol(&default_search_paths), &rInfo) == 0 || rInfo.dli_fname == nullptr ) {
125       return paths;
126     }
127 
128     exepath = std::string(rInfo.dli_fname);
129   }
130 
131   size_t lastslash = exepath.rfind('/');
132 
133   size_t secondtolastslash = exepath.rfind('/', lastslash - 1);
134   if (lastslash != std::string::npos)
135   {
136     paths.push_back(exepath.substr(0, lastslash+1));
137   }
138 
139   if (secondtolastslash != std::string::npos)
140   {
141     paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/");
142   }
143 #endif
144 #endif // ifndef CHAISCRIPT_NO_DYNLOAD
145 
146   return paths;
147 }
148 
help(int n)149 void help(int n) {
150   if ( n >= 0 ) {
151     std::cout << "ChaiScript evaluator.  To evaluate an expression, type it and press <enter>.\n";
152     std::cout << "Additionally, you can inspect the runtime system using:\n";
153     std::cout << "  dump_system() - outputs all functions registered to the system\n";
154     std::cout << "  dump_object(x) - dumps information about the given symbol\n";
155   } else {
156     std::cout << "usage : chai [option]+\n";
157     std::cout << "option:"                << '\n';
158     std::cout << "   -h | --help"         << '\n';
159     std::cout << "   -i | --interactive"  << '\n';
160     std::cout << "   -c | --command cmd"  << '\n';
161     std::cout << "   -v | --version"      << '\n';
162     std::cout << "   -    --stdin"        << '\n';
163     std::cout << "   filepath"            << '\n';
164   }
165 }
166 
throws_exception(const std::function<void ()> & f)167 std::string throws_exception(const std::function<void ()> &f)
168 {
169   try {
170     f();
171   } catch (const std::exception &e) {
172     return e.what();
173   }
174 
175   return "";
176 }
177 
get_eval_error(const std::function<void ()> & f)178 chaiscript::exception::eval_error get_eval_error(const std::function<void ()> &f)
179 {
180   try {
181     f();
182   } catch (const chaiscript::exception::eval_error &e) {
183     return e;
184   }
185 
186   throw std::runtime_error("no exception throw");
187 }
188 
get_next_command()189 std::string get_next_command() {
190   std::string retval("quit");
191   if ( ! std::cin.eof() ) {
192     char *input_raw = readline("eval> ");
193     if ( input_raw != nullptr ) {
194       add_history(input_raw);
195 
196       std::string val(input_raw);
197       size_t pos = val.find_first_not_of("\t \n");
198       if (pos != std::string::npos)
199       {
200         val.erase(0, pos);
201       }
202       pos = val.find_last_not_of("\t \n");
203       if (pos != std::string::npos)
204       {
205         val.erase(pos+1, std::string::npos);
206       }
207 
208       retval = val;
209 
210       ::free(input_raw);
211     }
212   }
213   if(   retval == "quit"
214      || retval == "exit"
215      || retval == "help"
216      || retval == "version")
217   {
218     retval += "(0)";
219   }
220   return retval;
221 }
222 
223 // We have to wrap exit with our own because Clang has a hard time with
224 // function pointers to functions with special attributes (system exit being marked NORETURN)
myexit(int return_val)225 void myexit(int return_val) {
226   exit(return_val);
227 }
228 
interactive(chaiscript::ChaiScript_Basic & chai)229 void interactive(chaiscript::ChaiScript_Basic& chai)
230 {
231   using_history();
232 
233   for (;;) {
234     std::string input = get_next_command();
235     try {
236       // evaluate input
237       chaiscript::Boxed_Value val = chai.eval(input);
238 
239       //Then, we try to print the result of the evaluation to the user
240       if (!val.get_type_info().bare_equal(chaiscript::user_type<void>())) {
241         try {
242           std::cout << chai.eval<std::function<std::string (const chaiscript::Boxed_Value &bv)> >("to_string")(val) << '\n';
243         }
244         catch (...) {} //If we can't, do nothing
245       }
246     }
247     catch (const chaiscript::exception::eval_error &ee) {
248       std::cout << ee.what();
249       if ( !ee.call_stack.empty() ) {
250         std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")";
251       }
252       std::cout << '\n';
253     }
254     catch (const std::exception &e) {
255       std::cout << e.what();
256       std::cout << '\n';
257     }
258   }
259 }
260 
now()261 double now()
262 {
263   using namespace std::chrono;
264   auto now = high_resolution_clock::now();
265   return duration_cast<duration<double>>(now.time_since_epoch()).count();
266 }
267 
main(int argc,char * argv[])268 int main(int argc, char *argv[])
269 {
270 
271   // Disable deprecation warning for getenv call.
272 #ifdef CHAISCRIPT_MSVC
273 #pragma warning(push)
274 #pragma warning(disable : 4996)
275 #endif
276 
277   const char *usepath = getenv("CHAI_USE_PATH");
278   const char *modulepath = getenv("CHAI_MODULE_PATH");
279 
280 #ifdef CHAISCRIPT_MSVC
281 #pragma warning(pop)
282 #endif
283 
284   std::vector<std::string> usepaths;
285   usepaths.push_back("");
286   if (usepath != nullptr)
287   {
288     usepaths.push_back(usepath);
289   }
290 
291   std::vector<std::string> modulepaths;
292   std::vector<std::string> searchpaths = default_search_paths();
293   modulepaths.insert(modulepaths.end(), searchpaths.begin(), searchpaths.end());
294   modulepaths.push_back("");
295   if (modulepath != nullptr)
296   {
297     modulepaths.push_back(modulepath);
298   }
299 
300   chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(),modulepaths,usepaths);
301 
302   chai.add(chaiscript::fun(&myexit), "exit");
303   chai.add(chaiscript::fun(&myexit), "quit");
304   chai.add(chaiscript::fun(&help), "help");
305   chai.add(chaiscript::fun(&throws_exception), "throws_exception");
306   chai.add(chaiscript::fun(&get_eval_error), "get_eval_error");
307   chai.add(chaiscript::fun(&now), "now");
308 
309   bool eval_error_ok = false;
310   bool boxed_exception_ok = false;
311   bool any_exception_ok = false;
312 
313   for (int i = 0; i < argc; ++i) {
314     if ( i == 0 && argc > 1 ) {
315       ++i;
316     }
317 
318     std::string arg( i != 0 ? argv[i] : "--interactive" );
319 
320     enum { eInteractive
321          , eCommand
322          , eFile
323     } mode = eCommand ;
324 
325     if  ( arg == "-c" || arg == "--command" ) {
326       if ( (i+1) >= argc ) {
327         std::cout << "insufficient input following " << arg << '\n';
328         return EXIT_FAILURE;
329       }
330         arg = argv[++i];
331 
332     } else if ( arg == "-" || arg == "--stdin" ) {
333       arg = "" ;
334       std::string line;
335       while ( std::getline(std::cin, line) ) {
336         arg += line + '\n' ;
337       }
338     } else if ( arg == "-v" || arg == "--version" ) {
339       arg = "print(version())" ;
340     } else if ( arg == "-h" || arg == "--help" ) {
341       arg = "help(-1)";
342     } else if ( arg == "-e" || arg == "--evalerrorok" ) {
343       eval_error_ok = true;
344       continue;
345     } else if ( arg == "--exception" ) {
346       boxed_exception_ok = true;
347       continue;
348     } else if ( arg == "--any-exception" ) {
349       any_exception_ok = true;
350       continue;
351     } else if ( arg == "-i" || arg == "--interactive" ) {
352       mode = eInteractive ;
353     } else if ( arg.find('-') == 0 ) {
354       std::cout << "unrecognised argument " << arg << '\n';
355       return EXIT_FAILURE;
356     } else {
357       mode = eFile;
358     }
359 
360     try {
361       switch ( mode ) {
362         case eInteractive:
363           interactive(chai);
364           break;
365         case eCommand:
366           chai.eval(arg);
367           break;
368         case eFile:
369           chai.eval_file(arg);
370       }
371     }
372     catch (const chaiscript::exception::eval_error &ee) {
373       std::cout << ee.pretty_print();
374       std::cout << '\n';
375 
376       if (!eval_error_ok) {
377         return EXIT_FAILURE;
378       }
379     }
380     catch (const chaiscript::Boxed_Value &e) {
381       std::cout << "Unhandled exception thrown of type " << e.get_type_info().name() << '\n';
382 
383       if (!boxed_exception_ok) {
384         return EXIT_FAILURE;
385       }
386     }
387     catch (const chaiscript::exception::load_module_error &e) {
388       std::cout << "Unhandled module load error\n" << e.what() << '\n';
389     }
390     catch (std::exception &e) {
391       std::cout << "Unhandled standard exception: " << e.what() << '\n';
392       if (!any_exception_ok) {
393         throw;
394       }
395     }
396     catch (...) {
397       std::cout << "Unhandled unknown exception" << '\n';
398       if (!any_exception_ok) {
399         throw;
400       }
401     }
402   }
403 
404   return EXIT_SUCCESS;
405 }
406