Implement $(cmd) subcommand syntax in commands (issue #27)
Add $() for inline subcommand substitution that runs a command and
inserts its stdout (trailing newlines stripped) into the outer command
string. Supports nesting, and mixing with ${expr} and $NAME.
This commit is contained in:
42
lparser.c
42
lparser.c
@@ -556,23 +556,59 @@ static void singlevar (LexState *ls, expdesc *var) {
|
||||
static void codeenvget (LexState *ls, expdesc *v, TString *name);
|
||||
|
||||
/*
|
||||
** Parse a single interpolation fragment in a command: either ${expr}
|
||||
** or $NAME. Emits tostring(<value>), reads the continuation fragment,
|
||||
** Parse a single interpolation fragment in a command: ${expr}, $NAME,
|
||||
** or $(cmd). Emits tostring(<value>), reads the continuation fragment,
|
||||
** and pushes it as a string constant.
|
||||
** For ${expr}: advances past the command token, parses expr, checks '}'.
|
||||
** For $NAME: the identifier was already collected by the lexer into
|
||||
** cmd_envvar — no tokens to consume, just emit getenv(name).
|
||||
** For $(cmd): enters subcommand mode (cmd_mode=3), parses the inner
|
||||
** command with its own interpolation loop, emits subcmd().
|
||||
*/
|
||||
static void parseinterp (LexState *ls, TString *tsname, int *nconcat) {
|
||||
FuncState *fs = ls->fs;
|
||||
expdesc interp, tostrfn;
|
||||
int tostr_base;
|
||||
TString *envname = ls->cmd_envvar;
|
||||
lu_byte subcmd = ls->cmd_subcmd;
|
||||
ls->cmd_envvar = NULL;
|
||||
ls->cmd_subcmd = 0;
|
||||
buildglobal(ls, tsname, &tostrfn);
|
||||
luaK_exp2nextreg(fs, &tostrfn);
|
||||
tostr_base = tostrfn.u.info;
|
||||
if (envname != NULL) {
|
||||
if (subcmd) {
|
||||
/* $(cmd) — parse inner command as subcommand */
|
||||
expdesc subfunc, cmdstr;
|
||||
int subbase, subnconcat = 0;
|
||||
lu_byte outer_saved = ls->saved_cmd_mode;
|
||||
TString *sub_tsname = NULL;
|
||||
codelushfunc(fs, LUSH_OP_SUBCMD, &subfunc);
|
||||
subbase = subfunc.u.info;
|
||||
/* enter subcommand lexing mode (terminated by ')') */
|
||||
ls->saved_cmd_mode = 3;
|
||||
luaX_readcommandcont(ls); /* read first fragment of inner command */
|
||||
codestring(&cmdstr, ls->t.seminfo.ts);
|
||||
luaK_exp2nextreg(fs, &cmdstr);
|
||||
subnconcat++;
|
||||
/* inner interpolation loop */
|
||||
while (ls->cmd_mode != 0) {
|
||||
if (sub_tsname == NULL)
|
||||
sub_tsname = luaX_newstring(ls, "tostring", 8);
|
||||
parseinterp(ls, sub_tsname, &subnconcat);
|
||||
}
|
||||
/* concatenate inner fragments if needed */
|
||||
if (subnconcat > 1) {
|
||||
int first = subbase + 1;
|
||||
luaK_codeABC(fs, OP_CONCAT, first, subnconcat, 0);
|
||||
fs->freereg = cast_byte(first + 1);
|
||||
}
|
||||
/* call subcmd(inner_cmd_string) */
|
||||
init_exp(&interp, VCALL, luaK_codeABC(fs, OP_CALL, subbase, 2, 2));
|
||||
fs->freereg = cast_byte(subbase + 1);
|
||||
/* restore outer command context */
|
||||
ls->saved_cmd_mode = outer_saved;
|
||||
}
|
||||
else if (envname != NULL) {
|
||||
/* $NAME — emit getenv(name) */
|
||||
codeenvget(ls, &interp, envname);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user