xref: /freebsd/contrib/libucl/lua/lua_ucl.c (revision 11dd9ed6647d821e7b43d4f8e64412a2623fbab5)
14bf54857SBaptiste Daroussin /* Copyright (c) 2014, Vsevolod Stakhov
24bf54857SBaptiste Daroussin  * All rights reserved.
34bf54857SBaptiste Daroussin  *
44bf54857SBaptiste Daroussin  * Redistribution and use in source and binary forms, with or without
54bf54857SBaptiste Daroussin  * modification, are permitted provided that the following conditions are met:
64bf54857SBaptiste Daroussin  *       * Redistributions of source code must retain the above copyright
74bf54857SBaptiste Daroussin  *         notice, this list of conditions and the following disclaimer.
84bf54857SBaptiste Daroussin  *       * Redistributions in binary form must reproduce the above copyright
94bf54857SBaptiste Daroussin  *         notice, this list of conditions and the following disclaimer in the
104bf54857SBaptiste Daroussin  *         documentation and/or other materials provided with the distribution.
114bf54857SBaptiste Daroussin  *
124bf54857SBaptiste Daroussin  * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
134bf54857SBaptiste Daroussin  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
144bf54857SBaptiste Daroussin  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
154bf54857SBaptiste Daroussin  * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
164bf54857SBaptiste Daroussin  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
174bf54857SBaptiste Daroussin  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
184bf54857SBaptiste Daroussin  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
194bf54857SBaptiste Daroussin  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
204bf54857SBaptiste Daroussin  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
214bf54857SBaptiste Daroussin  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
224bf54857SBaptiste Daroussin  */
234bf54857SBaptiste Daroussin 
244bf54857SBaptiste Daroussin /**
254bf54857SBaptiste Daroussin  * @file lua ucl bindings
264bf54857SBaptiste Daroussin  */
274bf54857SBaptiste Daroussin 
284bf54857SBaptiste Daroussin #include "ucl.h"
294bf54857SBaptiste Daroussin #include "ucl_internal.h"
304bf54857SBaptiste Daroussin #include "lua_ucl.h"
314bf54857SBaptiste Daroussin #include <strings.h>
324bf54857SBaptiste Daroussin 
334bf54857SBaptiste Daroussin /***
344bf54857SBaptiste Daroussin  * @module ucl
354bf54857SBaptiste Daroussin  * This lua module allows to parse objects from strings and to store data into
364bf54857SBaptiste Daroussin  * ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects.
374bf54857SBaptiste Daroussin  * @example
384bf54857SBaptiste Daroussin local ucl = require("ucl")
394bf54857SBaptiste Daroussin 
404bf54857SBaptiste Daroussin local parser = ucl.parser()
414bf54857SBaptiste Daroussin local res,err = parser:parse_string('{key=value}')
424bf54857SBaptiste Daroussin 
434bf54857SBaptiste Daroussin if not res then
444bf54857SBaptiste Daroussin 	print('parser error: ' .. err)
454bf54857SBaptiste Daroussin else
464bf54857SBaptiste Daroussin 	local obj = parser:get_object()
474bf54857SBaptiste Daroussin 	local got = ucl.to_format(obj, 'json')
484bf54857SBaptiste Daroussin endif
494bf54857SBaptiste Daroussin 
504bf54857SBaptiste Daroussin local table = {
514bf54857SBaptiste Daroussin   str = 'value',
524bf54857SBaptiste Daroussin   num = 100500,
534bf54857SBaptiste Daroussin   null = ucl.null,
544bf54857SBaptiste Daroussin   func = function ()
554bf54857SBaptiste Daroussin     return 'huh'
564bf54857SBaptiste Daroussin   end
574bf54857SBaptiste Daroussin }
584bf54857SBaptiste Daroussin 
594bf54857SBaptiste Daroussin print(ucl.to_format(table, 'ucl'))
604bf54857SBaptiste Daroussin -- Output:
614bf54857SBaptiste Daroussin --[[
624bf54857SBaptiste Daroussin num = 100500;
634bf54857SBaptiste Daroussin str = "value";
644bf54857SBaptiste Daroussin null = null;
654bf54857SBaptiste Daroussin func = "huh";
664bf54857SBaptiste Daroussin --]]
674bf54857SBaptiste Daroussin  */
684bf54857SBaptiste Daroussin 
694bf54857SBaptiste Daroussin #define PARSER_META "ucl.parser.meta"
704bf54857SBaptiste Daroussin #define EMITTER_META "ucl.emitter.meta"
714bf54857SBaptiste Daroussin #define NULL_META "null.emitter.meta"
7239ee7a7aSBaptiste Daroussin #define OBJECT_META "ucl.object.meta"
734bf54857SBaptiste Daroussin 
744bf54857SBaptiste Daroussin static int ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj);
754bf54857SBaptiste Daroussin static int ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj, bool allow_array);
764bf54857SBaptiste Daroussin static ucl_object_t* ucl_object_lua_fromtable (lua_State *L, int idx);
774bf54857SBaptiste Daroussin static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx);
784bf54857SBaptiste Daroussin 
794bf54857SBaptiste Daroussin static void *ucl_null;
804bf54857SBaptiste Daroussin 
814bf54857SBaptiste Daroussin /**
824bf54857SBaptiste Daroussin  * Push a single element of an object to lua
834bf54857SBaptiste Daroussin  * @param L
844bf54857SBaptiste Daroussin  * @param key
854bf54857SBaptiste Daroussin  * @param obj
864bf54857SBaptiste Daroussin  */
874bf54857SBaptiste Daroussin static void
884bf54857SBaptiste Daroussin ucl_object_lua_push_element (lua_State *L, const char *key,
894bf54857SBaptiste Daroussin 		const ucl_object_t *obj)
904bf54857SBaptiste Daroussin {
914bf54857SBaptiste Daroussin 	lua_pushstring (L, key);
924bf54857SBaptiste Daroussin 	ucl_object_push_lua (L, obj, true);
934bf54857SBaptiste Daroussin 	lua_settable (L, -3);
944bf54857SBaptiste Daroussin }
954bf54857SBaptiste Daroussin 
964bf54857SBaptiste Daroussin static void
974bf54857SBaptiste Daroussin lua_ucl_userdata_dtor (void *ud)
984bf54857SBaptiste Daroussin {
994bf54857SBaptiste Daroussin 	struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
1004bf54857SBaptiste Daroussin 
1014bf54857SBaptiste Daroussin 	luaL_unref (fd->L, LUA_REGISTRYINDEX, fd->idx);
1024bf54857SBaptiste Daroussin 	if (fd->ret != NULL) {
1034bf54857SBaptiste Daroussin 		free (fd->ret);
1044bf54857SBaptiste Daroussin 	}
1054bf54857SBaptiste Daroussin 	free (fd);
1064bf54857SBaptiste Daroussin }
1074bf54857SBaptiste Daroussin 
1084bf54857SBaptiste Daroussin static const char *
1094bf54857SBaptiste Daroussin lua_ucl_userdata_emitter (void *ud)
1104bf54857SBaptiste Daroussin {
1114bf54857SBaptiste Daroussin 	struct ucl_lua_funcdata *fd = (struct ucl_lua_funcdata *)ud;
1124bf54857SBaptiste Daroussin 	const char *out = "";
1134bf54857SBaptiste Daroussin 
1144bf54857SBaptiste Daroussin 	lua_rawgeti (fd->L, LUA_REGISTRYINDEX, fd->idx);
1154bf54857SBaptiste Daroussin 
1164bf54857SBaptiste Daroussin 	lua_pcall (fd->L, 0, 1, 0);
1174bf54857SBaptiste Daroussin 	out = lua_tostring (fd->L, -1);
1184bf54857SBaptiste Daroussin 
1194bf54857SBaptiste Daroussin 	if (out != NULL) {
1204bf54857SBaptiste Daroussin 		/* We need to store temporary string in a more appropriate place */
1214bf54857SBaptiste Daroussin 		if (fd->ret) {
1224bf54857SBaptiste Daroussin 			free (fd->ret);
1234bf54857SBaptiste Daroussin 		}
1244bf54857SBaptiste Daroussin 		fd->ret = strdup (out);
1254bf54857SBaptiste Daroussin 	}
1264bf54857SBaptiste Daroussin 
1274bf54857SBaptiste Daroussin 	lua_settop (fd->L, 0);
1284bf54857SBaptiste Daroussin 
1294bf54857SBaptiste Daroussin 	return fd->ret;
1304bf54857SBaptiste Daroussin }
1314bf54857SBaptiste Daroussin 
1324bf54857SBaptiste Daroussin /**
1334bf54857SBaptiste Daroussin  * Push a single object to lua
1344bf54857SBaptiste Daroussin  * @param L
1354bf54857SBaptiste Daroussin  * @param obj
1364bf54857SBaptiste Daroussin  * @return
1374bf54857SBaptiste Daroussin  */
1384bf54857SBaptiste Daroussin static int
1394bf54857SBaptiste Daroussin ucl_object_lua_push_object (lua_State *L, const ucl_object_t *obj,
1404bf54857SBaptiste Daroussin 		bool allow_array)
1414bf54857SBaptiste Daroussin {
1424bf54857SBaptiste Daroussin 	const ucl_object_t *cur;
1434bf54857SBaptiste Daroussin 	ucl_object_iter_t it = NULL;
1444bf54857SBaptiste Daroussin 	int nelt = 0;
1454bf54857SBaptiste Daroussin 
1464bf54857SBaptiste Daroussin 	if (allow_array && obj->next != NULL) {
1474bf54857SBaptiste Daroussin 		/* Actually we need to push this as an array */
1484bf54857SBaptiste Daroussin 		return ucl_object_lua_push_array (L, obj);
1494bf54857SBaptiste Daroussin 	}
1504bf54857SBaptiste Daroussin 
1514bf54857SBaptiste Daroussin 	/* Optimize allocation by preallocation of table */
152d9f0ce31SBaptiste Daroussin 	while (ucl_object_iterate (obj, &it, true) != NULL) {
1534bf54857SBaptiste Daroussin 		nelt ++;
1544bf54857SBaptiste Daroussin 	}
1554bf54857SBaptiste Daroussin 
1564bf54857SBaptiste Daroussin 	lua_createtable (L, 0, nelt);
1574bf54857SBaptiste Daroussin 	it = NULL;
1584bf54857SBaptiste Daroussin 
159d9f0ce31SBaptiste Daroussin 	while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
1604bf54857SBaptiste Daroussin 		ucl_object_lua_push_element (L, ucl_object_key (cur), cur);
1614bf54857SBaptiste Daroussin 	}
1624bf54857SBaptiste Daroussin 
1634bf54857SBaptiste Daroussin 	return 1;
1644bf54857SBaptiste Daroussin }
1654bf54857SBaptiste Daroussin 
1664bf54857SBaptiste Daroussin /**
1674bf54857SBaptiste Daroussin  * Push an array to lua as table indexed by integers
1684bf54857SBaptiste Daroussin  * @param L
1694bf54857SBaptiste Daroussin  * @param obj
1704bf54857SBaptiste Daroussin  * @return
1714bf54857SBaptiste Daroussin  */
1724bf54857SBaptiste Daroussin static int
1734bf54857SBaptiste Daroussin ucl_object_lua_push_array (lua_State *L, const ucl_object_t *obj)
1744bf54857SBaptiste Daroussin {
1754bf54857SBaptiste Daroussin 	const ucl_object_t *cur;
17639ee7a7aSBaptiste Daroussin 	ucl_object_iter_t it;
1774bf54857SBaptiste Daroussin 	int i = 1, nelt = 0;
1784bf54857SBaptiste Daroussin 
17939ee7a7aSBaptiste Daroussin 	if (obj->type == UCL_ARRAY) {
18039ee7a7aSBaptiste Daroussin 		nelt = obj->len;
18139ee7a7aSBaptiste Daroussin 		it = ucl_object_iterate_new (obj);
18239ee7a7aSBaptiste Daroussin 		lua_createtable (L, nelt, 0);
18339ee7a7aSBaptiste Daroussin 
18439ee7a7aSBaptiste Daroussin 		while ((cur = ucl_object_iterate_safe (it, true))) {
18539ee7a7aSBaptiste Daroussin 			ucl_object_push_lua (L, cur, false);
18639ee7a7aSBaptiste Daroussin 			lua_rawseti (L, -2, i);
18739ee7a7aSBaptiste Daroussin 			i ++;
18839ee7a7aSBaptiste Daroussin 		}
189*11dd9ed6SBaptiste Daroussin 
190*11dd9ed6SBaptiste Daroussin 		ucl_object_iterate_free (it);
19139ee7a7aSBaptiste Daroussin 	}
19239ee7a7aSBaptiste Daroussin 	else {
1934bf54857SBaptiste Daroussin 		/* Optimize allocation by preallocation of table */
1944bf54857SBaptiste Daroussin 		LL_FOREACH (obj, cur) {
1954bf54857SBaptiste Daroussin 			nelt ++;
1964bf54857SBaptiste Daroussin 		}
1974bf54857SBaptiste Daroussin 
1984bf54857SBaptiste Daroussin 		lua_createtable (L, nelt, 0);
1994bf54857SBaptiste Daroussin 
2004bf54857SBaptiste Daroussin 		LL_FOREACH (obj, cur) {
2014bf54857SBaptiste Daroussin 			ucl_object_push_lua (L, cur, false);
2024bf54857SBaptiste Daroussin 			lua_rawseti (L, -2, i);
2034bf54857SBaptiste Daroussin 			i ++;
2044bf54857SBaptiste Daroussin 		}
20539ee7a7aSBaptiste Daroussin 	}
2064bf54857SBaptiste Daroussin 
2074bf54857SBaptiste Daroussin 	return 1;
2084bf54857SBaptiste Daroussin }
2094bf54857SBaptiste Daroussin 
2104bf54857SBaptiste Daroussin /**
2114bf54857SBaptiste Daroussin  * Push a simple object to lua depending on its actual type
2124bf54857SBaptiste Daroussin  */
2134bf54857SBaptiste Daroussin static int
2144bf54857SBaptiste Daroussin ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
2154bf54857SBaptiste Daroussin 		bool allow_array)
2164bf54857SBaptiste Daroussin {
2174bf54857SBaptiste Daroussin 	struct ucl_lua_funcdata *fd;
2184bf54857SBaptiste Daroussin 
2194bf54857SBaptiste Daroussin 	if (allow_array && obj->next != NULL) {
2204bf54857SBaptiste Daroussin 		/* Actually we need to push this as an array */
2214bf54857SBaptiste Daroussin 		return ucl_object_lua_push_array (L, obj);
2224bf54857SBaptiste Daroussin 	}
2234bf54857SBaptiste Daroussin 
2244bf54857SBaptiste Daroussin 	switch (obj->type) {
2254bf54857SBaptiste Daroussin 	case UCL_BOOLEAN:
2264bf54857SBaptiste Daroussin 		lua_pushboolean (L, ucl_obj_toboolean (obj));
2274bf54857SBaptiste Daroussin 		break;
2284bf54857SBaptiste Daroussin 	case UCL_STRING:
2294bf54857SBaptiste Daroussin 		lua_pushstring (L, ucl_obj_tostring (obj));
2304bf54857SBaptiste Daroussin 		break;
2314bf54857SBaptiste Daroussin 	case UCL_INT:
2324bf54857SBaptiste Daroussin #if LUA_VERSION_NUM >= 501
2334bf54857SBaptiste Daroussin 		lua_pushinteger (L, ucl_obj_toint (obj));
2344bf54857SBaptiste Daroussin #else
2354bf54857SBaptiste Daroussin 		lua_pushnumber (L, ucl_obj_toint (obj));
2364bf54857SBaptiste Daroussin #endif
2374bf54857SBaptiste Daroussin 		break;
2384bf54857SBaptiste Daroussin 	case UCL_FLOAT:
2394bf54857SBaptiste Daroussin 	case UCL_TIME:
2404bf54857SBaptiste Daroussin 		lua_pushnumber (L, ucl_obj_todouble (obj));
2414bf54857SBaptiste Daroussin 		break;
2424bf54857SBaptiste Daroussin 	case UCL_NULL:
2434bf54857SBaptiste Daroussin 		lua_getfield (L, LUA_REGISTRYINDEX, "ucl.null");
2444bf54857SBaptiste Daroussin 		break;
2454bf54857SBaptiste Daroussin 	case UCL_USERDATA:
2464bf54857SBaptiste Daroussin 		fd = (struct ucl_lua_funcdata *)obj->value.ud;
2474bf54857SBaptiste Daroussin 		lua_rawgeti (L, LUA_REGISTRYINDEX, fd->idx);
2484bf54857SBaptiste Daroussin 		break;
2494bf54857SBaptiste Daroussin 	default:
2504bf54857SBaptiste Daroussin 		lua_pushnil (L);
2514bf54857SBaptiste Daroussin 		break;
2524bf54857SBaptiste Daroussin 	}
2534bf54857SBaptiste Daroussin 
2544bf54857SBaptiste Daroussin 	return 1;
2554bf54857SBaptiste Daroussin }
2564bf54857SBaptiste Daroussin 
2574bf54857SBaptiste Daroussin /***
2584bf54857SBaptiste Daroussin  * @function ucl_object_push_lua(L, obj, allow_array)
2594bf54857SBaptiste Daroussin  * This is a `C` function to push `UCL` object as lua variable. This function
2604bf54857SBaptiste Daroussin  * converts `obj` to lua representation using the following conversions:
2614bf54857SBaptiste Daroussin  *
2624bf54857SBaptiste Daroussin  * - *scalar* values are directly presented by lua objects
2634bf54857SBaptiste Daroussin  * - *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`,
2644bf54857SBaptiste Daroussin  * this can be used to pass functions from lua to c and vice-versa
2654bf54857SBaptiste Daroussin  * - *arrays* are converted to lua tables with numeric indicies suitable for `ipairs` iterations
2664bf54857SBaptiste Daroussin  * - *objects* are converted to lua tables with string indicies
2674bf54857SBaptiste Daroussin  * @param {lua_State} L lua state pointer
2684bf54857SBaptiste Daroussin  * @param {ucl_object_t} obj object to push
2694bf54857SBaptiste Daroussin  * @param {bool} allow_array expand implicit arrays (should be true for all but partial arrays)
2704bf54857SBaptiste Daroussin  * @return {int} `1` if an object is pushed to lua
2714bf54857SBaptiste Daroussin  */
2724bf54857SBaptiste Daroussin int
2734bf54857SBaptiste Daroussin ucl_object_push_lua (lua_State *L, const ucl_object_t *obj, bool allow_array)
2744bf54857SBaptiste Daroussin {
2754bf54857SBaptiste Daroussin 	switch (obj->type) {
2764bf54857SBaptiste Daroussin 	case UCL_OBJECT:
2774bf54857SBaptiste Daroussin 		return ucl_object_lua_push_object (L, obj, allow_array);
2784bf54857SBaptiste Daroussin 	case UCL_ARRAY:
27939ee7a7aSBaptiste Daroussin 		return ucl_object_lua_push_array (L, obj);
2804bf54857SBaptiste Daroussin 	default:
2814bf54857SBaptiste Daroussin 		return ucl_object_lua_push_scalar (L, obj, allow_array);
2824bf54857SBaptiste Daroussin 	}
2834bf54857SBaptiste Daroussin }
2844bf54857SBaptiste Daroussin 
2854bf54857SBaptiste Daroussin /**
2864bf54857SBaptiste Daroussin  * Parse lua table into object top
2874bf54857SBaptiste Daroussin  * @param L
2884bf54857SBaptiste Daroussin  * @param top
2894bf54857SBaptiste Daroussin  * @param idx
2904bf54857SBaptiste Daroussin  */
2914bf54857SBaptiste Daroussin static ucl_object_t *
2924bf54857SBaptiste Daroussin ucl_object_lua_fromtable (lua_State *L, int idx)
2934bf54857SBaptiste Daroussin {
2944bf54857SBaptiste Daroussin 	ucl_object_t *obj, *top = NULL;
2954bf54857SBaptiste Daroussin 	size_t keylen;
2964bf54857SBaptiste Daroussin 	const char *k;
2974bf54857SBaptiste Daroussin 	bool is_array = true;
2984bf54857SBaptiste Daroussin 	int max = INT_MIN;
2994bf54857SBaptiste Daroussin 
3004bf54857SBaptiste Daroussin 	if (idx < 0) {
3014bf54857SBaptiste Daroussin 		/* For negative indicies we want to invert them */
3024bf54857SBaptiste Daroussin 		idx = lua_gettop (L) + idx + 1;
3034bf54857SBaptiste Daroussin 	}
3044bf54857SBaptiste Daroussin 	/* Check for array */
3054bf54857SBaptiste Daroussin 	lua_pushnil (L);
3064bf54857SBaptiste Daroussin 	while (lua_next (L, idx) != 0) {
3074bf54857SBaptiste Daroussin 		if (lua_type (L, -2) == LUA_TNUMBER) {
3084bf54857SBaptiste Daroussin 			double num = lua_tonumber (L, -2);
3094bf54857SBaptiste Daroussin 			if (num == (int)num) {
3104bf54857SBaptiste Daroussin 				if (num > max) {
3114bf54857SBaptiste Daroussin 					max = num;
3124bf54857SBaptiste Daroussin 				}
3134bf54857SBaptiste Daroussin 			}
3144bf54857SBaptiste Daroussin 			else {
3154bf54857SBaptiste Daroussin 				/* Keys are not integer */
3164bf54857SBaptiste Daroussin 				lua_pop (L, 2);
3174bf54857SBaptiste Daroussin 				is_array = false;
3184bf54857SBaptiste Daroussin 				break;
3194bf54857SBaptiste Daroussin 			}
3204bf54857SBaptiste Daroussin 		}
3214bf54857SBaptiste Daroussin 		else {
3224bf54857SBaptiste Daroussin 			/* Keys are not numeric */
3234bf54857SBaptiste Daroussin 			lua_pop (L, 2);
3244bf54857SBaptiste Daroussin 			is_array = false;
3254bf54857SBaptiste Daroussin 			break;
3264bf54857SBaptiste Daroussin 		}
3274bf54857SBaptiste Daroussin 		lua_pop (L, 1);
3284bf54857SBaptiste Daroussin 	}
3294bf54857SBaptiste Daroussin 
3304bf54857SBaptiste Daroussin 	/* Table iterate */
3314bf54857SBaptiste Daroussin 	if (is_array) {
3324bf54857SBaptiste Daroussin 		int i;
3334bf54857SBaptiste Daroussin 
3344bf54857SBaptiste Daroussin 		top = ucl_object_typed_new (UCL_ARRAY);
3354bf54857SBaptiste Daroussin 		for (i = 1; i <= max; i ++) {
3364bf54857SBaptiste Daroussin 			lua_pushinteger (L, i);
3374bf54857SBaptiste Daroussin 			lua_gettable (L, idx);
3384bf54857SBaptiste Daroussin 			obj = ucl_object_lua_fromelt (L, lua_gettop (L));
3394bf54857SBaptiste Daroussin 			if (obj != NULL) {
3404bf54857SBaptiste Daroussin 				ucl_array_append (top, obj);
3414bf54857SBaptiste Daroussin 			}
34239ee7a7aSBaptiste Daroussin 			lua_pop (L, 1);
3434bf54857SBaptiste Daroussin 		}
3444bf54857SBaptiste Daroussin 	}
3454bf54857SBaptiste Daroussin 	else {
3464bf54857SBaptiste Daroussin 		lua_pushnil (L);
3474bf54857SBaptiste Daroussin 		top = ucl_object_typed_new (UCL_OBJECT);
3484bf54857SBaptiste Daroussin 		while (lua_next (L, idx) != 0) {
3494bf54857SBaptiste Daroussin 			/* copy key to avoid modifications */
3504bf54857SBaptiste Daroussin 			k = lua_tolstring (L, -2, &keylen);
3514bf54857SBaptiste Daroussin 			obj = ucl_object_lua_fromelt (L, lua_gettop (L));
3524bf54857SBaptiste Daroussin 
3534bf54857SBaptiste Daroussin 			if (obj != NULL) {
3544bf54857SBaptiste Daroussin 				ucl_object_insert_key (top, obj, k, keylen, true);
3554bf54857SBaptiste Daroussin 			}
3564bf54857SBaptiste Daroussin 			lua_pop (L, 1);
3574bf54857SBaptiste Daroussin 		}
3584bf54857SBaptiste Daroussin 	}
3594bf54857SBaptiste Daroussin 
3604bf54857SBaptiste Daroussin 	return top;
3614bf54857SBaptiste Daroussin }
3624bf54857SBaptiste Daroussin 
3634bf54857SBaptiste Daroussin /**
3644bf54857SBaptiste Daroussin  * Get a single element from lua to object obj
3654bf54857SBaptiste Daroussin  * @param L
3664bf54857SBaptiste Daroussin  * @param obj
3674bf54857SBaptiste Daroussin  * @param idx
3684bf54857SBaptiste Daroussin  */
3694bf54857SBaptiste Daroussin static ucl_object_t *
3704bf54857SBaptiste Daroussin ucl_object_lua_fromelt (lua_State *L, int idx)
3714bf54857SBaptiste Daroussin {
3724bf54857SBaptiste Daroussin 	int type;
3734bf54857SBaptiste Daroussin 	double num;
3744bf54857SBaptiste Daroussin 	ucl_object_t *obj = NULL;
3754bf54857SBaptiste Daroussin 	struct ucl_lua_funcdata *fd;
3764bf54857SBaptiste Daroussin 
3774bf54857SBaptiste Daroussin 	type = lua_type (L, idx);
3784bf54857SBaptiste Daroussin 
3794bf54857SBaptiste Daroussin 	switch (type) {
3804bf54857SBaptiste Daroussin 	case LUA_TSTRING:
3814bf54857SBaptiste Daroussin 		obj = ucl_object_fromstring_common (lua_tostring (L, idx), 0, 0);
3824bf54857SBaptiste Daroussin 		break;
3834bf54857SBaptiste Daroussin 	case LUA_TNUMBER:
3844bf54857SBaptiste Daroussin 		num = lua_tonumber (L, idx);
3854bf54857SBaptiste Daroussin 		if (num == (int64_t)num) {
3864bf54857SBaptiste Daroussin 			obj = ucl_object_fromint (num);
3874bf54857SBaptiste Daroussin 		}
3884bf54857SBaptiste Daroussin 		else {
3894bf54857SBaptiste Daroussin 			obj = ucl_object_fromdouble (num);
3904bf54857SBaptiste Daroussin 		}
3914bf54857SBaptiste Daroussin 		break;
3924bf54857SBaptiste Daroussin 	case LUA_TBOOLEAN:
3934bf54857SBaptiste Daroussin 		obj = ucl_object_frombool (lua_toboolean (L, idx));
3944bf54857SBaptiste Daroussin 		break;
3954bf54857SBaptiste Daroussin 	case LUA_TUSERDATA:
3964bf54857SBaptiste Daroussin 		if (lua_topointer (L, idx) == ucl_null) {
3974bf54857SBaptiste Daroussin 			obj = ucl_object_typed_new (UCL_NULL);
3984bf54857SBaptiste Daroussin 		}
3994bf54857SBaptiste Daroussin 		break;
4004bf54857SBaptiste Daroussin 	case LUA_TTABLE:
4014bf54857SBaptiste Daroussin 	case LUA_TFUNCTION:
4024bf54857SBaptiste Daroussin 	case LUA_TTHREAD:
4034bf54857SBaptiste Daroussin 		if (luaL_getmetafield (L, idx, "__gen_ucl")) {
4044bf54857SBaptiste Daroussin 			if (lua_isfunction (L, -1)) {
4054bf54857SBaptiste Daroussin 				lua_settop (L, 3); /* gen, obj, func */
4064bf54857SBaptiste Daroussin 				lua_insert (L, 1); /* func, gen, obj */
4074bf54857SBaptiste Daroussin 				lua_insert (L, 2); /* func, obj, gen */
4084bf54857SBaptiste Daroussin 				lua_call(L, 2, 1);
4094bf54857SBaptiste Daroussin 				obj = ucl_object_lua_fromelt (L, 1);
4104bf54857SBaptiste Daroussin 			}
4114bf54857SBaptiste Daroussin 			lua_pop (L, 2);
4124bf54857SBaptiste Daroussin 		}
4134bf54857SBaptiste Daroussin 		else {
4144bf54857SBaptiste Daroussin 			if (type == LUA_TTABLE) {
4154bf54857SBaptiste Daroussin 				obj = ucl_object_lua_fromtable (L, idx);
4164bf54857SBaptiste Daroussin 			}
4174bf54857SBaptiste Daroussin 			else if (type == LUA_TFUNCTION) {
4184bf54857SBaptiste Daroussin 				fd = malloc (sizeof (*fd));
4194bf54857SBaptiste Daroussin 				if (fd != NULL) {
4204bf54857SBaptiste Daroussin 					lua_pushvalue (L, idx);
4214bf54857SBaptiste Daroussin 					fd->L = L;
4224bf54857SBaptiste Daroussin 					fd->ret = NULL;
4234bf54857SBaptiste Daroussin 					fd->idx = luaL_ref (L, LUA_REGISTRYINDEX);
4244bf54857SBaptiste Daroussin 
4254bf54857SBaptiste Daroussin 					obj = ucl_object_new_userdata (lua_ucl_userdata_dtor,
426d9f0ce31SBaptiste Daroussin 							lua_ucl_userdata_emitter, (void *)fd);
4274bf54857SBaptiste Daroussin 				}
4284bf54857SBaptiste Daroussin 			}
4294bf54857SBaptiste Daroussin 		}
4304bf54857SBaptiste Daroussin 		break;
4314bf54857SBaptiste Daroussin 	}
4324bf54857SBaptiste Daroussin 
4334bf54857SBaptiste Daroussin 	return obj;
4344bf54857SBaptiste Daroussin }
4354bf54857SBaptiste Daroussin 
4364bf54857SBaptiste Daroussin /**
4374bf54857SBaptiste Daroussin  * @function ucl_object_lua_import(L, idx)
4384bf54857SBaptiste Daroussin  * Extracts ucl object from lua variable at `idx` position,
4394bf54857SBaptiste Daroussin  * @see ucl_object_push_lua for conversion definitions
4404bf54857SBaptiste Daroussin  * @param {lua_state} L lua state machine pointer
4414bf54857SBaptiste Daroussin  * @param {int} idx index where the source variable is placed
4424bf54857SBaptiste Daroussin  * @return {ucl_object_t} new ucl object extracted from lua variable. Reference count of this object is 1,
4434bf54857SBaptiste Daroussin  * this object thus needs to be unref'ed after usage.
4444bf54857SBaptiste Daroussin  */
4454bf54857SBaptiste Daroussin ucl_object_t *
4464bf54857SBaptiste Daroussin ucl_object_lua_import (lua_State *L, int idx)
4474bf54857SBaptiste Daroussin {
4484bf54857SBaptiste Daroussin 	ucl_object_t *obj;
4494bf54857SBaptiste Daroussin 	int t;
4504bf54857SBaptiste Daroussin 
4514bf54857SBaptiste Daroussin 	t = lua_type (L, idx);
4524bf54857SBaptiste Daroussin 	switch (t) {
4534bf54857SBaptiste Daroussin 	case LUA_TTABLE:
4544bf54857SBaptiste Daroussin 		obj = ucl_object_lua_fromtable (L, idx);
4554bf54857SBaptiste Daroussin 		break;
4564bf54857SBaptiste Daroussin 	default:
4574bf54857SBaptiste Daroussin 		obj = ucl_object_lua_fromelt (L, idx);
4584bf54857SBaptiste Daroussin 		break;
4594bf54857SBaptiste Daroussin 	}
4604bf54857SBaptiste Daroussin 
4614bf54857SBaptiste Daroussin 	return obj;
4624bf54857SBaptiste Daroussin }
4634bf54857SBaptiste Daroussin 
4644bf54857SBaptiste Daroussin static int
46539ee7a7aSBaptiste Daroussin lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
46639ee7a7aSBaptiste Daroussin {
46739ee7a7aSBaptiste Daroussin 	unsigned char *result;
46839ee7a7aSBaptiste Daroussin 
46939ee7a7aSBaptiste Daroussin 	result = ucl_object_emit (obj, type);
47039ee7a7aSBaptiste Daroussin 
47139ee7a7aSBaptiste Daroussin 	if (result != NULL) {
47239ee7a7aSBaptiste Daroussin 		lua_pushstring (L, (const char *)result);
47339ee7a7aSBaptiste Daroussin 		free (result);
47439ee7a7aSBaptiste Daroussin 	}
47539ee7a7aSBaptiste Daroussin 	else {
47639ee7a7aSBaptiste Daroussin 		lua_pushnil (L);
47739ee7a7aSBaptiste Daroussin 	}
47839ee7a7aSBaptiste Daroussin 
47939ee7a7aSBaptiste Daroussin 	return 1;
48039ee7a7aSBaptiste Daroussin }
48139ee7a7aSBaptiste Daroussin 
48239ee7a7aSBaptiste Daroussin static int
4834bf54857SBaptiste Daroussin lua_ucl_parser_init (lua_State *L)
4844bf54857SBaptiste Daroussin {
4854bf54857SBaptiste Daroussin 	struct ucl_parser *parser, **pparser;
486*11dd9ed6SBaptiste Daroussin 	int flags = UCL_PARSER_NO_FILEVARS;
4874bf54857SBaptiste Daroussin 
4884bf54857SBaptiste Daroussin 	if (lua_gettop (L) >= 1) {
4894bf54857SBaptiste Daroussin 		flags = lua_tonumber (L, 1);
4904bf54857SBaptiste Daroussin 	}
4914bf54857SBaptiste Daroussin 
4924bf54857SBaptiste Daroussin 	parser = ucl_parser_new (flags);
4934bf54857SBaptiste Daroussin 	if (parser == NULL) {
4944bf54857SBaptiste Daroussin 		lua_pushnil (L);
4954bf54857SBaptiste Daroussin 	}
4964bf54857SBaptiste Daroussin 
4974bf54857SBaptiste Daroussin 	pparser = lua_newuserdata (L, sizeof (parser));
4984bf54857SBaptiste Daroussin 	*pparser = parser;
4994bf54857SBaptiste Daroussin 	luaL_getmetatable (L, PARSER_META);
5004bf54857SBaptiste Daroussin 	lua_setmetatable (L, -2);
5014bf54857SBaptiste Daroussin 
5024bf54857SBaptiste Daroussin 	return 1;
5034bf54857SBaptiste Daroussin }
5044bf54857SBaptiste Daroussin 
5054bf54857SBaptiste Daroussin static struct ucl_parser *
5064bf54857SBaptiste Daroussin lua_ucl_parser_get (lua_State *L, int index)
5074bf54857SBaptiste Daroussin {
5084bf54857SBaptiste Daroussin 	return *((struct ucl_parser **) luaL_checkudata(L, index, PARSER_META));
5094bf54857SBaptiste Daroussin }
5104bf54857SBaptiste Daroussin 
51139ee7a7aSBaptiste Daroussin static ucl_object_t *
51239ee7a7aSBaptiste Daroussin lua_ucl_object_get (lua_State *L, int index)
51339ee7a7aSBaptiste Daroussin {
51439ee7a7aSBaptiste Daroussin 	return *((ucl_object_t **) luaL_checkudata(L, index, OBJECT_META));
51539ee7a7aSBaptiste Daroussin }
51639ee7a7aSBaptiste Daroussin 
517d9f0ce31SBaptiste Daroussin static void
518d9f0ce31SBaptiste Daroussin lua_ucl_push_opaque (lua_State *L, ucl_object_t *obj)
519d9f0ce31SBaptiste Daroussin {
520d9f0ce31SBaptiste Daroussin 	ucl_object_t **pobj;
521d9f0ce31SBaptiste Daroussin 
522d9f0ce31SBaptiste Daroussin 	pobj = lua_newuserdata (L, sizeof (*pobj));
523d9f0ce31SBaptiste Daroussin 	*pobj = obj;
524d9f0ce31SBaptiste Daroussin 	luaL_getmetatable (L, OBJECT_META);
525d9f0ce31SBaptiste Daroussin 	lua_setmetatable (L, -2);
526d9f0ce31SBaptiste Daroussin }
527d9f0ce31SBaptiste Daroussin 
528*11dd9ed6SBaptiste Daroussin static inline enum ucl_parse_type
529*11dd9ed6SBaptiste Daroussin lua_ucl_str_to_parse_type (const char *str)
530*11dd9ed6SBaptiste Daroussin {
531*11dd9ed6SBaptiste Daroussin 	enum ucl_parse_type type = UCL_PARSE_UCL;
532*11dd9ed6SBaptiste Daroussin 
533*11dd9ed6SBaptiste Daroussin 	if (str != NULL) {
534*11dd9ed6SBaptiste Daroussin 		if (strcasecmp (str, "msgpack") == 0) {
535*11dd9ed6SBaptiste Daroussin 			type = UCL_PARSE_MSGPACK;
536*11dd9ed6SBaptiste Daroussin 		}
537*11dd9ed6SBaptiste Daroussin 		else if (strcasecmp (str, "sexp") == 0 ||
538*11dd9ed6SBaptiste Daroussin 				strcasecmp (str, "csexp") == 0) {
539*11dd9ed6SBaptiste Daroussin 			type = UCL_PARSE_CSEXP;
540*11dd9ed6SBaptiste Daroussin 		}
541*11dd9ed6SBaptiste Daroussin 		else if (strcasecmp (str, "auto") == 0) {
542*11dd9ed6SBaptiste Daroussin 			type = UCL_PARSE_AUTO;
543*11dd9ed6SBaptiste Daroussin 		}
544*11dd9ed6SBaptiste Daroussin 	}
545*11dd9ed6SBaptiste Daroussin 
546*11dd9ed6SBaptiste Daroussin 	return type;
547*11dd9ed6SBaptiste Daroussin }
548*11dd9ed6SBaptiste Daroussin 
5494bf54857SBaptiste Daroussin /***
5504bf54857SBaptiste Daroussin  * @method parser:parse_file(name)
5514bf54857SBaptiste Daroussin  * Parse UCL object from file.
5524bf54857SBaptiste Daroussin  * @param {string} name filename to parse
5534bf54857SBaptiste Daroussin  * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
5544bf54857SBaptiste Daroussin @example
5554bf54857SBaptiste Daroussin local parser = ucl.parser()
5564bf54857SBaptiste Daroussin local res,err = parser:parse_file('/some/file.conf')
5574bf54857SBaptiste Daroussin 
5584bf54857SBaptiste Daroussin if not res then
5594bf54857SBaptiste Daroussin 	print('parser error: ' .. err)
5604bf54857SBaptiste Daroussin else
5614bf54857SBaptiste Daroussin 	-- Do something with object
5624bf54857SBaptiste Daroussin end
5634bf54857SBaptiste Daroussin  */
5644bf54857SBaptiste Daroussin static int
5654bf54857SBaptiste Daroussin lua_ucl_parser_parse_file (lua_State *L)
5664bf54857SBaptiste Daroussin {
5674bf54857SBaptiste Daroussin 	struct ucl_parser *parser;
5684bf54857SBaptiste Daroussin 	const char *file;
5694bf54857SBaptiste Daroussin 	int ret = 2;
5704bf54857SBaptiste Daroussin 
5714bf54857SBaptiste Daroussin 	parser = lua_ucl_parser_get (L, 1);
5724bf54857SBaptiste Daroussin 	file = luaL_checkstring (L, 2);
5734bf54857SBaptiste Daroussin 
5744bf54857SBaptiste Daroussin 	if (parser != NULL && file != NULL) {
5754bf54857SBaptiste Daroussin 		if (ucl_parser_add_file (parser, file)) {
5764bf54857SBaptiste Daroussin 			lua_pushboolean (L, true);
5774bf54857SBaptiste Daroussin 			ret = 1;
5784bf54857SBaptiste Daroussin 		}
5794bf54857SBaptiste Daroussin 		else {
5804bf54857SBaptiste Daroussin 			lua_pushboolean (L, false);
5814bf54857SBaptiste Daroussin 			lua_pushstring (L, ucl_parser_get_error (parser));
5824bf54857SBaptiste Daroussin 		}
5834bf54857SBaptiste Daroussin 	}
5844bf54857SBaptiste Daroussin 	else {
5854bf54857SBaptiste Daroussin 		lua_pushboolean (L, false);
5864bf54857SBaptiste Daroussin 		lua_pushstring (L, "invalid arguments");
5874bf54857SBaptiste Daroussin 	}
5884bf54857SBaptiste Daroussin 
5894bf54857SBaptiste Daroussin 	return ret;
5904bf54857SBaptiste Daroussin }
5914bf54857SBaptiste Daroussin 
5924bf54857SBaptiste Daroussin /***
5934bf54857SBaptiste Daroussin  * @method parser:parse_string(input)
5944bf54857SBaptiste Daroussin  * Parse UCL object from file.
5954bf54857SBaptiste Daroussin  * @param {string} input string to parse
5964bf54857SBaptiste Daroussin  * @return {bool[, string]} if res is `true` then file has been parsed successfully, otherwise an error string is also returned
5974bf54857SBaptiste Daroussin  */
5984bf54857SBaptiste Daroussin static int
5994bf54857SBaptiste Daroussin lua_ucl_parser_parse_string (lua_State *L)
6004bf54857SBaptiste Daroussin {
6014bf54857SBaptiste Daroussin 	struct ucl_parser *parser;
6024bf54857SBaptiste Daroussin 	const char *string;
6034bf54857SBaptiste Daroussin 	size_t llen;
604*11dd9ed6SBaptiste Daroussin 	enum ucl_parse_type type = UCL_PARSE_UCL;
6054bf54857SBaptiste Daroussin 	int ret = 2;
6064bf54857SBaptiste Daroussin 
6074bf54857SBaptiste Daroussin 	parser = lua_ucl_parser_get (L, 1);
6084bf54857SBaptiste Daroussin 	string = luaL_checklstring (L, 2, &llen);
6094bf54857SBaptiste Daroussin 
610*11dd9ed6SBaptiste Daroussin 	if (lua_type (L, 3) == LUA_TSTRING) {
611*11dd9ed6SBaptiste Daroussin 		type = lua_ucl_str_to_parse_type (lua_tostring (L, 3));
612*11dd9ed6SBaptiste Daroussin 	}
613*11dd9ed6SBaptiste Daroussin 
6144bf54857SBaptiste Daroussin 	if (parser != NULL && string != NULL) {
615*11dd9ed6SBaptiste Daroussin 		if (ucl_parser_add_chunk_full (parser, (const unsigned char *)string,
616*11dd9ed6SBaptiste Daroussin 				llen, 0, UCL_DUPLICATE_APPEND, type)) {
6174bf54857SBaptiste Daroussin 			lua_pushboolean (L, true);
6184bf54857SBaptiste Daroussin 			ret = 1;
6194bf54857SBaptiste Daroussin 		}
6204bf54857SBaptiste Daroussin 		else {
6214bf54857SBaptiste Daroussin 			lua_pushboolean (L, false);
6224bf54857SBaptiste Daroussin 			lua_pushstring (L, ucl_parser_get_error (parser));
6234bf54857SBaptiste Daroussin 		}
6244bf54857SBaptiste Daroussin 	}
6254bf54857SBaptiste Daroussin 	else {
6264bf54857SBaptiste Daroussin 		lua_pushboolean (L, false);
6274bf54857SBaptiste Daroussin 		lua_pushstring (L, "invalid arguments");
6284bf54857SBaptiste Daroussin 	}
6294bf54857SBaptiste Daroussin 
6304bf54857SBaptiste Daroussin 	return ret;
6314bf54857SBaptiste Daroussin }
6324bf54857SBaptiste Daroussin 
6334bf54857SBaptiste Daroussin /***
6344bf54857SBaptiste Daroussin  * @method parser:get_object()
6354bf54857SBaptiste Daroussin  * Get top object from parser and export it to lua representation.
6364bf54857SBaptiste Daroussin  * @return {variant or nil} ucl object as lua native variable
6374bf54857SBaptiste Daroussin  */
6384bf54857SBaptiste Daroussin static int
6394bf54857SBaptiste Daroussin lua_ucl_parser_get_object (lua_State *L)
6404bf54857SBaptiste Daroussin {
6414bf54857SBaptiste Daroussin 	struct ucl_parser *parser;
6424bf54857SBaptiste Daroussin 	ucl_object_t *obj;
6434bf54857SBaptiste Daroussin 	int ret = 1;
6444bf54857SBaptiste Daroussin 
6454bf54857SBaptiste Daroussin 	parser = lua_ucl_parser_get (L, 1);
6464bf54857SBaptiste Daroussin 	obj = ucl_parser_get_object (parser);
6474bf54857SBaptiste Daroussin 
6484bf54857SBaptiste Daroussin 	if (obj != NULL) {
6494bf54857SBaptiste Daroussin 		ret = ucl_object_push_lua (L, obj, false);
6504bf54857SBaptiste Daroussin 		/* no need to keep reference */
6514bf54857SBaptiste Daroussin 		ucl_object_unref (obj);
6524bf54857SBaptiste Daroussin 	}
6534bf54857SBaptiste Daroussin 	else {
6544bf54857SBaptiste Daroussin 		lua_pushnil (L);
6554bf54857SBaptiste Daroussin 	}
6564bf54857SBaptiste Daroussin 
6574bf54857SBaptiste Daroussin 	return ret;
6584bf54857SBaptiste Daroussin }
6594bf54857SBaptiste Daroussin 
66039ee7a7aSBaptiste Daroussin /***
66139ee7a7aSBaptiste Daroussin  * @method parser:get_object_wrapped()
66239ee7a7aSBaptiste Daroussin  * Get top object from parser and export it to userdata object without
66339ee7a7aSBaptiste Daroussin  * unwrapping to lua.
66439ee7a7aSBaptiste Daroussin  * @return {ucl.object or nil} ucl object wrapped variable
66539ee7a7aSBaptiste Daroussin  */
66639ee7a7aSBaptiste Daroussin static int
66739ee7a7aSBaptiste Daroussin lua_ucl_parser_get_object_wrapped (lua_State *L)
66839ee7a7aSBaptiste Daroussin {
66939ee7a7aSBaptiste Daroussin 	struct ucl_parser *parser;
670d9f0ce31SBaptiste Daroussin 	ucl_object_t *obj;
67139ee7a7aSBaptiste Daroussin 	int ret = 1;
67239ee7a7aSBaptiste Daroussin 
67339ee7a7aSBaptiste Daroussin 	parser = lua_ucl_parser_get (L, 1);
67439ee7a7aSBaptiste Daroussin 	obj = ucl_parser_get_object (parser);
67539ee7a7aSBaptiste Daroussin 
67639ee7a7aSBaptiste Daroussin 	if (obj != NULL) {
677d9f0ce31SBaptiste Daroussin 		lua_ucl_push_opaque (L, obj);
67839ee7a7aSBaptiste Daroussin 	}
67939ee7a7aSBaptiste Daroussin 	else {
68039ee7a7aSBaptiste Daroussin 		lua_pushnil (L);
68139ee7a7aSBaptiste Daroussin 	}
68239ee7a7aSBaptiste Daroussin 
68339ee7a7aSBaptiste Daroussin 	return ret;
68439ee7a7aSBaptiste Daroussin }
68539ee7a7aSBaptiste Daroussin 
68639ee7a7aSBaptiste Daroussin /***
68739ee7a7aSBaptiste Daroussin  * @method parser:validate(schema)
68839ee7a7aSBaptiste Daroussin  * Validates the top object in the parser against schema. Schema might be
68939ee7a7aSBaptiste Daroussin  * another object or a string that represents file to load schema from.
69039ee7a7aSBaptiste Daroussin  *
69139ee7a7aSBaptiste Daroussin  * @param {string/table} schema input schema
69239ee7a7aSBaptiste Daroussin  * @return {result,err} two values: boolean result and the corresponding error
69339ee7a7aSBaptiste Daroussin  *
69439ee7a7aSBaptiste Daroussin  */
69539ee7a7aSBaptiste Daroussin static int
69639ee7a7aSBaptiste Daroussin lua_ucl_parser_validate (lua_State *L)
69739ee7a7aSBaptiste Daroussin {
69839ee7a7aSBaptiste Daroussin 	struct ucl_parser *parser, *schema_parser;
69939ee7a7aSBaptiste Daroussin 	ucl_object_t *schema;
70039ee7a7aSBaptiste Daroussin 	const char *schema_file;
70139ee7a7aSBaptiste Daroussin 	struct ucl_schema_error err;
70239ee7a7aSBaptiste Daroussin 
70339ee7a7aSBaptiste Daroussin 	parser = lua_ucl_parser_get (L, 1);
70439ee7a7aSBaptiste Daroussin 
70539ee7a7aSBaptiste Daroussin 	if (parser && parser->top_obj) {
70639ee7a7aSBaptiste Daroussin 		if (lua_type (L, 2) == LUA_TTABLE) {
70739ee7a7aSBaptiste Daroussin 			schema = ucl_object_lua_import (L, 2);
70839ee7a7aSBaptiste Daroussin 
70939ee7a7aSBaptiste Daroussin 			if (schema == NULL) {
71039ee7a7aSBaptiste Daroussin 				lua_pushboolean (L, false);
71139ee7a7aSBaptiste Daroussin 				lua_pushstring (L, "cannot load schema from lua table");
71239ee7a7aSBaptiste Daroussin 
71339ee7a7aSBaptiste Daroussin 				return 2;
71439ee7a7aSBaptiste Daroussin 			}
71539ee7a7aSBaptiste Daroussin 		}
71639ee7a7aSBaptiste Daroussin 		else if (lua_type (L, 2) == LUA_TSTRING) {
71739ee7a7aSBaptiste Daroussin 			schema_parser = ucl_parser_new (0);
71839ee7a7aSBaptiste Daroussin 			schema_file = luaL_checkstring (L, 2);
71939ee7a7aSBaptiste Daroussin 
72039ee7a7aSBaptiste Daroussin 			if (!ucl_parser_add_file (schema_parser, schema_file)) {
72139ee7a7aSBaptiste Daroussin 				lua_pushboolean (L, false);
72239ee7a7aSBaptiste Daroussin 				lua_pushfstring (L, "cannot parse schema file \"%s\": "
72339ee7a7aSBaptiste Daroussin 						"%s", schema_file, ucl_parser_get_error (parser));
72439ee7a7aSBaptiste Daroussin 				ucl_parser_free (schema_parser);
72539ee7a7aSBaptiste Daroussin 
72639ee7a7aSBaptiste Daroussin 				return 2;
72739ee7a7aSBaptiste Daroussin 			}
72839ee7a7aSBaptiste Daroussin 
72939ee7a7aSBaptiste Daroussin 			schema = ucl_parser_get_object (schema_parser);
73039ee7a7aSBaptiste Daroussin 			ucl_parser_free (schema_parser);
73139ee7a7aSBaptiste Daroussin 		}
73239ee7a7aSBaptiste Daroussin 		else {
73339ee7a7aSBaptiste Daroussin 			lua_pushboolean (L, false);
73439ee7a7aSBaptiste Daroussin 			lua_pushstring (L, "invalid schema argument");
73539ee7a7aSBaptiste Daroussin 
73639ee7a7aSBaptiste Daroussin 			return 2;
73739ee7a7aSBaptiste Daroussin 		}
73839ee7a7aSBaptiste Daroussin 
73939ee7a7aSBaptiste Daroussin 		if (!ucl_object_validate (schema, parser->top_obj, &err)) {
74039ee7a7aSBaptiste Daroussin 			lua_pushboolean (L, false);
74139ee7a7aSBaptiste Daroussin 			lua_pushfstring (L, "validation error: "
74239ee7a7aSBaptiste Daroussin 					"%s", err.msg);
74339ee7a7aSBaptiste Daroussin 		}
74439ee7a7aSBaptiste Daroussin 		else {
74539ee7a7aSBaptiste Daroussin 			lua_pushboolean (L, true);
74639ee7a7aSBaptiste Daroussin 			lua_pushnil (L);
74739ee7a7aSBaptiste Daroussin 		}
74839ee7a7aSBaptiste Daroussin 
74939ee7a7aSBaptiste Daroussin 		ucl_object_unref (schema);
75039ee7a7aSBaptiste Daroussin 	}
75139ee7a7aSBaptiste Daroussin 	else {
75239ee7a7aSBaptiste Daroussin 		lua_pushboolean (L, false);
75339ee7a7aSBaptiste Daroussin 		lua_pushstring (L, "invalid parser or empty top object");
75439ee7a7aSBaptiste Daroussin 	}
75539ee7a7aSBaptiste Daroussin 
75639ee7a7aSBaptiste Daroussin 	return 2;
75739ee7a7aSBaptiste Daroussin }
75839ee7a7aSBaptiste Daroussin 
7594bf54857SBaptiste Daroussin static int
7604bf54857SBaptiste Daroussin lua_ucl_parser_gc (lua_State *L)
7614bf54857SBaptiste Daroussin {
7624bf54857SBaptiste Daroussin 	struct ucl_parser *parser;
7634bf54857SBaptiste Daroussin 
7644bf54857SBaptiste Daroussin 	parser = lua_ucl_parser_get (L, 1);
7654bf54857SBaptiste Daroussin 	ucl_parser_free (parser);
7664bf54857SBaptiste Daroussin 
7674bf54857SBaptiste Daroussin 	return 0;
7684bf54857SBaptiste Daroussin }
7694bf54857SBaptiste Daroussin 
77039ee7a7aSBaptiste Daroussin /***
77139ee7a7aSBaptiste Daroussin  * @method object:unwrap()
77239ee7a7aSBaptiste Daroussin  * Unwraps opaque ucl object to the native lua object (performing copying)
77339ee7a7aSBaptiste Daroussin  * @return {variant} any lua object
77439ee7a7aSBaptiste Daroussin  */
77539ee7a7aSBaptiste Daroussin static int
77639ee7a7aSBaptiste Daroussin lua_ucl_object_unwrap (lua_State *L)
77739ee7a7aSBaptiste Daroussin {
77839ee7a7aSBaptiste Daroussin 	ucl_object_t *obj;
77939ee7a7aSBaptiste Daroussin 
78039ee7a7aSBaptiste Daroussin 	obj = lua_ucl_object_get (L, 1);
78139ee7a7aSBaptiste Daroussin 
78239ee7a7aSBaptiste Daroussin 	if (obj) {
78339ee7a7aSBaptiste Daroussin 		ucl_object_push_lua (L, obj, true);
78439ee7a7aSBaptiste Daroussin 	}
78539ee7a7aSBaptiste Daroussin 	else {
78639ee7a7aSBaptiste Daroussin 		lua_pushnil (L);
78739ee7a7aSBaptiste Daroussin 	}
78839ee7a7aSBaptiste Daroussin 
78939ee7a7aSBaptiste Daroussin 	return 1;
79039ee7a7aSBaptiste Daroussin }
79139ee7a7aSBaptiste Daroussin 
792*11dd9ed6SBaptiste Daroussin static inline enum ucl_emitter
793*11dd9ed6SBaptiste Daroussin lua_ucl_str_to_emit_type (const char *strtype)
794*11dd9ed6SBaptiste Daroussin {
795*11dd9ed6SBaptiste Daroussin 	enum ucl_emitter format = UCL_EMIT_JSON_COMPACT;
796*11dd9ed6SBaptiste Daroussin 
797*11dd9ed6SBaptiste Daroussin 	if (strcasecmp (strtype, "json") == 0) {
798*11dd9ed6SBaptiste Daroussin 		format = UCL_EMIT_JSON;
799*11dd9ed6SBaptiste Daroussin 	}
800*11dd9ed6SBaptiste Daroussin 	else if (strcasecmp (strtype, "json-compact") == 0) {
801*11dd9ed6SBaptiste Daroussin 		format = UCL_EMIT_JSON_COMPACT;
802*11dd9ed6SBaptiste Daroussin 	}
803*11dd9ed6SBaptiste Daroussin 	else if (strcasecmp (strtype, "yaml") == 0) {
804*11dd9ed6SBaptiste Daroussin 		format = UCL_EMIT_YAML;
805*11dd9ed6SBaptiste Daroussin 	}
806*11dd9ed6SBaptiste Daroussin 	else if (strcasecmp (strtype, "config") == 0 ||
807*11dd9ed6SBaptiste Daroussin 			strcasecmp (strtype, "ucl") == 0) {
808*11dd9ed6SBaptiste Daroussin 		format = UCL_EMIT_CONFIG;
809*11dd9ed6SBaptiste Daroussin 	}
810*11dd9ed6SBaptiste Daroussin 
811*11dd9ed6SBaptiste Daroussin 	return format;
812*11dd9ed6SBaptiste Daroussin }
813*11dd9ed6SBaptiste Daroussin 
81439ee7a7aSBaptiste Daroussin /***
81539ee7a7aSBaptiste Daroussin  * @method object:tostring(type)
81639ee7a7aSBaptiste Daroussin  * Unwraps opaque ucl object to string (json by default). Optionally you can
81739ee7a7aSBaptiste Daroussin  * specify output format:
81839ee7a7aSBaptiste Daroussin  *
81939ee7a7aSBaptiste Daroussin  * - `json` - fine printed json
82039ee7a7aSBaptiste Daroussin  * - `json-compact` - compacted json
82139ee7a7aSBaptiste Daroussin  * - `config` - fine printed configuration
82239ee7a7aSBaptiste Daroussin  * - `ucl` - same as `config`
82339ee7a7aSBaptiste Daroussin  * - `yaml` - embedded yaml
82439ee7a7aSBaptiste Daroussin  * @param {string} type optional
82539ee7a7aSBaptiste Daroussin  * @return {string} string representation of the opaque ucl object
82639ee7a7aSBaptiste Daroussin  */
82739ee7a7aSBaptiste Daroussin static int
82839ee7a7aSBaptiste Daroussin lua_ucl_object_tostring (lua_State *L)
82939ee7a7aSBaptiste Daroussin {
83039ee7a7aSBaptiste Daroussin 	ucl_object_t *obj;
83139ee7a7aSBaptiste Daroussin 	enum ucl_emitter format = UCL_EMIT_JSON_COMPACT;
83239ee7a7aSBaptiste Daroussin 
83339ee7a7aSBaptiste Daroussin 	obj = lua_ucl_object_get (L, 1);
83439ee7a7aSBaptiste Daroussin 
83539ee7a7aSBaptiste Daroussin 	if (obj) {
83639ee7a7aSBaptiste Daroussin 		if (lua_gettop (L) > 1) {
83739ee7a7aSBaptiste Daroussin 			if (lua_type (L, 2) == LUA_TSTRING) {
83839ee7a7aSBaptiste Daroussin 				const char *strtype = lua_tostring (L, 2);
83939ee7a7aSBaptiste Daroussin 
840*11dd9ed6SBaptiste Daroussin 				format = lua_ucl_str_to_emit_type (strtype);
84139ee7a7aSBaptiste Daroussin 			}
84239ee7a7aSBaptiste Daroussin 		}
84339ee7a7aSBaptiste Daroussin 
84439ee7a7aSBaptiste Daroussin 		return lua_ucl_to_string (L, obj, format);
84539ee7a7aSBaptiste Daroussin 	}
84639ee7a7aSBaptiste Daroussin 	else {
84739ee7a7aSBaptiste Daroussin 		lua_pushnil (L);
84839ee7a7aSBaptiste Daroussin 	}
84939ee7a7aSBaptiste Daroussin 
85039ee7a7aSBaptiste Daroussin 	return 1;
85139ee7a7aSBaptiste Daroussin }
85239ee7a7aSBaptiste Daroussin 
85339ee7a7aSBaptiste Daroussin /***
854d9f0ce31SBaptiste Daroussin  * @method object:validate(schema[, path[, ext_refs]])
85539ee7a7aSBaptiste Daroussin  * Validates the given ucl object using schema object represented as another
85639ee7a7aSBaptiste Daroussin  * opaque ucl object. You can also specify path in the form `#/path/def` to
85739ee7a7aSBaptiste Daroussin  * specify the specific schema element to perform validation.
85839ee7a7aSBaptiste Daroussin  *
85939ee7a7aSBaptiste Daroussin  * @param {ucl.object} schema schema object
86039ee7a7aSBaptiste Daroussin  * @param {string} path optional path for validation procedure
861d9f0ce31SBaptiste Daroussin  * @return {result,err} two values: boolean result and the corresponding
862d9f0ce31SBaptiste Daroussin  * error, if `ext_refs` are also specified, then they are returned as opaque
863d9f0ce31SBaptiste Daroussin  * ucl object as {result,err,ext_refs}
86439ee7a7aSBaptiste Daroussin  */
86539ee7a7aSBaptiste Daroussin static int
86639ee7a7aSBaptiste Daroussin lua_ucl_object_validate (lua_State *L)
86739ee7a7aSBaptiste Daroussin {
868d9f0ce31SBaptiste Daroussin 	ucl_object_t *obj, *schema, *ext_refs = NULL;
86939ee7a7aSBaptiste Daroussin 	const ucl_object_t *schema_elt;
87039ee7a7aSBaptiste Daroussin 	bool res = false;
87139ee7a7aSBaptiste Daroussin 	struct ucl_schema_error err;
87239ee7a7aSBaptiste Daroussin 	const char *path = NULL;
87339ee7a7aSBaptiste Daroussin 
87439ee7a7aSBaptiste Daroussin 	obj = lua_ucl_object_get (L, 1);
87539ee7a7aSBaptiste Daroussin 	schema = lua_ucl_object_get (L, 2);
87639ee7a7aSBaptiste Daroussin 
87739ee7a7aSBaptiste Daroussin 	if (schema && obj && ucl_object_type (schema) == UCL_OBJECT) {
878d9f0ce31SBaptiste Daroussin 		if (lua_gettop (L) > 2) {
879d9f0ce31SBaptiste Daroussin 			if (lua_type (L, 3) == LUA_TSTRING) {
88039ee7a7aSBaptiste Daroussin 				path = lua_tostring (L, 3);
88139ee7a7aSBaptiste Daroussin 				if (path[0] == '#') {
88239ee7a7aSBaptiste Daroussin 					path++;
88339ee7a7aSBaptiste Daroussin 				}
88439ee7a7aSBaptiste Daroussin 			}
885d9f0ce31SBaptiste Daroussin 			else if (lua_type (L, 3) == LUA_TUSERDATA || lua_type (L, 3) ==
886d9f0ce31SBaptiste Daroussin 						LUA_TTABLE) {
887d9f0ce31SBaptiste Daroussin 				/* External refs */
888d9f0ce31SBaptiste Daroussin 				ext_refs = lua_ucl_object_get (L, 3);
889d9f0ce31SBaptiste Daroussin 			}
890d9f0ce31SBaptiste Daroussin 
891d9f0ce31SBaptiste Daroussin 			if (lua_gettop (L) > 3) {
892d9f0ce31SBaptiste Daroussin 				if (lua_type (L, 4) == LUA_TUSERDATA || lua_type (L, 4) ==
893d9f0ce31SBaptiste Daroussin 						LUA_TTABLE) {
894d9f0ce31SBaptiste Daroussin 					/* External refs */
895d9f0ce31SBaptiste Daroussin 					ext_refs = lua_ucl_object_get (L, 4);
896d9f0ce31SBaptiste Daroussin 				}
897d9f0ce31SBaptiste Daroussin 			}
898d9f0ce31SBaptiste Daroussin 		}
89939ee7a7aSBaptiste Daroussin 
90039ee7a7aSBaptiste Daroussin 		if (path) {
901d9f0ce31SBaptiste Daroussin 			schema_elt = ucl_object_lookup_path_char (schema, path, '/');
90239ee7a7aSBaptiste Daroussin 		}
90339ee7a7aSBaptiste Daroussin 		else {
90439ee7a7aSBaptiste Daroussin 			/* Use the top object */
90539ee7a7aSBaptiste Daroussin 			schema_elt = schema;
90639ee7a7aSBaptiste Daroussin 		}
90739ee7a7aSBaptiste Daroussin 
90839ee7a7aSBaptiste Daroussin 		if (schema_elt) {
909d9f0ce31SBaptiste Daroussin 			res = ucl_object_validate_root_ext (schema_elt, obj, schema,
910d9f0ce31SBaptiste Daroussin 					ext_refs, &err);
91139ee7a7aSBaptiste Daroussin 
91239ee7a7aSBaptiste Daroussin 			if (res) {
91339ee7a7aSBaptiste Daroussin 				lua_pushboolean (L, res);
91439ee7a7aSBaptiste Daroussin 				lua_pushnil (L);
915d9f0ce31SBaptiste Daroussin 
916d9f0ce31SBaptiste Daroussin 				if (ext_refs) {
917d9f0ce31SBaptiste Daroussin 					lua_ucl_push_opaque (L, ext_refs);
918d9f0ce31SBaptiste Daroussin 				}
91939ee7a7aSBaptiste Daroussin 			}
92039ee7a7aSBaptiste Daroussin 			else {
92139ee7a7aSBaptiste Daroussin 				lua_pushboolean (L, res);
92239ee7a7aSBaptiste Daroussin 				lua_pushfstring (L, "validation error: %s", err.msg);
923d9f0ce31SBaptiste Daroussin 
924d9f0ce31SBaptiste Daroussin 				if (ext_refs) {
925d9f0ce31SBaptiste Daroussin 					lua_ucl_push_opaque (L, ext_refs);
926d9f0ce31SBaptiste Daroussin 				}
92739ee7a7aSBaptiste Daroussin 			}
92839ee7a7aSBaptiste Daroussin 		}
92939ee7a7aSBaptiste Daroussin 		else {
93039ee7a7aSBaptiste Daroussin 			lua_pushboolean (L, res);
93139ee7a7aSBaptiste Daroussin 
93239ee7a7aSBaptiste Daroussin 			lua_pushfstring (L, "cannot find the requested path: %s", path);
933d9f0ce31SBaptiste Daroussin 
934d9f0ce31SBaptiste Daroussin 			if (ext_refs) {
935d9f0ce31SBaptiste Daroussin 				lua_ucl_push_opaque (L, ext_refs);
93639ee7a7aSBaptiste Daroussin 			}
93739ee7a7aSBaptiste Daroussin 		}
93839ee7a7aSBaptiste Daroussin 	}
93939ee7a7aSBaptiste Daroussin 	else {
94039ee7a7aSBaptiste Daroussin 		lua_pushboolean (L, res);
94139ee7a7aSBaptiste Daroussin 		lua_pushstring (L, "invalid object or schema");
94239ee7a7aSBaptiste Daroussin 	}
94339ee7a7aSBaptiste Daroussin 
944d9f0ce31SBaptiste Daroussin 	if (ext_refs) {
945d9f0ce31SBaptiste Daroussin 		return 3;
946d9f0ce31SBaptiste Daroussin 	}
947d9f0ce31SBaptiste Daroussin 
94839ee7a7aSBaptiste Daroussin 	return 2;
94939ee7a7aSBaptiste Daroussin }
95039ee7a7aSBaptiste Daroussin 
95139ee7a7aSBaptiste Daroussin static int
95239ee7a7aSBaptiste Daroussin lua_ucl_object_gc (lua_State *L)
95339ee7a7aSBaptiste Daroussin {
95439ee7a7aSBaptiste Daroussin 	ucl_object_t *obj;
95539ee7a7aSBaptiste Daroussin 
95639ee7a7aSBaptiste Daroussin 	obj = lua_ucl_object_get (L, 1);
95739ee7a7aSBaptiste Daroussin 
95839ee7a7aSBaptiste Daroussin 	ucl_object_unref (obj);
95939ee7a7aSBaptiste Daroussin 
96039ee7a7aSBaptiste Daroussin 	return 0;
96139ee7a7aSBaptiste Daroussin }
96239ee7a7aSBaptiste Daroussin 
9634bf54857SBaptiste Daroussin static void
9644bf54857SBaptiste Daroussin lua_ucl_parser_mt (lua_State *L)
9654bf54857SBaptiste Daroussin {
9664bf54857SBaptiste Daroussin 	luaL_newmetatable (L, PARSER_META);
9674bf54857SBaptiste Daroussin 
9684bf54857SBaptiste Daroussin 	lua_pushvalue(L, -1);
9694bf54857SBaptiste Daroussin 	lua_setfield(L, -2, "__index");
9704bf54857SBaptiste Daroussin 
9714bf54857SBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_parser_gc);
9724bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "__gc");
9734bf54857SBaptiste Daroussin 
9744bf54857SBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_parser_parse_file);
9754bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "parse_file");
9764bf54857SBaptiste Daroussin 
9774bf54857SBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_parser_parse_string);
9784bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "parse_string");
9794bf54857SBaptiste Daroussin 
9804bf54857SBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_parser_get_object);
9814bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "get_object");
9824bf54857SBaptiste Daroussin 
98339ee7a7aSBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_parser_get_object_wrapped);
98439ee7a7aSBaptiste Daroussin 	lua_setfield (L, -2, "get_object_wrapped");
98539ee7a7aSBaptiste Daroussin 
98639ee7a7aSBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_parser_validate);
98739ee7a7aSBaptiste Daroussin 	lua_setfield (L, -2, "validate");
98839ee7a7aSBaptiste Daroussin 
9894bf54857SBaptiste Daroussin 	lua_pop (L, 1);
9904bf54857SBaptiste Daroussin }
9914bf54857SBaptiste Daroussin 
99239ee7a7aSBaptiste Daroussin static void
99339ee7a7aSBaptiste Daroussin lua_ucl_object_mt (lua_State *L)
9944bf54857SBaptiste Daroussin {
99539ee7a7aSBaptiste Daroussin 	luaL_newmetatable (L, OBJECT_META);
9964bf54857SBaptiste Daroussin 
99739ee7a7aSBaptiste Daroussin 	lua_pushvalue(L, -1);
99839ee7a7aSBaptiste Daroussin 	lua_setfield(L, -2, "__index");
9994bf54857SBaptiste Daroussin 
100039ee7a7aSBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_object_gc);
100139ee7a7aSBaptiste Daroussin 	lua_setfield (L, -2, "__gc");
10024bf54857SBaptiste Daroussin 
100339ee7a7aSBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_object_tostring);
100439ee7a7aSBaptiste Daroussin 	lua_setfield (L, -2, "__tostring");
100539ee7a7aSBaptiste Daroussin 
100639ee7a7aSBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_object_tostring);
100739ee7a7aSBaptiste Daroussin 	lua_setfield (L, -2, "tostring");
100839ee7a7aSBaptiste Daroussin 
100939ee7a7aSBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_object_unwrap);
101039ee7a7aSBaptiste Daroussin 	lua_setfield (L, -2, "unwrap");
101139ee7a7aSBaptiste Daroussin 
101239ee7a7aSBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_object_unwrap);
101339ee7a7aSBaptiste Daroussin 	lua_setfield (L, -2, "tolua");
101439ee7a7aSBaptiste Daroussin 
101539ee7a7aSBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_object_validate);
101639ee7a7aSBaptiste Daroussin 	lua_setfield (L, -2, "validate");
101739ee7a7aSBaptiste Daroussin 
101839ee7a7aSBaptiste Daroussin 	lua_pushstring (L, OBJECT_META);
101939ee7a7aSBaptiste Daroussin 	lua_setfield (L, -2, "class");
102039ee7a7aSBaptiste Daroussin 
102139ee7a7aSBaptiste Daroussin 	lua_pop (L, 1);
10224bf54857SBaptiste Daroussin }
10234bf54857SBaptiste Daroussin 
10244bf54857SBaptiste Daroussin static int
10254bf54857SBaptiste Daroussin lua_ucl_to_json (lua_State *L)
10264bf54857SBaptiste Daroussin {
10274bf54857SBaptiste Daroussin 	ucl_object_t *obj;
10284bf54857SBaptiste Daroussin 	int format = UCL_EMIT_JSON;
10294bf54857SBaptiste Daroussin 
10304bf54857SBaptiste Daroussin 	if (lua_gettop (L) > 1) {
10314bf54857SBaptiste Daroussin 		if (lua_toboolean (L, 2)) {
10324bf54857SBaptiste Daroussin 			format = UCL_EMIT_JSON_COMPACT;
10334bf54857SBaptiste Daroussin 		}
10344bf54857SBaptiste Daroussin 	}
10354bf54857SBaptiste Daroussin 
10364bf54857SBaptiste Daroussin 	obj = ucl_object_lua_import (L, 1);
10374bf54857SBaptiste Daroussin 	if (obj != NULL) {
10384bf54857SBaptiste Daroussin 		lua_ucl_to_string (L, obj, format);
10394bf54857SBaptiste Daroussin 		ucl_object_unref (obj);
10404bf54857SBaptiste Daroussin 	}
10414bf54857SBaptiste Daroussin 	else {
10424bf54857SBaptiste Daroussin 		lua_pushnil (L);
10434bf54857SBaptiste Daroussin 	}
10444bf54857SBaptiste Daroussin 
10454bf54857SBaptiste Daroussin 	return 1;
10464bf54857SBaptiste Daroussin }
10474bf54857SBaptiste Daroussin 
10484bf54857SBaptiste Daroussin static int
10494bf54857SBaptiste Daroussin lua_ucl_to_config (lua_State *L)
10504bf54857SBaptiste Daroussin {
10514bf54857SBaptiste Daroussin 	ucl_object_t *obj;
10524bf54857SBaptiste Daroussin 
10534bf54857SBaptiste Daroussin 	obj = ucl_object_lua_import (L, 1);
10544bf54857SBaptiste Daroussin 	if (obj != NULL) {
10554bf54857SBaptiste Daroussin 		lua_ucl_to_string (L, obj, UCL_EMIT_CONFIG);
10564bf54857SBaptiste Daroussin 		ucl_object_unref (obj);
10574bf54857SBaptiste Daroussin 	}
10584bf54857SBaptiste Daroussin 	else {
10594bf54857SBaptiste Daroussin 		lua_pushnil (L);
10604bf54857SBaptiste Daroussin 	}
10614bf54857SBaptiste Daroussin 
10624bf54857SBaptiste Daroussin 	return 1;
10634bf54857SBaptiste Daroussin }
10644bf54857SBaptiste Daroussin 
10654bf54857SBaptiste Daroussin /***
10664bf54857SBaptiste Daroussin  * @function ucl.to_format(var, format)
10674bf54857SBaptiste Daroussin  * Converts lua variable `var` to the specified `format`. Formats supported are:
10684bf54857SBaptiste Daroussin  *
10694bf54857SBaptiste Daroussin  * - `json` - fine printed json
10704bf54857SBaptiste Daroussin  * - `json-compact` - compacted json
10714bf54857SBaptiste Daroussin  * - `config` - fine printed configuration
10724bf54857SBaptiste Daroussin  * - `ucl` - same as `config`
10734bf54857SBaptiste Daroussin  * - `yaml` - embedded yaml
10744bf54857SBaptiste Daroussin  *
10754bf54857SBaptiste Daroussin  * If `var` contains function, they are called during output formatting and if
10764bf54857SBaptiste Daroussin  * they return string value, then this value is used for ouptut.
10774bf54857SBaptiste Daroussin  * @param {variant} var any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
10784bf54857SBaptiste Daroussin  * @param {string} format any available format
10794bf54857SBaptiste Daroussin  * @return {string} string representation of `var` in the specific `format`.
10804bf54857SBaptiste Daroussin  * @example
10814bf54857SBaptiste Daroussin local table = {
10824bf54857SBaptiste Daroussin   str = 'value',
10834bf54857SBaptiste Daroussin   num = 100500,
10844bf54857SBaptiste Daroussin   null = ucl.null,
10854bf54857SBaptiste Daroussin   func = function ()
10864bf54857SBaptiste Daroussin     return 'huh'
10874bf54857SBaptiste Daroussin   end
10884bf54857SBaptiste Daroussin }
10894bf54857SBaptiste Daroussin 
10904bf54857SBaptiste Daroussin print(ucl.to_format(table, 'ucl'))
10914bf54857SBaptiste Daroussin -- Output:
10924bf54857SBaptiste Daroussin --[[
10934bf54857SBaptiste Daroussin num = 100500;
10944bf54857SBaptiste Daroussin str = "value";
10954bf54857SBaptiste Daroussin null = null;
10964bf54857SBaptiste Daroussin func = "huh";
10974bf54857SBaptiste Daroussin --]]
10984bf54857SBaptiste Daroussin  */
10994bf54857SBaptiste Daroussin static int
11004bf54857SBaptiste Daroussin lua_ucl_to_format (lua_State *L)
11014bf54857SBaptiste Daroussin {
11024bf54857SBaptiste Daroussin 	ucl_object_t *obj;
11034bf54857SBaptiste Daroussin 	int format = UCL_EMIT_JSON;
11044bf54857SBaptiste Daroussin 
11054bf54857SBaptiste Daroussin 	if (lua_gettop (L) > 1) {
11064bf54857SBaptiste Daroussin 		if (lua_type (L, 2) == LUA_TNUMBER) {
11074bf54857SBaptiste Daroussin 			format = lua_tonumber (L, 2);
11084bf54857SBaptiste Daroussin 			if (format < 0 || format >= UCL_EMIT_YAML) {
11094bf54857SBaptiste Daroussin 				lua_pushnil (L);
11104bf54857SBaptiste Daroussin 				return 1;
11114bf54857SBaptiste Daroussin 			}
11124bf54857SBaptiste Daroussin 		}
11134bf54857SBaptiste Daroussin 		else if (lua_type (L, 2) == LUA_TSTRING) {
11144bf54857SBaptiste Daroussin 			const char *strtype = lua_tostring (L, 2);
11154bf54857SBaptiste Daroussin 
11164bf54857SBaptiste Daroussin 			if (strcasecmp (strtype, "json") == 0) {
11174bf54857SBaptiste Daroussin 				format = UCL_EMIT_JSON;
11184bf54857SBaptiste Daroussin 			}
11194bf54857SBaptiste Daroussin 			else if (strcasecmp (strtype, "json-compact") == 0) {
11204bf54857SBaptiste Daroussin 				format = UCL_EMIT_JSON_COMPACT;
11214bf54857SBaptiste Daroussin 			}
11224bf54857SBaptiste Daroussin 			else if (strcasecmp (strtype, "yaml") == 0) {
11234bf54857SBaptiste Daroussin 				format = UCL_EMIT_YAML;
11244bf54857SBaptiste Daroussin 			}
11254bf54857SBaptiste Daroussin 			else if (strcasecmp (strtype, "config") == 0 ||
11264bf54857SBaptiste Daroussin 				strcasecmp (strtype, "ucl") == 0) {
11274bf54857SBaptiste Daroussin 				format = UCL_EMIT_CONFIG;
11284bf54857SBaptiste Daroussin 			}
1129*11dd9ed6SBaptiste Daroussin 			else if (strcasecmp (strtype, "msgpack") == 0) {
1130*11dd9ed6SBaptiste Daroussin 				format = UCL_EMIT_MSGPACK;
1131*11dd9ed6SBaptiste Daroussin 			}
11324bf54857SBaptiste Daroussin 		}
11334bf54857SBaptiste Daroussin 	}
11344bf54857SBaptiste Daroussin 
11354bf54857SBaptiste Daroussin 	obj = ucl_object_lua_import (L, 1);
11364bf54857SBaptiste Daroussin 	if (obj != NULL) {
11374bf54857SBaptiste Daroussin 		lua_ucl_to_string (L, obj, format);
11384bf54857SBaptiste Daroussin 		ucl_object_unref (obj);
11394bf54857SBaptiste Daroussin 	}
11404bf54857SBaptiste Daroussin 	else {
11414bf54857SBaptiste Daroussin 		lua_pushnil (L);
11424bf54857SBaptiste Daroussin 	}
11434bf54857SBaptiste Daroussin 
11444bf54857SBaptiste Daroussin 	return 1;
11454bf54857SBaptiste Daroussin }
11464bf54857SBaptiste Daroussin 
11474bf54857SBaptiste Daroussin static int
11484bf54857SBaptiste Daroussin lua_ucl_null_tostring (lua_State* L)
11494bf54857SBaptiste Daroussin {
11504bf54857SBaptiste Daroussin 	lua_pushstring (L, "null");
11514bf54857SBaptiste Daroussin 	return 1;
11524bf54857SBaptiste Daroussin }
11534bf54857SBaptiste Daroussin 
11544bf54857SBaptiste Daroussin static void
11554bf54857SBaptiste Daroussin lua_ucl_null_mt (lua_State *L)
11564bf54857SBaptiste Daroussin {
11574bf54857SBaptiste Daroussin 	luaL_newmetatable (L, NULL_META);
11584bf54857SBaptiste Daroussin 
11594bf54857SBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_null_tostring);
11604bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "__tostring");
11614bf54857SBaptiste Daroussin 
11624bf54857SBaptiste Daroussin 	lua_pop (L, 1);
11634bf54857SBaptiste Daroussin }
11644bf54857SBaptiste Daroussin 
11654bf54857SBaptiste Daroussin int
11664bf54857SBaptiste Daroussin luaopen_ucl (lua_State *L)
11674bf54857SBaptiste Daroussin {
11684bf54857SBaptiste Daroussin 	lua_ucl_parser_mt (L);
11694bf54857SBaptiste Daroussin 	lua_ucl_null_mt (L);
117039ee7a7aSBaptiste Daroussin 	lua_ucl_object_mt (L);
11714bf54857SBaptiste Daroussin 
11724bf54857SBaptiste Daroussin 	/* Create the refs weak table: */
11734bf54857SBaptiste Daroussin 	lua_createtable (L, 0, 2);
11744bf54857SBaptiste Daroussin 	lua_pushliteral (L, "v"); /* tbl, "v" */
11754bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "__mode");
11764bf54857SBaptiste Daroussin 	lua_pushvalue (L, -1); /* tbl, tbl */
11774bf54857SBaptiste Daroussin 	lua_setmetatable (L, -2); /* tbl */
11784bf54857SBaptiste Daroussin 	lua_setfield (L, LUA_REGISTRYINDEX, "ucl.refs");
11794bf54857SBaptiste Daroussin 
11804bf54857SBaptiste Daroussin 	lua_newtable (L);
11814bf54857SBaptiste Daroussin 
11824bf54857SBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_parser_init);
11834bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "parser");
11844bf54857SBaptiste Daroussin 
11854bf54857SBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_to_json);
11864bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "to_json");
11874bf54857SBaptiste Daroussin 
11884bf54857SBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_to_config);
11894bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "to_config");
11904bf54857SBaptiste Daroussin 
11914bf54857SBaptiste Daroussin 	lua_pushcfunction (L, lua_ucl_to_format);
11924bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "to_format");
11934bf54857SBaptiste Daroussin 
11944bf54857SBaptiste Daroussin 	ucl_null = lua_newuserdata (L, 0);
11954bf54857SBaptiste Daroussin 	luaL_getmetatable (L, NULL_META);
11964bf54857SBaptiste Daroussin 	lua_setmetatable (L, -2);
11974bf54857SBaptiste Daroussin 
11984bf54857SBaptiste Daroussin 	lua_pushvalue (L, -1);
11994bf54857SBaptiste Daroussin 	lua_setfield (L, LUA_REGISTRYINDEX, "ucl.null");
12004bf54857SBaptiste Daroussin 
12014bf54857SBaptiste Daroussin 	lua_setfield (L, -2, "null");
12024bf54857SBaptiste Daroussin 
12034bf54857SBaptiste Daroussin 	return 1;
12044bf54857SBaptiste Daroussin }
12054bf54857SBaptiste Daroussin 
12064bf54857SBaptiste Daroussin struct ucl_lua_funcdata*
12074bf54857SBaptiste Daroussin ucl_object_toclosure (const ucl_object_t *obj)
12084bf54857SBaptiste Daroussin {
12094bf54857SBaptiste Daroussin 	if (obj == NULL || obj->type != UCL_USERDATA) {
12104bf54857SBaptiste Daroussin 		return NULL;
12114bf54857SBaptiste Daroussin 	}
12124bf54857SBaptiste Daroussin 
12134bf54857SBaptiste Daroussin 	return (struct ucl_lua_funcdata*)obj->value.ud;
12144bf54857SBaptiste Daroussin }
1215