2021-01-02 13:31:18 -05:00
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// GENERAL CONCEPT
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// The original lua C API asks you to work with a stack machine. When you
|
|
|
|
|
// use the original API, you're manually pushing and popping in every
|
|
|
|
|
// line of code. I find it hard to remember what stack position contains
|
|
|
|
|
// what value. This wrapper is designed to alleviate that problem.
|
2021-01-17 16:23:10 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// However, the lua garbage collector demands that we keep all lua values
|
|
|
|
|
// on the lua stack. I can't change that. I can create a wrapper, but I
|
|
|
|
|
// still have to keep all lua values on the lua stack. So, here's how
|
|
|
|
|
// this wrapper works:
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// At the beginning of any C++ lua function, we allocate enough space on
|
|
|
|
|
// the lua stack to hold all the arguments, all the return values, and all
|
|
|
|
|
// the temporary values that we need. We give each of the stack slots
|
|
|
|
|
// that we allocate a human-readable name. From that point forward,
|
|
|
|
|
// we don't push or pop anything on the lua stack. Instead, we do all our
|
|
|
|
|
// lua value manipulation using the stack slots that we allocated and named
|
|
|
|
|
// at the beginning of the function.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
|
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
/////////////////////////////////////////////////////////
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// LUAARG, LUARET, LUAVAR, and LUADEFSTACK
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// The best way to explain this wrapper is with an example. This
|
|
|
|
|
// function, 'table_removevalue', takes a table and a 'removethis'
|
|
|
|
|
// value. It searches the table for any (key,val) pairs
|
|
|
|
|
// where val==removethis, and it removes those pairs.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// int table_removevalue(lua_State *L) {
|
|
|
|
|
// LuaArg table, removethis;
|
|
|
|
|
// LuaRet returnvalue;
|
|
|
|
|
// LuaVar key, val;
|
|
|
|
|
// LuaDefStack LS(L, table, removethis, returnvalue, key, val);
|
|
|
|
|
// LS.set(key, LuaNil);
|
|
|
|
|
// while (LS.next(table, key, val)) {
|
|
|
|
|
// if (LS.rawequal(val, removethis)) {
|
|
|
|
|
// LS.rawset(table, key, LuaNil);
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// LS.set(returnvalue, table);
|
|
|
|
|
// return LS.result();
|
2021-01-02 13:31:18 -05:00
|
|
|
// }
|
|
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// Now I'll explain this function. First, you have some declarations for
|
|
|
|
|
// LuaArg (lua function arguments), LuaRet (lua function return values), and
|
|
|
|
|
// LuaVar (lua local variables). These are small structs each containing
|
|
|
|
|
// an 'int' indicating an absolute position on the lua stack. These are all
|
|
|
|
|
// collectively called lua stack slots. Class LuaArg, LuaRet, and
|
|
|
|
|
// LuaVar all derive from a base class LuaSlot.
|
|
|
|
|
//
|
|
|
|
|
// At construction time, all the LuaSlots are initialized to zero, which
|
|
|
|
|
// means they aren't ready to use yet. They don't point to anywhere on the
|
|
|
|
|
// lua stack. Remember, lua numbers everything starting at 1, so zero is
|
|
|
|
|
// not a valid lua stack position.
|
|
|
|
|
//
|
|
|
|
|
// After the LuaSlot declarations, you have the LuaDefStack constructor.
|
|
|
|
|
// This takes the lua stack as a parameter, and then all the LuaSlots.
|
|
|
|
|
// This allocates space on the lua stack for all the LuaSlots. When it's
|
|
|
|
|
// done, the lua stack will contain exactly five values, one for each of the
|
|
|
|
|
// five LuaSlots. The LuaSlot structs will contain the stack positions of
|
|
|
|
|
// these values. The LuaRet and LuaVar stack slots will be initialized to nil.
|
|
|
|
|
// The LuaArg stack slots will be initialized with the function arguments.
|
|
|
|
|
// After the LuaDefStack constructor, you are ready to do lua calculations.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// Next, you have the loop that iterates over the table. To iterate over
|
|
|
|
|
// a table in lua, you initialize 'key' to nil, then you call the 'next'
|
|
|
|
|
// operator to get the next (key,val) pair. The 'next' operator will return
|
|
|
|
|
// false if there is no next pair. For each (key,val) pair in the table,
|
|
|
|
|
// we check if the val is equal to the thing we want to remove, and if so,
|
|
|
|
|
// we change the table entry to nil. After the loop, we set the returnvalue
|
|
|
|
|
// slot to the table that was passed in.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// The last line, return LS.result(), is a piece of boilerplate, every lua
|
|
|
|
|
// C function must end with this. This function removes everything but the
|
|
|
|
|
// return values from the stack, and returns the number of return values.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
|
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// CLASS LUAEXTSTACK.
|
|
|
|
|
//
|
|
|
|
|
// Class LuaDefStack, which I showed above, is meant to be used at the
|
|
|
|
|
// top level of a C++ lua function.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// Class LuaExtStack is meant to be used when you want to allocate
|
|
|
|
|
// some MORE stack slots halfway through a C++ lua function.
|
|
|
|
|
// Class LuaExtStack is particularly useful when you want to write
|
|
|
|
|
// a recursive implementation. Typically you would put LuaDefStack
|
|
|
|
|
// in the top-level function, and LuaExtStack in the recursive
|
|
|
|
|
// implementation.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// Class LuaExtStack differs in two ways: first of all, it doesn't allow
|
|
|
|
|
// LuaArg and LuaRet slots, only LuaVar. Second, it has a destructor that
|
|
|
|
|
// automatically puts the stack back the way it was when it was constructed.
|
|
|
|
|
//
|
|
|
|
|
// You might wonder why class LuaDefStack doesn't have a destructor that
|
|
|
|
|
// cleans up the lua stack. It's because of return values: it can't remove
|
|
|
|
|
// everything from the stack, because it has to leave the return values
|
|
|
|
|
// on the stack.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// I call these two classes the 'LuaStack' classes. When I say that
|
|
|
|
|
// the LuaStack classes do something, I mean that both LuaDefStack
|
|
|
|
|
// and LuaExtStack do that thing.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// THE LIBRARY OF BUILTIN OPERATORS
|
|
|
|
|
//
|
|
|
|
|
// The LuaStack classes provide a whole library of methods to operate
|
|
|
|
|
// on lua values. Roughly speaking, there is one function in LuaStack
|
|
|
|
|
// for every function in the raw lua API, and they are similarly named.
|
|
|
|
|
//
|
|
|
|
|
// However, the functions in the raw lua API push and pop values on the
|
|
|
|
|
// lua stack. The equivalent functions in LuaStack take inputs from
|
|
|
|
|
// LuaSlots, and store their outputs into other LuaSlots. Nothing is
|
|
|
|
|
// pushed or popped.
|
|
|
|
|
//
|
|
|
|
|
// To get the complete list, you will have to scroll down to class
|
|
|
|
|
// LuaCoreStack, below. All the prototypes are there, and they are all
|
|
|
|
|
// documented.
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// AUTOMATIC TYPE CONVERSION
|
|
|
|
|
//
|
|
|
|
|
// In some cases, LuaStack can do automatic conversions of C++ values into
|
|
|
|
|
// lua values. This is supported in any function where it makes sense.
|
|
|
|
|
// One function that supports automatic conversion is 'LuaStack::set':
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
|
|
|
|
// LS.set(val1, val2);
|
|
|
|
|
//
|
2023-04-07 14:20:45 -04:00
|
|
|
// This is actually a copy operation that copies from one lua local variable to
|
2024-03-20 14:29:20 -04:00
|
|
|
// another. But using auto conversions, it can also be used to assign C++
|
|
|
|
|
// values to lua slots. These are all valid:
|
|
|
|
|
//
|
|
|
|
|
// LS.set(value, 1); // int and int64_t can be converted.
|
|
|
|
|
// LS.set(value, "banana"); // char*, string, and string_view can be converted.
|
|
|
|
|
// LS.set(value, true); // bool can be converted.
|
|
|
|
|
// LS.set(value, 2.39); // float and double can be converted.
|
|
|
|
|
// LS.set(value, LuaNil); // This special token stores nil.
|
2023-04-07 14:20:45 -04:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// Automatic conversion is generally allowed in any context where it would
|
|
|
|
|
// make sense. For example, these are all valid:
|
|
|
|
|
//
|
|
|
|
|
// LS.rawget(value, mytable, 1);
|
|
|
|
|
// LS.rawget(value, mytable, "banana");
|
|
|
|
|
// LS.rawget(value, mytable, true);
|
|
|
|
|
// LS.rawget(value, mytable, 2.39);
|
|
|
|
|
// LS.rawget(value, mytable, LuaNil);
|
|
|
|
|
//
|
|
|
|
|
// There's also the 'LuaNewTable' constant. This is handled by the
|
|
|
|
|
// auto-conversion system, but it's not really a conversion:
|
|
|
|
|
//
|
|
|
|
|
// LS.set(value, LuaNewTable);
|
|
|
|
|
//
|
|
|
|
|
// This will cause a new table to be created, and stored in value.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// API INTEROPERABILITY
|
|
|
|
|
//
|
|
|
|
|
// This wrapper can intentionally be mixed with the standard, raw lua API.
|
|
|
|
|
// You still have an explicit handle to the lua stack, and you can get the
|
|
|
|
|
// stack addresses out of the LuaSlot variables using LuaSlot::index. So
|
|
|
|
|
// if there's some tricky thing you can't do with this wrapper, you can always
|
|
|
|
|
// fall back to the raw API for just the section of code that you need to.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// I also sometimes use the raw lua API for code that is doing truly
|
|
|
|
|
// unusual things, that aren't easy to do with this wrapper.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
/////////////////////////////////////////////////////////
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
|
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// ON RAWGET AND METAMETHODS
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// The raw lua API provides functions like lua_gettab and lua_equal
|
|
|
|
|
// which automatically call metamethods. I do not think it is wise to
|
|
|
|
|
// habitually use functions like that, because some of the code we write
|
|
|
|
|
// is not executed in a protected context. That means that a metamethod
|
|
|
|
|
// that generates an error would bring down the whole system rather than
|
|
|
|
|
// just stopping a script thread. It also means that a metamethod could
|
|
|
|
|
// return an invalid piece of data, corrupting a system data structure
|
|
|
|
|
// rather than just a script data structure.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// Because there are so many contexts where it is just not safe to call
|
|
|
|
|
// metamethods, I've made it deliberately difficult to call metamethods.
|
|
|
|
|
// Our API includes 'rawget' which doesn't call metamethods,
|
|
|
|
|
// but it omits 'gettab' which does.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// If someday we actually need metamethod support, we can do that,
|
|
|
|
|
// but I'll have to write a safe wrapper for them. I know how to do that,
|
|
|
|
|
// but it's a lot of work and I'm not going to bother unless it's needed.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
|
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
/////////////////////////////////////////////////////////
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// LUA TABLE TYPES
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// We have modified the lua interpreter to allow us to assign
|
|
|
|
|
// table subtypes to different tables. Most lua tables are
|
|
|
|
|
// marked as 'general' tables. If you create a table using the
|
|
|
|
|
// lua newtable operator, you'll get a general table.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// Aside from general tables, we have special table types for the
|
|
|
|
|
// lua registry, for lua global environment tables, for tangibles,
|
|
|
|
|
// for tangible metatables, and for classes. These tables get
|
|
|
|
|
// handled specially in various parts of the code.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
|
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LIGHTUSERDATA AND CLASS LUATOKEN
|
|
|
|
|
//
|
|
|
|
|
// Before I tell you what tokens are, I'm going to tell you what problem
|
|
|
|
|
// I was trying to solve that led me to create tokens.
|
|
|
|
|
//
|
|
|
|
|
// I was trying to write a JSON to LUA converter. That's mostly
|
|
|
|
|
// straightforward. Json tables are very similar to lua tables.
|
|
|
|
|
// For example, consider this JSON:
|
|
|
|
|
//
|
|
|
|
|
// {
|
|
|
|
|
// "name": "John Smith",
|
|
|
|
|
// "alive": true,
|
|
|
|
|
// "address": {
|
|
|
|
|
// "city": "New York",
|
|
|
|
|
// "state": "NY",
|
|
|
|
|
// },
|
|
|
|
|
// "spouse": null
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// That converts very straightforwardly to a lua table:
|
|
|
|
|
//
|
|
|
|
|
// {
|
|
|
|
|
// name = "John Smith",
|
|
|
|
|
// alive = true,
|
|
|
|
|
// address = {
|
|
|
|
|
// city = "New York",
|
|
|
|
|
// state = "NY",
|
|
|
|
|
// },
|
|
|
|
|
// spouse = nil
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// There's only one problem here: that "spouse" line doesn't really work.
|
|
|
|
|
// Setting a key to nil in lua is the same as not setting that key at all.
|
|
|
|
|
// That's not correct. Instead of storing json null as lua nil,
|
|
|
|
|
// we could store json null as the string "null". But that would be make it
|
|
|
|
|
// impossible to parse and store the literal string "null". That's not correct
|
|
|
|
|
// either.
|
|
|
|
|
//
|
|
|
|
|
// Lua has a datatype called 'lightuserdata'. A lightuserdata holds an
|
2026-02-06 17:34:26 -05:00
|
|
|
// int64. That gives me an option: I can store json null as a
|
|
|
|
|
// lightuserdata. When we see this lightuserdata value, we would know
|
|
|
|
|
// we have a json null.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
|
|
|
|
// So that finally brings me to what a "token" is. A token is a lightuserdata
|
2026-02-19 23:35:38 -05:00
|
|
|
// containing a short string encoded as a fixed-width base38 number. Tokens
|
|
|
|
|
// may only contain the characters a-z, 0-9, and underscore, and can be up to
|
|
|
|
|
// 12 characters long (since 38^12 fits in 64 bits). In effect, it's a short
|
|
|
|
|
// string, but it's
|
2026-02-06 17:34:26 -05:00
|
|
|
// a string that's distinguishable from a normal lua string. It doesn't have
|
|
|
|
|
// the same type as a lua string (it shows up as a lightuserdata).
|
2024-03-20 14:29:20 -04:00
|
|
|
// The purpose of tokens is to represent special unique values, like json null.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2026-02-06 17:34:26 -05:00
|
|
|
// To make working with tokens easy, I've created a C++ struct 'LuaToken'.
|
2024-03-20 14:29:20 -04:00
|
|
|
// It stores an int64. You can construct a LuaToken in two different ways:
|
|
|
|
|
//
|
2026-02-19 23:35:38 -05:00
|
|
|
// LuaToken(0x559D0F68151CB900)
|
2024-03-20 14:29:20 -04:00
|
|
|
// LuaToken("null")
|
|
|
|
|
//
|
|
|
|
|
// Those are equivalent. The second form is just as fast as the first,
|
|
|
|
|
// because of C++ constexpr magic. You can use our automatic type conversion
|
|
|
|
|
// system to automatically convert C++ LuaToken structs into lua lightuserdata
|
|
|
|
|
// values, like this:
|
|
|
|
|
//
|
|
|
|
|
// LS.set(value, LuaToken("null"))
|
|
|
|
|
//
|
|
|
|
|
// When our pretty-printer is printing out lua data structures, and it
|
|
|
|
|
// encounters a lightuserdata, it prints it out as a token, ie, as a short
|
|
|
|
|
// string, but using brackets instead of quotation marks.
|
|
|
|
|
//
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LUA CLASSES
|
|
|
|
|
//
|
|
|
|
|
// This module adds the concept of a 'class' to lua. The function
|
|
|
|
|
// LuaStack::makeclass creates a class. This function is exposed to lua.
|
|
|
|
|
// For example, you could create a lua class to manipulate deques:
|
|
|
|
|
//
|
|
|
|
|
// makeclass("deque")
|
|
|
|
|
//
|
|
|
|
|
// This creates a new table, which you will find in the global
|
|
|
|
|
// environment under the name 'deque'. The class is a table for
|
|
|
|
|
// storing functions related to deques. You can now add lua functions
|
|
|
|
|
// to the class:
|
|
|
|
|
//
|
|
|
|
|
// function deque.insertr(...)
|
|
|
|
|
// end
|
|
|
|
|
//
|
|
|
|
|
// If you call makeclass a second time with the same classname, this
|
|
|
|
|
// is a no-op. This is useful because if you have two sourcefiles that
|
|
|
|
|
// both add functions to class 'deque', they can both makeclass 'deque',
|
|
|
|
|
// and no conflict will occur.
|
|
|
|
|
//
|
|
|
|
|
// Class tables have a distinct LuaTableType: LUA_TT_CLASS. That
|
|
|
|
|
// way, it is easy to tell that the table is intended as a class.
|
|
|
|
|
// Class tables are treated uniquely by our engine in several ways.
|
|
|
|
|
//
|
|
|
|
|
// A class also has a field "__index" which points to itself.
|
|
|
|
|
// C.__index = C. That makes it possible to use the class as a
|
|
|
|
|
// metatable, and any attempt to look up a name in the table
|
|
|
|
|
// that fails will then attempt to look up that name in the class.
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LUADEFINE
|
|
|
|
|
//
|
|
|
|
|
// LuaDefine is a macro that defines a C function which is exposed to lua.
|
|
|
|
|
// In addition to actually defining the C function, it also creates a global
|
|
|
|
|
// registry of all such functions. The registry is used to actually
|
|
|
|
|
// know which functions to inject into lua.
|
|
|
|
|
//
|
|
|
|
|
// Here is an example of a typical LuaDefine:
|
|
|
|
|
//
|
|
|
|
|
// LuaDefine(table_removevalue, "table, removethis",
|
|
|
|
|
// "|This function removes a specified value from a table."
|
|
|
|
|
// "|",
|
|
|
|
|
// "|Iterates over all the (key, val) pairs in a table and"
|
|
|
|
|
// "|removes the ones where val == removethis."
|
|
|
|
|
// ) {
|
|
|
|
|
// LuaArg table, removethis;
|
|
|
|
|
// LuaRet returnvalue;
|
|
|
|
|
// LuaVar key, val;
|
|
|
|
|
// LuaDefStack LS(L, table, removethis, returnvalue, key, val);
|
|
|
|
|
// LS.set(key, LuaNil);
|
|
|
|
|
// while (LS.next(table, key, val)) {
|
|
|
|
|
// if (LS.rawequal(val, removethis)) {
|
|
|
|
|
// LS.rawset(table, key, LuaNil);
|
|
|
|
|
// }
|
2021-01-02 13:31:18 -05:00
|
|
|
// }
|
2024-03-20 14:29:20 -04:00
|
|
|
// LS.set(returnvalue, table);
|
|
|
|
|
// return LS.result();
|
|
|
|
|
// }
|
|
|
|
|
//
|
|
|
|
|
// So you might notice that this is the same example function from
|
|
|
|
|
// earlier, but this time with LuaDefine. If you omit the LuaDefine
|
|
|
|
|
// and write the function as it was shown earlier, you will get a
|
|
|
|
|
// function that can be called from C++, and which works fine when
|
|
|
|
|
// called from C++, but it won't be visible from lua.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// The example function above will show up in lua as 'table.removevalue'.
|
|
|
|
|
// This lua function name is derived straightforwardly from the C++
|
|
|
|
|
// function name.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// Note that both of the string parameters to LuaDefine are part of the
|
|
|
|
|
// function documentation, neither has any effect on how the lua function
|
|
|
|
|
// behaves. The function documentation goes into the registry and from
|
|
|
|
|
// there is accessible through the lua documentation system.
|
2021-01-02 13:31:18 -05:00
|
|
|
//
|
|
|
|
|
/////////////////////////////////////////////////////////
|
2020-11-27 13:21:07 -05:00
|
|
|
|
|
|
|
|
|
2023-04-07 14:20:45 -04:00
|
|
|
#ifndef LUASTACK_HPP
|
|
|
|
|
#define LUASTACK_HPP
|
2020-11-27 13:21:07 -05:00
|
|
|
|
2024-03-13 15:39:09 -04:00
|
|
|
#include "util.hpp"
|
2022-02-23 23:08:28 -05:00
|
|
|
#include "wrap-string.hpp"
|
2022-07-22 16:04:14 -04:00
|
|
|
#include "wrap-set.hpp"
|
|
|
|
|
#include <cstring>
|
2023-04-13 12:14:27 -04:00
|
|
|
#include <type_traits>
|
2023-05-08 13:32:22 -04:00
|
|
|
#include <cassert>
|
2024-03-13 17:46:26 -04:00
|
|
|
#include <optional>
|
2022-02-23 23:08:28 -05:00
|
|
|
|
2020-11-27 13:21:07 -05:00
|
|
|
#include "lua.h"
|
2021-02-28 14:24:59 -05:00
|
|
|
#include "lauxlib.h"
|
2021-02-28 15:05:45 -05:00
|
|
|
#include "lualib.h"
|
2021-03-01 18:10:40 -05:00
|
|
|
#include "eris.h"
|
2020-11-27 13:21:07 -05:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LUA TABLE TYPES
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
enum LuaTableType {
|
|
|
|
|
LUA_TT_GENERAL = LUA_NUMTAGS,
|
|
|
|
|
LUA_TT_REGISTRY,
|
|
|
|
|
LUA_TT_GLOBALENV,
|
|
|
|
|
LUA_TT_TANGIBLE,
|
|
|
|
|
LUA_TT_TANGIBLEMETA,
|
|
|
|
|
LUA_TT_CLASS,
|
|
|
|
|
|
|
|
|
|
LUA_TT_SENTINEL
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LuaToken
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
struct LuaToken {
|
2025-02-05 14:08:44 -05:00
|
|
|
private:
|
2026-02-19 23:35:38 -05:00
|
|
|
// Encode a token string as a fixed-width base38 number.
|
|
|
|
|
// Each character is mapped to a digit 1-37 (0 means "no character"),
|
|
|
|
|
// and the result is: CH0*38^11 + CH1*38^10 + ... + CH11*38^0.
|
2026-02-18 23:23:59 -05:00
|
|
|
// This fixed-width encoding ensures that numeric ordering matches
|
|
|
|
|
// lexicographic ordering of the original strings.
|
2026-02-19 00:11:44 -05:00
|
|
|
//
|
2026-02-19 23:35:38 -05:00
|
|
|
// Digit mapping: _ → 1, 0-9 → 2-11, a-z → 12-37.
|
|
|
|
|
//
|
2026-02-19 00:11:44 -05:00
|
|
|
// WARNING: The Lua lexer in llex.c contains a duplicate of this
|
|
|
|
|
// encoding logic (in the '@' token literal case). If you change
|
|
|
|
|
// the encoding here, you must update llex.c to match.
|
2026-02-18 23:23:59 -05:00
|
|
|
// Returns zero if the string is empty, too long, or contains
|
|
|
|
|
// invalid characters.
|
2025-02-04 18:35:26 -05:00
|
|
|
//
|
2025-02-05 14:08:44 -05:00
|
|
|
static constexpr uint64_t parse(std::string_view str) {
|
2026-02-18 23:23:59 -05:00
|
|
|
if (str.size() > 12) return 0;
|
|
|
|
|
if (str.empty()) return 0;
|
2025-02-05 14:08:44 -05:00
|
|
|
|
2026-02-18 23:23:59 -05:00
|
|
|
uint64_t result = 0;
|
2025-02-05 14:08:44 -05:00
|
|
|
for (int i = 0; i < int(str.size()); i++) {
|
|
|
|
|
char c = str[i];
|
2025-02-04 18:35:26 -05:00
|
|
|
uint64_t digit = 0;
|
2026-02-19 23:35:38 -05:00
|
|
|
if (c == '_') {
|
|
|
|
|
digit = 1;
|
|
|
|
|
} else if ((c >= '0') && (c <= '9')) {
|
|
|
|
|
digit = uint64_t(c - '0') + 2;
|
2025-02-04 18:35:26 -05:00
|
|
|
} else if ((c >= 'a') && (c <= 'z')) {
|
2026-02-19 23:35:38 -05:00
|
|
|
digit = uint64_t(c - 'a') + 12;
|
2025-02-04 18:35:26 -05:00
|
|
|
} else if ((c >= 'A') && (c <= 'Z')) {
|
2026-02-19 23:35:38 -05:00
|
|
|
digit = uint64_t(c - 'A') + 12;
|
2025-02-04 18:35:26 -05:00
|
|
|
} else {
|
2026-02-18 23:23:59 -05:00
|
|
|
return 0;
|
2025-02-04 18:35:26 -05:00
|
|
|
}
|
2026-02-19 23:35:38 -05:00
|
|
|
result = result * 38 + digit;
|
2026-02-18 23:23:59 -05:00
|
|
|
}
|
|
|
|
|
// Pad remaining positions with zeros (no character).
|
|
|
|
|
for (int i = int(str.size()); i < 12; i++) {
|
2026-02-19 23:35:38 -05:00
|
|
|
result = result * 38;
|
2025-02-04 18:35:26 -05:00
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
public:
|
|
|
|
|
uint64_t value;
|
|
|
|
|
|
|
|
|
|
// Get rid of the default constructors.
|
|
|
|
|
//
|
|
|
|
|
template<class T>
|
|
|
|
|
LuaToken(T arg) = delete;
|
|
|
|
|
|
2025-02-05 14:08:44 -05:00
|
|
|
// Construct a token from a string.
|
|
|
|
|
//
|
2026-02-18 23:23:59 -05:00
|
|
|
// If the string is not a valid token, then this
|
2025-02-05 15:45:48 -05:00
|
|
|
// initializes the token to the empty token (zero)
|
2025-02-05 14:08:44 -05:00
|
|
|
//
|
|
|
|
|
LuaToken(std::string_view s) : value(parse(s)) {}
|
2025-02-05 15:45:48 -05:00
|
|
|
LuaToken(const eng::string &s) : value(parse(s)) {}
|
2025-02-05 14:08:44 -05:00
|
|
|
|
|
|
|
|
// Construct a token from a compile-time constant string.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2025-02-05 14:08:44 -05:00
|
|
|
// It appears that the code below throws an exception if the
|
2025-02-05 15:45:48 -05:00
|
|
|
// string not parseable. But in reality, since this function is
|
2025-02-05 14:08:44 -05:00
|
|
|
// consteval (evaluated at compile time), the error is
|
|
|
|
|
// generated during the compilation.
|
2025-02-04 18:35:26 -05:00
|
|
|
//
|
2025-02-05 14:08:44 -05:00
|
|
|
consteval LuaToken(const char *s) : value(parse(s)) {
|
2025-02-05 15:45:48 -05:00
|
|
|
if (empty()) throw "cannot parse token";
|
2025-02-04 18:35:26 -05:00
|
|
|
}
|
2024-03-20 14:29:20 -04:00
|
|
|
|
|
|
|
|
// Construct a token from an int64.
|
|
|
|
|
//
|
|
|
|
|
LuaToken(uint64_t v) : value(v) {}
|
|
|
|
|
|
|
|
|
|
// Construct a token from a void pointer.
|
|
|
|
|
//
|
|
|
|
|
LuaToken(void *v) : value((uint64_t)v) {}
|
|
|
|
|
|
|
|
|
|
// Default constructor: construct the empty token.
|
|
|
|
|
//
|
|
|
|
|
LuaToken() : value(0) {}
|
|
|
|
|
|
2025-02-04 18:35:26 -05:00
|
|
|
// Assignment operator.
|
|
|
|
|
void operator =(const LuaToken &other) { value = other.value; }
|
|
|
|
|
|
2025-02-05 15:45:48 -05:00
|
|
|
// Empty: return true if the token is zero.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2025-02-05 15:45:48 -05:00
|
|
|
constexpr bool empty() const { return value == 0; }
|
2024-03-20 14:29:20 -04:00
|
|
|
|
|
|
|
|
// Compare two tokens for equality.
|
|
|
|
|
//
|
|
|
|
|
bool operator ==(const LuaToken &other) const { return value == other.value; }
|
|
|
|
|
|
|
|
|
|
// Convert the token to a void pointer.
|
|
|
|
|
//
|
|
|
|
|
void *voidvalue() const { return (void*)value; }
|
|
|
|
|
|
|
|
|
|
// Convert the token to a string.
|
|
|
|
|
//
|
2025-02-04 18:35:26 -05:00
|
|
|
// The conversion to string consists of expressing the value
|
2025-02-05 15:45:48 -05:00
|
|
|
// in base 36.
|
2025-02-04 18:35:26 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
eng::string str() const;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LuaSlots.
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2022-03-02 14:52:51 -05:00
|
|
|
class LuaSlot : public eng::nevernew {
|
2020-11-27 13:21:07 -05:00
|
|
|
protected:
|
|
|
|
|
int index_;
|
2023-04-13 13:26:45 -04:00
|
|
|
|
|
|
|
|
constexpr LuaSlot(int n) : index_(n) {}
|
|
|
|
|
|
2020-11-27 13:21:07 -05:00
|
|
|
public:
|
2024-03-20 14:29:20 -04:00
|
|
|
// LuaSlots are normally constructed without arguments.
|
|
|
|
|
// They are uninitialized until the LuaStack constructor runs.
|
|
|
|
|
//
|
2023-04-13 13:26:45 -04:00
|
|
|
constexpr LuaSlot() : index_(0) {}
|
2020-11-27 13:21:07 -05:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// You can fetch the stack index from the LuaSlot.
|
|
|
|
|
//
|
2023-04-13 13:26:45 -04:00
|
|
|
inline int index() const {
|
2020-11-27 13:21:07 -05:00
|
|
|
return index_;
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
private:
|
|
|
|
|
// Our code can fetch the stack index using an implicit conversion.
|
|
|
|
|
//
|
|
|
|
|
inline operator int() const {
|
|
|
|
|
return index_;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-06 14:05:12 -04:00
|
|
|
friend class LuaCoreStack;
|
2023-04-07 12:58:05 -04:00
|
|
|
friend class LuaDefStack;
|
2023-04-06 14:05:12 -04:00
|
|
|
friend class LuaExtStack;
|
2025-01-21 20:20:54 -05:00
|
|
|
friend class LuaKeywordParser;
|
2020-11-27 13:21:07 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class LuaArg : public LuaSlot {};
|
|
|
|
|
class LuaRet : public LuaSlot {};
|
|
|
|
|
class LuaVar : public LuaSlot {};
|
|
|
|
|
|
2020-12-05 18:57:53 -05:00
|
|
|
class LuaSpecial : public LuaSlot {
|
|
|
|
|
public:
|
2023-04-13 13:26:45 -04:00
|
|
|
constexpr LuaSpecial(int n) : LuaSlot(n) {}
|
2020-12-05 18:57:53 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
extern LuaSpecial LuaRegistry;
|
2020-11-27 13:21:07 -05:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LuaExtraArgs
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-04-13 13:26:45 -04:00
|
|
|
class LuaExtraArgs {
|
|
|
|
|
private:
|
|
|
|
|
int index_;
|
|
|
|
|
int size_;
|
|
|
|
|
|
|
|
|
|
public:
|
2025-01-20 21:11:59 -05:00
|
|
|
LuaExtraArgs() : index_(0), size_(0) {}
|
|
|
|
|
LuaExtraArgs(int i, int s) : index_(i), size_(s) {}
|
2023-04-13 13:26:45 -04:00
|
|
|
|
|
|
|
|
LuaSpecial operator[] (int n) const { return LuaSpecial(index_ + n); }
|
|
|
|
|
int size() const { return size_; }
|
|
|
|
|
|
|
|
|
|
friend class LuaCoreStack;
|
|
|
|
|
friend class LuaDefStack;
|
2020-11-27 13:21:07 -05:00
|
|
|
};
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LuaNil and LuaNewTable
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
2023-04-13 13:26:45 -04:00
|
|
|
|
2020-12-05 18:57:53 -05:00
|
|
|
class LuaNilMarker {};
|
|
|
|
|
extern LuaNilMarker LuaNil;
|
|
|
|
|
|
2021-01-02 13:31:18 -05:00
|
|
|
class LuaNewTableMarker {};
|
|
|
|
|
extern LuaNewTableMarker LuaNewTable;
|
|
|
|
|
|
2023-04-07 14:20:45 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LuaCoreStack
|
|
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// This is the common base class for LuaDefStack and LuaExtStack.
|
|
|
|
|
// You should use one of those classes in your code, not this class.
|
|
|
|
|
// However, this class is where all the interesting operators on lua
|
|
|
|
|
// local variables resides.
|
2023-04-07 14:20:45 -04:00
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-04-06 14:05:12 -04:00
|
|
|
class LuaCoreStack : public eng::nevernew {
|
|
|
|
|
protected:
|
2020-11-27 13:21:07 -05:00
|
|
|
lua_State *L_;
|
2023-04-07 14:20:45 -04:00
|
|
|
|
2020-11-27 13:21:07 -05:00
|
|
|
public:
|
2024-03-20 14:29:20 -04:00
|
|
|
// Constructor. You should almost never use this, instead,
|
|
|
|
|
// you should construct a LuaDefStack or a LuaExtStack.
|
|
|
|
|
//
|
2023-04-07 14:20:45 -04:00
|
|
|
LuaCoreStack(lua_State *L) : L_(L) {}
|
2022-03-31 17:15:15 -04:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Get the raw pointer to the lua_State.
|
|
|
|
|
//
|
|
|
|
|
lua_State *state() const { return L_; }
|
2022-02-25 19:57:23 -05:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Turn a Lua value into a C++ value, if possible.
|
|
|
|
|
//
|
2024-03-13 17:46:26 -04:00
|
|
|
// If the lua value doesn't match the desired type, then these return
|
2024-03-20 14:29:20 -04:00
|
|
|
// an empty optional. The ones that return bool only verify
|
2024-03-13 17:46:26 -04:00
|
|
|
// the value's type, they don't actually fetch the value.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2024-03-13 17:46:26 -04:00
|
|
|
std::optional<bool> tryboolean(LuaSlot s) const;
|
|
|
|
|
std::optional<lua_Integer> tryinteger(LuaSlot s) const;
|
|
|
|
|
std::optional<int> tryint(LuaSlot s) const;
|
|
|
|
|
std::optional<lua_Number> trynumber(LuaSlot s) const;
|
|
|
|
|
std::optional<eng::string> trystring(LuaSlot s) const;
|
|
|
|
|
std::optional<std::string_view> trystringview(LuaSlot s) const;
|
|
|
|
|
std::optional<lua_State*> trythread(LuaSlot s) const;
|
|
|
|
|
std::optional<LuaToken> trytoken(LuaSlot s) const;
|
|
|
|
|
std::optional<util::DXYZ> tryxyz(LuaSlot s) const;
|
|
|
|
|
bool trytable(LuaSlot s) const;
|
|
|
|
|
bool trynil(LuaSlot s) const;
|
|
|
|
|
bool tryfunction(LuaSlot s) const;
|
|
|
|
|
bool trycfunction(LuaSlot s) const;
|
|
|
|
|
bool trytangible(LuaSlot s) const;
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Turn a lua value into a C++ value, or throw an error.
|
|
|
|
|
//
|
2024-03-13 17:46:26 -04:00
|
|
|
// If the lua value doesn't match the desired type,
|
2024-03-20 14:29:20 -04:00
|
|
|
// then these throw a lua error. It is invalid to use these
|
|
|
|
|
// outside of a protected context. The argname is used
|
2024-03-13 17:46:26 -04:00
|
|
|
// for making a nice error message.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2024-03-13 17:46:26 -04:00
|
|
|
bool ckboolean(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
lua_Integer ckinteger(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
int ckint(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
lua_Number cknumber(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
eng::string ckstring(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
std::string_view ckstringview(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
lua_State * ckthread(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
LuaToken cktoken(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
util::DXYZ ckxyz(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
void cktable(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
void cknil(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
void ckfunction(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
void ckcfunction(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
void cktangible(LuaSlot s, const char *argname = "value") const;
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Check if a lua value can be converted to C++.
|
|
|
|
|
//
|
2024-03-13 17:46:26 -04:00
|
|
|
// These functions check if a value can be converted
|
|
|
|
|
// to a C++ value. They don't actually return the C++ value.
|
|
|
|
|
// It is more efficient to use the 'try' or 'ck' functions above if
|
2024-03-20 14:29:20 -04:00
|
|
|
// you also want the value.
|
|
|
|
|
//
|
|
|
|
|
bool isboolean(LuaSlot s) const { return lua_type(L_, s) == LUA_TBOOLEAN; }
|
|
|
|
|
bool isinteger(LuaSlot s) const { return bool(tryinteger(s)); }
|
|
|
|
|
bool isint(LuaSlot s) const { return bool(tryint(s)); }
|
|
|
|
|
bool isnumber(LuaSlot s) const { return lua_type(L_, s) == LUA_TNUMBER; }
|
|
|
|
|
bool isstring(LuaSlot s) const { return lua_type(L_, s) == LUA_TSTRING; }
|
|
|
|
|
bool isstringview(LuaSlot s) const { return lua_type(L_, s) == LUA_TSTRING; }
|
|
|
|
|
bool isthread(LuaSlot s) const { return lua_type(L_, s) == LUA_TTHREAD; }
|
|
|
|
|
bool istoken(LuaSlot s) const { return lua_type(L_, s) == LUA_TLIGHTUSERDATA; }
|
|
|
|
|
bool isxyz(LuaSlot s) const { return bool(tryxyz(s)); }
|
|
|
|
|
bool istable(LuaSlot s) const { return lua_istable(L_, s); }
|
|
|
|
|
bool isnil(LuaSlot s) const { return lua_isnil(L_, s); }
|
|
|
|
|
bool isfunction(LuaSlot s) const { return lua_isfunction(L_, s); }
|
|
|
|
|
bool iscfunction(LuaSlot s) const { return lua_iscfunction(L_, s); }
|
|
|
|
|
bool istangible(LuaSlot s) const { return bool(trytangible(s)); }
|
|
|
|
|
|
|
|
|
|
// Create a new interpreter using the specified allocator.
|
|
|
|
|
//
|
|
|
|
|
// Typically, the allocator used would be eng::l_alloc.
|
|
|
|
|
// You can also pass nullptr to use a default allocator based
|
|
|
|
|
// on malloc.
|
|
|
|
|
//
|
|
|
|
|
static lua_State *newstate (lua_Alloc allocf);
|
|
|
|
|
|
|
|
|
|
// Create a new thread.
|
|
|
|
|
//
|
|
|
|
|
// The target parameter is an output parameter, this will contain
|
|
|
|
|
// the new thread. Also returns a C++ pointer to the thread. Remember
|
|
|
|
|
// that the C++ pointer by itself doesn't prevent garbage collection,
|
|
|
|
|
// you must keep the thread in the LuaSlot or in some other lua data
|
|
|
|
|
// structure to prevent it from getting garbage collected.
|
|
|
|
|
//
|
|
|
|
|
lua_State *newthread(LuaSlot target) const;
|
|
|
|
|
|
|
|
|
|
// Get the type of a LuaSlot.
|
|
|
|
|
//
|
|
|
|
|
// Returns one of the standard lua type tags. These include:
|
|
|
|
|
//
|
|
|
|
|
// LUA_TBOOLEAN, LUA_TNUMBER, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION,
|
|
|
|
|
// LUA_TTHREAD, LUA_TLIGHTUSERDATA, LUA_TUSERDATA.
|
|
|
|
|
//
|
|
|
|
|
int type(LuaSlot s) const { return lua_type(L_, s); }
|
2026-02-17 19:57:58 -05:00
|
|
|
const char *typestr(LuaSlot s) const { return lua_typename(L_, lua_type(L_, s)); }
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Get the extended type of a LuaSlot.
|
|
|
|
|
//
|
|
|
|
|
// If the variable contains a table, returns one of the LuaTableType
|
|
|
|
|
// constants. Search for this enum above. If it is not a table,
|
|
|
|
|
// returns one of the standard lua type tags. See the 'type'
|
|
|
|
|
// method above.
|
|
|
|
|
//
|
|
|
|
|
int xtype(LuaSlot slot) const;
|
|
|
|
|
|
|
|
|
|
// Get the table type of a lua table.
|
|
|
|
|
//
|
|
|
|
|
// Tab must contain a lua table. Returns a value from enum LuaTableType.
|
|
|
|
|
//
|
|
|
|
|
int gettabletype(LuaSlot tab) const;
|
|
|
|
|
|
|
|
|
|
// Set the table type of a lua table.
|
|
|
|
|
//
|
|
|
|
|
// Tab must contain a lua table. T must be a value from enum LuaTableType.
|
|
|
|
|
//
|
|
|
|
|
void settabletype(LuaSlot tab, int t) const;
|
|
|
|
|
|
|
|
|
|
// Get the length of a lua string.
|
|
|
|
|
//
|
|
|
|
|
// Techically, you can also use this on tables, but it's not recommended.
|
|
|
|
|
// Instead, use 'nkeys' below. The semantics of rawlen on tables
|
|
|
|
|
// is just plain weird: see lua documentation if you are curious.
|
|
|
|
|
//
|
|
|
|
|
int rawlen(LuaSlot val) const;
|
2024-03-13 17:46:26 -04:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Get the number of key/value pairs in a lua table.
|
|
|
|
|
//
|
|
|
|
|
// This works on any table, even tables where the keys aren't integers.
|
|
|
|
|
//
|
|
|
|
|
int nkeys(LuaSlot tab) const;
|
2024-03-13 17:46:26 -04:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Get the metatable of a table.
|
|
|
|
|
//
|
|
|
|
|
// Tab must be a table. The metatable of tab is stored in mt.
|
|
|
|
|
//
|
|
|
|
|
bool getmetatable(LuaSlot mt, LuaSlot tab) const;
|
2024-03-13 17:46:26 -04:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Set the metatable of a table.
|
|
|
|
|
//
|
|
|
|
|
// Tab must be a table. Mt must be a table or nil.
|
|
|
|
|
//
|
|
|
|
|
void setmetatable(LuaSlot tab, LuaSlot mt) const;
|
2024-03-13 17:46:26 -04:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Remove the metatable from a table.
|
|
|
|
|
//
|
|
|
|
|
// Tab must be a table. The metatable, if any, is removed from tab.
|
|
|
|
|
//
|
2020-11-27 14:24:37 -05:00
|
|
|
void clearmetatable(LuaSlot tab) const;
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Create a new table.
|
|
|
|
|
//
|
|
|
|
|
// The new table is stored in target.
|
|
|
|
|
//
|
2020-11-27 14:24:37 -05:00
|
|
|
void newtable(LuaSlot target) const;
|
2024-03-20 14:29:20 -04:00
|
|
|
|
|
|
|
|
// Create a new table with a storage hint.
|
|
|
|
|
//
|
|
|
|
|
// The new table is stored in target. The new table has space
|
|
|
|
|
// pre-allocated for narr array elements and nrec non-array elements.
|
|
|
|
|
//
|
2021-07-03 19:49:55 -04:00
|
|
|
void createtable(LuaSlot target, int narr, int nrec) const;
|
2024-03-20 14:29:20 -04:00
|
|
|
|
|
|
|
|
// Get the global environment table.
|
|
|
|
|
//
|
|
|
|
|
// The global environment table is stored in gltab.
|
|
|
|
|
//
|
2021-02-28 16:32:42 -05:00
|
|
|
void getglobaltable(LuaSlot gltab) const;
|
2024-03-20 14:29:20 -04:00
|
|
|
|
|
|
|
|
// Delete everything in a table.
|
|
|
|
|
//
|
|
|
|
|
// Tab must be a table. Removes all (key,val) pairs from tab.
|
|
|
|
|
// If clearmeta is true, then the metatable is also removed from tab.
|
|
|
|
|
//
|
2021-10-25 14:47:37 -04:00
|
|
|
void cleartable(LuaSlot tab, bool clearmeta) const;
|
2021-02-09 17:15:54 -05:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Iterate over the key,val pairs in a table.
|
|
|
|
|
//
|
|
|
|
|
// Before the iteration begins, you should initialize 'key' to nil.
|
|
|
|
|
// Then, you should call 'next' to fetch the next key,val pair in
|
|
|
|
|
// the table. You can keep calling 'next' to obtain successive key,val
|
|
|
|
|
// pairs until 'next' returns false.
|
|
|
|
|
//
|
|
|
|
|
// Do not alter 'key' during the iteration, if you do, then the 'next'
|
|
|
|
|
// function will return the wrong next-value.
|
|
|
|
|
//
|
|
|
|
|
bool next(LuaSlot tab, LuaSlot key, LuaSlot val) const;
|
|
|
|
|
|
2025-12-03 19:55:53 -05:00
|
|
|
// Compile lua code.
|
|
|
|
|
//
|
|
|
|
|
// If the code contains a syntax error, then the result variable
|
|
|
|
|
// is set to the error message, and the error message is returned.
|
|
|
|
|
//
|
|
|
|
|
// If the code is valid, then the result variable is set to a
|
|
|
|
|
// closure, and an empty string is returned.
|
|
|
|
|
//
|
|
|
|
|
// If a syntax error occurs, the error message may contain the
|
|
|
|
|
// token <eof>. If so, the problem is an incomplete expression.
|
|
|
|
|
//
|
|
|
|
|
eng::string load(LuaSlot result, std::string_view code, std::string_view context);
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Return true if the int64 can be stored losslessly in a lua_Number.
|
|
|
|
|
//
|
|
|
|
|
// Lua numbers are actually double-precision floating point. double
|
|
|
|
|
// can hold integers losslessly as long as they're small enough to
|
|
|
|
|
// fit within the double's mantissa. The mantissa of an IEEE double
|
|
|
|
|
// is big enough to hold a 53-bit integer.
|
|
|
|
|
//
|
|
|
|
|
static bool validinteger(int64_t value) { return (value <= MAXINT) && (value >= -MAXINT); }
|
2023-04-14 14:52:44 -04:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Return true if the int64 can be stored losslessly and is positive.
|
|
|
|
|
//
|
|
|
|
|
// This returns true if the number is a validinteger (see above), and is
|
|
|
|
|
// a positive number.
|
|
|
|
|
//
|
|
|
|
|
static bool validpositiveinteger(int64_t value) { return (value <= MAXINT) && (value >= 1); }
|
2020-11-27 13:21:07 -05:00
|
|
|
|
2026-02-21 23:58:33 -05:00
|
|
|
// Get the class table and class name of X.
|
|
|
|
|
//
|
|
|
|
|
// The object passed in can be:
|
|
|
|
|
//
|
|
|
|
|
// * A valid class table.
|
|
|
|
|
// * A valid, existing class name.
|
|
|
|
|
// * A tangible that has a class.
|
|
|
|
|
// * A normal table with a class metatable.
|
|
|
|
|
//
|
|
|
|
|
// On success, sets classtab, classname, and clears error.
|
|
|
|
|
// On failure, sets classtab to nil, clears classname, and sets error.
|
|
|
|
|
//
|
|
|
|
|
void getclassinfo(LuaSlot classtab, eng::string &classname, eng::string &error, LuaSlot input) const;
|
|
|
|
|
|
2026-02-21 23:37:55 -05:00
|
|
|
// Get the class name of X.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2026-02-21 23:37:55 -05:00
|
|
|
// The object passed in can be:
|
|
|
|
|
//
|
|
|
|
|
// * A valid class table.
|
|
|
|
|
// * A valid, existing class name.
|
|
|
|
|
// * A tangible that has a class.
|
|
|
|
|
// * A normal table with a class metatable.
|
|
|
|
|
//
|
|
|
|
|
// If the object is none of these, returns empty string.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2022-02-24 02:17:41 -05:00
|
|
|
eng::string classname(LuaSlot x) const;
|
2021-12-27 16:44:12 -05:00
|
|
|
|
2026-02-21 23:37:55 -05:00
|
|
|
// Get the class table of X.
|
|
|
|
|
//
|
|
|
|
|
// The object passed in can be:
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2026-02-21 23:37:55 -05:00
|
|
|
// * A valid class table.
|
|
|
|
|
// * A valid, existing class name.
|
|
|
|
|
// * A tangible that has a class.
|
2026-02-21 23:58:33 -05:00
|
|
|
// * A normal table with a class metatable.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2026-02-21 23:58:33 -05:00
|
|
|
// If there is a problem, returns an error message
|
|
|
|
|
// and sets tab to nil.
|
2026-02-21 23:37:55 -05:00
|
|
|
//
|
|
|
|
|
eng::string getclass(LuaSlot tab, LuaSlot obj) const;
|
2022-02-25 19:57:23 -05:00
|
|
|
eng::string getclass(LuaSlot tab, std::string_view name) const;
|
2021-12-27 16:44:12 -05:00
|
|
|
|
|
|
|
|
// Create a class, or look up an existing class.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2026-02-21 23:37:55 -05:00
|
|
|
// Creates a new class, unless the class already exists.
|
|
|
|
|
// Stores the class in the global environment table, and
|
|
|
|
|
// in the class database. This routine assert-fails if
|
|
|
|
|
// the parameter is not a valid classname.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
2021-08-23 23:34:30 -04:00
|
|
|
void makeclass(LuaSlot tab, LuaSlot name) const;
|
2022-02-25 19:57:23 -05:00
|
|
|
void makeclass(LuaSlot tab, std::string_view name) const;
|
2021-11-26 12:28:59 -05:00
|
|
|
|
2023-03-16 23:31:29 -04:00
|
|
|
// Create a tangible, or look up an existing tangible.
|
2024-02-28 14:19:59 -05:00
|
|
|
//
|
2024-03-20 14:29:20 -04:00
|
|
|
// If the tangible doesn't exist yet, this creates a tangible stub.
|
|
|
|
|
// A stub tangible is an empty table with a metatable containing the
|
|
|
|
|
// tangible's ID. Nothing else is present in the stub. It is up to
|
|
|
|
|
// the World module to transform stubs into full-blown tangibles.
|
|
|
|
|
// Assert-fails if the tangible ID is not a validpositiveinteger.
|
2024-02-28 14:19:59 -05:00
|
|
|
//
|
2023-03-16 23:31:29 -04:00
|
|
|
void maketan(LuaSlot tab, int64_t id) const;
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Return true if a tangible is empty, ie, a stub.
|
|
|
|
|
//
|
2023-03-16 23:31:29 -04:00
|
|
|
bool tanblank(LuaSlot tab) const;
|
|
|
|
|
|
|
|
|
|
// Get the ID of a tangible.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
|
|
|
|
// This works on both full-blown tangibles and stubs. If tab
|
|
|
|
|
// is not a valid tangible, returns zero.
|
|
|
|
|
//
|
2021-11-26 12:28:59 -05:00
|
|
|
int64_t tanid(LuaSlot tab) const;
|
|
|
|
|
|
2024-02-28 14:19:59 -05:00
|
|
|
// Get the class of a tangible.
|
2024-03-20 14:29:20 -04:00
|
|
|
//
|
|
|
|
|
// If the tangible has been assigned a class, then puts the class
|
|
|
|
|
// table into classobj and returns true. Otherwise, sets classobj
|
|
|
|
|
// to nil and returns false.
|
|
|
|
|
//
|
2024-02-28 14:19:59 -05:00
|
|
|
bool tangetclass(LuaSlot classobj, LuaSlot tan);
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Assign a lua variable.
|
|
|
|
|
//
|
|
|
|
|
// Copies value into target. The 'value' parameter can be a LuaSlot or
|
|
|
|
|
// any lua-convertible C++ type.
|
|
|
|
|
//
|
|
|
|
|
template<typename VT>
|
|
|
|
|
void set(LuaSlot target, VT value) const {
|
|
|
|
|
push_any_value(value);
|
|
|
|
|
lua_replace(L_, target);
|
2021-01-02 13:31:18 -05:00
|
|
|
}
|
2021-02-10 16:22:24 -05:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Return true if two values are equal.
|
|
|
|
|
//
|
|
|
|
|
// Checks if the two values are equal. Note that in lua, if two strings
|
|
|
|
|
// contain the same text, then they're equal. The 'value' parameter
|
|
|
|
|
// can be a LuaSlot or any lua-convertible C++ type.
|
|
|
|
|
//
|
|
|
|
|
// This could possibly be faster if we were to write some specializations
|
|
|
|
|
// for strings, numbers, and bools.
|
|
|
|
|
//
|
2023-03-05 01:51:25 -05:00
|
|
|
template<typename VT>
|
|
|
|
|
bool rawequal(LuaSlot v1, VT value) const {
|
|
|
|
|
push_any_value(value);
|
2021-02-28 14:45:09 -05:00
|
|
|
bool result = lua_rawequal(L_, v1, -1);
|
2021-02-10 16:22:24 -05:00
|
|
|
lua_pop(L_, 1);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2021-02-07 17:26:48 -05:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Return true if val1 is less than val2.
|
|
|
|
|
//
|
|
|
|
|
// This is NOT the same as the lua_lessthan function. This is a more
|
|
|
|
|
// general function that can compare any two lua objects.
|
|
|
|
|
//
|
|
|
|
|
// Numbers are compared in the obvious numeric manner.
|
|
|
|
|
// Strings are compared alphabetically.
|
|
|
|
|
// Booleans are compared with false being less than true.
|
|
|
|
|
// Tables are all considered equal to other tables.
|
|
|
|
|
// Functions are all considered equal to other functions.
|
|
|
|
|
// Coroutines are all considered equal to other coroutines.
|
|
|
|
|
//
|
|
|
|
|
// Numbers are less than strings.
|
|
|
|
|
// Strings are less than booleans.
|
|
|
|
|
// Booleans are less than functions.
|
|
|
|
|
// Functions are less than coroutines.
|
|
|
|
|
// Coroutines are less than tables.
|
|
|
|
|
//
|
|
|
|
|
// Does not call metamethods.
|
|
|
|
|
//
|
|
|
|
|
bool genlt(LuaSlot val1, LuaSlot val2) const { return lua_genlt(L_, val1, val2); }
|
|
|
|
|
|
|
|
|
|
// Return true if the value is a sortable key.
|
|
|
|
|
//
|
|
|
|
|
// Sortable keys are: strings, booleans, and numbers.
|
|
|
|
|
// These three types can be meaningfully compared using genlt,
|
|
|
|
|
// above. They also can be meaningfully transferred from lua to C++
|
|
|
|
|
// and back without losing anything.
|
|
|
|
|
//
|
|
|
|
|
bool issortablekey(LuaSlot s) const;
|
|
|
|
|
|
|
|
|
|
// Move a sortable key to another lua environment.
|
|
|
|
|
//
|
|
|
|
|
// This is used when you've created two lua interpreters and you
|
|
|
|
|
// want to move data from one to the other. The only kinds of data
|
|
|
|
|
// you can move are strings, booleans, and numbers.
|
|
|
|
|
//
|
|
|
|
|
void movesortablekey(LuaSlot val, LuaCoreStack &other, LuaSlot otherslot);
|
|
|
|
|
|
|
|
|
|
// Fetch a value from a table.
|
|
|
|
|
//
|
|
|
|
|
// Fetches the specified key from the table tab, and stores the
|
|
|
|
|
// result in target. The key parameter can be a LuaSlot or any lua-
|
|
|
|
|
// convertible C++ value.
|
|
|
|
|
//
|
2021-12-27 17:03:42 -05:00
|
|
|
template<typename KT>
|
|
|
|
|
void rawget(LuaSlot target, LuaSlot tab, KT key) const {
|
2020-12-05 18:57:53 -05:00
|
|
|
push_any_value(key);
|
2020-11-27 13:21:07 -05:00
|
|
|
lua_rawget(L_, tab);
|
2021-12-27 17:03:42 -05:00
|
|
|
lua_replace(L_, target);
|
2020-11-27 13:21:07 -05:00
|
|
|
}
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Store a value in a table.
|
|
|
|
|
//
|
|
|
|
|
// Sets the table entry for key to value. The key and val
|
|
|
|
|
// parameters can be LuaSlots, or they can be any lua-convertible
|
|
|
|
|
// C++ value.
|
|
|
|
|
//
|
2020-12-05 18:57:53 -05:00
|
|
|
template<typename KT, typename VT>
|
|
|
|
|
void rawset(LuaSlot tab, KT key, VT value) const {
|
|
|
|
|
push_any_value(key);
|
|
|
|
|
push_any_value(value);
|
2020-11-27 13:21:07 -05:00
|
|
|
lua_rawset(L_, tab);
|
|
|
|
|
}
|
2021-02-20 19:17:20 -05:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Get the 'visited' bit from a lua table.
|
|
|
|
|
//
|
|
|
|
|
bool getvisited(LuaSlot tab) const;
|
|
|
|
|
|
|
|
|
|
// Set the 'visited' bit in a lua table.
|
|
|
|
|
//
|
|
|
|
|
void setvisited(LuaSlot tab, bool visited) const;
|
|
|
|
|
|
|
|
|
|
// This is the largest integer that can be stored in a lua_Number.
|
|
|
|
|
// In other words, any 53-bit number can be stored.
|
|
|
|
|
//
|
|
|
|
|
static const int64_t MAXINT = 0x001FFFFFFFFFFFFF;
|
|
|
|
|
|
|
|
|
|
// Template Specializations.
|
|
|
|
|
//
|
|
|
|
|
// These are all specializations of functions that are defined above.
|
|
|
|
|
// These are typically here purely to make the functions above faster.
|
|
|
|
|
//
|
|
|
|
|
void set(LuaSlot target, LuaSlot value) const {
|
|
|
|
|
lua_copy(L_, value, target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool rawequal(LuaSlot v1, LuaSlot v2) const {
|
|
|
|
|
return lua_rawequal(L_, v1, v2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void rawget(LuaSlot target, LuaSlot tab, int key) const {
|
|
|
|
|
lua_rawgeti(L_, tab, key);
|
|
|
|
|
lua_replace(L_, target);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-03 19:49:55 -04:00
|
|
|
template<typename VT>
|
2021-11-17 15:11:55 -05:00
|
|
|
void rawset(LuaSlot tab, int key, VT value) const {
|
2021-07-03 19:49:55 -04:00
|
|
|
push_any_value(value);
|
|
|
|
|
lua_rawseti(L_, tab, key);
|
|
|
|
|
}
|
2021-08-23 23:34:30 -04:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
protected:
|
2021-08-10 10:41:06 -04:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Assign slots: this is used by the LuaDefStack and LuaExtStack
|
|
|
|
|
// constructors to assign stack indices to LuaSlots.
|
|
|
|
|
//
|
|
|
|
|
template<class... SS>
|
|
|
|
|
inline void vassign_slots(int retp, int argp, int varp, int extrap, int extrac, LuaRet &v, SS & ... stackslots) {
|
|
|
|
|
v.index_ = retp;
|
|
|
|
|
vassign_slots(retp+1, argp, varp, extrap, extrac, stackslots...);
|
|
|
|
|
}
|
|
|
|
|
template<class... SS>
|
|
|
|
|
inline void vassign_slots(int retp, int argp, int varp, int extrap, int extrac, LuaArg &v, SS & ... stackslots) {
|
|
|
|
|
v.index_ = argp;
|
|
|
|
|
vassign_slots(retp, argp+1, varp, extrap, extrac, stackslots...);
|
|
|
|
|
}
|
|
|
|
|
template<class... SS>
|
|
|
|
|
inline void vassign_slots(int retp, int argp, int varp, int extrap, int extrac, LuaVar &v, SS & ... stackslots) {
|
|
|
|
|
v.index_ = varp;
|
|
|
|
|
vassign_slots(retp, argp, varp+1, extrap, extrac, stackslots...);
|
|
|
|
|
}
|
|
|
|
|
template<class... SS>
|
|
|
|
|
inline void vassign_slots(int retp, int argp, int varp, int extrap, int extrac, LuaExtraArgs &v, SS & ... stackslots) {
|
|
|
|
|
v.index_ = extrap;
|
|
|
|
|
v.size_ = extrac;
|
|
|
|
|
vassign_slots(retp, argp, varp, extrap, extrac, stackslots...);
|
|
|
|
|
}
|
|
|
|
|
inline void vassign_slots(int retp, int argp, int varp, int extrap, int extrac) {}
|
2022-06-06 23:03:26 -04:00
|
|
|
|
2023-03-01 16:07:13 -05:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
// Push any value on the stack, by type.
|
|
|
|
|
//
|
|
|
|
|
inline void push_any_value(LuaNewTableMarker s) const { lua_newtable(L_); }
|
|
|
|
|
inline void push_any_value(LuaNilMarker s) const { lua_pushnil(L_); }
|
|
|
|
|
inline void push_any_value(LuaSlot s) const { lua_pushvalue(L_, s); }
|
|
|
|
|
inline void push_any_value(const eng::string &s) const { lua_pushlstring(L_, s.c_str(), s.size()); }
|
|
|
|
|
inline void push_any_value(std::string_view s) const { lua_pushlstring(L_, s.data(), s.size()); }
|
|
|
|
|
inline void push_any_value(const char *s) const { lua_pushstring(L_, s); }
|
|
|
|
|
inline void push_any_value(float s) const { lua_pushnumber(L_, s); }
|
|
|
|
|
inline void push_any_value(double s) const { lua_pushnumber(L_, s); }
|
|
|
|
|
inline void push_any_value(int s) const { lua_pushinteger(L_, s); }
|
|
|
|
|
inline void push_any_value(lua_Integer s) const { lua_pushinteger(L_, s); }
|
|
|
|
|
inline void push_any_value(lua_CFunction s) const { lua_pushcfunction(L_, s); }
|
|
|
|
|
inline void push_any_value(bool b) const { lua_pushboolean(L_, b ? 1:0); }
|
|
|
|
|
inline void push_any_value(LuaToken token) const { lua_pushlightuserdata(L_, (void*)(token.value)); }
|
|
|
|
|
|
|
|
|
|
// Push multiple values on the stack, in order, by type.
|
|
|
|
|
//
|
|
|
|
|
template<typename T0, typename... T>
|
|
|
|
|
void push_any_values(T0 arg0, T... args) {
|
|
|
|
|
push_any_value(arg0);
|
|
|
|
|
push_any_values(args...);
|
|
|
|
|
}
|
|
|
|
|
void push_any_values() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Throw a lua error message
|
|
|
|
|
void argerr(const char *arg, const char *tp) const;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Argument Counting Templates.
|
|
|
|
|
//
|
|
|
|
|
// These are internal functions used by LuaDefStack and LuaExtStack
|
|
|
|
|
// to help with the processing of constructor arguments.
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
struct LuaArgCounts {
|
|
|
|
|
int nret;
|
|
|
|
|
int narg;
|
|
|
|
|
int nvar;
|
|
|
|
|
int nextra;
|
|
|
|
|
constexpr LuaArgCounts(int nr, int na, int nv, int ne) : nret(nr), narg(na), nvar(nv), nextra(ne) {}
|
|
|
|
|
constexpr LuaArgCounts operator +(LuaArgCounts b) const {
|
|
|
|
|
return LuaArgCounts(nret + b.nret, narg + b.narg, nvar + b.nvar, nextra + b.nextra);
|
|
|
|
|
}
|
|
|
|
|
};
|
2023-03-01 16:07:13 -05:00
|
|
|
|
|
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
template<class... Ts>
|
|
|
|
|
struct LuaCountArgs;
|
|
|
|
|
template<>
|
|
|
|
|
struct LuaCountArgs<> {
|
|
|
|
|
static constexpr LuaArgCounts value = LuaArgCounts(0,0,0,0);
|
|
|
|
|
};
|
|
|
|
|
template<class... Ts>
|
|
|
|
|
struct LuaCountArgs<LuaRet, Ts...> {
|
|
|
|
|
static constexpr LuaArgCounts value = LuaArgCounts(1, 0, 0, 0) + LuaCountArgs<Ts...>::value;
|
|
|
|
|
};
|
|
|
|
|
template<class... Ts>
|
|
|
|
|
struct LuaCountArgs<LuaArg, Ts...> {
|
|
|
|
|
static constexpr LuaArgCounts value = LuaArgCounts(0, 1, 0, 0) + LuaCountArgs<Ts...>::value;
|
|
|
|
|
};
|
|
|
|
|
template<class... Ts>
|
|
|
|
|
struct LuaCountArgs<LuaVar, Ts...> {
|
|
|
|
|
static constexpr LuaArgCounts value = LuaArgCounts(0, 0, 1, 0) + LuaCountArgs<Ts...>::value;
|
|
|
|
|
};
|
|
|
|
|
template<class... Ts>
|
|
|
|
|
struct LuaCountArgs<LuaExtraArgs, Ts...> {
|
|
|
|
|
static constexpr LuaArgCounts value = LuaArgCounts(0, 0, 0, 1) + LuaCountArgs<Ts...>::value;
|
2020-12-05 18:57:53 -05:00
|
|
|
};
|
|
|
|
|
|
2023-04-07 14:20:45 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LuaDefStack
|
|
|
|
|
//
|
2023-04-14 17:50:06 -04:00
|
|
|
// This version of LuaStack should only be used inside a LuaDefine. It can
|
|
|
|
|
// assign stack slots to LuaArg, LuaRet, LuaVar, and LuaExtraArgs. It
|
|
|
|
|
// arranges for the arguments to be in the LuaArg variables, and it arranges for
|
|
|
|
|
// the LuaRet variables to be returned. It also makes sure that the function
|
|
|
|
|
// has the correct number of arguments.
|
2023-04-07 14:20:45 -04:00
|
|
|
//
|
|
|
|
|
// At the end of the LuaDefine function, you're supposed to return LS.result().
|
|
|
|
|
// LS.result causes the allocated stack slots to be freed except for the LuaRet
|
|
|
|
|
// values, which have to stay on the stack in order to pass them back as return
|
|
|
|
|
// values. LS.result returns the number of LuaRet variables left on the stack.
|
|
|
|
|
//
|
|
|
|
|
// If you terminate a LuaDefine by calling lua_error or lua_yield, then
|
|
|
|
|
// obviously, you don't get a chance to call LS.result. That's not a problem.
|
|
|
|
|
// The lua interpreter will clean up after an error or yield.
|
|
|
|
|
//
|
|
|
|
|
// Implementation note: LuaDefStack doesn't have a destructor to deallocate
|
2023-04-14 17:50:06 -04:00
|
|
|
// stack slots. That's deliberate: you shouldn't expect this class to clean up
|
|
|
|
|
// its stack frame, because after all, it has to leave return values on the
|
|
|
|
|
// stack. It would be deceptive to put a destructor, which then doesn't
|
|
|
|
|
// actually clean up anyway. Better to just let it be known that this class
|
|
|
|
|
// doesn't clean up its stack frame.
|
2023-04-07 14:20:45 -04:00
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
2023-04-06 14:05:12 -04:00
|
|
|
|
2024-03-20 14:29:20 -04:00
|
|
|
|
2023-04-06 20:12:03 -04:00
|
|
|
class LuaDefStack : public LuaCoreStack {
|
|
|
|
|
private:
|
|
|
|
|
int nret_;
|
|
|
|
|
|
2023-04-13 13:26:45 -04:00
|
|
|
|
2023-04-06 20:12:03 -04:00
|
|
|
public:
|
|
|
|
|
template<class... SS>
|
2023-04-13 12:14:27 -04:00
|
|
|
inline LuaDefStack(lua_State *L, SS & ... stackslots) : LuaCoreStack(L) {
|
2023-05-08 15:49:47 -04:00
|
|
|
constexpr LuaArgCounts counts = LuaCountArgs<SS...>::value;
|
2023-04-13 14:55:21 -04:00
|
|
|
int nargs = lua_gettop(L);
|
|
|
|
|
if (counts.nextra == 0) {
|
|
|
|
|
if (nargs != counts.narg) {
|
|
|
|
|
luaL_error(L_, "function expects exactly %d arguments", counts.narg);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (nargs < counts.narg) {
|
|
|
|
|
luaL_error(L_, "function expects at least %d arguments", counts.narg);
|
|
|
|
|
}
|
2023-04-06 20:12:03 -04:00
|
|
|
}
|
2023-04-13 14:55:21 -04:00
|
|
|
lua_checkstack(L, counts.nret + counts.nvar + 20);
|
|
|
|
|
lua_insert_frame(L, counts.nret + counts.nvar);
|
|
|
|
|
vassign_slots(1, 1 + counts.nret + counts.nvar, 1 + counts.nret, 1 + counts.nret + counts.nvar + counts.narg, nargs - counts.narg, stackslots...);
|
2023-04-13 12:14:27 -04:00
|
|
|
nret_ = counts.nret;
|
2023-04-06 20:12:03 -04:00
|
|
|
}
|
|
|
|
|
|
2023-04-07 12:58:05 -04:00
|
|
|
int result() {
|
2023-04-13 14:55:21 -04:00
|
|
|
lua_settop(L_, nret_);
|
2023-04-07 12:58:05 -04:00
|
|
|
return nret_;
|
2023-04-06 20:12:03 -04:00
|
|
|
}
|
2023-04-07 12:58:05 -04:00
|
|
|
|
2025-01-21 20:20:54 -05:00
|
|
|
// Tail-call into lua.
|
|
|
|
|
//
|
|
|
|
|
// This is meant to be used as follows: return LS.tailcall(passup, func, arg, arg...)
|
|
|
|
|
//
|
|
|
|
|
// If passup is true, the return value to our caller consists of our
|
|
|
|
|
// LuaRet arguments concatenated to the return values from the tail-call.
|
|
|
|
|
// If passup is false, the return value to our caller consists solely
|
|
|
|
|
// of our LuaRet arguments.
|
|
|
|
|
//
|
|
|
|
|
template<typename... T>
|
|
|
|
|
int tailcall(bool passup, LuaSlot func, T... args) {
|
|
|
|
|
lua_checkstack(L_, nret_ + 20);
|
|
|
|
|
int base = lua_gettop(L_);
|
|
|
|
|
for (int i = 1; i <= nret_; i++) {
|
|
|
|
|
lua_pushvalue(L_, i);
|
|
|
|
|
}
|
|
|
|
|
push_any_value(func);
|
|
|
|
|
int argbase = lua_gettop(L_);
|
|
|
|
|
push_any_values(args...);
|
|
|
|
|
int nargs = lua_gettop(L_) - argbase;
|
|
|
|
|
return tailcall_internal(passup, base, nargs);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-07 12:58:05 -04:00
|
|
|
~LuaDefStack() { }
|
2025-01-21 20:20:54 -05:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
int tailcall_internal(bool passup, int base, int nargs);
|
2023-04-06 20:12:03 -04:00
|
|
|
};
|
|
|
|
|
|
2023-04-07 14:20:45 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LuaExtStack
|
|
|
|
|
//
|
|
|
|
|
// This version of LuaStack is meant to be used in any context where
|
|
|
|
|
// you want to assign stack slots to some LuaVars, and then you want
|
|
|
|
|
// to automatically deallocate those LuaVars when the LuaExtStack
|
|
|
|
|
// goes out of scope.
|
|
|
|
|
//
|
|
|
|
|
// Unlike LuaDefStack, this version of LuaStack is meant to fully
|
|
|
|
|
// deallocate its stack frame when it goes out of scope, so it does
|
|
|
|
|
// have a destructor to do that. There is a special case in the
|
|
|
|
|
// destructor: if lua is throwing an error, the destructor leaves
|
|
|
|
|
// the stack alone, in order to preserve the error message that's
|
|
|
|
|
// on the stack. After an error throw, the lua interpreter will
|
|
|
|
|
// clean up the stack.
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2023-04-06 20:12:03 -04:00
|
|
|
class LuaExtStack : public LuaCoreStack {
|
|
|
|
|
private:
|
|
|
|
|
int oldtop_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
template<class... SS>
|
2023-04-07 12:58:05 -04:00
|
|
|
LuaExtStack(lua_State *L, SS & ... stackslots) : LuaCoreStack(L) {
|
2023-05-08 15:49:47 -04:00
|
|
|
constexpr LuaArgCounts counts = LuaCountArgs<SS...>::value;
|
|
|
|
|
static_assert(counts.narg == 0, "LuaExtStack only allows LuaVar, not LuaArg");
|
|
|
|
|
static_assert(counts.nret == 0, "LuaExtStack only allows LuaVar, not LuaRet");
|
|
|
|
|
static_assert(counts.nextra == 0, "LuaExtStack only allows LuaVar, not LuaExtraArgs");
|
|
|
|
|
lua_checkstack(L_, counts.nvar + 20);
|
2023-04-06 20:12:03 -04:00
|
|
|
oldtop_ = lua_gettop(L_);
|
2023-05-08 15:49:47 -04:00
|
|
|
for (int i = 0; i < counts.nvar; i++) {
|
2023-04-06 20:12:03 -04:00
|
|
|
lua_pushnil(L_);
|
|
|
|
|
}
|
2024-03-20 14:29:20 -04:00
|
|
|
vassign_slots(0, 0, oldtop_ + 1, 0, 0, stackslots...);
|
2023-04-06 20:12:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template<class... SS>
|
2023-04-13 12:14:27 -04:00
|
|
|
LuaExtStack(const LuaCoreStack &LS0, SS & ... stackslots) : LuaCoreStack(LS0.state(), stackslots...) {}
|
2023-04-06 20:12:03 -04:00
|
|
|
|
2024-02-27 17:01:26 -05:00
|
|
|
int oldtop() const { return oldtop_; }
|
|
|
|
|
|
2023-04-06 20:12:03 -04:00
|
|
|
~LuaExtStack() {
|
|
|
|
|
if (!lua_isthrowing(L_)) {
|
2025-01-20 21:11:59 -05:00
|
|
|
if (lua_gettop(L_) > oldtop_) {
|
|
|
|
|
lua_settop(L_, oldtop_);
|
|
|
|
|
}
|
2023-04-06 20:12:03 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-07 14:20:45 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LuaKeywordParser
|
|
|
|
|
//
|
2022-07-22 16:04:14 -04:00
|
|
|
// This is a helper class to help parse tables full of keywords.
|
2023-04-07 14:20:45 -04:00
|
|
|
// It is meant to make it easier to write LuaDefine functions that
|
|
|
|
|
// accept keyword arguments. It helps with the following tasks:
|
|
|
|
|
//
|
|
|
|
|
// * It makes sure the keyword table actually is a table.
|
|
|
|
|
//
|
2025-01-20 18:54:05 -05:00
|
|
|
// * It makes sure that all required keywords are present.
|
2023-04-07 14:20:45 -04:00
|
|
|
//
|
|
|
|
|
// * It makes sure that you didn't put anything that isn't a
|
2025-01-20 18:54:05 -05:00
|
|
|
// known keyword into the keyword table.
|
|
|
|
|
//
|
|
|
|
|
// This module adds two fields to the table:
|
|
|
|
|
//
|
|
|
|
|
// [ERROR] - stores an error message, initially nil.
|
|
|
|
|
// [FOUND] - the set of keywords successfully parsed.
|
|
|
|
|
//
|
|
|
|
|
// If at any time, this module detects an error, it doesn't throw.
|
|
|
|
|
// Instead, it stores an error report in the table key [ERROR].
|
|
|
|
|
// Later, you can check for an error string using the function
|
|
|
|
|
// final_check or final_check_throw. If an error is
|
|
|
|
|
// detected when there is already an error report in the table,
|
|
|
|
|
// the error report is not overwritten, so therefore, the error
|
|
|
|
|
// reported is always the first error detected.
|
|
|
|
|
//
|
|
|
|
|
// When this module finds a keyword in the table, it adds the keyword
|
|
|
|
|
// to the [FOUND] set of all keywords successfully parsed.
|
|
|
|
|
//
|
|
|
|
|
// If the keyword table that you pass in isn't a table at all,
|
|
|
|
|
// then the keyword-fetching functions will always return false.
|
|
|
|
|
// Later, when you call 'check', an appropriate error will be
|
|
|
|
|
// generated.
|
|
|
|
|
//
|
|
|
|
|
// The lua module 'keywords' contains the same functions as this
|
|
|
|
|
// C++ class. You can write code where a C++ function does some
|
|
|
|
|
// of the parsing, and the lua code does the rest of the parsing.
|
2023-04-07 14:20:45 -04:00
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2022-07-22 16:04:14 -04:00
|
|
|
class LuaKeywordParser {
|
|
|
|
|
private:
|
2025-01-20 18:54:05 -05:00
|
|
|
LuaVar found, error, key, val;
|
|
|
|
|
LuaSpecial keytab;
|
2025-01-21 20:20:54 -05:00
|
|
|
LuaCoreStack LS;
|
2025-01-20 18:54:05 -05:00
|
|
|
bool istable;
|
2022-07-22 16:04:14 -04:00
|
|
|
|
|
|
|
|
void init(const lua_State *L, int slot);
|
|
|
|
|
public:
|
2025-01-20 18:54:05 -05:00
|
|
|
LuaKeywordParser(const LuaCoreStack &LS, LuaSlot slot);
|
|
|
|
|
|
|
|
|
|
// Fetch the value of the keyword. If the keyword is found, then the
|
|
|
|
|
// keyword is added to the [FOUND] set, the value is returned in slot,
|
|
|
|
|
// and returns true. Otherwise, sets slot to nil and returns false.
|
2025-01-20 21:11:59 -05:00
|
|
|
bool optional(LuaSlot slot, std::string_view kw);
|
2025-01-20 18:54:05 -05:00
|
|
|
|
|
|
|
|
// Fetch the value of the keyword. If the keyword is found, then the
|
|
|
|
|
// keyword is added to the [FOUND] set, the value is returned in slot,
|
|
|
|
|
// and returns true. Otherwise, sets slot to nil, returns false, and
|
|
|
|
|
// stores an [ERROR] report in the keyword table.
|
2025-01-20 21:11:59 -05:00
|
|
|
bool required(LuaSlot slot, std::string_view kw);
|
2025-01-20 18:54:05 -05:00
|
|
|
|
2025-01-21 20:20:54 -05:00
|
|
|
// Check if there are any errors so far, by checking for an [ERROR]
|
|
|
|
|
// report in the keyword table. If any error has been
|
2025-01-20 18:54:05 -05:00
|
|
|
// detected, returns an error message, otherwise, returns empty
|
|
|
|
|
// string.
|
|
|
|
|
eng::string check();
|
|
|
|
|
|
2025-01-21 20:20:54 -05:00
|
|
|
// Check if there are any errors so far, by checking for an [ERROR]
|
|
|
|
|
// report in the keyword table. Also check that all keyword
|
2025-01-20 18:54:05 -05:00
|
|
|
// arguments present in the table are in the [FOUND] set. If there are
|
|
|
|
|
// any errors, returns an error message, otherwise returns empty string.
|
2022-07-22 17:07:40 -04:00
|
|
|
eng::string final_check();
|
|
|
|
|
|
2025-01-20 18:54:05 -05:00
|
|
|
// If check() returns an error, throws the error using luaL_error.
|
|
|
|
|
void check_throw();
|
|
|
|
|
|
|
|
|
|
// If final_check() returns an error, throws the error using luaL_error.
|
2022-07-22 17:07:40 -04:00
|
|
|
void final_check_throw();
|
2022-07-22 16:04:14 -04:00
|
|
|
|
|
|
|
|
// Fetch the state pointer.
|
2025-01-20 18:54:05 -05:00
|
|
|
lua_State *state() const { return LS.state(); }
|
2022-07-22 16:04:14 -04:00
|
|
|
};
|
|
|
|
|
|
2025-01-20 18:54:05 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-10-18 14:03:05 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// Lua Byte Reader
|
|
|
|
|
//
|
|
|
|
|
// Converts a block of bytes in RAM into a lua_reader.
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
class LuaByteReader {
|
|
|
|
|
private:
|
|
|
|
|
const char *data_;
|
|
|
|
|
int64_t size_;
|
|
|
|
|
public:
|
|
|
|
|
LuaByteReader(const char *d, int64_t s) : data_(d), size_(s) {}
|
|
|
|
|
void *lua_reader_userdata() { return this; }
|
|
|
|
|
static const char *lua_reader(lua_State *L, void *ud, size_t *size);
|
|
|
|
|
};
|
|
|
|
|
|
2023-04-07 14:20:45 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// The Lua Constant Registry
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2022-06-06 23:03:26 -04:00
|
|
|
class LuaConstantReg : public eng::nevernew {
|
|
|
|
|
private:
|
|
|
|
|
const char *name_;
|
|
|
|
|
const char *docs_;
|
|
|
|
|
LuaToken tokenvalue_;
|
|
|
|
|
lua_Number numbervalue_;
|
|
|
|
|
LuaConstantReg *next_;
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static LuaConstantReg *All;
|
|
|
|
|
LuaConstantReg(const char *name, const char *docs, LuaToken tokenvalue, lua_Number numbervalue);
|
|
|
|
|
|
|
|
|
|
const char *get_name() const { return name_; }
|
|
|
|
|
const char *get_docs() const { return docs_; }
|
|
|
|
|
LuaToken get_tokenvalue() const { return tokenvalue_; }
|
|
|
|
|
lua_Number get_numbervalue() const { return numbervalue_; }
|
|
|
|
|
LuaConstantReg *next() const { return next_; }
|
|
|
|
|
};
|
2021-01-06 15:10:21 -05:00
|
|
|
|
2023-04-07 14:20:45 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// The Lua Function Registry
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2022-03-02 14:52:51 -05:00
|
|
|
class LuaFunctionReg : public eng::nevernew {
|
2020-12-05 18:57:53 -05:00
|
|
|
private:
|
|
|
|
|
const char *name_;
|
2021-12-15 23:03:43 -05:00
|
|
|
const char *args_;
|
|
|
|
|
const char *docs_;
|
2022-03-16 17:05:20 -04:00
|
|
|
bool sandbox_;
|
2020-12-05 18:57:53 -05:00
|
|
|
lua_CFunction func_;
|
|
|
|
|
LuaFunctionReg *next_;
|
|
|
|
|
|
|
|
|
|
public:
|
2022-02-25 19:57:23 -05:00
|
|
|
static LuaFunctionReg *All;
|
2022-03-16 17:05:20 -04:00
|
|
|
LuaFunctionReg(const char *name, const char *args, const char *docs, bool sand, lua_CFunction f);
|
2021-12-15 23:03:43 -05:00
|
|
|
static const LuaFunctionReg *lookup(lua_CFunction fn);
|
2020-12-05 18:57:53 -05:00
|
|
|
|
|
|
|
|
const char *get_name() const { return name_; }
|
2021-12-15 23:03:43 -05:00
|
|
|
const char *get_args() const { return args_; }
|
|
|
|
|
const char *get_docs() const { return docs_; }
|
2020-12-05 18:57:53 -05:00
|
|
|
lua_CFunction get_func() const { return func_; }
|
2022-03-16 17:05:20 -04:00
|
|
|
bool get_sandbox() const { return sandbox_; }
|
2022-02-25 19:57:23 -05:00
|
|
|
LuaFunctionReg *next() const { return next_; }
|
2021-12-16 13:06:15 -05:00
|
|
|
void set_func(lua_CFunction fn) { func_ = fn; }
|
2020-11-27 13:21:07 -05:00
|
|
|
};
|
|
|
|
|
|
2023-04-07 14:20:45 -04:00
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
//
|
|
|
|
|
// LuaDefine and friends.
|
|
|
|
|
//
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2022-06-06 23:03:26 -04:00
|
|
|
#define LuaTokenConstant(name, tvalue, docs) \
|
2023-03-05 01:51:25 -05:00
|
|
|
LuaToken ltoken_##name(tvalue); \
|
2022-06-06 23:03:26 -04:00
|
|
|
LuaConstantReg reg_##name(#name, docs, LuaToken(tvalue), 0);
|
|
|
|
|
|
|
|
|
|
#define LuaNumberConstant(name, nvalue, docs) \
|
2023-03-05 01:51:25 -05:00
|
|
|
lua_Number lnumber_##name(nvalue); \
|
2022-06-06 23:03:26 -04:00
|
|
|
LuaConstantReg reg_##name(#name, docs, LuaToken(), nvalue);
|
2021-01-06 15:10:21 -05:00
|
|
|
|
2021-12-15 23:03:43 -05:00
|
|
|
#define LuaDefine(name, args, docs) \
|
2021-09-07 17:37:23 -04:00
|
|
|
int lfn_##name(lua_State *L); \
|
2023-04-14 14:52:44 -04:00
|
|
|
const char *lfnarg_##name = args; \
|
|
|
|
|
const char *lfndoc_##name = docs; \
|
|
|
|
|
LuaFunctionReg reg_##name(#name, lfnarg_##name, lfndoc_##name, false, lfn_##name); \
|
2021-09-07 17:37:23 -04:00
|
|
|
int lfn_##name(lua_State *L)
|
2020-12-05 18:57:53 -05:00
|
|
|
|
2023-04-14 14:52:44 -04:00
|
|
|
#define LuaDefineAlias(name1, name2) \
|
|
|
|
|
LuaFunctionReg reg_##name1(#name1, lfnarg_##name2, lfndoc_##name2, false, lfn_##name2); \
|
2020-12-05 18:57:53 -05:00
|
|
|
|
2021-12-16 13:06:15 -05:00
|
|
|
#define LuaDefineBuiltin(name, args, docs) \
|
2022-03-16 17:05:20 -04:00
|
|
|
LuaFunctionReg reg_##name(#name, args, docs, false, nullptr);
|
2021-12-16 13:06:15 -05:00
|
|
|
|
2022-03-16 17:05:20 -04:00
|
|
|
#define LuaSandboxBuiltin(name, args, docs) \
|
|
|
|
|
LuaFunctionReg reg_##name(#name, args, docs, true, nullptr);
|
2021-12-16 13:06:15 -05:00
|
|
|
|
2021-01-12 15:49:05 -05:00
|
|
|
#define LuaStringify(x) #x
|
|
|
|
|
#define LuaAssert(L, x) if (!(x)) { luaL_error((L), "Assert failed: %s (file %s line %d)", LuaStringify(x), __FILE__, __LINE__); }
|
2022-02-24 02:17:41 -05:00
|
|
|
#define LuaAssertStrEq(L, x, y) { eng::string _s1_(x); eng::string _s2_(y); if (_s1_ != _s2_) luaL_error((L), "Assert failed: value=%s (file %s line %d)", _s1_.c_str(), __FILE__, __LINE__); }
|
2022-06-06 23:03:26 -04:00
|
|
|
|
2023-04-07 14:20:45 -04:00
|
|
|
#endif // LUASTACK_HPP
|