implement lua_args_parse
authorTero Marttila <terom@fixme.fi>
Sun, 19 Apr 2009 06:20:59 +0300
changeset 143 1edab39c88a8
parent 142 dc2bb09d412c
child 144 51f96539f9f3
implement lua_args_parse
src/CMakeLists.txt
src/error.h
src/lua_func.c
src/lua_func.h
src/lua_irc.c
src/lua_objs.c
src/lua_objs.h
src/lua_type.c
src/lua_type.h
--- a/src/CMakeLists.txt	Sun Apr 19 04:52:12 2009 +0300
+++ b/src/CMakeLists.txt	Sun Apr 19 06:20:59 2009 +0300
@@ -13,7 +13,7 @@
 set (CORE_SOURCES error.c log.c str.c)
 set (SOCK_SOURCES sock.c sock_fd.c sock_tcp.c sock_gnutls.c sock_test.c sock_fifo.c line_proto.c)
 set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c irc_queue.c)
-set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c)
+set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c lua_func.c lua_type.c)
 set (CONSOLE_SOURCES console.c lua_console.c)
 
 set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES} ${LUA_SOURCES} ${CONSOLE_SOURCES} signals.c module.c config.c)
--- a/src/error.h	Sun Apr 19 04:52:12 2009 +0300
+++ b/src/error.h	Sun Apr 19 06:20:59 2009 +0300
@@ -227,6 +227,7 @@
 /**
  * Macro used to mark code segments that should never be executed (e.g. switch-default), kind of like assert
  */
