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