forked from OctaForge/libcubescript
re-expose command and add hints/completion for now at least with linenoise
parent
0ff65e5ed4
commit
6aca518614
4
Makefile
4
Makefile
|
@ -29,8 +29,8 @@ $(LIBCS_LIB): $(LIBCS_OBJ)
|
|||
|
||||
repl: $(LIBCS_LIB) tools/repl.cc tools/linenoise.cc tools/linenoise.hh
|
||||
$(CXX) $(CXXFLAGS) $(LIBCS_CXXFLAGS) $(LDFLAGS) \
|
||||
-Itools -DCS_REPL_USE_LINENOISE tools/linenoise.cc \
|
||||
tools/repl.cc -o repl $(LIBCS_LIB)
|
||||
-Itools -DCS_REPL_USE_LINENOISE -DCS_REPL_HAS_COMPLETE -DCS_REPL_HAS_HINTS \
|
||||
tools/linenoise.cc tools/repl.cc -o repl $(LIBCS_LIB)
|
||||
|
||||
clean:
|
||||
rm -f $(LIBCS_LIB) $(LIBCS_OBJ)
|
||||
|
|
12
cs_gen.cc
12
cs_gen.cc
|
@ -517,9 +517,9 @@ lookupid:
|
|||
if (prevargs >= MaxResults) {
|
||||
gs.code.push(CODE_ENTER);
|
||||
}
|
||||
char const *fmt = static_cast<Command *>(id)->cargs;
|
||||
for (; *fmt; fmt++) {
|
||||
switch (*fmt) {
|
||||
auto fmt = static_cast<CsCommand *>(id)->get_args();
|
||||
for (char c: fmt) {
|
||||
switch (c) {
|
||||
case 'S':
|
||||
gs.gen_str();
|
||||
numargs++;
|
||||
|
@ -1204,8 +1204,8 @@ noid:
|
|||
case ID_COMMAND: {
|
||||
int comtype = CODE_COM, fakeargs = 0;
|
||||
bool rep = false;
|
||||
char const *fmt = static_cast<Command *>(id)->cargs;
|
||||
for (; *fmt; fmt++) {
|
||||
auto fmt = static_cast<CsCommand *>(id)->get_args();
|
||||
for (; !fmt.empty(); ++fmt) {
|
||||
switch (*fmt) {
|
||||
case 'S':
|
||||
case 's':
|
||||
|
@ -1223,7 +1223,7 @@ noid:
|
|||
ostd::ConstCharRange(), *fmt == 's'
|
||||
);
|
||||
fakeargs++;
|
||||
} else if (!fmt[1]) {
|
||||
} else if (fmt.size() == 1) {
|
||||
int numconc = 1;
|
||||
while ((numargs + numconc) < MaxArguments) {
|
||||
more = compilearg(
|
||||
|
|
28
cs_vm.cc
28
cs_vm.cc
|
@ -74,7 +74,7 @@ static inline bool cs_has_cmd_cb(CsIdent *id) {
|
|||
if (!id->is_command() && !id->is_special()) {
|
||||
return false;
|
||||
}
|
||||
Command *cb = static_cast<Command *>(id);
|
||||
CsCommand *cb = static_cast<CsCommand *>(id);
|
||||
return !!cb->cb_cftv;
|
||||
}
|
||||
|
||||
|
@ -347,12 +347,12 @@ void CsValue::copy_arg(CsValue &r) const {
|
|||
}
|
||||
|
||||
static inline void callcommand(
|
||||
CsState &cs, Command *id, CsValue *args, CsValue &res, int numargs,
|
||||
CsState &cs, CsCommand *id, CsValue *args, CsValue &res, int numargs,
|
||||
bool lookup = false
|
||||
) {
|
||||
int i = -1, fakeargs = 0;
|
||||
bool rep = false;
|
||||
for (char const *fmt = id->cargs; *fmt; fmt++) {
|
||||
for (auto fmt = id->get_args(); !fmt.empty(); ++fmt) {
|
||||
switch (*fmt) {
|
||||
case 'i':
|
||||
if (++i >= numargs) {
|
||||
|
@ -611,7 +611,7 @@ static inline int cs_get_lookupu_type(
|
|||
arg.cleanup();
|
||||
arg.set_null();
|
||||
CsValue buf[MaxArguments];
|
||||
callcommand(cs, static_cast<Command *>(id), buf, arg, 0, true);
|
||||
callcommand(cs, static_cast<CsCommand *>(id), buf, arg, 0, true);
|
||||
force_arg(arg, op & CODE_RET_MASK);
|
||||
return -2; /* ignore */
|
||||
}
|
||||
|
@ -1381,10 +1381,12 @@ static ostd::Uint32 *runcode(CsState &cs, ostd::Uint32 *code, CsValue &result) {
|
|||
case CODE_COM | RET_STR:
|
||||
case CODE_COM | RET_FLOAT:
|
||||
case CODE_COM | RET_INT: {
|
||||
Command *id = static_cast<Command *>(cs.identmap[op >> 8]);
|
||||
int offset = numargs - id->numargs;
|
||||
CsCommand *id = static_cast<CsCommand *>(cs.identmap[op >> 8]);
|
||||
int offset = numargs - id->get_num_args();
|
||||
result.force_null();
|
||||
id->cb_cftv(CsValueRange(args + offset, id->numargs), result);
|
||||
id->cb_cftv(
|
||||
CsValueRange(args + offset, id->get_num_args()), result
|
||||
);
|
||||
force_arg(result, op & CODE_RET_MASK);
|
||||
free_args(args, numargs, offset);
|
||||
continue;
|
||||
|
@ -1394,7 +1396,7 @@ static ostd::Uint32 *runcode(CsState &cs, ostd::Uint32 *code, CsValue &result) {
|
|||
case CODE_COMV | RET_STR:
|
||||
case CODE_COMV | RET_FLOAT:
|
||||
case CODE_COMV | RET_INT: {
|
||||
Command *id = static_cast<Command *>(cs.identmap[op >> 13]);
|
||||
CsCommand *id = static_cast<CsCommand *>(cs.identmap[op >> 13]);
|
||||
int callargs = (op >> 8) & 0x1F, offset = numargs - callargs;
|
||||
result.force_null();
|
||||
id->cb_cftv(ostd::iter(&args[offset], callargs), result);
|
||||
|
@ -1406,7 +1408,7 @@ static ostd::Uint32 *runcode(CsState &cs, ostd::Uint32 *code, CsValue &result) {
|
|||
case CODE_COMC | RET_STR:
|
||||
case CODE_COMC | RET_FLOAT:
|
||||
case CODE_COMC | RET_INT: {
|
||||
Command *id = static_cast<Command *>(cs.identmap[op >> 13]);
|
||||
CsCommand *id = static_cast<CsCommand *>(cs.identmap[op >> 13]);
|
||||
int callargs = (op >> 8) & 0x1F, offset = numargs - callargs;
|
||||
result.force_null();
|
||||
{
|
||||
|
@ -1559,7 +1561,7 @@ noid:
|
|||
case ID_COMMAND:
|
||||
idarg.cleanup();
|
||||
callcommand(
|
||||
cs, static_cast<Command *>(id), &args[offset],
|
||||
cs, static_cast<CsCommand *>(id), &args[offset],
|
||||
result, callargs
|
||||
);
|
||||
force_arg(result, op & CODE_RET_MASK);
|
||||
|
@ -1673,16 +1675,16 @@ void CsState::run_ret(CsIdent *id, CsValueRange args, CsValue &ret) {
|
|||
}
|
||||
/* fallthrough */
|
||||
case CsIdentType::command:
|
||||
if (nargs < static_cast<Command *>(id)->numargs) {
|
||||
if (nargs < static_cast<CsCommand *>(id)->get_num_args()) {
|
||||
CsValue buf[MaxArguments];
|
||||
memcpy(buf, args.data(), args.size() * sizeof(CsValue));
|
||||
callcommand(
|
||||
*this, static_cast<Command *>(id), buf, ret,
|
||||
*this, static_cast<CsCommand *>(id), buf, ret,
|
||||
nargs, false
|
||||
);
|
||||
} else {
|
||||
callcommand(
|
||||
*this, static_cast<Command *>(id), args.data(),
|
||||
*this, static_cast<CsCommand *>(id), args.data(),
|
||||
ret, nargs, false
|
||||
);
|
||||
}
|
||||
|
|
12
cs_vm.hh
12
cs_vm.hh
|
@ -37,18 +37,6 @@ static inline int cs_vtype_to_int(CsValueType v) {
|
|||
return cs_valtypet[int(v)];
|
||||
}
|
||||
|
||||
struct Command: CsIdent {
|
||||
char *cargs;
|
||||
ostd::Uint32 argmask;
|
||||
int numargs;
|
||||
CmdFunc cb_cftv;
|
||||
|
||||
Command(
|
||||
int type, ostd::ConstCharRange name, ostd::ConstCharRange args,
|
||||
ostd::Uint32 argmask, int numargs, CmdFunc func
|
||||
);
|
||||
};
|
||||
|
||||
enum {
|
||||
CODE_START = 0,
|
||||
CODE_OFFSET,
|
||||
|
|
|
@ -96,13 +96,12 @@ CsAlias::CsAlias(ostd::ConstCharRange name, CsValue const &v, int fl):
|
|||
p_acode(nullptr), p_astack(nullptr), p_val(v)
|
||||
{}
|
||||
|
||||
Command::Command(
|
||||
CsCommand::CsCommand(
|
||||
int tp, ostd::ConstCharRange name, ostd::ConstCharRange args,
|
||||
ostd::Uint32 amask, int nargs, CmdFunc f
|
||||
int nargs, CmdFunc f
|
||||
):
|
||||
CsIdent(CsIdentType::command, name, 0),
|
||||
cargs(cs_dup_ostr(args)),
|
||||
argmask(amask), numargs(nargs), cb_cftv(ostd::move(f))
|
||||
p_cargs(cs_dup_ostr(args)), p_numargs(nargs), cb_cftv(ostd::move(f))
|
||||
{
|
||||
p_type = tp;
|
||||
}
|
||||
|
@ -129,6 +128,20 @@ bool CsIdent::is_command() const {
|
|||
return get_type() == CsIdentType::command;
|
||||
}
|
||||
|
||||
CsCommand *CsIdent::get_command() {
|
||||
if (!is_command()) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<CsCommand *>(this);
|
||||
}
|
||||
|
||||
CsCommand const *CsIdent::get_command() const {
|
||||
if (!is_command()) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<CsCommand const *>(this);
|
||||
}
|
||||
|
||||
bool CsIdent::is_special() const {
|
||||
return get_type() == CsIdentType::special;
|
||||
}
|
||||
|
@ -273,7 +286,7 @@ CsState::~CsState() {
|
|||
a->get_value().force_null();
|
||||
a->clean_code();
|
||||
} else if (i->is_command() || i->is_special()) {
|
||||
delete[] static_cast<Command *>(i)->cargs;
|
||||
delete[] static_cast<CsCommand *>(i)->p_cargs;
|
||||
}
|
||||
delete i;
|
||||
}
|
||||
|
@ -1102,7 +1115,6 @@ static bool cs_add_command(
|
|||
CsState &cs, ostd::ConstCharRange name, ostd::ConstCharRange args,
|
||||
CmdFunc func, int type = ID_COMMAND
|
||||
) {
|
||||
ostd::Uint32 argmask = 0;
|
||||
int nargs = 0;
|
||||
for (ostd::ConstCharRange fmt(args); !fmt.empty(); ++fmt) {
|
||||
switch (*fmt) {
|
||||
|
@ -1114,18 +1126,12 @@ static bool cs_add_command(
|
|||
case 'T':
|
||||
case 'E':
|
||||
case 'N':
|
||||
case 'D':
|
||||
if (nargs < MaxArguments) {
|
||||
nargs++;
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
case 's':
|
||||
case 'e':
|
||||
case 'r':
|
||||
case '$':
|
||||
if (nargs < MaxArguments) {
|
||||
argmask |= 1 << nargs;
|
||||
nargs++;
|
||||
}
|
||||
break;
|
||||
|
@ -1134,7 +1140,7 @@ static bool cs_add_command(
|
|||
case '3':
|
||||
case '4':
|
||||
if (nargs < MaxArguments) {
|
||||
fmt.push_front_n(fmt.front() - '0' + 1);
|
||||
fmt.push_front_n(*fmt - '0' + 1);
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
|
@ -1148,7 +1154,7 @@ static bool cs_add_command(
|
|||
return false;
|
||||
}
|
||||
}
|
||||
cs.add_ident<Command>(type, name, args, argmask, nargs, ostd::move(func));
|
||||
cs.add_ident<CsCommand>(type, name, args, nargs, ostd::move(func));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -130,6 +130,7 @@ struct CsIvar;
|
|||
struct CsFvar;
|
||||
struct CsSvar;
|
||||
struct CsAlias;
|
||||
struct CsCommand;
|
||||
|
||||
struct OSTD_EXPORT CsIdent {
|
||||
friend struct CsState;
|
||||
|
@ -151,6 +152,9 @@ struct OSTD_EXPORT CsIdent {
|
|||
CsAlias const *get_alias() const;
|
||||
|
||||
bool is_command() const;
|
||||
CsCommand *get_command();
|
||||
CsCommand const *get_command() const;
|
||||
|
||||
bool is_special() const;
|
||||
|
||||
bool is_var() const;
|
||||
|
@ -291,6 +295,30 @@ private:
|
|||
CsValue p_val;
|
||||
};
|
||||
|
||||
using CmdFunc = ostd::Function<void(CsValueRange, CsValue &)>;
|
||||
|
||||
struct CsCommand: CsIdent {
|
||||
friend struct CsState;
|
||||
|
||||
ostd::ConstCharRange get_args() const {
|
||||
return p_cargs;
|
||||
}
|
||||
|
||||
int get_num_args() const {
|
||||
return p_numargs;
|
||||
}
|
||||
|
||||
char *p_cargs;
|
||||
int p_numargs;
|
||||
CmdFunc cb_cftv;
|
||||
|
||||
private:
|
||||
CsCommand(
|
||||
int type, ostd::ConstCharRange name, ostd::ConstCharRange args,
|
||||
int numargs, CmdFunc func
|
||||
);
|
||||
};
|
||||
|
||||
struct CsIdentLink {
|
||||
CsIdent *id;
|
||||
CsIdentLink *next;
|
||||
|
@ -306,8 +334,6 @@ enum {
|
|||
CS_LIB_ALL = 0b1111
|
||||
};
|
||||
|
||||
using CmdFunc = ostd::Function<void(CsValueRange, CsValue &)>;
|
||||
|
||||
struct OSTD_EXPORT CsState {
|
||||
CsMap<ostd::ConstCharRange, CsIdent *> idents;
|
||||
CsVector<CsIdent *> identmap;
|
||||
|
|
|
@ -1,18 +1,66 @@
|
|||
#ifdef CS_REPL_USE_LINENOISE
|
||||
#ifdef OSTD_PLATFORM_POSIX
|
||||
#ifndef CS_REPL_HAS_EDIT
|
||||
#define CS_REPL_HAS_EDIT
|
||||
/* use the bundled linenoise library, default */
|
||||
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <ostd/string.hh>
|
||||
#include <ostd/maybe.hh>
|
||||
|
||||
#include "linenoise.hh"
|
||||
|
||||
#ifdef CS_REPL_HAS_COMPLETE
|
||||
static void ln_complete(char const *buf, linenoiseCompletions *lc) {
|
||||
ostd::ConstCharRange cmd = get_complete_cmd(buf);
|
||||
for (auto id: gcs->identmap.iter()) {
|
||||
if (!id->is_command()) {
|
||||
continue;
|
||||
}
|
||||
ostd::ConstCharRange idname = id->get_name();
|
||||
if (idname.size() <= cmd.size()) {
|
||||
continue;
|
||||
}
|
||||
if (idname.slice(0, cmd.size()) == cmd) {
|
||||
linenoiseAddCompletion(lc, idname.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CS_REPL_HAS_COMPLETE */
|
||||
|
||||
#ifdef CS_REPL_HAS_HINTS
|
||||
static char *ln_hint(char const *buf, int *color, int *bold) {
|
||||
CsCommand *cmd = get_hint_cmd(buf);
|
||||
if (!cmd) {
|
||||
return nullptr;
|
||||
}
|
||||
ostd::String args = " [";
|
||||
fill_cmd_args(args, cmd->get_args());
|
||||
args += ']';
|
||||
*color = 35;
|
||||
*bold = 1;
|
||||
char *ret = new char[args.size() + 1];
|
||||
memcpy(ret, args.data(), args.size() + 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ln_hint_free(void *hint) {
|
||||
delete[] static_cast<char *>(hint);
|
||||
}
|
||||
#endif /* CS_REPL_HAS_HINTS */
|
||||
|
||||
static void init_lineedit(ostd::ConstCharRange) {
|
||||
/* sensible default history size */
|
||||
linenoiseHistorySetMaxLen(1000);
|
||||
#ifdef CS_REPL_HAS_COMPLETE
|
||||
linenoiseSetCompletionCallback(ln_complete);
|
||||
#endif
|
||||
#ifdef CS_REPL_HAS_HINTS
|
||||
linenoiseSetHintsCallback(ln_hint);
|
||||
linenoiseSetFreeHintsCallback(ln_hint_free);
|
||||
#endif
|
||||
}
|
||||
|
||||
static ostd::Maybe<ostd::String> read_line(CsSvar *pr) {
|
||||
|
@ -36,3 +84,4 @@ static void add_history(ostd::ConstCharRange line) {
|
|||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
|
133
tools/repl.cc
133
tools/repl.cc
|
@ -25,6 +25,138 @@ static bool stdin_is_tty() {
|
|||
|
||||
/* line editing support */
|
||||
|
||||
CsState *gcs = nullptr;
|
||||
|
||||
#ifdef CS_REPL_HAS_COMPLETE
|
||||
static ostd::ConstCharRange get_complete_cmd(ostd::ConstCharRange buf) {
|
||||
ostd::ConstCharRange not_allowed = "\"/;()[] \t\r\n\0";
|
||||
ostd::ConstCharRange found = ostd::find_one_of(buf, not_allowed);
|
||||
while (!found.empty()) {
|
||||
++found;
|
||||
buf = found;
|
||||
found = ostd::find_one_of(found, not_allowed);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
#endif /* CS_REPL_HAS_COMPLETE */
|
||||
|
||||
#ifdef CS_REPL_HAS_HINTS
|
||||
|
||||
static inline ostd::ConstCharRange get_arg_type(char arg) {
|
||||
switch (arg) {
|
||||
case 'i':
|
||||
return "int";
|
||||
case 'b':
|
||||
return "int_min";
|
||||
case 'f':
|
||||
return "float";
|
||||
case 'F':
|
||||
return "float_prev";
|
||||
case 't':
|
||||
return "any";
|
||||
case 'T':
|
||||
return "any_m";
|
||||
case 'E':
|
||||
return "cond";
|
||||
case 'N':
|
||||
return "numargs";
|
||||
case 'S':
|
||||
return "str_m";
|
||||
case 's':
|
||||
return "str";
|
||||
case 'e':
|
||||
return "block";
|
||||
case 'r':
|
||||
return "ident";
|
||||
case '$':
|
||||
return "self";
|
||||
}
|
||||
return "illegal";
|
||||
}
|
||||
|
||||
static void fill_cmd_args(ostd::String &writer, ostd::ConstCharRange args) {
|
||||
char variadic = '\0';
|
||||
int nrep = 0;
|
||||
if (!args.empty() && ((args.back() == 'V') || (args.back() == 'C'))) {
|
||||
variadic = args.back();
|
||||
args.pop_back();
|
||||
if (!args.empty() && isdigit(args.back())) {
|
||||
nrep = args.back() - '0';
|
||||
args.pop_back();
|
||||
}
|
||||
}
|
||||
if (args.empty()) {
|
||||
if (variadic == 'C') {
|
||||
writer += "concat(...)";
|
||||
} else if (variadic == 'V') {
|
||||
writer += "...";
|
||||
}
|
||||
return;
|
||||
}
|
||||
int norep = int(args.size()) - nrep;
|
||||
if (norep > 0) {
|
||||
for (int i = 0; i < norep; ++i) {
|
||||
if (i != 0) {
|
||||
writer += ", ";
|
||||
}
|
||||
writer += get_arg_type(*args);
|
||||
++args;
|
||||
}
|
||||
}
|
||||
if (variadic) {
|
||||
if (norep > 0) {
|
||||
writer += ", ";
|
||||
}
|
||||
if (variadic == 'C') {
|
||||
writer += "concat(";
|
||||
}
|
||||
if (!args.empty()) {
|
||||
if (args.size() > 1) {
|
||||
writer += '{';
|
||||
}
|
||||
for (ostd::Size i = 0; i < args.size(); ++i) {
|
||||
if (i) {
|
||||
writer += ", ";
|
||||
}
|
||||
writer += get_arg_type(args[i]);
|
||||
}
|
||||
if (args.size() > 1) {
|
||||
writer += '}';
|
||||
}
|
||||
}
|
||||
writer += "...";
|
||||
if (variadic == 'C') {
|
||||
writer += ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CsCommand *get_hint_cmd(ostd::ConstCharRange buf) {
|
||||
ostd::ConstCharRange nextchars = "([;";
|
||||
auto lp = ostd::find_one_of(buf, nextchars);
|
||||
if (!lp.empty()) {
|
||||
CsCommand *cmd = get_hint_cmd(buf + 1);
|
||||
if (cmd) {
|
||||
return cmd;
|
||||
}
|
||||
}
|
||||
while (!buf.empty() && isspace(buf.front())) {
|
||||
++buf;
|
||||
}
|
||||
ostd::ConstCharRange spaces = " \t\r\n";
|
||||
ostd::ConstCharRange s = ostd::find_one_of(buf, spaces);
|
||||
if (!s.empty()) {
|
||||
buf = ostd::slice_until(buf, s);
|
||||
}
|
||||
if (!buf.empty()) {
|
||||
auto cmd = gcs->get_ident(buf);
|
||||
return cmd ? cmd->get_command() : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif /* CS_REPL_HAS_HINTS */
|
||||
|
||||
#include "tools/edit_linenoise.hh"
|
||||
#include "tools/edit_libedit.hh"
|
||||
#include "tools/edit_readline.hh"
|
||||
|
@ -72,6 +204,7 @@ static void do_tty(CsState &cs) {
|
|||
|
||||
int main(int, char **argv) {
|
||||
CsState cs;
|
||||
gcs = &cs;
|
||||
cs.init_libs();
|
||||
if (stdin_is_tty()) {
|
||||
init_lineedit(argv[0]);
|
||||
|
|
Loading…
Reference in New Issue