first pass lua evaluator
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
#ifndef LUA_FUNCTIONS_H
|
||||
#define LUA_FUNCTIONS_H
|
||||
|
||||
#include "LuaHelpers.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
}
|
||||
|
||||
/* Argument helpers: */
|
||||
#define LUA_ASSERT( expr, err ) if( !(expr) ) { Lua::Fail( L, err ); }
|
||||
/* require exactly n arguments */
|
||||
#define REQ_ARGS(func, need) { \
|
||||
const int args = lua_gettop(L); \
|
||||
LUA_ASSERT( args == need, ssprintf( func " requires exactly %i argument%s, got %i", need, need == 1? "":"s", args) ); \
|
||||
}
|
||||
/* argument n must be of type "type" */
|
||||
#define REQ_ARG(func, n, type) { \
|
||||
LUA_ASSERT( lua_is##type(L, n), ssprintf("Argument %i to " func " must be %s", n, #type) ); }
|
||||
/* argument n must be a number between minimum...maximum */
|
||||
#define REQ_ARG_NUMBER_RANGE(func, n, minimum, maximum) { \
|
||||
REQ_ARG(func, n, number); \
|
||||
const int val = (int) lua_tonumber( L, n ); \
|
||||
LUA_ASSERT( val >= minimum && val <= maximum, ssprintf("Argument %i to " func " must be an integer between %i and %i (got %i)", n, minimum, maximum, val) ); \
|
||||
}
|
||||
#define LUA_RETURN( expr ) { Lua::PushStack( L, expr ); return 1; }
|
||||
|
||||
/* Helpers to create common functions: */
|
||||
/* Functions that take no arguments: */
|
||||
#define LuaFunction_NoArgs( func, call ) \
|
||||
int LuaFunc_##func( lua_State *L ) { \
|
||||
REQ_ARGS( #func, 0 ); \
|
||||
LUA_RETURN( call ); \
|
||||
} \
|
||||
LuaFunction( func ); /* register it */
|
||||
|
||||
#define LuaFunction_IntInt( func, call ) \
|
||||
int LuaFunc_##func( lua_State *L ) { \
|
||||
REQ_ARGS( #func, 2 ); \
|
||||
REQ_ARG( #func, 1, number ); \
|
||||
REQ_ARG( #func, 2, number ); \
|
||||
const int a1 = int(lua_tonumber( L, 1 )); \
|
||||
const int a2 = int(lua_tonumber( L, 2 )); \
|
||||
LUA_RETURN( call ); \
|
||||
} \
|
||||
LuaFunction( func ); /* register it */
|
||||
|
||||
#define LuaFunction_Str( func, call ) \
|
||||
int LuaFunc_##func( lua_State *L ) { \
|
||||
REQ_ARGS( #func, 1 ); \
|
||||
REQ_ARG( #func, 1, string ); \
|
||||
CString str; \
|
||||
Lua::PopStack( L, str ); \
|
||||
LUA_RETURN( call ); \
|
||||
} \
|
||||
LuaFunction( func ); /* register it */
|
||||
|
||||
/* Functions that take a single PlayerNumber argument: */
|
||||
#define LuaFunction_PlayerNumber( func, call ) \
|
||||
int LuaFunc_##func( lua_State *L ) { \
|
||||
REQ_ARGS( #func, 1 ); \
|
||||
REQ_ARG_NUMBER_RANGE( #func, 1, 1, NUM_PLAYERS ); \
|
||||
const PlayerNumber pn = (PlayerNumber) (int(lua_tonumber( L, -1 ))-1); \
|
||||
LUA_RETURN( call ); \
|
||||
} \
|
||||
LuaFunction( func ); /* register it */
|
||||
|
||||
|
||||
/* Linked list of functions we make available to Lua. */
|
||||
struct LuaFunctionList
|
||||
{
|
||||
LuaFunctionList( CString name, lua_CFunction func );
|
||||
CString name;
|
||||
lua_CFunction func;
|
||||
LuaFunctionList *next;
|
||||
} extern *g_LuaFunctionList;
|
||||
#define LuaFunction( func ) static LuaFunctionList g_##func( #func, LuaFunc_##func )
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Copyright (c) 2004 by the person(s) listed below. All rights reserved.
|
||||
* Glenn Maynard
|
||||
*/
|
||||
@@ -0,0 +1,183 @@
|
||||
#include "global.h"
|
||||
#include "LuaHelpers.h"
|
||||
#include "LuaFunctions.h"
|
||||
#include "RageUtil.h"
|
||||
#include "RageLog.h"
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <assert.h>
|
||||
|
||||
LuaFunctionList *g_LuaFunctionList = NULL;
|
||||
|
||||
#if defined(_WINDOWS)
|
||||
#pragma comment(lib, "lua-5.0/lua.lib")
|
||||
|
||||
/* "interaction between '_setjmp' and C++ object destruction is non-portable"
|
||||
* We don't care; we'll throw a fatal exception immediately anyway. */
|
||||
#pragma warning (disable : 4611)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
struct ChunkReaderData
|
||||
{
|
||||
const CString *buf;
|
||||
bool done;
|
||||
ChunkReaderData() { buf = NULL; done = false; }
|
||||
};
|
||||
|
||||
const char *ChunkReaderString( lua_State *L, void *ptr, size_t *size )
|
||||
{
|
||||
ChunkReaderData *data = (ChunkReaderData *) ptr;
|
||||
if( data->done )
|
||||
return NULL;
|
||||
|
||||
data->done = true;
|
||||
|
||||
*size = data->buf->size();
|
||||
const char *ret = data->buf->data();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Lua::PushStack( lua_State *L, int out )
|
||||
{
|
||||
/* XXX: stack bounds */
|
||||
lua_pushnumber( L, out );
|
||||
}
|
||||
|
||||
void Lua::PushStack( lua_State *L, bool out )
|
||||
{
|
||||
/* XXX: stack bounds */
|
||||
lua_pushboolean( L, out );
|
||||
}
|
||||
|
||||
|
||||
void Lua::PushStack( lua_State *L, void *out )
|
||||
{
|
||||
if( out )
|
||||
lua_pushlightuserdata( L, out );
|
||||
else
|
||||
lua_pushnil( L );
|
||||
}
|
||||
|
||||
void Lua::PushStack( lua_State *L, const CString &out )
|
||||
{
|
||||
lua_pushstring( L, out );
|
||||
}
|
||||
|
||||
void Lua::PopStack( lua_State *L, CString &out )
|
||||
{
|
||||
/* There must be at least one entry on the stack. */
|
||||
ASSERT( lua_gettop(L) > 0 );
|
||||
ASSERT( lua_isstring(L, -1) );
|
||||
|
||||
out = lua_tostring( L, -1 );
|
||||
lua_pop( L, -1 );
|
||||
}
|
||||
|
||||
void LoadFromString( lua_State *L, const CString &str )
|
||||
{
|
||||
ChunkReaderData data;
|
||||
data.buf = &str;
|
||||
int ret = lua_load( L, ChunkReaderString, &data, "in" );
|
||||
|
||||
if( ret )
|
||||
{
|
||||
CString err;
|
||||
Lua::PopStack( L, err );
|
||||
RageException::Throw( "Error loading script \"%s\": %s", str.c_str(), err.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static jmp_buf jbuf;
|
||||
static CString jbuf_error;
|
||||
|
||||
int LuaPanic( lua_State *L )
|
||||
{
|
||||
CString err;
|
||||
Lua::PopStack( L, err );
|
||||
|
||||
/* Grr. We can't throw an exception from here: it'll explode going through the
|
||||
* Lua stack for some reason. Get it off the stack with a longjmp and throw
|
||||
* an exception from there. */
|
||||
jbuf_error = err;
|
||||
longjmp( jbuf, 1 );
|
||||
}
|
||||
|
||||
bool Lua::RunExpression( const CString &str )
|
||||
{
|
||||
try {
|
||||
if( setjmp(jbuf) )
|
||||
throw jbuf_error;
|
||||
lua_State *L = lua_open();
|
||||
ASSERT( L );
|
||||
|
||||
lua_atpanic( L, LuaPanic );
|
||||
|
||||
luaopen_base( L );
|
||||
lua_settop(L, 0); // luaopen_* pushes stuff onto the stack that we don't need
|
||||
|
||||
Lua::RegisterFunctions( L );
|
||||
|
||||
LoadFromString( L, "return " + str );
|
||||
RAGE_ASSERT_M( lua_gettop(L) == 1, ssprintf("%i", lua_gettop(L)) );
|
||||
|
||||
int ret = lua_pcall(L, 0, 1, 0);
|
||||
if( ret )
|
||||
{
|
||||
CString err;
|
||||
Lua::PopStack( L, err );
|
||||
RageException::Throw( err );
|
||||
}
|
||||
|
||||
RAGE_ASSERT_M( lua_gettop(L) == 1, ssprintf("%i", lua_gettop(L)) );
|
||||
bool result = !!lua_toboolean( L, -1 );
|
||||
lua_pop( L, -1 );
|
||||
|
||||
lua_close( L );
|
||||
|
||||
return result;
|
||||
} catch( const CString &e ) {
|
||||
RageException::Throw( "Error running \"%s\": %s", str.c_str(), e.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
void Lua::Fail( lua_State *L, const CString &err )
|
||||
{
|
||||
lua_pushstring( L, err );
|
||||
lua_error( L );
|
||||
}
|
||||
|
||||
void Lua::RegisterFunctions( lua_State *L )
|
||||
{
|
||||
for( const LuaFunctionList *p = g_LuaFunctionList; p; p=p->next )
|
||||
lua_register( L, p->name, p->func );
|
||||
}
|
||||
|
||||
LuaFunctionList::LuaFunctionList( CString name_, lua_CFunction func_ )
|
||||
{
|
||||
name = name_;
|
||||
func = func_;
|
||||
next = g_LuaFunctionList;
|
||||
g_LuaFunctionList = this;
|
||||
}
|
||||
|
||||
|
||||
LuaFunction_NoArgs( MonthOfYear, GetLocalTime().tm_mon );
|
||||
LuaFunction_NoArgs( DayOfMonth, GetLocalTime().tm_mday );
|
||||
LuaFunction_NoArgs( Hour, GetLocalTime().tm_hour );
|
||||
LuaFunction_NoArgs( Minute, GetLocalTime().tm_min );
|
||||
LuaFunction_NoArgs( Second, GetLocalTime().tm_sec );
|
||||
LuaFunction_NoArgs( Year, GetLocalTime().tm_year+1900 );
|
||||
LuaFunction_NoArgs( Weekday, GetLocalTime().tm_wday );
|
||||
LuaFunction_NoArgs( DayOfYear, GetLocalTime().tm_yday );
|
||||
|
||||
/*
|
||||
* Copyright (c) 2004 by the person(s) listed below. All rights reserved.
|
||||
* Glenn Maynard
|
||||
*/
|
||||
@@ -0,0 +1,25 @@
|
||||
#ifndef LUA_HELPERS_H
|
||||
#define LUA_HELPERS_H
|
||||
|
||||
struct lua_State;
|
||||
namespace Lua
|
||||
{
|
||||
bool RunExpression( const CString &str );
|
||||
|
||||
void Fail( lua_State *L, const CString &err );
|
||||
|
||||
/* Add all registered functions into L. */
|
||||
void RegisterFunctions( lua_State *L );
|
||||
|
||||
void PushStack( lua_State *L, bool out );
|
||||
void PushStack( lua_State *L, int out );
|
||||
void PushStack( lua_State *L, void *out );
|
||||
void PushStack( lua_State *L, const CString &out );
|
||||
void PopStack( lua_State *L, CString &out );
|
||||
};
|
||||
|
||||
#endif
|
||||
/*
|
||||
* Copyright (c) 2004 by the person(s) listed below. All rights reserved.
|
||||
* Glenn Maynard
|
||||
*/
|
||||
Reference in New Issue
Block a user