From 0f15ee34d78ae45e9ccd03b124993248bff30add Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Fri, 9 Feb 2018 16:46:33 -0800 Subject: [PATCH] Added default tostring. --- limmutable.c | 81 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 69 insertions(+), 12 deletions(-) diff --git a/limmutable.c b/limmutable.c index b257481..deff4c0 100644 --- a/limmutable.c +++ b/limmutable.c @@ -1,6 +1,16 @@ /* * immutable.c -* An immutable table library. +* An immutable table library by Bruce Hill. This library returns a single function +* that can be used to declare immutable classes, like so: +* +* immutable = require 'immutable' +* local Foo = immutable({"baz","qux"}) +* local foo = Foo("hello", 99) +* assert(not pcall(function() foo.x = 'mutable' end)) +* local t = {[foo]="it works"} +* assert(t[Foo("hello", 99)] == "it works") +* +* Instances *are* garbage collected. */ #include "lua.h" @@ -199,14 +209,62 @@ static int Lindex(lua_State *L) } lua_pop(L, 1); // Stack: [mt] - lua_getfield(L, -1, "__methods"); - // Stack: [mt, __methods] lua_pushvalue(L, 2); - // Stack: [mt, __methods, key] + // Stack: [mt, key] lua_gettable(L, -2); return 1; } +static int Ltostring(lua_State *L) +{ + luaL_Buffer b; + luaL_buffinit(L, &b); + + lua_getmetatable(L, 1); + // Stack: [mt] + + lua_getfield(L, -1, "name"); + if (!lua_isnil(L, -1)) { + luaL_addvalue(&b); + } else { + lua_pop(L, 1); + } + luaL_addstring(&b, "("); + + lua_getfield(L, -1, "__instances"); + // Stack: [mt, buckets] + lua_rawgeti(L, -1, *((lua_Integer*)lua_touserdata(L, 1))); + // Stack: [mt, buckets, bucket] + lua_pushvalue(L, 1); + // Stack: [mt, buckets, bucket, inst_udata] + lua_rawget(L, -2); + // Stack: [mt, buckets, bucket, inst_table] + + lua_getfield(L, -4, "__fields"); + // Stack: [mt, buckets, bucket, inst_table, fields] + + lua_pushnil(L); + int needs_comma = 0; + while (lua_next(L, -2) != 0) { + // Stack: [mt, buckets, bucket, inst_table, fields, i, fieldname] + if (needs_comma) { + luaL_addstring(&b, ", "); + } else { + needs_comma = 1; + } + luaL_addvalue(&b); + // Stack: [mt, buckets, bucket, inst_table, fields, i] + luaL_addstring(&b, "="); + lua_rawgeti(L, -3, lua_tonumber(L, -1)); + // Stack: [mt, buckets, bucket, inst_table, fields, i, value] + luaL_addvalue(&b); + // Stack: [mt, buckets, bucket, inst_table, fields, i] + } + luaL_addstring(&b, ")"); + luaL_pushresult(&b); + return 1; +} + static const luaL_Reg R[] = { { "__len", Llen}, @@ -223,6 +281,8 @@ static int Lmake_class(lua_State *L) // CLS = {} lua_newtable(L); // Stack: [CLS] + lua_pushcfunction(L, Ltostring); + lua_setfield(L, -2, "__tostring"); // If metamethods were passed in, copy them over if (n_args > 2) { @@ -252,25 +312,22 @@ static int Lmake_class(lua_State *L) lua_setfield(L, -2, "__fields"); // Stack: [CLS] - lua_newtable(L); - // Stack: [CLS, __methods] // If methods were passed in, copy them over if (n_args > 1) { // Stack: [ lua_pushnil(L); - // Stack: [CLS, __methods, nil] + // Stack: [CLS, nil] while (lua_next(L, 2) != 0) { + // Stack: [CLS, method_name, method_value] lua_pushvalue(L, -2); lua_pushvalue(L, -2); - // Stack: [CLS, __methods, method_name, method_value, method_name, method_value] + // Stack: [CLS, method_name, method_value, method_name, method_value] lua_settable(L, -5); - // Stack: [CLS, __methods, method_name, method_value] + // Stack: [CLS, method_name, method_value] lua_pop(L, 1); - // Stack: [CLS, __methods, method_name] + // Stack: [CLS, method_name] } - // Stack: [CLS, __methods] } - lua_setfield(L, -2, "__methods"); // Stack: [CLS] // Populate __len, __eq, __index, new, etc.