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:
Cormac Shannon
2026-02-28 18:08:09 +00:00
parent a773398cab
commit 882a90be48
4 changed files with 94 additions and 17 deletions

View File

@@ -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"