Compile backtick commands as __command() calls with ${} interpolation
Backtick expressions now compile as calls to a global __command()
function instead of being treated as plain strings. Interpolation
via ${expr} is supported, with each interpolated value wrapped in
tostring() for type safety. Commands are parsed in primaryexp so
suffix operations like `.stdout` work directly.
Adds a stub __command that returns {code, stdout, stderr} for testing
until the real implementation (issue #03).
This commit is contained in:
95
llex.c
95
llex.c
@@ -49,7 +49,8 @@ static const char *const luaX_tokens [] = {
|
||||
"return", "then", "true", "until", "while",
|
||||
"//", "..", "...", "==", ">=", "<=", "~=",
|
||||
"<<", ">>", "::", "<eof>",
|
||||
"<number>", "<integer>", "<name>", "<string>"
|
||||
"<number>", "<integer>", "<name>", "<string>",
|
||||
"<command>", "<command_interp>"
|
||||
};
|
||||
|
||||
|
||||
@@ -186,6 +187,7 @@ void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source,
|
||||
ls->source = source;
|
||||
/* all three strings here ("_ENV", "break", "global") were fixed,
|
||||
so they cannot be collected */
|
||||
ls->in_command = 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)
|
||||
@@ -464,6 +466,93 @@ static void read_string (LexState *ls, int del, SemInfo *seminfo) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read a backtick command string, handling ${expr} interpolation.
|
||||
** Called initially when '`' is seen, and also continued via
|
||||
** luaX_readcommandcont() after the parser processes an interpolated
|
||||
** expression.
|
||||
** Returns TK_COMMAND if the string is complete (closing ` found),
|
||||
** or TK_COMMAND_INTERP if an interpolation ${...} was found.
|
||||
*/
|
||||
static int read_command_body (LexState *ls, SemInfo *seminfo) {
|
||||
luaZ_resetbuffer(ls->buff);
|
||||
for (;;) {
|
||||
switch (ls->current) {
|
||||
case EOZ:
|
||||
lexerror(ls, "unfinished command", TK_EOS);
|
||||
break; /* to avoid warnings */
|
||||
case '\n':
|
||||
case '\r':
|
||||
save(ls, '\n');
|
||||
inclinenumber(ls);
|
||||
break;
|
||||
case '$': {
|
||||
next(ls);
|
||||
if (ls->current == '{') {
|
||||
next(ls); /* skip '{' */
|
||||
/* store fragment so far */
|
||||
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
|
||||
luaZ_bufflen(ls->buff));
|
||||
ls->in_command = 1;
|
||||
return TK_COMMAND_INTERP;
|
||||
}
|
||||
else {
|
||||
save(ls, '$'); /* not an interpolation, keep literal '$' */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '`': {
|
||||
next(ls); /* skip closing '`' */
|
||||
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
|
||||
luaZ_bufflen(ls->buff));
|
||||
ls->in_command = 0;
|
||||
return TK_COMMAND;
|
||||
}
|
||||
case '\\': { /* escape sequences */
|
||||
int c;
|
||||
save_and_next(ls); /* keep '\\' for error messages */
|
||||
switch (ls->current) {
|
||||
case 'n': c = '\n'; goto cmd_read_save;
|
||||
case 't': c = '\t'; goto cmd_read_save;
|
||||
case '\\': c = '\\'; goto cmd_read_save;
|
||||
case '`': c = '`'; goto cmd_read_save;
|
||||
case '$': c = '$'; goto cmd_read_save;
|
||||
default: {
|
||||
/* not a recognized escape, keep both chars literally */
|
||||
break; /* backslash already saved, current will be saved next */
|
||||
}
|
||||
}
|
||||
break;
|
||||
cmd_read_save:
|
||||
next(ls);
|
||||
luaZ_buffremove(ls->buff, 1); /* remove '\\' */
|
||||
save(ls, c);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
save_and_next(ls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int read_command (LexState *ls, SemInfo *seminfo) {
|
||||
next(ls); /* skip opening '`' */
|
||||
return read_command_body(ls, seminfo);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Continue reading a backtick command string after the parser has
|
||||
** consumed the '}' that closes an interpolated ${expr}.
|
||||
*/
|
||||
int luaX_readcommandcont (LexState *ls) {
|
||||
int tk = read_command_body(ls, &ls->t.seminfo);
|
||||
ls->t.token = tk;
|
||||
return tk;
|
||||
}
|
||||
|
||||
|
||||
static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
luaZ_resetbuffer(ls->buff);
|
||||
for (;;) {
|
||||
@@ -541,6 +630,9 @@ static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
read_string(ls, ls->current, seminfo);
|
||||
return TK_STRING;
|
||||
}
|
||||
case '`': { /* explicit command `ls -l` with ${} interpolation */
|
||||
return read_command(ls, seminfo);
|
||||
}
|
||||
case '.': { /* '.', '..', '...', or number */
|
||||
save_and_next(ls);
|
||||
if (check_next1(ls, '.')) {
|
||||
@@ -601,4 +693,3 @@ int luaX_lookahead (LexState *ls) {
|
||||
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
|
||||
return ls->lookahead.token;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user