aboutsummaryrefslogtreecommitdiff
path: root/ext/yaml/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/yaml/parser.c')
-rw-r--r--ext/yaml/parser.c410
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;
+}