diff options
Diffstat (limited to 'ext/yaml/parser.c')
-rw-r--r-- | ext/yaml/parser.c | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/ext/yaml/parser.c b/ext/yaml/parser.c new file mode 100644 index 000000000000..3136abd49045 --- /dev/null +++ b/ext/yaml/parser.c @@ -0,0 +1,410 @@ +/* + * 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; +} |