-#define NOT_REACHED() assert(false)
+#include <stdlib.h>
+#define NOT_REACHED() abort()
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lua_func.c	Sun Apr 19 06:20:59 2009 +0300
@@ -0,0 +1,169 @@
+#include "lua_func.h"
+#include "error.h"
+
+#include <lua5.1/lauxlib.h>
+
+/**
+ * Pushes onto the stack the value at t[i]
+ */
+static void lua_getindex (lua_State *L, int t, int i)
+{
+    lua_pushinteger(L, i);
+    lua_gettable(L, t);
+}
+
+/**
+ * Pushes onto the stack either:
+ *  * the value at t[name]
+ *  * the value at t[index]
+ *
+ *  Returns the new index, or 0, if neither could be found
+ */
+static int lua_arg_lookup (lua_State *L, int t, const char *name, int index)
+{
+    // try name
+    lua_getfield(L, t, name);
+
+    if (!lua_isnil(L, -1))
+        return lua_gettop(L);
+    else
+        lua_pop(L, 1);
+    
+    // try index
+    lua_getindex(L, t, index);
+    
+    if (!lua_isnil(L, -1))
+        return lua_gettop(L);
+
+    else
+        lua_pop(L, 1);
+    
+    // not found
+    return 0;
+}
+
+static const char *_lua_arg_string (lua_State *L, int index, const char *name, const char *def)
+{
+    const char *value;
+
+    // use default?
+    if (lua_isnoneornil(L, index) && def != (const char *) LUA_ARG_REQUIRED)
+        return def;
+    
+    // value given?
+    if ((value = lua_tostring(L, index)))
+        return value;
+   
+    // error
+    luaL_error(L, "missing value for required string argument <%d:%s>", index, name); return NULL;
+}
+
+static bool _lua_arg_bool (lua_State *L, int index, const char *name, int def)
+{
+    (void) name;
+
+    // use default?
+    if (lua_isnoneornil(L, index) && def != LUA_ARG_REQUIRED)
+        return def;
+   
+    // value given
+    return lua_toboolean(L, index);
+}
+
+/**
+ * Look up the arg index to use for the given index/name.
+ *
+ * If no value is found for the corresponding index, returns zero.
+ */
+static int lua_arg_index (lua_State *L, int nargs, int index, const char *name)
+{
+    // lookup from table?
+    if (name && lua_istable(L, 2)) {
+        // push the value from the field onto the stack
+        lua_getfield(L, 2, name);
+        
+        // no named field?
+        if (lua_isnil(L, -1)) {
+            lua_pop(L, 1);
+
+            lua_getindex(L, 2, index - 1);
+        }
+
+        // no index field?
+        if (lua_isnil(L, -1)) {
+            lua_pop(L, 1);
+
+            return 0;
+        }
+        
+        // found either a named or indexed arg
+        return lua_gettop(L);
+
+    } else if (index <= nargs) {
+        // use the same index
+        return index;
+
+    } else {
+        // no index
+        return 0;
+    }
+}
+
+const char *lua_arg_string (lua_State *L, int nargs, int index, const char *name, const char *def)
+{
+    return _lua_arg_string(L, lua_arg_index(L, nargs, index, name), name, def);
+}
+
+bool lua_arg_bool (lua_State *L, int nargs, int index, const char *name, int def)
+{
+    return _lua_arg_bool(L, lua_arg_index(L, nargs, index, name), name, def);    
+}
+
+void lua_args_parse (lua_State *L, const struct lua_func *func, void **obj_ptr, ...)
+{
+    int argidx = 1, argtbl = 0, idx;
+    const struct lua_func_arg *arg;
+    va_list vargs;
+
+    // first, the obj argument
+    if (func->type) {
+        *obj_ptr = lua_obj_get_obj(L, func->name, func->type);
+        argidx++;
+    }
+
+    // were we given a table of arguments?
+    if (lua_istable(L, argidx))
+        argtbl = argidx++;
+
+    // parse the args
+    va_start(vargs, obj_ptr);
+
+    for (arg = func->args, idx = 1; arg->name && arg->type; arg++, idx++) {
+        int index;
+
+        // map index
+        if (!argtbl)
+            // direct
+            index = argidx++;
+        
+        else
+            // lookup from table
+            index = lua_arg_lookup(L, argtbl, arg->name, idx);
+        
+        // apply
+        switch (arg->type) {
+            case LUA_ARG_STRING:
+                *va_arg(vargs, const char **) = _lua_arg_string(L, index, arg->name, arg->def.string);
+                break;
+
+            case LUA_ARG_BOOL:
+                *va_arg(vargs, bool *) = _lua_arg_bool(L, index, arg->name, arg->def.boolean);
+                break;
+
+            default:
+                NOT_REACHED();
+        };
+    }
+
+    va_end(vargs);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lua_func.h	Sun Apr 19 06:20:59 2009 +0300
@@ -0,0 +1,88 @@
+#ifndef LUA_FUNC_H
+#define LUA_FUNC_H
+
+/**
+ * @file
+ *
+ * Convenience functions for working with lua C functions
+ */
+#include "lua_type.h"
+#include <stdbool.h>
+
+/**
+ * Lua function argument types
+ */
+enum lua_arg_type {
+    LUA_ARG_INVALID,
+
+    LUA_ARG_STRING,
+    LUA_ARG_BOOL,
+};
+
+/**
+ * Function argument def
+ */
+struct lua_func_arg {
+    /** Argument name */
+    const char *name;
+
+    /** Expected type */
+    enum lua_arg_type type;
+
+    /** Default value */
+    union {
+        const char *string;
+        int boolean;
+    } def;
+};
+
+/**
+ * Function def
+ */
+struct lua_func {
+    /** Type name, or NULL */
+    const char *type;
+
+    /** Function name */
+    const char *name;
+
+    /** Help string */
+    const char *help;
+
+    /** Arguments */
+    const struct lua_func_arg args[];
+};
+
+/**
+ * Used as the "invalid" default value
+ */
+#define LUA_ARG_REQUIRED (-1)
+#define LUA_ARG_STRING_REQUIRED ((const char *) (-1))
+
+/**
+ * Define a function argument
+ */
+#define LUA_FUNC_ARG_STRING(name, def) { (name), LUA_ARG_STRING, { .string = (def) } }
+#define LUA_FUNC_ARG_BOOL(name, def) { (name), LUA_ARG_BOOL, { .boolean = (def) } }
+
+/**
+ * Define a function
+ */
+#define LUA_FUNC(type, name, help, ...) { (type), (name), (help), { __VA_ARGS__, { NULL, 0, { 0 } } } }
+
+/**
+ * Parse and return a string argument
+ */
+const char *lua_arg_string (lua_State *L, int nargs, int index, const char *name, const char *def);
+
+/**
+ * Parse and return a boolean argument
+ */
+bool lua_arg_bool (lua_State *L, int nargs, int index, const char *name, int def);
+
+/**
+ * Parse function arguments as defined
+ */
+void lua_args_parse (lua_State *L, const struct lua_func *func, void **obj_ptr, ...);
+
+#endif
--- a/src/lua_irc.c	Sun Apr 19 04:52:12 2009 +0300
+++ b/src/lua_irc.c	Sun Apr 19 06:20:59 2009 +0300
@@ -1,4 +1,5 @@
 #include "lua_irc.h"
+#include "lua_func.h"
 
 #include <stdlib.h>
 #include <string.h>
@@ -147,31 +148,61 @@
     lua_obj_create_type(L, "evirc.net", lua_net_methods);
 }
 
