/* * scanner.c, libyaml scanner 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_token_t token; char validtoken; int document_count; } lyaml_scanner; static void scanner_delete_token (lyaml_scanner *scanner) { if (scanner->validtoken) { yaml_token_delete (&scanner->token); scanner->validtoken = 0; } } /* With the token result table on the top of the stack, insert a mark entry. */ static void scanner_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 token table, pre-populated with shared elements. */ static void scanner_push_tokentable (lyaml_scanner *scanner, const char *v, int n) { lua_State *L = scanner->L; lua_createtable (L, 0, n + 3); RAWSET_STRING ("type", v); #define MENTRY(_s) scanner_set_mark (L, #_s, scanner->token._s) MENTRY( start_mark ); MENTRY( end_mark ); #undef MENTRY } static void scan_STREAM_START (lyaml_scanner *scanner) { #define EVENTF(_f) (scanner->token.data.stream_start._f) lua_State *L = scanner->L; const char *encoding; switch (EVENTF (encoding)) { #define MENTRY(_s) \ case YAML_##_s##_ENCODING: encoding = #_s; break MENTRY( UTF8 ); MENTRY( UTF16LE ); MENTRY( UTF16BE ); #undef MENTRY default: lua_pushfstring (L, "invalid encoding %d", EVENTF (encoding)); lua_error (L); } scanner_push_tokentable (scanner, "STREAM_START", 1); RAWSET_STRING ("encoding", encoding); #undef EVENTF } static void scan_VERSION_DIRECTIVE (lyaml_scanner *scanner) { #define EVENTF(_f) (scanner->token.data.version_directive._f) lua_State *L = scanner->L; scanner_push_tokentable (scanner, "VERSION_DIRECTIVE", 2); #define MENTRY(_s) RAWSET_INTEGER (#_s, EVENTF (_s)) MENTRY( major ); MENTRY( minor ); #undef MENTRY #undef EVENTF } static void scan_TAG_DIRECTIVE (lyaml_scanner *scanner) { #define EVENTF(_f) (scanner->token.data.tag_directive._f) lua_State *L = scanner->L; scanner_push_tokentable (scanner, "TAG_DIRECTIVE", 2); RAWSET_EVENTF( handle ); RAWSET_EVENTF( prefix ); #undef EVENTF } static void scan_ALIAS (lyaml_scanner *scanner) { #define EVENTF(_f) (scanner->token.data.alias._f) lua_State *L = scanner->L; scanner_push_tokentable (scanner, "ALIAS", 1); RAWSET_EVENTF (value); #undef EVENTF } static void scan_ANCHOR (lyaml_scanner *scanner) { #define EVENTF(_f) (scanner->token.data.anchor._f) lua_State *L = scanner->L; scanner_push_tokentable (scanner, "ANCHOR", 1); RAWSET_EVENTF (value); #undef EVENTF } static void scan_TAG(lyaml_scanner *scanner) { #define EVENTF(_f) (scanner->token.data.tag._f) lua_State *L = scanner->L; scanner_push_tokentable (scanner, "TAG", 2); RAWSET_EVENTF( handle ); RAWSET_EVENTF( suffix ); #undef EVENTF } static void scan_SCALAR (lyaml_scanner *scanner) { #define EVENTF(_f) (scanner->token.data.scalar._f) lua_State *L = scanner->L; const char *style; switch (EVENTF (style)) { #define MENTRY(_s) \ case YAML_##_s##_SCALAR_STYLE: style = #_s; break MENTRY( PLAIN ); MENTRY( SINGLE_QUOTED ); MENTRY( DOUBLE_QUOTED ); MENTRY( LITERAL ); MENTRY( FOLDED ); #undef MENTRY default: lua_pushfstring (L, "invalid scalar style %d", EVENTF (style)); lua_error (L); } scanner_push_tokentable (scanner, "SCALAR", 3); RAWSET_EVENTF (value); RAWSET_INTEGER ("length", EVENTF (length)); RAWSET_STRING ("style", style); #undef EVENTF } static void scanner_generate_error_message (lyaml_scanner *scanner) { yaml_parser_t *P = &scanner->parser; char buf[256]; luaL_Buffer b; luaL_buffinit (scanner->L, &b); luaL_addstring (&b, P->problem ? P->problem : "A problem"); snprintf (buf, sizeof (buf), " at document: %d", scanner->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 token_iter (lua_State *L) { lyaml_scanner *scanner = (lyaml_scanner *)lua_touserdata(L, lua_upvalueindex(1)); char *str; scanner_delete_token (scanner); if (yaml_parser_scan (&scanner->parser, &scanner->token) != 1) { scanner_generate_error_message (scanner); return lua_error (L); } scanner->validtoken = 1; lua_newtable (L); lua_pushliteral (L, "type"); switch (scanner->token.type) { /* First the simple tokens, generated right here... */ #define MENTRY(_s) \ case YAML_##_s##_TOKEN: scanner_push_tokentable (scanner, #_s, 0); break MENTRY( STREAM_END ); MENTRY( DOCUMENT_START ); MENTRY( DOCUMENT_END ); MENTRY( BLOCK_SEQUENCE_START ); MENTRY( BLOCK_MAPPING_START ); MENTRY( BLOCK_END ); MENTRY( FLOW_SEQUENCE_START ); MENTRY( FLOW_SEQUENCE_END ); MENTRY( FLOW_MAPPING_START ); MENTRY( FLOW_MAPPING_END ); MENTRY( BLOCK_ENTRY ); MENTRY( FLOW_ENTRY ); MENTRY( KEY ); MENTRY( VALUE ); #undef MENTRY /* ...then the complex tokens, generated by a function call. */ #define MENTRY(_s) \ case YAML_##_s##_TOKEN: scan_##_s (scanner); break MENTRY( STREAM_START ); MENTRY( VERSION_DIRECTIVE ); MENTRY( TAG_DIRECTIVE ); MENTRY( ALIAS ); MENTRY( ANCHOR ); MENTRY( TAG ); MENTRY( SCALAR ); #undef MENTRY case YAML_NO_TOKEN: lua_pushnil (L); break; default: lua_pushfstring (L, "invalid token %d", scanner->token.type); return lua_error (L); } return 1; } static int scanner_gc (lua_State *L) { lyaml_scanner *scanner = (lyaml_scanner *) lua_touserdata (L, 1); if (scanner) { scanner_delete_token (scanner); yaml_parser_delete (&scanner->parser); } return 0; } void scanner_init (lua_State *L) { luaL_newmetatable (L, "lyaml.scanner"); lua_pushcfunction (L, scanner_gc); lua_setfield (L, -2, "__gc"); } int Pscanner (lua_State *L) { lyaml_scanner *scanner; 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 scanner */ scanner = (lyaml_scanner *) lua_newuserdata (L, sizeof (*scanner)); memset ((void *) scanner, 0, sizeof (*scanner)); scanner->L = L; /* set its metatable */ luaL_getmetatable (L, "lyaml.scanner"); lua_setmetatable (L, -2); /* try to initialize the scanner */ if (yaml_parser_initialize (&scanner->parser) == 0) luaL_error (L, "cannot initialize parser for %s", str); yaml_parser_set_input_string (&scanner->parser, str, lua_strlen (L, 1)); /* create and return the iterator function, with the loader userdatum as its sole upvalue */ lua_pushcclosure (L, token_iter, 1); return 1; }