Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
Nodemcu Firmware
Commits
dba57fa0
Commit
dba57fa0
authored
Aug 24, 2021
by
Johny Mattsson
Browse files
Merge branch 'dev-esp32-idf4-lua53' into dev-esp32-idf4
parents
3a6961cc
8e5ce49d
Changes
224
Show whitespace changes
Inline
Side-by-side
components/lua/lua-5.3/lmem.h
0 → 100644
View file @
dba57fa0
/*
** $Id: lmem.h,v 1.43.1.1 2017/04/19 17:20:42 roberto Exp $
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/
#ifndef lmem_h
#define lmem_h
#include <stddef.h>
#include "llimits.h"
#include "lua.h"
/*
** This macro reallocs a vector 'b' from 'on' to 'n' elements, where
** each element has size 'e'. In case of arithmetic overflow of the
** product 'n'*'e', it raises an error (calling 'luaM_toobig'). Because
** 'e' is always constant, it avoids the runtime division MAX_SIZET/(e).
**
** (The macro is somewhat complex to avoid warnings: The 'sizeof'
** comparison avoids a runtime comparison when overflow cannot occur.
** The compiler should be able to optimize the real test by itself, but
** when it does it, it may give a warning about "comparison is always
** false due to limited range of data type"; the +1 tricks the compiler,
** avoiding this warning but also this optimization.)
*/
#define luaM_reallocv(L,b,on,n,e) \
(((sizeof(n) >= sizeof(size_t) && cast(size_t, (n)) + 1 > MAX_SIZET/(e)) \
? luaM_toobig(L) : cast_void(0)) , \
luaM_realloc_(L, (b), (on)*(e), (n)*(e)))
/*
** Arrays of chars do not need any test
*/
#define luaM_reallocvchar(L,b,on,n) \
cast(char *, luaM_realloc_(L, (b), (on)*sizeof(char), (n)*sizeof(char)))
#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)
#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0)
#define luaM_freearray(L, b, n) luaM_realloc_(L, (b), (n)*sizeof(*(b)), 0)
#define luaM_malloc(L,s) luaM_realloc_(L, NULL, 0, (s))
#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t)))
#define luaM_newvector(L,n,t) \
cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t)))
#define luaM_newobject(L,tag,s) luaM_realloc_(L, NULL, tag, (s))
#define luaM_growvector(L,v,nelems,size,t,limit,e) \
if ((nelems)+1 > (size)) \
((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
#define luaM_reallocvector(L, v,oldn,n,t) \
((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))
LUAI_FUNC
l_noret
luaM_toobig
(
lua_State
*
L
);
/* not to be called directly */
LUAI_FUNC
void
*
luaM_realloc_
(
lua_State
*
L
,
void
*
block
,
size_t
oldsize
,
size_t
size
);
LUAI_FUNC
void
*
luaM_growaux_
(
lua_State
*
L
,
void
*
block
,
int
*
size
,
size_t
size_elem
,
int
limit
,
const
char
*
what
);
#endif
components/lua/lua-5.3/lnodemcu.c
0 → 100644
View file @
dba57fa0
#define LUA_CORE
#include "lua.h"
#include <string.h>
#include <stdlib.h>
#include "lobject.h"
#include "lstate.h"
#include "lapi.h"
#include "lauxlib.h"
#include "lfunc.h"
#include "lgc.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lnodemcu.h"
#include "lundump.h"
#include "lzio.h"
#include "lfs.h"
#ifdef LUA_USE_ESP
#include "platform.h"
#include "vfs.h"
#include "task/task.h"
#else
// On the cross-compiler we do need the LFS reload mechanism, regardless
#undef CONFIG_NODEMCU_EMBEDDED_LFS_SIZE
#endif
/*
** This is a mixed bag of NodeMCU additions broken into the following sections:
** * POSIX vs VFS file API abstraction
** * Emulate Platform_XXX() API
** * ESP and HOST lua_debugbreak() test stubs
** * NodeMCU lua.h LUA_API extensions
** * NodeMCU lauxlib.h LUALIB_API extensions
** * NodeMCU bootstrap to set up and to reimage LFS resources
**
** Just search down for //== or ==// to flip through the sections.
*/
#define byte_addr(p) cast(char *,p)
#define byteptr(p) cast(lu_byte *, p)
#define byteoffset(p,q) ((int) cast(ptrdiff_t, (byteptr(p) - byteptr(q))))
#define wordptr(p) cast(lu_int32 *, p)
#define wordoffset(p,q) (wordptr(p) - wordptr(q))
//====================== Wrap POSIX and VFS file API =========================//
#ifdef LUA_USE_ESP
int
luaopen_file
(
lua_State
*
L
);
# define l_file(f) int f
# define l_open(f) vfs_open(f, "r")
# define l_close(f) vfs_close(f)
# define l_feof(f) vfs_eof(f)
# define l_read(f,b) vfs_read(f, b, sizeof (b))
# define l_rewind(f) vfs_lseek(f, 0, VFS_SEEK_SET)
#else
# define l_file(f) FILE *f
# define l_open(n) fopen(n,"rb")
# define l_close(f) fclose(f)
# define l_feof(f) feof(f)
# define l_read(f,b) fread(b, 1, sizeof (b), f)
# define l_rewind(f) rewind(f)
#endif
#ifdef LUA_USE_ESP
extern
void
dbg_printf
(
const
char
*
fmt
,
...);
// DEBUG
#undef printf
#define printf(...) dbg_printf(__VA_ARGS__) // DEBUG
#define FLASH_PAGE_SIZE INTERNAL_FLASH_SECTOR_SIZE
/* Erasing the LFS invalidates ESP instruction cache, so doing a block 64Kb */
/* read is the simplest way to flush the icache, restoring cache coherency */
#define flush_icache(F) \
UNUSED(memcmp(F->addr, F->addr+(0x8000/sizeof(*F->addr)), 0x8000));
#define unlockFlashWrite()
#define lockFlashWrite()
#else // LUA_USE_HOST
//==== Emulate Platform_XXX() API within host luac.cross -e environement =====//
#include<stdio.h> // DEBUG
/*
** The ESP implementation use a platform_XXX() API to provide a level of
** H/W abstraction. The following functions and macros emulate a subset
** of this API for the host environment. LFSregion is the true address in
** the luac process address space of the mapped LFS region. All actual
** erasing and writing is done relative to this address.
**
** In normal LFS emulation the LFSaddr is also set to this LFSregion address
** so that any subsequent execution using LFS refers to the correct memory
** address.
**
** The second LFS mode is used to create absolute LFS images for directly
** downloading to the ESP or including in a firmware image, and in this case
** LFSaddr refers to the actual ESP mapped address of the ESP LFS region.
** This is a 32-bit address typically in the address range 0x40210000-0x402FFFFF
** (and with the high 32bits set to 0 in the case of 64-bit execution). Such
** images are solely intended for ESP execution and any attempt to execute
** them in a host execution environment will result in an address exception.
*/
#define PLATFORM_RCR_FLASHLFS 4
#define LFS_SIZE 0x40000
#define FLASH_PAGE_SIZE 0x1000
#define FLASH_BASE 0x90000
/* Some 'Random' but typical value */
void
*
LFSregion
=
NULL
;
static
void
*
LFSaddr
=
NULL
;
static
size_t
LFSbase
=
FLASH_BASE
;
extern
char
*
LFSimageName
;
#ifdef __unix__
/* On POSIX systems we can toggle the "Flash" write attribute */
#include <sys/mman.h>
#define aligned_malloc(a,n) posix_memalign(&a, FLASH_PAGE_SIZE, (n))
#define unlockFlashWrite() mprotect(LFSaddr, LFS_SIZE, PROT_READ| PROT_WRITE)
#define lockFlashWrite() mprotect(LFSaddr, LFS_SIZE, PROT_READ)
#else
#define aligned_malloc(a,n) ((a = malloc(n)) == NULL)
#define unlockFlashWrite()
#define lockFlashWrite()
#endif
#define platform_flash_get_sector_of_address(n) ((n)>>12)
void
luaN_setabsolute
(
lu_int32
addr
)
{
LFSaddr
=
cast
(
void
*
,
cast
(
intptr_t
,
addr
));
LFSbase
=
addr
;
}
bool
lfs_get_location
(
lfs_location_info_t
*
out
)
{
if
(
!
LFSregion
)
{
if
(
aligned_malloc
(
LFSregion
,
LFS_SIZE
))
return
false
;
memset
(
LFSregion
,
~
0
,
LFS_SIZE
);
lockFlashWrite
();
}
if
(
LFSaddr
==
NULL
)
LFSaddr
=
LFSregion
;
out
->
size
=
LFS_SIZE
;
out
->
addr_mem
=
LFSaddr
;
out
->
addr_phys
=
LFSbase
;
return
true
;
}
bool
lfs_get_load_filename
(
char
*
buf
,
size_t
bufsiz
)
{
if
(
LFSimageName
)
strncpy
(
buf
,
LFSimageName
,
bufsiz
);
else
if
(
buf
&&
bufsiz
)
*
buf
=
0
;
return
true
;
}
bool
lfs_clear_load_filename
(
void
)
{
LFSimageName
=
NULL
;
return
true
;
}
static
void
platform_flash_erase_sector
(
lu_int32
i
)
{
lua_assert
(
i
>=
LFSbase
/
FLASH_PAGE_SIZE
&&
i
<
(
LFSbase
+
LFS_SIZE
)
/
FLASH_PAGE_SIZE
);
unlockFlashWrite
();
memset
(
byteptr
(
LFSregion
)
+
(
i
*
FLASH_PAGE_SIZE
-
LFSbase
),
~
(
0
),
FLASH_PAGE_SIZE
);
lockFlashWrite
();
}
static
void
platform_s_flash_write
(
const
void
*
from
,
lu_int32
to
,
lu_int32
len
)
{
lua_assert
(
to
>=
LFSbase
&&
to
+
len
<
LFSbase
+
LFS_SIZE
);
/* DEBUG */
unlockFlashWrite
();
memcpy
(
byteptr
(
LFSregion
)
+
(
to
-
LFSbase
),
from
,
len
);
lockFlashWrite
();
}
#define flush_icache(F)
/* not needed */
#endif
//============= ESP and HOST lua_debugbreak() test stubs =====================//
#ifdef DEVELOPMENT_USE_GDB
/*
* lua_debugbreak is a stub used by lua_assert() if DEVELOPMENT_USE_GDB is
* defined. On the ESP, instead of crashing out with an assert error, this hook
* starts the GDB remote stub if not already running and then issues a break.
* The rationale here is that when testing the developer might be using screen /
* PuTTY to work interactively with the Lua Interpreter via UART0. However if
* an assert triggers, then there is the option to exit the interactive session
* and start the Xtensa remote GDB which will then sync up with the remote GDB
* client to allow forensics of the error. On the host it is an stub which can
* be set as a breakpoint in the gdb debugger.
*/
extern
void
gdbstub_init
(
void
);
extern
void
gdbstub_redirect_output
(
int
);
LUALIB_API
void
lua_debugbreak
(
void
)
{
#ifdef LUA_USE_HOST
/* allows debug backtrace analysis of assert fails */
lua_writestring
(
" lua_debugbreak "
,
sizeof
(
" lua_debugbreak "
)
-
1
);
#else
static
int
repeat_entry
=
0
;
if
(
repeat_entry
==
0
)
{
dbg_printf
(
"Start up the gdb stub if not already started
\n
"
);
gdbstub_init
();
gdbstub_redirect_output
(
1
);
repeat_entry
=
1
;
}
asm
(
"break 0,0"
::
);
#endif
}
#endif
//===================== NodeMCU lua.h API extensions =========================//
LUA_API
int
lua_freeheap
(
void
)
{
#ifdef LUA_USE_HOST
return
MAX_INT
;
#else
return
(
int
)
esp_get_free_heap_size
();
#endif
}
LUA_API
int
lua_pushstringsarray
(
lua_State
*
L
,
int
opt
)
{
stringtable
*
strt
=
NULL
;
int
i
,
j
=
1
;
lua_lock
(
L
);
if
(
opt
==
0
)
strt
=
&
G
(
L
)
->
strt
;
#ifdef LUA_USE_ESP
else
if
(
opt
==
1
&&
G
(
L
)
->
ROstrt
.
hash
)
strt
=
&
G
(
L
)
->
ROstrt
;
#endif
if
(
strt
==
NULL
)
{
setnilvalue
(
L
->
top
);
api_incr_top
(
L
);
lua_unlock
(
L
);
return
0
;
}
Table
*
t
=
luaH_new
(
L
);
sethvalue
(
L
,
L
->
top
,
t
);
api_incr_top
(
L
);
luaH_resize
(
L
,
t
,
strt
->
nuse
,
0
);
luaC_checkGC
(
L
);
lua_unlock
(
L
);
/* loop around all strt hash entries */
for
(
i
=
0
,
j
=
1
;
i
<
strt
->
size
;
i
++
)
{
TString
*
e
;
/* loop around all TStings in this entry's chain */
for
(
e
=
strt
->
hash
[
i
];
e
;
e
=
e
->
u
.
hnext
)
{
TValue
s
;
setsvalue
(
L
,
&
s
,
e
);
luaH_setint
(
L
,
hvalue
(
L
->
top
-
1
),
j
++
,
&
s
);
}
}
return
1
;
}
LUA_API
void
lua_createrotable
(
lua_State
*
L
,
ROTable
*
t
,
const
ROTable_entry
*
e
,
ROTable
*
mt
)
{
int
i
,
j
;
lu_byte
flags
=
~
0
;
const
char
*
plast
=
(
char
*
)
"_"
;
for
(
i
=
0
;
e
[
i
].
key
;
i
++
)
{
if
(
e
[
i
].
key
[
0
]
==
'_'
&&
strcmp
(
e
[
i
].
key
,
plast
))
{
plast
=
e
[
i
].
key
;
lua_pushstring
(
L
,
e
[
i
].
key
);
for
(
j
=
0
;
j
<
TM_EQ
;
j
++
){
if
(
tsvalue
(
L
->
top
-
1
)
==
G
(
L
)
->
tmname
[
i
])
{
flags
|=
cast_byte
(
1u
<<
i
);
break
;
}
}
lua_pop
(
L
,
1
);
}
}
t
->
next
=
(
GCObject
*
)
1
;
t
->
tt
=
LUA_TTBLROF
;
t
->
marked
=
LROT_MARKED
;
t
->
flags
=
flags
;
t
->
lsizenode
=
i
;
t
->
metatable
=
cast
(
Table
*
,
mt
);
t
->
entry
=
cast
(
ROTable_entry
*
,
e
);
}
LUA_API
void
lua_getlfsconfig
(
lua_State
*
L
,
intptr_t
*
config
)
{
global_State
*
g
=
G
(
L
);
LFSHeader
*
l
=
g
->
l_LFS
;
if
(
!
config
)
return
;
lfs_location_info_t
lfs_loc
;
if
(
lfs_get_location
(
&
lfs_loc
)
&&
lfs_loc
.
addr_mem
==
l
)
{
config
[
0
]
=
(
intptr_t
)
lfs_loc
.
addr_mem
;
config
[
1
]
=
(
intptr_t
)
lfs_loc
.
addr_phys
;
config
[
2
]
=
(
intptr_t
)
lfs_loc
.
size
;
}
if
(
g
->
ROstrt
.
hash
)
{
config
[
3
]
=
l
->
flash_size
;
/* LFS region used */
config
[
4
]
=
l
->
timestamp
;
/* LFS region timestamp */
}
else
{
config
[
3
]
=
config
[
4
]
=
0
;
}
}
LUA_API
int
lua_pushlfsindex
(
lua_State
*
L
)
{
lua_lock
(
L
);
setobj2n
(
L
,
L
->
top
,
&
G
(
L
)
->
LFStable
);
api_incr_top
(
L
);
lua_unlock
(
L
);
return
ttnov
(
L
->
top
-
1
);
}
/*
* In Lua 5.3 luac.cross generates a top level Proto for each source file with
* one upvalue that must be the set to the _ENV variable when its closure is
* created, and as such this parallels some ldo.c processing.
*/
LUA_API
int
lua_pushlfsfunc
(
lua_State
*
L
)
{
lua_lock
(
L
);
const
TValue
*
t
=
&
G
(
L
)
->
LFStable
;
if
(
ttisstring
(
L
->
top
-
1
)
&&
ttistable
(
t
))
{
const
TValue
*
v
=
luaH_getstr
(
hvalue
(
t
),
tsvalue
(
L
->
top
-
1
));
if
(
ttislightuserdata
(
v
))
{
Proto
*
f
=
pvalue
(
v
);
/* The pvalue is a Proto * for the Lua function */
LClosure
*
cl
=
luaF_newLclosure
(
L
,
f
->
sizeupvalues
);
setclLvalue
(
L
,
L
->
top
-
1
,
cl
);
luaF_initupvals
(
L
,
cl
);
cl
->
p
=
f
;
if
(
cl
->
nupvalues
>=
1
)
{
/* does it have an upvalue? */
UpVal
*
uv1
=
cl
->
upvals
[
0
];
TValue
*
val
=
uv1
->
v
;
/* set 1st upvalue as global env table from registry */
setobj
(
L
,
val
,
luaH_getint
(
hvalue
(
&
G
(
L
)
->
l_registry
),
LUA_RIDX_GLOBALS
));
luaC_upvalbarrier
(
L
,
uv1
);
}
return
1
;
}
}
setnilvalue
(
L
->
top
-
1
);
lua_unlock
(
L
);
return
0
;
}
//================ NodeMCU lauxlib.h LUALIB_API extensions ===================//
/*
* Return an array of functions in LFS
*/
LUALIB_API
int
luaL_pushlfsmodules
(
lua_State
*
L
)
{
int
i
=
1
;
if
(
lua_pushlfsindex
(
L
)
==
LUA_TNIL
)
return
0
;
/* return nil if LFS not loaded */
lua_newtable
(
L
);
/* create dest table and move above LFS index ROTable */
lua_insert
(
L
,
-
2
);
lua_pushnil
(
L
);
while
(
lua_next
(
L
,
-
2
)
!=
0
)
{
lua_pop
(
L
,
1
);
/* dump the value (ptr to the Proto) */
lua_pushvalue
(
L
,
-
1
);
/* dup key (module name) */
lua_rawseti
(
L
,
-
4
,
i
++
);
}
lua_pop
(
L
,
1
);
/* dump the LFS index ROTable */
return
1
;
}
LUALIB_API
int
luaL_pushlfsdts
(
lua_State
*
L
)
{
intptr_t
config
[
5
];
lua_getlfsconfig
(
L
,
config
);
lua_pushinteger
(
L
,
config
[
4
]);
return
1
;
}
//======== NodeMCU bootstrap to set up and to reimage LFS resources ==========//
/*
** This processing uses 2 init hooks during the Lua startup. The first is
** called early in the Lua state setup to initialize the LFS if present. The
** second is only used to rebuild the LFS region; this requires the Lua
** environment to be in place, so this second hook is immediately before
** processing LUA_INIT.
**
** An application library initiates an LFS rebuild by writing a FLASHLFS
** message to the Reboot Config Record area (RCR), and then restarting the
** processor. This RCR record is read during startup by the 2nd hook. The
** content is the name of the Lua LFS image file to be loaded. If present then
** the LFS reload process is initiated instead of LUA_INIT. This uses lundump
** functions to load the components directly into the LFS region.
**
** FlashState used to share context with the low level lua_load write routines
** is passed as a ZIO data field. Note this is only within the phase
** processing and not across phases.
*/
typedef
struct
LFSflashState
{
lua_State
*
L
;
LFSHeader
hdr
;
l_file
(
f
);
lu_int32
*
addr
;
lu_int32
oNdx
;
/* in size_t units */
lu_int32
oChunkNdx
;
/* in size_t units */
lu_int32
*
oBuff
;
/* FLASH_PAGE_SIZE bytes */
lu_byte
*
inBuff
;
/* FLASH_PAGE_SIZE bytes */
lu_int32
inNdx
;
/* in bytes */
lu_int32
addrPhys
;
lu_int32
size
;
lu_int32
allocmask
;
stringtable
ROstrt
;
GCObject
*
pLTShead
;
}
LFSflashState
;
#define WORDSIZE sizeof(lu_int32)
#define OSIZE (FLASH_PAGE_SIZE/WORDSIZE)
#define ISIZE (FLASH_PAGE_SIZE)
#ifdef LUA_USE_ESP
#define ALIGN(F,n) (n + WORDSIZE - 1) / WORDSIZE;
#else
#define ALIGN(F,n) ((n + F->allocmask) & ~(F->allocmask)) / WORDSIZE;
#endif
#ifndef CONFIG_NODEMCU_EMBEDDED_LFS_SIZE
/* This conforms to the ZIO lua_Reader spec, hence the L parameter */
static
const
char
*
readF
(
lua_State
*
L
,
void
*
ud
,
size_t
*
size
)
{
UNUSED
(
L
);
LFSflashState
*
F
=
cast
(
LFSflashState
*
,
ud
);
if
(
F
->
inNdx
>
0
)
{
*
size
=
F
->
inNdx
;
F
->
inNdx
=
0
;
}
else
{
if
(
l_feof
(
F
->
f
))
return
NULL
;
*
size
=
l_read
(
F
->
f
,
F
->
inBuff
)
;
/* read block */
}
return
cast
(
const
char
*
,
F
->
inBuff
);
}
static
void
eraseLFS
(
LFSflashState
*
F
)
{
lu_int32
i
;
#ifdef LUA_USE_ESP
printf
(
"
\n
Erasing LFS from flash addr 0x%06x"
,
F
->
addrPhys
);
#endif
unlockFlashWrite
();
for
(
i
=
0
;
i
<
F
->
size
;
i
+=
FLASH_PAGE_SIZE
)
{
size_t
*
f
=
cast
(
size_t
*
,
F
->
addr
+
i
/
sizeof
(
*
f
));
lu_int32
s
=
platform_flash_get_sector_of_address
(
F
->
addrPhys
+
i
);
/* it is far faster not erasing if you don't need to */
#ifdef LUA_USE_ESP
if
(
*
f
==
~
0
&&
!
memcmp
(
f
,
f
+
1
,
FLASH_PAGE_SIZE
-
sizeof
(
*
f
)))
continue
;
printf
(
"."
);
#endif
platform_flash_erase_sector
(
s
);
}
#ifdef LUA_USE_ESP
printf
(
" to 0x%06x
\n
"
,
F
->
addrPhys
+
F
->
size
-
1
);
#endif
flush_icache
(
F
);
lockFlashWrite
();
}
#endif
LUAI_FUNC
void
luaN_setFlash
(
void
*
F
,
unsigned
int
o
)
{
luaN_flushFlash
(
F
);
/* flush the pending write buffer */
lua_assert
((
o
&
(
WORDSIZE
-
1
))
==
0
);
cast
(
LFSflashState
*
,
F
)
->
oChunkNdx
=
o
/
WORDSIZE
;
}
LUAI_FUNC
void
luaN_flushFlash
(
void
*
vF
)
{
LFSflashState
*
F
=
cast
(
LFSflashState
*
,
vF
);
lu_int32
start
=
F
->
addrPhys
+
F
->
oChunkNdx
*
WORDSIZE
;
lu_int32
size
=
F
->
oNdx
*
WORDSIZE
;
lua_assert
(
start
+
size
<
F
->
addrPhys
+
F
->
size
);
/* is write in bounds? */
//printf("Flush Buf: %6x (%u)\n", F->oNdx, size); //DEBUG
platform_s_flash_write
(
F
->
oBuff
,
start
,
size
);
F
->
oChunkNdx
+=
F
->
oNdx
;
F
->
oNdx
=
0
;
}
LUAI_FUNC
void
*
luaN_writeFlash
(
void
*
vF
,
const
void
*
rec
,
size_t
n
)
{
LFSflashState
*
F
=
cast
(
LFSflashState
*
,
vF
);
lu_byte
*
p
=
byteptr
(
F
->
addr
+
F
->
oChunkNdx
+
F
->
oNdx
);
//int i; printf("writing %4u bytes:", (lu_int32) n); for (i=0;i<n;i++){printf(" %02x", byteptr(rec)[i]);} printf("\n");
if
(
n
==
0
)
return
p
;
while
(
1
)
{
int
nw
=
ALIGN
(
F
,
n
);
if
(
F
->
oNdx
+
nw
>
OSIZE
)
{
/* record overflows the buffer so fill buffer, flush and repeat */
int
rem
=
OSIZE
-
F
->
oNdx
;
if
(
rem
)
memcpy
(
F
->
oBuff
+
F
->
oNdx
,
rec
,
rem
*
WORDSIZE
);
rec
=
cast
(
void
*
,
cast
(
lu_int32
*
,
rec
)
+
rem
);
n
-=
rem
*
WORDSIZE
;
F
->
oNdx
=
OSIZE
;
luaN_flushFlash
(
F
);
}
else
{
/* append remaining record to buffer */
F
->
oBuff
[
F
->
oNdx
+
nw
-
1
]
=
0
;
/* ensure any trailing odd byte are 0 */
memcpy
(
F
->
oBuff
+
F
->
oNdx
,
rec
,
n
);
F
->
oNdx
+=
nw
;
break
;
}
}
//int i; for (i=0;i<(rem * WORDSIZE); i++) {printf("%c%02x",i?' ':'.',*((lu_byte*)rec+i));}
//for (i=0;i<n; i++) printf("%c%02x",i?' ':'.',*((lu_byte*)rec+i));
//printf("\n");
return
p
;
}
/*
** Hook used in Lua Startup to carry out the optional LFS startup processes.
*/
LUAI_FUNC
int
luaN_init
(
lua_State
*
L
)
{
static
LFSflashState
*
F
=
NULL
;
static
LFSHeader
*
fh
;
char
fname
[
CONFIG_NODEMCU_FS_OBJ_NAME_LEN
];
bool
have_load_file
=
lfs_get_load_filename
(
fname
,
sizeof
(
fname
));
/*
* The first entry is called from lstate.c:f_luaopen() before modules
* are initialised. This is detected because F is NULL on first entry.
*/
if
(
F
==
NULL
)
{
size_t
Fsize
=
sizeof
(
LFSflashState
)
+
OSIZE
*
WORDSIZE
+
ISIZE
;
/* outlining the buffers just makes debugging easier. Sorry */
F
=
calloc
(
Fsize
,
1
);
F
->
oBuff
=
wordptr
(
F
+
1
);
F
->
inBuff
=
byteptr
(
F
->
oBuff
+
OSIZE
);
lfs_location_info_t
lfs_loc
;
if
(
lfs_get_location
(
&
lfs_loc
))
{
F
->
size
=
lfs_loc
.
size
;
F
->
addr
=
cast
(
lu_int32
*
,
lfs_loc
.
addr_mem
);
F
->
addrPhys
=
lfs_loc
.
addr_phys
;
fh
=
cast
(
LFSHeader
*
,
F
->
addr
);
if
(
!
have_load_file
)
{
global_State
*
g
=
G
(
L
);
g
->
LFSsize
=
F
->
size
;
g
->
l_LFS
=
fh
;
/* Set up LFS hooks on normal Entry */
if
(
fh
->
flash_sig
==
FLASH_SIG
)
{
g
->
seed
=
fh
->
seed
;
g
->
ROstrt
.
hash
=
cast
(
TString
**
,
F
->
addr
+
fh
->
oROhash
);
g
->
ROstrt
.
nuse
=
fh
->
nROuse
;
g
->
ROstrt
.
size
=
fh
->
nROsize
;
sethvalue
(
L
,
&
g
->
LFStable
,
cast
(
Table
*
,
F
->
addr
+
fh
->
protoROTable
));
lua_writestringerror
(
"LFS image %s
\n
"
,
"loaded"
);
}
else
if
((
fh
->
flash_sig
!=
0
&&
fh
->
flash_sig
!=
~
0
))
{
lua_writestringerror
(
"LFS image %s
\n
"
,
"corrupted."
);
#ifndef CONFIG_NODEMCU_EMBEDDED_LFS_SIZE
eraseLFS
(
F
);
#endif
}
}
}
return
0
;
}
else
{
/* hook 2 called from protected pmain, so can throw errors. */
#ifndef CONFIG_NODEMCU_EMBEDDED_LFS_SIZE
int
status
=
0
;
if
(
have_load_file
)
{
/* hook == 2 LFS image load */
ZIO
z
;
/*
* To avoid reboot loops, the load is only attempted once, so we
* always deleted the RCR record if we enter this path. Also note
* that this load process can throw errors and if so these are
* caught by the parent function in lua.c
*/
#ifdef DEVELOPMENT_USE_GDB
/* For GDB builds, prefixing the filename with ! forces a break in the hook */
if
(
fname
[
0
]
==
'!'
)
{
lua_debugbreak
();
F
->
LFSfileName
++
;
}
#endif
lfs_clear_load_filename
();
#ifdef LUA_USE_ESP
luaopen_file
(
L
);
#endif
if
(
!
(
F
->
f
=
l_open
(
fname
)))
{
free
(
F
);
return
luaL_error
(
L
,
"cannot open %s"
,
fname
);
}
eraseLFS
(
F
);
luaZ_init
(
L
,
&
z
,
readF
,
F
);
lua_lock
(
L
);
#ifdef LUA_USE_HOST
F
->
allocmask
=
(
LFSaddr
==
LFSregion
)
?
sizeof
(
size_t
)
-
1
:
sizeof
(
lu_int32
)
-
1
;
status
=
luaU_undumpLFS
(
L
,
&
z
,
LFSaddr
!=
LFSregion
);
#else
status
=
luaU_undumpLFS
(
L
,
&
z
,
0
);
#endif
lua_unlock
(
L
);
l_close
(
F
->
f
);
free
(
F
);
F
=
NULL
;
if
(
status
==
LUA_OK
)
lua_pushstring
(
L
,
"!LFSrestart!"
);
/* Signal a restart */
lua_error
(
L
);
/* throw error / restart request */
}
else
{
/* hook == 2, Normal startup */
free
(
F
);
F
=
NULL
;
}
return
status
;
#else
return
0
;
// Embedded LFS - no reloading possible
#endif
}
}
// =============================================================================
#define getfield(L,t,f) \
lua_getglobal(L, #t); luaL_getmetafield( L, 1, #f ); lua_remove(L, -2);
LUALIB_API
void
luaL_lfsreload
(
lua_State
*
L
)
{
#if defined(CONFIG_NODEMCU_EMBEDDED_FLS_SIZE)
(
void
)
L
;
lua_pushstring
(
L
,
"Not allowed to write to LFS section"
);
return
1
;
#else
#ifdef LUA_USE_ESP
size_t
l
;
int
off
=
0
;
const
char
*
img
=
lua_tolstring
(
L
,
1
,
&
l
);
#ifdef DEVELOPMENT_USE_GDB
if
(
*
img
==
'!'
)
/* For GDB builds, any leading ! is ignored for checking */
off
=
1
;
/* existence. This forces a debug break in the init hook */
#endif
lua_settop
(
L
,
1
);
lua_getglobal
(
L
,
"file"
);
if
(
lua_isnil
(
L
,
2
))
{
lua_pushstring
(
L
,
"No file system mounted"
);
return
;
}
lua_getfield
(
L
,
2
,
"exists"
);
lua_pushstring
(
L
,
img
+
off
);
lua_call
(
L
,
1
,
1
);
if
(
G
(
L
)
->
LFSsize
==
0
||
lua_toboolean
(
L
,
-
1
)
==
0
)
{
lua_pushstring
(
L
,
"No LFS partition allocated"
);
return
;
}
if
(
lfs_set_load_filename
(
img
))
{
esp_restart
();
luaL_error
(
L
,
"system restarting"
);
}
#else
(
void
)
L
;
#endif
#endif
}
#ifdef LUA_USE_ESP
/*
** Task callback handler to support pcallx with full traceback
*/
static
void
do_task
(
task_param_t
task_fn_ref
,
task_prio_t
prio
)
{
lua_State
*
L
=
lua_getstate
();
if
(
prio
<
LUA_TASK_LOW
||
prio
>
LUA_TASK_HIGH
)
luaL_error
(
L
,
"invalid post task"
);
/* Pop the CB func from the Reg */
lua_rawgeti
(
L
,
LUA_REGISTRYINDEX
,
(
int
)
task_fn_ref
);
luaL_checktype
(
L
,
-
1
,
LUA_TFUNCTION
);
luaL_unref
(
L
,
LUA_REGISTRYINDEX
,
(
int
)
task_fn_ref
);
lua_pushinteger
(
L
,
prio
);
luaL_pcallx
(
L
,
1
,
0
);
}
/*
** Schedule a Lua function for task execution
*/
LUALIB_API
int
luaL_posttask
(
lua_State
*
L
,
int
prio
)
{
// [-1, +0, -]
static
task_handle_t
task_handle
=
0
;
if
(
!
task_handle
)
task_handle
=
task_get_id
(
do_task
);
if
(
lua_isfunction
(
L
,
-
1
)
&&
prio
>=
LUA_TASK_LOW
&&
prio
<=
LUA_TASK_HIGH
)
{
int
task_fn_ref
=
luaL_ref
(
L
,
LUA_REGISTRYINDEX
);
if
(
!
task_post
(
prio
,
task_handle
,
(
task_param_t
)
task_fn_ref
))
{
luaL_unref
(
L
,
LUA_REGISTRYINDEX
,
task_fn_ref
);
luaL_error
(
L
,
"Task queue overflow. Task not posted"
);
}
return
task_fn_ref
;
}
else
{
return
luaL_error
(
L
,
"invalid post task"
);
}
}
#else
/*
** Task execution isn't supported on HOST builds so returns a -1 status
*/
LUALIB_API
int
luaL_posttask
(
lua_State
*
L
,
int
prio
)
{
// [-1, +0, -]
(
void
)
L
;
(
void
)
prio
;
return
-
1
;
}
#endif
components/lua/lua-5.3/lnodemcu.h
0 → 100644
View file @
dba57fa0
/*
* NodeMCU extensions to Lua 5.3 for readonly Flash memory support
*/
#ifndef lnodemcu_h
#define lnodemcu_h
#include "lua.h"
#include "lobject.h"
#include "llimits.h"
#include "ltm.h"
#ifdef LUA_USE_HOST
#define LRO_STRKEY(k) k
#define LOCK_IN_SECTION(s)
#else
#define LRO_STRKEY(k) ((__attribute__((aligned(4))) const char *) k)
#define LOCK_IN_SECTION(s) __attribute__((used,unused,section(".lua_" #s)))
#endif
/* Macros used to declare rotable entries */
#define LRO_FUNCVAL(v) {{.f = v}, LUA_TLCF}
#define LRO_LUDATA(v) {{.p = (void *) v}, LUA_TLIGHTUSERDATA}
#define LRO_NILVAL {{.p = NULL}, LUA_TNIL}
#define LRO_NUMVAL(v) {{.i = v}, LUA_TNUMINT}
#define LRO_INTVAL(v) LRO_NUMVAL(v)
#define LRO_FLOATVAL(v) {{.n = v}, LUA_TNUMFLT}
#define LRO_ROVAL(v) {{.gc = cast(GCObject *, &(v ## _ROTable))}, LUA_TTBLROF}
#define LROT_MARKED 0 //<<<<<<<<<< *** TBD *** >>>>>>>>>>>
#define LROT_FUNCENTRY(n,f) {LRO_STRKEY(#n), LRO_FUNCVAL(f)},
#define LROT_LUDENTRY(n,x) {LRO_STRKEY(#n), LRO_LUDATA(x)},
#define LROT_NUMENTRY(n,x) {LRO_STRKEY(#n), LRO_NUMVAL(x)},
#define LROT_INTENTRY(n,x) LROT_NUMENTRY(n,x)
#define LROT_FLOATENTRY(n,x) {LRO_STRKEY(#n), LRO_FLOATVAL(x)},
#define LROT_TABENTRY(n,t) {LRO_STRKEY(#n), LRO_ROVAL(t)},
#define LROT_TABLE(rt) const ROTable rt ## _ROTable
#define LROT_ENTRYREF(rt) (rt ##_entries)
#define LROT_TABLEREF(rt) (&rt ##_ROTable)
#define LROT_BEGIN(rt,mt,f) LROT_TABLE(rt); \
static ROTable_entry rt ## _entries[] = {
#define LROT_ENTRIES_IN_SECTION(rt,s) \
static ROTable_entry LOCK_IN_SECTION(s) rt ## _entries[] = {
#define LROT_END(rt,mt,f) {NULL, LRO_NILVAL} }; \
const ROTable rt ## _ROTable = { \
(GCObject *)1, LUA_TTBLROF, LROT_MARKED, \
cast(lu_byte, ~(f)), (sizeof(rt ## _entries)/sizeof(ROTable_entry)) - 1, \
cast(Table *, mt), cast(ROTable_entry *, rt ## _entries) };
#define LROT_BREAK(rt) };
#define LROT_MASK(m) cast(lu_byte, 1<<TM_ ## m)
/*
* These are statically coded can be any combination of the fast index tags
* listed in ltm.h: EQ, GC, INDEX, LEN, MODE, NEWINDEX or combined by anding
* GC+INDEX is the only common combination used, hence the combinaton macro
*/
#define LROT_MASK_EQ LROT_MASK(EQ)
#define LROT_MASK_GC LROT_MASK(GC)
#define LROT_MASK_INDEX LROT_MASK(INDEX)
#define LROT_MASK_LEN LROT_MASK(LEN)
#define LROT_MASK_MODE LROT_MASK(MODE)
#define LROT_MASK_NEWINDEX LROT_MASK(NEWINDEX)
#define LROT_MASK_GC_INDEX (LROT_MASK_GC | LROT_MASK_INDEX)
#define LUA_MAX_ROTABLE_NAME 32
/* Maximum length of a rotable name and of a string key*/
#ifdef LUA_CORE
#include "lstate.h"
#include "lzio.h"
LUAI_FUNC
int
luaN_init
(
lua_State
*
L
);
LUAI_FUNC
void
*
luaN_writeFlash
(
void
*
data
,
const
void
*
rec
,
size_t
n
);
LUAI_FUNC
void
luaN_flushFlash
(
void
*
);
LUAI_FUNC
void
luaN_setFlash
(
void
*
,
unsigned
int
o
);
#endif
#endif
components/lua/lua-5.3/loadlib.c
0 → 100644
View file @
dba57fa0
/*
** $Id: loadlib.c,v 1.130.1.1 2017/04/19 17:20:42 roberto Exp $
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
** This module contains an implementation of loadlib for Unix systems
** that have dlfcn, an implementation for Windows, and a stub for other
** systems.
*/
#define loadlib_c
#define LUA_LIB
#include "lprefix.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#ifndef LUA_USE_HOST
#include <fcntl.h>
#include "vfs.h"
#endif
/*
** LUA_IGMARK is a mark to ignore all before it when building the
** luaopen_ function name.
*/
#if !defined (LUA_IGMARK)
#define LUA_IGMARK "-"
#endif
/*
** LUA_CSUBSEP is the character that replaces dots in submodule names
** when searching for a C loader.
** LUA_LSUBSEP is the character that replaces dots in submodule names
** when searching for a Lua loader.
*/
#if !defined(LUA_CSUBSEP)
#define LUA_CSUBSEP LUA_DIRSEP
#endif
#if !defined(LUA_LSUBSEP)
#define LUA_LSUBSEP LUA_DIRSEP
#endif
/* prefix for open functions in C libraries */
#define LUA_POF "luaopen_"
/* separator for open functions in C libraries */
#define LUA_OFSEP "_"
#ifndef LUA_NODEMCU_NOCLOADERS
/*
** unique key for table in the registry that keeps handles
** for all loaded C libraries
*/
static
const
int
CLIBS
=
0
;
#endif
#define LIB_FAIL "open"
#define setprogdir(L) ((void)0)
/*
** system-dependent functions
*/
/*
** unload library 'lib'
*/
#ifndef LUA_NODEMCU_NOCLOADERS
static
void
lsys_unloadlib
(
void
*
lib
);
/*
** load C library in file 'path'. If 'seeglb', load with all names in
** the library global.
** Returns the library; in case of error, returns NULL plus an
** error string in the stack.
*/
static
void
*
lsys_load
(
lua_State
*
L
,
const
char
*
path
,
int
seeglb
);
/*
** Try to find a function named 'sym' in library 'lib'.
** Returns the function; in case of error, returns NULL plus an
** error string in the stack.
*/
static
lua_CFunction
lsys_sym
(
lua_State
*
L
,
void
*
lib
,
const
char
*
sym
);
#endif
#ifndef LUA_NODEMCU_NOCLOADERS
#if defined(LUA_USE_DLOPEN)
/* { */
/*
** {========================================================================
** This is an implementation of loadlib based on the dlfcn interface.
** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
** as an emulation layer on top of native functions.
** =========================================================================
*/
#include <dlfcn.h>
/*
** Macro to convert pointer-to-void* to pointer-to-function. This cast
** is undefined according to ISO C, but POSIX assumes that it works.
** (The '__extension__' in gnu compilers is only to avoid warnings.)
*/
#if defined(__GNUC__)
#define cast_func(p) (__extension__ (lua_CFunction)(p))
#else
#define cast_func(p) ((lua_CFunction)(p))
#endif
static
void
lsys_unloadlib
(
void
*
lib
)
{
dlclose
(
lib
);
}
static
void
*
lsys_load
(
lua_State
*
L
,
const
char
*
path
,
int
seeglb
)
{
void
*
lib
=
dlopen
(
path
,
RTLD_NOW
|
(
seeglb
?
RTLD_GLOBAL
:
RTLD_LOCAL
));
if
(
lib
==
NULL
)
lua_pushstring
(
L
,
dlerror
());
return
lib
;
}
static
lua_CFunction
lsys_sym
(
lua_State
*
L
,
void
*
lib
,
const
char
*
sym
)
{
lua_CFunction
f
=
cast_func
(
dlsym
(
lib
,
sym
));
if
(
f
==
NULL
)
lua_pushstring
(
L
,
dlerror
());
return
f
;
}
/* }====================================================== */
#elif defined(LUA_DL_DLL)
/* }{ */
/*
** {======================================================================
** This is an implementation of loadlib for Windows using native functions.
** =======================================================================
*/
#include <windows.h>
/*
** optional flags for LoadLibraryEx
*/
#if !defined(LUA_LLE_FLAGS)
#define LUA_LLE_FLAGS 0
#endif
#undef setprogdir
/*
** Replace in the path (on the top of the stack) any occurrence
** of LUA_EXEC_DIR with the executable's path.
*/
static
void
setprogdir
(
lua_State
*
L
)
{
char
buff
[
MAX_PATH
+
1
];
char
*
lb
;
DWORD
nsize
=
sizeof
(
buff
)
/
sizeof
(
char
);
DWORD
n
=
GetModuleFileNameA
(
NULL
,
buff
,
nsize
);
/* get exec. name */
if
(
n
==
0
||
n
==
nsize
||
(
lb
=
strrchr
(
buff
,
'\\'
))
==
NULL
)
luaL_error
(
L
,
"unable to get ModuleFileName"
);
else
{
*
lb
=
'\0'
;
/* cut name on the last '\\' to get the path */
luaL_gsub
(
L
,
lua_tostring
(
L
,
-
1
),
LUA_EXEC_DIR
,
buff
);
lua_remove
(
L
,
-
2
);
/* remove original string */
}
}
static
void
pusherror
(
lua_State
*
L
)
{
int
error
=
GetLastError
();
char
buffer
[
128
];
if
(
FormatMessageA
(
FORMAT_MESSAGE_IGNORE_INSERTS
|
FORMAT_MESSAGE_FROM_SYSTEM
,
NULL
,
error
,
0
,
buffer
,
sizeof
(
buffer
)
/
sizeof
(
char
),
NULL
))
lua_pushstring
(
L
,
buffer
);
else
lua_pushfstring
(
L
,
"system error %d
\n
"
,
error
);
}
static
void
lsys_unloadlib
(
void
*
lib
)
{
FreeLibrary
((
HMODULE
)
lib
);
}
static
void
*
lsys_load
(
lua_State
*
L
,
const
char
*
path
,
int
seeglb
)
{
HMODULE
lib
=
LoadLibraryExA
(
path
,
NULL
,
LUA_LLE_FLAGS
);
(
void
)(
seeglb
);
/* not used: symbols are 'global' by default */
if
(
lib
==
NULL
)
pusherror
(
L
);
return
lib
;
}
static
lua_CFunction
lsys_sym
(
lua_State
*
L
,
void
*
lib
,
const
char
*
sym
)
{
lua_CFunction
f
=
(
lua_CFunction
)
GetProcAddress
((
HMODULE
)
lib
,
sym
);
if
(
f
==
NULL
)
pusherror
(
L
);
return
f
;
}
/* }====================================================== */
#else
/* }{ */
/*
** {======================================================
** Fallback for other systems
** =======================================================
*/
#undef LIB_FAIL
#define LIB_FAIL "absent"
#define DLMSG "dynamic libraries not enabled; check your Lua installation"
static
void
lsys_unloadlib
(
void
*
lib
)
{
(
void
)(
lib
);
/* not used */
}
static
void
*
lsys_load
(
lua_State
*
L
,
const
char
*
path
,
int
seeglb
)
{
(
void
)(
path
);
(
void
)(
seeglb
);
/* not used */
lua_pushliteral
(
L
,
DLMSG
);
return
NULL
;
}
static
lua_CFunction
lsys_sym
(
lua_State
*
L
,
void
*
lib
,
const
char
*
sym
)
{
(
void
)(
lib
);
(
void
)(
sym
);
/* not used */
lua_pushliteral
(
L
,
DLMSG
);
return
NULL
;
}
/* }====================================================== */
#endif
/* } */
#endif
/* LUA_NODEMCU_NOCLOADERS */
/*
** {==================================================================
** Set Paths
** ===================================================================
*/
/*
** LUA_PATH_VAR and LUA_CPATH_VAR are the names of the environment
** variables that Lua check to set its paths.
*/
#if !defined(LUA_PATH_VAR)
#define LUA_PATH_VAR "LUA_PATH"
#endif
#if !defined(LUA_CPATH_VAR)
#define LUA_CPATH_VAR "LUA_CPATH"
#endif
#define AUXMARK "\1"
/* auxiliary mark */
/*
** return registry.LUA_NOENV as a boolean
*/
static
int
noenv
(
lua_State
*
L
)
{
int
b
;
lua_getfield
(
L
,
LUA_REGISTRYINDEX
,
"LUA_NOENV"
);
b
=
lua_toboolean
(
L
,
-
1
);
lua_pop
(
L
,
1
);
/* remove value */
return
b
;
}
/*
** Set a path
*/
static
void
setpath
(
lua_State
*
L
,
const
char
*
fieldname
,
const
char
*
envname
,
const
char
*
dft
)
{
const
char
*
nver
=
lua_pushfstring
(
L
,
"%s%s"
,
envname
,
LUA_VERSUFFIX
);
const
char
*
path
=
getenv
(
nver
);
/* use versioned name */
if
(
path
==
NULL
)
/* no environment variable? */
path
=
getenv
(
envname
);
/* try unversioned name */
if
(
path
==
NULL
||
noenv
(
L
))
/* no environment variable? */
lua_pushstring
(
L
,
dft
);
/* use default */
else
{
/* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
path
=
luaL_gsub
(
L
,
path
,
LUA_PATH_SEP
LUA_PATH_SEP
,
LUA_PATH_SEP
AUXMARK
LUA_PATH_SEP
);
luaL_gsub
(
L
,
path
,
AUXMARK
,
dft
);
lua_remove
(
L
,
-
2
);
/* remove result from 1st 'gsub' */
}
setprogdir
(
L
);
lua_setfield
(
L
,
-
3
,
fieldname
);
/* package[fieldname] = path value */
lua_pop
(
L
,
1
);
/* pop versioned variable name */
}
/* }================================================================== */
#ifndef LUA_NODEMCU_NOCLOADERS
/*
** return registry.CLIBS[path]
*/
static
void
*
checkclib
(
lua_State
*
L
,
const
char
*
path
)
{
void
*
plib
;
lua_rawgetp
(
L
,
LUA_REGISTRYINDEX
,
&
CLIBS
);
lua_getfield
(
L
,
-
1
,
path
);
plib
=
lua_touserdata
(
L
,
-
1
);
/* plib = CLIBS[path] */
lua_pop
(
L
,
2
);
/* pop CLIBS table and 'plib' */
return
plib
;
}
/*
** registry.CLIBS[path] = plib -- for queries
** registry.CLIBS[#CLIBS + 1] = plib -- also keep a list of all libraries
*/
static
void
addtoclib
(
lua_State
*
L
,
const
char
*
path
,
void
*
plib
)
{
lua_rawgetp
(
L
,
LUA_REGISTRYINDEX
,
&
CLIBS
);
lua_pushlightuserdata
(
L
,
plib
);
lua_pushvalue
(
L
,
-
1
);
lua_setfield
(
L
,
-
3
,
path
);
/* CLIBS[path] = plib */
lua_rawseti
(
L
,
-
2
,
luaL_len
(
L
,
-
2
)
+
1
);
/* CLIBS[#CLIBS + 1] = plib */
lua_pop
(
L
,
1
);
/* pop CLIBS table */
}
/*
** __gc tag method for CLIBS table: calls 'lsys_unloadlib' for all lib
** handles in list CLIBS
*/
static
int
gctm
(
lua_State
*
L
)
{
lua_Integer
n
=
luaL_len
(
L
,
1
);
for
(;
n
>=
1
;
n
--
)
{
/* for each handle, in reverse order */
lua_rawgeti
(
L
,
1
,
n
);
/* get handle CLIBS[n] */
lsys_unloadlib
(
lua_touserdata
(
L
,
-
1
));
lua_pop
(
L
,
1
);
/* pop handle */
}
return
0
;
}
#endif
/* error codes for 'lookforfunc' */
#define ERRLIB 1
#define ERRFUNC 2
#ifndef LUA_NODEMCU_NOCLOADERS
/*
** Look for a C function named 'sym' in a dynamically loaded library
** 'path'.
** First, check whether the library is already loaded; if not, try
** to load it.
** Then, if 'sym' is '*', return true (as library has been loaded).
** Otherwise, look for symbol 'sym' in the library and push a
** C function with that symbol.
** Return 0 and 'true' or a function in the stack; in case of
** errors, return an error code and an error message in the stack.
*/
static
int
lookforfunc
(
lua_State
*
L
,
const
char
*
path
,
const
char
*
sym
)
{
void
*
reg
=
checkclib
(
L
,
path
);
/* check loaded C libraries */
if
(
reg
==
NULL
)
{
/* must load library? */
reg
=
lsys_load
(
L
,
path
,
*
sym
==
'*'
);
/* global symbols if 'sym'=='*' */
if
(
reg
==
NULL
)
return
ERRLIB
;
/* unable to load library */
addtoclib
(
L
,
path
,
reg
);
}
if
(
*
sym
==
'*'
)
{
/* loading only library (no function)? */
lua_pushboolean
(
L
,
1
);
/* return 'true' */
return
0
;
/* no errors */
}
else
{
lua_CFunction
f
=
lsys_sym
(
L
,
reg
,
sym
);
if
(
f
==
NULL
)
return
ERRFUNC
;
/* unable to find function */
lua_pushcfunction
(
L
,
f
);
/* else create new function */
return
0
;
/* no errors */
}
}
static
int
ll_loadlib
(
lua_State
*
L
)
{
const
char
*
path
=
luaL_checkstring
(
L
,
1
);
const
char
*
init
=
luaL_checkstring
(
L
,
2
);
int
stat
=
lookforfunc
(
L
,
path
,
init
);
if
(
stat
==
0
)
/* no errors? */
return
1
;
/* return the loaded function */
else
{
/* error; error message is on stack top */
lua_pushnil
(
L
);
lua_insert
(
L
,
-
2
);
lua_pushstring
(
L
,
(
stat
==
ERRLIB
)
?
LIB_FAIL
:
"init"
);
return
3
;
/* return nil, error message, and where */
}
}
#endif
/*
** {======================================================
** 'require' function
** =======================================================
*/
#ifdef LUA_USE_ESP
#define file_t int
#undef fopen
#undef fclose
#define fopen(n,m) vfs_open(n,m)
#define fclose(f) vfs_close(f)
#else
#define file_t FILE *
#endif
static
int
readable
(
const
char
*
filename
)
{
file_t
f
=
fopen
(
filename
,
"r"
);
/* try to open file */
if
(
!
f
)
return
0
;
/* open failed */
fclose
(
f
);
return
1
;
}
static
const
char
*
pushnexttemplate
(
lua_State
*
L
,
const
char
*
path
)
{
const
char
*
l
;
while
(
*
path
==
*
LUA_PATH_SEP
)
path
++
;
/* skip separators */
if
(
*
path
==
'\0'
)
return
NULL
;
/* no more templates */
l
=
strchr
(
path
,
*
LUA_PATH_SEP
);
/* find next separator */
if
(
l
==
NULL
)
l
=
path
+
strlen
(
path
);
lua_pushlstring
(
L
,
path
,
l
-
path
);
/* template */
return
l
;
}
static
const
char
*
searchpath
(
lua_State
*
L
,
const
char
*
name
,
const
char
*
path
,
const
char
*
sep
,
const
char
*
dirsep
)
{
luaL_Buffer
msg
;
/* to build error message */
luaL_buffinit
(
L
,
&
msg
);
if
(
*
sep
!=
'\0'
)
/* non-empty separator? */
name
=
luaL_gsub
(
L
,
name
,
sep
,
dirsep
);
/* replace it by 'dirsep' */
while
((
path
=
pushnexttemplate
(
L
,
path
))
!=
NULL
)
{
const
char
*
filename
=
luaL_gsub
(
L
,
lua_tostring
(
L
,
-
1
),
LUA_PATH_MARK
,
name
);
lua_remove
(
L
,
-
2
);
/* remove path template */
if
(
readable
(
filename
))
/* does file exist and is readable? */
return
filename
;
/* return that file name */
lua_pushfstring
(
L
,
"
\n\t
no file '%s'"
,
filename
);
lua_remove
(
L
,
-
2
);
/* remove file name */
luaL_addvalue
(
&
msg
);
/* concatenate error msg. entry */
}
luaL_pushresult
(
&
msg
);
/* create error message */
return
NULL
;
/* not found */
}
static
int
ll_searchpath
(
lua_State
*
L
)
{
const
char
*
f
=
searchpath
(
L
,
luaL_checkstring
(
L
,
1
),
luaL_checkstring
(
L
,
2
),
luaL_optstring
(
L
,
3
,
"."
),
luaL_optstring
(
L
,
4
,
LUA_DIRSEP
));
if
(
f
!=
NULL
)
return
1
;
else
{
/* error message is on top of the stack */
lua_pushnil
(
L
);
lua_insert
(
L
,
-
2
);
return
2
;
/* return nil + error message */
}
}
static
const
char
*
findfile
(
lua_State
*
L
,
const
char
*
name
,
const
char
*
pname
,
const
char
*
dirsep
)
{
const
char
*
path
;
lua_getfield
(
L
,
lua_upvalueindex
(
1
),
pname
);
path
=
lua_tostring
(
L
,
-
1
);
if
(
path
==
NULL
)
luaL_error
(
L
,
"'package.%s' must be a string"
,
pname
);
return
searchpath
(
L
,
name
,
path
,
"."
,
dirsep
);
}
static
int
checkload
(
lua_State
*
L
,
int
stat
,
const
char
*
filename
)
{
if
(
stat
)
{
/* module loaded successfully? */
lua_pushstring
(
L
,
filename
);
/* will be 2nd argument to module */
return
2
;
/* return open function and file name */
}
else
return
luaL_error
(
L
,
"error loading module '%s' from file '%s':
\n\t
%s"
,
lua_tostring
(
L
,
1
),
filename
,
lua_tostring
(
L
,
-
1
));
}
static
int
searcher_Lua
(
lua_State
*
L
)
{
const
char
*
filename
;
const
char
*
name
=
luaL_checkstring
(
L
,
1
);
filename
=
findfile
(
L
,
name
,
"path"
,
LUA_LSUBSEP
);
if
(
filename
==
NULL
)
return
1
;
/* module not found in this path */
return
checkload
(
L
,
(
luaL_loadfile
(
L
,
filename
)
==
LUA_OK
),
filename
);
}
#ifndef LUA_NODEMCU_NOCLOADERS
/*
** Try to find a load function for module 'modname' at file 'filename'.
** First, change '.' to '_' in 'modname'; then, if 'modname' has
** the form X-Y (that is, it has an "ignore mark"), build a function
** name "luaopen_X" and look for it. (For compatibility, if that
** fails, it also tries "luaopen_Y".) If there is no ignore mark,
** look for a function named "luaopen_modname".
*/
static
int
loadfunc
(
lua_State
*
L
,
const
char
*
filename
,
const
char
*
modname
)
{
const
char
*
openfunc
;
const
char
*
mark
;
modname
=
luaL_gsub
(
L
,
modname
,
"."
,
LUA_OFSEP
);
mark
=
strchr
(
modname
,
*
LUA_IGMARK
);
if
(
mark
)
{
int
stat
;
openfunc
=
lua_pushlstring
(
L
,
modname
,
mark
-
modname
);
openfunc
=
lua_pushfstring
(
L
,
LUA_POF
"%s"
,
openfunc
);
stat
=
lookforfunc
(
L
,
filename
,
openfunc
);
if
(
stat
!=
ERRFUNC
)
return
stat
;
modname
=
mark
+
1
;
/* else go ahead and try old-style name */
}
openfunc
=
lua_pushfstring
(
L
,
LUA_POF
"%s"
,
modname
);
return
lookforfunc
(
L
,
filename
,
openfunc
);
}
static
int
searcher_C
(
lua_State
*
L
)
{
const
char
*
name
=
luaL_checkstring
(
L
,
1
);
const
char
*
filename
=
findfile
(
L
,
name
,
"cpath"
,
LUA_CSUBSEP
);
if
(
filename
==
NULL
)
return
1
;
/* module not found in this path */
return
checkload
(
L
,
(
loadfunc
(
L
,
filename
,
name
)
==
0
),
filename
);
}
static
int
searcher_Croot
(
lua_State
*
L
)
{
const
char
*
filename
;
const
char
*
name
=
luaL_checkstring
(
L
,
1
);
const
char
*
p
=
strchr
(
name
,
'.'
);
int
stat
;
if
(
p
==
NULL
)
return
0
;
/* is root */
lua_pushlstring
(
L
,
name
,
p
-
name
);
filename
=
findfile
(
L
,
lua_tostring
(
L
,
-
1
),
"cpath"
,
LUA_CSUBSEP
);
if
(
filename
==
NULL
)
return
1
;
/* root not found */
if
((
stat
=
loadfunc
(
L
,
filename
,
name
))
!=
0
)
{
if
(
stat
!=
ERRFUNC
)
return
checkload
(
L
,
0
,
filename
);
/* real error */
else
{
/* open function not found */
lua_pushfstring
(
L
,
"
\n\t
no module '%s' in file '%s'"
,
name
,
filename
);
return
1
;
}
}
lua_pushstring
(
L
,
filename
);
/* will be 2nd argument to module */
return
2
;
}
#endif
static
int
searcher_preload
(
lua_State
*
L
)
{
const
char
*
name
=
luaL_checkstring
(
L
,
1
);
lua_getfield
(
L
,
LUA_REGISTRYINDEX
,
LUA_PRELOAD_TABLE
);
if
(
lua_getfield
(
L
,
-
1
,
name
)
==
LUA_TNIL
)
/* not found? */
lua_pushfstring
(
L
,
"
\n\t
no field package.preload['%s']"
,
name
);
return
1
;
}
static
void
findloader
(
lua_State
*
L
,
const
char
*
name
)
{
int
i
;
luaL_Buffer
msg
;
/* to build error message */
luaL_buffinit
(
L
,
&
msg
);
/* push 'package.searchers' to index 3 in the stack */
if
(
lua_getfield
(
L
,
lua_upvalueindex
(
1
),
"searchers"
)
!=
LUA_TTABLE
)
luaL_error
(
L
,
"'package.searchers' must be a table"
);
/* iterate over available searchers to find a loader */
for
(
i
=
1
;
;
i
++
)
{
if
(
lua_rawgeti
(
L
,
3
,
i
)
==
LUA_TNIL
)
{
/* no more searchers? */
lua_pop
(
L
,
1
);
/* remove nil */
luaL_pushresult
(
&
msg
);
/* create error message */
luaL_error
(
L
,
"module '%s' not found:%s"
,
name
,
lua_tostring
(
L
,
-
1
));
}
lua_pushstring
(
L
,
name
);
lua_call
(
L
,
1
,
2
);
/* call it */
if
(
lua_isfunction
(
L
,
-
2
))
/* did it find a loader? */
return
;
/* module loader found */
else
if
(
lua_isstring
(
L
,
-
2
))
{
/* searcher returned error message? */
lua_pop
(
L
,
1
);
/* remove extra return */
luaL_addvalue
(
&
msg
);
/* concatenate error message */
}
else
lua_pop
(
L
,
2
);
/* remove both returns */
}
}
static
int
ll_require
(
lua_State
*
L
)
{
const
char
*
name
=
luaL_checkstring
(
L
,
1
);
lua_settop
(
L
,
1
);
/* LOADED table will be at index 2 */
lua_getfield
(
L
,
LUA_REGISTRYINDEX
,
LUA_LOADED_TABLE
);
lua_getfield
(
L
,
2
,
name
);
/* LOADED[name] */
if
(
lua_toboolean
(
L
,
-
1
))
/* is it there? */
return
1
;
/* package is already loaded */
lua_getglobal
(
L
,
"ROM"
);
lua_getfield
(
L
,
-
1
,
name
);
/* ROM[name] */
if
(
lua_toboolean
(
L
,
-
1
))
/* is it there? */
return
1
;
/* package is already loaded */
lua_pop
(
L
,
3
);
/* remove ROM and 2 × 'getfield' results */
/* else must load package */
findloader
(
L
,
name
);
lua_pushstring
(
L
,
name
);
/* pass name as argument to module loader */
lua_insert
(
L
,
-
2
);
/* name is 1st argument (before search data) */
lua_call
(
L
,
2
,
1
);
/* run loader to load module */
if
(
!
lua_isnil
(
L
,
-
1
))
/* non-nil return? */
lua_setfield
(
L
,
2
,
name
);
/* LOADED[name] = returned value */
if
(
lua_getfield
(
L
,
2
,
name
)
==
LUA_TNIL
)
{
/* module set no value? */
lua_pushboolean
(
L
,
1
);
/* use true as result */
lua_pushvalue
(
L
,
-
1
);
/* extra copy to be returned */
lua_setfield
(
L
,
2
,
name
);
/* LOADED[name] = true */
}
return
1
;
}
/* }====================================================== */
/*
** {======================================================
** 'module' function
** =======================================================
*/
#if defined(LUA_COMPAT_MODULE)
/*
** changes the environment variable of calling function
*/
static
void
set_env
(
lua_State
*
L
)
{
lua_Debug
ar
;
if
(
lua_getstack
(
L
,
1
,
&
ar
)
==
0
||
lua_getinfo
(
L
,
"f"
,
&
ar
)
==
0
||
/* get calling function */
lua_iscfunction
(
L
,
-
1
))
luaL_error
(
L
,
"'module' not called from a Lua function"
);
lua_pushvalue
(
L
,
-
2
);
/* copy new environment table to top */
lua_setupvalue
(
L
,
-
2
,
1
);
lua_pop
(
L
,
1
);
/* remove function */
}
static
void
dooptions
(
lua_State
*
L
,
int
n
)
{
int
i
;
for
(
i
=
2
;
i
<=
n
;
i
++
)
{
if
(
lua_isfunction
(
L
,
i
))
{
/* avoid 'calling' extra info. */
lua_pushvalue
(
L
,
i
);
/* get option (a function) */
lua_pushvalue
(
L
,
-
2
);
/* module */
lua_call
(
L
,
1
,
0
);
}
}
}
static
void
modinit
(
lua_State
*
L
,
const
char
*
modname
)
{
const
char
*
dot
;
lua_pushvalue
(
L
,
-
1
);
lua_setfield
(
L
,
-
2
,
"_M"
);
/* module._M = module */
lua_pushstring
(
L
,
modname
);
lua_setfield
(
L
,
-
2
,
"_NAME"
);
dot
=
strrchr
(
modname
,
'.'
);
/* look for last dot in module name */
if
(
dot
==
NULL
)
dot
=
modname
;
else
dot
++
;
/* set _PACKAGE as package name (full module name minus last part) */
lua_pushlstring
(
L
,
modname
,
dot
-
modname
);
lua_setfield
(
L
,
-
2
,
"_PACKAGE"
);
}
static
int
ll_module
(
lua_State
*
L
)
{
const
char
*
modname
=
luaL_checkstring
(
L
,
1
);
int
lastarg
=
lua_gettop
(
L
);
/* last parameter */
luaL_pushmodule
(
L
,
modname
,
1
);
/* get/create module table */
/* check whether table already has a _NAME field */
if
(
lua_getfield
(
L
,
-
1
,
"_NAME"
)
!=
LUA_TNIL
)
lua_pop
(
L
,
1
);
/* table is an initialized module */
else
{
/* no; initialize it */
lua_pop
(
L
,
1
);
modinit
(
L
,
modname
);
}
lua_pushvalue
(
L
,
-
1
);
set_env
(
L
);
dooptions
(
L
,
lastarg
);
return
1
;
}
static
int
ll_seeall
(
lua_State
*
L
)
{
luaL_checktype
(
L
,
1
,
LUA_TTABLE
);
if
(
!
lua_getmetatable
(
L
,
1
))
{
lua_createtable
(
L
,
0
,
1
);
/* create new metatable */
lua_pushvalue
(
L
,
-
1
);
lua_setmetatable
(
L
,
1
);
}
lua_pushglobaltable
(
L
);
lua_setfield
(
L
,
-
2
,
"__index"
);
/* mt.__index = _G */
return
0
;
}
#endif
/* }====================================================== */
static
const
luaL_Reg
pk_funcs
[]
=
{
#ifndef LUA_NODEMCU_NOCLOADERS
{
"loadlib"
,
ll_loadlib
},
{
"cpath"
,
NULL
},
#endif
{
"searchpath"
,
ll_searchpath
},
#if defined(LUA_COMPAT_MODULE)
{
"seeall"
,
ll_seeall
},
#endif
/* placeholders */
{
"preload"
,
NULL
},
{
"path"
,
NULL
},
{
"searchers"
,
NULL
},
{
"loaded"
,
NULL
},
{
NULL
,
NULL
}
};
static
const
luaL_Reg
ll_funcs
[]
=
{
#if defined(LUA_COMPAT_MODULE)
{
"module"
,
ll_module
},
#endif
{
"require"
,
ll_require
},
{
NULL
,
NULL
}
};
static
void
createsearcherstable
(
lua_State
*
L
)
{
static
const
lua_CFunction
searchers
[]
=
{
searcher_preload
,
searcher_Lua
,
#ifndef LUA_NODEMCU_NOCLOADERS
searcher_C
,
searcher_Croot
,
#endif
NULL
};
int
i
;
/* create 'searchers' table */
lua_createtable
(
L
,
sizeof
(
searchers
)
/
sizeof
(
searchers
[
0
])
-
1
,
0
);
/* fill it with predefined searchers */
for
(
i
=
0
;
searchers
[
i
]
!=
NULL
;
i
++
)
{
lua_pushvalue
(
L
,
-
2
);
/* set 'package' as upvalue for all searchers */
lua_pushcclosure
(
L
,
searchers
[
i
],
1
);
lua_rawseti
(
L
,
-
2
,
i
+
1
);
}
#if defined(LUA_COMPAT_LOADERS)
lua_pushvalue
(
L
,
-
1
);
/* make a copy of 'searchers' table */
lua_setfield
(
L
,
-
3
,
"loaders"
);
/* put it in field 'loaders' */
#endif
lua_setfield
(
L
,
-
2
,
"searchers"
);
/* put it in field 'searchers' */
}
#ifndef LUA_NODEMCU_NOCLOADERS
/*
** create table CLIBS to keep track of loaded C libraries,
** setting a finalizer to close all libraries when closing state.
*/
static
void
createclibstable
(
lua_State
*
L
)
{
lua_newtable
(
L
);
/* create CLIBS table */
lua_createtable
(
L
,
0
,
1
);
/* create metatable for CLIBS */
lua_pushcfunction
(
L
,
gctm
);
lua_setfield
(
L
,
-
2
,
"__gc"
);
/* set finalizer for CLIBS table */
lua_setmetatable
(
L
,
-
2
);
lua_rawsetp
(
L
,
LUA_REGISTRYINDEX
,
&
CLIBS
);
/* set CLIBS table in registry */
}
#endif
LUAMOD_API
int
luaopen_package
(
lua_State
*
L
)
{
#ifndef LUA_NODEMCU_NOCLOADERS
createclibstable
(
L
);
#endif
luaL_newlib
(
L
,
pk_funcs
);
/* create 'package' table */
createsearcherstable
(
L
);
/* set paths */
setpath
(
L
,
"path"
,
LUA_PATH_VAR
,
LUA_PATH_DEFAULT
);
// setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT);
/* store config information */
lua_pushliteral
(
L
,
LUA_DIRSEP
"
\n
"
LUA_PATH_SEP
"
\n
"
LUA_PATH_MARK
"
\n
"
LUA_EXEC_DIR
"
\n
"
LUA_IGMARK
"
\n
"
);
lua_setfield
(
L
,
-
2
,
"config"
);
/* set field 'loaded' */
luaL_getsubtable
(
L
,
LUA_REGISTRYINDEX
,
LUA_LOADED_TABLE
);
lua_setfield
(
L
,
-
2
,
"loaded"
);
/* set field 'preload' */
luaL_getsubtable
(
L
,
LUA_REGISTRYINDEX
,
LUA_PRELOAD_TABLE
);
lua_setfield
(
L
,
-
2
,
"preload"
);
lua_pushglobaltable
(
L
);
lua_pushvalue
(
L
,
-
2
);
/* set 'package' as upvalue for next lib */
luaL_setfuncs
(
L
,
ll_funcs
,
1
);
/* open lib into global table */
lua_pop
(
L
,
1
);
/* pop global table */
return
1
;
/* return 'package' table */
}
components/lua/lua-5.3/lobject.c
0 → 100644
View file @
dba57fa0
/*
** $Id: lobject.c,v 2.113.1.1 2017/04/19 17:29:57 roberto Exp $
** Some generic functions over Lua objects
** See Copyright Notice in lua.h
*/
#define lobject_c
#define LUA_CORE
#include "lprefix.h"
#include <locale.h>
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lctype.h"
#include "ldebug.h"
#include "ldo.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#include "lvm.h"
LUAI_DDEF
const
TValue
luaO_nilobject_
=
{
NILCONSTANT
};
/*
** converts an integer to a "floating point byte", represented as
** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
** eeeee != 0 and (xxx) otherwise.
*/
int
luaO_int2fb
(
unsigned
int
x
)
{
int
e
=
0
;
/* exponent */
if
(
x
<
8
)
return
x
;
while
(
x
>=
(
8
<<
4
))
{
/* coarse steps */
x
=
(
x
+
0xf
)
>>
4
;
/* x = ceil(x / 16) */
e
+=
4
;
}
while
(
x
>=
(
8
<<
1
))
{
/* fine steps */
x
=
(
x
+
1
)
>>
1
;
/* x = ceil(x / 2) */
e
++
;
}
return
((
e
+
1
)
<<
3
)
|
(
cast_int
(
x
)
-
8
);
}
/* converts back */
int
luaO_fb2int
(
int
x
)
{
return
(
x
<
8
)
?
x
:
((
x
&
7
)
+
8
)
<<
((
x
>>
3
)
-
1
);
}
/*
** Computes ceil(log2(x))
*/
int
luaO_ceillog2
(
unsigned
int
x
)
{
#ifdef LUA_CROSS_COMPILER
static
const
lu_byte
log_2
[
256
]
=
{
/* log_2[i] = ceil(log2(i - 1)) */
0
,
1
,
2
,
2
,
3
,
3
,
3
,
3
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
4
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
5
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
6
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
7
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
,
8
};
int
l
=
0
;
x
--
;
while
(
x
>=
256
)
{
l
+=
8
;
x
>>=
8
;
}
return
l
+
log_2
[
x
];
#else
return
(
x
==
1
)
?
0
:
32
-
__builtin_clz
(
x
-
1
);
#endif
}
static
lua_Integer
intarith
(
lua_State
*
L
,
int
op
,
lua_Integer
v1
,
lua_Integer
v2
)
{
switch
(
op
)
{
case
LUA_OPADD
:
return
intop
(
+
,
v1
,
v2
);
case
LUA_OPSUB
:
return
intop
(
-
,
v1
,
v2
);
case
LUA_OPMUL
:
return
intop
(
*
,
v1
,
v2
);
case
LUA_OPMOD
:
return
luaV_mod
(
L
,
v1
,
v2
);
case
LUA_OPIDIV
:
return
luaV_div
(
L
,
v1
,
v2
);
case
LUA_OPBAND
:
return
intop
(
&
,
v1
,
v2
);
case
LUA_OPBOR
:
return
intop
(
|
,
v1
,
v2
);
case
LUA_OPBXOR
:
return
intop
(
^
,
v1
,
v2
);
case
LUA_OPSHL
:
return
luaV_shiftl
(
v1
,
v2
);
case
LUA_OPSHR
:
return
luaV_shiftl
(
v1
,
-
v2
);
case
LUA_OPUNM
:
return
intop
(
-
,
0
,
v1
);
case
LUA_OPBNOT
:
return
intop
(
^
,
~
l_castS2U
(
0
),
v1
);
default:
lua_assert
(
0
);
return
0
;
}
}
static
lua_Number
numarith
(
lua_State
*
L
,
int
op
,
lua_Number
v1
,
lua_Number
v2
)
{
switch
(
op
)
{
case
LUA_OPADD
:
return
luai_numadd
(
L
,
v1
,
v2
);
case
LUA_OPSUB
:
return
luai_numsub
(
L
,
v1
,
v2
);
case
LUA_OPMUL
:
return
luai_nummul
(
L
,
v1
,
v2
);
case
LUA_OPDIV
:
return
luai_numdiv
(
L
,
v1
,
v2
);
case
LUA_OPPOW
:
return
luai_numpow
(
L
,
v1
,
v2
);
case
LUA_OPIDIV
:
return
luai_numidiv
(
L
,
v1
,
v2
);
case
LUA_OPUNM
:
return
luai_numunm
(
L
,
v1
);
case
LUA_OPMOD
:
{
lua_Number
m
;
luai_nummod
(
L
,
v1
,
v2
,
m
);
return
m
;
}
default:
lua_assert
(
0
);
return
0
;
}
}
void
luaO_arith
(
lua_State
*
L
,
int
op
,
const
TValue
*
p1
,
const
TValue
*
p2
,
TValue
*
res
)
{
switch
(
op
)
{
case
LUA_OPBAND
:
case
LUA_OPBOR
:
case
LUA_OPBXOR
:
case
LUA_OPSHL
:
case
LUA_OPSHR
:
case
LUA_OPBNOT
:
{
/* operate only on integers */
lua_Integer
i1
;
lua_Integer
i2
;
if
(
tointeger
(
p1
,
&
i1
)
&&
tointeger
(
p2
,
&
i2
))
{
setivalue
(
res
,
intarith
(
L
,
op
,
i1
,
i2
));
return
;
}
else
break
;
/* go to the end */
}
case
LUA_OPDIV
:
case
LUA_OPPOW
:
{
/* operate only on floats */
lua_Number
n1
;
lua_Number
n2
;
if
(
tonumber
(
p1
,
&
n1
)
&&
tonumber
(
p2
,
&
n2
))
{
setfltvalue
(
res
,
numarith
(
L
,
op
,
n1
,
n2
));
return
;
}
else
break
;
/* go to the end */
}
default:
{
/* other operations */
lua_Number
n1
;
lua_Number
n2
;
if
(
ttisinteger
(
p1
)
&&
ttisinteger
(
p2
))
{
setivalue
(
res
,
intarith
(
L
,
op
,
ivalue
(
p1
),
ivalue
(
p2
)));
return
;
}
else
if
(
tonumber
(
p1
,
&
n1
)
&&
tonumber
(
p2
,
&
n2
))
{
setfltvalue
(
res
,
numarith
(
L
,
op
,
n1
,
n2
));
return
;
}
else
break
;
/* go to the end */
}
}
/* could not perform raw operation; try metamethod */
lua_assert
(
L
!=
NULL
);
/* should not fail when folding (compile time) */
luaT_trybinTM
(
L
,
p1
,
p2
,
res
,
cast
(
TMS
,
(
op
-
LUA_OPADD
)
+
TM_ADD
));
}
int
luaO_hexavalue
(
int
c
)
{
if
(
lisdigit
(
c
))
return
c
-
'0'
;
else
return
(
ltolower
(
c
)
-
'a'
)
+
10
;
}
static
int
isneg
(
const
char
**
s
)
{
if
(
**
s
==
'-'
)
{
(
*
s
)
++
;
return
1
;
}
else
if
(
**
s
==
'+'
)
(
*
s
)
++
;
return
0
;
}
/*
** {==================================================================
** Lua's implementation for 'lua_strx2number'
** ===================================================================
*/
#if !defined(lua_strx2number)
/* maximum number of significant digits to read (to avoid overflows
even with single floats) */
#define MAXSIGDIG 30
/*
** convert an hexadecimal numeric string to a number, following
** C99 specification for 'strtod'
*/
static
lua_Number
lua_strx2number
(
const
char
*
s
,
char
**
endptr
)
{
int
dot
=
lua_getlocaledecpoint
();
lua_Number
r
=
0
.
0
;
/* result (accumulator) */
int
sigdig
=
0
;
/* number of significant digits */
int
nosigdig
=
0
;
/* number of non-significant digits */
int
e
=
0
;
/* exponent correction */
int
neg
;
/* 1 if number is negative */
int
hasdot
=
0
;
/* true after seen a dot */
*
endptr
=
cast
(
char
*
,
s
);
/* nothing is valid yet */
while
(
lisspace
(
cast_uchar
(
*
s
)))
s
++
;
/* skip initial spaces */
neg
=
isneg
(
&
s
);
/* check signal */
if
(
!
(
*
s
==
'0'
&&
(
*
(
s
+
1
)
==
'x'
||
*
(
s
+
1
)
==
'X'
)))
/* check '0x' */
return
0
.
0
;
/* invalid format (no '0x') */
for
(
s
+=
2
;
;
s
++
)
{
/* skip '0x' and read numeral */
if
(
*
s
==
dot
)
{
if
(
hasdot
)
break
;
/* second dot? stop loop */
else
hasdot
=
1
;
}
else
if
(
lisxdigit
(
cast_uchar
(
*
s
)))
{
if
(
sigdig
==
0
&&
*
s
==
'0'
)
/* non-significant digit (zero)? */
nosigdig
++
;
else
if
(
++
sigdig
<=
MAXSIGDIG
)
/* can read it without overflow? */
r
=
(
r
*
cast_num
(
16
.
0
))
+
luaO_hexavalue
(
*
s
);
else
e
++
;
/* too many digits; ignore, but still count for exponent */
if
(
hasdot
)
e
--
;
/* decimal digit? correct exponent */
}
else
break
;
/* neither a dot nor a digit */
}
if
(
nosigdig
+
sigdig
==
0
)
/* no digits? */
return
0
.
0
;
/* invalid format */
*
endptr
=
cast
(
char
*
,
s
);
/* valid up to here */
e
*=
4
;
/* each digit multiplies/divides value by 2^4 */
if
(
*
s
==
'p'
||
*
s
==
'P'
)
{
/* exponent part? */
int
exp1
=
0
;
/* exponent value */
int
neg1
;
/* exponent signal */
s
++
;
/* skip 'p' */
neg1
=
isneg
(
&
s
);
/* signal */
if
(
!
lisdigit
(
cast_uchar
(
*
s
)))
return
0
.
0
;
/* invalid; must have at least one digit */
while
(
lisdigit
(
cast_uchar
(
*
s
)))
/* read exponent */
exp1
=
exp1
*
10
+
*
(
s
++
)
-
'0'
;
if
(
neg1
)
exp1
=
-
exp1
;
e
+=
exp1
;
*
endptr
=
cast
(
char
*
,
s
);
/* valid up to here */
}
if
(
neg
)
r
=
-
r
;
return
l_mathop
(
ldexp
)(
r
,
e
);
}
#endif
/* }====================================================== */
/* maximum length of a numeral */
#if !defined (L_MAXLENNUM)
#define L_MAXLENNUM 200
#endif
static
const
char
*
l_str2dloc
(
const
char
*
s
,
lua_Number
*
result
,
int
mode
)
{
char
*
endptr
;
*
result
=
(
mode
==
'x'
)
?
lua_strx2number
(
s
,
&
endptr
)
/* try to convert */
:
lua_str2number
(
s
,
&
endptr
);
if
(
endptr
==
s
)
return
NULL
;
/* nothing recognized? */
while
(
lisspace
(
cast_uchar
(
*
endptr
)))
endptr
++
;
/* skip trailing spaces */
return
(
*
endptr
==
'\0'
)
?
endptr
:
NULL
;
/* OK if no trailing characters */
}
/*
** Convert string 's' to a Lua number (put in 'result'). Return NULL
** on fail or the address of the ending '\0' on success.
** 'pmode' points to (and 'mode' contains) special things in the string:
** - 'x'/'X' means an hexadecimal numeral
** - 'n'/'N' means 'inf' or 'nan' (which should be rejected)
** - '.' just optimizes the search for the common case (nothing special)
** This function accepts both the current locale or a dot as the radix
** mark. If the convertion fails, it may mean number has a dot but
** locale accepts something else. In that case, the code copies 's'
** to a buffer (because 's' is read-only), changes the dot to the
** current locale radix mark, and tries to convert again.
*/
static
const
char
*
l_str2d
(
const
char
*
s
,
lua_Number
*
result
)
{
const
char
*
endptr
;
const
char
*
pmode
=
strpbrk
(
s
,
".xXnN"
);
int
mode
=
pmode
?
ltolower
(
cast_uchar
(
*
pmode
))
:
0
;
if
(
mode
==
'n'
)
/* reject 'inf' and 'nan' */
return
NULL
;
endptr
=
l_str2dloc
(
s
,
result
,
mode
);
/* try to convert */
if
(
endptr
==
NULL
)
{
/* failed? may be a different locale */
char
buff
[
L_MAXLENNUM
+
1
];
const
char
*
pdot
=
strchr
(
s
,
'.'
);
if
(
strlen
(
s
)
>
L_MAXLENNUM
||
pdot
==
NULL
)
return
NULL
;
/* string too long or no dot; fail */
strcpy
(
buff
,
s
);
/* copy string to buffer */
buff
[
pdot
-
s
]
=
lua_getlocaledecpoint
();
/* correct decimal point */
endptr
=
l_str2dloc
(
buff
,
result
,
mode
);
/* try again */
if
(
endptr
!=
NULL
)
endptr
=
s
+
(
endptr
-
buff
);
/* make relative to 's' */
}
return
endptr
;
}
#define MAXBY10 cast(lua_Unsigned, LUA_MAXINTEGER / 10)
#define MAXLASTD cast_int(LUA_MAXINTEGER % 10)
static
const
char
*
l_str2int
(
const
char
*
s
,
lua_Integer
*
result
)
{
lua_Unsigned
a
=
0
;
int
empty
=
1
;
int
neg
;
while
(
lisspace
(
cast_uchar
(
*
s
)))
s
++
;
/* skip initial spaces */
neg
=
isneg
(
&
s
);
if
(
s
[
0
]
==
'0'
&&
(
s
[
1
]
==
'x'
||
s
[
1
]
==
'X'
))
{
/* hex? */
s
+=
2
;
/* skip '0x' */
for
(;
lisxdigit
(
cast_uchar
(
*
s
));
s
++
)
{
a
=
a
*
16
+
luaO_hexavalue
(
*
s
);
empty
=
0
;
}
}
else
{
/* decimal */
for
(;
lisdigit
(
cast_uchar
(
*
s
));
s
++
)
{
int
d
=
*
s
-
'0'
;
if
(
a
>=
MAXBY10
&&
(
a
>
MAXBY10
||
d
>
MAXLASTD
+
neg
))
/* overflow? */
return
NULL
;
/* do not accept it (as integer) */
a
=
a
*
10
+
d
;
empty
=
0
;
}
}
while
(
lisspace
(
cast_uchar
(
*
s
)))
s
++
;
/* skip trailing spaces */
if
(
empty
||
*
s
!=
'\0'
)
return
NULL
;
/* something wrong in the numeral */
else
{
*
result
=
l_castU2S
((
neg
)
?
0u
-
a
:
a
);
return
s
;
}
}
size_t
luaO_str2num
(
const
char
*
s
,
TValue
*
o
)
{
lua_Integer
i
;
lua_Number
n
;
const
char
*
e
;
if
((
e
=
l_str2int
(
s
,
&
i
))
!=
NULL
)
{
/* try as an integer */
setivalue
(
o
,
i
);
}
else
if
((
e
=
l_str2d
(
s
,
&
n
))
!=
NULL
)
{
/* else try as a float */
setfltvalue
(
o
,
n
);
}
else
return
0
;
/* conversion failed */
return
(
e
-
s
)
+
1
;
/* success; return string size */
}
int
luaO_utf8esc
(
char
*
buff
,
unsigned
long
x
)
{
int
n
=
1
;
/* number of bytes put in buffer (backwards) */
lua_assert
(
x
<=
0x10FFFF
);
if
(
x
<
0x80
)
/* ascii? */
buff
[
UTF8BUFFSZ
-
1
]
=
cast
(
char
,
x
);
else
{
/* need continuation bytes */
unsigned
int
mfb
=
0x3f
;
/* maximum that fits in first byte */
do
{
/* add continuation bytes */
buff
[
UTF8BUFFSZ
-
(
n
++
)]
=
cast
(
char
,
0x80
|
(
x
&
0x3f
));
x
>>=
6
;
/* remove added bits */
mfb
>>=
1
;
/* now there is one less bit available in first byte */
}
while
(
x
>
mfb
);
/* still needs continuation byte? */
buff
[
UTF8BUFFSZ
-
n
]
=
cast
(
char
,
(
~
mfb
<<
1
)
|
x
);
/* add first byte */
}
return
n
;
}
/* maximum length of the conversion of a number to a string */
#define MAXNUMBER2STR 50
/*
** Convert a number object to a string
*/
void
luaO_tostring
(
lua_State
*
L
,
StkId
obj
)
{
char
buff
[
MAXNUMBER2STR
];
size_t
len
;
lua_assert
(
ttisnumber
(
obj
));
if
(
ttisinteger
(
obj
))
len
=
lua_integer2str
(
buff
,
sizeof
(
buff
),
ivalue
(
obj
));
else
{
len
=
lua_number2str
(
buff
,
sizeof
(
buff
),
fltvalue
(
obj
));
#if !defined(LUA_COMPAT_FLOATSTRING)
if
(
buff
[
strspn
(
buff
,
"-0123456789"
)]
==
'\0'
)
{
/* looks like an int? */
buff
[
len
++
]
=
lua_getlocaledecpoint
();
buff
[
len
++
]
=
'0'
;
/* adds '.0' to result */
}
#endif
}
setsvalue2s
(
L
,
obj
,
luaS_newlstr
(
L
,
buff
,
len
));
}
static
void
pushstr
(
lua_State
*
L
,
const
char
*
str
,
size_t
l
)
{
setsvalue2s
(
L
,
L
->
top
,
luaS_newlstr
(
L
,
str
,
l
));
luaD_inctop
(
L
);
}
/*
** this function handles only '%d', '%c', '%f', '%p', and '%s'
conventional formats, plus Lua-specific '%I' and '%U'
*/
const
char
*
luaO_pushvfstring
(
lua_State
*
L
,
const
char
*
fmt
,
va_list
argp
)
{
int
n
=
0
;
for
(;;)
{
const
char
*
e
=
strchr
(
fmt
,
'%'
);
if
(
e
==
NULL
)
break
;
pushstr
(
L
,
fmt
,
e
-
fmt
);
switch
(
*
(
e
+
1
))
{
case
's'
:
{
/* zero-terminated string */
const
char
*
s
=
va_arg
(
argp
,
char
*
);
if
(
s
==
NULL
)
s
=
"(null)"
;
pushstr
(
L
,
s
,
strlen
(
s
));
break
;
}
case
'c'
:
{
/* an 'int' as a character */
char
buff
=
cast
(
char
,
va_arg
(
argp
,
int
));
if
(
lisprint
(
cast_uchar
(
buff
)))
pushstr
(
L
,
&
buff
,
1
);
else
/* non-printable character; print its code */
luaO_pushfstring
(
L
,
"<
\\
%d>"
,
cast_uchar
(
buff
));
break
;
}
case
'd'
:
{
/* an 'int' */
setivalue
(
L
->
top
,
va_arg
(
argp
,
int
));
goto
top2str
;
}
case
'I'
:
{
/* a 'lua_Integer' */
setivalue
(
L
->
top
,
cast
(
lua_Integer
,
va_arg
(
argp
,
l_uacInt
)));
goto
top2str
;
}
case
'f'
:
{
/* a 'lua_Number' */
setfltvalue
(
L
->
top
,
cast_num
(
va_arg
(
argp
,
l_uacNumber
)));
top2str:
/* convert the top element to a string */
luaD_inctop
(
L
);
luaO_tostring
(
L
,
L
->
top
-
1
);
break
;
}
case
'p'
:
{
/* a pointer */
char
buff
[
4
*
sizeof
(
void
*
)
+
8
];
/* should be enough space for a '%p' */
void
*
p
=
va_arg
(
argp
,
void
*
);
int
l
=
lua_pointer2str
(
buff
,
sizeof
(
buff
),
p
);
pushstr
(
L
,
buff
,
l
);
break
;
}
case
'U'
:
{
/* an 'int' as a UTF-8 sequence */
char
buff
[
UTF8BUFFSZ
];
int
l
=
luaO_utf8esc
(
buff
,
cast
(
long
,
va_arg
(
argp
,
long
)));
pushstr
(
L
,
buff
+
UTF8BUFFSZ
-
l
,
l
);
break
;
}
case
'%'
:
{
pushstr
(
L
,
"%"
,
1
);
break
;
}
default:
{
luaG_runerror
(
L
,
"invalid option '%%%c' to 'lua_pushfstring'"
,
*
(
e
+
1
));
}
}
n
+=
2
;
fmt
=
e
+
2
;
}
luaD_checkstack
(
L
,
1
);
pushstr
(
L
,
fmt
,
strlen
(
fmt
));
if
(
n
>
0
)
luaV_concat
(
L
,
n
+
1
);
return
svalue
(
L
->
top
-
1
);
}
const
char
*
luaO_pushfstring
(
lua_State
*
L
,
const
char
*
fmt
,
...)
{
const
char
*
msg
;
va_list
argp
;
va_start
(
argp
,
fmt
);
msg
=
luaO_pushvfstring
(
L
,
fmt
,
argp
);
va_end
(
argp
);
return
msg
;
}
/* number of chars of a literal string without the ending \0 */
#define LL(x) (sizeof(x)/sizeof(char) - 1)
#define RETS "..."
#define PRE "[string \""
#define POS "\"]"
#define addstr(a,b,l) ( memcpy(a,b,(l) * sizeof(char)), a += (l) )
void
luaO_chunkid
(
char
*
out
,
const
char
*
source
,
size_t
bufflen
)
{
size_t
l
=
strlen
(
source
);
if
(
*
source
==
'='
)
{
/* 'literal' source */
if
(
l
<=
bufflen
)
/* small enough? */
memcpy
(
out
,
source
+
1
,
l
*
sizeof
(
char
));
else
{
/* truncate it */
addstr
(
out
,
source
+
1
,
bufflen
-
1
);
*
out
=
'\0'
;
}
}
else
if
(
*
source
==
'@'
)
{
/* file name */
if
(
l
<=
bufflen
)
/* small enough? */
memcpy
(
out
,
source
+
1
,
l
*
sizeof
(
char
));
else
{
/* add '...' before rest of name */
addstr
(
out
,
RETS
,
LL
(
RETS
));
bufflen
-=
LL
(
RETS
);
memcpy
(
out
,
source
+
1
+
l
-
bufflen
,
bufflen
*
sizeof
(
char
));
}
}
else
{
/* string; format as [string "source"] */
const
char
*
nl
=
strchr
(
source
,
'\n'
);
/* find first new line (if any) */
addstr
(
out
,
PRE
,
LL
(
PRE
));
/* add prefix */
bufflen
-=
LL
(
PRE
RETS
POS
)
+
1
;
/* save space for prefix+suffix+'\0' */
if
(
l
<
bufflen
&&
nl
==
NULL
)
{
/* small one-line source? */
addstr
(
out
,
source
,
l
);
/* keep it */
}
else
{
if
(
nl
!=
NULL
)
l
=
nl
-
source
;
/* stop at first newline */
if
(
l
>
bufflen
)
l
=
bufflen
;
addstr
(
out
,
source
,
l
);
addstr
(
out
,
RETS
,
LL
(
RETS
));
}
memcpy
(
out
,
POS
,
(
LL
(
POS
)
+
1
)
*
sizeof
(
char
));
}
}
components/lua/lua-5.3/lobject.h
0 → 100644
View file @
dba57fa0
/*
** $Id: lobject.h,v 2.117.1.1 2017/04/19 17:39:34 roberto Exp $
** Type definitions for Lua objects
** See Copyright Notice in lua.h
*/
#ifndef lobject_h
#define lobject_h
#include <stdarg.h>
#include "llimits.h"
#include "lua.h"
/*
** Extra tags for non-values
*/
#define LUA_TPROTO LUA_NUMTAGS
/* function prototypes */
#define LUA_TDEADKEY (LUA_NUMTAGS+1)
/* removed keys in tables */
/*
** number of all possible tags (including LUA_TNONE but excluding DEADKEY)
*/
#define LUA_TOTALTAGS (LUA_TPROTO + 2)
/*
** tags for Tagged Values have the following use of bits:
** bits 0-3: actual tag (a LUA_T* value)
** bits 4-5: variant bits
** bit 6: whether value is collectable
*/
/*
** LUA_TFUNCTION variants:
** 0 - Lua function
** 1 - light C function
** 2 - regular C function (closure)
*/
/* Variant tags for functions */
#define LUA_TLCL (LUA_TFUNCTION | (0 << 4))
/* Lua closure */
#define LUA_TLCF (LUA_TFUNCTION | (1 << 4))
/* light C function */
#define LUA_TCCL (LUA_TFUNCTION | (2 << 4))
/* C closure */
/* Variant tags for strings */
#define LUA_TSHRSTR (LUA_TSTRING | (0 << 4))
/* short strings */
#define LUA_TLNGSTR (LUA_TSTRING | (1 << 4))
/* long strings */
/* Variant tags for numbers */
#define LUA_TNUMFLT (LUA_TNUMBER | (0 << 4))
/* float numbers */
#define LUA_TNUMINT (LUA_TNUMBER | (1 << 4))
/* integer numbers */
/* Bit mark for collectable types */
#define LUA_TTBLRAM (LUA_TTABLE | (0 << 4))
/* RAM based Table */
#define LUA_TTBLROF (LUA_TTABLE | (1 << 4))
/* RO Flash based ROTable */
/* Bit mark for collectable types */
#define BIT_ISCOLLECTABLE (1 << 6)
/* mark a tag as collectable */
#define ctb(t) ((t) | BIT_ISCOLLECTABLE)
/*
** Byte field access macro. On ESP targets this causes the compiler to emit
** a l32i + extui instruction pair instead of a single l8ui avoiding a call
** the S/W unaligned exception handler. This is used to force aligned access
** to commonly accessed fields in Flash-based record structures. It is not
** needed for RAM-only structures.
**
** wo is the offset of aligned word in bytes 0,4,8,..
** bo is the field within the word in bits 0..31
*/
#if defined(LUA_USE_ESP8266)
#define GET_BYTE_FN(name,t,wo,bo) \
static inline lu_int32 get ## name(const void *o) { \
lu_int32 res;
/* extract named field */
\
asm ("l32i %0, %1, " #wo "; extui %0, %0, " #bo ", 8;" : "=r"(res) : "r"(o) : );\
return res; }
#else
#define GET_BYTE_FN(name,t,wo,bo) \
static inline lu_byte get ## name(const void *o) { return (cast(const t *,o))->name; }
#endif
/*
** Common type for all collectable objects
*/
typedef
struct
GCObject
GCObject
;
/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
/*
** Common type has only the common header
*/
struct
GCObject
{
CommonHeader
;
};
GET_BYTE_FN
(
tt
,
GCObject
,
4
,
0
)
GET_BYTE_FN
(
marked
,
GCObject
,
4
,
8
)
/*
** Tagged Values. This is the basic representation of values in Lua,
** an actual value plus a tag with its type.
*/
/*
** Union of all Lua values
*/
typedef
union
Value
{
GCObject
*
gc
;
/* collectable objects */
void
*
p
;
/* light userdata */
int
b
;
/* booleans */
lua_CFunction
f
;
/* light C functions */
lua_Integer
i
;
/* integer numbers */
lua_Number
n
;
/* float numbers */
}
Value
;
#define TValuefields Value value_; int tt_
#ifdef LUA_USE_ESP
# pragma pack(4)
#endif
typedef
struct
lua_TValue
{
TValuefields
;
}
TValue
;
#ifdef LUA_USE_ESP
# pragma pack()
#endif
/* macro defining a nil value */
#define NILCONSTANT {NULL}, LUA_TNIL
#define val_(o) ((o)->value_)
/* raw type tag of a TValue */
#define rttype(o) ((o)->tt_)
/* tag with no variants (bits 0-3) */
#define novariant(x) ((x) & 0x0F)
/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
#define ttype(o) (rttype(o) & 0x3F)
/* type tag of a TValue with no variants (bits 0-3) */
#define ttnov(o) (novariant(rttype(o)))
/* Macros to test type */
#define checktag(o,t) (rttype(o) == (t))
#define checktype(o,t) (ttnov(o) == (t))
#define ttisnumber(o) checktype((o), LUA_TNUMBER)
#define ttisfloat(o) checktag((o), LUA_TNUMFLT)
#define ttisinteger(o) checktag((o), LUA_TNUMINT)
#define ttisnil(o) checktag((o), LUA_TNIL)
#define ttisboolean(o) checktag((o), LUA_TBOOLEAN)
#define ttislightuserdata(o) checktag((o), LUA_TLIGHTUSERDATA)
#define ttisstring(o) checktype((o), LUA_TSTRING)
#define ttisshrstring(o) checktag((o), ctb(LUA_TSHRSTR))
#define ttislngstring(o) checktag((o), ctb(LUA_TLNGSTR))
#define ttistable(o) checktype((o), LUA_TTABLE)
#define ttisrwtable(o) checktag((o), ctb(LUA_TTBLRAM))
#define ttisrotable(o) checktag((o), ctb(LUA_TTBLROF))
#define ttisfunction(o) checktype(o, LUA_TFUNCTION)
#define ttisclosure(o) ((rttype(o) & 0x1F) == LUA_TFUNCTION)
#define ttisCclosure(o) checktag((o), ctb(LUA_TCCL))
#define ttisLclosure(o) checktag((o), ctb(LUA_TLCL))
#define ttislcf(o) checktag((o), LUA_TLCF)
#define ttisfulluserdata(o) checktag((o), ctb(LUA_TUSERDATA))
#define ttisthread(o) checktag((o), ctb(LUA_TTHREAD))
#define ttisdeadkey(o) checktag((o), LUA_TDEADKEY)
/* Macros to access values */
#define ivalue(o) check_exp(ttisinteger(o), val_(o).i)
#define fltvalue(o) check_exp(ttisfloat(o), val_(o).n)
#define nvalue(o) check_exp(ttisnumber(o), \
(ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o)))
#define gcvalue(o) check_exp(iscollectable(o), val_(o).gc)
#define pvalue(o) check_exp(ttislightuserdata(o), val_(o).p)
#define tsvalue(o) check_exp(ttisstring(o), gco2ts(val_(o).gc))
#define uvalue(o) check_exp(ttisfulluserdata(o), gco2u(val_(o).gc))
#define clvalue(o) check_exp(ttisclosure(o), gco2cl(val_(o).gc))
#define clLvalue(o) check_exp(ttisLclosure(o), gco2lcl(val_(o).gc))
#define clCvalue(o) check_exp(ttisCclosure(o), gco2ccl(val_(o).gc))
#define fvalue(o) check_exp(ttislcf(o), val_(o).f)
#define hvalue(o) check_exp(ttistable(o), gco2t(val_(o).gc))
#define rwhvalue(o) check_exp(ttisrwtable(o), gco2rot(val_(o).gc))
#define rohvalue(o) check_exp(ttisrotable(o), gco2rwt(val_(o).gc))
#define bvalue(o) check_exp(ttisboolean(o), val_(o).b)
#define thvalue(o) check_exp(ttisthread(o), gco2th(val_(o).gc))
/* a dead value may get the 'gc' field, but cannot access its contents */
#define deadvalue(o) check_exp(ttisdeadkey(o), cast(void *, val_(o).gc))
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
#define iscollectable(o) (rttype(o) & BIT_ISCOLLECTABLE)
/* Macros for internal tests */
#define righttt(obj) (ttype(obj) == gettt(gcvalue(obj)))
#define checkliveness(L,obj) \
lua_longassert(!iscollectable(obj) || \
(righttt(obj) && (L == NULL || !isdead(G(L),gcvalue(obj)))))
/* Macros to set values */
#define settt_(o,t) ((o)->tt_=(t))
#define setfltvalue(obj,x) \
{ TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_TNUMFLT); }
#define chgfltvalue(obj,x) \
{ TValue *io=(obj); lua_assert(ttisfloat(io)); val_(io).n=(x); }
#define setivalue(obj,x) \
{ TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); }
#define chgivalue(obj,x) \
{ TValue *io=(obj); lua_assert(ttisinteger(io)); val_(io).i=(x); }
#define setnilvalue(obj) settt_(obj, LUA_TNIL)
#define setfvalue(obj,x) \
{ TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_TLCF); }
#define setpvalue(obj,x) \
{ TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); }
#define setbvalue(obj,x) \
{ TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); }
#define setgcovalue(L,obj,x) \
{ TValue *io = (obj); GCObject *i_g=(x); \
val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); }
#define setsvalue(L,obj,x) \
{ TValue *io = (obj); TString *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(gettt(x_))); \
checkliveness(L,io); }
#define setuvalue(L,obj,x) \
{ TValue *io = (obj); Udata *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TUSERDATA)); \
checkliveness(L,io); }
#define setthvalue(L,obj,x) \
{ TValue *io = (obj); lua_State *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTHREAD)); \
checkliveness(L,io); }
#define setclLvalue(L,obj,x) \
{ TValue *io = (obj); LClosure *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TLCL)); \
checkliveness(L,io); }
#define setclCvalue(L,obj,x) \
{ TValue *io = (obj); CClosure *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TCCL)); \
checkliveness(L,io); }
#define sethvalue(L,obj,x) \
{ TValue *io = (obj); Table *x_ = (x); \
val_(io).gc = obj2gco(x_); settt_(io, ctb(gettt(x_))); \
checkliveness(L,io); }
#define setdeadvalue(obj) settt_(obj, LUA_TDEADKEY)
#define setobj(L,obj1,obj2) \
{ TValue *io1=(obj1); *io1 = *(obj2); \
(void)L; checkliveness(L,io1); }
/*
** different types of assignments, according to destination
*/
/* from stack to (same) stack */
#define setobjs2s setobj
/* to stack (not from same stack) */
#define setobj2s setobj
#define setsvalue2s setsvalue
#define sethvalue2s sethvalue
#define setptvalue2s setptvalue
/* from table to same table */
#define setobjt2t setobj
/* to new object */
#define setobj2n setobj
#define setsvalue2n setsvalue
/* to table (define it as an expression to be used in macros) */
#define setobj2t(L,o1,o2) ((void)L, *(o1)=*(o2), checkliveness(L,(o1)))
/*
** {======================================================
** types and prototypes
** =======================================================
*/
typedef
TValue
*
StkId
;
/* index to stack elements */
/*
** Header for string value; string bytes follow the end of this structure
** (aligned according to 'UTString'; see next).
*/
typedef
struct
TString
{
CommonHeader
;
lu_byte
extra
;
/* reserved words for short strings; "has hash" for longs */
lu_byte
shrlen
;
/* length for short strings */
unsigned
int
hash
;
union
{
size_t
lnglen
;
/* length for long strings */
struct
TString
*
hnext
;
/* linked list for hash table */
}
u
;
}
TString
;
GET_BYTE_FN
(
extra
,
TString
,
4
,
16
)
GET_BYTE_FN
(
shrlen
,
TString
,
4
,
24
)
/*
** Ensures that address after this type is always fully aligned.
*/
typedef
union
UTString
{
L_Umaxalign
dummy
;
/* ensures maximum alignment for strings */
TString
tsv
;
}
UTString
;
/*
** Get the actual string (array of bytes) from a 'TString'.
** (Access to 'extra' ensures that value is really a 'TString'.)
*/
#define getstr(ts) \
check_exp(sizeof((ts)->extra), cast(char *, (ts)) + sizeof(UTString))
/* get the actual string (array of bytes) from a Lua value */
#define svalue(o) getstr(tsvalue(o))
/* get string length from 'TString *s' */
#define tsslen(s) (gettt(s) == LUA_TSHRSTR ? getshrlen(s) : (s)->u.lnglen)
/* get string length from 'TValue *o' */
#define vslen(o) tsslen(tsvalue(o))
/*
** Header for userdata; memory area follows the end of this structure
** (aligned according to 'UUdata'; see next).
*/
typedef
struct
Udata
{
CommonHeader
;
lu_byte
ttuv_
;
/* user value's tag */
struct
Table
*
metatable
;
size_t
len
;
/* number of bytes */
union
Value
user_
;
/* user value */
}
Udata
;
/*
** Ensures that address after this type is always fully aligned.
*/
typedef
union
UUdata
{
L_Umaxalign
dummy
;
/* ensures maximum alignment for 'local' udata */
Udata
uv
;
}
UUdata
;
/*
** Get the address of memory block inside 'Udata'.
** (Access to 'ttuv_' ensures that value is really a 'Udata'.)
*/
#define getudatamem(u) \
check_exp(sizeof((u)->ttuv_), (cast(char*, (u)) + sizeof(UUdata)))
#define setuservalue(L,u,o) \
{ const TValue *io=(o); Udata *iu = (u); \
iu->user_ = io->value_; iu->ttuv_ = rttype(io); \
checkliveness(L,io); }
#define getuservalue(L,u,o) \
{ TValue *io=(o); const Udata *iu = (u); \
io->value_ = iu->user_; settt_(io, iu->ttuv_); \
checkliveness(L,io); }
/*
** Description of an upvalue for function prototypes
*/
typedef
struct
Upvaldesc
{
TString
*
name
;
/* upvalue name (for debug information) */
lu_byte
instack
;
/* whether it is in stack (register) */
lu_byte
idx
;
/* index of upvalue (in stack or in outer function's list) */
}
Upvaldesc
;
/*
** Description of a local variable for function prototypes
** (used for debug information)
*/
typedef
struct
LocVar
{
TString
*
varname
;
int
startpc
;
/* first point where variable is active */
int
endpc
;
/* first point where variable is dead */
}
LocVar
;
/*
** Function Prototypes
*/
typedef
struct
Proto
{
CommonHeader
;
lu_byte
numparams
;
/* number of fixed parameters */
lu_byte
is_vararg
;
lu_byte
maxstacksize
;
/* number of registers needed by this function */
int
sizeupvalues
;
/* size of 'upvalues' */
int
sizek
;
/* size of 'k' */
int
sizecode
;
int
sizelineinfo
;
int
sizep
;
/* size of 'p' */
int
sizelocvars
;
int
linedefined
;
/* debug information */
int
lastlinedefined
;
/* debug information */
TValue
*
k
;
/* constants used by the function */
Instruction
*
code
;
/* opcodes */
struct
Proto
**
p
;
/* functions defined inside the function */
lu_byte
*
lineinfo
;
/* packedmap from opcodes to source lines (debug inf) */
LocVar
*
locvars
;
/* information about local variables (debug information) */
Upvaldesc
*
upvalues
;
/* upvalue information */
TString
*
source
;
/* used for debug information */
GCObject
*
gclist
;
}
Proto
;
GET_BYTE_FN
(
numparams
,
Proto
,
4
,
16
)
GET_BYTE_FN
(
is_vararg
,
Proto
,
4
,
24
)
GET_BYTE_FN
(
maxstacksize
,
Proto
,
8
,
0
)
/*
** Lua Upvalues
*/
typedef
struct
UpVal
UpVal
;
/*
** Closures
*/
#define ClosureHeader \
CommonHeader; lu_byte nupvalues; GCObject *gclist
typedef
struct
CClosure
{
ClosureHeader
;
lua_CFunction
f
;
TValue
upvalue
[
1
];
/* list of upvalues */
}
CClosure
;
typedef
struct
LClosure
{
ClosureHeader
;
struct
Proto
*
p
;
UpVal
*
upvals
[
1
];
/* list of upvalues */
}
LClosure
;
typedef
union
Closure
{
CClosure
c
;
LClosure
l
;
}
Closure
;
#define isLfunction(o) ttisLclosure(o)
#define getproto(o) (clLvalue(o)->p)
/*
** Common Table fields for both table versions (like CommonHeader in
** macro form, to be included in table structure definitions).
**
** Note that the sethvalue() macro works much like the setsvalue()
** macro and handles the abstracted type. the hvalue(o) macro can be
** used to access CommonTable fields, but the rwhvalue(o) and
** rohvalue(o) value variants must be used if accessing variant-specfic
** fields
*/
#define CommonTable CommonHeader; \
lu_byte flags; lu_byte lsizenode; struct Table *metatable;
/*
** Tables
*/
typedef
union
TKey
{
struct
{
TValuefields
;
int
next
;
/* for chaining (offset for next node) */
}
nk
;
TValue
tvk
;
}
TKey
;
/* copy a value into a key without messing up field 'next' */
#define setnodekey(L,key,obj) \
{ TKey *k_=(key); const TValue *io_=(obj); \
k_->nk.value_ = io_->value_; k_->nk.tt_ = io_->tt_; \
(void)L; checkliveness(L,io_); }
typedef
struct
Node
{
TValue
i_val
;
TKey
i_key
;
}
Node
;
typedef
struct
Table
{
/* flags & 1<<p means tagmethod(p) is not present */
/* lsizenode = log2 of size of 'node' array */
CommonTable
;
unsigned
int
sizearray
;
/* size of 'array' array */
TValue
*
array
;
/* array part */
Node
*
node
;
Node
*
lastfree
;
/* any free position is before this position */
GCObject
*
gclist
;
}
Table
;
GET_BYTE_FN
(
flags
,
Table
,
4
,
16
)
GET_BYTE_FN
(
lsizenode
,
Table
,
4
,
24
)
typedef
const
struct
ROTable_entry
{
const
char
*
key
;
const
TValue
value
;
}
ROTable_entry
;
typedef
struct
ROTable
{
/* next always has the value (GCObject *)((size_t) 1); */
/* flags & 1<<p means tagmethod(p) is not present */
/* lsizenode is the number of ROTable entries */
/* Like TStrings, the ROTable_entry vector follows the ROTable */
CommonTable
;
ROTable_entry
*
entry
;
}
ROTable
;
/*
** 'module' operation for hashing (size is always a power of 2)
*/
#define lmod(s,size) \
(check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))
#define twoto(x) (1<<(x))
#define sizenode(t) (twoto((t)->lsizenode))
/*
** (address of) a fixed nil value
*/
#define luaO_nilobject (&luaO_nilobject_)
LUAI_DDEC
const
TValue
luaO_nilobject_
;
/* size of buffer for 'luaO_utf8esc' function */
#define UTF8BUFFSZ 8
LUAI_FUNC
int
luaO_int2fb
(
unsigned
int
x
);
LUAI_FUNC
int
luaO_fb2int
(
int
x
);
LUAI_FUNC
int
luaO_utf8esc
(
char
*
buff
,
unsigned
long
x
);
LUAI_FUNC
int
luaO_ceillog2
(
unsigned
int
x
);
LUAI_FUNC
void
luaO_arith
(
lua_State
*
L
,
int
op
,
const
TValue
*
p1
,
const
TValue
*
p2
,
TValue
*
res
);
LUAI_FUNC
size_t
luaO_str2num
(
const
char
*
s
,
TValue
*
o
);
LUAI_FUNC
int
luaO_hexavalue
(
int
c
);
LUAI_FUNC
void
luaO_tostring
(
lua_State
*
L
,
StkId
obj
);
LUAI_FUNC
const
char
*
luaO_pushvfstring
(
lua_State
*
L
,
const
char
*
fmt
,
va_list
argp
);
LUAI_FUNC
const
char
*
luaO_pushfstring
(
lua_State
*
L
,
const
char
*
fmt
,
...);
LUAI_FUNC
void
luaO_chunkid
(
char
*
out
,
const
char
*
source
,
size_t
len
);
#endif
components/lua/lua-5.3/lopcodes.c
0 → 100644
View file @
dba57fa0
/*
** $Id: lopcodes.c,v 1.55.1.1 2017/04/19 17:20:42 roberto Exp $
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
#define lopcodes_c
#define LUA_CORE
#include "lprefix.h"
#include <stddef.h>
#include "lopcodes.h"
/* ORDER OP */
LUAI_DDEF
const
char
*
const
luaP_opnames
[
NUM_OPCODES
+
1
]
=
{
"MOVE"
,
"LOADK"
,
"LOADKX"
,
"LOADBOOL"
,
"LOADNIL"
,
"GETUPVAL"
,
"GETTABUP"
,
"GETTABLE"
,
"SETTABUP"
,
"SETUPVAL"
,
"SETTABLE"
,
"NEWTABLE"
,
"SELF"
,
"ADD"
,
"SUB"
,
"MUL"
,
"MOD"
,
"POW"
,
"DIV"
,
"IDIV"
,
"BAND"
,
"BOR"
,
"BXOR"
,
"SHL"
,
"SHR"
,
"UNM"
,
"BNOT"
,
"NOT"
,
"LEN"
,
"CONCAT"
,
"JMP"
,
"EQ"
,
"LT"
,
"LE"
,
"TEST"
,
"TESTSET"
,
"CALL"
,
"TAILCALL"
,
"RETURN"
,
"FORLOOP"
,
"FORPREP"
,
"TFORCALL"
,
"TFORLOOP"
,
"SETLIST"
,
"CLOSURE"
,
"VARARG"
,
"EXTRAARG"
,
NULL
};
#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
LUAI_DDEF
const
lu_byte
luaP_opmodes
[
NUM_OPCODES
]
=
{
/* T A B C mode opcode */
opmode
(
0
,
1
,
OpArgR
,
OpArgN
,
iABC
)
/* OP_MOVE */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgN
,
iABx
)
/* OP_LOADK */
,
opmode
(
0
,
1
,
OpArgN
,
OpArgN
,
iABx
)
/* OP_LOADKX */
,
opmode
(
0
,
1
,
OpArgU
,
OpArgU
,
iABC
)
/* OP_LOADBOOL */
,
opmode
(
0
,
1
,
OpArgU
,
OpArgN
,
iABC
)
/* OP_LOADNIL */
,
opmode
(
0
,
1
,
OpArgU
,
OpArgN
,
iABC
)
/* OP_GETUPVAL */
,
opmode
(
0
,
1
,
OpArgU
,
OpArgK
,
iABC
)
/* OP_GETTABUP */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgK
,
iABC
)
/* OP_GETTABLE */
,
opmode
(
0
,
0
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_SETTABUP */
,
opmode
(
0
,
0
,
OpArgU
,
OpArgN
,
iABC
)
/* OP_SETUPVAL */
,
opmode
(
0
,
0
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_SETTABLE */
,
opmode
(
0
,
1
,
OpArgU
,
OpArgU
,
iABC
)
/* OP_NEWTABLE */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgK
,
iABC
)
/* OP_SELF */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_ADD */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_SUB */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_MUL */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_MOD */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_POW */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_DIV */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_IDIV */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_BAND */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_BOR */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_BXOR */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_SHL */
,
opmode
(
0
,
1
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_SHR */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgN
,
iABC
)
/* OP_UNM */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgN
,
iABC
)
/* OP_BNOT */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgN
,
iABC
)
/* OP_NOT */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgN
,
iABC
)
/* OP_LEN */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgR
,
iABC
)
/* OP_CONCAT */
,
opmode
(
0
,
0
,
OpArgR
,
OpArgN
,
iAsBx
)
/* OP_JMP */
,
opmode
(
1
,
0
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_EQ */
,
opmode
(
1
,
0
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_LT */
,
opmode
(
1
,
0
,
OpArgK
,
OpArgK
,
iABC
)
/* OP_LE */
,
opmode
(
1
,
0
,
OpArgN
,
OpArgU
,
iABC
)
/* OP_TEST */
,
opmode
(
1
,
1
,
OpArgR
,
OpArgU
,
iABC
)
/* OP_TESTSET */
,
opmode
(
0
,
1
,
OpArgU
,
OpArgU
,
iABC
)
/* OP_CALL */
,
opmode
(
0
,
1
,
OpArgU
,
OpArgU
,
iABC
)
/* OP_TAILCALL */
,
opmode
(
0
,
0
,
OpArgU
,
OpArgN
,
iABC
)
/* OP_RETURN */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgN
,
iAsBx
)
/* OP_FORLOOP */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgN
,
iAsBx
)
/* OP_FORPREP */
,
opmode
(
0
,
0
,
OpArgN
,
OpArgU
,
iABC
)
/* OP_TFORCALL */
,
opmode
(
0
,
1
,
OpArgR
,
OpArgN
,
iAsBx
)
/* OP_TFORLOOP */
,
opmode
(
0
,
0
,
OpArgU
,
OpArgU
,
iABC
)
/* OP_SETLIST */
,
opmode
(
0
,
1
,
OpArgU
,
OpArgN
,
iABx
)
/* OP_CLOSURE */
,
opmode
(
0
,
1
,
OpArgU
,
OpArgN
,
iABC
)
/* OP_VARARG */
,
opmode
(
0
,
0
,
OpArgU
,
OpArgU
,
iAx
)
/* OP_EXTRAARG */
};
components/lua/lua-5.3/lopcodes.h
0 → 100644
View file @
dba57fa0
/*
** $Id: lopcodes.h,v 1.149.1.1 2017/04/19 17:20:42 roberto Exp $
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
#ifndef lopcodes_h
#define lopcodes_h
#include "llimits.h"
/*===========================================================================
We assume that instructions are unsigned numbers.
All instructions have an opcode in the first 6 bits.
Instructions can have the following fields:
'A' : 8 bits
'B' : 9 bits
'C' : 9 bits
'Ax' : 26 bits ('A', 'B', and 'C' together)
'Bx' : 18 bits ('B' and 'C' together)
'sBx' : signed Bx
A signed argument is represented in excess K; that is, the number
value is the unsigned value minus K. K is exactly the maximum value
for that argument (so that -max is represented by 0, and +max is
represented by 2*max), which is half the maximum for the corresponding
unsigned argument.
===========================================================================*/
enum
OpMode
{
iABC
,
iABx
,
iAsBx
,
iAx
};
/* basic instruction format */
/*
** size and position of opcode arguments.
*/
#define SIZE_C 9
#define SIZE_B 9
#define SIZE_Bx (SIZE_C + SIZE_B)
#define SIZE_A 8
#define SIZE_Ax (SIZE_C + SIZE_B + SIZE_A)
#define SIZE_OP 6
#define POS_OP 0
#define POS_A (POS_OP + SIZE_OP)
#define POS_C (POS_A + SIZE_A)
#define POS_B (POS_C + SIZE_C)
#define POS_Bx POS_C
#define POS_Ax POS_A
/*
** limits for opcode arguments.
** we use (signed) int to manipulate most arguments,
** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
*/
#if SIZE_Bx < LUAI_BITSINT-1
#define MAXARG_Bx ((1<<SIZE_Bx)-1)
#define MAXARG_sBx (MAXARG_Bx>>1)
/* 'sBx' is signed */
#else
#define MAXARG_Bx MAX_INT
#define MAXARG_sBx MAX_INT
#endif
#if SIZE_Ax < LUAI_BITSINT-1
#define MAXARG_Ax ((1<<SIZE_Ax)-1)
#else
#define MAXARG_Ax MAX_INT
#endif
#define MAXARG_A ((1<<SIZE_A)-1)
#define MAXARG_B ((1<<SIZE_B)-1)
#define MAXARG_C ((1<<SIZE_C)-1)
/* creates a mask with 'n' 1 bits at position 'p' */
#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p))
/* creates a mask with 'n' 0 bits at position 'p' */
#define MASK0(n,p) (~MASK1(n,p))
/*
** the following macros help to manipulate instructions
*/
#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
#define getarg(i,pos,size) (cast(int, ((i)>>pos) & MASK1(size,0)))
#define setarg(i,v,pos,size) ((i) = (((i)&MASK0(size,pos)) | \
((cast(Instruction, v)<<pos)&MASK1(size,pos))))
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
#define SETARG_A(i,v) setarg(i, v, POS_A, SIZE_A)
#define GETARG_B(i) getarg(i, POS_B, SIZE_B)
#define SETARG_B(i,v) setarg(i, v, POS_B, SIZE_B)
#define GETARG_C(i) getarg(i, POS_C, SIZE_C)
#define SETARG_C(i,v) setarg(i, v, POS_C, SIZE_C)
#define GETARG_Bx(i) getarg(i, POS_Bx, SIZE_Bx)
#define SETARG_Bx(i,v) setarg(i, v, POS_Bx, SIZE_Bx)
#define GETARG_Ax(i) getarg(i, POS_Ax, SIZE_Ax)
#define SETARG_Ax(i,v) setarg(i, v, POS_Ax, SIZE_Ax)
#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
#define SETARG_sBx(i,b) SETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))
#define CREATE_ABC(o,a,b,c) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_A) \
| (cast(Instruction, b)<<POS_B) \
| (cast(Instruction, c)<<POS_C))
#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_A) \
| (cast(Instruction, bc)<<POS_Bx))
#define CREATE_Ax(o,a) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_Ax))
/*
** Macros to operate RK indices
*/
/* this bit 1 means constant (0 means register) */
#define BITRK (1 << (SIZE_B - 1))
/* test whether value is a constant */
#define ISK(x) ((x) & BITRK)
/* gets the index of the constant */
#define INDEXK(r) ((int)(r) & ~BITRK)
#if !defined(MAXINDEXRK)
/* (for debugging only) */
#define MAXINDEXRK (BITRK - 1)
#endif
/* code a constant index as a RK value */
#define RKASK(x) ((x) | BITRK)
/*
** invalid register that fits in 8 bits
*/
#define NO_REG MAXARG_A
/*
** R(x) - register
** Kst(x) - constant (in constant table)
** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
*/
/*
** grep "ORDER OP" if you change these enums
*/
typedef
enum
{
/*----------------------------------------------------------------------
name args description
------------------------------------------------------------------------*/
OP_MOVE
,
/* A B R(A) := R(B) */
OP_LOADK
,
/* A Bx R(A) := Kst(Bx) */
OP_LOADKX
,
/* A R(A) := Kst(extra arg) */
OP_LOADBOOL
,
/* A B C R(A) := (Bool)B; if (C) pc++ */
OP_LOADNIL
,
/* A B R(A), R(A+1), ..., R(A+B) := nil */
OP_GETUPVAL
,
/* A B R(A) := UpValue[B] */
OP_GETTABUP
,
/* A B C R(A) := UpValue[B][RK(C)] */
OP_GETTABLE
,
/* A B C R(A) := R(B)[RK(C)] */
OP_SETTABUP
,
/* A B C UpValue[A][RK(B)] := RK(C) */
OP_SETUPVAL
,
/* A B UpValue[B] := R(A) */
OP_SETTABLE
,
/* A B C R(A)[RK(B)] := RK(C) */
OP_NEWTABLE
,
/* A B C R(A) := {} (size = B,C) */
OP_SELF
,
/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
OP_ADD
,
/* A B C R(A) := RK(B) + RK(C) */
OP_SUB
,
/* A B C R(A) := RK(B) - RK(C) */
OP_MUL
,
/* A B C R(A) := RK(B) * RK(C) */
OP_MOD
,
/* A B C R(A) := RK(B) % RK(C) */
OP_POW
,
/* A B C R(A) := RK(B) ^ RK(C) */
OP_DIV
,
/* A B C R(A) := RK(B) / RK(C) */
OP_IDIV
,
/* A B C R(A) := RK(B) // RK(C) */
OP_BAND
,
/* A B C R(A) := RK(B) & RK(C) */
OP_BOR
,
/* A B C R(A) := RK(B) | RK(C) */
OP_BXOR
,
/* A B C R(A) := RK(B) ~ RK(C) */
OP_SHL
,
/* A B C R(A) := RK(B) << RK(C) */
OP_SHR
,
/* A B C R(A) := RK(B) >> RK(C) */
OP_UNM
,
/* A B R(A) := -R(B) */
OP_BNOT
,
/* A B R(A) := ~R(B) */
OP_NOT
,
/* A B R(A) := not R(B) */
OP_LEN
,
/* A B R(A) := length of R(B) */
OP_CONCAT
,
/* A B C R(A) := R(B).. ... ..R(C) */
OP_JMP
,
/* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */
OP_EQ
,
/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
OP_LT
,
/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
OP_LE
,
/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
OP_TEST
,
/* A C if not (R(A) <=> C) then pc++ */
OP_TESTSET
,
/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
OP_CALL
,
/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
OP_TAILCALL
,
/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
OP_RETURN
,
/* A B return R(A), ... ,R(A+B-2) (see note) */
OP_FORLOOP
,
/* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
OP_FORPREP
,
/* A sBx R(A)-=R(A+2); pc+=sBx */
OP_TFORCALL
,
/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); */
OP_TFORLOOP
,
/* A sBx if R(A+1) ~= nil then { R(A)=R(A+1); pc += sBx }*/
OP_SETLIST
,
/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
OP_CLOSURE
,
/* A Bx R(A) := closure(KPROTO[Bx]) */
OP_VARARG
,
/* A B R(A), R(A+1), ..., R(A+B-2) = vararg */
OP_EXTRAARG
/* Ax extra (larger) argument for previous opcode */
}
OpCode
;
#define NUM_OPCODES (cast(int, OP_EXTRAARG) + 1)
/*===========================================================================
Notes:
(*) In OP_CALL, if (B == 0) then B = top. If (C == 0), then 'top' is
set to last_result+1, so next open instruction (OP_CALL, OP_RETURN,
OP_SETLIST) may use 'top'.
(*) In OP_VARARG, if (B == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0).
(*) In OP_RETURN, if (B == 0) then return up to 'top'.
(*) In OP_SETLIST, if (B == 0) then B = 'top'; if (C == 0) then next
'instruction' is EXTRAARG(real C).
(*) In OP_LOADKX, the next 'instruction' is always EXTRAARG.
(*) For comparisons, A specifies what condition the test should accept
(true or false).
(*) All 'skips' (pc++) assume that next instruction is a jump.
===========================================================================*/
/*
** masks for instruction properties. The format is:
** bits 0-1: op mode
** bits 2-3: C arg mode
** bits 4-5: B arg mode
** bit 6: instruction set register A
** bit 7: operator is a test (next instruction must be a jump)
*/
enum
OpArgMask
{
OpArgN
,
/* argument is not used */
OpArgU
,
/* argument is used */
OpArgR
,
/* argument is a register or a jump offset */
OpArgK
/* argument is a constant or register/constant */
};
LUAI_DDEC
const
lu_byte
luaP_opmodes
[
NUM_OPCODES
];
#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
LUAI_DDEC
const
char
*
const
luaP_opnames
[
NUM_OPCODES
+
1
];
/* opcode names */
/* number of list items to accumulate before a SETLIST instruction */
#define LFIELDS_PER_FLUSH 50
#endif
components/lua/lua-5.3/lparser.c
0 → 100644
View file @
dba57fa0
/*
** $Id: lparser.c,v 2.155.1.2 2017/04/29 18:11:40 roberto Exp $
** Lua Parser
** See Copyright Notice in lua.h
*/
#define lparser_c
#define LUA_CORE
#include "lprefix.h"
#include <string.h>
#include "lua.h"
#include "lcode.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "llex.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "lstring.h"
#include "ltable.h"
#include "lundump.h"
/* maximum number of local variables per function (must be smaller
than 250, due to the bytecode format) */
#define MAXVARS 200
#define hasmultret(k) ((k) == VCALL || (k) == VVARARG)
/* because all strings are unified by the scanner, the parser
can use pointer equality for string equality */
#define eqstr(a,b) ((a) == (b))
/*
** nodes for block list (list of active blocks)
*/
typedef
struct
BlockCnt
{
struct
BlockCnt
*
previous
;
/* chain */
int
firstlabel
;
/* index of first label in this block */
int
firstgoto
;
/* index of first pending goto in this block */
lu_byte
nactvar
;
/* # active locals outside the block */
lu_byte
upval
;
/* true if some variable in the block is an upvalue */
lu_byte
isloop
;
/* true if 'block' is a loop */
}
BlockCnt
;
/*
** prototypes for recursive non-terminal functions
*/
static
void
statement
(
LexState
*
ls
);
static
void
expr
(
LexState
*
ls
,
expdesc
*
v
);
/* semantic error */
static
l_noret
semerror
(
LexState
*
ls
,
const
char
*
msg
)
{
ls
->
t
.
token
=
0
;
/* remove "near <token>" from final message */
luaX_syntaxerror
(
ls
,
msg
);
}
static
l_noret
error_expected
(
LexState
*
ls
,
int
token
)
{
luaX_syntaxerror
(
ls
,
luaO_pushfstring
(
ls
->
L
,
"%s expected"
,
luaX_token2str
(
ls
,
token
)));
}
static
l_noret
errorlimit
(
FuncState
*
fs
,
int
limit
,
const
char
*
what
)
{
lua_State
*
L
=
fs
->
ls
->
L
;
const
char
*
msg
;
int
line
=
fs
->
f
->
linedefined
;
const
char
*
where
=
(
line
==
0
)
?
"main function"
:
luaO_pushfstring
(
L
,
"function at line %d"
,
line
);
msg
=
luaO_pushfstring
(
L
,
"too many %s (limit is %d) in %s"
,
what
,
limit
,
where
);
luaX_syntaxerror
(
fs
->
ls
,
msg
);
}
static
void
checklimit
(
FuncState
*
fs
,
int
v
,
int
l
,
const
char
*
what
)
{
if
(
v
>
l
)
errorlimit
(
fs
,
l
,
what
);
}
static
int
testnext
(
LexState
*
ls
,
int
c
)
{
if
(
ls
->
t
.
token
==
c
)
{
luaX_next
(
ls
);
return
1
;
}
else
return
0
;
}
static
void
check
(
LexState
*
ls
,
int
c
)
{
if
(
ls
->
t
.
token
!=
c
)
error_expected
(
ls
,
c
);
}
static
void
checknext
(
LexState
*
ls
,
int
c
)
{
check
(
ls
,
c
);
luaX_next
(
ls
);
}
#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); }
static
void
check_match
(
LexState
*
ls
,
int
what
,
int
who
,
int
where
)
{
if
(
!
testnext
(
ls
,
what
))
{
if
(
where
==
ls
->
linenumber
)
error_expected
(
ls
,
what
);
else
{
luaX_syntaxerror
(
ls
,
luaO_pushfstring
(
ls
->
L
,
"%s expected (to close %s at line %d)"
,
luaX_token2str
(
ls
,
what
),
luaX_token2str
(
ls
,
who
),
where
));
}
}
}
static
TString
*
str_checkname
(
LexState
*
ls
)
{
TString
*
ts
;
check
(
ls
,
TK_NAME
);
ts
=
ls
->
t
.
seminfo
.
ts
;
luaX_next
(
ls
);
return
ts
;
}
static
void
init_exp
(
expdesc
*
e
,
expkind
k
,
int
i
)
{
e
->
f
=
e
->
t
=
NO_JUMP
;
e
->
k
=
k
;
e
->
u
.
info
=
i
;
}
static
void
codestring
(
LexState
*
ls
,
expdesc
*
e
,
TString
*
s
)
{
init_exp
(
e
,
VK
,
luaK_stringK
(
ls
->
fs
,
s
));
}
static
void
checkname
(
LexState
*
ls
,
expdesc
*
e
)
{
codestring
(
ls
,
e
,
str_checkname
(
ls
));
}
static
int
registerlocalvar
(
LexState
*
ls
,
TString
*
varname
)
{
FuncState
*
fs
=
ls
->
fs
;
Proto
*
f
=
fs
->
f
;
int
oldsize
=
f
->
sizelocvars
;
luaM_growvector
(
ls
->
L
,
f
->
locvars
,
fs
->
nlocvars
,
f
->
sizelocvars
,
LocVar
,
SHRT_MAX
,
"local variables"
);
while
(
oldsize
<
f
->
sizelocvars
)
f
->
locvars
[
oldsize
++
].
varname
=
NULL
;
f
->
locvars
[
fs
->
nlocvars
].
varname
=
varname
;
luaC_objbarrier
(
ls
->
L
,
f
,
varname
);
return
fs
->
nlocvars
++
;
}
static
void
new_localvar
(
LexState
*
ls
,
TString
*
name
)
{
FuncState
*
fs
=
ls
->
fs
;
Dyndata
*
dyd
=
ls
->
dyd
;
int
reg
=
registerlocalvar
(
ls
,
name
);
checklimit
(
fs
,
dyd
->
actvar
.
n
+
1
-
fs
->
firstlocal
,
MAXVARS
,
"local variables"
);
luaM_growvector
(
ls
->
L
,
dyd
->
actvar
.
arr
,
dyd
->
actvar
.
n
+
1
,
dyd
->
actvar
.
size
,
Vardesc
,
MAX_INT
,
"local variables"
);
dyd
->
actvar
.
arr
[
dyd
->
actvar
.
n
++
].
idx
=
cast
(
short
,
reg
);
}
static
void
new_localvarliteral_
(
LexState
*
ls
,
const
char
*
name
,
size_t
sz
)
{
new_localvar
(
ls
,
luaX_newstring
(
ls
,
name
,
sz
));
}
#define new_localvarliteral(ls,v) \
new_localvarliteral_(ls, "" v, (sizeof(v)/sizeof(char))-1)
static
LocVar
*
getlocvar
(
FuncState
*
fs
,
int
i
)
{
int
idx
=
fs
->
ls
->
dyd
->
actvar
.
arr
[
fs
->
firstlocal
+
i
].
idx
;
lua_assert
(
idx
<
fs
->
nlocvars
);
return
&
fs
->
f
->
locvars
[
idx
];
}
static
void
adjustlocalvars
(
LexState
*
ls
,
int
nvars
)
{
FuncState
*
fs
=
ls
->
fs
;
fs
->
nactvar
=
cast_byte
(
fs
->
nactvar
+
nvars
);
for
(;
nvars
;
nvars
--
)
{
getlocvar
(
fs
,
fs
->
nactvar
-
nvars
)
->
startpc
=
fs
->
pc
;
}
}
static
void
removevars
(
FuncState
*
fs
,
int
tolevel
)
{
fs
->
ls
->
dyd
->
actvar
.
n
-=
(
fs
->
nactvar
-
tolevel
);
while
(
fs
->
nactvar
>
tolevel
)
getlocvar
(
fs
,
--
fs
->
nactvar
)
->
endpc
=
fs
->
pc
;
}
static
int
searchupvalue
(
FuncState
*
fs
,
TString
*
name
)
{
int
i
;
Upvaldesc
*
up
=
fs
->
f
->
upvalues
;
for
(
i
=
0
;
i
<
fs
->
nups
;
i
++
)
{
if
(
eqstr
(
up
[
i
].
name
,
name
))
return
i
;
}
return
-
1
;
/* not found */
}
static
int
newupvalue
(
FuncState
*
fs
,
TString
*
name
,
expdesc
*
v
)
{
Proto
*
f
=
fs
->
f
;
int
oldsize
=
f
->
sizeupvalues
;
checklimit
(
fs
,
fs
->
nups
+
1
,
MAXUPVAL
,
"upvalues"
);
luaM_growvector
(
fs
->
ls
->
L
,
f
->
upvalues
,
fs
->
nups
,
f
->
sizeupvalues
,
Upvaldesc
,
MAXUPVAL
,
"upvalues"
);
while
(
oldsize
<
f
->
sizeupvalues
)
f
->
upvalues
[
oldsize
++
].
name
=
NULL
;
f
->
upvalues
[
fs
->
nups
].
instack
=
(
v
->
k
==
VLOCAL
);
f
->
upvalues
[
fs
->
nups
].
idx
=
cast_byte
(
v
->
u
.
info
);
f
->
upvalues
[
fs
->
nups
].
name
=
name
;
luaC_objbarrier
(
fs
->
ls
->
L
,
f
,
name
);
return
fs
->
nups
++
;
}
static
int
searchvar
(
FuncState
*
fs
,
TString
*
n
)
{
int
i
;
for
(
i
=
cast_int
(
fs
->
nactvar
)
-
1
;
i
>=
0
;
i
--
)
{
if
(
eqstr
(
n
,
getlocvar
(
fs
,
i
)
->
varname
))
return
i
;
}
return
-
1
;
/* not found */
}
/*
Mark block where variable at given level was defined
(to emit close instructions later).
*/
static
void
markupval
(
FuncState
*
fs
,
int
level
)
{
BlockCnt
*
bl
=
fs
->
bl
;
while
(
bl
->
nactvar
>
level
)
bl
=
bl
->
previous
;
bl
->
upval
=
1
;
}
/*
Find variable with given name 'n'. If it is an upvalue, add this
upvalue into all intermediate functions.
*/
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
);
/* look up locals at current level */
if
(
v
>=
0
)
{
/* found? */
init_exp
(
var
,
VLOCAL
,
v
);
/* variable is local */
if
(
!
base
)
markupval
(
fs
,
v
);
/* local will be used as an upval */
}
else
{
/* not found as local at current level; try upvalues */
int
idx
=
searchupvalue
(
fs
,
n
);
/* try existing upvalues */
if
(
idx
<
0
)
{
/* not found? */
singlevaraux
(
fs
->
prev
,
n
,
var
,
0
);
/* try upper levels */
if
(
var
->
k
==
VVOID
)
/* not found? */
return
;
/* it is a global */
/* else was LOCAL or UPVAL */
idx
=
newupvalue
(
fs
,
n
,
var
);
/* will be a new upvalue */
}
init_exp
(
var
,
VUPVAL
,
idx
);
/* new or old upvalue */
}
}
}
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? */
expdesc
key
;
singlevaraux
(
fs
,
ls
->
envn
,
var
,
1
);
/* get environment variable */
lua_assert
(
var
->
k
!=
VVOID
);
/* this one must exist */
codestring
(
ls
,
&
key
,
varname
);
/* key is variable name */
luaK_indexed
(
fs
,
var
,
&
key
);
/* env[varname] */
}
}
static
void
adjust_assign
(
LexState
*
ls
,
int
nvars
,
int
nexps
,
expdesc
*
e
)
{
FuncState
*
fs
=
ls
->
fs
;
int
extra
=
nvars
-
nexps
;
if
(
hasmultret
(
e
->
k
))
{
extra
++
;
/* includes call itself */
if
(
extra
<
0
)
extra
=
0
;
luaK_setreturns
(
fs
,
e
,
extra
);
/* last exp. provides the difference */
if
(
extra
>
1
)
luaK_reserveregs
(
fs
,
extra
-
1
);
}
else
{
if
(
e
->
k
!=
VVOID
)
luaK_exp2nextreg
(
fs
,
e
);
/* close last expression */
if
(
extra
>
0
)
{
int
reg
=
fs
->
freereg
;
luaK_reserveregs
(
fs
,
extra
);
luaK_nil
(
fs
,
reg
,
extra
);
}
}
if
(
nexps
>
nvars
)
ls
->
fs
->
freereg
-=
nexps
-
nvars
;
/* remove extra values */
}
static
void
enterlevel
(
LexState
*
ls
)
{
lua_State
*
L
=
ls
->
L
;
++
L
->
nCcalls
;
checklimit
(
ls
->
fs
,
L
->
nCcalls
,
LUAI_MAXCCALLS
,
"C levels"
);
}
#define leavelevel(ls) ((ls)->L->nCcalls--)
static
void
closegoto
(
LexState
*
ls
,
int
g
,
Labeldesc
*
label
)
{
int
i
;
FuncState
*
fs
=
ls
->
fs
;
Labellist
*
gl
=
&
ls
->
dyd
->
gt
;
Labeldesc
*
gt
=
&
gl
->
arr
[
g
];
lua_assert
(
eqstr
(
gt
->
name
,
label
->
name
));
if
(
gt
->
nactvar
<
label
->
nactvar
)
{
TString
*
vname
=
getlocvar
(
fs
,
gt
->
nactvar
)
->
varname
;
const
char
*
msg
=
luaO_pushfstring
(
ls
->
L
,
"<goto %s> at line %d jumps into the scope of local '%s'"
,
getstr
(
gt
->
name
),
gt
->
line
,
getstr
(
vname
));
semerror
(
ls
,
msg
);
}
luaK_patchlist
(
fs
,
gt
->
pc
,
label
->
pc
);
/* remove goto from pending list */
for
(
i
=
g
;
i
<
gl
->
n
-
1
;
i
++
)
gl
->
arr
[
i
]
=
gl
->
arr
[
i
+
1
];
gl
->
n
--
;
}
/*
** try to close a goto with existing labels; this solves backward jumps
*/
static
int
findlabel
(
LexState
*
ls
,
int
g
)
{
int
i
;
BlockCnt
*
bl
=
ls
->
fs
->
bl
;
Dyndata
*
dyd
=
ls
->
dyd
;
Labeldesc
*
gt
=
&
dyd
->
gt
.
arr
[
g
];
/* check labels in current block for a match */
for
(
i
=
bl
->
firstlabel
;
i
<
dyd
->
label
.
n
;
i
++
)
{
Labeldesc
*
lb
=
&
dyd
->
label
.
arr
[
i
];
if
(
eqstr
(
lb
->
name
,
gt
->
name
))
{
/* correct label? */
if
(
gt
->
nactvar
>
lb
->
nactvar
&&
(
bl
->
upval
||
dyd
->
label
.
n
>
bl
->
firstlabel
))
luaK_patchclose
(
ls
->
fs
,
gt
->
pc
,
lb
->
nactvar
);
closegoto
(
ls
,
g
,
lb
);
/* close it */
return
1
;
}
}
return
0
;
/* label not found; cannot close goto */
}
static
int
newlabelentry
(
LexState
*
ls
,
Labellist
*
l
,
TString
*
name
,
int
line
,
int
pc
)
{
int
n
=
l
->
n
;
luaM_growvector
(
ls
->
L
,
l
->
arr
,
n
,
l
->
size
,
Labeldesc
,
SHRT_MAX
,
"labels/gotos"
);
l
->
arr
[
n
].
name
=
name
;
l
->
arr
[
n
].
line
=
line
;
l
->
arr
[
n
].
nactvar
=
ls
->
fs
->
nactvar
;
l
->
arr
[
n
].
pc
=
pc
;
l
->
n
=
n
+
1
;
return
n
;
}
/*
** check whether new label 'lb' matches any pending gotos in current
** block; solves forward jumps
*/
static
void
findgotos
(
LexState
*
ls
,
Labeldesc
*
lb
)
{
Labellist
*
gl
=
&
ls
->
dyd
->
gt
;
int
i
=
ls
->
fs
->
bl
->
firstgoto
;
while
(
i
<
gl
->
n
)
{
if
(
eqstr
(
gl
->
arr
[
i
].
name
,
lb
->
name
))
closegoto
(
ls
,
i
,
lb
);
else
i
++
;
}
}
/*
** export pending gotos to outer level, to check them against
** outer labels; if the block being exited has upvalues, and
** the goto exits the scope of any variable (which can be the
** upvalue), close those variables being exited.
*/
static
void
movegotosout
(
FuncState
*
fs
,
BlockCnt
*
bl
)
{
int
i
=
bl
->
firstgoto
;
Labellist
*
gl
=
&
fs
->
ls
->
dyd
->
gt
;
/* correct pending gotos to current block and try to close it
with visible labels */
while
(
i
<
gl
->
n
)
{
Labeldesc
*
gt
=
&
gl
->
arr
[
i
];
if
(
gt
->
nactvar
>
bl
->
nactvar
)
{
if
(
bl
->
upval
)
luaK_patchclose
(
fs
,
gt
->
pc
,
bl
->
nactvar
);
gt
->
nactvar
=
bl
->
nactvar
;
}
if
(
!
findlabel
(
fs
->
ls
,
i
))
i
++
;
/* move to next one */
}
}
static
void
enterblock
(
FuncState
*
fs
,
BlockCnt
*
bl
,
lu_byte
isloop
)
{
bl
->
isloop
=
isloop
;
bl
->
nactvar
=
fs
->
nactvar
;
bl
->
firstlabel
=
fs
->
ls
->
dyd
->
label
.
n
;
bl
->
firstgoto
=
fs
->
ls
->
dyd
->
gt
.
n
;
bl
->
upval
=
0
;
bl
->
previous
=
fs
->
bl
;
fs
->
bl
=
bl
;
lua_assert
(
fs
->
freereg
==
fs
->
nactvar
);
}
/*
** create a label named 'break' to resolve break statements
*/
static
void
breaklabel
(
LexState
*
ls
)
{
TString
*
n
=
luaS_new
(
ls
->
L
,
"break"
);
int
l
=
newlabelentry
(
ls
,
&
ls
->
dyd
->
label
,
n
,
0
,
ls
->
fs
->
pc
);
findgotos
(
ls
,
&
ls
->
dyd
->
label
.
arr
[
l
]);
}
/*
** generates an error for an undefined 'goto'; choose appropriate
** message when label name is a reserved word (which can only be 'break')
*/
static
l_noret
undefgoto
(
LexState
*
ls
,
Labeldesc
*
gt
)
{
const
char
*
msg
=
isreserved
(
gt
->
name
)
?
"<%s> at line %d not inside a loop"
:
"no visible label '%s' for <goto> at line %d"
;
msg
=
luaO_pushfstring
(
ls
->
L
,
msg
,
getstr
(
gt
->
name
),
gt
->
line
);
semerror
(
ls
,
msg
);
}
static
void
leaveblock
(
FuncState
*
fs
)
{
BlockCnt
*
bl
=
fs
->
bl
;
LexState
*
ls
=
fs
->
ls
;
if
(
bl
->
previous
&&
bl
->
upval
)
{
/* create a 'jump to here' to close upvalues */
int
j
=
luaK_jump
(
fs
);
luaK_patchclose
(
fs
,
j
,
bl
->
nactvar
);
luaK_patchtohere
(
fs
,
j
);
}
if
(
bl
->
isloop
)
breaklabel
(
ls
);
/* close pending breaks */
fs
->
bl
=
bl
->
previous
;
removevars
(
fs
,
bl
->
nactvar
);
lua_assert
(
bl
->
nactvar
==
fs
->
nactvar
);
fs
->
freereg
=
fs
->
nactvar
;
/* free registers */
ls
->
dyd
->
label
.
n
=
bl
->
firstlabel
;
/* remove local labels */
if
(
bl
->
previous
)
/* inner block? */
movegotosout
(
fs
,
bl
);
/* update pending gotos to outer block */
else
if
(
bl
->
firstgoto
<
ls
->
dyd
->
gt
.
n
)
/* pending gotos in outer block? */
undefgoto
(
ls
,
&
ls
->
dyd
->
gt
.
arr
[
bl
->
firstgoto
]);
/* error */
}
/*
** adds a new prototype into list of prototypes
*/
static
Proto
*
addprototype
(
LexState
*
ls
)
{
Proto
*
clp
;
lua_State
*
L
=
ls
->
L
;
FuncState
*
fs
=
ls
->
fs
;
Proto
*
f
=
fs
->
f
;
/* prototype of current function */
if
(
fs
->
np
>=
f
->
sizep
)
{
int
oldsize
=
f
->
sizep
;
luaM_growvector
(
L
,
f
->
p
,
fs
->
np
,
f
->
sizep
,
Proto
*
,
MAXARG_Bx
,
"functions"
);
while
(
oldsize
<
f
->
sizep
)
f
->
p
[
oldsize
++
]
=
NULL
;
}
f
->
p
[
fs
->
np
++
]
=
clp
=
luaF_newproto
(
L
);
luaC_objbarrier
(
L
,
f
,
clp
);
return
clp
;
}
/*
** codes instruction to create new closure in parent function.
** The OP_CLOSURE instruction must use the last available register,
** so that, if it invokes the GC, the GC knows which registers
** are in use at that time.
*/
static
void
codeclosure
(
LexState
*
ls
,
expdesc
*
v
)
{
FuncState
*
fs
=
ls
->
fs
->
prev
;
init_exp
(
v
,
VRELOCABLE
,
luaK_codeABx
(
fs
,
OP_CLOSURE
,
0
,
fs
->
np
-
1
));
luaK_exp2nextreg
(
fs
,
v
);
/* fix it at the last register */
}
static
void
open_func
(
LexState
*
ls
,
FuncState
*
fs
,
BlockCnt
*
bl
)
{
Proto
*
f
;
/* Initialise all fields in fs apart from fs->f which is done in the caller */
fs
->
prev
=
ls
->
fs
;
/* linked list of funcstates */
fs
->
ls
=
ls
;
ls
->
fs
=
fs
;
fs
->
pc
=
0
;
fs
->
lasttarget
=
0
;
fs
->
jpc
=
NO_JUMP
;
fs
->
freereg
=
0
;
fs
->
nk
=
0
;
fs
->
np
=
0
;
fs
->
nups
=
0
;
fs
->
nlocvars
=
0
;
fs
->
nactvar
=
0
;
fs
->
firstlocal
=
ls
->
dyd
->
actvar
.
n
;
fs
->
bl
=
NULL
;
f
=
fs
->
f
;
f
->
source
=
ls
->
source
;
f
->
maxstacksize
=
2
;
/* registers 0/1 are always valid */
f
->
lineinfo
=
0
;
fs
->
sizelineinfo
=
0
;
fs
->
lastline
=
0
;
fs
->
lastpc
=
-
1
;
enterblock
(
fs
,
bl
,
0
);
}
static
void
close_func
(
LexState
*
ls
)
{
lua_State
*
L
=
ls
->
L
;
FuncState
*
fs
=
ls
->
fs
;
Proto
*
f
=
fs
->
f
;
luaK_ret
(
fs
,
0
,
0
);
/* final return */
leaveblock
(
fs
);
luaM_reallocvector
(
L
,
f
->
code
,
f
->
sizecode
,
fs
->
pc
,
Instruction
);
f
->
sizecode
=
fs
->
pc
;
luaM_growvector
(
fs
->
ls
->
L
,
f
->
lineinfo
,
fs
->
sizelineinfo
,
f
->
sizelineinfo
,
lu_byte
,
MAX_INT
,
"line codes"
);
f
->
lineinfo
[
fs
->
sizelineinfo
++
]
=
0
;
luaM_reallocvector
(
fs
->
ls
->
L
,
f
->
lineinfo
,
f
->
sizelineinfo
,
fs
->
sizelineinfo
,
lu_byte
);
f
->
sizelineinfo
=
fs
->
sizelineinfo
;
luaM_reallocvector
(
L
,
f
->
k
,
f
->
sizek
,
fs
->
nk
,
TValue
);
f
->
sizek
=
fs
->
nk
;
luaM_reallocvector
(
L
,
f
->
p
,
f
->
sizep
,
fs
->
np
,
Proto
*
);
f
->
sizep
=
fs
->
np
;
luaM_reallocvector
(
L
,
f
->
locvars
,
f
->
sizelocvars
,
fs
->
nlocvars
,
LocVar
);
f
->
sizelocvars
=
fs
->
nlocvars
;
luaM_reallocvector
(
L
,
f
->
upvalues
,
f
->
sizeupvalues
,
fs
->
nups
,
Upvaldesc
);
f
->
sizeupvalues
=
fs
->
nups
;
lua_assert
(
fs
->
bl
==
NULL
);
ls
->
fs
=
fs
->
prev
;
luaC_checkGC
(
L
);
}
/*============================================================*/
/* GRAMMAR RULES */
/*============================================================*/
/*
** check whether current token is in the follow set of a block.
** 'until' closes syntactical blocks, but do not close scope,
** so it is handled in separate.
*/
static
int
block_follow
(
LexState
*
ls
,
int
withuntil
)
{
switch
(
ls
->
t
.
token
)
{
case
TK_ELSE
:
case
TK_ELSEIF
:
case
TK_END
:
case
TK_EOS
:
return
1
;
case
TK_UNTIL
:
return
withuntil
;
default:
return
0
;
}
}
static
void
statlist
(
LexState
*
ls
)
{
/* statlist -> { stat [';'] } */
while
(
!
block_follow
(
ls
,
1
))
{
if
(
ls
->
t
.
token
==
TK_RETURN
)
{
statement
(
ls
);
return
;
/* 'return' must be last statement */
}
statement
(
ls
);
}
}
static
void
fieldsel
(
LexState
*
ls
,
expdesc
*
v
)
{
/* fieldsel -> ['.' | ':'] NAME */
FuncState
*
fs
=
ls
->
fs
;
expdesc
key
;
luaK_exp2anyregup
(
fs
,
v
);
luaX_next
(
ls
);
/* skip the dot or colon */
checkname
(
ls
,
&
key
);
luaK_indexed
(
fs
,
v
,
&
key
);
}
static
void
yindex
(
LexState
*
ls
,
expdesc
*
v
)
{
/* index -> '[' expr ']' */
luaX_next
(
ls
);
/* skip the '[' */
expr
(
ls
,
v
);
luaK_exp2val
(
ls
->
fs
,
v
);
checknext
(
ls
,
']'
);
}
/*
** {======================================================================
** Rules for Constructors
** =======================================================================
*/
struct
ConsControl
{
expdesc
v
;
/* last list item read */
expdesc
*
t
;
/* table descriptor */
int
nh
;
/* total number of 'record' elements */
int
na
;
/* total number of array elements */
int
tostore
;
/* number of array elements pending to be stored */
};
static
void
recfield
(
LexState
*
ls
,
struct
ConsControl
*
cc
)
{
/* recfield -> (NAME | '['exp1']') = exp1 */
FuncState
*
fs
=
ls
->
fs
;
int
reg
=
ls
->
fs
->
freereg
;
expdesc
key
,
val
;
int
rkkey
;
if
(
ls
->
t
.
token
==
TK_NAME
)
{
checklimit
(
fs
,
cc
->
nh
,
MAX_INT
,
"items in a constructor"
);
checkname
(
ls
,
&
key
);
}
else
/* ls->t.token == '[' */
yindex
(
ls
,
&
key
);
cc
->
nh
++
;
checknext
(
ls
,
'='
);
rkkey
=
luaK_exp2RK
(
fs
,
&
key
);
expr
(
ls
,
&
val
);
luaK_codeABC
(
fs
,
OP_SETTABLE
,
cc
->
t
->
u
.
info
,
rkkey
,
luaK_exp2RK
(
fs
,
&
val
));
fs
->
freereg
=
reg
;
/* free registers */
}
static
void
closelistfield
(
FuncState
*
fs
,
struct
ConsControl
*
cc
)
{
if
(
cc
->
v
.
k
==
VVOID
)
return
;
/* there is no list item */
luaK_exp2nextreg
(
fs
,
&
cc
->
v
);
cc
->
v
.
k
=
VVOID
;
if
(
cc
->
tostore
==
LFIELDS_PER_FLUSH
)
{
luaK_setlist
(
fs
,
cc
->
t
->
u
.
info
,
cc
->
na
,
cc
->
tostore
);
/* flush */
cc
->
tostore
=
0
;
/* no more items pending */
}
}
static
void
lastlistfield
(
FuncState
*
fs
,
struct
ConsControl
*
cc
)
{
if
(
cc
->
tostore
==
0
)
return
;
if
(
hasmultret
(
cc
->
v
.
k
))
{
luaK_setmultret
(
fs
,
&
cc
->
v
);
luaK_setlist
(
fs
,
cc
->
t
->
u
.
info
,
cc
->
na
,
LUA_MULTRET
);
cc
->
na
--
;
/* do not count last expression (unknown number of elements) */
}
else
{
if
(
cc
->
v
.
k
!=
VVOID
)
luaK_exp2nextreg
(
fs
,
&
cc
->
v
);
luaK_setlist
(
fs
,
cc
->
t
->
u
.
info
,
cc
->
na
,
cc
->
tostore
);
}
}
static
void
listfield
(
LexState
*
ls
,
struct
ConsControl
*
cc
)
{
/* listfield -> exp */
expr
(
ls
,
&
cc
->
v
);
checklimit
(
ls
->
fs
,
cc
->
na
,
MAX_INT
,
"items in a constructor"
);
cc
->
na
++
;
cc
->
tostore
++
;
}
static
void
field
(
LexState
*
ls
,
struct
ConsControl
*
cc
)
{
/* field -> listfield | recfield */
switch
(
ls
->
t
.
token
)
{
case
TK_NAME
:
{
/* may be 'listfield' or 'recfield' */
if
(
luaX_lookahead
(
ls
)
!=
'='
)
/* expression? */
listfield
(
ls
,
cc
);
else
recfield
(
ls
,
cc
);
break
;
}
case
'['
:
{
recfield
(
ls
,
cc
);
break
;
}
default:
{
listfield
(
ls
,
cc
);
break
;
}
}
}
static
void
constructor
(
LexState
*
ls
,
expdesc
*
t
)
{
/* constructor -> '{' [ field { sep field } [sep] ] '}'
sep -> ',' | ';' */
FuncState
*
fs
=
ls
->
fs
;
int
line
=
ls
->
linenumber
;
int
pc
=
luaK_codeABC
(
fs
,
OP_NEWTABLE
,
0
,
0
,
0
);
struct
ConsControl
cc
;
cc
.
na
=
cc
.
nh
=
cc
.
tostore
=
0
;
cc
.
t
=
t
;
init_exp
(
t
,
VRELOCABLE
,
pc
);
init_exp
(
&
cc
.
v
,
VVOID
,
0
);
/* no value (yet) */
luaK_exp2nextreg
(
ls
->
fs
,
t
);
/* fix it at stack top */
checknext
(
ls
,
'{'
);
do
{
lua_assert
(
cc
.
v
.
k
==
VVOID
||
cc
.
tostore
>
0
);
if
(
ls
->
t
.
token
==
'}'
)
break
;
closelistfield
(
fs
,
&
cc
);
field
(
ls
,
&
cc
);
}
while
(
testnext
(
ls
,
','
)
||
testnext
(
ls
,
';'
));
check_match
(
ls
,
'}'
,
'{'
,
line
);
lastlistfield
(
fs
,
&
cc
);
SETARG_B
(
fs
->
f
->
code
[
pc
],
luaO_int2fb
(
cc
.
na
));
/* set initial array size */
SETARG_C
(
fs
->
f
->
code
[
pc
],
luaO_int2fb
(
cc
.
nh
));
/* set initial table size */
}
/* }====================================================================== */
static
void
parlist
(
LexState
*
ls
)
{
/* parlist -> [ param { ',' param } ] */
FuncState
*
fs
=
ls
->
fs
;
Proto
*
f
=
fs
->
f
;
int
nparams
=
0
;
f
->
is_vararg
=
0
;
if
(
ls
->
t
.
token
!=
')'
)
{
/* is 'parlist' not empty? */
do
{
switch
(
ls
->
t
.
token
)
{
case
TK_NAME
:
{
/* param -> NAME */
new_localvar
(
ls
,
str_checkname
(
ls
));
nparams
++
;
break
;
}
case
TK_DOTS
:
{
/* param -> '...' */
luaX_next
(
ls
);
f
->
is_vararg
=
1
;
/* declared vararg */
break
;
}
default:
luaX_syntaxerror
(
ls
,
"<name> or '...' expected"
);
}
}
while
(
!
f
->
is_vararg
&&
testnext
(
ls
,
','
));
}
adjustlocalvars
(
ls
,
nparams
);
f
->
numparams
=
cast_byte
(
fs
->
nactvar
);
luaK_reserveregs
(
fs
,
fs
->
nactvar
);
/* reserve register for parameters */
}
static
void
body
(
LexState
*
ls
,
expdesc
*
e
,
int
ismethod
,
int
line
)
{
/* body -> '(' parlist ')' block END */
FuncState
new_fs
;
BlockCnt
bl
;
new_fs
.
f
=
addprototype
(
ls
);
new_fs
.
f
->
linedefined
=
line
;
open_func
(
ls
,
&
new_fs
,
&
bl
);
checknext
(
ls
,
'('
);
if
(
ismethod
)
{
new_localvarliteral
(
ls
,
"self"
);
/* create 'self' parameter */
adjustlocalvars
(
ls
,
1
);
}
parlist
(
ls
);
checknext
(
ls
,
')'
);
statlist
(
ls
);
new_fs
.
f
->
lastlinedefined
=
ls
->
linenumber
;
check_match
(
ls
,
TK_END
,
TK_FUNCTION
,
line
);
codeclosure
(
ls
,
e
);
close_func
(
ls
);
}
static
int
explist
(
LexState
*
ls
,
expdesc
*
v
)
{
/* explist -> expr { ',' expr } */
int
n
=
1
;
/* at least one expression */
expr
(
ls
,
v
);
while
(
testnext
(
ls
,
','
))
{
luaK_exp2nextreg
(
ls
->
fs
,
v
);
expr
(
ls
,
v
);
n
++
;
}
return
n
;
}
static
void
funcargs
(
LexState
*
ls
,
expdesc
*
f
,
int
line
)
{
FuncState
*
fs
=
ls
->
fs
;
expdesc
args
;
int
base
,
nparams
;
switch
(
ls
->
t
.
token
)
{
case
'('
:
{
/* funcargs -> '(' [ explist ] ')' */
luaX_next
(
ls
);
if
(
ls
->
t
.
token
==
')'
)
/* arg list is empty? */
args
.
k
=
VVOID
;
else
{
explist
(
ls
,
&
args
);
luaK_setmultret
(
fs
,
&
args
);
}
check_match
(
ls
,
')'
,
'('
,
line
);
break
;
}
case
'{'
:
{
/* funcargs -> constructor */
constructor
(
ls
,
&
args
);
break
;
}
case
TK_STRING
:
{
/* funcargs -> STRING */
codestring
(
ls
,
&
args
,
ls
->
t
.
seminfo
.
ts
);
luaX_next
(
ls
);
/* must use 'seminfo' before 'next' */
break
;
}
default:
{
luaX_syntaxerror
(
ls
,
"function arguments expected"
);
}
}
lua_assert
(
f
->
k
==
VNONRELOC
);
base
=
f
->
u
.
info
;
/* base register for call */
if
(
hasmultret
(
args
.
k
))
nparams
=
LUA_MULTRET
;
/* open call */
else
{
if
(
args
.
k
!=
VVOID
)
luaK_exp2nextreg
(
fs
,
&
args
);
/* close last argument */
nparams
=
fs
->
freereg
-
(
base
+
1
);
}
init_exp
(
f
,
VCALL
,
luaK_codeABC
(
fs
,
OP_CALL
,
base
,
nparams
+
1
,
2
));
luaK_addlineinfo
(
fs
,
fs
->
pc
-
1
,
line
);
fs
->
freereg
=
base
+
1
;
/* call remove function and arguments and leaves
(unless changed) one result */
}
/*
** {======================================================================
** Expression parsing
** =======================================================================
*/
static
void
primaryexp
(
LexState
*
ls
,
expdesc
*
v
)
{
/* primaryexp -> NAME | '(' expr ')' */
switch
(
ls
->
t
.
token
)
{
case
'('
:
{
int
line
=
ls
->
linenumber
;
luaX_next
(
ls
);
expr
(
ls
,
v
);
check_match
(
ls
,
')'
,
'('
,
line
);
luaK_dischargevars
(
ls
->
fs
,
v
);
return
;
}
case
TK_NAME
:
{
singlevar
(
ls
,
v
);
return
;
}
default:
{
luaX_syntaxerror
(
ls
,
"unexpected symbol"
);
}
}
}
static
void
suffixedexp
(
LexState
*
ls
,
expdesc
*
v
)
{
/* suffixedexp ->
primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
FuncState
*
fs
=
ls
->
fs
;
int
line
=
ls
->
linenumber
;
primaryexp
(
ls
,
v
);
for
(;;)
{
switch
(
ls
->
t
.
token
)
{
case
'.'
:
{
/* fieldsel */
fieldsel
(
ls
,
v
);
break
;
}
case
'['
:
{
/* '[' exp1 ']' */
expdesc
key
;
luaK_exp2anyregup
(
fs
,
v
);
yindex
(
ls
,
&
key
);
luaK_indexed
(
fs
,
v
,
&
key
);
break
;
}
case
':'
:
{
/* ':' NAME funcargs */
expdesc
key
;
luaX_next
(
ls
);
checkname
(
ls
,
&
key
);
luaK_self
(
fs
,
v
,
&
key
);
funcargs
(
ls
,
v
,
line
);
break
;
}
case
'('
:
case
TK_STRING
:
case
'{'
:
{
/* funcargs */
luaK_exp2nextreg
(
fs
,
v
);
funcargs
(
ls
,
v
,
line
);
break
;
}
default:
return
;
}
}
}
static
void
simpleexp
(
LexState
*
ls
,
expdesc
*
v
)
{
/* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... |
constructor | FUNCTION body | suffixedexp */
switch
(
ls
->
t
.
token
)
{
case
TK_FLT
:
{
init_exp
(
v
,
VKFLT
,
0
);
v
->
u
.
nval
=
ls
->
t
.
seminfo
.
r
;
break
;
}
case
TK_INT
:
{
init_exp
(
v
,
VKINT
,
0
);
v
->
u
.
ival
=
ls
->
t
.
seminfo
.
i
;
break
;
}
case
TK_STRING
:
{
codestring
(
ls
,
v
,
ls
->
t
.
seminfo
.
ts
);
break
;
}
case
TK_NIL
:
{
init_exp
(
v
,
VNIL
,
0
);
break
;
}
case
TK_TRUE
:
{
init_exp
(
v
,
VTRUE
,
0
);
break
;
}
case
TK_FALSE
:
{
init_exp
(
v
,
VFALSE
,
0
);
break
;
}
case
TK_DOTS
:
{
/* vararg */
FuncState
*
fs
=
ls
->
fs
;
check_condition
(
ls
,
fs
->
f
->
is_vararg
,
"cannot use '...' outside a vararg function"
);
init_exp
(
v
,
VVARARG
,
luaK_codeABC
(
fs
,
OP_VARARG
,
0
,
1
,
0
));
break
;
}
case
'{'
:
{
/* constructor */
constructor
(
ls
,
v
);
return
;
}
case
TK_FUNCTION
:
{
luaX_next
(
ls
);
body
(
ls
,
v
,
0
,
ls
->
linenumber
);
return
;
}
default:
{
suffixedexp
(
ls
,
v
);
return
;
}
}
luaX_next
(
ls
);
}
static
UnOpr
getunopr
(
int
op
)
{
switch
(
op
)
{
case
TK_NOT
:
return
OPR_NOT
;
case
'-'
:
return
OPR_MINUS
;
case
'~'
:
return
OPR_BNOT
;
case
'#'
:
return
OPR_LEN
;
default:
return
OPR_NOUNOPR
;
}
}
static
BinOpr
getbinopr
(
int
op
)
{
switch
(
op
)
{
case
'+'
:
return
OPR_ADD
;
case
'-'
:
return
OPR_SUB
;
case
'*'
:
return
OPR_MUL
;
case
'%'
:
return
OPR_MOD
;
case
'^'
:
return
OPR_POW
;
case
'/'
:
return
OPR_DIV
;
case
TK_IDIV
:
return
OPR_IDIV
;
case
'&'
:
return
OPR_BAND
;
case
'|'
:
return
OPR_BOR
;
case
'~'
:
return
OPR_BXOR
;
case
TK_SHL
:
return
OPR_SHL
;
case
TK_SHR
:
return
OPR_SHR
;
case
TK_CONCAT
:
return
OPR_CONCAT
;
case
TK_NE
:
return
OPR_NE
;
case
TK_EQ
:
return
OPR_EQ
;
case
'<'
:
return
OPR_LT
;
case
TK_LE
:
return
OPR_LE
;
case
'>'
:
return
OPR_GT
;
case
TK_GE
:
return
OPR_GE
;
case
TK_AND
:
return
OPR_AND
;
case
TK_OR
:
return
OPR_OR
;
default:
return
OPR_NOBINOPR
;
}
}
static
const
struct
{
lu_byte
left
;
/* left priority for each binary operator */
lu_byte
right
;
/* right priority */
}
priority
[]
=
{
/* ORDER OPR */
{
10
,
10
},
{
10
,
10
},
/* '+' '-' */
{
11
,
11
},
{
11
,
11
},
/* '*' '%' */
{
14
,
13
},
/* '^' (right associative) */
{
11
,
11
},
{
11
,
11
},
/* '/' '//' */
{
6
,
6
},
{
4
,
4
},
{
5
,
5
},
/* '&' '|' '~' */
{
7
,
7
},
{
7
,
7
},
/* '<<' '>>' */
{
9
,
8
},
/* '..' (right associative) */
{
3
,
3
},
{
3
,
3
},
{
3
,
3
},
/* ==, <, <= */
{
3
,
3
},
{
3
,
3
},
{
3
,
3
},
/* ~=, >, >= */
{
2
,
2
},
{
1
,
1
}
/* and, or */
};
#define UNARY_PRIORITY 12
/* priority for unary operators */
/*
** subexpr -> (simpleexp | unop subexpr) { binop subexpr }
** where 'binop' is any binary operator with a priority higher than 'limit'
*/
static
BinOpr
subexpr
(
LexState
*
ls
,
expdesc
*
v
,
int
limit
)
{
BinOpr
op
;
UnOpr
uop
;
enterlevel
(
ls
);
uop
=
getunopr
(
ls
->
t
.
token
);
if
(
uop
!=
OPR_NOUNOPR
)
{
int
line
=
ls
->
linenumber
;
luaX_next
(
ls
);
subexpr
(
ls
,
v
,
UNARY_PRIORITY
);
luaK_prefix
(
ls
->
fs
,
uop
,
v
,
line
);
}
else
simpleexp
(
ls
,
v
);
/* expand while operators have priorities higher than 'limit' */
op
=
getbinopr
(
ls
->
t
.
token
);
while
(
op
!=
OPR_NOBINOPR
&&
priority
[
op
].
left
>
limit
)
{
expdesc
v2
;
BinOpr
nextop
;
int
line
=
ls
->
linenumber
;
luaX_next
(
ls
);
luaK_infix
(
ls
->
fs
,
op
,
v
);
/* read sub-expression with higher priority */
nextop
=
subexpr
(
ls
,
&
v2
,
priority
[
op
].
right
);
luaK_posfix
(
ls
->
fs
,
op
,
v
,
&
v2
,
line
);
op
=
nextop
;
}
leavelevel
(
ls
);
return
op
;
/* return first untreated operator */
}
static
void
expr
(
LexState
*
ls
,
expdesc
*
v
)
{
subexpr
(
ls
,
v
,
0
);
}
/* }==================================================================== */
/*
** {======================================================================
** Rules for Statements
** =======================================================================
*/
static
void
block
(
LexState
*
ls
)
{
/* block -> statlist */
FuncState
*
fs
=
ls
->
fs
;
BlockCnt
bl
;
enterblock
(
fs
,
&
bl
,
0
);
statlist
(
ls
);
leaveblock
(
fs
);
}
/*
** structure to chain all variables in the left-hand side of an
** assignment
*/
struct
LHS_assign
{
struct
LHS_assign
*
prev
;
expdesc
v
;
/* variable (global, local, upvalue, or indexed) */
};
/*
** check whether, in an assignment to an upvalue/local variable, the
** upvalue/local variable is begin used in a previous assignment to a
** table. If so, save original upvalue/local value in a safe place and
** use this safe copy in the previous assignment.
*/
static
void
check_conflict
(
LexState
*
ls
,
struct
LHS_assign
*
lh
,
expdesc
*
v
)
{
FuncState
*
fs
=
ls
->
fs
;
int
extra
=
fs
->
freereg
;
/* eventual position to save local variable */
int
conflict
=
0
;
for
(;
lh
;
lh
=
lh
->
prev
)
{
/* check all previous assignments */
if
(
lh
->
v
.
k
==
VINDEXED
)
{
/* assigning to a table? */
/* table is the upvalue/local being assigned now? */
if
(
lh
->
v
.
u
.
ind
.
vt
==
v
->
k
&&
lh
->
v
.
u
.
ind
.
t
==
v
->
u
.
info
)
{
conflict
=
1
;
lh
->
v
.
u
.
ind
.
vt
=
VLOCAL
;
lh
->
v
.
u
.
ind
.
t
=
extra
;
/* previous assignment will use safe copy */
}
/* index is the local being assigned? (index cannot be upvalue) */
if
(
v
->
k
==
VLOCAL
&&
lh
->
v
.
u
.
ind
.
idx
==
v
->
u
.
info
)
{
conflict
=
1
;
lh
->
v
.
u
.
ind
.
idx
=
extra
;
/* previous assignment will use safe copy */
}
}
}
if
(
conflict
)
{
/* copy upvalue/local value to a temporary (in position 'extra') */
OpCode
op
=
(
v
->
k
==
VLOCAL
)
?
OP_MOVE
:
OP_GETUPVAL
;
luaK_codeABC
(
fs
,
op
,
extra
,
v
->
u
.
info
,
0
);
luaK_reserveregs
(
fs
,
1
);
}
}
static
void
assignment
(
LexState
*
ls
,
struct
LHS_assign
*
lh
,
int
nvars
)
{
expdesc
e
;
check_condition
(
ls
,
vkisvar
(
lh
->
v
.
k
),
"syntax error"
);
if
(
testnext
(
ls
,
','
))
{
/* assignment -> ',' suffixedexp assignment */
struct
LHS_assign
nv
;
nv
.
prev
=
lh
;
suffixedexp
(
ls
,
&
nv
.
v
);
if
(
nv
.
v
.
k
!=
VINDEXED
)
check_conflict
(
ls
,
lh
,
&
nv
.
v
);
checklimit
(
ls
->
fs
,
nvars
+
ls
->
L
->
nCcalls
,
LUAI_MAXCCALLS
,
"C levels"
);
assignment
(
ls
,
&
nv
,
nvars
+
1
);
}
else
{
/* assignment -> '=' explist */
int
nexps
;
checknext
(
ls
,
'='
);
nexps
=
explist
(
ls
,
&
e
);
if
(
nexps
!=
nvars
)
adjust_assign
(
ls
,
nvars
,
nexps
,
&
e
);
else
{
luaK_setoneret
(
ls
->
fs
,
&
e
);
/* close last expression */
luaK_storevar
(
ls
->
fs
,
&
lh
->
v
,
&
e
);
return
;
/* avoid default */
}
}
init_exp
(
&
e
,
VNONRELOC
,
ls
->
fs
->
freereg
-
1
);
/* default assignment */
luaK_storevar
(
ls
->
fs
,
&
lh
->
v
,
&
e
);
}
static
int
cond
(
LexState
*
ls
)
{
/* cond -> exp */
expdesc
v
;
expr
(
ls
,
&
v
);
/* read condition */
if
(
v
.
k
==
VNIL
)
v
.
k
=
VFALSE
;
/* 'falses' are all equal here */
luaK_goiftrue
(
ls
->
fs
,
&
v
);
return
v
.
f
;
}
static
void
gotostat
(
LexState
*
ls
,
int
pc
)
{
int
line
=
ls
->
linenumber
;
TString
*
label
;
int
g
;
if
(
testnext
(
ls
,
TK_GOTO
))
label
=
str_checkname
(
ls
);
else
{
luaX_next
(
ls
);
/* skip break */
label
=
luaS_new
(
ls
->
L
,
"break"
);
}
g
=
newlabelentry
(
ls
,
&
ls
->
dyd
->
gt
,
label
,
line
,
pc
);
findlabel
(
ls
,
g
);
/* close it if label already defined */
}
/* check for repeated labels on the same block */
static
void
checkrepeated
(
FuncState
*
fs
,
Labellist
*
ll
,
TString
*
label
)
{
int
i
;
for
(
i
=
fs
->
bl
->
firstlabel
;
i
<
ll
->
n
;
i
++
)
{
if
(
eqstr
(
label
,
ll
->
arr
[
i
].
name
))
{
const
char
*
msg
=
luaO_pushfstring
(
fs
->
ls
->
L
,
"label '%s' already defined on line %d"
,
getstr
(
label
),
ll
->
arr
[
i
].
line
);
semerror
(
fs
->
ls
,
msg
);
}
}
}
/* skip no-op statements */
static
void
skipnoopstat
(
LexState
*
ls
)
{
while
(
ls
->
t
.
token
==
';'
||
ls
->
t
.
token
==
TK_DBCOLON
)
statement
(
ls
);
}
static
void
labelstat
(
LexState
*
ls
,
TString
*
label
,
int
line
)
{
/* label -> '::' NAME '::' */
FuncState
*
fs
=
ls
->
fs
;
Labellist
*
ll
=
&
ls
->
dyd
->
label
;
int
l
;
/* index of new label being created */
checkrepeated
(
fs
,
ll
,
label
);
/* check for repeated labels */
checknext
(
ls
,
TK_DBCOLON
);
/* skip double colon */
/* create new entry for this label */
l
=
newlabelentry
(
ls
,
ll
,
label
,
line
,
luaK_getlabel
(
fs
));
skipnoopstat
(
ls
);
/* skip other no-op statements */
if
(
block_follow
(
ls
,
0
))
{
/* label is last no-op statement in the block? */
/* assume that locals are already out of scope */
ll
->
arr
[
l
].
nactvar
=
fs
->
bl
->
nactvar
;
}
findgotos
(
ls
,
&
ll
->
arr
[
l
]);
}
static
void
whilestat
(
LexState
*
ls
,
int
line
)
{
/* whilestat -> WHILE cond DO block END */
FuncState
*
fs
=
ls
->
fs
;
int
whileinit
;
int
condexit
;
BlockCnt
bl
;
luaX_next
(
ls
);
/* skip WHILE */
whileinit
=
luaK_getlabel
(
fs
);
condexit
=
cond
(
ls
);
enterblock
(
fs
,
&
bl
,
1
);
checknext
(
ls
,
TK_DO
);
block
(
ls
);
luaK_jumpto
(
fs
,
whileinit
);
check_match
(
ls
,
TK_END
,
TK_WHILE
,
line
);
leaveblock
(
fs
);
luaK_patchtohere
(
fs
,
condexit
);
/* false conditions finish the loop */
}
static
void
repeatstat
(
LexState
*
ls
,
int
line
)
{
/* repeatstat -> REPEAT block UNTIL cond */
int
condexit
;
FuncState
*
fs
=
ls
->
fs
;
int
repeat_init
=
luaK_getlabel
(
fs
);
BlockCnt
bl1
,
bl2
;
enterblock
(
fs
,
&
bl1
,
1
);
/* loop block */
enterblock
(
fs
,
&
bl2
,
0
);
/* scope block */
luaX_next
(
ls
);
/* skip REPEAT */
statlist
(
ls
);
check_match
(
ls
,
TK_UNTIL
,
TK_REPEAT
,
line
);
condexit
=
cond
(
ls
);
/* read condition (inside scope block) */
if
(
bl2
.
upval
)
/* upvalues? */
luaK_patchclose
(
fs
,
condexit
,
bl2
.
nactvar
);
leaveblock
(
fs
);
/* finish scope */
luaK_patchlist
(
fs
,
condexit
,
repeat_init
);
/* close the loop */
leaveblock
(
fs
);
/* finish loop */
}
static
int
exp1
(
LexState
*
ls
)
{
expdesc
e
;
int
reg
;
expr
(
ls
,
&
e
);
luaK_exp2nextreg
(
ls
->
fs
,
&
e
);
lua_assert
(
e
.
k
==
VNONRELOC
);
reg
=
e
.
u
.
info
;
return
reg
;
}
static
void
forbody
(
LexState
*
ls
,
int
base
,
int
line
,
int
nvars
,
int
isnum
)
{
/* forbody -> DO block */
BlockCnt
bl
;
FuncState
*
fs
=
ls
->
fs
;
int
prep
,
endfor
;
adjustlocalvars
(
ls
,
3
);
/* control variables */
checknext
(
ls
,
TK_DO
);
prep
=
isnum
?
luaK_codeAsBx
(
fs
,
OP_FORPREP
,
base
,
NO_JUMP
)
:
luaK_jump
(
fs
);
enterblock
(
fs
,
&
bl
,
0
);
/* scope for declared variables */
adjustlocalvars
(
ls
,
nvars
);
luaK_reserveregs
(
fs
,
nvars
);
block
(
ls
);
leaveblock
(
fs
);
/* end of scope for declared variables */
luaK_patchtohere
(
fs
,
prep
);
if
(
isnum
)
/* numeric for? */
endfor
=
luaK_codeAsBx
(
fs
,
OP_FORLOOP
,
base
,
NO_JUMP
);
else
{
/* generic for */
luaK_codeABC
(
fs
,
OP_TFORCALL
,
base
,
0
,
nvars
);
luaK_addlineinfo
(
fs
,
fs
->
pc
-
1
,
line
);
endfor
=
luaK_codeAsBx
(
fs
,
OP_TFORLOOP
,
base
+
2
,
NO_JUMP
);
}
luaK_patchlist
(
fs
,
endfor
,
prep
+
1
);
luaK_addlineinfo
(
fs
,
fs
->
pc
-
1
,
line
);
}
static
void
fornum
(
LexState
*
ls
,
TString
*
varname
,
int
line
)
{
/* fornum -> NAME = exp1,exp1[,exp1] forbody */
FuncState
*
fs
=
ls
->
fs
;
int
base
=
fs
->
freereg
;
new_localvarliteral
(
ls
,
"(for index)"
);
new_localvarliteral
(
ls
,
"(for limit)"
);
new_localvarliteral
(
ls
,
"(for step)"
);
new_localvar
(
ls
,
varname
);
checknext
(
ls
,
'='
);
exp1
(
ls
);
/* initial value */
checknext
(
ls
,
','
);
exp1
(
ls
);
/* limit */
if
(
testnext
(
ls
,
','
))
exp1
(
ls
);
/* optional step */
else
{
/* default step = 1 */
luaK_codek
(
fs
,
fs
->
freereg
,
luaK_intK
(
fs
,
1
));
luaK_reserveregs
(
fs
,
1
);
}
forbody
(
ls
,
base
,
line
,
1
,
1
);
}
static
void
forlist
(
LexState
*
ls
,
TString
*
indexname
)
{
/* forlist -> NAME {,NAME} IN explist forbody */
FuncState
*
fs
=
ls
->
fs
;
expdesc
e
;
int
nvars
=
4
;
/* gen, state, control, plus at least one declared var */
int
line
;
int
base
=
fs
->
freereg
;
/* create control variables */
new_localvarliteral
(
ls
,
"(for generator)"
);
new_localvarliteral
(
ls
,
"(for state)"
);
new_localvarliteral
(
ls
,
"(for control)"
);
/* create declared variables */
new_localvar
(
ls
,
indexname
);
while
(
testnext
(
ls
,
','
))
{
new_localvar
(
ls
,
str_checkname
(
ls
));
nvars
++
;
}
checknext
(
ls
,
TK_IN
);
line
=
ls
->
linenumber
;
adjust_assign
(
ls
,
3
,
explist
(
ls
,
&
e
),
&
e
);
luaK_checkstack
(
fs
,
3
);
/* extra space to call generator */
forbody
(
ls
,
base
,
line
,
nvars
-
3
,
0
);
}
static
void
forstat
(
LexState
*
ls
,
int
line
)
{
/* forstat -> FOR (fornum | forlist) END */
FuncState
*
fs
=
ls
->
fs
;
TString
*
varname
;
BlockCnt
bl
;
enterblock
(
fs
,
&
bl
,
1
);
/* scope for loop and control variables */
luaX_next
(
ls
);
/* skip 'for' */
varname
=
str_checkname
(
ls
);
/* first variable name */
switch
(
ls
->
t
.
token
)
{
case
'='
:
fornum
(
ls
,
varname
,
line
);
break
;
case
','
:
case
TK_IN
:
forlist
(
ls
,
varname
);
break
;
default:
luaX_syntaxerror
(
ls
,
"'=' or 'in' expected"
);
}
check_match
(
ls
,
TK_END
,
TK_FOR
,
line
);
leaveblock
(
fs
);
/* loop scope ('break' jumps to this point) */
}
static
void
test_then_block
(
LexState
*
ls
,
int
*
escapelist
)
{
/* test_then_block -> [IF | ELSEIF] cond THEN block */
BlockCnt
bl
;
FuncState
*
fs
=
ls
->
fs
;
expdesc
v
;
int
jf
;
/* instruction to skip 'then' code (if condition is false) */
luaX_next
(
ls
);
/* skip IF or ELSEIF */
expr
(
ls
,
&
v
);
/* read condition */
checknext
(
ls
,
TK_THEN
);
if
(
ls
->
t
.
token
==
TK_GOTO
||
ls
->
t
.
token
==
TK_BREAK
)
{
luaK_goiffalse
(
ls
->
fs
,
&
v
);
/* will jump to label if condition is true */
enterblock
(
fs
,
&
bl
,
0
);
/* must enter block before 'goto' */
gotostat
(
ls
,
v
.
t
);
/* handle goto/break */
while
(
testnext
(
ls
,
';'
))
{}
/* skip colons */
if
(
block_follow
(
ls
,
0
))
{
/* 'goto' is the entire block? */
leaveblock
(
fs
);
return
;
/* and that is it */
}
else
/* must skip over 'then' part if condition is false */
jf
=
luaK_jump
(
fs
);
}
else
{
/* regular case (not goto/break) */
luaK_goiftrue
(
ls
->
fs
,
&
v
);
/* skip over block if condition is false */
enterblock
(
fs
,
&
bl
,
0
);
jf
=
v
.
f
;
}
statlist
(
ls
);
/* 'then' part */
leaveblock
(
fs
);
if
(
ls
->
t
.
token
==
TK_ELSE
||
ls
->
t
.
token
==
TK_ELSEIF
)
/* followed by 'else'/'elseif'? */
luaK_concat
(
fs
,
escapelist
,
luaK_jump
(
fs
));
/* must jump over it */
luaK_patchtohere
(
fs
,
jf
);
}
static
void
ifstat
(
LexState
*
ls
,
int
line
)
{
/* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
FuncState
*
fs
=
ls
->
fs
;
int
escapelist
=
NO_JUMP
;
/* exit list for finished parts */
test_then_block
(
ls
,
&
escapelist
);
/* IF cond THEN block */
while
(
ls
->
t
.
token
==
TK_ELSEIF
)
test_then_block
(
ls
,
&
escapelist
);
/* ELSEIF cond THEN block */
if
(
testnext
(
ls
,
TK_ELSE
))
block
(
ls
);
/* 'else' part */
check_match
(
ls
,
TK_END
,
TK_IF
,
line
);
luaK_patchtohere
(
fs
,
escapelist
);
/* patch escape list to 'if' end */
}
static
void
localfunc
(
LexState
*
ls
)
{
expdesc
b
;
FuncState
*
fs
=
ls
->
fs
;
new_localvar
(
ls
,
str_checkname
(
ls
));
/* new local variable */
adjustlocalvars
(
ls
,
1
);
/* enter its scope */
body
(
ls
,
&
b
,
0
,
ls
->
linenumber
);
/* function created in next register */
/* debug information will only see the variable after this point! */
getlocvar
(
fs
,
b
.
u
.
info
)
->
startpc
=
fs
->
pc
;
}
static
void
localstat
(
LexState
*
ls
)
{
/* stat -> LOCAL NAME {',' NAME} ['=' explist] */
int
nvars
=
0
;
int
nexps
;
expdesc
e
;
do
{
new_localvar
(
ls
,
str_checkname
(
ls
));
nvars
++
;
}
while
(
testnext
(
ls
,
','
));
if
(
testnext
(
ls
,
'='
))
nexps
=
explist
(
ls
,
&
e
);
else
{
e
.
k
=
VVOID
;
nexps
=
0
;
}
adjust_assign
(
ls
,
nvars
,
nexps
,
&
e
);
adjustlocalvars
(
ls
,
nvars
);
}
static
int
funcname
(
LexState
*
ls
,
expdesc
*
v
)
{
/* funcname -> NAME {fieldsel} [':' NAME] */
int
ismethod
=
0
;
singlevar
(
ls
,
v
);
while
(
ls
->
t
.
token
==
'.'
)
fieldsel
(
ls
,
v
);
if
(
ls
->
t
.
token
==
':'
)
{
ismethod
=
1
;
fieldsel
(
ls
,
v
);
}
return
ismethod
;
}
static
void
funcstat
(
LexState
*
ls
,
int
line
)
{
/* funcstat -> FUNCTION funcname body */
int
ismethod
;
expdesc
v
,
b
;
luaX_next
(
ls
);
/* skip FUNCTION */
ismethod
=
funcname
(
ls
,
&
v
);
body
(
ls
,
&
b
,
ismethod
,
line
);
luaK_storevar
(
ls
->
fs
,
&
v
,
&
b
);
luaK_addlineinfo
(
ls
->
fs
,
ls
->
fs
->
pc
-
1
,
line
);
/* definition "happens" in the first line */
}
static
void
exprstat
(
LexState
*
ls
)
{
/* stat -> func | assignment */
FuncState
*
fs
=
ls
->
fs
;
struct
LHS_assign
v
;
suffixedexp
(
ls
,
&
v
.
v
);
if
(
ls
->
t
.
token
==
'='
||
ls
->
t
.
token
==
','
)
{
/* stat -> assignment ? */
v
.
prev
=
NULL
;
assignment
(
ls
,
&
v
,
1
);
}
else
{
/* stat -> func */
check_condition
(
ls
,
v
.
v
.
k
==
VCALL
,
"syntax error"
);
SETARG_C
(
getinstruction
(
fs
,
&
v
.
v
),
1
);
/* call statement uses no results */
}
}
static
void
retstat
(
LexState
*
ls
)
{
/* stat -> RETURN [explist] [';'] */
FuncState
*
fs
=
ls
->
fs
;
expdesc
e
;
int
first
,
nret
;
/* registers with returned values */
if
(
block_follow
(
ls
,
1
)
||
ls
->
t
.
token
==
';'
)
first
=
nret
=
0
;
/* return no values */
else
{
nret
=
explist
(
ls
,
&
e
);
/* optional return values */
if
(
hasmultret
(
e
.
k
))
{
luaK_setmultret
(
fs
,
&
e
);
if
(
e
.
k
==
VCALL
&&
nret
==
1
)
{
/* tail call? */
SET_OPCODE
(
getinstruction
(
fs
,
&
e
),
OP_TAILCALL
);
lua_assert
(
GETARG_A
(
getinstruction
(
fs
,
&
e
))
==
fs
->
nactvar
);
}
first
=
fs
->
nactvar
;
nret
=
LUA_MULTRET
;
/* return all values */
}
else
{
if
(
nret
==
1
)
/* only one single value? */
first
=
luaK_exp2anyreg
(
fs
,
&
e
);
else
{
luaK_exp2nextreg
(
fs
,
&
e
);
/* values must go to the stack */
first
=
fs
->
nactvar
;
/* return all active values */
lua_assert
(
nret
==
fs
->
freereg
-
first
);
}
}
}
luaK_ret
(
fs
,
first
,
nret
);
testnext
(
ls
,
';'
);
/* skip optional semicolon */
}
static
void
statement
(
LexState
*
ls
)
{
int
line
=
ls
->
linenumber
;
/* may be needed for error messages */
enterlevel
(
ls
);
switch
(
ls
->
t
.
token
)
{
case
';'
:
{
/* stat -> ';' (empty statement) */
luaX_next
(
ls
);
/* skip ';' */
break
;
}
case
TK_IF
:
{
/* stat -> ifstat */
ifstat
(
ls
,
line
);
break
;
}
case
TK_WHILE
:
{
/* stat -> whilestat */
whilestat
(
ls
,
line
);
break
;
}
case
TK_DO
:
{
/* stat -> DO block END */
luaX_next
(
ls
);
/* skip DO */
block
(
ls
);
check_match
(
ls
,
TK_END
,
TK_DO
,
line
);
break
;
}
case
TK_FOR
:
{
/* stat -> forstat */
forstat
(
ls
,
line
);
break
;
}
case
TK_REPEAT
:
{
/* stat -> repeatstat */
repeatstat
(
ls
,
line
);
break
;
}
case
TK_FUNCTION
:
{
/* stat -> funcstat */
funcstat
(
ls
,
line
);
break
;
}
case
TK_LOCAL
:
{
/* stat -> localstat */
luaX_next
(
ls
);
/* skip LOCAL */
if
(
testnext
(
ls
,
TK_FUNCTION
))
/* local function? */
localfunc
(
ls
);
else
localstat
(
ls
);
break
;
}
case
TK_DBCOLON
:
{
/* stat -> label */
luaX_next
(
ls
);
/* skip double colon */
labelstat
(
ls
,
str_checkname
(
ls
),
line
);
break
;
}
case
TK_RETURN
:
{
/* stat -> retstat */
luaX_next
(
ls
);
/* skip RETURN */
retstat
(
ls
);
break
;
}
case
TK_BREAK
:
/* stat -> breakstat */
case
TK_GOTO
:
{
/* stat -> 'goto' NAME */
gotostat
(
ls
,
luaK_jump
(
ls
->
fs
));
break
;
}
default:
{
/* stat -> func | assignment */
exprstat
(
ls
);
break
;
}
}
lua_assert
(
ls
->
fs
->
f
->
maxstacksize
>=
ls
->
fs
->
freereg
&&
ls
->
fs
->
freereg
>=
ls
->
fs
->
nactvar
);
ls
->
fs
->
freereg
=
ls
->
fs
->
nactvar
;
/* free registers */
leavelevel
(
ls
);
}
/* }====================================================================== */
/*
** compiles the main function, which is a regular vararg function with an
** upvalue named LUA_ENV
*/
static
void
mainfunc
(
LexState
*
ls
,
FuncState
*
fs
)
{
BlockCnt
bl
;
expdesc
v
;
open_func
(
ls
,
fs
,
&
bl
);
fs
->
f
->
is_vararg
=
1
;
/* main function is always declared vararg */
init_exp
(
&
v
,
VLOCAL
,
0
);
/* create and... */
newupvalue
(
fs
,
ls
->
envn
,
&
v
);
/* ...set environment upvalue */
luaX_next
(
ls
);
/* read first token */
statlist
(
ls
);
/* parse main body */
check
(
ls
,
TK_EOS
);
close_func
(
ls
);
}
static
void
compile_stripdebug
(
lua_State
*
L
,
Proto
*
f
)
{
int
level
=
G
(
L
)
->
stripdefault
;
if
(
level
>
0
)
luaU_stripdebug
(
L
,
f
,
level
,
1
);
}
LClosure
*
luaY_parser
(
lua_State
*
L
,
ZIO
*
z
,
Mbuffer
*
buff
,
Dyndata
*
dyd
,
const
char
*
name
,
int
firstchar
)
{
LexState
lexstate
;
FuncState
funcstate
;
LClosure
*
cl
=
luaF_newLclosure
(
L
,
1
);
/* create main closure */
setclLvalue
(
L
,
L
->
top
,
cl
);
/* anchor it (to avoid being collected) */
luaD_inctop
(
L
);
lexstate
.
h
=
luaH_new
(
L
);
/* create table for scanner */
sethvalue
(
L
,
L
->
top
,
lexstate
.
h
);
/* anchor it */
luaD_inctop
(
L
);
funcstate
.
f
=
cl
->
p
=
luaF_newproto
(
L
);
funcstate
.
f
->
source
=
luaS_new
(
L
,
name
);
/* create and anchor TString */
lua_assert
(
iswhite
(
funcstate
.
f
));
/* do not need barrier here */
lexstate
.
buff
=
buff
;
lexstate
.
dyd
=
dyd
;
dyd
->
actvar
.
n
=
dyd
->
gt
.
n
=
dyd
->
label
.
n
=
0
;
luaX_setinput
(
L
,
&
lexstate
,
z
,
funcstate
.
f
->
source
,
firstchar
);
mainfunc
(
&
lexstate
,
&
funcstate
);
lua_assert
(
!
funcstate
.
prev
&&
funcstate
.
nups
==
1
&&
!
lexstate
.
fs
);
/* all scopes should be correctly finished */
lua_assert
(
dyd
->
actvar
.
n
==
0
&&
dyd
->
gt
.
n
==
0
&&
dyd
->
label
.
n
==
0
);
compile_stripdebug
(
L
,
funcstate
.
f
);
L
->
top
--
;
/* remove scanner's table */
return
cl
;
/* closure is on the stack, too */
}
components/lua/lua-5.3/lparser.h
0 → 100644
View file @
dba57fa0
/*
** $Id: lparser.h,v 1.76.1.1 2017/04/19 17:20:42 roberto Exp $
** Lua Parser
** See Copyright Notice in lua.h
*/
#ifndef lparser_h
#define lparser_h
#include "llimits.h"
#include "lobject.h"
#include "lzio.h"
/*
** Expression and variable descriptor.
** Code generation for variables and expressions can be delayed to allow
** optimizations; An 'expdesc' structure describes a potentially-delayed
** variable/expression. It has a description of its "main" value plus a
** list of conditional jumps that can also produce its value (generated
** by short-circuit operators 'and'/'or').
*/
/* kinds of variables/expressions */
typedef
enum
{
VVOID
,
/* when 'expdesc' describes the last expression a list,
this kind means an empty list (so, no expression) */
VNIL
,
/* constant nil */
VTRUE
,
/* constant true */
VFALSE
,
/* constant false */
VK
,
/* constant in 'k'; info = index of constant in 'k' */
VKFLT
,
/* floating constant; nval = numerical float value */
VKINT
,
/* integer constant; nval = numerical integer value */
VNONRELOC
,
/* expression has its value in a fixed register;
info = result register */
VLOCAL
,
/* local variable; info = local register */
VUPVAL
,
/* upvalue variable; info = index of upvalue in 'upvalues' */
VINDEXED
,
/* indexed variable;
ind.vt = whether 't' is register or upvalue;
ind.t = table register or upvalue;
ind.idx = key's R/K index */
VJMP
,
/* expression is a test/comparison;
info = pc of corresponding jump instruction */
VRELOCABLE
,
/* expression can put result in any register;
info = instruction pc */
VCALL
,
/* expression is a function call; info = instruction pc */
VVARARG
/* vararg expression; info = instruction pc */
}
expkind
;
#define vkisvar(k) (VLOCAL <= (k) && (k) <= VINDEXED)
#define vkisinreg(k) ((k) == VNONRELOC || (k) == VLOCAL)
typedef
struct
expdesc
{
expkind
k
;
union
{
lua_Integer
ival
;
/* for VKINT */
lua_Number
nval
;
/* for VKFLT */
int
info
;
/* for generic use */
struct
{
/* for indexed variables (VINDEXED) */
short
idx
;
/* index (R/K) */
lu_byte
t
;
/* table (register or upvalue) */
lu_byte
vt
;
/* whether 't' is register (VLOCAL) or upvalue (VUPVAL) */
}
ind
;
}
u
;
int
t
;
/* patch list of 'exit when true' */
int
f
;
/* patch list of 'exit when false' */
}
expdesc
;
/* description of active local variable */
typedef
struct
Vardesc
{
short
idx
;
/* variable index in stack */
}
Vardesc
;
/* description of pending goto statements and label statements */
typedef
struct
Labeldesc
{
TString
*
name
;
/* label identifier */
int
pc
;
/* position in code */
int
line
;
/* line where it appeared */
lu_byte
nactvar
;
/* local level where it appears in current block */
}
Labeldesc
;
/* list of labels or gotos */
typedef
struct
Labellist
{
Labeldesc
*
arr
;
/* array */
int
n
;
/* number of entries in use */
int
size
;
/* array size */
}
Labellist
;
/* dynamic structures used by the parser */
typedef
struct
Dyndata
{
struct
{
/* list of active local variables */
Vardesc
*
arr
;
int
n
;
int
size
;
}
actvar
;
Labellist
gt
;
/* list of pending gotos */
Labellist
label
;
/* list of active labels */
}
Dyndata
;
/* control of blocks */
struct
BlockCnt
;
/* defined in lparser.c */
/* state needed to generate code for a given function */
typedef
struct
FuncState
{
Proto
*
f
;
/* current function header */
struct
FuncState
*
prev
;
/* enclosing function */
struct
LexState
*
ls
;
/* lexical state */
struct
BlockCnt
*
bl
;
/* chain of current blocks */
int
pc
;
/* next position to code (equivalent to 'ncode') */
int
lasttarget
;
/* 'label' of last 'jump label' */
int
jpc
;
/* list of pending jumps to 'pc' */
int
nk
;
/* number of elements in 'k' */
int
np
;
/* number of elements in 'p' */
int
firstlocal
;
/* index of first local var (in Dyndata array) */
short
nlocvars
;
/* number of elements in 'f->locvars' */
lu_byte
nactvar
;
/* number of active local variables */
lu_byte
nups
;
/* number of upvalues */
lu_byte
freereg
;
/* first free register */
int
sizelineinfo
;
/* only used during compilation for line info */
int
lastline
;
/* ditto */
int
lastpc
;
/* ditto */
}
FuncState
;
LUAI_FUNC
LClosure
*
luaY_parser
(
lua_State
*
L
,
ZIO
*
z
,
Mbuffer
*
buff
,
Dyndata
*
dyd
,
const
char
*
name
,
int
firstchar
);
#endif
components/lua/lua-5.3/lprefix.h
0 → 100644
View file @
dba57fa0
/*
** $Id: lprefix.h,v 1.2.1.1 2017/04/19 17:20:42 roberto Exp $
** Definitions for Lua code that must come before any other header file
** See Copyright Notice in lua.h
*/
#ifndef lprefix_h
#define lprefix_h
/*
** Allows POSIX/XSI stuff
*/
#if !defined(LUA_USE_C89)
/* { */
#if !defined(_XOPEN_SOURCE)
#define _XOPEN_SOURCE 600
#elif _XOPEN_SOURCE == 0
#undef _XOPEN_SOURCE
/* use -D_XOPEN_SOURCE=0 to undefine it */
#endif
/*
** Allows manipulation of large files in gcc and some other compilers
*/
#if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS)
#define _LARGEFILE_SOURCE 1
#define _FILE_OFFSET_BITS 64
#endif
#endif
/* } */
/*
** Windows stuff
*/
#if defined(_WIN32)
/* { */
#if !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
/* avoid warnings about ISO C functions */
#endif
#endif
/* } */
#endif
components/lua/lua-5.3/lrotable.h
0 → 100644
View file @
dba57fa0
// 5.1/5.3 compatibility shim
#include "lnodemcu.h"
components/lua/lua-5.3/lstate.c
0 → 100644
View file @
dba57fa0
/*
** $Id: lstate.c,v 2.133.1.1 2017/04/19 17:39:34 roberto Exp $
** Global State
** See Copyright Notice in lua.h
*/
#define lstate_c
#define LUA_CORE
#include "lprefix.h"
#include <stddef.h>
#include <string.h>
#include "lua.h"
#include "lapi.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "llex.h"
#include "lmem.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#if !defined(LUAI_GCPAUSE)
#define LUAI_GCPAUSE 200
/* 200% */
#endif
#if !defined(LUAI_GCMUL)
#define LUAI_GCMUL 200
/* GC runs 'twice the speed' of memory allocation */
#endif
/*
** a macro to help the creation of a unique random seed when a state is
** created; the seed is used to randomize hashes.
*/
#if !defined(luai_makeseed)
#if defined(LUA_USE_ESP8266)
static
inline
unsigned
int
luai_makeseed
(
void
)
{
unsigned
int
r
;
asm
volatile
(
"rsr %0, ccount"
:
"=r"
(
r
));
return
r
;
}
#elif defined(LUA_USE_ESP)
# include "esp_random.h"
# define luai_makeseed() esp_random()
#else
#include <time.h>
#define luai_makeseed() cast(unsigned int, time(NULL))
#endif
#endif
/*
** thread state + extra space
*/
typedef
struct
LX
{
lu_byte
extra_
[
LUA_EXTRASPACE
];
lua_State
l
;
}
LX
;
/*
** Main thread combines a thread state and the global state
*/
typedef
struct
LG
{
LX
l
;
global_State
g
;
}
LG
;
#define fromstate(L) (cast(LX *, cast(lu_byte *, (L)) - offsetof(LX, l)))
/*
** Compute an initial seed as random as possible. Rely on Address Space
** Layout Randomization (if present) to increase randomness..
*/
#define addbuff(b,p,e) \
{ size_t t = cast(size_t, e); \
memcpy(b + p, &t, sizeof(t)); p += sizeof(t); }
static
unsigned
int
makeseed
(
lua_State
*
L
)
{
char
buff
[
4
*
sizeof
(
size_t
)];
unsigned
int
h
=
luai_makeseed
();
int
p
=
0
;
addbuff
(
buff
,
p
,
L
);
/* heap variable */
addbuff
(
buff
,
p
,
&
h
);
/* local variable */
addbuff
(
buff
,
p
,
luaO_nilobject
);
/* global variable */
addbuff
(
buff
,
p
,
&
lua_newstate
);
/* public function */
lua_assert
(
p
==
sizeof
(
buff
));
return
luaS_hash
(
buff
,
p
,
h
);
}
/*
** set GCdebt to a new value keeping the value (totalbytes + GCdebt)
** invariant (and avoiding underflows in 'totalbytes')
*/
void
luaE_setdebt
(
global_State
*
g
,
l_mem
debt
)
{
l_mem
tb
=
gettotalbytes
(
g
);
lua_assert
(
tb
>
0
);
if
(
debt
<
tb
-
MAX_LMEM
)
debt
=
tb
-
MAX_LMEM
;
/* will make 'totalbytes == MAX_LMEM' */
g
->
totalbytes
=
tb
-
debt
;
g
->
GCdebt
=
debt
;
}
CallInfo
*
luaE_extendCI
(
lua_State
*
L
)
{
CallInfo
*
ci
=
luaM_new
(
L
,
CallInfo
);
lua_assert
(
L
->
ci
->
next
==
NULL
);
L
->
ci
->
next
=
ci
;
ci
->
previous
=
L
->
ci
;
ci
->
next
=
NULL
;
L
->
nci
++
;
return
ci
;
}
/*
** free all CallInfo structures not in use by a thread
*/
void
luaE_freeCI
(
lua_State
*
L
)
{
CallInfo
*
ci
=
L
->
ci
;
CallInfo
*
next
=
ci
->
next
;
ci
->
next
=
NULL
;
while
((
ci
=
next
)
!=
NULL
)
{
next
=
ci
->
next
;
luaM_free
(
L
,
ci
);
L
->
nci
--
;
}
}
/*
** free half of the CallInfo structures not in use by a thread
*/
void
luaE_shrinkCI
(
lua_State
*
L
)
{
CallInfo
*
ci
=
L
->
ci
;
CallInfo
*
next2
;
/* next's next */
/* while there are two nexts */
while
(
ci
->
next
!=
NULL
&&
(
next2
=
ci
->
next
->
next
)
!=
NULL
)
{
luaM_free
(
L
,
ci
->
next
);
/* free next */
L
->
nci
--
;
ci
->
next
=
next2
;
/* remove 'next' from the list */
next2
->
previous
=
ci
;
ci
=
next2
;
/* keep next's next */
}
}
static
void
stack_init
(
lua_State
*
L1
,
lua_State
*
L
)
{
int
i
;
CallInfo
*
ci
;
/* initialize stack array */
L1
->
stack
=
luaM_newvector
(
L
,
BASIC_STACK_SIZE
,
TValue
);
L1
->
stacksize
=
BASIC_STACK_SIZE
;
for
(
i
=
0
;
i
<
BASIC_STACK_SIZE
;
i
++
)
setnilvalue
(
L1
->
stack
+
i
);
/* erase new stack */
L1
->
top
=
L1
->
stack
;
L1
->
stack_last
=
L1
->
stack
+
L1
->
stacksize
-
EXTRA_STACK
;
/* initialize first ci */
ci
=
&
L1
->
base_ci
;
ci
->
next
=
ci
->
previous
=
NULL
;
ci
->
callstatus
=
0
;
ci
->
func
=
L1
->
top
;
setnilvalue
(
L1
->
top
++
);
/* 'function' entry for this 'ci' */
ci
->
top
=
L1
->
top
+
LUA_MINSTACK
;
L1
->
ci
=
ci
;
}
static
void
freestack
(
lua_State
*
L
)
{
if
(
L
->
stack
==
NULL
)
return
;
/* stack not completely built yet */
L
->
ci
=
&
L
->
base_ci
;
/* free the entire 'ci' list */
luaE_freeCI
(
L
);
lua_assert
(
L
->
nci
==
0
);
luaM_freearray
(
L
,
L
->
stack
,
L
->
stacksize
);
/* free stack array */
}
/*
** Create registry table and its predefined values
*/
static
void
init_registry
(
lua_State
*
L
,
global_State
*
g
)
{
TValue
temp
;
/* create registry */
Table
*
registry
=
luaH_new
(
L
);
sethvalue
(
L
,
&
g
->
l_registry
,
registry
);
luaH_resize
(
L
,
registry
,
LUA_RIDX_LAST
,
0
);
/* registry[LUA_RIDX_MAINTHREAD] = L */
setthvalue
(
L
,
&
temp
,
L
);
/* temp = L */
luaH_setint
(
L
,
registry
,
LUA_RIDX_MAINTHREAD
,
&
temp
);
/* registry[LUA_RIDX_GLOBALS] = table of globals */
sethvalue
(
L
,
&
temp
,
luaH_new
(
L
));
/* temp = new table (global table) */
luaH_setint
(
L
,
registry
,
LUA_RIDX_GLOBALS
,
&
temp
);
}
LUAI_FUNC
int
luaN_init
(
lua_State
*
L
);
/*
** open parts of the state that may cause memory-allocation errors.
** ('g->version' != NULL flags that the state was completely build)
*/
static
void
f_luaopen
(
lua_State
*
L
,
void
*
ud
)
{
global_State
*
g
=
G
(
L
);
UNUSED
(
ud
);
stack_init
(
L
,
L
);
/* init stack */
init_registry
(
L
,
g
);
luaN_init
(
L
);
/* optionally map RO string table */
luaS_init
(
L
);
luaT_init
(
L
);
luaX_init
(
L
);
g
->
gcrunning
=
1
;
/* allow gc */
g
->
version
=
lua_version
(
NULL
);
luai_userstateopen
(
L
);
}
/*
** preinitialize a thread with consistent values without allocating
** any memory (to avoid errors)
*/
static
void
preinit_thread
(
lua_State
*
L
,
global_State
*
g
)
{
G
(
L
)
=
g
;
L
->
stack
=
NULL
;
L
->
ci
=
NULL
;
L
->
nci
=
0
;
L
->
stacksize
=
0
;
L
->
twups
=
L
;
/* thread has no upvalues */
L
->
errorJmp
=
NULL
;
L
->
nCcalls
=
0
;
L
->
hook
=
NULL
;
L
->
hookmask
=
0
;
L
->
basehookcount
=
0
;
L
->
allowhook
=
1
;
resethookcount
(
L
);
L
->
openupval
=
NULL
;
L
->
nny
=
1
;
L
->
status
=
LUA_OK
;
L
->
errfunc
=
0
;
}
static
lua_State
*
L0
=
NULL
;
static
void
close_state
(
lua_State
*
L
)
{
global_State
*
g
=
G
(
L
);
luaF_close
(
L
,
L
->
stack
);
/* close all upvalues for this thread */
luaC_freeallobjects
(
L
);
/* collect all objects */
if
(
g
->
version
)
/* closing a fully built state? */
luai_userstateclose
(
L
);
luaM_freearray
(
L
,
G
(
L
)
->
strt
.
hash
,
G
(
L
)
->
strt
.
size
);
freestack
(
L
);
if
(
L
==
L0
)
{
(
*
g
->
frealloc
)(
g
->
ud
,
g
->
cache
,
KEYCACHE_N
*
sizeof
(
KeyCacheLine
),
0
);
L0
=
NULL
;
/* so reopening state initialises properly */
}
lua_assert
(
gettotalbytes
(
g
)
==
sizeof
(
LG
));
(
*
g
->
frealloc
)(
g
->
ud
,
fromstate
(
L
),
sizeof
(
LG
),
0
);
/* free main block */
}
LUA_API
lua_State
*
lua_newthread
(
lua_State
*
L
)
{
global_State
*
g
=
G
(
L
);
lua_State
*
L1
;
lua_lock
(
L
);
luaC_checkGC
(
L
);
/* create new thread */
L1
=
&
cast
(
LX
*
,
luaM_newobject
(
L
,
LUA_TTHREAD
,
sizeof
(
LX
)))
->
l
;
L1
->
marked
=
luaC_white
(
g
);
L1
->
tt
=
LUA_TTHREAD
;
/* link it on list 'allgc' */
L1
->
next
=
g
->
allgc
;
g
->
allgc
=
obj2gco
(
L1
);
/* anchor it on L stack */
setthvalue
(
L
,
L
->
top
,
L1
);
api_incr_top
(
L
);
preinit_thread
(
L1
,
g
);
L1
->
hookmask
=
L
->
hookmask
;
L1
->
basehookcount
=
L
->
basehookcount
;
L1
->
hook
=
L
->
hook
;
resethookcount
(
L1
);
/* initialize L1 extra space */
memcpy
(
lua_getextraspace
(
L1
),
lua_getextraspace
(
g
->
mainthread
),
LUA_EXTRASPACE
);
luai_userstatethread
(
L
,
L1
);
stack_init
(
L1
,
L
);
/* init stack */
lua_unlock
(
L
);
return
L1
;
}
void
luaE_freethread
(
lua_State
*
L
,
lua_State
*
L1
)
{
LX
*
l
=
fromstate
(
L1
);
luaF_close
(
L1
,
L1
->
stack
);
/* close all upvalues for this thread */
lua_assert
(
L1
->
openupval
==
NULL
);
luai_userstatefree
(
L
,
L1
);
freestack
(
L1
);
luaM_free
(
L
,
l
);
}
LUAI_FUNC
KeyCache
*
luaE_getcache
(
int
lineno
)
{
return
&
G
(
L0
)
->
cache
[
lineno
][
0
];
}
LUA_API
lua_State
*
lua_newstate
(
lua_Alloc
f
,
void
*
ud
)
{
int
i
;
lua_State
*
L
;
global_State
*
g
;
LG
*
l
=
cast
(
LG
*
,
(
*
f
)(
ud
,
NULL
,
LUA_TTHREAD
,
sizeof
(
LG
)));
if
(
l
==
NULL
)
return
NULL
;
L
=
&
l
->
l
.
l
;
g
=
&
l
->
g
;
L
->
next
=
NULL
;
L
->
tt
=
LUA_TTHREAD
;
g
->
currentwhite
=
bitmask
(
WHITE0BIT
);
L
->
marked
=
luaC_white
(
g
);
preinit_thread
(
L
,
g
);
g
->
frealloc
=
f
;
g
->
ud
=
ud
;
g
->
mainthread
=
L
;
g
->
seed
=
makeseed
(
L
);
/* overwritten by LFS value if LFS loaded */
g
->
gcrunning
=
0
;
/* no GC while building state */
g
->
GCestimate
=
0
;
g
->
strt
.
size
=
g
->
strt
.
nuse
=
0
;
g
->
strt
.
hash
=
NULL
;
setnilvalue
(
&
g
->
l_registry
);
g
->
panic
=
NULL
;
g
->
version
=
NULL
;
g
->
gcstate
=
GCSpause
;
g
->
gckind
=
KGC_NORMAL
;
g
->
allgc
=
g
->
finobj
=
g
->
tobefnz
=
g
->
fixedgc
=
NULL
;
g
->
sweepgc
=
NULL
;
g
->
gray
=
g
->
grayagain
=
NULL
;
g
->
weak
=
g
->
ephemeron
=
g
->
allweak
=
NULL
;
g
->
twups
=
NULL
;
g
->
totalbytes
=
sizeof
(
LG
);
g
->
GCdebt
=
0
;
g
->
gcfinnum
=
0
;
g
->
gcpause
=
LUAI_GCPAUSE
;
g
->
gcstepmul
=
LUAI_GCMUL
;
g
->
stripdefault
=
CONFIG_LUA_OPTIMIZE_DEBUG
;
g
->
ROstrt
.
size
=
0
;
g
->
ROstrt
.
nuse
=
0
;
g
->
ROstrt
.
hash
=
NULL
;
g
->
LFSsize
=
0
;
setnilvalue
(
&
g
->
LFStable
);
g
->
l_LFS
=
NULL
;
#ifdef LUA_ENABLE_TEST
if
(
L0
)
{
/* This is a second state */
g
->
cache
=
G
(
L0
)
->
cache
;
}
else
{
#endif
L0
=
L
;
g
->
cache
=
cast
(
KeyCacheLine
*
,
(
*
f
)(
ud
,
NULL
,
0
,
KEYCACHE_N
*
sizeof
(
KeyCacheLine
)));
memset
(
g
->
cache
,
0
,
KEYCACHE_N
*
sizeof
(
KeyCacheLine
));
#ifdef LUA_ENABLE_TEST
}
#endif
for
(
i
=
0
;
i
<
LUA_NUMTAGS
;
i
++
)
g
->
mt
[
i
]
=
NULL
;
if
(
luaD_rawrunprotected
(
L
,
f_luaopen
,
NULL
)
!=
LUA_OK
)
{
/* memory allocation error: free partial state */
close_state
(
L
);
L
=
NULL
;
}
return
L
;
}
LUA_API
void
lua_close
(
lua_State
*
L
)
{
L
=
G
(
L
)
->
mainthread
;
/* only the main thread can be closed */
lua_lock
(
L
);
close_state
(
L
);
}
components/lua/lua-5.3/lstate.h
0 → 100644
View file @
dba57fa0
/*
** $Id: lstate.h,v 2.133.1.1 2017/04/19 17:39:34 roberto Exp $
** Global State
** See Copyright Notice in lua.h
*/
#ifndef lstate_h
#define lstate_h
#include "lua.h"
#include "lobject.h"
#include "ltm.h"
#include "lzio.h"
/*
** Some notes about garbage-collected objects: All objects in Lua must
** be kept somehow accessible until being freed, so all objects always
** belong to one (and only one) of these lists, using field 'next' of
** the 'CommonHeader' for the link:
**
** 'allgc': all objects not marked for finalization;
** 'finobj': all objects marked for finalization;
** 'tobefnz': all objects ready to be finalized;
** 'fixedgc': all objects that are not to be collected (currently
** only small strings, such as reserved words).
**
** Moreover, there is another set of lists that control gray objects.
** These lists are linked by fields 'gclist'. (All objects that
** can become gray have such a field. The field is not the same
** in all objects, but it always has this name.) Any gray object
** must belong to one of these lists, and all objects in these lists
** must be gray:
**
** 'gray': regular gray objects, still waiting to be visited.
** 'grayagain': objects that must be revisited at the atomic phase.
** That includes
** - black objects got in a write barrier;
** - all kinds of weak tables during propagation phase;
** - all threads.
** 'weak': tables with weak values to be cleared;
** 'ephemeron': ephemeron tables with white->white entries;
** 'allweak': tables with weak keys and/or weak values to be cleared.
** The last three lists are used only during the atomic phase.
*/
struct
lua_longjmp
;
/* defined in ldo.c */
/*
** Atomic type (relative to signals) to better ensure that 'lua_sethook'
** is thread safe
*/
#ifdef LUA_USE_ESP8266
# define l_define l_signal_t size_t
#endif
#if !defined(l_signalT)
#include <signal.h>
#define l_signalT sig_atomic_t
#endif
/* extra stack space to handle TM calls and some other extras */
#define EXTRA_STACK 5
#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
/* kinds of Garbage Collection */
#define KGC_NORMAL 0
#define KGC_EMERGENCY 1
/* gc was forced by an allocation failure */
typedef
struct
stringtable
{
TString
**
hash
;
int
nuse
;
/* number of elements */
int
size
;
}
stringtable
;
/*
** Information about a call.
** When a thread yields, 'func' is adjusted to pretend that the
** top function has only the yielded values in its stack; in that
** case, the actual 'func' value is saved in field 'extra'.
** When a function calls another with a continuation, 'extra' keeps
** the function index so that, in case of errors, the continuation
** function can be called with the correct top.
*/
typedef
struct
CallInfo
{
StkId
func
;
/* function index in the stack */
StkId
top
;
/* top for this function */
struct
CallInfo
*
previous
,
*
next
;
/* dynamic call link */
union
{
struct
{
/* only for Lua functions */
StkId
base
;
/* base for this function */
const
Instruction
*
savedpc
;
}
l
;
struct
{
/* only for C functions */
lua_KFunction
k
;
/* continuation in case of yields */
ptrdiff_t
old_errfunc
;
lua_KContext
ctx
;
/* context info. in case of yields */
}
c
;
}
u
;
ptrdiff_t
extra
;
short
nresults
;
/* expected number of results from this function */
unsigned
short
callstatus
;
}
CallInfo
;
/*
** Bits in CallInfo status
*/
#define CIST_OAH (1<<0)
/* original value of 'allowhook' */
#define CIST_LUA (1<<1)
/* call is running a Lua function */
#define CIST_HOOKED (1<<2)
/* call is running a debug hook */
#define CIST_FRESH (1<<3)
/* call is running on a fresh invocation
of luaV_execute */
#define CIST_YPCALL (1<<4)
/* call is a yieldable protected call */
#define CIST_TAIL (1<<5)
/* call was tail called */
#define CIST_HOOKYIELD (1<<6)
/* last hook called yielded */
#define CIST_LEQ (1<<7)
/* using __lt for __le */
#define CIST_FIN (1<<8)
/* call is running a finalizer */
#define isLua(ci) ((ci)->callstatus & CIST_LUA)
/* assume that CIST_OAH has offset 0 and that 'v' is strictly 0/1 */
#define setoah(st,v) ((st) = ((st) & ~CIST_OAH) | (v))
#define getoah(st) ((st) & CIST_OAH)
/*
** KeyCache used for resolution of ROTable entries and Cstrings
*/
typedef
size_t
KeyCache
;
typedef
KeyCache
KeyCacheLine
[
KEYCACHE_M
];
/*
** 'global state', shared by all threads of this state
*/
typedef
struct
FlashHeader
LFSHeader
;
typedef
struct
global_State
{
lua_Alloc
frealloc
;
/* function to reallocate memory */
void
*
ud
;
/* auxiliary data to 'frealloc' */
l_mem
totalbytes
;
/* number of bytes currently allocated - GCdebt */
l_mem
GCdebt
;
/* bytes allocated not yet compensated by the collector */
lu_mem
GCmemtrav
;
/* memory traversed by the GC */
lu_mem
GCestimate
;
/* an estimate of the non-garbage memory in use */
stringtable
strt
;
/* hash table for strings */
TValue
l_registry
;
unsigned
int
seed
;
/* randomized seed for hashes */
lu_byte
currentwhite
;
lu_byte
gcstate
;
/* state of garbage collector */
lu_byte
gckind
;
/* kind of GC running */
lu_byte
gcrunning
;
/* true if GC is running */
GCObject
*
allgc
;
/* list of all collectable objects */
GCObject
**
sweepgc
;
/* current position of sweep in list */
GCObject
*
finobj
;
/* list of collectable objects with finalizers */
GCObject
*
gray
;
/* list of gray objects */
GCObject
*
grayagain
;
/* list of objects to be traversed atomically */
GCObject
*
weak
;
/* list of tables with weak values */
GCObject
*
ephemeron
;
/* list of ephemeron tables (weak keys) */
GCObject
*
allweak
;
/* list of all-weak tables */
GCObject
*
tobefnz
;
/* list of userdata to be GC */
GCObject
*
fixedgc
;
/* list of objects not to be collected */
struct
lua_State
*
twups
;
/* list of threads with open upvalues */
unsigned
int
gcfinnum
;
/* number of finalizers to call in each GC step */
int
gcpause
;
/* size of pause between successive GCs */
int
gcstepmul
;
/* GC 'granularity' */
int
stripdefault
;
/* default stripping level for compilation */
l_mem
gcmemfreeboard
;
/* Free board which triggers EGC */
lua_CFunction
panic
;
/* to be called in unprotected errors */
struct
lua_State
*
mainthread
;
const
lua_Number
*
version
;
/* pointer to version number */
TString
*
memerrmsg
;
/* memory-error message */
TString
*
tmname
[
TM_N
];
/* array with tag-method names */
struct
Table
*
mt
[
LUA_NUMTAGS
];
/* metatables for basic types */
stringtable
ROstrt
;
/* Flash-based hash table for RO strings */
TValue
LFStable
;
/* Flash-based Proto main */
LFSHeader
*
l_LFS
;
/* Lua Flash Store header */
unsigned
int
LFSsize
;
/* size of LFS partition */
KeyCacheLine
*
cache
;
/* cache for strings in API */
}
global_State
;
/*
** 'per thread' state
*/
struct
lua_State
{
CommonHeader
;
unsigned
short
nci
;
/* number of items in 'ci' list */
lu_byte
status
;
StkId
top
;
/* first free slot in the stack */
global_State
*
l_G
;
CallInfo
*
ci
;
/* call info for current function */
const
Instruction
*
oldpc
;
/* last pc traced */
StkId
stack_last
;
/* last free slot in the stack */
StkId
stack
;
/* stack base */
UpVal
*
openupval
;
/* list of open upvalues in this stack */
GCObject
*
gclist
;
struct
lua_State
*
twups
;
/* list of threads with open upvalues */
struct
lua_longjmp
*
errorJmp
;
/* current error recover point */
CallInfo
base_ci
;
/* CallInfo for first level (C calling Lua) */
volatile
lua_Hook
hook
;
ptrdiff_t
errfunc
;
/* current error handling function (stack index) */
int
stacksize
;
int
basehookcount
;
int
hookcount
;
unsigned
short
nny
;
/* number of non-yieldable calls in stack */
unsigned
short
nCcalls
;
/* number of nested C calls */
l_signalT
hookmask
;
lu_byte
allowhook
;
};
#define G(L) (L->l_G)
/*
** Union of all collectable objects (only for conversions)
*/
union
GCUnion
{
GCObject
gc
;
/* common header */
struct
TString
ts
;
struct
Udata
u
;
union
Closure
cl
;
struct
Table
h
;
struct
ROTable
roh
;
struct
Proto
p
;
struct
lua_State
th
;
/* thread */
};
#define cast_u(o) cast(union GCUnion *, (o))
/* macros to convert a GCObject into a specific value */
#define gco2ts(o) \
check_exp(novariant((gettt(o))) == LUA_TSTRING, &((cast_u(o))->ts))
#define gco2u(o) check_exp((gettt(o)) == LUA_TUSERDATA, &((cast_u(o))->u))
#define gco2lcl(o) check_exp((gettt(o)) == LUA_TLCL, &((cast_u(o))->cl.l))
#define gco2ccl(o) check_exp((gettt(o)) == LUA_TCCL, &((cast_u(o))->cl.c))
#define gco2cl(o) \
check_exp(novariant((gettt(o))) == LUA_TFUNCTION, &((cast_u(o))->cl))
#define gco2t(o) \
check_exp(novariant((gettt(o))) == LUA_TTABLE, &((cast_u(o))->h))
#define gco2rwt(o) check_exp((gettt(o)) == LUA_TTBLRAM, &((cast_u(o))->h))
#define gco2rot(o) check_exp((gettt(o)) == LUA_TTBLROF, &((cast_u(o))->roh))
#define gco2p(o) check_exp((gettt(o)) == LUA_TPROTO, &((cast_u(o))->p))
#define gco2th(o) check_exp((gettt(o)) == LUA_TTHREAD, &((cast_u(o))->th))
/* macro to convert a Lua object into a GCObject */
#define obj2gco(v) \
check_exp(novariant((v)->tt) < LUA_TDEADKEY, (&(cast_u(v)->gc)))
/* actual number of total bytes allocated */
#define gettotalbytes(g) cast(lu_mem, (g)->totalbytes + (g)->GCdebt)
LUAI_FUNC
void
luaE_setdebt
(
global_State
*
g
,
l_mem
debt
);
LUAI_FUNC
void
luaE_freethread
(
lua_State
*
L
,
lua_State
*
L1
);
LUAI_FUNC
CallInfo
*
luaE_extendCI
(
lua_State
*
L
);
LUAI_FUNC
void
luaE_freeCI
(
lua_State
*
L
);
LUAI_FUNC
void
luaE_shrinkCI
(
lua_State
*
L
);
LUAI_FUNC
KeyCache
*
luaE_getcache
(
int
cl
);
#endif
components/lua/lua-5.3/lstring.c
0 → 100644
View file @
dba57fa0
/*
** $Id: lstring.c,v 2.56.1.1 2017/04/19 17:20:42 roberto Exp $
** String table (keeps all strings handled by Lua)
** See Copyright Notice in lua.h
*/
#define lstring_c
#define LUA_CORE
#include "lprefix.h"
#include <string.h>
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#define MEMERRMSG "not enough memory"
/*
** Lua will use at most ~(2^LUAI_HASHLIMIT) bytes from a string to
** compute its hash
*/
#if !defined(LUAI_HASHLIMIT)
#define LUAI_HASHLIMIT 5
#endif
/*
** equality for long strings
*/
int
luaS_eqlngstr
(
TString
*
a
,
TString
*
b
)
{
size_t
len
=
a
->
u
.
lnglen
;
lua_assert
(
gettt
(
a
)
==
LUA_TLNGSTR
&&
gettt
(
b
)
==
LUA_TLNGSTR
);
return
(
a
==
b
)
||
/* same instance or... */
((
len
==
b
->
u
.
lnglen
)
&&
/* equal length and ... */
(
memcmp
(
getstr
(
a
),
getstr
(
b
),
len
)
==
0
));
/* equal contents */
}
unsigned
int
luaS_hash
(
const
char
*
str
,
size_t
l
,
unsigned
int
seed
)
{
unsigned
int
h
=
seed
^
cast
(
unsigned
int
,
l
);
size_t
step
=
(
l
>>
LUAI_HASHLIMIT
)
+
1
;
for
(;
l
>=
step
;
l
-=
step
)
h
^=
((
h
<<
5
)
+
(
h
>>
2
)
+
cast_byte
(
str
[
l
-
1
]));
return
h
;
}
unsigned
int
luaS_hashlongstr
(
TString
*
ts
)
{
lua_assert
(
ts
->
tt
==
LUA_TLNGSTR
);
if
(
getextra
(
ts
)
==
0
)
{
/* no hash? */
ts
->
hash
=
luaS_hash
(
getstr
(
ts
),
ts
->
u
.
lnglen
,
ts
->
hash
);
ts
->
extra
=
1
;
/* now it has its hash */
}
return
ts
->
hash
;
}
/*
** resizes the string table
*/
void
luaS_resize
(
lua_State
*
L
,
int
newsize
)
{
int
i
;
//***FIX*** rentrancy guard during GC
stringtable
*
tb
=
&
G
(
L
)
->
strt
;
if
(
newsize
>
tb
->
size
)
{
/* grow table if needed */
luaM_reallocvector
(
L
,
tb
->
hash
,
tb
->
size
,
newsize
,
TString
*
);
for
(
i
=
tb
->
size
;
i
<
newsize
;
i
++
)
tb
->
hash
[
i
]
=
NULL
;
}
for
(
i
=
0
;
i
<
tb
->
size
;
i
++
)
{
/* rehash */
TString
*
p
=
tb
->
hash
[
i
];
tb
->
hash
[
i
]
=
NULL
;
while
(
p
)
{
/* for each node in the list */
TString
*
hnext
=
p
->
u
.
hnext
;
/* save next */
unsigned
int
h
=
lmod
(
p
->
hash
,
newsize
);
/* new position */
p
->
u
.
hnext
=
tb
->
hash
[
h
];
/* chain it */
tb
->
hash
[
h
]
=
p
;
p
=
hnext
;
}
}
if
(
newsize
<
tb
->
size
)
{
/* shrink table if needed */
/* vanishing slice should be empty */
lua_assert
(
tb
->
hash
[
newsize
]
==
NULL
&&
tb
->
hash
[
tb
->
size
-
1
]
==
NULL
);
luaM_reallocvector
(
L
,
tb
->
hash
,
tb
->
size
,
newsize
,
TString
*
);
}
tb
->
size
=
newsize
;
}
#define STRING_ENTRY(e) (cast(KeyCache,((size_t)(e)) & 1));
/*
** Initialize the string table and the key cache
*/
void
luaS_init
(
lua_State
*
L
)
{
global_State
*
g
=
G
(
L
);
int
i
,
j
;
luaS_resize
(
L
,
MINSTRTABSIZE
);
/* initial size of string table */
/* pre-create memory-error message */
g
->
memerrmsg
=
luaS_newliteral
(
L
,
MEMERRMSG
);
luaC_fix
(
L
,
obj2gco
(
g
->
memerrmsg
));
/* it should never be collected */
/* Initialise the global cache to dummy string entries */
for
(
i
=
0
;
i
<
KEYCACHE_N
;
i
++
)
{
KeyCache
*
p
=
g
->
cache
[
i
];
for
(
j
=
0
;
j
<
KEYCACHE_M
;
j
++
)
p
[
j
]
=
STRING_ENTRY
(
g
->
memerrmsg
);
}
}
/*
** creates a new string object
*/
static
TString
*
createstrobj
(
lua_State
*
L
,
size_t
l
,
int
tag
,
unsigned
int
h
)
{
TString
*
ts
;
GCObject
*
o
;
size_t
totalsize
;
/* total size of TString object */
totalsize
=
sizelstring
(
l
);
o
=
luaC_newobj
(
L
,
tag
,
totalsize
);
ts
=
gco2ts
(
o
);
ts
->
hash
=
h
;
ts
->
extra
=
0
;
getstr
(
ts
)[
l
]
=
'\0'
;
/* ending 0 */
return
ts
;
}
TString
*
luaS_createlngstrobj
(
lua_State
*
L
,
size_t
l
)
{
TString
*
ts
=
createstrobj
(
L
,
l
,
LUA_TLNGSTR
,
G
(
L
)
->
seed
);
ts
->
u
.
lnglen
=
l
;
return
ts
;
}
void
luaS_remove
(
lua_State
*
L
,
TString
*
ts
)
{
stringtable
*
tb
=
&
G
(
L
)
->
strt
;
TString
**
p
=
&
tb
->
hash
[
lmod
(
ts
->
hash
,
tb
->
size
)];
while
(
*
p
!=
ts
)
/* find previous element */
p
=
&
(
*
p
)
->
u
.
hnext
;
*
p
=
(
*
p
)
->
u
.
hnext
;
/* remove element from its list */
tb
->
nuse
--
;
}
/*
** checks whether short string exists and reuses it or creates a new one
*/
static
TString
*
internshrstr
(
lua_State
*
L
,
const
char
*
str
,
size_t
l
)
{
TString
*
ts
;
global_State
*
g
=
G
(
L
);
unsigned
int
h
=
luaS_hash
(
str
,
l
,
g
->
seed
);
TString
**
list
=
&
g
->
strt
.
hash
[
lmod
(
h
,
g
->
strt
.
size
)];
lua_assert
(
str
!=
NULL
);
/* otherwise 'memcmp'/'memcpy' are undefined */
for
(
ts
=
*
list
;
ts
!=
NULL
;
ts
=
ts
->
u
.
hnext
)
{
if
(
l
==
getshrlen
(
ts
)
&&
(
memcmp
(
str
,
getstr
(
ts
),
l
*
sizeof
(
char
))
==
0
))
{
/* found! */
if
(
isdead
(
g
,
ts
))
/* dead (but not collected yet)? */
changewhite
(
ts
);
/* resurrect it */
return
ts
;
}
}
/*
* The RAM strt is searched first since RAM access is faster than flash
* access. If a miss, then search the RO string table.
*/
if
(
g
->
ROstrt
.
hash
)
{
for
(
ts
=
g
->
ROstrt
.
hash
[
lmod
(
h
,
g
->
ROstrt
.
size
)];
ts
!=
NULL
;
ts
=
ts
->
u
.
hnext
)
{
if
(
l
==
getshrlen
(
ts
)
&&
memcmp
(
str
,
getstr
(
ts
),
l
*
sizeof
(
char
))
==
0
)
{
/* found in ROstrt! */
return
ts
;
}
}
}
if
(
g
->
strt
.
nuse
>=
g
->
strt
.
size
&&
g
->
strt
.
size
<=
MAX_INT
/
2
)
{
luaS_resize
(
L
,
g
->
strt
.
size
*
2
);
list
=
&
g
->
strt
.
hash
[
lmod
(
h
,
g
->
strt
.
size
)];
/* recompute with new size */
}
ts
=
createstrobj
(
L
,
l
,
LUA_TSHRSTR
,
h
);
memcpy
(
getstr
(
ts
),
str
,
l
*
sizeof
(
char
));
ts
->
shrlen
=
cast_byte
(
l
);
ts
->
u
.
hnext
=
*
list
;
*
list
=
ts
;
g
->
strt
.
nuse
++
;
return
ts
;
}
/*
** new string (with explicit length)
*/
TString
*
luaS_newlstr
(
lua_State
*
L
,
const
char
*
str
,
size_t
l
)
{
if
(
l
<=
LUAI_MAXSHORTLEN
)
/* short string? */
return
internshrstr
(
L
,
str
,
l
);
else
{
TString
*
ts
;
if
(
l
>=
(
MAX_SIZE
-
sizeof
(
TString
))
/
sizeof
(
char
))
luaM_toobig
(
L
);
ts
=
luaS_createlngstrobj
(
L
,
l
);
memcpy
(
getstr
(
ts
),
str
,
l
*
sizeof
(
char
));
return
ts
;
}
}
/*
** Create or reuse a zero-terminated string, If the null terminated
** length > sizeof (unisigned) then first check the cache (using the
** string address as a key). The cache can contain only zero-
** terminated strings, so it is safe to use 'strcmp' to check hits.
**
** Note that the cache contains both TStrings and Tables entries but
** both of these addresses word are always aligned, so the address is
** a mulitple of size_t. The lowbit of the address in the cache is
** overwritten with a boolean to tag TString entries
*/
#define IS_STRING_ENTRY(e) (e & 1)
#define TSTRING(e) cast(TString *, ((size_t) e) & (~1u))
TString
*
luaS_new
(
lua_State
*
L
,
const
char
*
str
)
{
unsigned
int
i
=
point2uint
(
str
)
%
KEYCACHE_N
;
/* hash */
int
j
;
TString
*
ps
;
KeyCache
*
p
=
G
(
L
)
->
cache
[
i
];
for
(
j
=
0
;
j
<
KEYCACHE_M
;
j
++
)
{
ps
=
TSTRING
(
p
[
j
]);
/* string cache entries always point to a valid TString */
if
(
IS_STRING_ENTRY
(
p
[
j
])
&&
strcmp
(
str
,
getstr
(
ps
))
==
0
)
/* hit? */
return
ps
;
/* that is it */
}
/* normal route, move out last element inserting new string at fist slot */
for
(
j
=
KEYCACHE_M
-
1
;
j
>
0
;
j
--
)
{
p
[
j
]
=
p
[
j
-
1
];
}
ps
=
luaS_newlstr
(
L
,
str
,
strlen
(
str
));
p
[
0
]
=
STRING_ENTRY
(
ps
);
return
ps
;
}
/*
** Clear API cache of dirty string entries.
*/
void
luaS_clearcache
(
global_State
*
g
)
{
int
i
,
j
,
k
;
TString
*
ps
;
for
(
i
=
0
;
i
<
KEYCACHE_N
;
i
++
)
{
KeyCache
*
p
=
g
->
cache
[
i
];
for
(
j
=
0
,
k
=
0
;
j
<
KEYCACHE_M
;
j
++
)
{
ps
=
TSTRING
(
p
[
j
]);
if
(
!
IS_STRING_ENTRY
(
p
[
j
])
||
!
iswhite
(
cast
(
GCObject
*
,
ps
)))
{
/* keep entry? */
if
(
k
<
j
)
p
[
k
]
=
p
[
j
];
/* shift down element */
k
++
;
}
}
for
(;
k
<
KEYCACHE_M
;
k
++
)
p
[
k
]
=
STRING_ENTRY
(
g
->
memerrmsg
);
}
}
Udata
*
luaS_newudata
(
lua_State
*
L
,
size_t
s
)
{
Udata
*
u
;
GCObject
*
o
;
if
(
s
>
MAX_SIZE
-
sizeof
(
Udata
))
luaM_toobig
(
L
);
o
=
luaC_newobj
(
L
,
LUA_TUSERDATA
,
sizeludata
(
s
));
u
=
gco2u
(
o
);
u
->
len
=
s
;
u
->
metatable
=
NULL
;
setuservalue
(
L
,
u
,
luaO_nilobject
);
return
u
;
}
components/lua/lua-5.3/lstring.h
0 → 100644
View file @
dba57fa0
/*
** $Id: lstring.h,v 1.61.1.1 2017/04/19 17:20:42 roberto Exp $
** String table (keep all strings handled by Lua)
** See Copyright Notice in lua.h
*/
#ifndef lstring_h
#define lstring_h
#include "lgc.h"
#include "lobject.h"
#include "lstate.h"
#define sizelstring(l) (sizeof(union UTString) + ((l) + 1) * sizeof(char))
#define sizeludata(l) (sizeof(union UUdata) + (l))
#define sizeudata(u) sizeludata((u)->len)
#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \
(sizeof(s)/sizeof(char))-1))
/*
** test whether a string is a reserved word
*/
#define isreserved(s) (gettt(s) == LUA_TSHRSTR && getextra(s) > 0)
/*
** equality for short strings, which are always internalized
*/
#define eqshrstr(a,b) check_exp(gettt(a) == LUA_TSHRSTR, (a) == (b))
LUAI_FUNC
unsigned
int
luaS_hash
(
const
char
*
str
,
size_t
l
,
unsigned
int
seed
);
LUAI_FUNC
unsigned
int
luaS_hashlongstr
(
TString
*
ts
);
LUAI_FUNC
int
luaS_eqlngstr
(
TString
*
a
,
TString
*
b
);
LUAI_FUNC
void
luaS_resize
(
lua_State
*
L
,
int
newsize
);
LUAI_FUNC
void
luaS_clearcache
(
global_State
*
g
);
LUAI_FUNC
void
luaS_init
(
lua_State
*
L
);
LUAI_FUNC
void
luaS_remove
(
lua_State
*
L
,
TString
*
ts
);
LUAI_FUNC
Udata
*
luaS_newudata
(
lua_State
*
L
,
size_t
s
);
LUAI_FUNC
TString
*
luaS_newlstr
(
lua_State
*
L
,
const
char
*
str
,
size_t
l
);
LUAI_FUNC
TString
*
luaS_new
(
lua_State
*
L
,
const
char
*
str
);
LUAI_FUNC
TString
*
luaS_createlngstrobj
(
lua_State
*
L
,
size_t
l
);
#endif
components/lua/lua-5.3/lstrlib.c
0 → 100644
View file @
dba57fa0
/*
** $Id: lstrlib.c,v 1.254.1.1 2017/04/19 17:29:57 roberto Exp $
** Standard library for string operations and pattern-matching
** See Copyright Notice in lua.h
*/
#define lstrlib_c
#define LUA_LIB
#include "lprefix.h"
#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <locale.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/*
** maximum number of captures that a pattern can do during
** pattern-matching. This limit is arbitrary, but must fit in
** an unsigned char.
*/
#if !defined(LUA_MAXCAPTURES)
#define LUA_MAXCAPTURES 32
#endif
/* macro to 'unsign' a character */
#define uchar(c) ((unsigned char)(c))
/*
** Some sizes are better limited to fit in 'int', but must also fit in
** 'size_t'. (We assume that 'lua_Integer' cannot be smaller than 'int'.)
*/
#define MAX_SIZET ((size_t)(~(size_t)0))
#define MAXSIZE \
(sizeof(size_t) < sizeof(int) ? MAX_SIZET : (size_t)(INT_MAX))
static
int
str_len
(
lua_State
*
L
)
{
size_t
l
;
luaL_checklstring
(
L
,
1
,
&
l
);
lua_pushinteger
(
L
,
(
lua_Integer
)
l
);
return
1
;
}
/* translate a relative string position: negative means back from end */
static
lua_Integer
posrelat
(
lua_Integer
pos
,
size_t
len
)
{
if
(
pos
>=
0
)
return
pos
;
else
if
(
0u
-
(
size_t
)
pos
>
len
)
return
0
;
else
return
(
lua_Integer
)
len
+
pos
+
1
;
}
static
int
str_sub
(
lua_State
*
L
)
{
size_t
l
;
const
char
*
s
=
luaL_checklstring
(
L
,
1
,
&
l
);
lua_Integer
start
=
posrelat
(
luaL_checkinteger
(
L
,
2
),
l
);
lua_Integer
end
=
posrelat
(
luaL_optinteger
(
L
,
3
,
-
1
),
l
);
if
(
start
<
1
)
start
=
1
;
if
(
end
>
(
lua_Integer
)
l
)
end
=
l
;
if
(
start
<=
end
)
lua_pushlstring
(
L
,
s
+
start
-
1
,
(
size_t
)(
end
-
start
)
+
1
);
else
lua_pushliteral
(
L
,
""
);
return
1
;
}
static
int
str_reverse
(
lua_State
*
L
)
{
size_t
l
,
i
;
luaL_Buffer
b
;
const
char
*
s
=
luaL_checklstring
(
L
,
1
,
&
l
);
char
*
p
=
luaL_buffinitsize
(
L
,
&
b
,
l
);
for
(
i
=
0
;
i
<
l
;
i
++
)
p
[
i
]
=
s
[
l
-
i
-
1
];
luaL_pushresultsize
(
&
b
,
l
);
return
1
;
}
static
int
str_lower
(
lua_State
*
L
)
{
size_t
l
;
size_t
i
;
luaL_Buffer
b
;
const
char
*
s
=
luaL_checklstring
(
L
,
1
,
&
l
);
char
*
p
=
luaL_buffinitsize
(
L
,
&
b
,
l
);
for
(
i
=
0
;
i
<
l
;
i
++
)
p
[
i
]
=
tolower
(
uchar
(
s
[
i
]));
luaL_pushresultsize
(
&
b
,
l
);
return
1
;
}
static
int
str_upper
(
lua_State
*
L
)
{
size_t
l
;
size_t
i
;
luaL_Buffer
b
;
const
char
*
s
=
luaL_checklstring
(
L
,
1
,
&
l
);
char
*
p
=
luaL_buffinitsize
(
L
,
&
b
,
l
);
for
(
i
=
0
;
i
<
l
;
i
++
)
p
[
i
]
=
toupper
(
uchar
(
s
[
i
]));
luaL_pushresultsize
(
&
b
,
l
);
return
1
;
}
static
int
str_rep
(
lua_State
*
L
)
{
size_t
l
,
lsep
;
const
char
*
s
=
luaL_checklstring
(
L
,
1
,
&
l
);
lua_Integer
n
=
luaL_checkinteger
(
L
,
2
);
const
char
*
sep
=
luaL_optlstring
(
L
,
3
,
""
,
&
lsep
);
if
(
n
<=
0
)
lua_pushliteral
(
L
,
""
);
else
if
(
l
+
lsep
<
l
||
l
+
lsep
>
MAXSIZE
/
n
)
/* may overflow? */
return
luaL_error
(
L
,
"resulting string too large"
);
else
{
size_t
totallen
=
(
size_t
)
n
*
l
+
(
size_t
)(
n
-
1
)
*
lsep
;
luaL_Buffer
b
;
char
*
p
=
luaL_buffinitsize
(
L
,
&
b
,
totallen
);
while
(
n
--
>
1
)
{
/* first n-1 copies (followed by separator) */
memcpy
(
p
,
s
,
l
*
sizeof
(
char
));
p
+=
l
;
if
(
lsep
>
0
)
{
/* empty 'memcpy' is not that cheap */
memcpy
(
p
,
sep
,
lsep
*
sizeof
(
char
));
p
+=
lsep
;
}
}
memcpy
(
p
,
s
,
l
*
sizeof
(
char
));
/* last copy (not followed by separator) */
luaL_pushresultsize
(
&
b
,
totallen
);
}
return
1
;
}
static
int
str_byte
(
lua_State
*
L
)
{
size_t
l
;
const
char
*
s
=
luaL_checklstring
(
L
,
1
,
&
l
);
lua_Integer
posi
=
posrelat
(
luaL_optinteger
(
L
,
2
,
1
),
l
);
lua_Integer
pose
=
posrelat
(
luaL_optinteger
(
L
,
3
,
posi
),
l
);
int
n
,
i
;
if
(
posi
<
1
)
posi
=
1
;
if
(
pose
>
(
lua_Integer
)
l
)
pose
=
l
;
if
(
posi
>
pose
)
return
0
;
/* empty interval; return no values */
if
(
pose
-
posi
>=
INT_MAX
)
/* arithmetic overflow? */
return
luaL_error
(
L
,
"string slice too long"
);
n
=
(
int
)(
pose
-
posi
)
+
1
;
luaL_checkstack
(
L
,
n
,
"string slice too long"
);
for
(
i
=
0
;
i
<
n
;
i
++
)
lua_pushinteger
(
L
,
uchar
(
s
[
posi
+
i
-
1
]));
return
n
;
}
static
int
str_char
(
lua_State
*
L
)
{
int
n
=
lua_gettop
(
L
);
/* number of arguments */
int
i
;
luaL_Buffer
b
;
char
*
p
=
luaL_buffinitsize
(
L
,
&
b
,
n
);
for
(
i
=
1
;
i
<=
n
;
i
++
)
{
lua_Integer
c
=
luaL_checkinteger
(
L
,
i
);
luaL_argcheck
(
L
,
uchar
(
c
)
==
c
,
i
,
"value out of range"
);
p
[
i
-
1
]
=
uchar
(
c
);
}
luaL_pushresultsize
(
&
b
,
n
);
return
1
;
}
static
int
writer
(
lua_State
*
L
,
const
void
*
b
,
size_t
size
,
void
*
B
)
{
(
void
)
L
;
luaL_addlstring
((
luaL_Buffer
*
)
B
,
(
const
char
*
)
b
,
size
);
return
0
;
}
static
int
str_dump
(
lua_State
*
L
)
{
luaL_Buffer
b
;
int
strip
,
tstrip
=
lua_type
(
L
,
2
);
if
(
tstrip
==
LUA_TBOOLEAN
)
{
strip
=
lua_toboolean
(
L
,
2
)
?
2
:
0
;
}
else
if
(
tstrip
==
LUA_TNONE
||
tstrip
==
LUA_TNIL
)
{
strip
=
-
1
;
/* This tells lua_dump to use the global strip default */
}
else
{
strip
=
lua_tointeger
(
L
,
2
);
luaL_argcheck
(
L
,
(
unsigned
)(
strip
)
<
3
,
2
,
"strip out of range"
);
}
luaL_checktype
(
L
,
1
,
LUA_TFUNCTION
);
lua_settop
(
L
,
1
);
luaL_buffinit
(
L
,
&
b
);
if
(
lua_dump
(
L
,
writer
,
&
b
,
strip
)
!=
0
)
return
luaL_error
(
L
,
"unable to dump given function"
);
luaL_pushresult
(
&
b
);
return
1
;
}
/*
** {======================================================
** PATTERN MATCHING
** =======================================================
*/
#define CAP_UNFINISHED (-1)
#define CAP_POSITION (-2)
typedef
struct
MatchState
{
const
char
*
src_init
;
/* init of source string */
const
char
*
src_end
;
/* end ('\0') of source string */
const
char
*
p_end
;
/* end ('\0') of pattern */
lua_State
*
L
;
int
matchdepth
;
/* control for recursive depth (to avoid C stack overflow) */
unsigned
char
level
;
/* total number of captures (finished or unfinished) */
struct
{
const
char
*
init
;
ptrdiff_t
len
;
}
capture
[
LUA_MAXCAPTURES
];
}
MatchState
;
/* recursive function */
static
const
char
*
match
(
MatchState
*
ms
,
const
char
*
s
,
const
char
*
p
);
/* maximum recursion depth for 'match' */
#if !defined(MAXCCALLS)
#define MAXCCALLS 200
#endif
#define L_ESC '%'
#define SPECIALS "^$*+?.([%-"
static
int
check_capture
(
MatchState
*
ms
,
int
l
)
{
l
-=
'1'
;
if
(
l
<
0
||
l
>=
ms
->
level
||
ms
->
capture
[
l
].
len
==
CAP_UNFINISHED
)
return
luaL_error
(
ms
->
L
,
"invalid capture index %%%d"
,
l
+
1
);
return
l
;
}
static
int
capture_to_close
(
MatchState
*
ms
)
{
int
level
=
ms
->
level
;
for
(
level
--
;
level
>=
0
;
level
--
)
if
(
ms
->
capture
[
level
].
len
==
CAP_UNFINISHED
)
return
level
;
return
luaL_error
(
ms
->
L
,
"invalid pattern capture"
);
}
static
const
char
*
classend
(
MatchState
*
ms
,
const
char
*
p
)
{
switch
(
*
p
++
)
{
case
L_ESC
:
{
if
(
p
==
ms
->
p_end
)
luaL_error
(
ms
->
L
,
"malformed pattern (ends with '%%')"
);
return
p
+
1
;
}
case
'['
:
{
if
(
*
p
==
'^'
)
p
++
;
do
{
/* look for a ']' */
if
(
p
==
ms
->
p_end
)
luaL_error
(
ms
->
L
,
"malformed pattern (missing ']')"
);
if
(
*
(
p
++
)
==
L_ESC
&&
p
<
ms
->
p_end
)
p
++
;
/* skip escapes (e.g. '%]') */
}
while
(
*
p
!=
']'
);
return
p
+
1
;
}
default:
{
return
p
;
}
}
}
static
int
match_class
(
int
c
,
int
cl
)
{
int
res
;
switch
(
tolower
(
cl
))
{
case
'a'
:
res
=
isalpha
(
c
);
break
;
case
'c'
:
res
=
iscntrl
(
c
);
break
;
case
'd'
:
res
=
isdigit
(
c
);
break
;
case
'g'
:
res
=
isgraph
(
c
);
break
;
case
'l'
:
res
=
islower
(
c
);
break
;
case
'p'
:
res
=
ispunct
(
c
);
break
;
case
's'
:
res
=
isspace
(
c
);
break
;
case
'u'
:
res
=
isupper
(
c
);
break
;
case
'w'
:
res
=
isalnum
(
c
);
break
;
case
'x'
:
res
=
isxdigit
(
c
);
break
;
case
'z'
:
res
=
(
c
==
0
);
break
;
/* deprecated option */
default:
return
(
cl
==
c
);
}
return
(
islower
(
cl
)
?
res
:
!
res
);
}
static
int
matchbracketclass
(
int
c
,
const
char
*
p
,
const
char
*
ec
)
{
int
sig
=
1
;
if
(
*
(
p
+
1
)
==
'^'
)
{
sig
=
0
;
p
++
;
/* skip the '^' */
}
while
(
++
p
<
ec
)
{
if
(
*
p
==
L_ESC
)
{
p
++
;
if
(
match_class
(
c
,
uchar
(
*
p
)))
return
sig
;
}
else
if
((
*
(
p
+
1
)
==
'-'
)
&&
(
p
+
2
<
ec
))
{
p
+=
2
;
if
(
uchar
(
*
(
p
-
2
))
<=
c
&&
c
<=
uchar
(
*
p
))
return
sig
;
}
else
if
(
uchar
(
*
p
)
==
c
)
return
sig
;
}
return
!
sig
;
}
static
int
singlematch
(
MatchState
*
ms
,
const
char
*
s
,
const
char
*
p
,
const
char
*
ep
)
{
if
(
s
>=
ms
->
src_end
)
return
0
;
else
{
int
c
=
uchar
(
*
s
);
switch
(
*
p
)
{
case
'.'
:
return
1
;
/* matches any char */
case
L_ESC
:
return
match_class
(
c
,
uchar
(
*
(
p
+
1
)));
case
'['
:
return
matchbracketclass
(
c
,
p
,
ep
-
1
);
default:
return
(
uchar
(
*
p
)
==
c
);
}
}
}
static
const
char
*
matchbalance
(
MatchState
*
ms
,
const
char
*
s
,
const
char
*
p
)
{
if
(
p
>=
ms
->
p_end
-
1
)
luaL_error
(
ms
->
L
,
"malformed pattern (missing arguments to '%%b')"
);
if
(
*
s
!=
*
p
)
return
NULL
;
else
{
int
b
=
*
p
;
int
e
=
*
(
p
+
1
);
int
cont
=
1
;
while
(
++
s
<
ms
->
src_end
)
{
if
(
*
s
==
e
)
{
if
(
--
cont
==
0
)
return
s
+
1
;
}
else
if
(
*
s
==
b
)
cont
++
;
}
}
return
NULL
;
/* string ends out of balance */
}
static
const
char
*
max_expand
(
MatchState
*
ms
,
const
char
*
s
,
const
char
*
p
,
const
char
*
ep
)
{
ptrdiff_t
i
=
0
;
/* counts maximum expand for item */
while
(
singlematch
(
ms
,
s
+
i
,
p
,
ep
))
i
++
;
/* keeps trying to match with the maximum repetitions */
while
(
i
>=
0
)
{
const
char
*
res
=
match
(
ms
,
(
s
+
i
),
ep
+
1
);
if
(
res
)
return
res
;
i
--
;
/* else didn't match; reduce 1 repetition to try again */
}
return
NULL
;
}
static
const
char
*
min_expand
(
MatchState
*
ms
,
const
char
*
s
,
const
char
*
p
,
const
char
*
ep
)
{
for
(;;)
{
const
char
*
res
=
match
(
ms
,
s
,
ep
+
1
);
if
(
res
!=
NULL
)
return
res
;
else
if
(
singlematch
(
ms
,
s
,
p
,
ep
))
s
++
;
/* try with one more repetition */
else
return
NULL
;
}
}
static
const
char
*
start_capture
(
MatchState
*
ms
,
const
char
*
s
,
const
char
*
p
,
int
what
)
{
const
char
*
res
;
int
level
=
ms
->
level
;
if
(
level
>=
LUA_MAXCAPTURES
)
luaL_error
(
ms
->
L
,
"too many captures"
);
ms
->
capture
[
level
].
init
=
s
;
ms
->
capture
[
level
].
len
=
what
;
ms
->
level
=
level
+
1
;
if
((
res
=
match
(
ms
,
s
,
p
))
==
NULL
)
/* match failed? */
ms
->
level
--
;
/* undo capture */
return
res
;
}
static
const
char
*
end_capture
(
MatchState
*
ms
,
const
char
*
s
,
const
char
*
p
)
{
int
l
=
capture_to_close
(
ms
);
const
char
*
res
;
ms
->
capture
[
l
].
len
=
s
-
ms
->
capture
[
l
].
init
;
/* close capture */
if
((
res
=
match
(
ms
,
s
,
p
))
==
NULL
)
/* match failed? */
ms
->
capture
[
l
].
len
=
CAP_UNFINISHED
;
/* undo capture */
return
res
;
}
static
const
char
*
match_capture
(
MatchState
*
ms
,
const
char
*
s
,
int
l
)
{
size_t
len
;
l
=
check_capture
(
ms
,
l
);
len
=
ms
->
capture
[
l
].
len
;
if
((
size_t
)(
ms
->
src_end
-
s
)
>=
len
&&
memcmp
(
ms
->
capture
[
l
].
init
,
s
,
len
)
==
0
)
return
s
+
len
;
else
return
NULL
;
}
static
const
char
*
match
(
MatchState
*
ms
,
const
char
*
s
,
const
char
*
p
)
{
if
(
ms
->
matchdepth
--
==
0
)
luaL_error
(
ms
->
L
,
"pattern too complex"
);
init:
/* using goto's to optimize tail recursion */
if
(
p
!=
ms
->
p_end
)
{
/* end of pattern? */
switch
(
*
p
)
{
case
'('
:
{
/* start capture */
if
(
*
(
p
+
1
)
==
')'
)
/* position capture? */
s
=
start_capture
(
ms
,
s
,
p
+
2
,
CAP_POSITION
);
else
s
=
start_capture
(
ms
,
s
,
p
+
1
,
CAP_UNFINISHED
);
break
;
}
case
')'
:
{
/* end capture */
s
=
end_capture
(
ms
,
s
,
p
+
1
);
break
;
}
case
'$'
:
{
if
((
p
+
1
)
!=
ms
->
p_end
)
/* is the '$' the last char in pattern? */
goto
dflt
;
/* no; go to default */
s
=
(
s
==
ms
->
src_end
)
?
s
:
NULL
;
/* check end of string */
break
;
}
case
L_ESC
:
{
/* escaped sequences not in the format class[*+?-]? */
switch
(
*
(
p
+
1
))
{
case
'b'
:
{
/* balanced string? */
s
=
matchbalance
(
ms
,
s
,
p
+
2
);
if
(
s
!=
NULL
)
{
p
+=
4
;
goto
init
;
/* return match(ms, s, p + 4); */
}
/* else fail (s == NULL) */
break
;
}
case
'f'
:
{
/* frontier? */
const
char
*
ep
;
char
previous
;
p
+=
2
;
if
(
*
p
!=
'['
)
luaL_error
(
ms
->
L
,
"missing '[' after '%%f' in pattern"
);
ep
=
classend
(
ms
,
p
);
/* points to what is next */
previous
=
(
s
==
ms
->
src_init
)
?
'\0'
:
*
(
s
-
1
);
if
(
!
matchbracketclass
(
uchar
(
previous
),
p
,
ep
-
1
)
&&
matchbracketclass
(
uchar
(
*
s
),
p
,
ep
-
1
))
{
p
=
ep
;
goto
init
;
/* return match(ms, s, ep); */
}
s
=
NULL
;
/* match failed */
break
;
}
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
{
/* capture results (%0-%9)? */
s
=
match_capture
(
ms
,
s
,
uchar
(
*
(
p
+
1
)));
if
(
s
!=
NULL
)
{
p
+=
2
;
goto
init
;
/* return match(ms, s, p + 2) */
}
break
;
}
default:
goto
dflt
;
}
break
;
}
default:
dflt:
{
/* pattern class plus optional suffix */
const
char
*
ep
=
classend
(
ms
,
p
);
/* points to optional suffix */
/* does not match at least once? */
if
(
!
singlematch
(
ms
,
s
,
p
,
ep
))
{
if
(
*
ep
==
'*'
||
*
ep
==
'?'
||
*
ep
==
'-'
)
{
/* accept empty? */
p
=
ep
+
1
;
goto
init
;
/* return match(ms, s, ep + 1); */
}
else
/* '+' or no suffix */
s
=
NULL
;
/* fail */
}
else
{
/* matched once */
switch
(
*
ep
)
{
/* handle optional suffix */
case
'?'
:
{
/* optional */
const
char
*
res
;
if
((
res
=
match
(
ms
,
s
+
1
,
ep
+
1
))
!=
NULL
)
s
=
res
;
else
{
p
=
ep
+
1
;
goto
init
;
/* else return match(ms, s, ep + 1); */
}
break
;
}
case
'+'
:
/* 1 or more repetitions */
s
++
;
/* 1 match already done */
/* FALLTHROUGH */
case
'*'
:
/* 0 or more repetitions */
s
=
max_expand
(
ms
,
s
,
p
,
ep
);
break
;
case
'-'
:
/* 0 or more repetitions (minimum) */
s
=
min_expand
(
ms
,
s
,
p
,
ep
);
break
;
default:
/* no suffix */
s
++
;
p
=
ep
;
goto
init
;
/* return match(ms, s + 1, ep); */
}
}
break
;
}
}
}
ms
->
matchdepth
++
;
return
s
;
}
static
const
char
*
lmemfind
(
const
char
*
s1
,
size_t
l1
,
const
char
*
s2
,
size_t
l2
)
{
if
(
l2
==
0
)
return
s1
;
/* empty strings are everywhere */
else
if
(
l2
>
l1
)
return
NULL
;
/* avoids a negative 'l1' */
else
{
const
char
*
init
;
/* to search for a '*s2' inside 's1' */
l2
--
;
/* 1st char will be checked by 'memchr' */
l1
=
l1
-
l2
;
/* 's2' cannot be found after that */
while
(
l1
>
0
&&
(
init
=
(
const
char
*
)
memchr
(
s1
,
*
s2
,
l1
))
!=
NULL
)
{
init
++
;
/* 1st char is already checked */
if
(
memcmp
(
init
,
s2
+
1
,
l2
)
==
0
)
return
init
-
1
;
else
{
/* correct 'l1' and 's1' to try again */
l1
-=
init
-
s1
;
s1
=
init
;
}
}
return
NULL
;
/* not found */
}
}
static
void
push_onecapture
(
MatchState
*
ms
,
int
i
,
const
char
*
s
,
const
char
*
e
)
{
if
(
i
>=
ms
->
level
)
{
if
(
i
==
0
)
/* ms->level == 0, too */
lua_pushlstring
(
ms
->
L
,
s
,
e
-
s
);
/* add whole match */
else
luaL_error
(
ms
->
L
,
"invalid capture index %%%d"
,
i
+
1
);
}
else
{
ptrdiff_t
l
=
ms
->
capture
[
i
].
len
;
if
(
l
==
CAP_UNFINISHED
)
luaL_error
(
ms
->
L
,
"unfinished capture"
);
if
(
l
==
CAP_POSITION
)
lua_pushinteger
(
ms
->
L
,
(
ms
->
capture
[
i
].
init
-
ms
->
src_init
)
+
1
);
else
lua_pushlstring
(
ms
->
L
,
ms
->
capture
[
i
].
init
,
l
);
}
}
static
int
push_captures
(
MatchState
*
ms
,
const
char
*
s
,
const
char
*
e
)
{
int
i
;
int
nlevels
=
(
ms
->
level
==
0
&&
s
)
?
1
:
ms
->
level
;
luaL_checkstack
(
ms
->
L
,
nlevels
,
"too many captures"
);
for
(
i
=
0
;
i
<
nlevels
;
i
++
)
push_onecapture
(
ms
,
i
,
s
,
e
);
return
nlevels
;
/* number of strings pushed */
}
/* check whether pattern has no special characters */
static
int
nospecials
(
const
char
*
p
,
size_t
l
)
{
size_t
upto
=
0
;
do
{
if
(
strpbrk
(
p
+
upto
,
SPECIALS
))
return
0
;
/* pattern has a special character */
upto
+=
strlen
(
p
+
upto
)
+
1
;
/* may have more after \0 */
}
while
(
upto
<=
l
);
return
1
;
/* no special chars found */
}
static
void
prepstate
(
MatchState
*
ms
,
lua_State
*
L
,
const
char
*
s
,
size_t
ls
,
const
char
*
p
,
size_t
lp
)
{
ms
->
L
=
L
;
ms
->
matchdepth
=
MAXCCALLS
;
ms
->
src_init
=
s
;
ms
->
src_end
=
s
+
ls
;
ms
->
p_end
=
p
+
lp
;
}
static
void
reprepstate
(
MatchState
*
ms
)
{
ms
->
level
=
0
;
lua_assert
(
ms
->
matchdepth
==
MAXCCALLS
);
}
static
int
str_find_aux
(
lua_State
*
L
,
int
find
)
{
size_t
ls
,
lp
;
const
char
*
s
=
luaL_checklstring
(
L
,
1
,
&
ls
);
const
char
*
p
=
luaL_checklstring
(
L
,
2
,
&
lp
);
lua_Integer
init
=
posrelat
(
luaL_optinteger
(
L
,
3
,
1
),
ls
);
if
(
init
<
1
)
init
=
1
;
else
if
(
init
>
(
lua_Integer
)
ls
+
1
)
{
/* start after string's end? */
lua_pushnil
(
L
);
/* cannot find anything */
return
1
;
}
/* explicit request or no special characters? */
if
(
find
&&
(
lua_toboolean
(
L
,
4
)
||
nospecials
(
p
,
lp
)))
{
/* do a plain search */
const
char
*
s2
=
lmemfind
(
s
+
init
-
1
,
ls
-
(
size_t
)
init
+
1
,
p
,
lp
);
if
(
s2
)
{
lua_pushinteger
(
L
,
(
s2
-
s
)
+
1
);
lua_pushinteger
(
L
,
(
s2
-
s
)
+
lp
);
return
2
;
}
}
else
{
MatchState
ms
;
const
char
*
s1
=
s
+
init
-
1
;
int
anchor
=
(
*
p
==
'^'
);
if
(
anchor
)
{
p
++
;
lp
--
;
/* skip anchor character */
}
prepstate
(
&
ms
,
L
,
s
,
ls
,
p
,
lp
);
do
{
const
char
*
res
;
reprepstate
(
&
ms
);
if
((
res
=
match
(
&
ms
,
s1
,
p
))
!=
NULL
)
{
if
(
find
)
{
lua_pushinteger
(
L
,
(
s1
-
s
)
+
1
);
/* start */
lua_pushinteger
(
L
,
res
-
s
);
/* end */
return
push_captures
(
&
ms
,
NULL
,
0
)
+
2
;
}
else
return
push_captures
(
&
ms
,
s1
,
res
);
}
}
while
(
s1
++
<
ms
.
src_end
&&
!
anchor
);
}
lua_pushnil
(
L
);
/* not found */
return
1
;
}
static
int
str_find
(
lua_State
*
L
)
{
return
str_find_aux
(
L
,
1
);
}
static
int
str_match
(
lua_State
*
L
)
{
return
str_find_aux
(
L
,
0
);
}
/* state for 'gmatch' */
typedef
struct
GMatchState
{
const
char
*
src
;
/* current position */
const
char
*
p
;
/* pattern */
const
char
*
lastmatch
;
/* end of last match */
MatchState
ms
;
/* match state */
}
GMatchState
;
static
int
gmatch_aux
(
lua_State
*
L
)
{
GMatchState
*
gm
=
(
GMatchState
*
)
lua_touserdata
(
L
,
lua_upvalueindex
(
3
));
const
char
*
src
;
gm
->
ms
.
L
=
L
;
for
(
src
=
gm
->
src
;
src
<=
gm
->
ms
.
src_end
;
src
++
)
{
const
char
*
e
;
reprepstate
(
&
gm
->
ms
);
if
((
e
=
match
(
&
gm
->
ms
,
src
,
gm
->
p
))
!=
NULL
&&
e
!=
gm
->
lastmatch
)
{
gm
->
src
=
gm
->
lastmatch
=
e
;
return
push_captures
(
&
gm
->
ms
,
src
,
e
);
}
}
return
0
;
/* not found */
}
static
int
gmatch
(
lua_State
*
L
)
{
size_t
ls
,
lp
;
const
char
*
s
=
luaL_checklstring
(
L
,
1
,
&
ls
);
const
char
*
p
=
luaL_checklstring
(
L
,
2
,
&
lp
);
GMatchState
*
gm
;
lua_settop
(
L
,
2
);
/* keep them on closure to avoid being collected */
gm
=
(
GMatchState
*
)
lua_newuserdata
(
L
,
sizeof
(
GMatchState
));
prepstate
(
&
gm
->
ms
,
L
,
s
,
ls
,
p
,
lp
);
gm
->
src
=
s
;
gm
->
p
=
p
;
gm
->
lastmatch
=
NULL
;
lua_pushcclosure
(
L
,
gmatch_aux
,
3
);
return
1
;
}
static
void
add_s
(
MatchState
*
ms
,
luaL_Buffer
*
b
,
const
char
*
s
,
const
char
*
e
)
{
size_t
l
,
i
;
lua_State
*
L
=
ms
->
L
;
const
char
*
news
=
lua_tolstring
(
L
,
3
,
&
l
);
for
(
i
=
0
;
i
<
l
;
i
++
)
{
if
(
news
[
i
]
!=
L_ESC
)
luaL_addchar
(
b
,
news
[
i
]);
else
{
i
++
;
/* skip ESC */
if
(
!
isdigit
(
uchar
(
news
[
i
])))
{
if
(
news
[
i
]
!=
L_ESC
)
luaL_error
(
L
,
"invalid use of '%c' in replacement string"
,
L_ESC
);
luaL_addchar
(
b
,
news
[
i
]);
}
else
if
(
news
[
i
]
==
'0'
)
luaL_addlstring
(
b
,
s
,
e
-
s
);
else
{
push_onecapture
(
ms
,
news
[
i
]
-
'1'
,
s
,
e
);
luaL_tolstring
(
L
,
-
1
,
NULL
);
/* if number, convert it to string */
lua_remove
(
L
,
-
2
);
/* remove original value */
luaL_addvalue
(
b
);
/* add capture to accumulated result */
}
}
}
}
static
void
add_value
(
MatchState
*
ms
,
luaL_Buffer
*
b
,
const
char
*
s
,
const
char
*
e
,
int
tr
)
{
lua_State
*
L
=
ms
->
L
;
switch
(
tr
)
{
case
LUA_TFUNCTION
:
{
int
n
;
lua_pushvalue
(
L
,
3
);
n
=
push_captures
(
ms
,
s
,
e
);
lua_call
(
L
,
n
,
1
);
break
;
}
case
LUA_TTABLE
:
{
push_onecapture
(
ms
,
0
,
s
,
e
);
lua_gettable
(
L
,
3
);
break
;
}
default:
{
/* LUA_TNUMBER or LUA_TSTRING */
add_s
(
ms
,
b
,
s
,
e
);
return
;
}
}
if
(
!
lua_toboolean
(
L
,
-
1
))
{
/* nil or false? */
lua_pop
(
L
,
1
);
lua_pushlstring
(
L
,
s
,
e
-
s
);
/* keep original text */
}
else
if
(
!
lua_isstring
(
L
,
-
1
))
luaL_error
(
L
,
"invalid replacement value (a %s)"
,
luaL_typename
(
L
,
-
1
));
luaL_addvalue
(
b
);
/* add result to accumulator */
}
static
int
str_gsub
(
lua_State
*
L
)
{
size_t
srcl
,
lp
;
const
char
*
src
=
luaL_checklstring
(
L
,
1
,
&
srcl
);
/* subject */
const
char
*
p
=
luaL_checklstring
(
L
,
2
,
&
lp
);
/* pattern */
const
char
*
lastmatch
=
NULL
;
/* end of last match */
int
tr
=
lua_type
(
L
,
3
);
/* replacement type */
lua_Integer
max_s
=
luaL_optinteger
(
L
,
4
,
srcl
+
1
);
/* max replacements */
int
anchor
=
(
*
p
==
'^'
);
lua_Integer
n
=
0
;
/* replacement count */
MatchState
ms
;
luaL_Buffer
b
;
luaL_argcheck
(
L
,
tr
==
LUA_TNUMBER
||
tr
==
LUA_TSTRING
||
tr
==
LUA_TFUNCTION
||
tr
==
LUA_TTABLE
,
3
,
"string/function/table expected"
);
luaL_buffinit
(
L
,
&
b
);
if
(
anchor
)
{
p
++
;
lp
--
;
/* skip anchor character */
}
prepstate
(
&
ms
,
L
,
src
,
srcl
,
p
,
lp
);
while
(
n
<
max_s
)
{
const
char
*
e
;
reprepstate
(
&
ms
);
/* (re)prepare state for new match */
if
((
e
=
match
(
&
ms
,
src
,
p
))
!=
NULL
&&
e
!=
lastmatch
)
{
/* match? */
n
++
;
add_value
(
&
ms
,
&
b
,
src
,
e
,
tr
);
/* add replacement to buffer */
src
=
lastmatch
=
e
;
}
else
if
(
src
<
ms
.
src_end
)
/* otherwise, skip one character */
luaL_addchar
(
&
b
,
*
src
++
);
else
break
;
/* end of subject */
if
(
anchor
)
break
;
}
luaL_addlstring
(
&
b
,
src
,
ms
.
src_end
-
src
);
luaL_pushresult
(
&
b
);
lua_pushinteger
(
L
,
n
);
/* number of substitutions */
return
2
;
}
/* }====================================================== */
/*
** {======================================================
** STRING FORMAT
** =======================================================
*/
#if !defined(lua_number2strx)
/* { */
/*
** Hexadecimal floating-point formatter
*/
#include <math.h>
#define SIZELENMOD (sizeof(LUA_NUMBER_FRMLEN)/sizeof(char))
/*
** Number of bits that goes into the first digit. It can be any value
** between 1 and 4; the following definition tries to align the number
** to nibble boundaries by making what is left after that first digit a
** multiple of 4.
*/
#define L_NBFD ((l_mathlim(MANT_DIG) - 1) % 4 + 1)
/*
** Add integer part of 'x' to buffer and return new 'x'
*/
static
lua_Number
adddigit
(
char
*
buff
,
int
n
,
lua_Number
x
)
{
lua_Number
dd
=
l_mathop
(
floor
)(
x
);
/* get integer part from 'x' */
int
d
=
(
int
)
dd
;
buff
[
n
]
=
(
d
<
10
?
d
+
'0'
:
d
-
10
+
'a'
);
/* add to buffer */
return
x
-
dd
;
/* return what is left */
}
static
int
num2straux
(
char
*
buff
,
int
sz
,
lua_Number
x
)
{
/* if 'inf' or 'NaN', format it like '%g' */
if
(
x
!=
x
||
x
==
(
lua_Number
)
HUGE_VAL
||
x
==
-
(
lua_Number
)
HUGE_VAL
)
return
l_sprintf
(
buff
,
sz
,
LUA_NUMBER_FMT
,
(
LUAI_UACNUMBER
)
x
);
else
if
(
x
==
0
)
{
/* can be -0... */
/* create "0" or "-0" followed by exponent */
return
l_sprintf
(
buff
,
sz
,
LUA_NUMBER_FMT
"x0p+0"
,
(
LUAI_UACNUMBER
)
x
);
}
else
{
int
e
;
lua_Number
m
=
l_mathop
(
frexp
)(
x
,
&
e
);
/* 'x' fraction and exponent */
int
n
=
0
;
/* character count */
if
(
m
<
0
)
{
/* is number negative? */
buff
[
n
++
]
=
'-'
;
/* add signal */
m
=
-
m
;
/* make it positive */
}
buff
[
n
++
]
=
'0'
;
buff
[
n
++
]
=
'x'
;
/* add "0x" */
m
=
adddigit
(
buff
,
n
++
,
m
*
(
1
<<
L_NBFD
));
/* add first digit */
e
-=
L_NBFD
;
/* this digit goes before the radix point */
if
(
m
>
0
)
{
/* more digits? */
buff
[
n
++
]
=
lua_getlocaledecpoint
();
/* add radix point */
do
{
/* add as many digits as needed */
m
=
adddigit
(
buff
,
n
++
,
m
*
16
);
}
while
(
m
>
0
);
}
n
+=
l_sprintf
(
buff
+
n
,
sz
-
n
,
"p%+d"
,
e
);
/* add exponent */
lua_assert
(
n
<
sz
);
return
n
;
}
}
static
int
lua_number2strx
(
lua_State
*
L
,
char
*
buff
,
int
sz
,
const
char
*
fmt
,
lua_Number
x
)
{
int
n
=
num2straux
(
buff
,
sz
,
x
);
if
(
fmt
[
SIZELENMOD
]
==
'A'
)
{
int
i
;
for
(
i
=
0
;
i
<
n
;
i
++
)
buff
[
i
]
=
toupper
(
uchar
(
buff
[
i
]));
}
else
if
(
fmt
[
SIZELENMOD
]
!=
'a'
)
return
luaL_error
(
L
,
"modifiers for format '%%a'/'%%A' not implemented"
);
return
n
;
}
#endif
/* } */
/*
** Maximum size of each formatted item. Unlike standard Lua which is
** based on the maximum size is produceD by format('%.99f', -maxfloat),
** NodeMCU just limits this to 128.
*/
#define MAX_ITEM 128
/* valid flags in a format specification */
#define FLAGS "-+ #0"
/*
** maximum size of each format specification (such as "%-099.99d")
*/
#define MAX_FORMAT 32
static
void
addquoted
(
luaL_Buffer
*
b
,
const
char
*
s
,
size_t
len
)
{
luaL_addchar
(
b
,
'"'
);
while
(
len
--
)
{
if
(
*
s
==
'"'
||
*
s
==
'\\'
||
*
s
==
'\n'
)
{
luaL_addchar
(
b
,
'\\'
);
luaL_addchar
(
b
,
*
s
);
}
else
if
(
iscntrl
(
uchar
(
*
s
)))
{
char
buff
[
10
];
if
(
!
isdigit
(
uchar
(
*
(
s
+
1
))))
l_sprintf
(
buff
,
sizeof
(
buff
),
"
\\
%d"
,
(
int
)
uchar
(
*
s
));
else
l_sprintf
(
buff
,
sizeof
(
buff
),
"
\\
%03d"
,
(
int
)
uchar
(
*
s
));
luaL_addstring
(
b
,
buff
);
}
else
luaL_addchar
(
b
,
*
s
);
s
++
;
}
luaL_addchar
(
b
,
'"'
);
}
/*
** Ensures the 'buff' string uses a dot as the radix character.
*/
static
void
checkdp
(
char
*
buff
,
int
nb
)
{
if
(
memchr
(
buff
,
'.'
,
nb
)
==
NULL
)
{
/* no dot? */
char
point
=
lua_getlocaledecpoint
();
/* try locale point */
char
*
ppoint
=
(
char
*
)
memchr
(
buff
,
point
,
nb
);
if
(
ppoint
)
*
ppoint
=
'.'
;
/* change it to a dot */
}
}
static
void
addliteral
(
lua_State
*
L
,
luaL_Buffer
*
b
,
int
arg
)
{
switch
(
lua_type
(
L
,
arg
))
{
case
LUA_TSTRING
:
{
size_t
len
;
const
char
*
s
=
lua_tolstring
(
L
,
arg
,
&
len
);
addquoted
(
b
,
s
,
len
);
break
;
}
case
LUA_TNUMBER
:
{
char
*
buff
=
luaL_prepbuffsize
(
b
,
MAX_ITEM
);
int
nb
;
if
(
!
lua_isinteger
(
L
,
arg
))
{
/* float? */
lua_Number
n
=
lua_tonumber
(
L
,
arg
);
/* write as hexa ('%a') */
nb
=
lua_number2strx
(
L
,
buff
,
MAX_ITEM
,
"%"
LUA_NUMBER_FRMLEN
"a"
,
n
);
checkdp
(
buff
,
nb
);
/* ensure it uses a dot */
}
else
{
/* integers */
lua_Integer
n
=
lua_tointeger
(
L
,
arg
);
const
char
*
format
=
(
n
==
LUA_MININTEGER
)
/* corner case? */
?
"0x%"
LUA_INTEGER_FRMLEN
"x"
/* use hexa */
:
LUA_INTEGER_FMT
;
/* else use default format */
nb
=
l_sprintf
(
buff
,
MAX_ITEM
,
format
,
(
LUAI_UACINT
)
n
);
}
luaL_addsize
(
b
,
nb
);
break
;
}
case
LUA_TNIL
:
case
LUA_TBOOLEAN
:
{
luaL_tolstring
(
L
,
arg
,
NULL
);
luaL_addvalue
(
b
);
break
;
}
default:
{
luaL_argerror
(
L
,
arg
,
"value has no literal form"
);
}
}
}
static
const
char
*
scanformat
(
lua_State
*
L
,
const
char
*
strfrmt
,
char
*
form
)
{
const
char
*
p
=
strfrmt
;
while
(
*
p
!=
'\0'
&&
strchr
(
FLAGS
,
*
p
)
!=
NULL
)
p
++
;
/* skip flags */
if
((
size_t
)(
p
-
strfrmt
)
>=
sizeof
(
FLAGS
)
/
sizeof
(
char
))
luaL_error
(
L
,
"invalid format (repeated flags)"
);
if
(
isdigit
(
uchar
(
*
p
)))
p
++
;
/* skip width */
if
(
isdigit
(
uchar
(
*
p
)))
p
++
;
/* (2 digits at most) */
if
(
*
p
==
'.'
)
{
p
++
;
if
(
isdigit
(
uchar
(
*
p
)))
p
++
;
/* skip precision */
if
(
isdigit
(
uchar
(
*
p
)))
p
++
;
/* (2 digits at most) */
}
if
(
isdigit
(
uchar
(
*
p
)))
luaL_error
(
L
,
"invalid format (width or precision too long)"
);
*
(
form
++
)
=
'%'
;
memcpy
(
form
,
strfrmt
,
((
p
-
strfrmt
)
+
1
)
*
sizeof
(
char
));
form
+=
(
p
-
strfrmt
)
+
1
;
*
form
=
'\0'
;
return
p
;
}
/*
** add length modifier into formats
*/
static
void
addlenmod
(
char
*
form
,
const
char
*
lenmod
)
{
size_t
l
=
strlen
(
form
);
size_t
lm
=
strlen
(
lenmod
);
char
spec
=
form
[
l
-
1
];
strcpy
(
form
+
l
-
1
,
lenmod
);
form
[
l
+
lm
-
1
]
=
spec
;
form
[
l
+
lm
]
=
'\0'
;
}
static
int
str_format
(
lua_State
*
L
)
{
int
top
=
lua_gettop
(
L
);
int
arg
=
1
;
size_t
sfl
;
const
char
*
strfrmt
=
luaL_checklstring
(
L
,
arg
,
&
sfl
);
const
char
*
strfrmt_end
=
strfrmt
+
sfl
;
luaL_Buffer
b
;
luaL_buffinit
(
L
,
&
b
);
while
(
strfrmt
<
strfrmt_end
)
{
if
(
*
strfrmt
!=
L_ESC
)
luaL_addchar
(
&
b
,
*
strfrmt
++
);
else
if
(
*++
strfrmt
==
L_ESC
)
luaL_addchar
(
&
b
,
*
strfrmt
++
);
/* %% */
else
{
/* format item */
char
form
[
MAX_FORMAT
];
/* to store the format ('%...') */
char
*
buff
=
luaL_prepbuffsize
(
&
b
,
MAX_ITEM
);
/* to put formatted item */
int
nb
=
0
;
/* number of bytes in added item */
if
(
++
arg
>
top
)
luaL_argerror
(
L
,
arg
,
"no value"
);
strfrmt
=
scanformat
(
L
,
strfrmt
,
form
);
switch
(
*
strfrmt
++
)
{
case
'c'
:
{
nb
=
l_sprintf
(
buff
,
MAX_ITEM
,
form
,
(
int
)
luaL_checkinteger
(
L
,
arg
));
break
;
}
case
'd'
:
case
'i'
:
case
'o'
:
case
'u'
:
case
'x'
:
case
'X'
:
{
lua_Integer
n
=
luaL_checkinteger
(
L
,
arg
);
addlenmod
(
form
,
LUA_INTEGER_FRMLEN
);
nb
=
l_sprintf
(
buff
,
MAX_ITEM
,
form
,
(
LUAI_UACINT
)
n
);
break
;
}
case
'a'
:
case
'A'
:
addlenmod
(
form
,
LUA_NUMBER_FRMLEN
);
nb
=
lua_number2strx
(
L
,
buff
,
MAX_ITEM
,
form
,
luaL_checknumber
(
L
,
arg
));
break
;
case
'e'
:
case
'E'
:
case
'f'
:
case
'g'
:
case
'G'
:
{
lua_Number
n
=
luaL_checknumber
(
L
,
arg
);
addlenmod
(
form
,
LUA_NUMBER_FRMLEN
);
nb
=
l_sprintf
(
buff
,
MAX_ITEM
,
form
,
(
LUAI_UACNUMBER
)
n
);
break
;
}
case
'q'
:
{
addliteral
(
L
,
&
b
,
arg
);
break
;
}
case
's'
:
{
size_t
l
;
const
char
*
s
=
luaL_tolstring
(
L
,
arg
,
&
l
);
if
(
form
[
2
]
==
'\0'
)
/* no modifiers? */
luaL_addvalue
(
&
b
);
/* keep entire string */
else
{
luaL_argcheck
(
L
,
l
==
strlen
(
s
),
arg
,
"string contains zeros"
);
if
(
!
strchr
(
form
,
'.'
)
&&
l
>=
100
)
{
/* no precision and string is too long to be formatted */
luaL_addvalue
(
&
b
);
/* keep entire string */
}
else
{
/* format the string into 'buff' */
nb
=
l_sprintf
(
buff
,
MAX_ITEM
,
form
,
s
);
lua_pop
(
L
,
1
);
/* remove result from 'luaL_tolstring' */
}
}
break
;
}
default:
{
/* also treat cases 'pnLlh' */
return
luaL_error
(
L
,
"invalid option '%%%c' to 'format'"
,
*
(
strfrmt
-
1
));
}
}
lua_assert
(
nb
<
MAX_ITEM
);
luaL_addsize
(
&
b
,
nb
);
}
}
luaL_pushresult
(
&
b
);
return
1
;
}
static
int
str_format2
(
lua_State
*
L
)
{
if
(
lua_type
(
L
,
2
)
==
LUA_TTABLE
)
{
int
i
,
n
=
lua_rawlen
(
L
,
2
);
lua_settop
(
L
,
2
);
for
(
i
=
1
;
i
<=
n
;
i
++
)
lua_rawgeti
(
L
,
2
,
i
);
lua_remove
(
L
,
2
);
}
return
str_format
(
L
);
}
/* }====================================================== */
/*
** {======================================================
** PACK/UNPACK
** =======================================================
*/
/* value used for padding */
#if !defined(LUAL_PACKPADBYTE)
#define LUAL_PACKPADBYTE 0x00
#endif
/* maximum size for the binary representation of an integer */
#define MAXINTSIZE 16
/* number of bits in a character */
#define NB CHAR_BIT
/* mask for one character (NB 1's) */
#define MC ((1 << NB) - 1)
/* size of a lua_Integer */
#define SZINT ((int)sizeof(lua_Integer))
/* dummy union to get native endianness */
static
const
union
{
int
dummy
;
char
little
;
/* true iff machine is little endian */
}
nativeendian
=
{
1
};
/* dummy structure to get native alignment requirements */
struct
cD
{
char
c
;
union
{
double
d
;
void
*
p
;
lua_Integer
i
;
lua_Number
n
;
}
u
;
};
#define MAXALIGN (offsetof(struct cD, u))
/*
** Union for serializing floats
*/
typedef
union
Ftypes
{
float
f
;
double
d
;
lua_Number
n
;
char
buff
[
5
*
sizeof
(
lua_Number
)];
/* enough for any float type */
}
Ftypes
;
/*
** information to pack/unpack stuff
*/
typedef
struct
Header
{
lua_State
*
L
;
int
islittle
;
int
maxalign
;
}
Header
;
/*
** options for pack/unpack
*/
typedef
enum
KOption
{
Kint
,
/* signed integers */
Kuint
,
/* unsigned integers */
Kfloat
,
/* floating-point numbers */
Kchar
,
/* fixed-length strings */
Kstring
,
/* strings with prefixed length */
Kzstr
,
/* zero-terminated strings */
Kpadding
,
/* padding */
Kpaddalign
,
/* padding for alignment */
Knop
/* no-op (configuration or spaces) */
}
KOption
;
/*
** Read an integer numeral from string 'fmt' or return 'df' if
** there is no numeral
*/
static
int
digit
(
int
c
)
{
return
'0'
<=
c
&&
c
<=
'9'
;
}
static
int
getnum
(
const
char
**
fmt
,
int
df
)
{
if
(
!
digit
(
**
fmt
))
/* no number? */
return
df
;
/* return default value */
else
{
int
a
=
0
;
do
{
a
=
a
*
10
+
(
*
((
*
fmt
)
++
)
-
'0'
);
}
while
(
digit
(
**
fmt
)
&&
a
<=
((
int
)
MAXSIZE
-
9
)
/
10
);
return
a
;
}
}
/*
** Read an integer numeral and raises an error if it is larger
** than the maximum size for integers.
*/
static
int
getnumlimit
(
Header
*
h
,
const
char
**
fmt
,
int
df
)
{
int
sz
=
getnum
(
fmt
,
df
);
if
(
sz
>
MAXINTSIZE
||
sz
<=
0
)
return
luaL_error
(
h
->
L
,
"integral size (%d) out of limits [1,%d]"
,
sz
,
MAXINTSIZE
);
return
sz
;
}
/*
** Initialize Header
*/
static
void
initheader
(
lua_State
*
L
,
Header
*
h
)
{
h
->
L
=
L
;
h
->
islittle
=
nativeendian
.
little
;
h
->
maxalign
=
1
;
}
/*
** Read and classify next option. 'size' is filled with option's size.
*/
static
KOption
getoption
(
Header
*
h
,
const
char
**
fmt
,
int
*
size
)
{
int
opt
=
*
((
*
fmt
)
++
);
*
size
=
0
;
/* default */
switch
(
opt
)
{
case
'b'
:
*
size
=
sizeof
(
char
);
return
Kint
;
case
'B'
:
*
size
=
sizeof
(
char
);
return
Kuint
;
case
'h'
:
*
size
=
sizeof
(
short
);
return
Kint
;
case
'H'
:
*
size
=
sizeof
(
short
);
return
Kuint
;
case
'l'
:
*
size
=
sizeof
(
long
);
return
Kint
;
case
'L'
:
*
size
=
sizeof
(
long
);
return
Kuint
;
case
'j'
:
*
size
=
sizeof
(
lua_Integer
);
return
Kint
;
case
'J'
:
*
size
=
sizeof
(
lua_Integer
);
return
Kuint
;
case
'T'
:
*
size
=
sizeof
(
size_t
);
return
Kuint
;
case
'f'
:
*
size
=
sizeof
(
float
);
return
Kfloat
;
case
'd'
:
*
size
=
sizeof
(
double
);
return
Kfloat
;
case
'n'
:
*
size
=
sizeof
(
lua_Number
);
return
Kfloat
;
case
'i'
:
*
size
=
getnumlimit
(
h
,
fmt
,
sizeof
(
int
));
return
Kint
;
case
'I'
:
*
size
=
getnumlimit
(
h
,
fmt
,
sizeof
(
int
));
return
Kuint
;
case
's'
:
*
size
=
getnumlimit
(
h
,
fmt
,
sizeof
(
size_t
));
return
Kstring
;
case
'c'
:
*
size
=
getnum
(
fmt
,
-
1
);
if
(
*
size
==
-
1
)
luaL_error
(
h
->
L
,
"missing size for format option 'c'"
);
return
Kchar
;
case
'z'
:
return
Kzstr
;
case
'x'
:
*
size
=
1
;
return
Kpadding
;
case
'X'
:
return
Kpaddalign
;
case
' '
:
break
;
case
'<'
:
h
->
islittle
=
1
;
break
;
case
'>'
:
h
->
islittle
=
0
;
break
;
case
'='
:
h
->
islittle
=
nativeendian
.
little
;
break
;
case
'!'
:
h
->
maxalign
=
getnumlimit
(
h
,
fmt
,
MAXALIGN
);
break
;
default:
luaL_error
(
h
->
L
,
"invalid format option '%c'"
,
opt
);
}
return
Knop
;
}
/*
** Read, classify, and fill other details about the next option.
** 'psize' is filled with option's size, 'notoalign' with its
** alignment requirements.
** Local variable 'size' gets the size to be aligned. (Kpadal option
** always gets its full alignment, other options are limited by
** the maximum alignment ('maxalign'). Kchar option needs no alignment
** despite its size.
*/
static
KOption
getdetails
(
Header
*
h
,
size_t
totalsize
,
const
char
**
fmt
,
int
*
psize
,
int
*
ntoalign
)
{
KOption
opt
=
getoption
(
h
,
fmt
,
psize
);
int
align
=
*
psize
;
/* usually, alignment follows size */
if
(
opt
==
Kpaddalign
)
{
/* 'X' gets alignment from following option */
if
(
**
fmt
==
'\0'
||
getoption
(
h
,
fmt
,
&
align
)
==
Kchar
||
align
==
0
)
luaL_argerror
(
h
->
L
,
1
,
"invalid next option for option 'X'"
);
}
if
(
align
<=
1
||
opt
==
Kchar
)
/* need no alignment? */
*
ntoalign
=
0
;
else
{
if
(
align
>
h
->
maxalign
)
/* enforce maximum alignment */
align
=
h
->
maxalign
;
if
((
align
&
(
align
-
1
))
!=
0
)
/* is 'align' not a power of 2? */
luaL_argerror
(
h
->
L
,
1
,
"format asks for alignment not power of 2"
);
*
ntoalign
=
(
align
-
(
int
)(
totalsize
&
(
align
-
1
)))
&
(
align
-
1
);
}
return
opt
;
}
/*
** Pack integer 'n' with 'size' bytes and 'islittle' endianness.
** The final 'if' handles the case when 'size' is larger than
** the size of a Lua integer, correcting the extra sign-extension
** bytes if necessary (by default they would be zeros).
*/
static
void
packint
(
luaL_Buffer
*
b
,
lua_Unsigned
n
,
int
islittle
,
int
size
,
int
neg
)
{
char
*
buff
=
luaL_prepbuffsize
(
b
,
size
);
int
i
;
buff
[
islittle
?
0
:
size
-
1
]
=
(
char
)(
n
&
MC
);
/* first byte */
for
(
i
=
1
;
i
<
size
;
i
++
)
{
n
>>=
NB
;
buff
[
islittle
?
i
:
size
-
1
-
i
]
=
(
char
)(
n
&
MC
);
}
if
(
neg
&&
size
>
SZINT
)
{
/* negative number need sign extension? */
for
(
i
=
SZINT
;
i
<
size
;
i
++
)
/* correct extra bytes */
buff
[
islittle
?
i
:
size
-
1
-
i
]
=
(
char
)
MC
;
}
luaL_addsize
(
b
,
size
);
/* add result to buffer */
}
/*
** Copy 'size' bytes from 'src' to 'dest', correcting endianness if
** given 'islittle' is different from native endianness.
*/
static
void
copywithendian
(
volatile
char
*
dest
,
volatile
const
char
*
src
,
int
size
,
int
islittle
)
{
if
(
islittle
==
nativeendian
.
little
)
{
while
(
size
--
!=
0
)
*
(
dest
++
)
=
*
(
src
++
);
}
else
{
dest
+=
size
-
1
;
while
(
size
--
!=
0
)
*
(
dest
--
)
=
*
(
src
++
);
}
}
static
int
str_pack
(
lua_State
*
L
)
{
luaL_Buffer
b
;
Header
h
;
const
char
*
fmt
=
luaL_checkstring
(
L
,
1
);
/* format string */
int
arg
=
1
;
/* current argument to pack */
size_t
totalsize
=
0
;
/* accumulate total size of result */
initheader
(
L
,
&
h
);
lua_pushnil
(
L
);
/* mark to separate arguments from string buffer */
luaL_buffinit
(
L
,
&
b
);
while
(
*
fmt
!=
'\0'
)
{
int
size
,
ntoalign
;
KOption
opt
=
getdetails
(
&
h
,
totalsize
,
&
fmt
,
&
size
,
&
ntoalign
);
totalsize
+=
ntoalign
+
size
;
while
(
ntoalign
--
>
0
)
luaL_addchar
(
&
b
,
LUAL_PACKPADBYTE
);
/* fill alignment */
arg
++
;
switch
(
opt
)
{
case
Kint
:
{
/* signed integers */
lua_Integer
n
=
luaL_checkinteger
(
L
,
arg
);
if
(
size
<
SZINT
)
{
/* need overflow check? */
lua_Integer
lim
=
(
lua_Integer
)
1
<<
((
size
*
NB
)
-
1
);
luaL_argcheck
(
L
,
-
lim
<=
n
&&
n
<
lim
,
arg
,
"integer overflow"
);
}
packint
(
&
b
,
(
lua_Unsigned
)
n
,
h
.
islittle
,
size
,
(
n
<
0
));
break
;
}
case
Kuint
:
{
/* unsigned integers */
lua_Integer
n
=
luaL_checkinteger
(
L
,
arg
);
if
(
size
<
SZINT
)
/* need overflow check? */
luaL_argcheck
(
L
,
(
lua_Unsigned
)
n
<
((
lua_Unsigned
)
1
<<
(
size
*
NB
)),
arg
,
"unsigned overflow"
);
packint
(
&
b
,
(
lua_Unsigned
)
n
,
h
.
islittle
,
size
,
0
);
break
;
}
case
Kfloat
:
{
/* floating-point options */
volatile
Ftypes
u
;
char
*
buff
=
luaL_prepbuffsize
(
&
b
,
size
);
lua_Number
n
=
luaL_checknumber
(
L
,
arg
);
/* get argument */
if
(
size
==
sizeof
(
u
.
f
))
u
.
f
=
(
float
)
n
;
/* copy it into 'u' */
else
if
(
size
==
sizeof
(
u
.
d
))
u
.
d
=
(
double
)
n
;
else
u
.
n
=
n
;
/* move 'u' to final result, correcting endianness if needed */
copywithendian
(
buff
,
u
.
buff
,
size
,
h
.
islittle
);
luaL_addsize
(
&
b
,
size
);
break
;
}
case
Kchar
:
{
/* fixed-size string */
size_t
len
;
const
char
*
s
=
luaL_checklstring
(
L
,
arg
,
&
len
);
luaL_argcheck
(
L
,
len
<=
(
size_t
)
size
,
arg
,
"string longer than given size"
);
luaL_addlstring
(
&
b
,
s
,
len
);
/* add string */
while
(
len
++
<
(
size_t
)
size
)
/* pad extra space */
luaL_addchar
(
&
b
,
LUAL_PACKPADBYTE
);
break
;
}
case
Kstring
:
{
/* strings with length count */
size_t
len
;
const
char
*
s
=
luaL_checklstring
(
L
,
arg
,
&
len
);
luaL_argcheck
(
L
,
size
>=
(
int
)
sizeof
(
size_t
)
||
len
<
((
size_t
)
1
<<
(
size
*
NB
)),
arg
,
"string length does not fit in given size"
);
packint
(
&
b
,
(
lua_Unsigned
)
len
,
h
.
islittle
,
size
,
0
);
/* pack length */
luaL_addlstring
(
&
b
,
s
,
len
);
totalsize
+=
len
;
break
;
}
case
Kzstr
:
{
/* zero-terminated string */
size_t
len
;
const
char
*
s
=
luaL_checklstring
(
L
,
arg
,
&
len
);
luaL_argcheck
(
L
,
strlen
(
s
)
==
len
,
arg
,
"string contains zeros"
);
luaL_addlstring
(
&
b
,
s
,
len
);
luaL_addchar
(
&
b
,
'\0'
);
/* add zero at the end */
totalsize
+=
len
+
1
;
break
;
}
case
Kpadding
:
luaL_addchar
(
&
b
,
LUAL_PACKPADBYTE
);
/* FALLTHROUGH */
case
Kpaddalign
:
case
Knop
:
arg
--
;
/* undo increment */
break
;
}
}
luaL_pushresult
(
&
b
);
return
1
;
}
static
int
str_packsize
(
lua_State
*
L
)
{
Header
h
;
const
char
*
fmt
=
luaL_checkstring
(
L
,
1
);
/* format string */
size_t
totalsize
=
0
;
/* accumulate total size of result */
initheader
(
L
,
&
h
);
while
(
*
fmt
!=
'\0'
)
{
int
size
,
ntoalign
;
KOption
opt
=
getdetails
(
&
h
,
totalsize
,
&
fmt
,
&
size
,
&
ntoalign
);
size
+=
ntoalign
;
/* total space used by option */
luaL_argcheck
(
L
,
totalsize
<=
MAXSIZE
-
size
,
1
,
"format result too large"
);
totalsize
+=
size
;
switch
(
opt
)
{
case
Kstring
:
/* strings with length count */
case
Kzstr
:
/* zero-terminated string */
luaL_argerror
(
L
,
1
,
"variable-length format"
);
/* call never return, but to avoid warnings: *//* FALLTHROUGH */
default:
break
;
}
}
lua_pushinteger
(
L
,
(
lua_Integer
)
totalsize
);
return
1
;
}
/*
** Unpack an integer with 'size' bytes and 'islittle' endianness.
** If size is smaller than the size of a Lua integer and integer
** is signed, must do sign extension (propagating the sign to the
** higher bits); if size is larger than the size of a Lua integer,
** it must check the unread bytes to see whether they do not cause an
** overflow.
*/
static
lua_Integer
unpackint
(
lua_State
*
L
,
const
char
*
str
,
int
islittle
,
int
size
,
int
issigned
)
{
lua_Unsigned
res
=
0
;
int
i
;
int
limit
=
(
size
<=
SZINT
)
?
size
:
SZINT
;
for
(
i
=
limit
-
1
;
i
>=
0
;
i
--
)
{
res
<<=
NB
;
res
|=
(
lua_Unsigned
)(
unsigned
char
)
str
[
islittle
?
i
:
size
-
1
-
i
];
}
if
(
size
<
SZINT
)
{
/* real size smaller than lua_Integer? */
if
(
issigned
)
{
/* needs sign extension? */
lua_Unsigned
mask
=
(
lua_Unsigned
)
1
<<
(
size
*
NB
-
1
);
res
=
((
res
^
mask
)
-
mask
);
/* do sign extension */
}
}
else
if
(
size
>
SZINT
)
{
/* must check unread bytes */
int
mask
=
(
!
issigned
||
(
lua_Integer
)
res
>=
0
)
?
0
:
MC
;
for
(
i
=
limit
;
i
<
size
;
i
++
)
{
if
((
unsigned
char
)
str
[
islittle
?
i
:
size
-
1
-
i
]
!=
mask
)
luaL_error
(
L
,
"%d-byte integer does not fit into Lua Integer"
,
size
);
}
}
return
(
lua_Integer
)
res
;
}
static
int
str_unpack
(
lua_State
*
L
)
{
Header
h
;
const
char
*
fmt
=
luaL_checkstring
(
L
,
1
);
size_t
ld
;
const
char
*
data
=
luaL_checklstring
(
L
,
2
,
&
ld
);
size_t
pos
=
(
size_t
)
posrelat
(
luaL_optinteger
(
L
,
3
,
1
),
ld
)
-
1
;
int
n
=
0
;
/* number of results */
luaL_argcheck
(
L
,
pos
<=
ld
,
3
,
"initial position out of string"
);
initheader
(
L
,
&
h
);
while
(
*
fmt
!=
'\0'
)
{
int
size
,
ntoalign
;
KOption
opt
=
getdetails
(
&
h
,
pos
,
&
fmt
,
&
size
,
&
ntoalign
);
if
((
size_t
)
ntoalign
+
size
>
~
pos
||
pos
+
ntoalign
+
size
>
ld
)
luaL_argerror
(
L
,
2
,
"data string too short"
);
pos
+=
ntoalign
;
/* skip alignment */
/* stack space for item + next position */
luaL_checkstack
(
L
,
2
,
"too many results"
);
n
++
;
switch
(
opt
)
{
case
Kint
:
case
Kuint
:
{
lua_Integer
res
=
unpackint
(
L
,
data
+
pos
,
h
.
islittle
,
size
,
(
opt
==
Kint
));
lua_pushinteger
(
L
,
res
);
break
;
}
case
Kfloat
:
{
volatile
Ftypes
u
;
lua_Number
num
;
copywithendian
(
u
.
buff
,
data
+
pos
,
size
,
h
.
islittle
);
if
(
size
==
sizeof
(
u
.
f
))
num
=
(
lua_Number
)
u
.
f
;
else
if
(
size
==
sizeof
(
u
.
d
))
num
=
(
lua_Number
)
u
.
d
;
else
num
=
u
.
n
;
lua_pushnumber
(
L
,
num
);
break
;
}
case
Kchar
:
{
lua_pushlstring
(
L
,
data
+
pos
,
size
);
break
;
}
case
Kstring
:
{
size_t
len
=
(
size_t
)
unpackint
(
L
,
data
+
pos
,
h
.
islittle
,
size
,
0
);
luaL_argcheck
(
L
,
pos
+
len
+
size
<=
ld
,
2
,
"data string too short"
);
lua_pushlstring
(
L
,
data
+
pos
+
size
,
len
);
pos
+=
len
;
/* skip string */
break
;
}
case
Kzstr
:
{
size_t
len
=
(
int
)
strlen
(
data
+
pos
);
lua_pushlstring
(
L
,
data
+
pos
,
len
);
pos
+=
len
+
1
;
/* skip string plus final '\0' */
break
;
}
case
Kpaddalign
:
case
Kpadding
:
case
Knop
:
n
--
;
/* undo increment */
break
;
}
pos
+=
size
;
}
lua_pushinteger
(
L
,
pos
+
1
);
/* next position */
return
n
+
1
;
}
/* }====================================================== */
LROT_BEGIN
(
strlib
,
NULL
,
LROT_MASK_INDEX
)
LROT_TABENTRY
(
__index
,
strlib
)
LROT_FUNCENTRY
(
__mod
,
str_format2
)
LROT_FUNCENTRY
(
byte
,
str_byte
)
LROT_FUNCENTRY
(
char
,
str_char
)
LROT_FUNCENTRY
(
dump
,
str_dump
)
LROT_FUNCENTRY
(
find
,
str_find
)
LROT_FUNCENTRY
(
format
,
str_format
)
LROT_FUNCENTRY
(
gmatch
,
gmatch
)
LROT_FUNCENTRY
(
gsub
,
str_gsub
)
LROT_FUNCENTRY
(
len
,
str_len
)
LROT_FUNCENTRY
(
lower
,
str_lower
)
LROT_FUNCENTRY
(
match
,
str_match
)
LROT_FUNCENTRY
(
rep
,
str_rep
)
LROT_FUNCENTRY
(
reverse
,
str_reverse
)
LROT_FUNCENTRY
(
sub
,
str_sub
)
LROT_FUNCENTRY
(
upper
,
str_upper
)
LROT_FUNCENTRY
(
pack
,
str_pack
)
LROT_FUNCENTRY
(
packsize
,
str_packsize
)
LROT_FUNCENTRY
(
unpack
,
str_unpack
)
LROT_END
(
strlib
,
NULL
,
LROT_MASK_INDEX
)
/*
** Open string library
*/
LUAMOD_API
int
luaopen_string
(
lua_State
*
L
)
{
lua_pushliteral
(
L
,
""
);
/* dummy string */
lua_pushrotable
(
L
,
LROT_TABLEREF
(
strlib
));
lua_setmetatable
(
L
,
-
2
);
/* set table as metatable for strings */
lua_pop
(
L
,
1
);
/* pop dummy string */
return
0
;
}
components/lua/lua-5.3/ltable.c
0 → 100644
View file @
dba57fa0
/*
** $Id: ltable.c,v 2.118.1.4 2018/06/08 16:22:51 roberto Exp $
** Lua tables (hash)
** See Copyright Notice in lua.h
*/
#define ltable_c
#define LUA_CORE
#include "lprefix.h"
/*
** Implementation of tables (aka arrays, objects, or hash tables).
** Tables keep its elements in two parts: an array part and a hash part.
** Non-negative integer keys are all candidates to be kept in the array
** part. The actual size of the array is the largest 'n' such that
** more than half the slots between 1 and n are in use.
** Hash uses a mix of chained scatter table with Brent's variation.
** A main invariant of these tables is that, if an element is not
** in its main position (i.e. the 'original' position that its hash gives
** to it), then the colliding element is in its own main position.
** Hence even when the load factor reaches 100%, performance remains good.
*/
#include <math.h>
#include <limits.h>
#include <string.h>
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "lvm.h"
/*
** Maximum size of array part (MAXASIZE) is 2^MAXABITS. MAXABITS is
** the largest integer such that MAXASIZE fits in an unsigned int.
*/
#define MAXABITS cast_int(sizeof(int) * CHAR_BIT - 1)
#define MAXASIZE (1u << MAXABITS)
/*
** Maximum size of hash part is 2^MAXHBITS. MAXHBITS is the largest
** integer such that 2^MAXHBITS fits in a signed int. (Note that the
** maximum number of elements in a table, 2^MAXABITS + 2^MAXHBITS, still
** fits comfortably in an unsigned int.)
*/
#define MAXHBITS (MAXABITS - 1)
#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t))))
#define hashstr(t,str) hashpow2(t, (str)->hash)
#define hashboolean(t,p) hashpow2(t, p)
#define hashint(t,i) hashpow2(t, i)
/*
** for some types, it is better to avoid modulus by power of 2, as
** they tend to have many 2 factors.
*/
#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1))))
#define hashpointer(t,p) hashmod(t, point2uint(p))
#define dummynode (&dummynode_)
static
const
Node
dummynode_
=
{
{
NILCONSTANT
},
/* value */
{{
NILCONSTANT
,
0
}}
/* key */
};
/*
** Hash for floating-point numbers.
** The main computation should be just
** n = frexp(n, &i); return (n * INT_MAX) + i
** but there are some numerical subtleties.
** In a two-complement representation, INT_MAX does not has an exact
** representation as a float, but INT_MIN does; because the absolute
** value of 'frexp' is smaller than 1 (unless 'n' is inf/NaN), the
** absolute value of the product 'frexp * -INT_MIN' is smaller or equal
** to INT_MAX. Next, the use of 'unsigned int' avoids overflows when
** adding 'i'; the use of '~u' (instead of '-u') avoids problems with
** INT_MIN.
*/
#if !defined(l_hashfloat)
static
int
l_hashfloat
(
lua_Number
n
)
{
int
i
;
lua_Integer
ni
;
n
=
l_mathop
(
frexp
)(
n
,
&
i
)
*
-
cast_num
(
INT_MIN
);
if
(
!
lua_numbertointeger
(
n
,
&
ni
))
{
/* is 'n' inf/-inf/NaN? */
lua_assert
(
luai_numisnan
(
n
)
||
l_mathop
(
fabs
)(
n
)
==
cast_num
(
HUGE_VAL
));
return
0
;
}
else
{
/* normal case */
unsigned
int
u
=
cast
(
unsigned
int
,
i
)
+
cast
(
unsigned
int
,
ni
);
return
cast_int
(
u
<=
cast
(
unsigned
int
,
INT_MAX
)
?
u
:
~
u
);
}
}
#endif
/*
** returns the 'main' position of an element in a table (that is, the index
** of its hash value)
*/
static
Node
*
mainposition
(
const
Table
*
t
,
const
TValue
*
key
)
{
switch
(
ttype
(
key
))
{
case
LUA_TNUMINT
:
return
hashint
(
t
,
ivalue
(
key
));
case
LUA_TNUMFLT
:
return
hashmod
(
t
,
l_hashfloat
(
fltvalue
(
key
)));
case
LUA_TSHRSTR
:
return
hashstr
(
t
,
tsvalue
(
key
));
case
LUA_TLNGSTR
:
return
hashpow2
(
t
,
luaS_hashlongstr
(
tsvalue
(
key
)));
case
LUA_TBOOLEAN
:
return
hashboolean
(
t
,
bvalue
(
key
));
case
LUA_TLIGHTUSERDATA
:
return
hashpointer
(
t
,
pvalue
(
key
));
case
LUA_TLCF
:
return
hashpointer
(
t
,
fvalue
(
key
));
default:
lua_assert
(
!
ttisdeadkey
(
key
));
return
hashpointer
(
t
,
gcvalue
(
key
));
}
}
/*
** returns the index for 'key' if 'key' is an appropriate key to live in
** the array part of the table, 0 otherwise.
*/
static
unsigned
int
arrayindex
(
const
TValue
*
key
)
{
if
(
ttisinteger
(
key
))
{
lua_Integer
k
=
ivalue
(
key
);
if
(
0
<
k
&&
(
lua_Unsigned
)
k
<=
MAXASIZE
)
return
cast
(
unsigned
int
,
k
);
/* 'key' is an appropriate array index */
}
return
0
;
/* 'key' did not match some condition */
}
/*
** returns the index of a 'key' for table traversals. First goes all
** elements in the array part, then elements in the hash part. The
** beginning of a traversal is signaled by 0.
*/
static
unsigned
int
findindex
(
lua_State
*
L
,
Table
*
t
,
StkId
key
)
{
unsigned
int
i
;
if
(
ttisnil
(
key
))
return
0
;
/* first iteration */
i
=
arrayindex
(
key
);
if
(
i
!=
0
&&
i
<=
t
->
sizearray
)
/* is 'key' inside array part? */
return
i
;
/* yes; that's the index */
else
{
int
nx
;
Node
*
n
=
mainposition
(
t
,
key
);
for
(;;)
{
/* check whether 'key' is somewhere in the chain */
/* key may be dead already, but it is ok to use it in 'next' */
if
(
luaV_rawequalobj
(
gkey
(
n
),
key
)
||
(
ttisdeadkey
(
gkey
(
n
))
&&
iscollectable
(
key
)
&&
deadvalue
(
gkey
(
n
))
==
gcvalue
(
key
)))
{
i
=
cast_int
(
n
-
gnode
(
t
,
0
));
/* key index in hash table */
/* hash elements are numbered after array ones */
return
(
i
+
1
)
+
t
->
sizearray
;
}
nx
=
gnext
(
n
);
if
(
nx
==
0
)
luaG_runerror
(
L
,
"invalid key to 'next'"
);
/* key not found */
else
n
+=
nx
;
}
}
}
static
void
rotable_next
(
lua_State
*
L
,
ROTable
*
t
,
TValue
*
key
,
TValue
*
val
);
int
luaH_next
(
lua_State
*
L
,
Table
*
t
,
StkId
key
)
{
unsigned
int
i
;
if
(
isrotable
(
t
))
{
rotable_next
(
L
,
(
ROTable
*
)
t
,
key
,
key
+
1
);
return
ttisnil
(
key
)
?
0
:
1
;
}
i
=
findindex
(
L
,
t
,
key
);
/* find original element */
for
(;
i
<
t
->
sizearray
;
i
++
)
{
/* try first array part */
if
(
!
ttisnil
(
&
t
->
array
[
i
]))
{
/* a non-nil value? */
setivalue
(
key
,
i
+
1
);
setobj2s
(
L
,
key
+
1
,
&
t
->
array
[
i
]);
return
1
;
}
}
for
(
i
-=
t
->
sizearray
;
cast_int
(
i
)
<
sizenode
(
t
);
i
++
)
{
/* hash part */
if
(
!
ttisnil
(
gval
(
gnode
(
t
,
i
))))
{
/* a non-nil value? */
setobj2s
(
L
,
key
,
gkey
(
gnode
(
t
,
i
)));
setobj2s
(
L
,
key
+
1
,
gval
(
gnode
(
t
,
i
)));
return
1
;
}
}
return
0
;
/* no more elements */
}
/*
** {=============================================================
** Rehash
** ==============================================================
*/
/*
** Compute the optimal size for the array part of table 't'. 'nums' is a
** "count array" where 'nums[i]' is the number of integers in the table
** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of
** integer keys in the table and leaves with the number of keys that
** will go to the array part; return the optimal size.
*/
static
unsigned
int
computesizes
(
unsigned
int
nums
[],
unsigned
int
*
pna
)
{
int
i
;
unsigned
int
twotoi
;
/* 2^i (candidate for optimal size) */
unsigned
int
a
=
0
;
/* number of elements smaller than 2^i */
unsigned
int
na
=
0
;
/* number of elements to go to array part */
unsigned
int
optimal
=
0
;
/* optimal size for array part */
/* loop while keys can fill more than half of total size */
for
(
i
=
0
,
twotoi
=
1
;
twotoi
>
0
&&
*
pna
>
twotoi
/
2
;
i
++
,
twotoi
*=
2
)
{
if
(
nums
[
i
]
>
0
)
{
a
+=
nums
[
i
];
if
(
a
>
twotoi
/
2
)
{
/* more than half elements present? */
optimal
=
twotoi
;
/* optimal size (till now) */
na
=
a
;
/* all elements up to 'optimal' will go to array part */
}
}
}
lua_assert
((
optimal
==
0
||
optimal
/
2
<
na
)
&&
na
<=
optimal
);
*
pna
=
na
;
return
optimal
;
}
static
int
countint
(
const
TValue
*
key
,
unsigned
int
*
nums
)
{
unsigned
int
k
=
arrayindex
(
key
);
if
(
k
!=
0
)
{
/* is 'key' an appropriate array index? */
nums
[
luaO_ceillog2
(
k
)]
++
;
/* count as such */
return
1
;
}
else
return
0
;
}
/*
** Count keys in array part of table 't': Fill 'nums[i]' with
** number of keys that will go into corresponding slice and return
** total number of non-nil keys.
*/
static
unsigned
int
numusearray
(
const
Table
*
t
,
unsigned
int
*
nums
)
{
int
lg
;
unsigned
int
ttlg
;
/* 2^lg */
unsigned
int
ause
=
0
;
/* summation of 'nums' */
unsigned
int
i
=
1
;
/* count to traverse all array keys */
/* traverse each slice */
for
(
lg
=
0
,
ttlg
=
1
;
lg
<=
MAXABITS
;
lg
++
,
ttlg
*=
2
)
{
unsigned
int
lc
=
0
;
/* counter */
unsigned
int
lim
=
ttlg
;
if
(
lim
>
t
->
sizearray
)
{
lim
=
t
->
sizearray
;
/* adjust upper limit */
if
(
i
>
lim
)
break
;
/* no more elements to count */
}
/* count elements in range (2^(lg - 1), 2^lg] */
for
(;
i
<=
lim
;
i
++
)
{
if
(
!
ttisnil
(
&
t
->
array
[
i
-
1
]))
lc
++
;
}
nums
[
lg
]
+=
lc
;
ause
+=
lc
;
}
return
ause
;
}
static
int
numusehash
(
const
Table
*
t
,
unsigned
int
*
nums
,
unsigned
int
*
pna
)
{
int
totaluse
=
0
;
/* total number of elements */
int
ause
=
0
;
/* elements added to 'nums' (can go to array part) */
int
i
=
sizenode
(
t
);
while
(
i
--
)
{
Node
*
n
=
&
t
->
node
[
i
];
if
(
!
ttisnil
(
gval
(
n
)))
{
ause
+=
countint
(
gkey
(
n
),
nums
);
totaluse
++
;
}
}
*
pna
+=
ause
;
return
totaluse
;
}
static
void
setarrayvector
(
lua_State
*
L
,
Table
*
t
,
unsigned
int
size
)
{
unsigned
int
i
;
luaM_reallocvector
(
L
,
t
->
array
,
t
->
sizearray
,
size
,
TValue
);
for
(
i
=
t
->
sizearray
;
i
<
size
;
i
++
)
setnilvalue
(
&
t
->
array
[
i
]);
t
->
sizearray
=
size
;
}
static
void
setnodevector
(
lua_State
*
L
,
Table
*
t
,
unsigned
int
size
)
{
if
(
size
==
0
)
{
/* no elements to hash part? */
t
->
node
=
cast
(
Node
*
,
dummynode
);
/* use common 'dummynode' */
t
->
lsizenode
=
0
;
t
->
lastfree
=
NULL
;
/* signal that it is using dummy node */
}
else
{
int
i
;
int
lsize
=
luaO_ceillog2
(
size
);
if
(
lsize
>
MAXHBITS
)
luaG_runerror
(
L
,
"table overflow"
);
size
=
twoto
(
lsize
);
t
->
node
=
luaM_newvector
(
L
,
size
,
Node
);
for
(
i
=
0
;
i
<
(
int
)
size
;
i
++
)
{
Node
*
n
=
gnode
(
t
,
i
);
gnext
(
n
)
=
0
;
setnilvalue
(
wgkey
(
n
));
setnilvalue
(
gval
(
n
));
}
t
->
lsizenode
=
cast_byte
(
lsize
);
t
->
lastfree
=
gnode
(
t
,
size
);
/* all positions are free */
}
}
typedef
struct
{
Table
*
t
;
unsigned
int
nhsize
;
}
AuxsetnodeT
;
static
void
auxsetnode
(
lua_State
*
L
,
void
*
ud
)
{
AuxsetnodeT
*
asn
=
cast
(
AuxsetnodeT
*
,
ud
);
setnodevector
(
L
,
asn
->
t
,
asn
->
nhsize
);
}
void
luaH_resize
(
lua_State
*
L
,
Table
*
t
,
unsigned
int
nasize
,
unsigned
int
nhsize
)
{
unsigned
int
i
;
int
j
;
AuxsetnodeT
asn
;
unsigned
int
oldasize
=
t
->
sizearray
;
int
oldhsize
=
allocsizenode
(
t
);
Node
*
nold
=
t
->
node
;
/* save old hash ... */
if
(
nasize
>
oldasize
)
/* array part must grow? */
setarrayvector
(
L
,
t
,
nasize
);
/* create new hash part with appropriate size */
asn
.
t
=
t
;
asn
.
nhsize
=
nhsize
;
if
(
luaD_rawrunprotected
(
L
,
auxsetnode
,
&
asn
)
!=
LUA_OK
)
{
/* mem. error? */
setarrayvector
(
L
,
t
,
oldasize
);
/* array back to its original size */
luaD_throw
(
L
,
LUA_ERRMEM
);
/* rethrow memory error */
}
if
(
nasize
<
oldasize
)
{
/* array part must shrink? */
t
->
sizearray
=
nasize
;
/* re-insert elements from vanishing slice */
for
(
i
=
nasize
;
i
<
oldasize
;
i
++
)
{
if
(
!
ttisnil
(
&
t
->
array
[
i
]))
luaH_setint
(
L
,
t
,
i
+
1
,
&
t
->
array
[
i
]);
}
/* shrink array */
luaM_reallocvector
(
L
,
t
->
array
,
oldasize
,
nasize
,
TValue
);
}
/* re-insert elements from hash part */
for
(
j
=
oldhsize
-
1
;
j
>=
0
;
j
--
)
{
Node
*
old
=
nold
+
j
;
if
(
!
ttisnil
(
gval
(
old
)))
{
/* doesn't need barrier/invalidate cache, as entry was
already present in the table */
setobjt2t
(
L
,
luaH_set
(
L
,
t
,
gkey
(
old
)),
gval
(
old
));
}
}
if
(
oldhsize
>
0
)
/* not the dummy node? */
luaM_freearray
(
L
,
nold
,
cast
(
size_t
,
oldhsize
));
/* free old hash */
}
void
luaH_resizearray
(
lua_State
*
L
,
Table
*
t
,
unsigned
int
nasize
)
{
int
nsize
=
allocsizenode
(
t
);
luaH_resize
(
L
,
t
,
nasize
,
nsize
);
}
/*
** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i
*/
static
void
rehash
(
lua_State
*
L
,
Table
*
t
,
const
TValue
*
ek
)
{
unsigned
int
asize
;
/* optimal size for array part */
unsigned
int
na
;
/* number of keys in the array part */
unsigned
int
nums
[
MAXABITS
+
1
];
int
i
;
int
totaluse
;
for
(
i
=
0
;
i
<=
MAXABITS
;
i
++
)
nums
[
i
]
=
0
;
/* reset counts */
na
=
numusearray
(
t
,
nums
);
/* count keys in array part */
totaluse
=
na
;
/* all those keys are integer keys */
totaluse
+=
numusehash
(
t
,
nums
,
&
na
);
/* count keys in hash part */
/* count extra key */
na
+=
countint
(
ek
,
nums
);
totaluse
++
;
/* compute new size for array part */
asize
=
computesizes
(
nums
,
&
na
);
/* resize the table to new computed sizes */
luaH_resize
(
L
,
t
,
asize
,
totaluse
-
na
);
}
/*
** }=============================================================
*/
Table
*
luaH_new
(
lua_State
*
L
)
{
GCObject
*
o
=
luaC_newobj
(
L
,
LUA_TTABLE
,
sizeof
(
Table
));
Table
*
t
=
gco2t
(
o
);
t
->
metatable
=
NULL
;
t
->
flags
=
cast_byte
(
~
0
);
t
->
array
=
NULL
;
t
->
sizearray
=
0
;
setnodevector
(
L
,
t
,
0
);
return
t
;
}
void
luaH_free
(
lua_State
*
L
,
Table
*
t
)
{
if
(
!
isdummy
(
t
))
luaM_freearray
(
L
,
t
->
node
,
cast
(
size_t
,
sizenode
(
t
)));
luaM_freearray
(
L
,
t
->
array
,
t
->
sizearray
);
luaM_free
(
L
,
t
);
}
static
Node
*
getfreepos
(
Table
*
t
)
{
if
(
!
isdummy
(
t
))
{
while
(
t
->
lastfree
>
t
->
node
)
{
t
->
lastfree
--
;
if
(
ttisnil
(
gkey
(
t
->
lastfree
)))
return
t
->
lastfree
;
}
}
return
NULL
;
/* could not find a free place */
}
/*
** inserts a new key into a hash table; first, check whether key's main
** position is free. If not, check whether colliding node is in its main
** position or not: if it is not, move colliding node to an empty place and
** put new key in its main position; otherwise (colliding node is in its main
** position), new key goes to an empty position.
*/
TValue
*
luaH_newkey
(
lua_State
*
L
,
Table
*
t
,
const
TValue
*
key
)
{
Node
*
mp
;
TValue
aux
;
if
(
!
isrwtable
(
t
))
luaG_runerror
(
L
,
"table is Readonly"
);
if
(
ttisnil
(
key
))
luaG_runerror
(
L
,
"table index is nil"
);
else
if
(
ttisfloat
(
key
))
{
lua_Integer
k
;
if
(
luaV_tointeger
(
key
,
&
k
,
0
))
{
/* does index fit in an integer? */
setivalue
(
&
aux
,
k
);
key
=
&
aux
;
/* insert it as an integer */
}
else
if
(
luai_numisnan
(
fltvalue
(
key
)))
luaG_runerror
(
L
,
"table index is NaN"
);
}
mp
=
mainposition
(
t
,
key
);
if
(
!
ttisnil
(
gval
(
mp
))
||
isdummy
(
t
))
{
/* main position is taken? */
Node
*
othern
;
Node
*
f
=
getfreepos
(
t
);
/* get a free place */
if
(
f
==
NULL
)
{
/* cannot find a free place? */
rehash
(
L
,
t
,
key
);
/* grow table */
/* whatever called 'newkey' takes care of TM cache */
return
luaH_set
(
L
,
t
,
key
);
/* insert key into grown table */
}
lua_assert
(
!
isdummy
(
t
));
othern
=
mainposition
(
t
,
gkey
(
mp
));
if
(
othern
!=
mp
)
{
/* is colliding node out of its main position? */
/* yes; move colliding node into free position */
while
(
othern
+
gnext
(
othern
)
!=
mp
)
/* find previous */
othern
+=
gnext
(
othern
);
gnext
(
othern
)
=
cast_int
(
f
-
othern
);
/* rechain to point to 'f' */
*
f
=
*
mp
;
/* copy colliding node into free pos. (mp->next also goes) */
if
(
gnext
(
mp
)
!=
0
)
{
gnext
(
f
)
+=
cast_int
(
mp
-
f
);
/* correct 'next' */
gnext
(
mp
)
=
0
;
/* now 'mp' is free */
}
setnilvalue
(
gval
(
mp
));
}
else
{
/* colliding node is in its own main position */
/* new node will go into free position */
if
(
gnext
(
mp
)
!=
0
)
gnext
(
f
)
=
cast_int
((
mp
+
gnext
(
mp
))
-
f
);
/* chain new position */
else
lua_assert
(
gnext
(
f
)
==
0
);
gnext
(
mp
)
=
cast_int
(
f
-
mp
);
mp
=
f
;
}
}
setnodekey
(
L
,
&
mp
->
i_key
,
key
);
luaC_barrierback
(
L
,
t
,
key
);
lua_assert
(
ttisnil
(
gval
(
mp
)));
return
gval
(
mp
);
}
/*
** search function for integers
*/
const
TValue
*
luaH_getint
(
Table
*
t
,
lua_Integer
key
)
{
if
(
isrotable
(
t
))
return
luaO_nilobject
;
/* (1 <= key && key <= t->sizearray) */
if
(
l_castS2U
(
key
)
-
1
<
t
->
sizearray
)
return
&
t
->
array
[
key
-
1
];
else
{
Node
*
n
=
hashint
(
t
,
key
);
for
(;;)
{
/* check whether 'key' is somewhere in the chain */
if
(
ttisinteger
(
gkey
(
n
))
&&
ivalue
(
gkey
(
n
))
==
key
)
return
gval
(
n
);
/* that's it */
else
{
int
nx
=
gnext
(
n
);
if
(
nx
==
0
)
break
;
n
+=
nx
;
}
}
return
luaO_nilobject
;
}
}
/*
** search function for short strings
*/
static
const
TValue
*
rotable_findentry
(
ROTable
*
rotable
,
TString
*
key
,
unsigned
*
ppos
);
const
TValue
*
luaH_getshortstr
(
Table
*
t
,
TString
*
key
)
{
Node
*
n
;
if
(
isrotable
(
t
))
return
rotable_findentry
((
ROTable
*
)
t
,
key
,
NULL
);
n
=
hashstr
(
t
,
key
);
lua_assert
(
gettt
(
key
)
==
LUA_TSHRSTR
);
for
(;;)
{
/* check whether 'key' is somewhere in the chain */
const
TValue
*
k
=
gkey
(
n
);
if
(
ttisshrstring
(
k
)
&&
eqshrstr
(
tsvalue
(
k
),
key
))
return
gval
(
n
);
/* that's it */
else
{
int
nx
=
gnext
(
n
);
if
(
nx
==
0
)
return
luaO_nilobject
;
/* not found */
n
+=
nx
;
}
}
}
/*
** "Generic" get version. (Not that generic: not valid for integers,
** which may be in array part, nor for floats with integral values.)
*/
static
const
TValue
*
getgeneric
(
Table
*
t
,
const
TValue
*
key
)
{
Node
*
n
;
if
(
isrotable
(
t
))
return
luaO_nilobject
;
n
=
mainposition
(
t
,
key
);
for
(;;)
{
/* check whether 'key' is somewhere in the chain */
if
(
luaV_rawequalobj
(
gkey
(
n
),
key
))
return
gval
(
n
);
/* that's it */
else
{
int
nx
=
gnext
(
n
);
if
(
nx
==
0
)
return
luaO_nilobject
;
/* not found */
n
+=
nx
;
}
}
}
const
TValue
*
luaH_getstr
(
Table
*
t
,
TString
*
key
)
{
if
(
gettt
(
key
)
==
LUA_TSHRSTR
)
return
luaH_getshortstr
(
t
,
key
);
else
{
/* for long strings, use generic case */
TValue
ko
;
setsvalue
(
cast
(
lua_State
*
,
NULL
),
&
ko
,
key
);
return
getgeneric
(
t
,
&
ko
);
}
}
/*
** main search function
*/
const
TValue
*
luaH_get
(
Table
*
t
,
const
TValue
*
key
)
{
switch
(
ttype
(
key
))
{
case
LUA_TSHRSTR
:
return
luaH_getshortstr
(
t
,
tsvalue
(
key
));
case
LUA_TNUMINT
:
return
luaH_getint
(
t
,
ivalue
(
key
));
case
LUA_TNIL
:
return
luaO_nilobject
;
case
LUA_TNUMFLT
:
{
lua_Integer
k
;
if
(
luaV_tointeger
(
key
,
&
k
,
0
))
/* index is int? */
return
luaH_getint
(
t
,
k
);
/* use specialized version */
/* else... */
}
/* FALLTHROUGH */
default:
return
getgeneric
(
t
,
key
);
}
}
/*
** beware: when using this function you probably need to check a GC
** barrier and invalidate the TM cache.
*/
TValue
*
luaH_set
(
lua_State
*
L
,
Table
*
t
,
const
TValue
*
key
)
{
const
TValue
*
p
;
if
(
isrotable
(
t
))
luaG_runerror
(
L
,
"table is readonly"
);
p
=
luaH_get
(
t
,
key
);
if
(
p
!=
luaO_nilobject
)
return
cast
(
TValue
*
,
p
);
else
return
luaH_newkey
(
L
,
t
,
key
);
}
void
luaH_setint
(
lua_State
*
L
,
Table
*
t
,
lua_Integer
key
,
TValue
*
value
)
{
const
TValue
*
p
;
if
(
isrotable
(
t
))
luaG_runerror
(
L
,
"table is readonly"
);
p
=
luaH_getint
(
t
,
key
);
TValue
*
cell
;
if
(
p
!=
luaO_nilobject
)
cell
=
cast
(
TValue
*
,
p
);
else
{
TValue
k
;
setivalue
(
&
k
,
key
);
cell
=
luaH_newkey
(
L
,
t
,
&
k
);
}
setobj2t
(
L
,
cell
,
value
);
}
static
lua_Unsigned
unbound_search
(
Table
*
t
,
lua_Unsigned
j
)
{
lua_Unsigned
i
=
j
;
/* i is zero or a present index */
j
++
;
/* find 'i' and 'j' such that i is present and j is not */
while
(
!
ttisnil
(
luaH_getint
(
t
,
j
)))
{
i
=
j
;
if
(
j
>
l_castS2U
(
LUA_MAXINTEGER
)
/
2
)
{
/* overflow? */
/* table was built with bad purposes: resort to linear search */
i
=
1
;
while
(
!
ttisnil
(
luaH_getint
(
t
,
i
)))
i
++
;
return
i
-
1
;
}
j
*=
2
;
}
/* now do a binary search between them */
while
(
j
-
i
>
1
)
{
lua_Unsigned
m
=
(
i
+
j
)
/
2
;
if
(
ttisnil
(
luaH_getint
(
t
,
m
)))
j
=
m
;
else
i
=
m
;
}
return
i
;
}
/*
** Try to find a boundary in table 't'. A 'boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
lua_Unsigned
luaH_getn
(
Table
*
t
)
{
unsigned
int
j
;
if
(
isrotable
(
t
))
return
0
;
j
=
t
->
sizearray
;
if
(
j
>
0
&&
ttisnil
(
&
t
->
array
[
j
-
1
]))
{
/* there is a boundary in the array part: (binary) search for it */
unsigned
int
i
=
0
;
while
(
j
-
i
>
1
)
{
unsigned
int
m
=
(
i
+
j
)
/
2
;
if
(
ttisnil
(
&
t
->
array
[
m
-
1
]))
j
=
m
;
else
i
=
m
;
}
return
i
;
}
/* else must find a boundary in hash part */
else
if
(
isdummy
(
t
))
/* hash part is empty? */
return
j
;
/* that is easy... */
else
return
unbound_search
(
t
,
j
);
}
int
luaH_isdummy
(
const
Table
*
t
)
{
return
isdummy
(
t
);
}
/*
** All keyed ROTable access passes through rotable_findentry(). ROTables
** are simply a list of <key><TValue value> pairs.
**
** The global KeyCache is used to avoid a relatively expensive Flash memory
** vector scan. A simple hash on the key's TString addr and the ROTable
** addr selects the cache line. The line's slots are then scanned for a
** hit.
**
** Unlike the standard hash which uses a prime line count therefore requires
** the use of modulus operation which is expensive on an IoT processor
** without H/W divide. This hash is power of 2 based which might not be quite
** so uniform but can be calculated without using H/W-based instructions.
**
** If a match is found and the table addresses match, then this entry is
** probed first. In practice the hit-rate here is over 99% so the code
** rarely fails back to doing the linear scan in ROM.
** Note that this hash does a couple of prime multiples and a modulus 2^X
** with is all evaluated in H/W, and adequately randomizes the lookup.
*/
#define HASH(a,b) ((((29*(size_t)(a)) ^ (37*((b)->hash)))>>4)&(KEYCACHE_N-1))
#define NDX_SHFT 24
#define ADDR_MASK (((size_t) 1<<24)-1)
/*
* Find a string key entry in a rotable and return it.
*/
static
const
TValue
*
rotable_findentry
(
ROTable
*
t
,
TString
*
key
,
unsigned
*
ppos
)
{
const
ROTable_entry
*
e
=
cast
(
const
ROTable_entry
*
,
t
->
entry
);
const
int
tl
=
getlsizenode
(
t
);
const
char
*
strkey
=
getstr
(
key
);
const
int
hash
=
HASH
(
t
,
key
);
KeyCache
*
cl
=
luaE_getcache
(
hash
);
int
i
,
j
=
1
,
l
;
if
(
!
e
||
gettt
(
key
)
!=
LUA_TSHRSTR
)
return
luaO_nilobject
;
l
=
getshrlen
(
key
);
/* scan the ROTable key cache and return if hit found */
for
(
i
=
0
;
i
<
KEYCACHE_M
;
i
++
)
{
int
cl_ndx
=
cl
[
i
]
>>
NDX_SHFT
;
if
((((
size_t
)
t
-
cl
[
i
])
&
ADDR_MASK
)
==
0
&&
cl_ndx
<
tl
&&
strcmp
(
e
[
cl_ndx
].
key
,
strkey
)
==
0
)
{
if
(
ppos
)
*
ppos
=
cl_ndx
;
return
&
e
[
cl_ndx
].
value
;
}
}
/*
* In practice most table scans are from a table miss due to the key cache
* short-circuiting almost all table hits. ROTable keys can be unsorted
* because of legacy compatibility, so the search must use a sequential
* equality match.
*
* The masked name4 comparison is a safe 4-byte comparison for all supported
* NodeMCU hosts and targets; It generate fast efficient access that avoids
* unaligned exceptions and costly strcmp() except for a last hit validation.
* However, this is ENDIAN SENSITIVE which is validate during initialisation.
*
* The majority of search misses are for metavalues (keys starting with __),
* so all metavalues if any must be at the front of each entry list.
*/
lu_int32
name4
=
*
(
lu_int32
*
)
strkey
;
lu_int32
mask4
=
l
>
2
?
(
~
0u
)
:
(
~
0u
)
>>
((
3
-
l
)
*
8
);
lua_assert
(
*
(
int
*
)
"abcd"
==
0x64636261
);
#define eq4(s) (((*(lu_int32 *)s ^ name4) & mask4) == 0)
#define ismeta(s) ((*(lu_int32 *)s & 0xffff) == *(lu_int32 *)"__\0")
if
(
ismeta
(
&
name4
))
{
for
(
i
=
0
;
i
<
tl
&&
ismeta
(
e
[
i
].
key
);
i
++
)
{
if
(
eq4
(
e
[
i
].
key
)
&&
!
strcmp
(
e
[
i
].
key
,
strkey
))
{
j
=
0
;
break
;
}
}
}
else
{
for
(
i
=
0
;
i
<
tl
;
i
++
)
{
if
(
eq4
(
e
[
i
].
key
)
&&
!
strcmp
(
e
[
i
].
key
,
strkey
))
{
j
=
0
;
break
;
}
}
}
if
(
j
)
return
luaO_nilobject
;
if
(
ppos
)
*
ppos
=
i
;
/* In the case of a hit, update the lookaside cache */
for
(
j
=
KEYCACHE_M
-
1
;
j
>
0
;
j
--
)
cl
[
j
]
=
cl
[
j
-
1
];
cl
[
0
]
=
((
size_t
)
t
&
ADDR_MASK
)
+
(
i
<<
NDX_SHFT
);
return
&
e
[
i
].
value
;
}
static
void
rotable_next_helper
(
lua_State
*
L
,
ROTable
*
t
,
int
pos
,
TValue
*
key
,
TValue
*
val
)
{
const
ROTable_entry
*
e
=
cast
(
const
ROTable_entry
*
,
t
->
entry
);
if
(
pos
<
getlsizenode
(
t
))
{
/* Found an entry */
setsvalue
(
L
,
key
,
luaS_new
(
L
,
e
[
pos
].
key
));
setobj2s
(
L
,
val
,
&
e
[
pos
].
value
);
}
else
{
setnilvalue
(
key
);
setnilvalue
(
val
);
}
}
/* next (used for iteration) */
static
void
rotable_next
(
lua_State
*
L
,
ROTable
*
t
,
TValue
*
key
,
TValue
*
val
)
{
unsigned
keypos
=
getlsizenode
(
t
);
/* Special case: if key is nil, return the first element of the rotable */
if
(
ttisnil
(
key
))
rotable_next_helper
(
L
,
t
,
0
,
key
,
val
);
else
if
(
ttisstring
(
key
))
{
/* Find the previous key again */
if
(
ttisstring
(
key
))
{
rotable_findentry
(
t
,
tsvalue
(
key
),
&
keypos
);
}
/* Advance to next key */
rotable_next_helper
(
L
,
t
,
++
keypos
,
key
,
val
);
}
}
#if defined(LUA_DEBUG)
Node
*
luaH_mainposition
(
const
Table
*
t
,
const
TValue
*
key
)
{
return
mainposition
(
t
,
key
);
}
#endif
components/lua/lua-5.3/ltable.h
0 → 100644
View file @
dba57fa0
/*
** $Id: ltable.h,v 2.23.1.2 2018/05/24 19:39:05 roberto Exp $
** Lua tables (hash)
** See Copyright Notice in lua.h
*/
#ifndef ltable_h
#define ltable_h
#include "lobject.h"
#define gnode(t,i) (&(t)->node[i])
#define gval(n) (&(n)->i_val)
#define gnext(n) ((n)->i_key.nk.next)
/* 'const' to avoid wrong writings that can mess up field 'next' */
#define gkey(n) cast(const TValue*, (&(n)->i_key.tvk))
/*
** writable version of 'gkey'; allows updates to individual fields,
** but not to the whole (which has incompatible type)
*/
#define wgkey(n) (&(n)->i_key.nk)
#define invalidateTMcache(t) ((t)->flags = 0)
/* true when 't' is using 'dummynode' as its hash part */
#define isdummy(t) ((t)->lastfree == NULL)
/* allocated size for hash nodes */
#define allocsizenode(t) (isdummy(t) ? 0 : sizenode(t))
/* returns the key, given the value of a table entry */
#define keyfromval(v) \
(gkey(cast(Node *, cast(char *, (v)) - offsetof(Node, i_val))))
/* test Table to determine if it is a RW or RO table */
#define isrotable(t) (gettt(t)==LUA_TTBLROF)
#define isrwtable(t) (gettt(t)==LUA_TTBLRAM)
LUAI_FUNC
const
TValue
*
luaH_getint
(
Table
*
t
,
lua_Integer
key
);
LUAI_FUNC
void
luaH_setint
(
lua_State
*
L
,
Table
*
t
,
lua_Integer
key
,
TValue
*
value
);
LUAI_FUNC
const
TValue
*
luaH_getshortstr
(
Table
*
t
,
TString
*
key
);
LUAI_FUNC
const
TValue
*
luaH_getstr
(
Table
*
t
,
TString
*
key
);
LUAI_FUNC
const
TValue
*
luaH_get
(
Table
*
t
,
const
TValue
*
key
);
LUAI_FUNC
TValue
*
luaH_newkey
(
lua_State
*
L
,
Table
*
t
,
const
TValue
*
key
);
LUAI_FUNC
TValue
*
luaH_set
(
lua_State
*
L
,
Table
*
t
,
const
TValue
*
key
);
LUAI_FUNC
Table
*
luaH_new
(
lua_State
*
L
);
LUAI_FUNC
void
luaH_resize
(
lua_State
*
L
,
Table
*
t
,
unsigned
int
nasize
,
unsigned
int
nhsize
);
LUAI_FUNC
void
luaH_resizearray
(
lua_State
*
L
,
Table
*
t
,
unsigned
int
nasize
);
LUAI_FUNC
void
luaH_free
(
lua_State
*
L
,
Table
*
t
);
LUAI_FUNC
int
luaH_next
(
lua_State
*
L
,
Table
*
t
,
StkId
key
);
LUAI_FUNC
lua_Unsigned
luaH_getn
(
Table
*
t
);
#if defined(LUA_DEBUG)
LUAI_FUNC
Node
*
luaH_mainposition
(
const
Table
*
t
,
const
TValue
*
key
);
LUAI_FUNC
int
luaH_isdummy
(
const
Table
*
t
);
#endif
#endif
components/lua/lua-5.3/ltablib.c
0 → 100644
View file @
dba57fa0
/*
** $Id: ltablib.c,v 1.93.1.1 2017/04/19 17:20:42 roberto Exp $
** Library for Table Manipulation
** See Copyright Notice in lua.h
*/
#define ltablib_c
#define LUA_LIB
#include "lprefix.h"
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/*
** Operations that an object must define to mimic a table
** (some functions only need some of them)
*/
#define TAB_R 1
/* read */
#define TAB_W 2
/* write */
#define TAB_L 4
/* length */
#define TAB_RW (TAB_R | TAB_W)
/* read/write */
#define aux_getn(L,n,w) (checktab(L, n, (w) | TAB_L), luaL_len(L, n))
static
int
checkfield
(
lua_State
*
L
,
const
char
*
key
,
int
n
)
{
lua_pushstring
(
L
,
key
);
return
(
lua_rawget
(
L
,
-
n
)
!=
LUA_TNIL
);
}
/*
** Check that 'arg' either is a table or can behave like one (that is,
** has a metatable with the required metamethods)
*/
static
void
checktab
(
lua_State
*
L
,
int
arg
,
int
what
)
{
if
(
lua_type
(
L
,
arg
)
!=
LUA_TTABLE
)
{
/* is it not a table? */
int
n
=
1
;
/* number of elements to pop */
if
(
lua_getmetatable
(
L
,
arg
)
&&
/* must have metatable */
(
!
(
what
&
TAB_R
)
||
checkfield
(
L
,
"__index"
,
++
n
))
&&
(
!
(
what
&
TAB_W
)
||
checkfield
(
L
,
"__newindex"
,
++
n
))
&&
(
!
(
what
&
TAB_L
)
||
checkfield
(
L
,
"__len"
,
++
n
)))
{
lua_pop
(
L
,
n
);
/* pop metatable and tested metamethods */
}
else
luaL_checktype
(
L
,
arg
,
LUA_TTABLE
);
/* force an error */
}
}
#if defined(LUA_COMPAT_MAXN)
static
int
maxn
(
lua_State
*
L
)
{
lua_Number
max
=
0
;
luaL_checktype
(
L
,
1
,
LUA_TTABLE
);
lua_pushnil
(
L
);
/* first key */
while
(
lua_next
(
L
,
1
))
{
lua_pop
(
L
,
1
);
/* remove value */
if
(
lua_type
(
L
,
-
1
)
==
LUA_TNUMBER
)
{
lua_Number
v
=
lua_tonumber
(
L
,
-
1
);
if
(
v
>
max
)
max
=
v
;
}
}
lua_pushnumber
(
L
,
max
);
return
1
;
}
#endif
static
int
tinsert
(
lua_State
*
L
)
{
lua_Integer
e
=
aux_getn
(
L
,
1
,
TAB_RW
)
+
1
;
/* first empty element */
lua_Integer
pos
;
/* where to insert new element */
switch
(
lua_gettop
(
L
))
{
case
2
:
{
/* called with only 2 arguments */
pos
=
e
;
/* insert new element at the end */
break
;
}
case
3
:
{
lua_Integer
i
;
pos
=
luaL_checkinteger
(
L
,
2
);
/* 2nd argument is the position */
luaL_argcheck
(
L
,
1
<=
pos
&&
pos
<=
e
,
2
,
"position out of bounds"
);
for
(
i
=
e
;
i
>
pos
;
i
--
)
{
/* move up elements */
lua_geti
(
L
,
1
,
i
-
1
);
lua_seti
(
L
,
1
,
i
);
/* t[i] = t[i - 1] */
}
break
;
}
default:
{
return
luaL_error
(
L
,
"wrong number of arguments to 'insert'"
);
}
}
lua_seti
(
L
,
1
,
pos
);
/* t[pos] = v */
return
0
;
}
static
int
tremove
(
lua_State
*
L
)
{
lua_Integer
size
=
aux_getn
(
L
,
1
,
TAB_RW
);
lua_Integer
pos
=
luaL_optinteger
(
L
,
2
,
size
);
if
(
pos
!=
size
)
/* validate 'pos' if given */
luaL_argcheck
(
L
,
1
<=
pos
&&
pos
<=
size
+
1
,
1
,
"position out of bounds"
);
lua_geti
(
L
,
1
,
pos
);
/* result = t[pos] */
for
(
;
pos
<
size
;
pos
++
)
{
lua_geti
(
L
,
1
,
pos
+
1
);
lua_seti
(
L
,
1
,
pos
);
/* t[pos] = t[pos + 1] */
}
lua_pushnil
(
L
);
lua_seti
(
L
,
1
,
pos
);
/* t[pos] = nil */
return
1
;
}
/*
** Copy elements (1[f], ..., 1[e]) into (tt[t], tt[t+1], ...). Whenever
** possible, copy in increasing order, which is better for rehashing.
** "possible" means destination after original range, or smaller
** than origin, or copying to another table.
*/
static
int
tmove
(
lua_State
*
L
)
{
lua_Integer
f
=
luaL_checkinteger
(
L
,
2
);
lua_Integer
e
=
luaL_checkinteger
(
L
,
3
);
lua_Integer
t
=
luaL_checkinteger
(
L
,
4
);
int
tt
=
!
lua_isnoneornil
(
L
,
5
)
?
5
:
1
;
/* destination table */
checktab
(
L
,
1
,
TAB_R
);
checktab
(
L
,
tt
,
TAB_W
);
if
(
e
>=
f
)
{
/* otherwise, nothing to move */
lua_Integer
n
,
i
;
luaL_argcheck
(
L
,
f
>
0
||
e
<
LUA_MAXINTEGER
+
f
,
3
,
"too many elements to move"
);
n
=
e
-
f
+
1
;
/* number of elements to move */
luaL_argcheck
(
L
,
t
<=
LUA_MAXINTEGER
-
n
+
1
,
4
,
"destination wrap around"
);
if
(
t
>
e
||
t
<=
f
||
(
tt
!=
1
&&
!
lua_compare
(
L
,
1
,
tt
,
LUA_OPEQ
)))
{
for
(
i
=
0
;
i
<
n
;
i
++
)
{
lua_geti
(
L
,
1
,
f
+
i
);
lua_seti
(
L
,
tt
,
t
+
i
);
}
}
else
{
for
(
i
=
n
-
1
;
i
>=
0
;
i
--
)
{
lua_geti
(
L
,
1
,
f
+
i
);
lua_seti
(
L
,
tt
,
t
+
i
);
}
}
}
lua_pushvalue
(
L
,
tt
);
/* return destination table */
return
1
;
}
static
void
addfield
(
lua_State
*
L
,
luaL_Buffer
*
b
,
lua_Integer
i
)
{
lua_geti
(
L
,
1
,
i
);
if
(
!
lua_isstring
(
L
,
-
1
))
luaL_error
(
L
,
"invalid value (%s) at index %d in table for 'concat'"
,
luaL_typename
(
L
,
-
1
),
i
);
luaL_addvalue
(
b
);
}
static
int
tconcat
(
lua_State
*
L
)
{
luaL_Buffer
b
;
lua_Integer
last
=
aux_getn
(
L
,
1
,
TAB_R
);
size_t
lsep
;
const
char
*
sep
=
luaL_optlstring
(
L
,
2
,
""
,
&
lsep
);
lua_Integer
i
=
luaL_optinteger
(
L
,
3
,
1
);
last
=
luaL_optinteger
(
L
,
4
,
last
);
luaL_buffinit
(
L
,
&
b
);
for
(;
i
<
last
;
i
++
)
{
addfield
(
L
,
&
b
,
i
);
luaL_addlstring
(
&
b
,
sep
,
lsep
);
}
if
(
i
==
last
)
/* add last value (if interval was not empty) */
addfield
(
L
,
&
b
,
i
);
luaL_pushresult
(
&
b
);
return
1
;
}
/*
** {======================================================
** Pack/unpack
** =======================================================
*/
static
int
pack
(
lua_State
*
L
)
{
int
i
;
int
n
=
lua_gettop
(
L
);
/* number of elements to pack */
lua_createtable
(
L
,
n
,
1
);
/* create result table */
lua_insert
(
L
,
1
);
/* put it at index 1 */
for
(
i
=
n
;
i
>=
1
;
i
--
)
/* assign elements */
lua_seti
(
L
,
1
,
i
);
lua_pushinteger
(
L
,
n
);
lua_setfield
(
L
,
1
,
"n"
);
/* t.n = number of elements */
return
1
;
/* return table */
}
static
int
unpack
(
lua_State
*
L
)
{
lua_Unsigned
n
;
lua_Integer
i
=
luaL_optinteger
(
L
,
2
,
1
);
lua_Integer
e
=
luaL_opt
(
L
,
luaL_checkinteger
,
3
,
luaL_len
(
L
,
1
));
if
(
i
>
e
)
return
0
;
/* empty range */
n
=
(
lua_Unsigned
)
e
-
i
;
/* number of elements minus 1 (avoid overflows) */
if
(
n
>=
(
unsigned
int
)
INT_MAX
||
!
lua_checkstack
(
L
,
(
int
)(
++
n
)))
return
luaL_error
(
L
,
"too many results to unpack"
);
for
(;
i
<
e
;
i
++
)
{
/* push arg[i..e - 1] (to avoid overflows) */
lua_geti
(
L
,
1
,
i
);
}
lua_geti
(
L
,
1
,
e
);
/* push last element */
return
(
int
)
n
;
}
/* }====================================================== */
/*
** {======================================================
** Quicksort
** (based on 'Algorithms in MODULA-3', Robert Sedgewick;
** Addison-Wesley, 1993.)
** =======================================================
*/
/* type for array indices */
typedef
unsigned
int
IdxT
;
/*
** Produce a "random" 'unsigned int' to randomize pivot choice. This
** macro is used only when 'sort' detects a big imbalance in the result
** of a partition. (If you don't want/need this "randomness", ~0 is a
** good choice.)
*/
#define l_randomizePivot() (~0);
#if !defined(l_randomizePivot)
/* { */
#include <time.h>
/* size of 'e' measured in number of 'unsigned int's */
#define sof(e) (sizeof(e) / sizeof(unsigned int))
/*
** Use 'time' and 'clock' as sources of "randomness". Because we don't
** know the types 'clock_t' and 'time_t', we cannot cast them to
** anything without risking overflows. A safe way to use their values
** is to copy them to an array of a known type and use the array values.
*/
static
unsigned
int
l_randomizePivot
(
void
)
{
clock_t
c
=
clock
();
time_t
t
=
time
(
NULL
);
unsigned
int
buff
[
sof
(
c
)
+
sof
(
t
)];
unsigned
int
i
,
rnd
=
0
;
memcpy
(
buff
,
&
c
,
sof
(
c
)
*
sizeof
(
unsigned
int
));
memcpy
(
buff
+
sof
(
c
),
&
t
,
sof
(
t
)
*
sizeof
(
unsigned
int
));
for
(
i
=
0
;
i
<
sof
(
buff
);
i
++
)
rnd
+=
buff
[
i
];
return
rnd
;
}
#endif
/* } */
/* arrays larger than 'RANLIMIT' may use randomized pivots */
#define RANLIMIT 100u
static
void
set2
(
lua_State
*
L
,
IdxT
i
,
IdxT
j
)
{
lua_seti
(
L
,
1
,
i
);
lua_seti
(
L
,
1
,
j
);
}
/*
** Return true iff value at stack index 'a' is less than the value at
** index 'b' (according to the order of the sort).
*/
static
int
sort_comp
(
lua_State
*
L
,
int
a
,
int
b
)
{
if
(
lua_isnil
(
L
,
2
))
/* no function? */
return
lua_compare
(
L
,
a
,
b
,
LUA_OPLT
);
/* a < b */
else
{
/* function */
int
res
;
lua_pushvalue
(
L
,
2
);
/* push function */
lua_pushvalue
(
L
,
a
-
1
);
/* -1 to compensate function */
lua_pushvalue
(
L
,
b
-
2
);
/* -2 to compensate function and 'a' */
lua_call
(
L
,
2
,
1
);
/* call function */
res
=
lua_toboolean
(
L
,
-
1
);
/* get result */
lua_pop
(
L
,
1
);
/* pop result */
return
res
;
}
}
/*
** Does the partition: Pivot P is at the top of the stack.
** precondition: a[lo] <= P == a[up-1] <= a[up],
** so it only needs to do the partition from lo + 1 to up - 2.
** Pos-condition: a[lo .. i - 1] <= a[i] == P <= a[i + 1 .. up]
** returns 'i'.
*/
static
IdxT
partition
(
lua_State
*
L
,
IdxT
lo
,
IdxT
up
)
{
IdxT
i
=
lo
;
/* will be incremented before first use */
IdxT
j
=
up
-
1
;
/* will be decremented before first use */
/* loop invariant: a[lo .. i] <= P <= a[j .. up] */
for
(;;)
{
/* next loop: repeat ++i while a[i] < P */
while
(
lua_geti
(
L
,
1
,
++
i
),
sort_comp
(
L
,
-
1
,
-
2
))
{
if
(
i
==
up
-
1
)
/* a[i] < P but a[up - 1] == P ?? */
luaL_error
(
L
,
"invalid order function for sorting"
);
lua_pop
(
L
,
1
);
/* remove a[i] */
}
/* after the loop, a[i] >= P and a[lo .. i - 1] < P */
/* next loop: repeat --j while P < a[j] */
while
(
lua_geti
(
L
,
1
,
--
j
),
sort_comp
(
L
,
-
3
,
-
1
))
{
if
(
j
<
i
)
/* j < i but a[j] > P ?? */
luaL_error
(
L
,
"invalid order function for sorting"
);
lua_pop
(
L
,
1
);
/* remove a[j] */
}
/* after the loop, a[j] <= P and a[j + 1 .. up] >= P */
if
(
j
<
i
)
{
/* no elements out of place? */
/* a[lo .. i - 1] <= P <= a[j + 1 .. i .. up] */
lua_pop
(
L
,
1
);
/* pop a[j] */
/* swap pivot (a[up - 1]) with a[i] to satisfy pos-condition */
set2
(
L
,
up
-
1
,
i
);
return
i
;
}
/* otherwise, swap a[i] - a[j] to restore invariant and repeat */
set2
(
L
,
i
,
j
);
}
}
/*
** Choose an element in the middle (2nd-3th quarters) of [lo,up]
** "randomized" by 'rnd'
*/
static
IdxT
choosePivot
(
IdxT
lo
,
IdxT
up
,
unsigned
int
rnd
)
{
IdxT
r4
=
(
up
-
lo
)
/
4
;
/* range/4 */
IdxT
p
=
rnd
%
(
r4
*
2
)
+
(
lo
+
r4
);
lua_assert
(
lo
+
r4
<=
p
&&
p
<=
up
-
r4
);
return
p
;
}
/*
** QuickSort algorithm (recursive function)
*/
static
void
auxsort
(
lua_State
*
L
,
IdxT
lo
,
IdxT
up
,
unsigned
int
rnd
)
{
while
(
lo
<
up
)
{
/* loop for tail recursion */
IdxT
p
;
/* Pivot index */
IdxT
n
;
/* to be used later */
/* sort elements 'lo', 'p', and 'up' */
lua_geti
(
L
,
1
,
lo
);
lua_geti
(
L
,
1
,
up
);
if
(
sort_comp
(
L
,
-
1
,
-
2
))
/* a[up] < a[lo]? */
set2
(
L
,
lo
,
up
);
/* swap a[lo] - a[up] */
else
lua_pop
(
L
,
2
);
/* remove both values */
if
(
up
-
lo
==
1
)
/* only 2 elements? */
return
;
/* already sorted */
if
(
up
-
lo
<
RANLIMIT
||
rnd
==
0
)
/* small interval or no randomize? */
p
=
(
lo
+
up
)
/
2
;
/* middle element is a good pivot */
else
/* for larger intervals, it is worth a random pivot */
p
=
choosePivot
(
lo
,
up
,
rnd
);
lua_geti
(
L
,
1
,
p
);
lua_geti
(
L
,
1
,
lo
);
if
(
sort_comp
(
L
,
-
2
,
-
1
))
/* a[p] < a[lo]? */
set2
(
L
,
p
,
lo
);
/* swap a[p] - a[lo] */
else
{
lua_pop
(
L
,
1
);
/* remove a[lo] */
lua_geti
(
L
,
1
,
up
);
if
(
sort_comp
(
L
,
-
1
,
-
2
))
/* a[up] < a[p]? */
set2
(
L
,
p
,
up
);
/* swap a[up] - a[p] */
else
lua_pop
(
L
,
2
);
}
if
(
up
-
lo
==
2
)
/* only 3 elements? */
return
;
/* already sorted */
lua_geti
(
L
,
1
,
p
);
/* get middle element (Pivot) */
lua_pushvalue
(
L
,
-
1
);
/* push Pivot */
lua_geti
(
L
,
1
,
up
-
1
);
/* push a[up - 1] */
set2
(
L
,
p
,
up
-
1
);
/* swap Pivot (a[p]) with a[up - 1] */
p
=
partition
(
L
,
lo
,
up
);
/* a[lo .. p - 1] <= a[p] == P <= a[p + 1 .. up] */
if
(
p
-
lo
<
up
-
p
)
{
/* lower interval is smaller? */
auxsort
(
L
,
lo
,
p
-
1
,
rnd
);
/* call recursively for lower interval */
n
=
p
-
lo
;
/* size of smaller interval */
lo
=
p
+
1
;
/* tail call for [p + 1 .. up] (upper interval) */
}
else
{
auxsort
(
L
,
p
+
1
,
up
,
rnd
);
/* call recursively for upper interval */
n
=
up
-
p
;
/* size of smaller interval */
up
=
p
-
1
;
/* tail call for [lo .. p - 1] (lower interval) */
}
if
((
up
-
lo
)
/
128
>
n
)
/* partition too imbalanced? */
rnd
=
l_randomizePivot
();
/* try a new randomization */
}
/* tail call auxsort(L, lo, up, rnd) */
}
static
int
sort
(
lua_State
*
L
)
{
lua_Integer
n
=
aux_getn
(
L
,
1
,
TAB_RW
);
if
(
n
>
1
)
{
/* non-trivial interval? */
luaL_argcheck
(
L
,
n
<
INT_MAX
,
1
,
"array too big"
);
if
(
!
lua_isnoneornil
(
L
,
2
))
/* is there a 2nd argument? */
luaL_checktype
(
L
,
2
,
LUA_TFUNCTION
);
/* must be a function */
lua_settop
(
L
,
2
);
/* make sure there are two arguments */
auxsort
(
L
,
1
,
(
IdxT
)
n
,
0
);
}
return
0
;
}
/* }====================================================== */
LROT_BEGIN
(
tab_funcs
,
NULL
,
0
)
LROT_FUNCENTRY
(
concat
,
tconcat
)
#if defined(LUA_COMPAT_MAXN)
LROT_FUNCENTRY
(
maxn
,
maxn
)
#endif
LROT_FUNCENTRY
(
insert
,
tinsert
)
LROT_FUNCENTRY
(
pack
,
pack
)
LROT_FUNCENTRY
(
unpack
,
unpack
)
LROT_FUNCENTRY
(
move
,
tmove
)
LROT_FUNCENTRY
(
remove
,
tremove
)
LROT_FUNCENTRY
(
sort
,
sort
)
LROT_END
(
tab_funcs
,
NULL
,
0
)
LUAMOD_API
int
luaopen_table
(
lua_State
*
L
)
{
(
void
)
L
;
return
0
;
}
Prev
1
…
3
4
5
6
7
8
9
10
11
12
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment