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:
Cormac Shannon
2026-03-18 09:29:51 +00:00
parent 42baabde34
commit 1766e40a68
8 changed files with 143 additions and 7 deletions

21
llex.c
View File

@@ -193,6 +193,7 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source,
so they cannot be collected */
ls->cmd_mode = 0;
ls->cmd_envvar = NULL;
ls->cmd_subcmd = 0;
ls->envn = luaS_newliteral(L, LUA_ENV); /* get env string */
ls->brkn = luaS_newliteral(L, "break"); /* get "break" string */
#if defined(LUA_COMPAT_GLOBAL)
@@ -516,6 +517,15 @@ static int read_command_body (LexState *ls, SemInfo *seminfo) {
ls->saved_cmd_mode = ls->cmd_mode; /* save for readcommandcont */
return interactive ? TK_INTERACTIVE : TK_COMMAND;
}
else if (ls->current == '(') {
next(ls); /* skip '(' */
/* $() subcommand interpolation */
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff));
ls->saved_cmd_mode = ls->cmd_mode;
ls->cmd_subcmd = 1;
return interactive ? TK_INTERACTIVE : TK_COMMAND;
}
else if (lislalpha(ls->current)) {
/* $NAME envvar expansion */
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
@@ -532,6 +542,17 @@ static int read_command_body (LexState *ls, SemInfo *seminfo) {
}
break;
}
case ')': {
if (ls->cmd_mode == 3) {
next(ls); /* skip closing ')' */
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff));
ls->cmd_mode = 0;
return TK_COMMAND;
}
save_and_next(ls); /* literal ')' in non-subcommand mode */
break;
}
case '`': {
if (interactive) {
/* backtick is literal in interactive mode */