First implementation of global declarations

This commit is contained in:
Roberto Ierusalimschy
2025-05-05 16:24:59 -03:00
parent e055905914
commit be81209063
10 changed files with 272 additions and 117 deletions

7
llex.c
View File

@@ -40,11 +40,16 @@
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
#if defined(LUA_COMPAT_GLOBAL)
#define GLOBALLEX ".g" /* not recognizable by the scanner */
#else
#define GLOBALLEX "global"
#endif
/* ORDER RESERVED */
static const char *const luaX_tokens [] = {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "goto", "if",
"end", "false", "for", "function", GLOBALLEX, "goto", "if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"//", "..", "...", "==", ">=", "<=", "~=",

4
llex.h
View File

@@ -33,8 +33,8 @@ enum RESERVED {
/* terminal symbols denoted by reserved words */
TK_AND = FIRST_RESERVED, TK_BREAK,
TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
TK_GLOBAL, TK_GOTO, TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR,
TK_REPEAT, TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
/* other terminal symbols */
TK_IDIV, TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE,
TK_SHL, TK_SHR,

View File

@@ -30,8 +30,8 @@
/* maximum number of local variables per function (must be smaller
than 250, due to the bytecode format) */
/* maximum number of variable declarationss per function (must be
smaller than 250, due to the bytecode format) */
#define MAXVARS 200
@@ -54,6 +54,7 @@ typedef struct BlockCnt {
lu_byte upval; /* true if some variable in the block is an upvalue */
lu_byte isloop; /* 1 if 'block' is a loop; 2 if it has pending breaks */
lu_byte insidetbc; /* true if inside the scope of a to-be-closed var. */
lu_byte globdec; /* true if inside the scope of any global declaration */
} BlockCnt;
@@ -188,10 +189,10 @@ static short registerlocalvar (LexState *ls, FuncState *fs,
/*
** Create a new local variable with the given 'name' and given 'kind'.
** Create a new variable with the given 'name' and given 'kind'.
** Return its index in the function.
*/
static int new_localvarkind (LexState *ls, TString *name, lu_byte kind) {
static int new_varkind (LexState *ls, TString *name, lu_byte kind) {
lua_State *L = ls->L;
FuncState *fs = ls->fs;
Dyndata *dyd = ls->dyd;
@@ -211,7 +212,7 @@ static int new_localvarkind (LexState *ls, TString *name, lu_byte kind) {
** Create a new local variable with the given 'name' and regular kind.
*/
static int new_localvar (LexState *ls, TString *name) {
return new_localvarkind(ls, name, VDKREG);
return new_varkind(ls, name, VDKREG);
}
#define new_localvarliteral(ls,v) \
@@ -238,7 +239,7 @@ static Vardesc *getlocalvardesc (FuncState *fs, int vidx) {
static lu_byte reglevel (FuncState *fs, int nvar) {
while (nvar-- > 0) {
Vardesc *vd = getlocalvardesc(fs, nvar); /* get previous variable */
if (vd->vd.kind != RDKCTC) /* is in a register? */
if (varinreg(vd)) /* is in a register? */
return cast_byte(vd->vd.ridx + 1);
}
return 0; /* no variables in registers */
@@ -259,7 +260,7 @@ lu_byte luaY_nvarstack (FuncState *fs) {
*/
static LocVar *localdebuginfo (FuncState *fs, int vidx) {
Vardesc *vd = getlocalvardesc(fs, vidx);
if (vd->vd.kind == RDKCTC)
if (!varinreg(vd))
return NULL; /* no debug info. for constants */
else {
int idx = vd->vd.pidx;
@@ -401,7 +402,9 @@ static int searchvar (FuncState *fs, TString *n, expdesc *var) {
if (eqstr(n, vd->vd.name)) { /* found? */
if (vd->vd.kind == RDKCTC) /* compile-time constant? */
init_exp(var, VCONST, fs->firstlocal + i);
else /* real variable */
else if (vd->vd.kind == GDKREG || vd->vd.kind == GDKCONST)
init_exp(var, VGLOBAL, i);
else /* local variable */
init_var(fs, var, i);
return cast_int(var->k);
}
@@ -440,9 +443,6 @@ static void marktobeclosed (FuncState *fs) {
** 'var' as 'void' as a flag.
*/
static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
if (fs == NULL) /* no more levels? */
init_exp(var, VVOID, 0); /* default is global */
else {
int v = searchvar(fs, n, var); /* look up locals at current level */
if (v >= 0) { /* found? */
if (v == VLOCAL && !base)
@@ -451,7 +451,10 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
else { /* not found as local at current level; try upvalues */
int idx = searchupvalue(fs, n); /* try existing upvalues */
if (idx < 0) { /* not found? */
if (fs->prev != NULL) /* more levels? */
singlevaraux(fs->prev, n, var, 0); /* try upper levels */
else /* no more levels */
init_exp(var, VGLOBAL, -1); /* global by default */
if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
idx = newupvalue(fs, n, var); /* will be a new upvalue */
else /* it is a global or a constant */
@@ -459,7 +462,6 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
}
init_exp(var, VUPVAL, idx); /* new or old upvalue */
}
}
}
@@ -471,10 +473,15 @@ static void singlevar (LexState *ls, expdesc *var) {
TString *varname = str_checkname(ls);
FuncState *fs = ls->fs;
singlevaraux(fs, varname, var, 1);
if (var->k == VVOID) { /* global name? */
if (var->k == VGLOBAL) { /* global name? */
expdesc key;
/* global by default in the scope of a global declaration? */
if (var->u.info == -1 && fs->bl->globdec)
luaK_semerror(ls, "variable '%s' not declared", getstr(varname));
singlevaraux(fs, ls->envn, var, 1); /* get environment variable */
lua_assert(var->k != VVOID); /* this one must exist */
if (var->k == VGLOBAL)
luaK_semerror(ls, "_ENV is global when accessing variable '%s'",
getstr(varname));
luaK_exp2anyregup(fs, var); /* but could be a constant */
codestring(&key, varname); /* key is variable name */
luaK_indexed(fs, var, &key); /* env[varname] */
@@ -664,8 +671,13 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
bl->firstlabel = fs->ls->dyd->label.n;
bl->firstgoto = fs->ls->dyd->gt.n;
bl->upval = 0;
/* inherit 'insidetbc' from enclosing block */
bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
bl->previous = fs->bl;
/* inherit 'globdec' from enclosing block or enclosing function */
bl->globdec = fs->bl != NULL ? fs->bl->globdec
: fs->prev != NULL ? fs->prev->bl->globdec
: 0; /* chunk's first block */
bl->previous = fs->bl; /* link block in function's block list */
fs->bl = bl;
lua_assert(fs->freereg == luaY_nvarstack(fs));
}
@@ -1600,7 +1612,7 @@ static void fornum (LexState *ls, TString *varname, int line) {
int base = fs->freereg;
new_localvarliteral(ls, "(for state)");
new_localvarliteral(ls, "(for state)");
new_localvarkind(ls, varname, RDKCONST); /* control variable */
new_varkind(ls, varname, RDKCONST); /* control variable */
checknext(ls, '=');
exp1(ls); /* initial value */
checknext(ls, ',');
@@ -1627,7 +1639,7 @@ static void forlist (LexState *ls, TString *indexname) {
new_localvarliteral(ls, "(for state)"); /* iterator function */
new_localvarliteral(ls, "(for state)"); /* state */
new_localvarliteral(ls, "(for state)"); /* closing var. (after swap) */
new_localvarkind(ls, indexname, RDKCONST); /* control variable */
new_varkind(ls, indexname, RDKCONST); /* control variable */
/* other declared variables */
while (testnext(ls, ',')) {
new_localvar(ls, str_checkname(ls));
@@ -1702,7 +1714,7 @@ static void localfunc (LexState *ls) {
}
static lu_byte getlocalattribute (LexState *ls) {
static lu_byte getvarattribute (LexState *ls) {
/* ATTRIB -> ['<' Name '>'] */
if (testnext(ls, '<')) {
TString *ts = str_checkname(ls);
@@ -1738,8 +1750,8 @@ static void localstat (LexState *ls) {
expdesc e;
do {
TString *vname = str_checkname(ls);
lu_byte kind = getlocalattribute(ls);
vidx = new_localvarkind(ls, vname, kind);
lu_byte kind = getvarattribute(ls);
vidx = new_varkind(ls, vname, kind);
if (kind == RDKTOCLOSE) { /* to-be-closed? */
if (toclose != -1) /* one already present? */
luaK_semerror(ls, "multiple to-be-closed variables in local list");
@@ -1769,6 +1781,24 @@ static void localstat (LexState *ls) {
}
static void globalstat (LexState *ls) {
FuncState *fs = ls->fs;
luaX_next(ls); /* skip 'global' */
do {
TString *vname = str_checkname(ls);
lu_byte kind = getvarattribute(ls);
if (kind == RDKTOCLOSE)
luaK_semerror(ls, "global variable ('%s') cannot be to-be-closed",
getstr(vname));
/* adjust kind for global variable */
kind = (kind == VDKREG) ? GDKREG : GDKCONST;
new_varkind(ls, vname, kind);
fs->nactvar++; /* activate declaration */
} while (testnext(ls, ','));
fs->bl->globdec = 1; /* code is in the scope of a global declaration */
}
static int funcname (LexState *ls, expdesc *v) {
/* funcname -> NAME {fieldsel} [':' NAME] */
int ismethod = 0;
@@ -1888,6 +1918,10 @@ static void statement (LexState *ls) {
localstat(ls);
break;
}
case TK_GLOBAL: { /* stat -> globalstat */
globalstat(ls);
break;
}
case TK_DBCOLON: { /* stat -> label */
luaX_next(ls); /* skip double colon */
labelstat(ls, str_checkname(ls), line);
@@ -1907,6 +1941,17 @@ static void statement (LexState *ls) {
gotostat(ls, line);
break;
}
case TK_NAME: {
/* compatibility code to parse global keyword when "global"
is not reserved */
if (eqstr(ls->t.seminfo.ts, luaS_newliteral(ls->L, "global"))) {
int lk = luaX_lookahead(ls);
if (lk == TK_NAME) { /* 'global name'? */
globalstat(ls);
break;
}
} /* else... */
} /* FALLTHROUGH */
default: { /* stat -> func | assignment */
exprstat(ls);
break;

View File

@@ -37,6 +37,9 @@ typedef enum {
info = result register */
VLOCAL, /* local variable; var.ridx = register index;
var.vidx = relative index in 'actvar.arr' */
VGLOBAL, /* global variable;
info = relative index in 'actvar.arr' (or -1 for
implicit declaration) */
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
VCONST, /* compile-time <const> variable;
info = absolute index in 'actvar.arr' */
@@ -87,10 +90,16 @@ typedef struct expdesc {
/* kinds of variables */
#define VDKREG 0 /* regular */
#define RDKCONST 1 /* constant */
#define VDKREG 0 /* regular local */
#define RDKCONST 1 /* local constant */
#define RDKTOCLOSE 2 /* to-be-closed */
#define RDKCTC 3 /* compile-time constant */
#define RDKCTC 3 /* local compile-time constant */
#define GDKREG 4 /* regular global */
#define GDKCONST 5 /* global constant */
/* variables that live in registers */
#define varinreg(v) ((v)->vd.kind <= RDKTOCLOSE)
/* description of an active local variable */
typedef union Vardesc {

View File

@@ -14,6 +14,7 @@
/* test Lua with compatibility code */
#define LUA_COMPAT_MATHLIB
#define LUA_COMPAT_LT_LE
#undef LUA_COMPAT_GLOBAL
#define LUA_DEBUG

View File

@@ -355,6 +355,12 @@
** ===================================================================
*/
/*
@@ LUA_COMPAT_GLOBAL avoids 'global' being a reserved word
*/
#define LUA_COMPAT_GLOBAL
/*
@@ LUA_COMPAT_5_3 controls other macros for compatibility with Lua 5.3.
** You can define it to get all options, or change specific options

View File

@@ -213,11 +213,88 @@ of a given value @seeF{type}.
}
@sect2{globalenv| @title{Environments and the Global Environment}
@sect2{globalenv| @title{Scopes, Variables, and Environments}
@index{visibility}
A variable name refers to a global or a local variable according
to the declaration that is in context at that point of the code.
(For the purposes of this discussion,
a function's formal parameter is equivalent to a local variable.)
All chunks start with an implicit declaration @T{global *},
which declares all free names as global variables;
this implicit declaration becomes void inside the scope of any other
@Rw{global} declaration, regardless of the names being declared.
@verbatim{
X = 1 -- Ok, global by default
do
global Y -- voids implicit initial declaration
X = 1 -- ERROR, X not declared
Y = 1 -- Ok, Y declared as global
end
X = 2 -- Ok, global by default again
}
So, outside any global declaration,
Lua works as @x{global-by-default}.
Inside any global declaration,
Lua works without a default:
All variables must be declared.
Lua is a lexically scoped language.
The scope of a variable declaration begins at the first statement after
the declaration and lasts until the last non-void statement
of the innermost block that includes the declaration.
(@emph{Void statements} are labels and empty statements.)
A declaration shadows any declaration for the same name that
is in context at the point of the declaration. Inside this
shadow, any outer declaration for that name is void.
See the next example:
@verbatim{
global print, x
x = 10 -- global variable
do -- new block
local x = x -- new 'x', with value 10
print(x) --> 10
x = x+1
do -- another block
local x = x+1 -- another 'x'
print(x) --> 12
end
print(x) --> 11
end
print(x) --> 10 (the global one)
}
Notice that, in a declaration like @T{local x = x},
the new @id{x} being declared is not in scope yet,
and so the @id{x} in the left-hand side refers to the outside variable.
Because of the @x{lexical scoping} rules,
local variables can be freely accessed by functions
defined inside their scope.
A local variable used by an inner function is called an @def{upvalue}
(or @emphx{external local variable}, or simply @emphx{external variable})
inside the inner function.
Notice that each execution of a @Rw{local} statement
defines new local variables.
Consider the following example:
@verbatim{
a = {}
local x = 20
for i = 1, 10 do
local y = 0
a[i] = function () y = y + 1; return x + y end
end
}
The loop creates ten closures
(that is, ten instances of the anonymous function).
Each of these closures uses a different @id{y} variable,
while all of them share the same @id{x}.
As we will discuss further in @refsec{variables} and @refsec{assignment},
any reference to a free name
(that is, a name not bound to any declaration) @id{var}
any reference to a global variable @id{var}
is syntactically translated to @T{_ENV.var}.
Moreover, every chunk is compiled in the scope of
an external local variable named @id{_ENV} @see{chunks},
@@ -225,12 +302,14 @@ so @id{_ENV} itself is never a free name in a chunk.
Despite the existence of this external @id{_ENV} variable and
the translation of free names,
@id{_ENV} is a completely regular name.
@id{_ENV} is a regular name.
In particular,
you can define new variables and parameters with that name.
Each reference to a free name uses the @id{_ENV} that is
visible at that point in the program,
following the usual visibility rules of Lua @see{visibility}.
(However, you should not define @id{_ENV} as a global variable,
otherwise @T{_ENV.var} would translate to
@T{_ENV._ENV.var} and so on, in an infinite loop.)
Each reference to a global variable name uses the @id{_ENV} that is
visible at that point in the program.
Any table used as the value of @id{_ENV} is called an @def{environment}.
@@ -244,8 +323,8 @@ When Lua loads a chunk,
the default value for its @id{_ENV} variable
is the global environment @seeF{load}.
Therefore, by default,
free names in Lua code refer to entries in the global environment
and, therefore, they are also called @def{global variables}.
global variables in Lua code refer to entries in the global environment
and, therefore, they act as conventional global variables.
Moreover, all standard libraries are loaded in the global environment
and some functions there operate on that environment.
You can use @Lid{load} (or @Lid{loadfile})
@@ -1198,17 +1277,15 @@ global variables, local variables, and table fields.
A single name can denote a global variable or a local variable
(or a function's formal parameter,
which is a particular kind of local variable):
which is a particular kind of local variable) @see{globalenv}:
@Produc{
@producname{var}@producbody{@bnfNter{Name}}
}
@bnfNter{Name} denotes identifiers @see{lexical}.
Any variable name is assumed to be global unless explicitly declared
as a local @see{localvar}.
@x{Local variables} are @emph{lexically scoped}:
Because variables are @emph{lexically scoped},
local variables can be freely accessed by functions
defined inside their scope @see{visibility}.
defined inside their scope @see{globalenv}.
Before the first assignment to a variable, its value is @nil.
@@ -1227,8 +1304,6 @@ The syntax @id{var.Name} is just syntactic sugar for
An access to a global variable @id{x}
is equivalent to @id{_ENV.x}.
Due to the way that chunks are compiled,
the variable @id{_ENV} itself is never global @see{globalenv}.
}
@@ -1571,17 +1646,18 @@ Function calls are explained in @See{functioncall}.
}
@sect3{localvar| @title{Local Declarations}
@x{Local variables} can be declared anywhere inside a block.
The declaration can include an initialization:
@sect3{localvar| @title{Variable Declarations}
Local and global variables can be declared anywhere inside a block.
The declaration for locals can include an initialization:
@Produc{
@producname{stat}@producbody{@Rw{local} attnamelist @bnfopt{@bnfter{=} explist}}
@producname{stat}@producbody{@Rw{global} attnamelist}
@producname{attnamelist}@producbody{
@bnfNter{Name} attrib @bnfrep{@bnfter{,} @bnfNter{Name} attrib}}
}
If present, an initial assignment has the same semantics
of a multiple assignment @see{assignment}.
Otherwise, all variables are initialized with @nil.
Otherwise, all local variables are initialized with @nil.
Each variable name may be postfixed by an attribute
(a name between angle brackets):
@@ -1595,11 +1671,22 @@ that is, a variable that cannot be assigned to
after its initialization;
and @id{close}, which declares a to-be-closed variable @see{to-be-closed}.
A list of variables can contain at most one to-be-closed variable.
Only local variables can have the @id{close} attribute.
Note that, for global variables,
the @emph{read-only} atribute is only a syntactical restriction:
@verbatim{
global X <const>
X = 1 -- ERROR
_ENV.X = 1 -- Ok
foo() -- 'foo' can freely change the global X
}
A chunk is also a block @see{chunks},
and so local variables can be declared in a chunk outside any explicit block.
and so variables can be declared in a chunk outside any explicit block.
The visibility rules for local variables are explained in @See{visibility}.
The visibility rules for variable declarations
are explained in @See{globalenv}.
}
@@ -2356,58 +2443,6 @@ return x,y,f() -- returns x, y, and all results from f().
}
@sect2{visibility| @title{Visibility Rules}
@index{visibility}
Lua is a lexically scoped language.
The scope of a local variable begins at the first statement after
its declaration and lasts until the last non-void statement
of the innermost block that includes the declaration.
(@emph{Void statements} are labels and empty statements.)
Consider the following example:
@verbatim{
x = 10 -- global variable
do -- new block
local x = x -- new 'x', with value 10
print(x) --> 10
x = x+1
do -- another block
local x = x+1 -- another 'x'
print(x) --> 12
end
print(x) --> 11
end
print(x) --> 10 (the global one)
}
Notice that, in a declaration like @T{local x = x},
the new @id{x} being declared is not in scope yet,
and so the second @id{x} refers to the outside variable.
Because of the @x{lexical scoping} rules,
local variables can be freely accessed by functions
defined inside their scope.
A local variable used by an inner function is called an @def{upvalue}
(or @emphx{external local variable}, or simply @emphx{external variable})
inside the inner function.
Notice that each execution of a @Rw{local} statement
defines new local variables.
Consider the following example:
@verbatim{
a = {}
local x = 20
for i = 1, 10 do
local y = 0
a[i] = function () y = y + 1; return x + y end
end
}
The loop creates ten closures
(that is, ten instances of the anonymous function).
Each of these closures uses a different @id{y} variable,
while all of them share the same @id{x}.
}
}
@@ -9535,6 +9570,7 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
@OrNL @Rw{function} funcname funcbody
@OrNL @Rw{local} @Rw{function} @bnfNter{Name} funcbody
@OrNL @Rw{local} attnamelist @bnfopt{@bnfter{=} explist}
@OrNL @Rw{global} attnamelist
}
@producname{attnamelist}@producbody{

View File

@@ -349,6 +349,7 @@ end, "crl")
function f(a,b)
global collectgarbage, assert, g, string
collectgarbage()
local _, x = debug.getlocal(1, 1)
local _, y = debug.getlocal(1, 2)

View File

@@ -1,6 +1,8 @@
-- $Id: testes/goto.lua $
-- See Copyright Notice in file lua.h
print("testing goto and global declarations")
collectgarbage()
local function errmsg (code, m)
@@ -280,7 +282,47 @@ end
foo()
--------------------------------------------------------------------------------
--------------------------------------------------------------------------
do
global print, load, T<const>; global assert<const>
global string
local function checkerr (code, err)
local st, msg = load(code)
assert(not st and string.find(msg, err))
end
-- globals must be declared after a global declaration
checkerr("global none; X = 1", "variable 'X'")
-- global variables cannot be to-be-closed
checkerr("global X<close>", "cannot be")
do
local X = 10
do global X; X = 20 end
assert(X == 10) -- local X
end
assert(_ENV.X == 20) -- global X
-- '_ENV' cannot be global
checkerr("global _ENV, a; a = 10", "variable 'a'")
-- global declarations inside functions
checkerr([[
global none
local function foo () XXX = 1 end --< ERROR]], "variable 'XXX'")
if not T then -- when not in "test mode", "global" isn't reserved
assert(load("global = 1; return global")() == 1)
print " ('global' is not a reserved word)"
else
-- "global" reserved, cannot be used as a variable
assert(not load("global = 1; return global"))
end
end
print'OK'

View File

@@ -3,6 +3,14 @@
print("testing numbers and math lib")
local math = require "math"
local string = require "string"
global none
global print, assert, pcall, type, pairs, load
global tonumber, tostring, select
local minint <const> = math.mininteger
local maxint <const> = math.maxinteger
@@ -184,7 +192,7 @@ do
for i = -3, 3 do -- variables avoid constant folding
for j = -3, 3 do
-- domain errors (0^(-n)) are not portable
if not _port or i ~= 0 or j > 0 then
if not _ENV._port or i ~= 0 or j > 0 then
assert(eq(i^j, 1 / i^(-j)))
end
end
@@ -430,7 +438,7 @@ for i = 2,36 do
assert(tonumber('\t10000000000\t', i) == i10)
end
if not _soft then
if not _ENV._soft then
-- tests with very long numerals
assert(tonumber("0x"..string.rep("f", 13)..".0") == 2.0^(4*13) - 1)
assert(tonumber("0x"..string.rep("f", 150)..".0") == 2.0^(4*150) - 1)
@@ -632,7 +640,7 @@ assert(maxint % -2 == -1)
-- non-portable tests because Windows C library cannot compute
-- fmod(1, huge) correctly
if not _port then
if not _ENV._port then
local function anan (x) assert(isNaN(x)) end -- assert Not a Number
anan(0.0 % 0)
anan(1.3 % 0)
@@ -779,6 +787,7 @@ assert(a == '10' and b == '20')
do
print("testing -0 and NaN")
global rawset, undef
local mz <const> = -0.0
local z <const> = 0.0
assert(mz == z)
@@ -1074,6 +1083,7 @@ do
-- different numbers should print differently.
-- check pairs of floats with minimum detectable difference
local p = floatbits - 1
global ipairs
for i = 1, maxexp - 1 do
for _, i in ipairs{-i, i} do
local x = 2^i