Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
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
Hide 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