first pass lua evaluator

This commit is contained in:
Glenn Maynard
2004-02-14 21:08:12 +00:00
parent 394a3f8395
commit d17e1179c0
3 changed files with 293 additions and 0 deletions
+85
View File
@@ -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
*/
+183
View File
@@ -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
*/
+25
View File
@@ -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
*/