forked from OctaForge/libcubescript
more feature complete REPL
parent
935b5bf634
commit
522be08eb0
|
@ -1,57 +0,0 @@
|
|||
#ifdef CS_REPL_USE_LIBEDIT
|
||||
#ifndef CS_REPL_HAS_EDIT
|
||||
#define CS_REPL_HAS_EDIT
|
||||
/* use the NetBSD libedit library */
|
||||
|
||||
#include <ostd/string.hh>
|
||||
#include <ostd/maybe.hh>
|
||||
|
||||
#include <histedit.h>
|
||||
|
||||
static EditLine *els = nullptr;
|
||||
static History *elh = nullptr;
|
||||
|
||||
static char *el_prompt(EditLine *el) {
|
||||
void *prompt = nullptr;
|
||||
el_get(el, EL_CLIENTDATA, &prompt);
|
||||
if (!prompt) {
|
||||
return const_cast<char *>("");
|
||||
}
|
||||
return const_cast<char *>(static_cast<CsSvar *>(prompt)->get_value().data());
|
||||
}
|
||||
|
||||
static void init_lineedit(ostd::ConstCharRange progname) {
|
||||
els = el_init(progname.data(), stdin, stdout, stderr);
|
||||
elh = history_init();
|
||||
|
||||
/* init history with reasonable size */
|
||||
HistEvent ev;
|
||||
history(elh, &ev, H_SETSIZE, 1000);
|
||||
el_set(els, EL_HIST, history, elh);
|
||||
|
||||
el_set(els, EL_PROMPT, el_prompt);
|
||||
}
|
||||
|
||||
static ostd::Maybe<ostd::String> read_line(CsSvar *pr) {
|
||||
int count;
|
||||
el_set(els, EL_CLIENTDATA, static_cast<void *>(pr));
|
||||
auto line = el_gets(els, &count);
|
||||
if (count > 0) {
|
||||
ostd::String ret = line;
|
||||
/* libedit keeps the trailing \n */
|
||||
ret.resize(ret.size() - 1);
|
||||
return ostd::move(ret);
|
||||
} else if (!count) {
|
||||
return ostd::String();
|
||||
}
|
||||
return ostd::nothing;
|
||||
}
|
||||
|
||||
static void add_history(ostd::ConstCharRange line) {
|
||||
HistEvent ev;
|
||||
/* backed by ostd::String so it's terminated */
|
||||
history(elh, &ev, H_ENTER, line.data());
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
180
tools/repl.cc
180
tools/repl.cc
|
@ -9,7 +9,7 @@
|
|||
|
||||
using namespace cscript;
|
||||
|
||||
ostd::ConstCharRange version = "CubeScript 0.0.1 (REPL mode)";
|
||||
ostd::ConstCharRange version = "CubeScript 0.0.1";
|
||||
|
||||
/* util */
|
||||
|
||||
|
@ -43,7 +43,6 @@ static ostd::ConstCharRange get_complete_cmd(ostd::ConstCharRange buf) {
|
|||
#endif /* CS_REPL_HAS_COMPLETE */
|
||||
|
||||
#ifdef CS_REPL_HAS_HINTS
|
||||
|
||||
static inline ostd::ConstCharRange get_arg_type(char arg) {
|
||||
switch (arg) {
|
||||
case 'i':
|
||||
|
@ -156,14 +155,33 @@ static CsCommand *get_hint_cmd(ostd::ConstCharRange buf) {
|
|||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif /* CS_REPL_HAS_HINTS */
|
||||
|
||||
#include "tools/edit_linenoise.hh"
|
||||
#include "tools/edit_libedit.hh"
|
||||
#include "tools/edit_readline.hh"
|
||||
#include "tools/edit_fallback.hh"
|
||||
|
||||
/* usage */
|
||||
|
||||
void print_usage(ostd::ConstCharRange progname, bool err) {
|
||||
auto &s = err ? ostd::err : ostd::out;
|
||||
s.writeln(
|
||||
"Usage: ", progname, " [options] [file]\n"
|
||||
"Options:\n"
|
||||
" -e str run string \"str\"\n"
|
||||
" -i enter interactive mode after the above\n"
|
||||
" -v show version information\n"
|
||||
" -h show this message\n"
|
||||
" -- stop handling options\n"
|
||||
" - execute stdin and stop handling options"
|
||||
);
|
||||
s.flush();
|
||||
}
|
||||
|
||||
void print_version() {
|
||||
ostd::writeln(version);
|
||||
}
|
||||
|
||||
struct InterruptedException {
|
||||
};
|
||||
|
||||
|
@ -178,11 +196,17 @@ static void do_sigint(int n) {
|
|||
}
|
||||
}
|
||||
|
||||
static void do_call(CsState &cs, ostd::ConstCharRange line) {
|
||||
static void do_call(CsState &cs, ostd::ConstCharRange line, bool file = false) {
|
||||
CsValue ret;
|
||||
signal(SIGINT, do_sigint);
|
||||
try {
|
||||
cs.run(line, ret);
|
||||
if (file) {
|
||||
if (!cs.run_file(line, ret)) {
|
||||
ostd::err.writeln("cannot read file: ", line);
|
||||
}
|
||||
} else {
|
||||
cs.run(line, ret);
|
||||
}
|
||||
} catch (InterruptedException) {
|
||||
signal(SIGINT, SIG_DFL);
|
||||
ostd::writeln("<execution interrupted>");
|
||||
|
@ -203,24 +227,7 @@ static void do_tty(CsState &cs) {
|
|||
do_exit = true;
|
||||
});
|
||||
|
||||
cs.new_command("exec", "sb", [&cs](CsValueRange args, CsValue &res) {
|
||||
auto file = args[0].get_strr();
|
||||
bool ret = cs.run_file(file);
|
||||
if (!ret) {
|
||||
if (args[1].get_int()) {
|
||||
cs.get_err().writefln("could not run file \"%s\"", file);
|
||||
}
|
||||
res.set_int(0);
|
||||
} else {
|
||||
res.set_int(1);
|
||||
}
|
||||
});
|
||||
|
||||
cs.new_command("echo", "C", [&cs](CsValueRange args, CsValue &) {
|
||||
cs.get_out().writeln(args[0].get_strr());
|
||||
});
|
||||
|
||||
ostd::writeln(version);
|
||||
ostd::writeln(version, " (REPL mode)");
|
||||
for (;;) {
|
||||
auto line = read_line(prompt);
|
||||
if (!line) {
|
||||
|
@ -246,14 +253,127 @@ static void do_tty(CsState &cs) {
|
|||
}
|
||||
}
|
||||
|
||||
int main(int, char **argv) {
|
||||
int main(int argc, char **argv) {
|
||||
CsState cs;
|
||||
gcs = &cs;
|
||||
cs.init_libs();
|
||||
if (stdin_is_tty()) {
|
||||
init_lineedit(argv[0]);
|
||||
do_tty(cs);
|
||||
} else {
|
||||
ostd::err.writeln("Only interactive mode is supported for now.");
|
||||
|
||||
cs.new_command("exec", "sb", [&cs](auto args, auto &res) {
|
||||
auto file = args[0].get_strr();
|
||||
bool ret = cs.run_file(file);
|
||||
if (!ret) {
|
||||
if (args[1].get_int()) {
|
||||
cs.get_err().writefln("could not run file \"%s\"", file);
|
||||
}
|
||||
res.set_int(0);
|
||||
} else {
|
||||
res.set_int(1);
|
||||
}
|
||||
});
|
||||
|
||||
cs.new_command("echo", "C", [&cs](auto args, auto &) {
|
||||
cs.get_out().writeln(args[0].get_strr());
|
||||
});
|
||||
|
||||
int firstarg = 0;
|
||||
bool has_inter = false, has_ver = false, has_help = false, has_str = false;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if (argv[i][0] != '-') {
|
||||
firstarg = i;
|
||||
goto endargs;
|
||||
}
|
||||
switch (argv[i][1]) {
|
||||
case '-':
|
||||
if (argv[i][2] != '\0') {
|
||||
firstarg = -1;
|
||||
goto endargs;
|
||||
}
|
||||
firstarg = (argv[i + 1] != nullptr) ? (i + 1) : 0;
|
||||
goto endargs;
|
||||
case '\0':
|
||||
firstarg = i;
|
||||
goto endargs;
|
||||
case 'i':
|
||||
if (argv[i][2] != '\0') {
|
||||
firstarg = -1;
|
||||
goto endargs;
|
||||
}
|
||||
has_inter = true;
|
||||
break;
|
||||
case 'v':
|
||||
if (argv[i][2] != '\0') {
|
||||
firstarg = -1;
|
||||
goto endargs;
|
||||
}
|
||||
has_ver = true;
|
||||
break;
|
||||
case 'h':
|
||||
if (argv[i][2] != '\0') {
|
||||
firstarg = -1;
|
||||
goto endargs;
|
||||
}
|
||||
has_help = true;
|
||||
break;
|
||||
case 'e':
|
||||
has_str = true;
|
||||
if (argv[i][2] == '\0') {
|
||||
++i;
|
||||
if (!argv[i]) {
|
||||
firstarg = -1;
|
||||
goto endargs;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
firstarg = -1;
|
||||
goto endargs;
|
||||
}
|
||||
}
|
||||
endargs:
|
||||
if (firstarg < 0) {
|
||||
print_usage(argv[0], true);
|
||||
return 1;
|
||||
}
|
||||
if (has_ver && !has_inter) {
|
||||
print_version();
|
||||
}
|
||||
if (has_help) {
|
||||
print_usage(argv[0], false);
|
||||
return 0;
|
||||
}
|
||||
for (int i = 1; i < ((firstarg > 0) ? firstarg : argc); ++i) {
|
||||
switch (argv[i][1]) {
|
||||
case 'e': {
|
||||
auto str = argv[i] + 2;
|
||||
if (*str == '\0') {
|
||||
str = argv[++i];
|
||||
}
|
||||
do_call(cs, str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (firstarg) {
|
||||
do_call(cs, argv[firstarg], true);
|
||||
}
|
||||
if (!firstarg && !has_str && !has_ver) {
|
||||
if (stdin_is_tty()) {
|
||||
init_lineedit(argv[0]);
|
||||
do_tty(cs);
|
||||
return 0;
|
||||
} else {
|
||||
ostd::String str;
|
||||
for (char c = '\0'; (c = ostd::in.getchar()) != EOF;) {
|
||||
str += c;
|
||||
}
|
||||
do_call(cs, str);
|
||||
}
|
||||
}
|
||||
if (has_inter) {
|
||||
if (stdin_is_tty()) {
|
||||
init_lineedit(argv[0]);
|
||||
do_tty(cs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue