Skip to content
Permalink
f566e81c77
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
1027 lines (863 sloc) 30.2 KB
#include <yajl/yajl_parse.h>
#include <yajl/yajl_gen.h>
#include <lua.h>
#include <lauxlib.h>
#include <math.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define js_check_generator(L, narg) \
(yajl_gen*)luaL_checkudata((L), (narg), "yajl.generator.meta")
static void* js_null;
static int js_generator(lua_State *L);
static int js_generator_value(lua_State *L);
static void js_parser_assert(lua_State* L,
yajl_status status,
yajl_handle* handle,
const unsigned char* json_text,
unsigned int json_text_len,
int expect_complete,
const char* file,
int line);
static int got_map_key(lua_State* L);
static int got_map_value(lua_State* L);
//////////////////////////////////////////////////////////////////////
static double todouble(lua_State* L, const char* val, unsigned int len) {
// Convert into number using a temporary:
char* tmp = (char*)lua_newuserdata(L, len+1);
memcpy(tmp, val, len);
tmp[len] = '\0';
double num = strtod(tmp, NULL);
/*
if ((num == HUGE_VAL || num == -HUGE_VAL) &&
errno == ERANGE)
{
TODO: Add appropriate handling of large numbers by delegating.
}
TODO: How can we tell if there was information loss? aka the
number of significant digits in the string exceeds the
significant digits in the double.
*/
lua_pop(L, 1);
return num;
}
//////////////////////////////////////////////////////////////////////
static int js_to_string(lua_State *L) {
lua_pushcfunction(L, js_generator);
// convert_me, {extra}, ?, js_gen
if ( lua_istable(L, 2) ) {
// Be sure printer is not defined:
lua_pushliteral(L, "printer");
lua_pushnil(L);
lua_rawset(L, 2);
lua_pushvalue(L, 2);
// convert_me, {extra}, ?, js_gen, {extra}
} else {
lua_newtable(L);
// convert_me, {extra}, ?, js_gen, {}
}
lua_call(L, 1, 1);
// convert_me, {extra}, ?, gen_ud
lua_pushcfunction(L, js_generator_value);
// convert_me, {extra}, ?, gen_ud, js_gen_val
lua_pushvalue(L, -2);
// convert_me, {extra}, ?, gen_ud, js_gen_val, gen_ud
lua_pushvalue(L, 1);
// convert_me, {extra}, ?, gen_ud, js_gen_val, gen_ud, convert_me
lua_call(L, 2, 0);
// convert_me, {extra}, ?, gen_ud
yajl_gen* gen = js_check_generator(L, -1);
const unsigned char *buf;
unsigned int len;
yajl_gen_get_buf(*gen, &buf, &len);
// Copy into results:
lua_pushlstring(L, (char*)buf, len);
yajl_gen_clear(*gen);
return 1;
}
static int to_value_null(void* ctx) {
lua_State* L = (lua_State*)ctx;
lua_getfield(L, LUA_REGISTRYINDEX, "yajl.null");
(lua_tocfunction(L, -2))(L);
return 1;
}
static int to_value_boolean(void* ctx, int val) {
lua_State* L = (lua_State*)ctx;
lua_pushboolean(L, val);
(lua_tocfunction(L, -2))(L);
return 1;
}
static int to_value_number(void* ctx, const char* val, unsigned int len) {
lua_State* L = (lua_State*)ctx;
lua_pushnumber(L, todouble(L, val, len));
(lua_tocfunction(L, -2))(L);
return 1;
}
static int to_value_string(void* ctx, const unsigned char *val, unsigned int len) {
lua_State* L = (lua_State*)ctx;
lua_pushlstring(L, (const char*)val, len);
(lua_tocfunction(L, -2))(L);
return 1;
}
static int got_map_value(lua_State* L) {
/* ..., Table, Key, Func, Value */
lua_insert(L, -2);
lua_pop(L, 1);
lua_rawset(L, -3);
lua_pushnil(L); // Store future key here.
lua_pushcfunction(L, got_map_key);
return 0; // Ignored.
}
static int got_map_key(lua_State* L) {
lua_replace(L, -3);
lua_pop(L, 1);
lua_pushcfunction(L, got_map_value);
return 0; // Ignored.
}
static int got_array_value(lua_State* L) {
/* ..., Table, Integer, Func, Value */
lua_rawseti(L, -4, lua_tointeger(L, -3));
lua_pushinteger(L, lua_tointeger(L, -2)+1);
lua_replace(L, -3);
}
static int to_value_start_map(void* ctx) {
lua_State* L = (lua_State*)ctx;
/* The layout of the stack for "objects" is:
- Table we are appending to.
- Storage for the "last key found".
- Function to call in to_value_* functions.
- Value pushed on the stack by to_value_* functions.
*/
if ( ! lua_checkstack(L, 4) ) {
// TODO: So far, YAJL seems to be fine with the longjmp, but
// perhaps we should do errors a different way?
return luaL_error(L, "lua stack overflow");
}
lua_newtable(L);
lua_pushnil(L); // Store future key here.
lua_pushcfunction(L, got_map_key);
return 1;
}
static int to_value_start_array(void* ctx) {
lua_State* L = (lua_State*)ctx;
/* The layout of the stack for "arrays" is:
- Table we are appending to.
- The index to use for the next insertion.
- Function to call in to_value_* functions.
- Value pushed on the stack by to_value_* functions.
*/
if ( ! lua_checkstack(L, 4) ) {
// TODO: So far, YAJL seems to be fine with the longjmp, but
// perhaps we should do errors a different way?
return luaL_error(L, "lua stack overflow");
}
lua_newtable(L);
lua_pushinteger(L, 1);
lua_pushcfunction(L, got_array_value);
return 1;
}
static int to_value_end(void* ctx) {
lua_State* L = (lua_State*)ctx;
// Simply pop the stack and call the cfunction:
lua_pop(L, 2);
(lua_tocfunction(L, -2))(L);
return 1;
}
static int noop(lua_State* L) {
return 0;
}
static yajl_callbacks js_to_value_callbacks = {
to_value_null,
to_value_boolean,
NULL,
NULL,
to_value_number,
to_value_string,
to_value_start_map,
to_value_string,
to_value_end,
to_value_start_array,
to_value_end,
};
//////////////////////////////////////////////////////////////////////
static int js_to_value(lua_State *L) {
yajl_parser_config cfg = { 1, 1 };
yajl_handle handle;
size_t len;
const unsigned char* buff = (const unsigned char*) luaL_checklstring(L, 1, &len);
int expect_complete = 1;
if ( NULL == buff ) return 0;
if ( lua_istable(L, 2) ) {
lua_getfield(L, 2, "allow_comments");
if ( ! lua_isnil(L, -1) ) {
cfg.allowComments = lua_toboolean(L, -1);
}
lua_pop(L, 1);
lua_getfield(L, 2, "check_utf8");
if ( ! lua_isnil(L, -1) ) {
cfg.checkUTF8 = lua_toboolean(L, -1);
}
lua_pop(L, 1);
}
handle = yajl_alloc(&js_to_value_callbacks, &cfg, NULL, (void*)L);
lua_pushcfunction(L, noop);
js_parser_assert(L,
yajl_parse(handle, buff, len),
&handle,
buff,
len,
expect_complete,
__FILE__,
__LINE__);
yajl_free(handle);
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_null(void *ctx) {
lua_State *L=(lua_State*)ctx;
lua_getfield(L, lua_upvalueindex(2), "value");
if ( ! lua_isnil(L, -1) ) {
lua_pushvalue(L, lua_upvalueindex(2));
lua_getfield(L, LUA_REGISTRYINDEX, "yajl.null");
lua_pushliteral(L, "null");
lua_call(L, 3, 0);
} else {
lua_pop(L, 1);
}
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_boolean(void *ctx, int val) {
lua_State *L=(lua_State*)ctx;
lua_getfield(L, lua_upvalueindex(2), "value");
if ( ! lua_isnil(L, -1) ) {
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushboolean(L, val);
lua_pushliteral(L, "boolean");
lua_call(L, 3, 0);
} else {
lua_pop(L, 1);
}
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_number(void *ctx, const char* buffer, unsigned int buffer_len) {
lua_State *L=(lua_State*)ctx;
lua_getfield(L, lua_upvalueindex(2), "value");
if ( ! lua_isnil(L, -1) ) {
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushnumber(L, todouble(L, buffer, buffer_len));
lua_pushliteral(L, "number");
lua_call(L, 3, 0);
} else {
lua_pop(L, 1);
}
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_string(void *ctx, const unsigned char *val, unsigned int len) {
lua_State *L=(lua_State*)ctx;
lua_getfield(L, lua_upvalueindex(2), "value");
if ( ! lua_isnil(L, -1) ) {
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushlstring(L, (const char*)val, len);
lua_pushliteral(L, "string");
lua_call(L, 3, 0);
} else {
lua_pop(L, 1);
}
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_start_map(void *ctx) {
lua_State *L=(lua_State*)ctx;
lua_getfield(L, lua_upvalueindex(2), "open_object");
if ( ! lua_isnil(L, -1) ) {
lua_pushvalue(L, lua_upvalueindex(2));
lua_call(L, 1, 0);
} else {
lua_pop(L, 1);
}
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_map_key(void *ctx, const unsigned char *val, unsigned int len) {
lua_State *L=(lua_State*)ctx;
// TODO: Do we want to fall-back to calling "value"?
lua_getfield(L, lua_upvalueindex(2), "object_key");
if ( ! lua_isnil(L, -1) ) {
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushlstring(L, (const char*)val, len);
lua_call(L, 2, 0);
} else {
lua_pop(L, 1);
}
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_end_map(void *ctx) {
lua_State *L=(lua_State*)ctx;
lua_getfield(L, lua_upvalueindex(2), "close");
if ( ! lua_isnil(L, -1) ) {
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushliteral(L, "object");
lua_call(L, 2, 0);
} else {
lua_pop(L, 1);
}
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_start_array(void *ctx) {
lua_State *L=(lua_State*)ctx;
lua_getfield(L, lua_upvalueindex(2), "open_array");
if ( ! lua_isnil(L, -1) ) {
lua_pushvalue(L, lua_upvalueindex(2));
lua_call(L, 1, 0);
} else {
lua_pop(L, 1);
}
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_end_array(void *ctx) {
lua_State *L=(lua_State*)ctx;
lua_getfield(L, lua_upvalueindex(2), "close");
if ( ! lua_isnil(L, -1) ) {
lua_pushvalue(L, lua_upvalueindex(2));
lua_pushliteral(L, "array");
lua_call(L, 2, 0);
} else {
lua_pop(L, 1);
}
return 1;
}
static yajl_callbacks js_parser_callbacks = {
js_parser_null,
js_parser_boolean,
NULL,
NULL,
js_parser_number,
js_parser_string,
js_parser_start_map,
js_parser_map_key,
js_parser_end_map,
js_parser_start_array,
js_parser_end_array
};
//////////////////////////////////////////////////////////////////////
static void js_parser_assert(lua_State* L,
yajl_status status,
yajl_handle* handle,
const unsigned char* json_text,
unsigned int json_text_len,
int expect_complete,
const char* file,
int line)
{
int verbose = 1;
unsigned char* msg;
switch ( status ) {
case yajl_status_ok:
return;
case yajl_status_client_canceled:
lua_pushfstring(L, "Unreachable: yajl_status_client_canceled should never be returned since all callbacks return true at %s line %d",
file, line);
break;
case yajl_status_insufficient_data:
if ( ! expect_complete ) return;
lua_pushfstring(L, "IncompleteInput: js_parser_parse called with nil input, but the json input was not complete at %s line %d",
file, line);
break;
case yajl_status_error:
msg = yajl_get_error(*handle, verbose, json_text, json_text_len);
lua_pushfstring(L, "InvalidJSONInput: %s at %s line %d", msg, file, line);
yajl_free_error(*handle, msg);
break;
}
lua_error(L);
}
//////////////////////////////////////////////////////////////////////
static int js_parser_parse(lua_State *L) {
yajl_handle* handle = (yajl_handle*)
lua_touserdata(L, lua_upvalueindex(1));
if ( lua_isnil(L, 1) ) {
int expect_complete = 1;
js_parser_assert(L,
yajl_parse_complete(*handle),
handle,
NULL,
0,
expect_complete,
__FILE__,
__LINE__);
} else {
int expect_complete = 0;
size_t len;
const unsigned char* buff = (const unsigned char*) luaL_checklstring(L, 1, &len);
if ( NULL == buff ) return 0;
js_parser_assert(L,
yajl_parse(*handle, buff, len),
handle,
buff,
len,
expect_complete,
__FILE__,
__LINE__);
}
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_parser_delete(lua_State *L) {
luaL_checktype(L, 1, LUA_TUSERDATA);
yajl_free(*(yajl_handle*)lua_touserdata(L, 1));
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_parser(lua_State *L) {
yajl_parser_config cfg = { 1, 1 };
luaL_checktype(L, 1, LUA_TTABLE);
lua_getfield(L, 1, "allow_comments");
if ( ! lua_isnil(L, -1) ) {
cfg.allowComments = lua_toboolean(L, -1);
}
lua_pop(L, 1);
lua_getfield(L, 1, "check_utf8");
if ( ! lua_isnil(L, -1) ) {
cfg.checkUTF8 = lua_toboolean(L, -1);
}
lua_pop(L, 1);
yajl_handle* handle = (yajl_handle*)lua_newuserdata(L, sizeof(yajl_handle));
*handle = yajl_alloc(&js_parser_callbacks, &cfg, NULL, (void*)L);
luaL_getmetatable(L, "yajl.parser.meta");
lua_setmetatable(L, -2);
lua_getfield(L, 1, "events");
// Create callback function that calls yajl_parse[_complete]()
//
// upvalue(1) = yajl_handle*
// upvalue(2) = events table
lua_pushcclosure(L, &js_parser_parse, 2);
return 1;
}
//////////////////////////////////////////////////////////////////////
static int js_generator_delete(lua_State *L) {
yajl_gen* handle = js_check_generator(L, 1);
yajl_gen_free(*handle);
return 0;
}
//////////////////////////////////////////////////////////////////////
static void js_generator_assert(lua_State *L,
yajl_gen_status status,
const char* file,
int line)
{
switch ( status ) {
case yajl_gen_status_ok:
case yajl_gen_generation_complete:
return;
case yajl_gen_keys_must_be_strings:
lua_pushfstring(L, "InvalidState: expected either a call to close() or string() since we are in the middle of an object declaration at %s line %d", file, line);
break;
case yajl_max_depth_exceeded:
lua_pushfstring(L, "StackOverflow: YAJL's max generation depth was exceeded at %s line %d", file, line);
break;
case yajl_gen_in_error_state:
lua_pushfstring(L, "AlreadyInError: generator method was called when the generator is already in an error state at %s line %d", file, line);
break;
default:
lua_pushfstring(L, "Unreachable: yajl_gen_status (%d) not recognized at %s line %d", status, file, line);
break;
}
lua_error(L);
}
//////////////////////////////////////////////////////////////////////
static int js_generator_integer(lua_State *L) {
js_generator_assert(L,
yajl_gen_integer(*js_check_generator(L, 1),
luaL_checkinteger(L, 2)),
__FILE__, __LINE__);
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_generator_double(lua_State *L) {
js_generator_assert(L,
yajl_gen_double(*js_check_generator(L, 1),
luaL_checknumber(L, 2)),
__FILE__, __LINE__);
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_generator_number(lua_State *L) {
// It would be better to make it so an arbitrary string can be
// used here, however we would then need to validate that the
// generated string is a JSON number which is a bit beyond scope
// at this point. Perhaps in the future we will loosen this
// restriction, it is always easier to loosen restrictions than it
// is to make new restrictions that break other people's code.
double num = luaL_checknumber(L, 2);
size_t len;
const char* str;
// These are special cases, not sure how better to represent them
// :-(.
if ( num == HUGE_VAL ) {
str = "1e+666";
len = 6;
} else if ( num == -HUGE_VAL ) {
str = "-1e+666";
len = 7;
} else if ( isnan(num) ) {
str = "-0";
len = 2;
} else {
str = luaL_checklstring(L, 2, &len);
}
js_generator_assert(L,
yajl_gen_number(*js_check_generator(L, 1),
str, len),
__FILE__, __LINE__);
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_generator_string(lua_State *L) {
size_t len;
const unsigned char* str = (const unsigned char*)luaL_checklstring(L, 2, &len);
js_generator_assert(L,
yajl_gen_string(*js_check_generator(L, 1),
str, len),
__FILE__, __LINE__);
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_generator_null(lua_State *L) {
js_generator_assert(L,
yajl_gen_null(*js_check_generator(L, 1)),
__FILE__, __LINE__);
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_generator_boolean(lua_State *L) {
luaL_checktype(L, 2, LUA_TBOOLEAN);
js_generator_assert(L,
yajl_gen_bool(*js_check_generator(L, 1),
lua_toboolean(L, 2)),
__FILE__, __LINE__);
return 0;
}
#define JS_OPEN_OBJECT 1
#define JS_OPEN_ARRAY 2
//////////////////////////////////////////////////////////////////////
static int js_generator_open_object(lua_State *L) {
js_generator_assert(L,
yajl_gen_map_open(*js_check_generator(L, 1)),
__FILE__, __LINE__);
// Why doesn't yajl_gen keep track of this!?
lua_getfenv(L, 1);
lua_getfield(L, -1, "stack");
lua_pushinteger(L, JS_OPEN_OBJECT);
lua_rawseti(L, -2, lua_objlen(L, -2) + 1);
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_generator_open_array(lua_State *L) {
js_generator_assert(L,
yajl_gen_array_open(*js_check_generator(L, 1)),
__FILE__, __LINE__);
// Why doesn't yajl_gen keep track of this!?
lua_getfenv(L, 1);
lua_getfield(L, -1, "stack");
lua_pushinteger(L, JS_OPEN_ARRAY);
lua_rawseti(L, -2, lua_objlen(L, -2) + 1);
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_generator_close(lua_State *L) {
// Why doesn't yajl_gen keep track of this!?
lua_getfenv(L, 1);
lua_getfield(L, -1, "stack");
lua_rawgeti(L, -1, lua_objlen(L, -1));
if ( lua_isnil(L, -1) ) {
lua_pushfstring(L, "StackUnderflow: Attempt to call close() when no array or object has been opened at %s line %d", __FILE__, __LINE__);
lua_error(L);
}
lua_Integer type = lua_tointeger(L, -1);
switch ( type ) {
case JS_OPEN_OBJECT:
js_generator_assert(L,
yajl_gen_map_close(*js_check_generator(L, 1)),
__FILE__, __LINE__);
break;
case JS_OPEN_ARRAY:
js_generator_assert(L,
yajl_gen_array_close(*js_check_generator(L, 1)),
__FILE__, __LINE__);
break;
default:
lua_pushfstring(L, "Unreachable: internal 'stack' contained invalid integer (%d) at %s line %d", type, __FILE__, __LINE__);
lua_error(L);
}
// delete the top of the "stack":
lua_pop(L, 1);
lua_pushnil(L);
lua_rawseti(L, -2, lua_objlen(L, -2));
return 0;
}
//////////////////////////////////////////////////////////////////////
static int js_generator_value(lua_State *L) {
int type = lua_type(L, 2);
switch ( type ) {
case LUA_TNIL:
return js_generator_null(L);
case LUA_TNUMBER:
return js_generator_number(L);
case LUA_TBOOLEAN:
return js_generator_boolean(L);
case LUA_TSTRING:
return js_generator_string(L);
case LUA_TUSERDATA:
if ( lua_topointer(L, 2) == js_null ) {
return js_generator_null(L);
}
case LUA_TLIGHTUSERDATA:
case LUA_TTABLE:
case LUA_TFUNCTION:
case LUA_TTHREAD:
if ( luaL_getmetafield(L, 2, "__gen_json") ) {
if ( lua_isfunction(L, -1) ) {
lua_settop(L, 3); // gen, obj, func
lua_insert(L, 1); // func, gen, obj
lua_insert(L, 2); // func, obj, gen
lua_call(L, 2, 0);
return 0;
}
lua_pop(L, 1);
}
// Simply ignore it, perhaps we should warn?
if ( type != LUA_TTABLE ) return 0;
int max = 0;
int is_array = 1;
// First iterate over the table to see if it is an array:
lua_pushnil(L);
while ( lua_next(L, 2) != 0 ) {
if ( lua_type(L, -2) == LUA_TNUMBER ) {
double num = lua_tonumber(L, -2);
if ( num == floor(num) ) {
if ( num > max ) max = num;
} else {
lua_pop(L, 2);
is_array = 0;
break;
}
} else {
lua_pop(L, 2);
is_array = 0;
break;
}
lua_pop(L, 1);
}
if ( is_array ) {
int i;
js_generator_open_array(L);
for ( i=1; i <= max; i++ ) {
lua_pushinteger(L, i);
lua_gettable(L, 2);
// RECURSIVE CALL:
// gen, obj, ?, val, func, gen, val
lua_pushcfunction(L, js_generator_value);
lua_pushvalue(L, 1);
lua_pushvalue(L, -3);
lua_call(L, 2, 0);
lua_pop(L, 1);
}
} else {
js_generator_open_object(L);
lua_pushnil(L);
while ( lua_next(L, 2) != 0 ) {
size_t len;
const char* str;
// gen, obj, ?, key, val, func, gen, key
lua_pushcfunction(L, js_generator_string);
lua_pushvalue(L, 1);
if ( lua_isstring(L, -4) ) {
lua_pushvalue(L, -4);
} else {
// Must coerce into a string:
lua_getglobal(L, "tostring");
lua_pushvalue(L, -5);
lua_call(L, 1, 1);
}
lua_call(L, 2, 0);
// RECURSIVE CALL:
// gen, obj, ?, key, val, func, gen, val
lua_pushcfunction(L, js_generator_value);
lua_pushvalue(L, 1);
lua_pushvalue(L, -3);
lua_call(L, 2, 0);
lua_pop(L, 1);
}
}
js_generator_close(L);
return 0;
case LUA_TNONE:
lua_pushfstring(L, "MissingArgument: second parameter to js_generator_value() must be defined at %s line %d", type, __FILE__, __LINE__);
default:
lua_pushfstring(L, "Unreachable: js_generator_value passed lua type (%d) not recognized at %s line %d", type, __FILE__, __LINE__);
}
// Shouldn't get here:
lua_error(L);
return 0;
}
typedef struct {
lua_State* L;
int printer_ref;
} js_printer_ctx;
//////////////////////////////////////////////////////////////////////
static void js_printer(void* void_ctx, const char* str, unsigned int len) {
js_printer_ctx* ctx = (js_printer_ctx*)void_ctx;
lua_State* L = ctx->L;
// refs
lua_getfield(L, LUA_REGISTRYINDEX, "yajl.refs");
// refs, printer
lua_rawgeti(L, -1, ctx->printer_ref);
if ( lua_isfunction(L, -1) ) {
lua_pushlstring(L, str, len);
// Not sure if yajl can handle longjmp's if this errors...
lua_call(L, 1, 0);
lua_pop(L, 1);
} else {
lua_pop(L, 2);
}
}
//////////////////////////////////////////////////////////////////////
static int js_generator(lua_State *L) {
yajl_gen_config cfg = { 0, NULL };
yajl_print_t print = NULL;
void * ctx = NULL;
luaL_checktype(L, 1, LUA_TTABLE);
// {args}, ?, tbl
lua_newtable(L);
// Validate and save in fenv so it isn't gc'ed:
lua_getfield(L, 1, "printer");
if ( ! lua_isnil(L, -1) ) {
luaL_checktype(L, -1, LUA_TFUNCTION);
lua_pushvalue(L, -1);
// {args}, ?, tbl, printer, printer
lua_setfield(L, -3, "printer");
js_printer_ctx* print_ctx = (js_printer_ctx*)
lua_newuserdata(L, sizeof(js_printer_ctx));
// {args}, ?, tbl, printer, printer_ctx
lua_setfield(L, -3, "printer_ctx");
// {args}, ?, tbl, printer
lua_getfield(L, LUA_REGISTRYINDEX, "yajl.refs");
// {args}, ?, tbl, printer, refs
lua_insert(L, -2);
// {args}, ?, tbl, refs, printer
print_ctx->printer_ref = luaL_ref(L, -2);
print_ctx->L = L;
print = &js_printer;
ctx = print_ctx;
}
lua_pop(L, 1);
// {args}, ?, tbl
// Get the indent and save so it isn't gc'ed:
lua_getfield(L, 1, "indent");
if ( ! lua_isnil(L, -1) ) {
cfg.beautify = 1;
cfg.indentString = lua_tostring(L, -1);
lua_setfield(L, -2, "indent");
} else {
lua_pop(L, 1);
}
// {args}, ?, tbl
// Sucks that yajl's generator doesn't keep track of this for me
// (this is a stack of strings "array" and "object" so I can keep
// track of what to "close"):
lua_newtable(L);
lua_setfield(L, -2, "stack");
// {args}, ?, tbl
yajl_gen* handle = (yajl_gen*)lua_newuserdata(L, sizeof(yajl_gen));
*handle = yajl_gen_alloc2(print, &cfg, NULL, ctx);
// {args}, ?, tbl, ud, meta
luaL_getmetatable(L, "yajl.generator.meta");
lua_setmetatable(L, -2);
// {args}, ?, tbl, ud
lua_insert(L, -2);
// {args}, ?, ud, tbl
lua_setfenv(L, -2);
return 1;
}
//////////////////////////////////////////////////////////////////////
static void js_create_parser_mt(lua_State *L) {
luaL_newmetatable(L, "yajl.parser.meta"); // {}
lua_pushcfunction(L, js_parser_delete);
lua_setfield(L, -2, "__gc");
lua_pop(L, 1); // <empty>
}
//////////////////////////////////////////////////////////////////////
static void js_create_generator_mt(lua_State *L) {
luaL_newmetatable(L, "yajl.generator.meta"); // {}
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, js_generator_delete);
lua_setfield(L, -2, "__gc");
lua_pushcfunction(L, js_generator_value);
lua_setfield(L, -2, "value");
lua_pushcfunction(L, js_generator_integer);
lua_setfield(L, -2, "integer");
lua_pushcfunction(L, js_generator_double);
lua_setfield(L, -2, "double");
lua_pushcfunction(L, js_generator_number);
lua_setfield(L, -2, "number");
lua_pushcfunction(L, js_generator_string);
lua_setfield(L, -2, "string");
lua_pushcfunction(L, js_generator_null);
lua_setfield(L, -2, "null");
lua_pushcfunction(L, js_generator_boolean);
lua_setfield(L, -2, "boolean");
lua_pushcfunction(L, js_generator_open_object);
lua_setfield(L, -2, "open_object");
lua_pushcfunction(L, js_generator_open_array);
lua_setfield(L, -2, "open_array");
lua_pushcfunction(L, js_generator_close);
lua_setfield(L, -2, "close");
lua_pop(L, 1); // <empty>
}
static int js_null_tostring(lua_State* L) {
lua_pushstring(L, "null");
return 1;
}
//////////////////////////////////////////////////////////////////////
static void js_create_null_mt(lua_State *L) {
luaL_newmetatable(L, "yajl.null.meta"); // {}
lua_pushcfunction(L, js_null_tostring);
lua_setfield(L, -2, "__tostring");
lua_pop(L, 1); // <empty>
}
//////////////////////////////////////////////////////////////////////
LUALIB_API int luaopen_yajl(lua_State *L) {
js_create_parser_mt(L);
js_create_generator_mt(L);
js_create_null_mt(L);
// Create the yajl.refs weak table:
lua_createtable(L, 0, 2);
lua_pushliteral(L, "v"); // tbl, "v"
lua_setfield(L, -2, "__mode");
lua_pushvalue(L, -1); // tbl, tbl
lua_setmetatable(L, -2); // tbl
lua_setfield(L, LUA_REGISTRYINDEX, "yajl.refs");
lua_createtable(L, 0, 4);
lua_pushcfunction(L, js_to_string);
lua_setfield(L, -2, "to_string");
lua_pushcfunction(L, js_to_value);
lua_setfield(L, -2, "to_value");
lua_pushcfunction(L, js_parser);
lua_setfield(L, -2, "parser");
lua_pushcfunction(L, js_generator);
lua_setfield(L, -2, "generator");
js_null = lua_newuserdata(L, 0);
luaL_getmetatable(L, "yajl.null.meta");
lua_setmetatable(L, -2);
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "yajl.null");
lua_setfield(L, -2, "null");
return 1;
}