lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


2015-07-28 5:45 GMT+02:00 Daurnimator <quae@daurnimator.com>:

>   - __tostring metamethods that have previously needed a `rawtostring`

I thought implementing `rawtostring` would be trivial. However, there
is no `lua_rawtostring` or `luaL_rawtostring`.

`LuaB_tostring` is very short, though:

static int luaB_tostring (lua_State *L) {
  luaL_checkany(L, 1);
  luaL_tolstring(L, 1, NULL);
  return 1;
}

The manual says this of `luaL_tolstring`:

If the value has a metatable with a "__tostring" field, then
luaL_tolstring calls the corresponding metamethod with the value as
argument, and uses the result of the call as its result.

It says nothing of the kind at `lua_tostring`. Instead, it says:

The Lua value must be a string or a number; otherwise, the function
returns NULL.

Therefore the functionality that represents tables, etc sits inside
luaL_tolstring, bundled in with the metatable test.

It would be more flexible to unbundle that functionality, i.e. to have
a function luaL_rawtostring that would be called if there is no
metamethod, but could also be called by itself.
diff -ru lua-5.3.1/src/lauxlib.c lua-5.3.1-patched/src/lauxlib.c
--- lua-5.3.1/src/lauxlib.c	2015-02-03 19:38:24.000000000 +0200
+++ lua-5.3.1-patched/src/lauxlib.c	2015-07-28 12:27:52.559150119 +0200
@@ -744,35 +744,39 @@
   return l;
 }
 
-
-LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
-  if (!luaL_callmeta(L, idx, "__tostring")) {  /* no metafield? */
-    switch (lua_type(L, idx)) {
-      case LUA_TNUMBER: {
-        if (lua_isinteger(L, idx))
-          lua_pushfstring(L, "%I", lua_tointeger(L, idx));
-        else
-          lua_pushfstring(L, "%f", lua_tonumber(L, idx));
-        break;
-      }
-      case LUA_TSTRING:
-        lua_pushvalue(L, idx);
-        break;
-      case LUA_TBOOLEAN:
-        lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false"));
-        break;
-      case LUA_TNIL:
-        lua_pushliteral(L, "nil");
-        break;
-      default:
-        lua_pushfstring(L, "%s: %p", luaL_typename(L, idx),
-                                            lua_topointer(L, idx));
-        break;
+LUALIB_API const char *luaL_rawtolstring (lua_State *L, int idx, size_t *len) {    switch (lua_type(L, idx)) {
+    case LUA_TNUMBER: {
+      if (lua_isinteger(L, idx))
+        lua_pushfstring(L, "%I", lua_tointeger(L, idx));
+      else
+        lua_pushfstring(L, "%f", lua_tonumber(L, idx));
+      break;
     }
+    case LUA_TSTRING:
+      lua_pushvalue(L, idx);
+      break;
+    case LUA_TBOOLEAN:
+      lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false"));
+      break;
+    case LUA_TNIL:
+      lua_pushliteral(L, "nil");
+      break;
+    default:
+      lua_pushfstring(L, "%s: %p", luaL_typename(L, idx),
+                                          lua_topointer(L, idx));
+      break;
   }
   return lua_tolstring(L, -1, len);
 }
 
+LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
+  idx = lua_absindex(L, idx);
+  int has_meta = luaL_getmetafield(L, idx, "__tostring");
+  if (has_meta == LUA_TNIL) return luaL_rawtolstring(L, idx, len);
+  lua_pushvalue(L, idx);
+  lua_call(L, 1, 1);  
+  return lua_tolstring(L, -1, len);
+}
 
 /*
 ** {======================================================
diff -ru lua-5.3.1/src/lauxlib.h lua-5.3.1-patched/src/lauxlib.h
--- lua-5.3.1/src/lauxlib.h	2014-10-29 18:11:17.000000000 +0200
+++ lua-5.3.1-patched/src/lauxlib.h	2015-07-28 12:07:09.831182752 +0200
@@ -34,6 +34,7 @@
 
 LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
 LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
+LUALIB_API const char *(luaL_rawtolstring) (lua_State *L, int idx, size_t *len);
 LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len);
 LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg);
 LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg,
diff -ru lua-5.3.1/src/lbaselib.c lua-5.3.1-patched/src/lbaselib.c
--- lua-5.3.1/src/lbaselib.c	2015-03-28 21:14:47.000000000 +0200
+++ lua-5.3.1-patched/src/lbaselib.c	2015-07-28 12:06:03.447184495 +0200
@@ -458,13 +458,18 @@
   return finishpcall(L, status, 2);
 }
 
-
 static int luaB_tostring (lua_State *L) {
   luaL_checkany(L, 1);
   luaL_tolstring(L, 1, NULL);
   return 1;
 }
 
+/* DPL 2015-07-28 */
+static int luaB_rawtostring (lua_State *L) {
+  luaL_checkany(L, 1);
+  luaL_rawtolstring(L, 1, NULL);
+  return 1;
+}
 
 static const luaL_Reg base_funcs[] = {
   {"assert", luaB_assert},
@@ -486,6 +491,7 @@
   {"rawlen", luaB_rawlen},
   {"rawget", luaB_rawget},
   {"rawset", luaB_rawset},
+  {"rawtostring", luaB_rawtostring}, /* DPL 2015-07-28 */
   {"select", luaB_select},
   {"setmetatable", luaB_setmetatable},
   {"tonumber", luaB_tonumber},
@@ -509,6 +515,8 @@
   lua_setfield(L, -2, "_G");
   /* set global _VERSION */
   lua_pushliteral(L, LUA_VERSION);
+  lua_pushliteral(L, " patched by DPL");
+  lua_concat(L,2);
   lua_setfield(L, -2, "_VERSION");
   /* set function 'type' with proper upvalues */
   for (i = 0; i < LUA_NUMTAGS; i++)  /* push all type names as upvalues */