+static struct lua_func lua_client_set_defaults_func = LUA_FUNC("evirc.client", "set_defaults", 
+        "set the default settings to use for evirc.client.connect",
+
+        LUA_FUNC_ARG_STRING("nickname",     LUA_ARG_STRING_REQUIRED ),
+        LUA_FUNC_ARG_STRING("username",     LUA_ARG_STRING_REQUIRED ),
+        LUA_FUNC_ARG_STRING("realname",     LUA_ARG_STRING_REQUIRED ),
+        LUA_FUNC_ARG_STRING("service",      IRC_PORT                ),
+        LUA_FUNC_ARG_STRING("service_ssl",  IRC_SSL_PORT            )
+);
+
 static int lua_client_set_defaults (lua_State *L)
 {
-    int nargs = lua_gettop(L);
-    struct lua_client *lua_client = lua_obj_get_obj(L, __func__, "evirc.client");
+    struct lua_client *lua_client;
+    const char *nickname, *username, *realname, *service, *service_ssl;
 
-    // required args
-    lua_client->defaults.register_info.nickname = lua_arg_string(L, nargs, 2, "nickname", LUA_ARG_STRING_REQUIRED);
-    lua_client->defaults.register_info.username = lua_arg_string(L, nargs, 3, "username", LUA_ARG_STRING_REQUIRED);
-    lua_client->defaults.register_info.realname = lua_arg_string(L, nargs, 4, "realname", LUA_ARG_STRING_REQUIRED);
+    // parse args
+    lua_args_parse(L, &lua_client_set_defaults_func, (void *) &lua_client,
+        &nickname, &username, &realname, &service, &service_ssl
+    );
 
-    // optional args
-    lua_client->defaults.service        = lua_arg_string(L, nargs, 5, "service", IRC_PORT);
-    lua_client->defaults.service_ssl    = lua_arg_string(L, nargs, 6, "service_ssl", IRC_SSL_PORT);
+    // set
+    struct irc_client_defaults defaults = {
+        .register_info = {
+            .nickname   = nickname,
+            .username   = username,
+            .realname   = realname
+        },
+        .service        = service,
+        .service_ssl    = service_ssl
+    };
 
     // invoke
-    irc_client_set_defaults(lua_client->client, &lua_client->defaults);
+    // XXX: needs to be copied
+    irc_client_set_defaults(lua_client->client, &defaults);
 
     // ok
     return 0;
 }
 
