1 /*
2 * Copyright (c) 2016, Masatake YAMATO
3 * Copyright (c) 2016, Red Hat, Inc.
4 * Copyright (c) 2022, Vasily Kulikov
5 *
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License version 2 or (at your option) any later version.
8 *
9 * This module contains functions for applying yaml traversing.
10 */
11
12 #include "general.h" /* must always come first */
13
14 #include <inttypes.h>
15
16 #include "debug.h"
17 #include "entry.h"
18 #include "htable.h"
19 #include "options.h"
20 #include "parse.h"
21 #include "read.h"
22 #include "subparser.h"
23 #include "trace.h"
24 #include "types.h"
25 #include "yaml.h"
26
27 #include "numarray.h"
28 #include "keyword.h"
29 #include "trashbox.h"
30
31
32 #define entry(X) [X] = #X
33 static const char *tokenTypeName [] = {
34 entry (YAML_NO_TOKEN),
35 entry (YAML_STREAM_START_TOKEN),
36 entry (YAML_STREAM_END_TOKEN),
37 entry (YAML_VERSION_DIRECTIVE_TOKEN),
38 entry (YAML_TAG_DIRECTIVE_TOKEN),
39 entry (YAML_DOCUMENT_START_TOKEN),
40 entry (YAML_DOCUMENT_END_TOKEN),
41 entry (YAML_BLOCK_SEQUENCE_START_TOKEN),
42 entry (YAML_BLOCK_MAPPING_START_TOKEN),
43 entry (YAML_BLOCK_END_TOKEN),
44 entry (YAML_FLOW_SEQUENCE_START_TOKEN),
45 entry (YAML_FLOW_SEQUENCE_END_TOKEN),
46 entry (YAML_FLOW_MAPPING_START_TOKEN),
47 entry (YAML_FLOW_MAPPING_END_TOKEN),
48 entry (YAML_BLOCK_ENTRY_TOKEN),
49 entry (YAML_FLOW_ENTRY_TOKEN),
50 entry (YAML_KEY_TOKEN),
51 entry (YAML_VALUE_TOKEN),
52 entry (YAML_ALIAS_TOKEN),
53 entry (YAML_ANCHOR_TOKEN),
54 entry (YAML_TAG_TOKEN),
55 entry (YAML_SCALAR_TOKEN),
56 };
57
yamlInit(yaml_parser_t * yaml)58 static void yamlInit (yaml_parser_t *yaml)
59 {
60 const unsigned char* data;
61 size_t size;
62
63 yaml_parser_initialize (yaml);
64
65 data = getInputFileData (&size);
66 Assert (data);
67
68 yaml_parser_set_input_string (yaml, data, size);
69 }
70
yamlFini(yaml_parser_t * yaml)71 static void yamlFini (yaml_parser_t *yaml)
72 {
73 yaml_parser_delete (yaml);
74 }
75
attachYamlPosition(tagEntryInfo * tag,yaml_token_t * token,bool asEndPosition)76 extern void attachYamlPosition (tagEntryInfo *tag, yaml_token_t *token, bool asEndPosition)
77 {
78 unsigned int ln = token->start_mark.line + 1;
79
80 if (asEndPosition)
81 tag->extensionFields.endLine = ln;
82 else
83 {
84 tag->lineNumber = ln;
85 tag->filePosition = getInputFilePositionForLine (tag->lineNumber);
86 }
87 }
88
89 enum YamlKind {
90 K_ANCHOR,
91 };
92
93 enum YamlAnchorRole {
94 R_ANCHOR_ALIAS,
95 };
96
97 static roleDefinition YamlAnchorRoles [] = {
98 { true, "alias", "alias" },
99 };
100
101 static kindDefinition YamlKinds [] = {
102 { true, 'a', "anchor", "anchors",
103 .referenceOnly = false, ATTACH_ROLES(YamlAnchorRoles) },
104 };
105
handlYamlToken(yaml_token_t * token)106 static void handlYamlToken (yaml_token_t *token)
107 {
108 tagEntryInfo tag;
109
110 if (token->type == YAML_ANCHOR_TOKEN)
111 {
112 initTagEntry (&tag, (char *)token->data.anchor.value,
113 K_ANCHOR);
114 attachYamlPosition (&tag, token, false);
115 makeTagEntry (&tag);
116 }
117 else if (token->type == YAML_ALIAS_TOKEN)
118 {
119 initRefTagEntry (&tag, (char *)token->data.alias.value,
120 K_ANCHOR, R_ANCHOR_ALIAS);
121 attachYamlPosition (&tag, token, false);
122 makeTagEntry (&tag);
123 }
124 }
125
findYamlTags(void)126 static void findYamlTags (void)
127 {
128 subparser *sub;
129 yaml_parser_t yaml;
130 yaml_token_t token;
131 bool done;
132
133 yamlInit (&yaml);
134
135 findRegexTags ();
136
137 foreachSubparser(sub, false)
138 {
139 enterSubparser (sub);
140 ((yamlSubparser*)sub)->ypathTypeStack = NULL;
141 leaveSubparser ();
142 }
143
144 sub = getSubparserRunningBaseparser();
145 if (sub)
146 chooseExclusiveSubparser (sub, NULL);
147
148 done = false;
149 while (!done)
150 {
151 if (!yaml_parser_scan (&yaml, &token))
152 break;
153
154 handlYamlToken (&token);
155 foreachSubparser(sub, false)
156 {
157 enterSubparser (sub);
158 ((yamlSubparser *)sub)->newTokenNotfify ((yamlSubparser *)sub, &token);
159 leaveSubparser ();
160 }
161
162 TRACE_PRINT("yaml token:%s<%d>@Line:%"PRIuPTR"", tokenTypeName[token.type], token.type,
163 token.start_mark.line + 1);
164 if (isTraced() && token.type == YAML_SCALAR_TOKEN)
165 {
166 TRACE_PRINT_FMT(" ");
167 for (size_t i = 0; i < token.data.scalar.length; i++)
168 TRACE_PRINT_FMT("%c", token.data.scalar.value[i]);
169 TRACE_PRINT_NEWLINE();
170 }
171
172 if (token.type == YAML_STREAM_END_TOKEN)
173 done = true;
174
175 yaml_token_delete (&token);
176 }
177
178 foreachSubparser(sub, false)
179 {
180 enterSubparser (sub);
181 ypathPopAllTypes ((yamlSubparser*)sub);
182 leaveSubparser ();
183 }
184
185 yamlFini (&yaml);
186 }
187
YamlParser(void)188 extern parserDefinition* YamlParser (void)
189 {
190 static const char *const extensions [] = { "yml", "yaml", NULL };
191 parserDefinition* const def = parserNew ("Yaml");
192
193 def->kindTable = YamlKinds;
194 def->extensions = extensions;
195 def->parser = findYamlTags;
196 def->useCork = CORK_QUEUE;
197 def->kindTable = YamlKinds;
198 def->kindCount = ARRAY_SIZE (YamlKinds);
199 def->useMemoryStreamInput = true;
200
201 return def;
202 }
203
204 /*
205 * Experimental Ypath code
206 */
207 struct ypathTypeStack {
208 yaml_token_type_t type;
209 int key;
210 struct ypathTypeStack *next;
211 };
212
ypathCompileTable(langType language,tagYpathTable * table,int keywordId)213 extern int ypathCompileTable (langType language, tagYpathTable *table, int keywordId)
214 {
215 vString *tmpkey = vStringNew();
216 intArray *code = intArrayNew ();
217
218 for (const char *ypath = table->ypath; true; ypath++)
219 {
220 if (*ypath == '/' || *ypath == '\0')
221 {
222 if (!vStringIsEmpty (tmpkey))
223 {
224 int k;
225 if (vStringLength (tmpkey) == 1 && (vStringValue (tmpkey)[0] == '*'))
226 k = KEYWORD_NONE;
227 else
228 {
229 k = lookupKeyword (vStringValue (tmpkey), language);
230 if (k == KEYWORD_NONE)
231 {
232 char *keyword = DEFAULT_TRASH_BOX(vStringStrdup (tmpkey), eFree);
233 k = keywordId++;
234 addKeyword (keyword, language, k);
235 }
236 }
237 intArrayAdd (code, k);
238 vStringClear (tmpkey);
239 }
240 if (*ypath == '\0')
241 break;
242 }
243 else
244 vStringPut (tmpkey, *ypath);
245 }
246
247 intArrayReverse (code);
248 table->code = code;
249
250 vStringDelete (tmpkey);
251 return keywordId;
252 }
253
ypathCompileTables(langType language,tagYpathTable tables[],size_t count,int keywordId)254 extern void ypathCompileTables (langType language, tagYpathTable tables[], size_t count, int keywordId)
255 {
256 for (size_t i = 0; i < count; i++)
257 keywordId = ypathCompileTable (language, tables + i, keywordId);
258 }
259
ypathCompiledCodeDelete(tagYpathTable tables[],size_t count)260 extern void ypathCompiledCodeDelete (tagYpathTable tables[], size_t count)
261 {
262 for (size_t i = 0; i < count; i++)
263 {
264 intArrayDelete (tables[i].code);
265 tables[i].code = NULL;
266 }
267 }
268
ypathPushType(yamlSubparser * yaml,yaml_token_t * token)269 extern void ypathPushType (yamlSubparser *yaml, yaml_token_t *token)
270 {
271 struct ypathTypeStack *s;
272
273 s = xMalloc (1, struct ypathTypeStack);
274
275 s->next = yaml->ypathTypeStack;
276 yaml->ypathTypeStack = s;
277
278 s->type = token->type;
279 s->key = KEYWORD_NONE;
280 }
281
ypathPopType(yamlSubparser * yaml)282 extern void ypathPopType (yamlSubparser *yaml)
283 {
284 struct ypathTypeStack *s;
285
286 s = yaml->ypathTypeStack;
287 yaml->ypathTypeStack = s->next;
288
289 s->next = NULL;
290
291 eFree (s);
292 }
293
ypathPopAllTypes(yamlSubparser * yaml)294 extern void ypathPopAllTypes (yamlSubparser *yaml)
295 {
296 while (yaml->ypathTypeStack)
297 ypathPopType (yaml);
298 }
299
ypathFillKeywordOfTokenMaybe(yamlSubparser * yaml,yaml_token_t * token,langType lang)300 extern void ypathFillKeywordOfTokenMaybe (yamlSubparser *yaml, yaml_token_t *token, langType lang)
301 {
302 if (!yaml->ypathTypeStack)
303 return;
304
305 int k = lookupKeyword ((char *)token->data.scalar.value, lang);
306 yaml->ypathTypeStack->key = k;
307 }
308
ypathStateStackMatch(struct ypathTypeStack * stack,intArray * code,size_t offset)309 static bool ypathStateStackMatch (struct ypathTypeStack *stack,
310 intArray *code, size_t offset)
311 {
312 if (intArrayCount (code) - offset == 0)
313 {
314 if (stack == NULL)
315 return true;
316 else
317 return false;
318 }
319
320 if (stack == NULL)
321 return false;
322
323 if (stack->key == intArrayItem (code, offset))
324 return ypathStateStackMatch (stack->next, code, offset + 1);
325 else
326 return false;
327 }
328
ypathHandleToken(yamlSubparser * yaml,yaml_token_t * token,int state,tagYpathTable tables[],size_t count)329 extern void ypathHandleToken (yamlSubparser *yaml, yaml_token_t *token, int state,
330 tagYpathTable tables[], size_t count)
331 {
332 if (!yaml->ypathTypeStack)
333 return;
334
335 for (size_t i = 0; i < count; i++)
336 {
337 if (tables[i].expected_state != state)
338 continue;
339
340 if (ypathStateStackMatch(yaml->ypathTypeStack, tables[i].code, 0))
341 {
342 tagEntryInfo tag;
343 bool r = true;
344 if (tables[i].initTagEntry)
345 r = (* tables[i].initTagEntry) (&tag, (char *)token->data.scalar.value,
346 tables[i].data);
347 else
348 initTagEntry (&tag, (char *)token->data.scalar.value, tables[i].kind);
349
350 if (r)
351 {
352 attachYamlPosition (&tag, token, false);
353 makeTagEntry (&tag);
354 }
355 break;
356 }
357 }
358 }
359
360 #ifdef DO_TRACING
ypathPrintTypeStack0(struct ypathTypeStack * stack)361 extern void ypathPrintTypeStack0(struct ypathTypeStack *stack)
362 {
363 if (!stack)
364 {
365 TRACE_PRINT_NEWLINE();
366 return;
367 }
368
369 tracePrintFmt("[%d] - ", stack->key);
370 ypathPrintTypeStack0 (stack->next);
371 }
372
ypathPrintTypeStack(yamlSubparser * yaml)373 extern void ypathPrintTypeStack(yamlSubparser *yaml)
374 {
375 ypathPrintTypeStack0 (yaml->ypathTypeStack);
376 }
377 #else
ypathPrintTypeStack(yamlSubparser * yaml)378 extern void ypathPrintTypeStack(yamlSubparser *yaml) {}
379 #endif
380