xref: /Universal-ctags/parsers/ansibleplaybook.c (revision 9b034e2a470a28f764b92f4a51033fba645df9a0)
1 /*
2 *
3 *   Copyright (c) 2016, Masatake YAMATO
4 *   Copyright (c) 2016, Red Hat, K.K.
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 */
10 
11 #include "general.h"	/* must always come first */
12 #include "debug.h"
13 #include "entry.h"
14 #include "kind.h"
15 #include "yaml.h"
16 #include "parse.h"
17 #include "subparser.h"
18 
19 #include <stdio.h>
20 
21 typedef enum {
22 	K_PLAY
23 } ansiblePlaybookKind;
24 
25 static kindDefinition AnsiblePlaybookKinds [] = {
26 	{ true,  'p', "play", "plays" },
27 };
28 
29 struct yamlBlockTypeStack {
30 	yaml_token_type_t type;
31 	int associatedCorkIndex;
32 	struct yamlBlockTypeStack *next;
33 };
34 
35 /* - name: "THE NAME" */
36 enum ansiblePlaybookPlayDetectingState {
37 	DSTAT_PLAY_NAME_INITIAL,
38 	DSTAT_PLAY_NAME_KEY,
39 	DSTAT_PLAY_NAME_KEY_SCALAR,
40 	DSTAT_PLAY_NAME_VALUE,
41 };
42 
43 
44 struct sAnsiblePlaybookSubparser {
45 	yamlSubparser yaml;
46 	struct yamlBlockTypeStack *type_stack;
47 	enum ansiblePlaybookPlayDetectingState play_detection_state;
48 };
49 
pushBlockType(struct sAnsiblePlaybookSubparser * ansible,yaml_token_type_t t)50 static void pushBlockType (struct sAnsiblePlaybookSubparser *ansible, yaml_token_type_t t)
51 {
52 	struct yamlBlockTypeStack *s;
53 
54 	s = xMalloc (1, struct yamlBlockTypeStack);
55 
56 	s->next = ansible->type_stack;
57 	ansible->type_stack = s;
58 
59 	s->type = t;
60 	s->associatedCorkIndex = CORK_NIL;
61 }
62 
popBlockType(struct sAnsiblePlaybookSubparser * ansible,yaml_token_t * token)63 static void popBlockType (struct sAnsiblePlaybookSubparser *ansible,
64 						  yaml_token_t *token)
65 {
66 	struct yamlBlockTypeStack *s;
67 
68 	s = ansible->type_stack;
69 	ansible->type_stack = s->next;
70 
71 	s->next = NULL;
72 	tagEntryInfo *tag = getEntryInCorkQueue (s->associatedCorkIndex);
73 	if (tag && token)
74 		attachYamlPosition (tag, token, true);
75 
76 	eFree (s);
77 }
78 
popAllBlockType(struct sAnsiblePlaybookSubparser * ansible,yaml_token_t * token)79 static void popAllBlockType (struct sAnsiblePlaybookSubparser *ansible,
80 							 yaml_token_t *token)
81 {
82 	while (ansible->type_stack)
83 		popBlockType (ansible, token);
84 }
85 
stateStackMatch(struct yamlBlockTypeStack * stack,yaml_token_type_t * expectation,unsigned int length,bool partial)86 static bool stateStackMatch (struct yamlBlockTypeStack *stack,
87 							 yaml_token_type_t *expectation,
88 							 unsigned int length,
89 							 bool partial)
90 {
91 	if (length == 0)
92 	{
93 		if (stack == NULL)
94 			return true;
95 		else if (partial)
96 			return true;
97 		else
98 			return false;
99 	}
100 
101 	if (stack == NULL)
102 		return false;
103 
104 	if (stack->type == expectation[0])
105 		return stateStackMatch (stack->next, expectation + 1, length - 1, partial);
106 	else
107 		return false;
108 }
109 
scalarNeq(yaml_token_t * token,unsigned int len,const char * val)110 static bool scalarNeq (yaml_token_t *token, unsigned int len, const char* val)
111 {
112 	if ((token->data.scalar.length == len)
113 		&& (strncmp (val, (char *)token->data.scalar.value, len) == 0))
114 		return true;
115 	else
116 		return false;
117 }
118 
ansiblePlaybookPlayStateMachine(struct sAnsiblePlaybookSubparser * ansible,yaml_token_t * token)119 static void	ansiblePlaybookPlayStateMachine (struct sAnsiblePlaybookSubparser *ansible,
120 											 yaml_token_t *token)
121 {
122 	yaml_token_type_t play_expect[] = {
123 		YAML_BLOCK_MAPPING_START_TOKEN,
124 		YAML_BLOCK_SEQUENCE_START_TOKEN,
125 	};
126 
127 	switch (token->type)
128 	{
129 	case YAML_KEY_TOKEN:
130 		if (stateStackMatch (ansible->type_stack,
131 							 play_expect, ARRAY_SIZE (play_expect),
132 							 false))
133 			ansible->play_detection_state = DSTAT_PLAY_NAME_KEY;
134 		else
135 			ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
136 		break;
137 	case YAML_SCALAR_TOKEN:
138 		if ((ansible->play_detection_state == DSTAT_PLAY_NAME_KEY)
139 			&& scalarNeq (token, 4, "name"))
140 			ansible->play_detection_state = DSTAT_PLAY_NAME_KEY_SCALAR;
141 		else if (ansible->play_detection_state == DSTAT_PLAY_NAME_VALUE)
142 		{
143 			tagEntryInfo tag;
144 			initTagEntry (&tag, (char *)token->data.scalar.value,
145 						  K_PLAY);
146 			attachYamlPosition (&tag, token, false);
147 
148 			Assert (ansible->type_stack->associatedCorkIndex == CORK_NIL);
149 			ansible->type_stack->associatedCorkIndex = makeTagEntry (&tag);
150 			ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
151 		}
152 		else
153 			ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
154 		break;
155 	case YAML_VALUE_TOKEN:
156 		if (ansible->play_detection_state == DSTAT_PLAY_NAME_KEY_SCALAR)
157 			ansible->play_detection_state = DSTAT_PLAY_NAME_VALUE;
158 		else
159 			ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
160 		break;
161 	default:
162 		ansible->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
163 		break;
164 	}
165 }
166 
newTokenCallback(yamlSubparser * s,yaml_token_t * token)167 static void newTokenCallback (yamlSubparser *s, yaml_token_t *token)
168 {
169 	if (token->type == YAML_BLOCK_SEQUENCE_START_TOKEN
170 		|| token->type == YAML_BLOCK_MAPPING_START_TOKEN)
171 		pushBlockType ((struct sAnsiblePlaybookSubparser *)s, token->type);
172 
173 	ansiblePlaybookPlayStateMachine ((struct sAnsiblePlaybookSubparser *)s, token);
174 
175 	if (token->type == YAML_BLOCK_END_TOKEN)
176 		popBlockType ((struct sAnsiblePlaybookSubparser *)s, token);
177 	else if (token->type == YAML_STREAM_END_TOKEN)
178 		popAllBlockType ((struct sAnsiblePlaybookSubparser *)s, token);
179 }
180 
inputStart(subparser * s)181 static void inputStart(subparser *s)
182 {
183 	((struct sAnsiblePlaybookSubparser*)s)->play_detection_state = DSTAT_PLAY_NAME_INITIAL;
184 	((struct sAnsiblePlaybookSubparser*)s)->type_stack = NULL;
185 }
186 
inputEnd(subparser * s)187 static void inputEnd(subparser *s)
188 {
189 	popAllBlockType ((struct sAnsiblePlaybookSubparser *)s, NULL);
190 	Assert (((struct sAnsiblePlaybookSubparser*)s)->type_stack == NULL);
191 }
192 
193 static void
findAnsiblePlaybookTags(void)194 findAnsiblePlaybookTags (void)
195 {
196 	scheduleRunningBaseparser (0);
197 }
198 
AnsiblePlaybookParser(void)199 extern parserDefinition* AnsiblePlaybookParser (void)
200 {
201 	static struct sAnsiblePlaybookSubparser ansiblePlaybookSubparser = {
202 		.yaml = {
203 			.subparser = {
204 				.direction = SUBPARSER_BI_DIRECTION,
205 				.inputStart = inputStart,
206 				.inputEnd = inputEnd,
207 			},
208 			.newTokenNotfify = newTokenCallback
209 		},
210 	};
211 	static parserDependency dependencies [] = {
212 		{ DEPTYPE_SUBPARSER, "Yaml", &ansiblePlaybookSubparser },
213 	};
214 
215 	parserDefinition* const def = parserNew ("AnsiblePlaybook");
216 
217 
218 	def->dependencies = dependencies;
219 	def->dependencyCount = ARRAY_SIZE (dependencies);
220 
221 	def->kindTable         = AnsiblePlaybookKinds;
222 	def->kindCount     = ARRAY_SIZE (AnsiblePlaybookKinds);
223 	def->parser        = findAnsiblePlaybookTags;
224 	def->useCork       = CORK_QUEUE;
225 	return def;
226 }
227 
228 /* vi:set tabstop=4 shiftwidth=4: */
229