Unify command tokens: remove TK_COMMAND_INTERP and TK_INTERACTIVE_INTERP
Use cmd_mode (already on LexState) to signal whether interpolation
follows, instead of separate *_INTERP token variants. This eliminates
duplicate simple/interpolated code paths in the parser — commandexp()
and interactivestat() now each use a single unified loop that checks
cmd_mode != 0. A shared parseinterp() helper handles each ${expr}
interpolation cycle.
This commit is contained in:
10
llex.c
10
llex.c
@@ -50,8 +50,8 @@ static const char *const luaX_tokens [] = {
|
|||||||
"//", "..", "...", "==", ">=", "<=", "~=",
|
"//", "..", "...", "==", ">=", "<=", "~=",
|
||||||
"<<", ">>", "::", "<eof>",
|
"<<", ">>", "::", "<eof>",
|
||||||
"<number>", "<integer>", "<name>", "<string>",
|
"<number>", "<integer>", "<name>", "<string>",
|
||||||
"<command>", "<command_interp>", "<envvar>",
|
"<command>", "<envvar>",
|
||||||
"<interactive>", "<interactive_interp>"
|
"<interactive>"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -475,8 +475,8 @@ static void read_string (LexState *ls, int del, SemInfo *seminfo) {
|
|||||||
** Called initially when '`' is seen, and also continued via
|
** Called initially when '`' is seen, and also continued via
|
||||||
** luaX_readcommandcont() after the parser processes an interpolated
|
** luaX_readcommandcont() after the parser processes an interpolated
|
||||||
** expression.
|
** expression.
|
||||||
** Returns TK_COMMAND if the string is complete (closing ` found),
|
** Returns TK_COMMAND or TK_INTERACTIVE in all cases.
|
||||||
** or TK_COMMAND_INTERP if an interpolation ${...} was found.
|
** When an interpolation ${...} is found, cmd_mode remains non-zero.
|
||||||
*/
|
*/
|
||||||
static int read_command_body (LexState *ls, SemInfo *seminfo) {
|
static int read_command_body (LexState *ls, SemInfo *seminfo) {
|
||||||
int interactive = (ls->cmd_mode == 2);
|
int interactive = (ls->cmd_mode == 2);
|
||||||
@@ -513,7 +513,7 @@ static int read_command_body (LexState *ls, SemInfo *seminfo) {
|
|||||||
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
|
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
|
||||||
luaZ_bufflen(ls->buff));
|
luaZ_bufflen(ls->buff));
|
||||||
ls->saved_cmd_mode = ls->cmd_mode; /* save for readcommandcont */
|
ls->saved_cmd_mode = ls->cmd_mode; /* save for readcommandcont */
|
||||||
return interactive ? TK_INTERACTIVE_INTERP : TK_COMMAND_INTERP;
|
return interactive ? TK_INTERACTIVE : TK_COMMAND;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
save(ls, '$'); /* not an interpolation, keep literal '$' */
|
save(ls, '$'); /* not an interpolation, keep literal '$' */
|
||||||
|
|||||||
4
llex.h
4
llex.h
@@ -41,10 +41,8 @@ enum RESERVED {
|
|||||||
TK_DBCOLON, TK_EOS,
|
TK_DBCOLON, TK_EOS,
|
||||||
TK_FLT, TK_INT, TK_NAME, TK_STRING,
|
TK_FLT, TK_INT, TK_NAME, TK_STRING,
|
||||||
TK_COMMAND,
|
TK_COMMAND,
|
||||||
TK_COMMAND_INTERP,
|
|
||||||
TK_ENVVAR,
|
TK_ENVVAR,
|
||||||
TK_INTERACTIVE,
|
TK_INTERACTIVE
|
||||||
TK_INTERACTIVE_INTERP
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* number of reserved words */
|
/* number of reserved words */
|
||||||
|
|||||||
226
lparser.c
226
lparser.c
@@ -554,24 +554,34 @@ static void singlevar (LexState *ls, expdesc *var) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Compile a command expression: emit code equivalent to
|
** Helper: parse a single ${expr} interpolation fragment.
|
||||||
** __command(cmdstring)
|
** Emits tostring(expr), reads the next command continuation fragment,
|
||||||
** where cmdstring is already built (possibly via concatenation
|
** and pushes it as a string constant. Returns with the continuation
|
||||||
** from interpolation fragments).
|
** token already set (TK_COMMAND or TK_INTERACTIVE).
|
||||||
*/
|
*/
|
||||||
static void codecommand (LexState *ls, expdesc *v, expdesc *cmdstr) {
|
static void parseinterp (LexState *ls, TString *tsname, int *nconcat) {
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
int base, line;
|
expdesc interp, tostrfn;
|
||||||
expdesc func;
|
int tostr_base;
|
||||||
line = ls->linenumber;
|
luaX_next(ls); /* advance past TK_COMMAND / TK_INTERACTIVE */
|
||||||
codelushfunc(fs, LUSH_OP_COMMAND, &func);
|
/* emit tostring(expr) */
|
||||||
base = func.u.info;
|
buildglobal(ls, tsname, &tostrfn);
|
||||||
/* push the command string argument */
|
luaK_exp2nextreg(fs, &tostrfn);
|
||||||
luaK_exp2nextreg(fs, cmdstr);
|
tostr_base = tostrfn.u.info;
|
||||||
/* emit OP_CALL: 1 arg, 1 result */
|
expr(ls, &interp);
|
||||||
init_exp(v, VCALL, luaK_codeABC(fs, OP_CALL, base, 2, 2));
|
check(ls, '}');
|
||||||
luaK_fixline(fs, line);
|
luaK_exp2nextreg(fs, &interp);
|
||||||
fs->freereg = cast_byte(base + 1);
|
luaK_codeABC(fs, OP_CALL, tostr_base, 2, 2);
|
||||||
|
fs->freereg = cast_byte(tostr_base + 1);
|
||||||
|
(*nconcat)++;
|
||||||
|
/* read continuation fragment */
|
||||||
|
luaX_readcommandcont(ls);
|
||||||
|
{
|
||||||
|
expdesc frag;
|
||||||
|
codestring(&frag, ls->t.seminfo.ts);
|
||||||
|
luaK_exp2nextreg(fs, &frag);
|
||||||
|
(*nconcat)++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1230,83 +1240,38 @@ static void funcargs (LexState *ls, expdesc *f) {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Parse a backtick command expression (TK_COMMAND or TK_COMMAND_INTERP).
|
** Parse a backtick command expression (TK_COMMAND).
|
||||||
** Compiles `cmd` as __command("cmd") and `cmd ${expr}` as
|
** Compiles `cmd` as __command("cmd") and `cmd ${expr}` as
|
||||||
** __command("cmd " .. tostring(expr) .. "").
|
** __command("cmd " .. tostring(expr) .. "").
|
||||||
** On entry, ls->t.token is TK_COMMAND or TK_COMMAND_INTERP.
|
** On entry, ls->t.token is TK_COMMAND.
|
||||||
** On exit, the token has been consumed (ls->t has the next token).
|
** On exit, the token has been consumed (ls->t has the next token).
|
||||||
*/
|
*/
|
||||||
static void commandexp (LexState *ls, expdesc *v) {
|
static void commandexp (LexState *ls, expdesc *v) {
|
||||||
if (ls->t.token == TK_COMMAND) {
|
FuncState *fs = ls->fs;
|
||||||
/* simple command with no interpolation */
|
expdesc func, cmdstr;
|
||||||
expdesc cmdstr;
|
int base, nconcat = 0;
|
||||||
codestring(&cmdstr, ls->t.seminfo.ts);
|
int line = ls->linenumber;
|
||||||
luaX_next(ls);
|
TString *tsname = NULL;
|
||||||
codecommand(ls, v, &cmdstr);
|
codelushfunc(fs, LUSH_OP_COMMAND, &func);
|
||||||
|
base = func.u.info;
|
||||||
|
/* load the first fragment */
|
||||||
|
codestring(&cmdstr, ls->t.seminfo.ts);
|
||||||
|
luaK_exp2nextreg(fs, &cmdstr);
|
||||||
|
nconcat++;
|
||||||
|
while (ls->cmd_mode != 0) {
|
||||||
|
if (tsname == NULL)
|
||||||
|
tsname = luaX_newstring(ls, "tostring", 8);
|
||||||
|
parseinterp(ls, tsname, &nconcat);
|
||||||
}
|
}
|
||||||
else {
|
luaX_next(ls); /* consume final TK_COMMAND */
|
||||||
/* command with ${expr} interpolation */
|
if (nconcat > 1) {
|
||||||
FuncState *fs = ls->fs;
|
int first = base + 1;
|
||||||
expdesc func, cmdstr;
|
luaK_codeABC(fs, OP_CONCAT, first, nconcat, 0);
|
||||||
int base, nconcat = 0;
|
fs->freereg = cast_byte(first + 1);
|
||||||
int line = ls->linenumber;
|
|
||||||
TString *tsname = luaX_newstring(ls, "tostring", 8);
|
|
||||||
/* load __command function first so it occupies the base register */
|
|
||||||
codelushfunc(fs, LUSH_OP_COMMAND, &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;
|
|
||||||
/* parse the interpolated expression */
|
|
||||||
luaX_next(ls); /* advance past TK_COMMAND_INTERP */
|
|
||||||
/* emit tostring(expr): load tostring function, then the expr,
|
|
||||||
** then OP_CALL — result occupies one register in the concat chain */
|
|
||||||
buildglobal(ls, tsname, &tostrfn);
|
|
||||||
luaK_exp2nextreg(fs, &tostrfn);
|
|
||||||
tostr_base = tostrfn.u.info;
|
|
||||||
expr(ls, &interp);
|
|
||||||
check(ls, '}');
|
|
||||||
/* don't call luaX_next — luaX_readcommandcont reads directly
|
|
||||||
from ls->current, which is already past the '}' */
|
|
||||||
luaK_exp2nextreg(fs, &interp);
|
|
||||||
luaK_codeABC(fs, OP_CALL, tostr_base, 2, 2);
|
|
||||||
fs->freereg = cast_byte(tostr_base + 1);
|
|
||||||
nconcat++;
|
|
||||||
/* continue reading the command string */
|
|
||||||
if (luaX_readcommandcont(ls) == TK_COMMAND_INTERP) {
|
|
||||||
/* another fragment + more interpolation coming */
|
|
||||||
expdesc frag;
|
|
||||||
codestring(&frag, ls->t.seminfo.ts);
|
|
||||||
luaK_exp2nextreg(fs, &frag);
|
|
||||||
nconcat++;
|
|
||||||
/* loop continues */
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* TK_COMMAND: final fragment */
|
|
||||||
expdesc frag;
|
|
||||||
codestring(&frag, ls->t.seminfo.ts);
|
|
||||||
luaK_exp2nextreg(fs, &frag);
|
|
||||||
nconcat++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* advance past the final TK_COMMAND token */
|
|
||||||
luaX_next(ls);
|
|
||||||
/* emit OP_CONCAT for all fragments (result goes into base+1) */
|
|
||||||
if (nconcat > 1) {
|
|
||||||
int first = base + 1;
|
|
||||||
luaK_codeABC(fs, OP_CONCAT, first, nconcat, 0);
|
|
||||||
fs->freereg = cast_byte(first + 1);
|
|
||||||
}
|
|
||||||
/* emit OP_CALL: 1 arg (the concatenated string), 1 result */
|
|
||||||
init_exp(v, VCALL, luaK_codeABC(fs, OP_CALL, base, 2, 2));
|
|
||||||
luaK_fixline(fs, line);
|
|
||||||
fs->freereg = cast_byte(base + 1);
|
|
||||||
}
|
}
|
||||||
|
init_exp(v, VCALL, luaK_codeABC(fs, OP_CALL, base, 2, 2));
|
||||||
|
luaK_fixline(fs, line);
|
||||||
|
fs->freereg = cast_byte(base + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1340,7 +1305,7 @@ static void primaryexp (LexState *ls, expdesc *v) {
|
|||||||
singlevar(ls, v);
|
singlevar(ls, v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case TK_COMMAND: case TK_COMMAND_INTERP: {
|
case TK_COMMAND: {
|
||||||
commandexp(ls, v);
|
commandexp(ls, v);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2216,71 +2181,29 @@ static void envstat (LexState *ls) {
|
|||||||
*/
|
*/
|
||||||
static void interactivestat (LexState *ls) {
|
static void interactivestat (LexState *ls) {
|
||||||
FuncState *fs = ls->fs;
|
FuncState *fs = ls->fs;
|
||||||
if (ls->t.token == TK_INTERACTIVE) {
|
expdesc func, cmdstr, v;
|
||||||
/* simple interactive command, no interpolation */
|
int base, nconcat = 0;
|
||||||
int base, line;
|
int line = ls->linenumber;
|
||||||
expdesc func, cmdstr, v;
|
TString *tsname = NULL;
|
||||||
line = ls->linenumber;
|
codelushfunc(fs, LUSH_OP_INTERACTIVE, &func);
|
||||||
codelushfunc(fs, LUSH_OP_INTERACTIVE, &func);
|
base = func.u.info;
|
||||||
base = func.u.info;
|
codestring(&cmdstr, ls->t.seminfo.ts);
|
||||||
codestring(&cmdstr, ls->t.seminfo.ts);
|
luaK_exp2nextreg(fs, &cmdstr);
|
||||||
luaK_exp2nextreg(fs, &cmdstr);
|
nconcat++;
|
||||||
luaX_next(ls); /* consume TK_INTERACTIVE */
|
while (ls->cmd_mode != 0) {
|
||||||
init_exp(&v, VCALL, luaK_codeABC(fs, OP_CALL, base, 2, 1));
|
if (tsname == NULL)
|
||||||
luaK_fixline(fs, line);
|
tsname = luaX_newstring(ls, "tostring", 8);
|
||||||
fs->freereg = cast_byte(base);
|
parseinterp(ls, tsname, &nconcat);
|
||||||
}
|
}
|
||||||
else {
|
luaX_next(ls); /* consume final TK_INTERACTIVE */
|
||||||
/* interactive command with ${expr} interpolation */
|
if (nconcat > 1) {
|
||||||
expdesc func, cmdstr, v;
|
int first = base + 1;
|
||||||
int base, nconcat = 0;
|
luaK_codeABC(fs, OP_CONCAT, first, nconcat, 0);
|
||||||
int line = ls->linenumber;
|
fs->freereg = cast_byte(first + 1);
|
||||||
TString *tsname = luaX_newstring(ls, "tostring", 8);
|
|
||||||
codelushfunc(fs, LUSH_OP_INTERACTIVE, &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);
|
|
||||||
}
|
}
|
||||||
|
init_exp(&v, VCALL, luaK_codeABC(fs, OP_CALL, base, 2, 1));
|
||||||
|
luaK_fixline(fs, line);
|
||||||
|
fs->freereg = cast_byte(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2353,8 +2276,7 @@ static void statement (LexState *ls) {
|
|||||||
envstat(ls);
|
envstat(ls);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TK_INTERACTIVE:
|
case TK_INTERACTIVE: { /* stat -> !command */
|
||||||
case TK_INTERACTIVE_INTERP: { /* stat -> !command */
|
|
||||||
interactivestat(ls);
|
interactivestat(ls);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user