+static struct lua_func lua_client_connect_func = LUA_FUNC("evirc.client", "connect",
+        "Create and return a new IRC network",
+
+        LUA_FUNC_ARG_STRING("network",      LUA_ARG_STRING_REQUIRED ),
+        LUA_FUNC_ARG_STRING("hostname",     LUA_ARG_STRING_REQUIRED ),
+        LUA_FUNC_ARG_STRING("service",      NULL                    ),
+        LUA_FUNC_ARG_BOOL(  "ssl",          false                   ),
+        LUA_FUNC_ARG_STRING("ssl_cafile",   NULL                    ),
+        LUA_FUNC_ARG_BOOL(  "ssl_verify",   false                   ),
+        LUA_FUNC_ARG_STRING("ssl_cert",     NULL                    ),
+        LUA_FUNC_ARG_STRING("ssl_pkey",     NULL                    )
+);
+
 static int lua_client_connect (lua_State *L)
 {
-    int nargs = lua_gettop(L);
-    struct lua_client *lua_client = lua_obj_get_obj(L, __func__, "evirc.client");
+    struct lua_client *lua_client;
     struct irc_net_info net_info;
     const char *ssl_cafile = NULL, *ssl_cert = NULL, *ssl_pkey = NULL;
     bool use_ssl = false, ssl_verify = false;
@@ -181,19 +212,11 @@
     // init net_info
     memset(&net_info, 0, sizeof(net_info));
 
-    // required args
-    net_info.network    = lua_arg_string(L, nargs, 2, "network",    LUA_ARG_STRING_REQUIRED);
-    net_info.hostname   = lua_arg_string(L, nargs, 3, "hostname",   LUA_ARG_STRING_REQUIRED);
-
-    // optional args
-    net_info.service    = lua_arg_string(L, nargs, 4, "service",    NULL);
-
-    // ssl stuff
-    use_ssl             = lua_arg_bool  (L, nargs, 5, "use_ssl",    false);
-    ssl_cafile          = lua_arg_string(L, nargs, 6, "ssl_cafile", NULL);
-    ssl_verify          = lua_arg_bool  (L, nargs, 7, "ssl_verify", false);
-    ssl_cert            = lua_arg_string(L, nargs, 8, "ssl_cert",   NULL);
-    ssl_pkey            = lua_arg_string(L, nargs, 9, "ssl_pkey",   NULL);
+    // parse args
+    lua_args_parse(L, &lua_client_connect_func, (void *) &lua_client,
+        &net_info.network, &net_info.hostname, &net_info.service,
+        &use_ssl, &ssl_cafile, &ssl_verify, &ssl_cert, &ssl_pkey
+    );
 
     // SSL?
     if (use_ssl || ssl_cafile || ssl_verify || ssl_cert || ssl_pkey) {
--- a/src/lua_objs.c	Sun Apr 19 04:52:12 2009 +0300
+++ b/src/lua_objs.c	Sun Apr 19 06:20:59 2009 +0300
@@ -5,151 +5,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-void lua_obj_create_type (lua_State *L, const char *name, const struct luaL_Reg methods[])
-{
-    luaL_newmetatable(L, name);
-
-    // set the metatable __index to itself
-    lua_pushvalue(L, -1);
-    lua_setfield(L, -1, "__index");
-
-    // register the methods to the metatable
-    luaL_register(L, NULL, methods);
-}
-
-void* lua_obj_create_obj (lua_State *L, const char *name, size_t size)
-{
-    // create the new userdata on the stack
-    void *ud = lua_newuserdata(L, size);
-
-    // get the type and set it
-    luaL_getmetatable(L, name);
-    lua_setmetatable(L, -2);
-    
-    // ok
-    return ud;
-}
-
-void* lua_obj_create_global_type (lua_State *L, const char *type_name, const struct luaL_Reg methods[], const char *global_name, size_t size)
-{
-    // allocate the global object
-    void *obj = lua_newuserdata(L, size);
-    
-    // create the type metatable
-    lua_obj_create_type(L, type_name, methods);
-
-    // set the userdata's metatable
-    lua_setmetatable(L, -2);
-
-    // store it as a global
-    lua_setglobal(L, global_name);
-    
-    // ok
-    return obj;
-}
-
-void* lua_obj_get_obj (lua_State *L, const char *func, const char *name)
-{
-    void *ud;
-
-    // validate the userdata arg
-    if ((ud = luaL_checkudata(L, 1, name)) == NULL) {
-        luaL_error(L, "bad type argument to %s: `%s` expected", func, name);
-        
-        // XXX: needs a __noreturn__ attribute
-        return NULL;
-
-    } else {
-        // ok
-        return ud;
-
-    }
-}
-
-static void lua_getindex (lua_State *L, int t, int i)
-{
-    lua_pushinteger(L, i);
-    lua_gettable(L, t);
-}
-
-/**
- * Look up the arg index to use for the given index/name.
- *
- * If no value is found for the corresponding index, returns zero.
- */
-static int lua_arg_index (lua_State *L, int nargs, int index, const char *name)
-{
-    // lookup from table?
-    if (name && lua_istable(L, 2)) {
-        // push the value from the field onto the stack
-        lua_getfield(L, 2, name);
-        
-        // no named field?
-        if (lua_isnil(L, -1)) {
-            lua_pop(L, 1);
-
-            lua_getindex(L, 2, index - 1);
-        }
-
-        // no index field?
-        if (lua_isnil(L, -1)) {
-            lua_pop(L, 1);
-
-            return 0;
-        }
-        
-        // found either a named or indexed arg
-        return lua_gettop(L);
-
-    } else if (index <= nargs) {
-        // use the same index
-        return index;
-
-    } else {
-        // no index
-        return 0;
-    }
-}
-
-const char *lua_arg_string (lua_State *L, int nargs, int index, const char *name, const char *def)
-{
-    const char *value;
-
-    // map index
-    index = lua_arg_index(L, nargs, index, name);
- 
-    // use default?
-    if (lua_isnoneornil(L, index) && def != (const char *) LUA_ARG_REQUIRED)
-        return def;
-    
-    // value given?
-    if ((value = lua_tostring(L, index)))
-        return value;
-   
-    // error
-    luaL_error(L, "missing value for required string argument <%d:%s>", index, name);
-}
-
-bool lua_arg_bool (lua_State *L, int nargs, int index, const char *name, int def)
-{
-    bool value;
-    
-    // map index
-    index = lua_arg_index(L, nargs, index, name);
- 
-    // use default?
-    if (lua_isnoneornil(L, index) && def != LUA_ARG_REQUIRED)
-        return def;
-   
-    // value given?
-    value = lua_toboolean(L, index);
-
-    return value;
-    
-    // error
-    // luaL_error(L, "missing value of required boolean argument <%d:%s>", index, name);
-}
-
 /**
  * Wrapper for module
  */
--- a/src/lua_objs.h	Sun Apr 19 04:52:12 2009 +0300
+++ b/src/lua_objs.h	Sun Apr 19 06:20:59 2009 +0300
@@ -11,41 +11,8 @@
 #include <lua5.1/lua.h>
 #include <lua5.1/lauxlib.h>
 
-/**
- * Register a new metatable for a named type, this leaves the metatable on the stack.
- */
-void lua_obj_create_type (lua_State *L, const char *name, const struct luaL_Reg methods[]);
-
-/**
- * Create a new userdata with the given type metatable name, return the pointer, and keep it on the stack.
- */
-void* lua_obj_create_obj (lua_State *L, const char *name, size_t size);
-
-/**
- * Create a new metatable for a type, a userdata for that type, and register it as a global
- */
-void* lua_obj_create_global_type (lua_State *L, const char *type_name, const struct luaL_Reg methods[], const char *global_name, size_t size);
-
-/**
- * Get a userdata with the given type metatable name as the first argument for a function.
- */
-void* lua_obj_get_obj (lua_State *L, const char *func, const char *name);
-
-/**
- * Used as the "invalid" default value
- */
-#define LUA_ARG_REQUIRED (-1)
-#define LUA_ARG_STRING_REQUIRED ((const char *) (-1))
-
-/**
- * Parse and return a string argument
- */
-const char *lua_arg_string (lua_State *L, int nargs, int index, const char *name, const char *def);
-
-/**
- * Parse and return a boolean argument
- */
-bool lua_arg_bool (lua_State *L, int nargs, int index, const char *name, int def);
+// XXX: remove
+#include "lua_type.h"
 
 /**
  * Registers our lua runtime objects into the given lua state.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lua_type.c	Sun Apr 19 06:20:59 2009 +0300
@@ -0,0 +1,64 @@
+#include "lua_type.h"
+
+#include <lua5.1/lauxlib.h>
+
+void lua_obj_create_type (lua_State *L, const char *name, const struct luaL_Reg methods[])
+{
+    luaL_newmetatable(L, name);
+
+    // set the metatable __index to itself
+    lua_pushvalue(L, -1);
+    lua_setfield(L, -1, "__index");
+
+    // register the methods to the metatable
+    luaL_register(L, NULL, methods);
+}
+
+void* lua_obj_create_obj (lua_State *L, const char *name, size_t size)
+{
+    // create the new userdata on the stack
+    void *ud = lua_newuserdata(L, size);
+
+    // get the type and set it
+    luaL_getmetatable(L, name);
+    lua_setmetatable(L, -2);
+    
+    // ok
+    return ud;
+}
+
+void* lua_obj_create_global_type (lua_State *L, const char *type_name, const struct luaL_Reg methods[], const char *global_name, size_t size)
+{
+    // allocate the global object
+    void *obj = lua_newuserdata(L, size);
+    
+    // create the type metatable
+    lua_obj_create_type(L, type_name, methods);
+
+    // set the userdata's metatable
+    lua_setmetatable(L, -2);
+
+    // store it as a global
+    lua_setglobal(L, global_name);
+    
+    // ok
+    return obj;
+}
+
+void* lua_obj_get_obj (lua_State *L, const char *func, const char *name)
+{
+    void *ud;
+
+    // validate the userdata arg
+    if ((ud = luaL_checkudata(L, 1, name)) == NULL) {
+        luaL_error(L, "bad type argument to %s: `%s` expected", func, name);
+        
+        // XXX: needs a __noreturn__ attribute
+        return NULL;
+
+    } else {
+        // ok
+        return ud;
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lua_type.h	Sun Apr 19 06:20:59 2009 +0300
@@ -0,0 +1,34 @@
+#ifndef LUA_TYPE_H
+#define LUA_TYPE_H
+
+/**
+ * @file
+ *
+ * Convenience functions for defining "types" in lua
+ */
+#include <lua5.1/lua.h>
+
+// XXX: remove
+#include <lua5.1/lauxlib.h>
+
+/**
+ * Register a new metatable for a named type, this leaves the metatable on the stack.
+ */
+void lua_obj_create_type (lua_State *L, const char *name, const struct luaL_Reg methods[]);
+
+/**
+ * Create a new userdata with the given type metatable name, return the pointer, and keep it on the stack.
+ */
+void* lua_obj_create_obj (lua_State *L, const char *name, size_t size);
+
+/**
+ * Create a new metatable for a type, a userdata for that type, and register it as a global
+ */
+void* lua_obj_create_global_type (lua_State *L, const char *type_name, const struct luaL_Reg methods[], const char *global_name, size_t size);
+
+/**
+ * Get a userdata with the given type metatable name as the first argument for a function.
+ */
+void* lua_obj_get_obj (lua_State *L, const char *func, const char *name);
+
+#endif