xref: /Universal-ctags/parsers/yaml.c (revision 5ee7d66f499a2ddc0eada4e7e6edbd66ee1cadac)
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