int main(int argc, char **argv) { struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"legal", no_argument, NULL, 'l'}, {"verbose", no_argument, NULL, 'v'}, {"quiet", no_argument, NULL, 'q'}, {"repl", no_argument, NULL, 'r'}, {"static-fns", no_argument, NULL, 's'}, {"elide-asserts", no_argument, NULL, 'a'}, {"cache", required_argument, NULL, 'k'}, {"eval", required_argument, NULL, 'e'}, {"theme", required_argument, NULL, 't'}, {"classpath", required_argument, NULL, 'c'}, {"auto-cache", no_argument, NULL, 'K'}, {"init", required_argument, NULL, 'i'}, {"main", required_argument, NULL, 'm'}, // development options {"javascript", no_argument, NULL, 'j'}, {"out", required_argument, NULL, 'o'}, {0, 0, 0, 0} }; int opt, option_index; while ((opt = getopt_long(argc, argv, "h?lvrsak:je:t:c:o:Ki:qm:", long_options, &option_index)) != -1) { switch (opt) { case 'h': usage(argv[0]); exit(0); case 'l': legal(); return 0; case 'v': verbose = true; break; case 'q': quiet = true; break; case 'r': repl = true; break; case 's': static_fns = true; break; case 'a': elide_asserts = true; break; case 'k': cache_path = argv[optind - 1]; break; case 'K': cache_path = ".planck_cache"; { char *path_copy = strdup(cache_path); char *dir = dirname(path_copy); if (mkdir_p(dir) < 0) { fprintf(stderr, "Could not create %s: %s\n", cache_path, strerror(errno)); } free(path_copy); } break; case 'j': javascript = true; break; case 'e': num_scripts += 1; scripts = realloc(scripts, num_scripts * sizeof(struct script)); scripts[num_scripts - 1].type = "text"; scripts[num_scripts - 1].expression = true; scripts[num_scripts - 1].source = argv[optind - 1]; break; case 'i': num_scripts += 1; scripts = realloc(scripts, num_scripts * sizeof(struct script)); scripts[num_scripts - 1].type = "path"; scripts[num_scripts - 1].expression = false; scripts[num_scripts - 1].source = argv[optind - 1]; break; case 'm': main_ns_name = argv[optind - 1]; case 't': theme = argv[optind - 1]; break; case 'c': { char *classpath = argv[optind - 1]; char *source = strtok(classpath, ":"); while (source != NULL) { char *type = "src"; if (str_has_suffix(source, ".jar") == 0) { type = "jar"; } num_src_paths += 1; src_paths = realloc(src_paths, num_src_paths * sizeof(struct src_path)); src_paths[num_src_paths - 1].type = type; src_paths[num_src_paths - 1].path = strdup(source); source = strtok(NULL, ":"); } break; } case 'o': out_path = argv[optind - 1]; break; case '?': usage(argv[0]); exit(1); default: printf("unhandled argument: %c\n", opt); } } int num_rest_args = 0; char **rest_args = NULL; if (optind < argc) { num_rest_args = argc - optind; rest_args = malloc((argc - optind) * sizeof(char*)); int i = 0; while (optind < argc) { rest_args[i++] = argv[optind++]; } } if (num_scripts == 0 && main_ns_name == NULL && num_rest_args == 0) { repl = true; } if (main_ns_name != NULL && repl) { printf("Only one main-opt can be specified."); } JSGlobalContextRef ctx = JSGlobalContextCreate(NULL); JSStringRef nameRef = JSStringCreateWithUTF8CString("planck"); JSGlobalContextSetName(ctx, nameRef); evaluate_script(ctx, "var global = this;", "<init>"); register_global_function(ctx, "AMBLY_IMPORT_SCRIPT", function_import_script); bootstrap(ctx, out_path); register_global_function(ctx, "PLANCK_CONSOLE_LOG", function_console_log); register_global_function(ctx, "PLANCK_CONSOLE_ERROR", function_console_error); evaluate_script(ctx, "var console = {};"\ "console.log = PLANCK_CONSOLE_LOG;"\ "console.error = PLANCK_CONSOLE_ERROR;", "<init>"); evaluate_script(ctx, "var PLANCK_VERSION = \"" PLANCK_VERSION "\";", "<init>"); // require app namespaces evaluate_script(ctx, "goog.require('planck.repl');", "<init>"); // without this things won't work evaluate_script(ctx, "var window = global;", "<init>"); register_global_function(ctx, "PLANCK_READ_FILE", function_read_file); register_global_function(ctx, "PLANCK_LOAD", function_load); register_global_function(ctx, "PLANCK_LOAD_DEPS_CLJS_FILES", function_load_deps_cljs_files); register_global_function(ctx, "PLANCK_CACHE", function_cache); register_global_function(ctx, "PLANCK_EVAL", function_eval); register_global_function(ctx, "PLANCK_GET_TERM_SIZE", function_get_term_size); register_global_function(ctx, "PLANCK_PRINT_FN", function_print_fn); register_global_function(ctx, "PLANCK_PRINT_ERR_FN", function_print_err_fn); register_global_function(ctx, "PLANCK_SET_EXIT_VALUE", function_set_exit_value); is_tty = isatty(STDIN_FILENO) == 1; register_global_function(ctx, "PLANCK_RAW_READ_STDIN", function_raw_read_stdin); register_global_function(ctx, "PLANCK_RAW_WRITE_STDOUT", function_raw_write_stdout); register_global_function(ctx, "PLANCK_RAW_FLUSH_STDOUT", function_raw_flush_stdout); register_global_function(ctx, "PLANCK_RAW_WRITE_STDERR", function_raw_write_stderr); register_global_function(ctx, "PLANCK_RAW_FLUSH_STDERR", function_raw_flush_stderr); { JSValueRef arguments[num_rest_args]; for (int i = 0; i < num_rest_args; i++) { arguments[i] = c_string_to_value(ctx, rest_args[i]); } JSValueRef args_ref = JSObjectMakeArray(ctx, num_rest_args, arguments, NULL); JSValueRef global_obj = JSContextGetGlobalObject(ctx); JSStringRef prop = JSStringCreateWithUTF8CString("PLANCK_INITIAL_COMMAND_LINE_ARGS"); JSObjectSetProperty(ctx, JSValueToObject(ctx, global_obj, NULL), prop, args_ref, kJSPropertyAttributeNone, NULL); JSStringRelease(prop); } evaluate_script(ctx, "cljs.core.set_print_fn_BANG_.call(null,PLANCK_PRINT_FN);", "<init>"); evaluate_script(ctx, "cljs.core.set_print_err_fn_BANG_.call(null,PLANCK_PRINT_ERR_FN);", "<init>"); char *elide_script = str_concat("cljs.core._STAR_assert_STAR_ = ", elide_asserts ? "false" : "true"); evaluate_script(ctx, elide_script, "<init>"); free(elide_script); { JSValueRef arguments[4]; arguments[0] = JSValueMakeBoolean(ctx, repl); arguments[1] = JSValueMakeBoolean(ctx, verbose); JSValueRef cache_path_ref = NULL; if (cache_path != NULL) { JSStringRef cache_path_str = JSStringCreateWithUTF8CString(cache_path); cache_path_ref = JSValueMakeString(ctx, cache_path_str); } arguments[2] = cache_path_ref; arguments[3] = JSValueMakeBoolean(ctx, static_fns); JSValueRef ex = NULL; JSObjectCallAsFunction(ctx, get_function(ctx, "planck.repl", "init"), JSContextGetGlobalObject(ctx), 4, arguments, &ex); debug_print_value("planck.repl/init", ctx, ex); } if (repl) { evaluate_source(ctx, "text", "(require '[planck.repl :refer-macros [apropos dir find-doc doc source pst]])", true, false, "cljs.user", "dumb"); } evaluate_script(ctx, "goog.provide('cljs.user');", "<init>"); evaluate_script(ctx, "goog.require('cljs.core');", "<init>"); evaluate_script(ctx, "cljs.core._STAR_assert_STAR_ = true;", "<init>"); // Process init arguments for (int i = 0; i < num_scripts; i++) { // TODO: exit if not successfull evaluate_source(ctx, scripts[i].type, scripts[i].source, scripts[i].expression, false, NULL, theme); } // Process main arguments if (main_ns_name != NULL) { run_main_in_ns(ctx, main_ns_name, num_rest_args, rest_args); } else if (!repl && num_rest_args > 0) { char *path = rest_args[0]; struct script script; if (strcmp(path, "-") == 0) { char *source = read_all(stdin); script.type = "text"; script.source = source; script.expression = false; } else { script.type = "path"; script.source = path; script.expression = false; } evaluate_source(ctx, script.type, script.source, script.expression, false, NULL, theme); } else if (repl) { if (!quiet) { banner(); } char *home = getenv("HOME"); char *history_path = NULL; if (home != NULL) { char history_name[] = ".planck_history"; int len = strlen(home) + strlen(history_name) + 2; history_path = malloc(len * sizeof(char)); snprintf(history_path, len, "%s/%s", home, history_name); linenoiseHistoryLoad(history_path); } char *prompt = javascript ? " > " : " => "; char *line; while ((line = linenoise(prompt)) != NULL) { if (javascript) { JSValueRef res = evaluate_script(ctx, line, "<stdin>"); print_value("", ctx, res); } else { evaluate_source(ctx, "text", line, true, true, "cljs.user", theme); } linenoiseHistoryAdd(line); if (history_path != NULL) { linenoiseHistorySave(history_path); } free(line); } } return exit_value; }
bool process_line(repl_t *repl, char *input_line) { // Accumulate input lines if (repl->input == NULL) { repl->input = input_line; } else { repl->input = realloc(repl->input, (strlen(repl->input) + strlen(input_line) + 2) * sizeof(char)); sprintf(repl->input + strlen(repl->input), "\n%s", input_line); } repl->num_previous_lines += 1; repl->previous_lines = realloc(repl->previous_lines, repl->num_previous_lines * sizeof(char *)); repl->previous_lines[repl->num_previous_lines - 1] = strdup(input_line); // Check for explicit exit if (strcmp(repl->input, ":cljs/quit") == 0 || strcmp(repl->input, "quit") == 0 || strcmp(repl->input, "exit") == 0) { if (repl->session_id == 0) { exit_value = EXIT_SUCCESS_INTERNAL; } return true; } // Add input line to history if (repl->history_path != NULL && !is_whitespace(repl->input)) { linenoiseHistoryAdd(input_line); linenoiseHistorySave(repl->history_path); } // Check if we now have readable forms // and if so, evaluate them bool done = false; char *balance_text = NULL; while (!done) { if ((balance_text = cljs_is_readable(repl->input)) != NULL) { repl->input[strlen(repl->input) - strlen(balance_text)] = '\0'; if (!is_whitespace(repl->input)) { // Guard against empty string being read return_termsize = !config.dumb_terminal; if (repl->session_id == 0) { set_int_handler(); } // TODO: set exit value const char *theme = repl->session_id == 0 ? config.theme : "dumb"; evaluate_source("text", repl->input, true, true, repl->current_ns, theme, true, repl->session_id); if (repl->session_id == 0) { clear_int_handler(); } return_termsize = false; if (exit_value != 0) { free(repl->input); return true; } } else { printf("\n"); } // Now that we've evaluated the input, reset for next round free(repl->input); repl->input = balance_text; empty_previous_lines(repl); // Fetch the current namespace and use it to set the prompt free(repl->current_ns); free(repl->current_prompt); repl->current_ns = cljs_get_current_ns(); repl->current_prompt = form_prompt(repl->current_ns, false); if (is_whitespace(balance_text)) { done = true; free(repl->input); repl->input = NULL; } } else { // Prepare for reading non-1st of input with secondary prompt if (repl->history_path != NULL) { repl->indent_space_count = cljs_indent_space_count(repl->input); } free(repl->current_prompt); repl->current_prompt = form_prompt(repl->current_ns, true); done = true; } } return false; }
int main(int argc, char **argv) { config.verbose = false; config.quiet = false; config.repl = false; config.javascript = false; config.static_fns = false; config.elide_asserts = false; config.cache_path = NULL; config.theme = "light"; config.dumb_terminal = false; config.out_path = NULL; config.num_src_paths = 0; config.src_paths = NULL; config.num_scripts = 0; config.scripts = NULL; config.main_ns_name = NULL; struct option long_options[] = { {"help", no_argument, NULL, 'h'}, {"legal", no_argument, NULL, 'l'}, {"verbose", no_argument, NULL, 'v'}, {"quiet", no_argument, NULL, 'q'}, {"repl", no_argument, NULL, 'r'}, {"static-fns", no_argument, NULL, 's'}, {"elide-asserts", no_argument, NULL, 'a'}, {"cache", required_argument, NULL, 'k'}, {"eval", required_argument, NULL, 'e'}, {"theme", required_argument, NULL, 't'}, {"dumb-terminal", no_argument, NULL, 'd'}, {"classpath", required_argument, NULL, 'c'}, {"auto-cache", no_argument, NULL, 'K'}, {"init", required_argument, NULL, 'i'}, {"main", required_argument, NULL, 'm'}, // development options {"javascript", no_argument, NULL, 'j'}, {"out", required_argument, NULL, 'o'}, {0, 0, 0, 0} }; int opt, option_index; while ((opt = getopt_long(argc, argv, "h?lvrsak:je:t:dc:o:Ki:qm:", long_options, &option_index)) != -1) { switch (opt) { case 'h': usage(argv[0]); exit(0); case 'l': legal(); return 0; case 'v': config.verbose = true; break; case 'q': config.quiet = true; break; case 'r': config.repl = true; break; case 's': config.static_fns = true; break; case 'a': config.elide_asserts = true; break; case 'k': config.cache_path = argv[optind - 1]; break; case 'K': config.cache_path = ".planck_cache"; { char *path_copy = strdup(config.cache_path); char *dir = dirname(path_copy); if (mkdir_p(dir) < 0) { fprintf(stderr, "Could not create %s: %s\n", config.cache_path, strerror(errno)); } free(path_copy); } break; case 'j': config.javascript = true; break; case 'e': config.num_scripts += 1; config.scripts = realloc(config.scripts, config.num_scripts * sizeof(struct script)); config.scripts[config.num_scripts - 1].type = "text"; config.scripts[config.num_scripts - 1].expression = true; config.scripts[config.num_scripts - 1].source = argv[optind - 1]; break; case 'i': config.num_scripts += 1; config.scripts = realloc(config.scripts, config.num_scripts * sizeof(struct script)); config.scripts[config.num_scripts - 1].type = "path"; config.scripts[config.num_scripts - 1].expression = false; config.scripts[config.num_scripts - 1].source = argv[optind - 1]; break; case 'm': config.main_ns_name = argv[optind - 1]; break; case 't': config.theme = argv[optind - 1]; break; case 'd': config.dumb_terminal = true; break; case 'c': { char *classpath = argv[optind - 1]; char *source = strtok(classpath, ":"); while (source != NULL) { char *type = "src"; if (str_has_suffix(source, ".jar") == 0) { type = "jar"; } config.num_src_paths += 1; config.src_paths = realloc(config.src_paths, config.num_src_paths * sizeof(struct src_path)); config.src_paths[config.num_src_paths - 1].type = type; config.src_paths[config.num_src_paths - 1].path = strcmp(type, "jar") == 0 ? strdup(source) : ensure_trailing_slash(source); source = strtok(NULL, ":"); } break; } case 'o': config.out_path = ensure_trailing_slash(argv[optind - 1]); break; case '?': usage(argv[0]); exit(1); default: printf("unhandled argument: %c\n", opt); } } if (config.dumb_terminal) { config.theme = "dumb"; } config.num_rest_args = 0; config.rest_args = NULL; if (optind < argc) { config.num_rest_args = argc - optind; config.rest_args = malloc((argc - optind) * sizeof(char*)); int i = 0; while (optind < argc) { config.rest_args[i++] = argv[optind++]; } } if (config.num_scripts == 0 && config.main_ns_name == NULL && config.num_rest_args == 0) { config.repl = true; } if (config.main_ns_name != NULL && config.repl) { printf("Only one main-opt can be specified.\n"); exit(1); } config.is_tty = isatty(STDIN_FILENO) == 1; JSGlobalContextRef ctx = JSGlobalContextCreate(NULL); global_ctx = ctx; cljs_engine_init(ctx); // Process init arguments for (int i = 0; i < config.num_scripts; i++) { // TODO: exit if not successfull struct script script = config.scripts[i]; evaluate_source(ctx, script.type, script.source, script.expression, false, NULL, config.theme, true); } // Process main arguments if (config.main_ns_name != NULL) { run_main_in_ns(ctx, config.main_ns_name, config.num_rest_args, config.rest_args); } else if (!config.repl && config.num_rest_args > 0) { char *path = config.rest_args[0]; struct script script; if (strcmp(path, "-") == 0) { char *source = read_all(stdin); script.type = "text"; script.source = source; script.expression = false; } else { script.type = "path"; script.source = path; script.expression = false; } evaluate_source(ctx, script.type, script.source, script.expression, false, NULL, config.theme, true); } else if (config.repl) { if (!config.quiet) { banner(); } run_repl(ctx); } return exit_value; }