Add environment variable access with $NAME syntax
Lexes $NAME as TK_ENVVAR, compiles reads as __getenv("NAME") calls
and writes ($NAME = expr) as __setenv("NAME", expr) calls. Runtime
functions wrap getenv/setenv/unsetenv with automatic tostring coercion.
This commit is contained in:
39
linit.c
39
linit.c
@@ -13,12 +13,14 @@
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lua.h"
|
||||
|
||||
#include "lualib.h"
|
||||
#include "lauxlib.h"
|
||||
#include "llimits.h"
|
||||
#include "lcmd.h"
|
||||
|
||||
|
||||
/*
|
||||
@@ -40,30 +42,39 @@ static const luaL_Reg stdlibs[] = {
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
** Stub __command function for backtick command syntax.
|
||||
** Returns a table {code=0, stdout=cmdstring, stderr=""}.
|
||||
** The real implementation will be provided by issue #03.
|
||||
*/
|
||||
static int luaB_command (lua_State *L) {
|
||||
const char *cmd = luaL_checkstring(L, 1);
|
||||
lua_createtable(L, 0, 3);
|
||||
lua_pushinteger(L, 0);
|
||||
lua_setfield(L, -2, "code");
|
||||
lua_pushstring(L, cmd);
|
||||
lua_setfield(L, -2, "stdout");
|
||||
lua_pushliteral(L, "");
|
||||
lua_setfield(L, -2, "stderr");
|
||||
static int luaB_getenv (lua_State *L) {
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
const char *val = getenv(name);
|
||||
if (val == NULL)
|
||||
lua_pushnil(L);
|
||||
else
|
||||
lua_pushstring(L, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int luaB_setenv (lua_State *L) {
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
if (lua_isnoneornil(L, 2))
|
||||
unsetenv(name);
|
||||
else {
|
||||
const char *val = luaL_tolstring(L, 2, NULL);
|
||||
setenv(name, val, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Register shell built-in globals (called after standard libraries).
|
||||
*/
|
||||
static void opencommand (lua_State *L) {
|
||||
lua_pushcfunction(L, luaB_command);
|
||||
lua_setglobal(L, "__command");
|
||||
lua_pushcfunction(L, luaB_getenv);
|
||||
lua_setglobal(L, "__getenv");
|
||||
lua_pushcfunction(L, luaB_setenv);
|
||||
lua_setglobal(L, "__setenv");
|
||||
}
|
||||
|
||||
|
||||
|
||||
17
llex.c
17
llex.c
@@ -50,7 +50,7 @@ static const char *const luaX_tokens [] = {
|
||||
"//", "..", "...", "==", ">=", "<=", "~=",
|
||||
"<<", ">>", "::", "<eof>",
|
||||
"<number>", "<integer>", "<name>", "<string>",
|
||||
"<command>", "<command_interp>"
|
||||
"<command>", "<command_interp>", "<envvar>"
|
||||
};
|
||||
|
||||
|
||||
@@ -108,6 +108,9 @@ static const char *txtToken (LexState *ls, int token) {
|
||||
case TK_FLT: case TK_INT:
|
||||
save(ls, '\0');
|
||||
return luaO_pushfstring(ls->L, "'%s'", luaZ_buffer(ls->buff));
|
||||
case TK_ENVVAR:
|
||||
save(ls, '\0');
|
||||
return luaO_pushfstring(ls->L, "'$%s'", luaZ_buffer(ls->buff));
|
||||
default:
|
||||
return luaX_token2str(ls, token);
|
||||
}
|
||||
@@ -633,6 +636,18 @@ static int llex (LexState *ls, SemInfo *seminfo) {
|
||||
case '`': { /* explicit command `ls -l` with ${} interpolation */
|
||||
return read_command(ls, seminfo);
|
||||
}
|
||||
case '$': { /* environment variable $NAME */
|
||||
next(ls);
|
||||
if (lislalpha(ls->current)) { /* $NAME */
|
||||
do {
|
||||
save_and_next(ls);
|
||||
} while (lislalnum(ls->current));
|
||||
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
|
||||
luaZ_bufflen(ls->buff));
|
||||
return TK_ENVVAR;
|
||||
}
|
||||
else return '$'; /* bare $ as single-char token */
|
||||
}
|
||||
case '.': { /* '.', '..', '...', or number */
|
||||
save_and_next(ls);
|
||||
if (check_next1(ls, '.')) {
|
||||
|
||||
3
llex.h
3
llex.h
@@ -41,7 +41,8 @@ enum RESERVED {
|
||||
TK_DBCOLON, TK_EOS,
|
||||
TK_FLT, TK_INT, TK_NAME, TK_STRING,
|
||||
TK_COMMAND,
|
||||
TK_COMMAND_INTERP
|
||||
TK_COMMAND_INTERP,
|
||||
TK_ENVVAR
|
||||
};
|
||||
|
||||
/* number of reserved words */
|
||||
|
||||
52
lparser.c
52
lparser.c
@@ -1302,8 +1302,25 @@ static void commandexp (LexState *ls, expdesc *v) {
|
||||
}
|
||||
|
||||
|
||||
static void codeenvget (LexState *ls, expdesc *v, TString *name) {
|
||||
FuncState *fs = ls->fs;
|
||||
int base, line;
|
||||
expdesc func, arg;
|
||||
TString *fname = luaX_newstring(ls, "__getenv", 8);
|
||||
line = ls->linenumber;
|
||||
buildglobal(ls, fname, &func);
|
||||
luaK_exp2nextreg(fs, &func);
|
||||
base = func.u.info;
|
||||
codestring(&arg, name);
|
||||
luaK_exp2nextreg(fs, &arg);
|
||||
init_exp(v, VCALL, luaK_codeABC(fs, OP_CALL, base, 2, 2));
|
||||
luaK_fixline(fs, line);
|
||||
fs->freereg = cast_byte(base + 1);
|
||||
}
|
||||
|
||||
|
||||
static void primaryexp (LexState *ls, expdesc *v) {
|
||||
/* primaryexp -> NAME | '(' expr ')' | COMMAND */
|
||||
/* primaryexp -> NAME | '(' expr ')' | COMMAND | ENVVAR */
|
||||
switch (ls->t.token) {
|
||||
case '(': {
|
||||
int line = ls->linenumber;
|
||||
@@ -1321,6 +1338,11 @@ static void primaryexp (LexState *ls, expdesc *v) {
|
||||
commandexp(ls, v);
|
||||
return;
|
||||
}
|
||||
case TK_ENVVAR: {
|
||||
codeenvget(ls, v, ls->t.seminfo.ts);
|
||||
luaX_next(ls);
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
luaX_syntaxerror(ls, "unexpected symbol");
|
||||
}
|
||||
@@ -2160,6 +2182,30 @@ static void retstat (LexState *ls) {
|
||||
}
|
||||
|
||||
|
||||
static void envstat (LexState *ls) {
|
||||
/* envstat -> '$' NAME '=' expr */
|
||||
FuncState *fs = ls->fs;
|
||||
TString *name = ls->t.seminfo.ts;
|
||||
int base, line;
|
||||
expdesc func, arg, val;
|
||||
TString *fname;
|
||||
line = ls->linenumber;
|
||||
luaX_next(ls); /* skip TK_ENVVAR */
|
||||
checknext(ls, '=');
|
||||
fname = luaX_newstring(ls, "__setenv", 8);
|
||||
buildglobal(ls, fname, &func);
|
||||
luaK_exp2nextreg(fs, &func);
|
||||
base = func.u.info;
|
||||
codestring(&arg, name);
|
||||
luaK_exp2nextreg(fs, &arg);
|
||||
expr(ls, &val);
|
||||
luaK_exp2nextreg(fs, &val);
|
||||
init_exp(&val, VCALL, luaK_codeABC(fs, OP_CALL, base, 3, 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);
|
||||
@@ -2225,6 +2271,10 @@ static void statement (LexState *ls) {
|
||||
gotostat(ls, line);
|
||||
break;
|
||||
}
|
||||
case TK_ENVVAR: { /* stat -> '$' NAME '=' expr */
|
||||
envstat(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