411 lines
10 KiB
C
411 lines
10 KiB
C
/*
|
|
* parser.c, libyaml parser binding for Lua
|
|
* Written by Gary V. Vaughan, 2013
|
|
*
|
|
* Copyright (C) 2013-2022 Gary V. Vaughan
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
#include "lyaml.h"
|
|
|
|
typedef struct {
|
|
lua_State *L;
|
|
yaml_parser_t parser;
|
|
yaml_event_t event;
|
|
char validevent;
|
|
int document_count;
|
|
} lyaml_parser;
|
|
|
|
|
|
static void
|
|
parser_delete_event (lyaml_parser *parser)
|
|
{
|
|
if (parser->validevent)
|
|
{
|
|
yaml_event_delete (&parser->event);
|
|
parser->validevent = 0;
|
|
}
|
|
}
|
|
|
|
/* With the event result table on the top of the stack, insert
|
|
a mark entry. */
|
|
static void
|
|
parser_set_mark (lua_State *L, const char *k, yaml_mark_t mark)
|
|
{
|
|
lua_pushstring (L, k);
|
|
lua_createtable (L, 0, 3);
|
|
#define MENTRY(_s) RAWSET_INTEGER(#_s, mark._s)
|
|
MENTRY( index );
|
|
MENTRY( line );
|
|
MENTRY( column );
|
|
#undef MENTRY
|
|
lua_rawset (L, -3);
|
|
}
|
|
|
|
/* Push a new event table, pre-populated with shared elements. */
|
|
static void
|
|
parser_push_eventtable (lyaml_parser *parser, const char *v, int n)
|
|
{
|
|
lua_State *L = parser->L;
|
|
|
|
lua_createtable (L, 0, n + 3);
|
|
RAWSET_STRING ("type", v);
|
|
#define MENTRY(_s) parser_set_mark (L, #_s, parser->event._s)
|
|
MENTRY( start_mark );
|
|
MENTRY( end_mark );
|
|
#undef MENTRY
|
|
}
|
|
|
|
static void
|
|
parse_STREAM_START (lyaml_parser *parser)
|
|
{
|
|
#define EVENTF(_f) (parser->event.data.stream_start._f)
|
|
lua_State *L = parser->L;
|
|
const char *encoding;
|
|
|
|
switch (EVENTF (encoding))
|
|
{
|
|
#define MENTRY(_s) \
|
|
case YAML_##_s##_ENCODING: encoding = #_s; break
|
|
|
|
MENTRY( ANY );
|
|
MENTRY( UTF8 );
|
|
MENTRY( UTF16LE );
|
|
MENTRY( UTF16BE );
|
|
#undef MENTRY
|
|
|
|
default:
|
|
lua_pushfstring (L, "invalid encoding %d", EVENTF (encoding));
|
|
lua_error (L);
|
|
}
|
|
|
|
parser_push_eventtable (parser, "STREAM_START", 1);
|
|
RAWSET_STRING ("encoding", encoding);
|
|
#undef EVENTF
|
|
}
|
|
|
|
/* With the tag list on the top of the stack, append TAG. */
|
|
static void
|
|
parser_append_tag (lua_State *L, yaml_tag_directive_t tag)
|
|
{
|
|
lua_createtable (L, 0, 2);
|
|
#define MENTRY(_s) RAWSET_STRING(#_s, tag._s)
|
|
MENTRY( handle );
|
|
MENTRY( prefix );
|
|
#undef MENTRY
|
|
lua_rawseti (L, -2, lua_objlen (L, -2) + 1);
|
|
}
|
|
|
|
static void
|
|
parse_DOCUMENT_START (lyaml_parser *parser)
|
|
{
|
|
#define EVENTF(_f) (parser->event.data.document_start._f)
|
|
lua_State *L = parser->L;
|
|
|
|
/* increment document count */
|
|
parser->document_count++;
|
|
|
|
parser_push_eventtable (parser, "DOCUMENT_START", 1);
|
|
RAWSET_BOOLEAN ("implicit", EVENTF (implicit));
|
|
|
|
/* version_directive = { major = M, minor = N } */
|
|
if (EVENTF (version_directive))
|
|
{
|
|
lua_pushliteral (L, "version_directive");
|
|
lua_createtable (L, 0, 2);
|
|
#define MENTRY(_s) RAWSET_INTEGER(#_s, EVENTF (version_directive->_s))
|
|
MENTRY( major );
|
|
MENTRY( minor );
|
|
#undef MENTRY
|
|
lua_rawset (L, -3);
|
|
}
|
|
|
|
/* tag_directives = { {handle = H1, prefix = P1}, ... } */
|
|
if (EVENTF (tag_directives.start) &&
|
|
EVENTF (tag_directives.end)) {
|
|
yaml_tag_directive_t *cur;
|
|
|
|
lua_pushliteral (L, "tag_directives");
|
|
lua_newtable (L);
|
|
for (cur = EVENTF (tag_directives.start);
|
|
cur != EVENTF (tag_directives.end);
|
|
cur = cur + 1)
|
|
{
|
|
parser_append_tag (L, *cur);
|
|
}
|
|
lua_rawset (L, -3);
|
|
}
|
|
#undef EVENTF
|
|
}
|
|
|
|
static void
|
|
parse_DOCUMENT_END (lyaml_parser *parser)
|
|
{
|
|
#define EVENTF(_f) (parser->event.data.document_end._f)
|
|
lua_State *L = parser->L;
|
|
|
|
parser_push_eventtable (parser, "DOCUMENT_END", 1);
|
|
RAWSET_BOOLEAN ("implicit", EVENTF (implicit));
|
|
#undef EVENTF
|
|
}
|
|
|
|
static void
|
|
parse_ALIAS (lyaml_parser *parser)
|
|
{
|
|
#define EVENTF(_f) (parser->event.data.alias._f)
|
|
lua_State *L = parser->L;
|
|
|
|
parser_push_eventtable (parser, "ALIAS", 1);
|
|
RAWSET_EVENTF (anchor);
|
|
#undef EVENTF
|
|
}
|
|
|
|
static void
|
|
parse_SCALAR (lyaml_parser *parser)
|
|
{
|
|
#define EVENTF(_f) (parser->event.data.scalar._f)
|
|
lua_State *L = parser->L;
|
|
const char *style;
|
|
|
|
switch (EVENTF (style))
|
|
{
|
|
#define MENTRY(_s) \
|
|
case YAML_##_s##_SCALAR_STYLE: style = #_s; break
|
|
|
|
MENTRY( ANY );
|
|
MENTRY( PLAIN );
|
|
MENTRY( SINGLE_QUOTED );
|
|
MENTRY( DOUBLE_QUOTED );
|
|
MENTRY( LITERAL );
|
|
MENTRY( FOLDED );
|
|
#undef MENTRY
|
|
|
|
default:
|
|
lua_pushfstring (L, "invalid sequence style %d", EVENTF (style));
|
|
lua_error (L);
|
|
}
|
|
|
|
|
|
parser_push_eventtable (parser, "SCALAR", 6);
|
|
RAWSET_EVENTF (anchor);
|
|
RAWSET_EVENTF (tag);
|
|
RAWSET_EVENTF (value);
|
|
|
|
RAWSET_BOOLEAN ("plain_implicit", EVENTF (plain_implicit));
|
|
RAWSET_BOOLEAN ("quoted_implicit", EVENTF (quoted_implicit));
|
|
RAWSET_STRING ("style", style);
|
|
#undef EVENTF
|
|
}
|
|
|
|
static void
|
|
parse_SEQUENCE_START (lyaml_parser *parser)
|
|
{
|
|
#define EVENTF(_f) (parser->event.data.sequence_start._f)
|
|
lua_State *L = parser->L;
|
|
const char *style;
|
|
|
|
switch (EVENTF (style))
|
|
{
|
|
#define MENTRY(_s) \
|
|
case YAML_##_s##_SEQUENCE_STYLE: style = #_s; break
|
|
|
|
MENTRY( ANY );
|
|
MENTRY( BLOCK );
|
|
MENTRY( FLOW );
|
|
#undef MENTRY
|
|
|
|
default:
|
|
lua_pushfstring (L, "invalid sequence style %d", EVENTF (style));
|
|
lua_error (L);
|
|
}
|
|
|
|
parser_push_eventtable (parser, "SEQUENCE_START", 4);
|
|
RAWSET_EVENTF (anchor);
|
|
RAWSET_EVENTF (tag);
|
|
RAWSET_BOOLEAN ("implicit", EVENTF (implicit));
|
|
RAWSET_STRING ("style", style);
|
|
#undef EVENTF
|
|
}
|
|
|
|
static void
|
|
parse_MAPPING_START (lyaml_parser *parser)
|
|
{
|
|
#define EVENTF(_f) (parser->event.data.mapping_start._f)
|
|
lua_State *L = parser->L;
|
|
const char *style;
|
|
|
|
switch (EVENTF (style))
|
|
{
|
|
#define MENTRY(_s) \
|
|
case YAML_##_s##_MAPPING_STYLE: style = #_s; break
|
|
|
|
MENTRY( ANY );
|
|
MENTRY( BLOCK );
|
|
MENTRY( FLOW );
|
|
#undef MENTRY
|
|
|
|
default:
|
|
lua_pushfstring (L, "invalid mapping style %d", EVENTF (style));
|
|
lua_error (L);
|
|
}
|
|
|
|
parser_push_eventtable (parser, "MAPPING_START", 4);
|
|
RAWSET_EVENTF (anchor);
|
|
RAWSET_EVENTF (tag);
|
|
RAWSET_BOOLEAN ("implicit", EVENTF (implicit));
|
|
RAWSET_STRING ("style", style);
|
|
#undef EVENTF
|
|
}
|
|
|
|
static void
|
|
parser_generate_error_message (lyaml_parser *parser)
|
|
{
|
|
yaml_parser_t *P = &parser->parser;
|
|
char buf[256];
|
|
luaL_Buffer b;
|
|
|
|
luaL_buffinit (parser->L, &b);
|
|
luaL_addstring (&b, P->problem ? P->problem : "A problem");
|
|
snprintf (buf, sizeof (buf), " at document: %d", parser->document_count);
|
|
luaL_addstring (&b, buf);
|
|
|
|
if (P->problem_mark.line || P->problem_mark.column)
|
|
{
|
|
snprintf (buf, sizeof (buf), ", line: %lu, column: %lu",
|
|
(unsigned long) P->problem_mark.line + 1,
|
|
(unsigned long) P->problem_mark.column + 1);
|
|
luaL_addstring (&b, buf);
|
|
}
|
|
luaL_addstring (&b, "\n");
|
|
|
|
if (P->context)
|
|
{
|
|
snprintf (buf, sizeof (buf), "%s at line: %lu, column: %lu\n",
|
|
P->context,
|
|
(unsigned long) P->context_mark.line + 1,
|
|
(unsigned long) P->context_mark.column + 1);
|
|
luaL_addstring (&b, buf);
|
|
}
|
|
|
|
luaL_pushresult (&b);
|
|
}
|
|
|
|
static int
|
|
event_iter (lua_State *L)
|
|
{
|
|
lyaml_parser *parser = (lyaml_parser *)lua_touserdata(L, lua_upvalueindex(1));
|
|
char *str;
|
|
|
|
parser_delete_event (parser);
|
|
if (yaml_parser_parse (&parser->parser, &parser->event) != 1)
|
|
{
|
|
parser_generate_error_message (parser);
|
|
return lua_error (L);
|
|
}
|
|
|
|
parser->validevent = 1;
|
|
|
|
lua_newtable (L);
|
|
lua_pushliteral (L, "type");
|
|
|
|
switch (parser->event.type)
|
|
{
|
|
/* First the simple events, generated right here... */
|
|
#define MENTRY(_s) \
|
|
case YAML_##_s##_EVENT: parser_push_eventtable (parser, #_s, 0); break
|
|
MENTRY( STREAM_END );
|
|
MENTRY( SEQUENCE_END );
|
|
MENTRY( MAPPING_END );
|
|
#undef MENTRY
|
|
|
|
/* ...then the complex events, generated by a function call. */
|
|
#define MENTRY(_s) \
|
|
case YAML_##_s##_EVENT: parse_##_s (parser); break
|
|
MENTRY( STREAM_START );
|
|
MENTRY( DOCUMENT_START );
|
|
MENTRY( DOCUMENT_END );
|
|
MENTRY( ALIAS );
|
|
MENTRY( SCALAR );
|
|
MENTRY( SEQUENCE_START );
|
|
MENTRY( MAPPING_START );
|
|
#undef MENTRY
|
|
|
|
case YAML_NO_EVENT:
|
|
lua_pushnil (L);
|
|
break;
|
|
default:
|
|
lua_pushfstring (L, "invalid event %d", parser->event.type);
|
|
return lua_error (L);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
parser_gc (lua_State *L)
|
|
{
|
|
lyaml_parser *parser = (lyaml_parser *) lua_touserdata (L, 1);
|
|
|
|
if (parser)
|
|
{
|
|
parser_delete_event (parser);
|
|
yaml_parser_delete (&parser->parser);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
parser_init (lua_State *L)
|
|
{
|
|
luaL_newmetatable(L, "lyaml.parser");
|
|
lua_pushcfunction(L, parser_gc);
|
|
lua_setfield(L, -2, "__gc");
|
|
}
|
|
|
|
int
|
|
Pparser (lua_State *L)
|
|
{
|
|
lyaml_parser *parser;
|
|
const unsigned char *str;
|
|
|
|
/* requires a single string type argument */
|
|
luaL_argcheck (L, lua_isstring (L, 1), 1, "must provide a string argument");
|
|
str = (const unsigned char *) lua_tostring (L, 1);
|
|
|
|
/* create a user datum to store the parser */
|
|
parser = (lyaml_parser *) lua_newuserdata (L, sizeof (*parser));
|
|
memset ((void *) parser, 0, sizeof (*parser));
|
|
parser->L = L;
|
|
|
|
/* set its metatable */
|
|
luaL_getmetatable (L, "lyaml.parser");
|
|
lua_setmetatable (L, -2);
|
|
|
|
/* try to initialize the parser */
|
|
if (yaml_parser_initialize (&parser->parser) == 0)
|
|
luaL_error (L, "cannot initialize parser for %s", str);
|
|
yaml_parser_set_input_string (&parser->parser, str, lua_strlen (L, 1));
|
|
|
|
/* create and return the iterator function, with the loader userdatum as
|
|
its sole upvalue */
|
|
lua_pushcclosure (L, event_iter, 1);
|
|
return 1;
|
|
}
|