Implement prefix-based interactive commands via ! (issue #14)
Add !command syntax that runs commands with inherited terminal (no
capture). The lexer treats ! as a statement-starting token, reading
to end-of-line with the same interpolation/escape/pipe support as
backtick commands. The C function sets _ = {code=N, stdout="", stderr=""}.
This commit is contained in:
84
lparser.c
84
lparser.c
@@ -2206,6 +2206,85 @@ static void envstat (LexState *ls) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Parse an interactive command statement (!cmd).
|
||||
** Compiles !cmd as __interactive("cmd") with result discarded.
|
||||
** The C function sets global _ = {code=N, stdout="", stderr=""}.
|
||||
*/
|
||||
static void interactivestat (LexState *ls) {
|
||||
FuncState *fs = ls->fs;
|
||||
if (ls->t.token == TK_INTERACTIVE) {
|
||||
/* simple interactive command, no interpolation */
|
||||
int base, line;
|
||||
expdesc func, cmdstr, v;
|
||||
TString *fname = luaX_newstring(ls, "__interactive", 13);
|
||||
line = ls->linenumber;
|
||||
buildglobal(ls, fname, &func);
|
||||
luaK_exp2nextreg(fs, &func);
|
||||
base = func.u.info;
|
||||
codestring(&cmdstr, ls->t.seminfo.ts);
|
||||
luaK_exp2nextreg(fs, &cmdstr);
|
||||
luaX_next(ls); /* consume TK_INTERACTIVE */
|
||||
init_exp(&v, VCALL, luaK_codeABC(fs, OP_CALL, base, 2, 1));
|
||||
luaK_fixline(fs, line);
|
||||
fs->freereg = cast_byte(base);
|
||||
}
|
||||
else {
|
||||
/* interactive command with ${expr} interpolation */
|
||||
expdesc func, cmdstr, v;
|
||||
int base, nconcat = 0;
|
||||
int line = ls->linenumber;
|
||||
TString *fname = luaX_newstring(ls, "__interactive", 13);
|
||||
TString *tsname = luaX_newstring(ls, "tostring", 8);
|
||||
buildglobal(ls, fname, &func);
|
||||
luaK_exp2nextreg(fs, &func);
|
||||
base = func.u.info;
|
||||
/* load the first fragment */
|
||||
codestring(&cmdstr, ls->t.seminfo.ts);
|
||||
luaK_exp2nextreg(fs, &cmdstr);
|
||||
nconcat++;
|
||||
for (;;) {
|
||||
expdesc interp, tostrfn;
|
||||
int tostr_base;
|
||||
luaX_next(ls); /* advance past TK_INTERACTIVE_INTERP */
|
||||
buildglobal(ls, tsname, &tostrfn);
|
||||
luaK_exp2nextreg(fs, &tostrfn);
|
||||
tostr_base = tostrfn.u.info;
|
||||
expr(ls, &interp);
|
||||
check(ls, '}');
|
||||
luaK_exp2nextreg(fs, &interp);
|
||||
luaK_codeABC(fs, OP_CALL, tostr_base, 2, 2);
|
||||
fs->freereg = cast_byte(tostr_base + 1);
|
||||
nconcat++;
|
||||
if (luaX_readcommandcont(ls) == TK_INTERACTIVE_INTERP) {
|
||||
expdesc frag;
|
||||
codestring(&frag, ls->t.seminfo.ts);
|
||||
luaK_exp2nextreg(fs, &frag);
|
||||
nconcat++;
|
||||
}
|
||||
else {
|
||||
/* TK_INTERACTIVE: final fragment */
|
||||
expdesc frag;
|
||||
codestring(&frag, ls->t.seminfo.ts);
|
||||
luaK_exp2nextreg(fs, &frag);
|
||||
nconcat++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
luaX_next(ls); /* advance past final TK_INTERACTIVE */
|
||||
if (nconcat > 1) {
|
||||
int first = base + 1;
|
||||
luaK_codeABC(fs, OP_CONCAT, first, nconcat, 0);
|
||||
fs->freereg = cast_byte(first + 1);
|
||||
}
|
||||
/* emit void call (C=1 means 0 return values) */
|
||||
init_exp(&v, VCALL, luaK_codeABC(fs, OP_CALL, base, 2, 1));
|
||||
luaK_fixline(fs, line);
|
||||
fs->freereg = cast_byte(base);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void statement (LexState *ls) {
|
||||
int line = ls->linenumber; /* may be needed for error messages */
|
||||
enterlevel(ls);
|
||||
@@ -2275,6 +2354,11 @@ static void statement (LexState *ls) {
|
||||
envstat(ls);
|
||||
break;
|
||||
}
|
||||
case TK_INTERACTIVE:
|
||||
case TK_INTERACTIVE_INTERP: { /* stat -> !command */
|
||||
interactivestat(ls);
|
||||
break;
|
||||
}
|
||||
#if defined(LUA_COMPAT_GLOBAL)
|
||||
case TK_NAME: {
|
||||
/* compatibility code to parse global keyword when "global"
|
||||
|
||||
Reference in New Issue
Block a user