xref: /freebsd/contrib/lyaml/ext/yaml/scanner.c (revision 2bc180ef045e5911cce0cea1c2a139cffd2b577a)
1 /*
2  * scanner.c, libyaml scanner binding for Lua
3  * Written by Gary V. Vaughan, 2013
4  *
5  * Copyright (C) 2013-2022 Gary V. Vaughan
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  */
25 
26 #include "lyaml.h"
27 
28 
29 typedef struct {
30    lua_State	 *L;
31    yaml_parser_t  parser;
32    yaml_token_t	  token;
33    char		  validtoken;
34    int		  document_count;
35 } lyaml_scanner;
36 
37 
38 static void
scanner_delete_token(lyaml_scanner * scanner)39 scanner_delete_token (lyaml_scanner *scanner)
40 {
41    if (scanner->validtoken)
42    {
43       yaml_token_delete (&scanner->token);
44       scanner->validtoken = 0;
45    }
46 }
47 
48 /* With the token result table on the top of the stack, insert
49    a mark entry. */
50 static void
scanner_set_mark(lua_State * L,const char * k,yaml_mark_t mark)51 scanner_set_mark (lua_State *L, const char *k, yaml_mark_t mark)
52 {
53    lua_pushstring  (L, k);
54    lua_createtable (L, 0, 3);
55 #define MENTRY(_s)	RAWSET_INTEGER (#_s, mark._s)
56          MENTRY( index	);
57          MENTRY( line	);
58          MENTRY( column	);
59 #undef MENTRY
60    lua_rawset (L, -3);
61 }
62 
63 /* Push a new token table, pre-populated with shared elements. */
64 static void
scanner_push_tokentable(lyaml_scanner * scanner,const char * v,int n)65 scanner_push_tokentable (lyaml_scanner *scanner, const char *v, int n)
66 {
67    lua_State *L = scanner->L;
68 
69    lua_createtable (L, 0, n + 3);
70    RAWSET_STRING   ("type", v);
71 
72 #define MENTRY(_s)	scanner_set_mark (L, #_s, scanner->token._s)
73          MENTRY( start_mark	);
74          MENTRY( end_mark	);
75 #undef MENTRY
76 }
77 
78 static void
scan_STREAM_START(lyaml_scanner * scanner)79 scan_STREAM_START (lyaml_scanner *scanner)
80 {
81 #define EVENTF(_f)	(scanner->token.data.stream_start._f)
82    lua_State *L = scanner->L;
83    const char *encoding;
84 
85    switch (EVENTF (encoding))
86    {
87 #define MENTRY(_s)		\
88       case YAML_##_s##_ENCODING: encoding = #_s; break
89          MENTRY( UTF8		);
90          MENTRY( UTF16LE	);
91          MENTRY( UTF16BE	);
92 #undef MENTRY
93 
94       default:
95          lua_pushfstring (L, "invalid encoding %d", EVENTF (encoding));
96          lua_error (L);
97    }
98 
99    scanner_push_tokentable (scanner, "STREAM_START", 1);
100    RAWSET_STRING ("encoding", encoding);
101 #undef EVENTF
102 }
103 
104 static void
scan_VERSION_DIRECTIVE(lyaml_scanner * scanner)105 scan_VERSION_DIRECTIVE (lyaml_scanner *scanner)
106 {
107 #define EVENTF(_f)	(scanner->token.data.version_directive._f)
108    lua_State *L = scanner->L;
109 
110    scanner_push_tokentable (scanner, "VERSION_DIRECTIVE", 2);
111 
112 #define MENTRY(_s)	RAWSET_INTEGER (#_s, EVENTF (_s))
113          MENTRY( major	);
114          MENTRY( minor	);
115 #undef MENTRY
116 #undef EVENTF
117 }
118 
119 static void
scan_TAG_DIRECTIVE(lyaml_scanner * scanner)120 scan_TAG_DIRECTIVE (lyaml_scanner *scanner)
121 {
122 #define EVENTF(_f)	(scanner->token.data.tag_directive._f)
123    lua_State *L = scanner->L;
124 
125    scanner_push_tokentable (scanner, "TAG_DIRECTIVE", 2);
126    RAWSET_EVENTF( handle	);
127    RAWSET_EVENTF( prefix	);
128 #undef EVENTF
129 }
130 
131 static void
scan_ALIAS(lyaml_scanner * scanner)132 scan_ALIAS (lyaml_scanner *scanner)
133 {
134 #define EVENTF(_f)	(scanner->token.data.alias._f)
135    lua_State *L = scanner->L;
136 
137    scanner_push_tokentable (scanner, "ALIAS", 1);
138    RAWSET_EVENTF (value);
139 #undef EVENTF
140 }
141 
142 static void
scan_ANCHOR(lyaml_scanner * scanner)143 scan_ANCHOR (lyaml_scanner *scanner)
144 {
145 #define EVENTF(_f)	(scanner->token.data.anchor._f)
146    lua_State *L = scanner->L;
147 
148    scanner_push_tokentable (scanner, "ANCHOR", 1);
149    RAWSET_EVENTF (value);
150 #undef EVENTF
151 }
152 
153 static void
scan_TAG(lyaml_scanner * scanner)154 scan_TAG(lyaml_scanner *scanner)
155 {
156 #define EVENTF(_f)	(scanner->token.data.tag._f)
157    lua_State *L = scanner->L;
158 
159    scanner_push_tokentable (scanner, "TAG", 2);
160    RAWSET_EVENTF( handle	);
161    RAWSET_EVENTF( suffix	);
162 #undef EVENTF
163 }
164 
165 static void
scan_SCALAR(lyaml_scanner * scanner)166 scan_SCALAR (lyaml_scanner *scanner)
167 {
168 #define EVENTF(_f)	(scanner->token.data.scalar._f)
169    lua_State *L = scanner->L;
170    const char *style;
171 
172    switch (EVENTF (style))
173    {
174 #define MENTRY(_s)		\
175       case YAML_##_s##_SCALAR_STYLE: style = #_s; break
176 
177         MENTRY( PLAIN		);
178         MENTRY( SINGLE_QUOTED	);
179         MENTRY( DOUBLE_QUOTED	);
180 	MENTRY( LITERAL		);
181 	MENTRY( FOLDED		);
182 #undef MENTRY
183 
184       default:
185          lua_pushfstring (L, "invalid scalar style %d", EVENTF (style));
186          lua_error (L);
187    }
188 
189    scanner_push_tokentable (scanner, "SCALAR", 3);
190    RAWSET_EVENTF  (value);
191    RAWSET_INTEGER ("length", EVENTF (length));
192    RAWSET_STRING  ("style", style);
193 #undef EVENTF
194 }
195 
196 static void
scanner_generate_error_message(lyaml_scanner * scanner)197 scanner_generate_error_message (lyaml_scanner *scanner)
198 {
199    yaml_parser_t *P = &scanner->parser;
200    char buf[256];
201    luaL_Buffer b;
202 
203    luaL_buffinit (scanner->L, &b);
204    luaL_addstring (&b, P->problem ? P->problem : "A problem");
205    snprintf (buf, sizeof (buf), " at document: %d", scanner->document_count);
206    luaL_addstring (&b, buf);
207 
208    if (P->problem_mark.line || P->problem_mark.column)
209    {
210       snprintf (buf, sizeof (buf), ", line: %lu, column: %lu",
211          (unsigned long) P->problem_mark.line + 1,
212          (unsigned long) P->problem_mark.column + 1);
213       luaL_addstring (&b, buf);
214    }
215    luaL_addstring (&b, "\n");
216 
217    if (P->context)
218    {
219       snprintf (buf, sizeof (buf), "%s at line: %lu, column: %lu\n",
220          P->context,
221          (unsigned long) P->context_mark.line + 1,
222          (unsigned long) P->context_mark.column + 1);
223       luaL_addstring (&b, buf);
224    }
225 
226    luaL_pushresult (&b);
227 }
228 
229 static int
token_iter(lua_State * L)230 token_iter (lua_State *L)
231 {
232    lyaml_scanner *scanner = (lyaml_scanner *)lua_touserdata(L, lua_upvalueindex(1));
233    char *str;
234 
235    scanner_delete_token (scanner);
236    if (yaml_parser_scan (&scanner->parser, &scanner->token) != 1)
237    {
238       scanner_generate_error_message (scanner);
239       return lua_error (L);
240    }
241 
242    scanner->validtoken = 1;
243 
244    lua_newtable    (L);
245    lua_pushliteral (L, "type");
246 
247    switch (scanner->token.type)
248    {
249       /* First the simple tokens, generated right here... */
250 #define MENTRY(_s)			\
251       case YAML_##_s##_TOKEN: scanner_push_tokentable (scanner, #_s, 0); break
252          MENTRY( STREAM_END		);
253          MENTRY( DOCUMENT_START		);
254          MENTRY( DOCUMENT_END		);
255          MENTRY( BLOCK_SEQUENCE_START	);
256          MENTRY( BLOCK_MAPPING_START	);
257          MENTRY( BLOCK_END		);
258          MENTRY( FLOW_SEQUENCE_START	);
259          MENTRY( FLOW_SEQUENCE_END	);
260          MENTRY( FLOW_MAPPING_START	);
261          MENTRY( FLOW_MAPPING_END	);
262 	 MENTRY( BLOCK_ENTRY		);
263 	 MENTRY( FLOW_ENTRY		);
264 	 MENTRY( KEY			);
265 	 MENTRY( VALUE			);
266 #undef MENTRY
267 
268       /* ...then the complex tokens, generated by a function call. */
269 #define MENTRY(_s)		\
270       case YAML_##_s##_TOKEN: scan_##_s (scanner); break
271          MENTRY( STREAM_START		);
272 	 MENTRY( VERSION_DIRECTIVE	);
273 	 MENTRY( TAG_DIRECTIVE		);
274          MENTRY( ALIAS			);
275 	 MENTRY( ANCHOR			);
276 	 MENTRY( TAG			);
277          MENTRY( SCALAR			);
278 #undef MENTRY
279 
280       case YAML_NO_TOKEN:
281          lua_pushnil (L);
282          break;
283       default:
284          lua_pushfstring  (L, "invalid token %d", scanner->token.type);
285          return lua_error (L);
286    }
287 
288    return 1;
289 }
290 
291 static int
scanner_gc(lua_State * L)292 scanner_gc (lua_State *L)
293 {
294    lyaml_scanner *scanner = (lyaml_scanner *) lua_touserdata (L, 1);
295 
296    if (scanner)
297    {
298       scanner_delete_token (scanner);
299       yaml_parser_delete (&scanner->parser);
300    }
301    return 0;
302 }
303 
304 void
scanner_init(lua_State * L)305 scanner_init (lua_State *L)
306 {
307    luaL_newmetatable (L, "lyaml.scanner");
308    lua_pushcfunction (L, scanner_gc);
309    lua_setfield      (L, -2, "__gc");
310 }
311 
312 int
Pscanner(lua_State * L)313 Pscanner (lua_State *L)
314 {
315    lyaml_scanner *scanner;
316    const unsigned char *str;
317 
318    /* requires a single string type argument */
319    luaL_argcheck (L, lua_isstring (L, 1), 1, "must provide a string argument");
320    str = (const unsigned char *) lua_tostring (L, 1);
321 
322    /* create a user datum to store the scanner */
323    scanner = (lyaml_scanner *) lua_newuserdata (L, sizeof (*scanner));
324    memset ((void *) scanner, 0, sizeof (*scanner));
325    scanner->L = L;
326 
327    /* set its metatable */
328    luaL_getmetatable (L, "lyaml.scanner");
329    lua_setmetatable  (L, -2);
330 
331    /* try to initialize the scanner */
332    if (yaml_parser_initialize (&scanner->parser) == 0)
333       luaL_error (L, "cannot initialize parser for %s", str);
334    yaml_parser_set_input_string (&scanner->parser, str, lua_strlen (L, 1));
335 
336    /* create and return the iterator function, with the loader userdatum as
337       its sole upvalue */
338    lua_pushcclosure (L, token_iter, 1);
339    return 1;
340 }
341