xref: /Universal-ctags/parsers/rspec.c (revision e8daccd09e1c611624d995b73e529eca85ebef7f)
1eff0ecc1SMasatake YAMATO /*
2eff0ecc1SMasatake YAMATO *
3eff0ecc1SMasatake YAMATO * Copyright (c) 2017, Red Hat, Inc.
4eff0ecc1SMasatake YAMATO * Copyright (c) 2017, Masatake YAMATO
5eff0ecc1SMasatake YAMATO *
6eff0ecc1SMasatake YAMATO * Author: Masatake YAMATO <yamato@redhat.com>
7eff0ecc1SMasatake YAMATO *
8eff0ecc1SMasatake YAMATO * This program is free software; you can redistribute it and/or
9eff0ecc1SMasatake YAMATO * modify it under the terms of the GNU General Public License
10eff0ecc1SMasatake YAMATO * as published by the Free Software Foundation; either version 2
11eff0ecc1SMasatake YAMATO * of the License, or (at your option) any later version.
12eff0ecc1SMasatake YAMATO *
13eff0ecc1SMasatake YAMATO * This program is distributed in the hope that it will be useful,
14eff0ecc1SMasatake YAMATO * but WITHOUT ANY WARRANTY; without even the implied warranty of
15eff0ecc1SMasatake YAMATO * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16eff0ecc1SMasatake YAMATO * GNU General Public License for more details.
17eff0ecc1SMasatake YAMATO *
18eff0ecc1SMasatake YAMATO * You should have received a copy of the GNU General Public License
19eff0ecc1SMasatake YAMATO * along with this program; if not, write to the Free Software
20eff0ecc1SMasatake YAMATO * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21eff0ecc1SMasatake YAMATO * USA.
22eff0ecc1SMasatake YAMATO *
23eff0ecc1SMasatake YAMATO * Inspired by the following commit but written from scratch:
24eff0ecc1SMasatake YAMATO * ==========================================================
25eff0ecc1SMasatake YAMATO *
26eff0ecc1SMasatake YAMATO * commit 76dbed4de88875d8c8409dfd65da4f94f901c94a
27eff0ecc1SMasatake YAMATO * Author: Ram Singla <ram.singla@gmail.com>
28eff0ecc1SMasatake YAMATO * Date:   Tue Jan 18 13:24:46 2011 +0800
29eff0ecc1SMasatake YAMATO *
30eff0ecc1SMasatake YAMATO *    RSpec Code added. Courtesy: mortice
31eff0ecc1SMasatake YAMATO *
32eff0ecc1SMasatake YAMATO * ==========================================================
33eff0ecc1SMasatake YAMATO *
34eff0ecc1SMasatake YAMATO * Reference:
35eff0ecc1SMasatake YAMATO * - https://rubydoc.info/gems/rspec-core/frames
36eff0ecc1SMasatake YAMATO */
37eff0ecc1SMasatake YAMATO 
38eff0ecc1SMasatake YAMATO /*
39eff0ecc1SMasatake YAMATO *   INCLUDE FILES
40eff0ecc1SMasatake YAMATO */
41eff0ecc1SMasatake YAMATO #include "general.h"  /* must always come first */
42eff0ecc1SMasatake YAMATO 
43eff0ecc1SMasatake YAMATO #include "entry.h"
44eff0ecc1SMasatake YAMATO #include "kind.h"
45eff0ecc1SMasatake YAMATO #include "numarray.h"
46eff0ecc1SMasatake YAMATO #include "parse.h"
47eff0ecc1SMasatake YAMATO #include "subparser.h"
48eff0ecc1SMasatake YAMATO 
49eff0ecc1SMasatake YAMATO #include "ruby.h"
50eff0ecc1SMasatake YAMATO 
51eff0ecc1SMasatake YAMATO #include <string.h>
52eff0ecc1SMasatake YAMATO 
53eff0ecc1SMasatake YAMATO /*
54eff0ecc1SMasatake YAMATO * DATA STRUCTURES
55eff0ecc1SMasatake YAMATO */
56eff0ecc1SMasatake YAMATO struct sRSpecSubparser {
57eff0ecc1SMasatake YAMATO 	rubySubparser ruby;
58eff0ecc1SMasatake YAMATO 	int scope;
59eff0ecc1SMasatake YAMATO };
60eff0ecc1SMasatake YAMATO 
61eff0ecc1SMasatake YAMATO typedef enum {
62eff0ecc1SMasatake YAMATO 	K_DESCRIBE,
63eff0ecc1SMasatake YAMATO 	K_CONTEXT,
64*e8daccd0SMasatake YAMATO 	K_IT,
65eff0ecc1SMasatake YAMATO } rspecKind;
66eff0ecc1SMasatake YAMATO 
67eff0ecc1SMasatake YAMATO static kindDefinition RSpecKinds [] = {
68eff0ecc1SMasatake YAMATO 	{ true, 'd', "describe", "describes" },
69eff0ecc1SMasatake YAMATO 	{ true, 'c', "context", "contexts" },
70*e8daccd0SMasatake YAMATO 	{ true, 'i', "it", "things described with \"it\"" },
71eff0ecc1SMasatake YAMATO };
72eff0ecc1SMasatake YAMATO 
73eff0ecc1SMasatake YAMATO /*
74eff0ecc1SMasatake YAMATO * FUNCTIONS
75eff0ecc1SMasatake YAMATO */
makeSimpleRSpecTag(vString * vstr,int kindIndex,rubySubparser * subparser)76eff0ecc1SMasatake YAMATO static int makeSimpleRSpecTag (vString *vstr, int kindIndex, rubySubparser *subparser)
77eff0ecc1SMasatake YAMATO {
78eff0ecc1SMasatake YAMATO 	int r = makeSimpleTag (vstr, kindIndex);
79eff0ecc1SMasatake YAMATO 	tagEntryInfo *e = getEntryInCorkQueue (r);
80eff0ecc1SMasatake YAMATO 	if (e)
81eff0ecc1SMasatake YAMATO 	{
82eff0ecc1SMasatake YAMATO 		struct sRSpecSubparser *rspec = (struct sRSpecSubparser *)subparser;
83eff0ecc1SMasatake YAMATO 		e->extensionFields.scopeIndex = rspec->scope;
84eff0ecc1SMasatake YAMATO 	}
85eff0ecc1SMasatake YAMATO 	return r;
86eff0ecc1SMasatake YAMATO }
87eff0ecc1SMasatake YAMATO 
readRest(const unsigned char ** cp)88eff0ecc1SMasatake YAMATO static vString *readRest (const unsigned char **cp)
89eff0ecc1SMasatake YAMATO {
90eff0ecc1SMasatake YAMATO 	vString *vstr = NULL;
91eff0ecc1SMasatake YAMATO 	unsigned char b;
92eff0ecc1SMasatake YAMATO 
93eff0ecc1SMasatake YAMATO 	switch (**cp)
94eff0ecc1SMasatake YAMATO 	{
95eff0ecc1SMasatake YAMATO 	case '\'':
96eff0ecc1SMasatake YAMATO 	case '"':
97eff0ecc1SMasatake YAMATO 		b = **cp;
98eff0ecc1SMasatake YAMATO 		++*cp;
99eff0ecc1SMasatake YAMATO 		vstr = vStringNew ();
100eff0ecc1SMasatake YAMATO 		if (!rubyParseString (cp, b, vstr))
101eff0ecc1SMasatake YAMATO 		{
102eff0ecc1SMasatake YAMATO 			vStringDelete (vstr);
103eff0ecc1SMasatake YAMATO 			vstr = NULL;
104eff0ecc1SMasatake YAMATO 		}
105eff0ecc1SMasatake YAMATO 		break;
106eff0ecc1SMasatake YAMATO 	case ':':
107eff0ecc1SMasatake YAMATO 		/* symbol, Should this be part of the Ruby parser side? */
108eff0ecc1SMasatake YAMATO 		break;
109eff0ecc1SMasatake YAMATO 	default:
110eff0ecc1SMasatake YAMATO 		vstr = vStringNew ();
111eff0ecc1SMasatake YAMATO 		if (!rubyParseModuleName (cp, vstr))
112eff0ecc1SMasatake YAMATO 		{
113eff0ecc1SMasatake YAMATO 			vStringDelete (vstr);
114eff0ecc1SMasatake YAMATO 			vstr = NULL;
115eff0ecc1SMasatake YAMATO 		}
116eff0ecc1SMasatake YAMATO 		else if (strcmp (vStringValue (vstr), "do") == 0)
117eff0ecc1SMasatake YAMATO 		{
118eff0ecc1SMasatake YAMATO 			vStringDelete (vstr);
119eff0ecc1SMasatake YAMATO 			vstr = NULL;
120eff0ecc1SMasatake YAMATO 			*cp += -2;			/* push back "do" */
121eff0ecc1SMasatake YAMATO 		}
122eff0ecc1SMasatake YAMATO 
123eff0ecc1SMasatake YAMATO 		break;
124eff0ecc1SMasatake YAMATO 	}
125eff0ecc1SMasatake YAMATO 
126eff0ecc1SMasatake YAMATO 	if (vstr)
127eff0ecc1SMasatake YAMATO 	{
128eff0ecc1SMasatake YAMATO 		rubySkipWhitespace (cp);
129eff0ecc1SMasatake YAMATO 		if (**cp == ',')
130eff0ecc1SMasatake YAMATO 		{
131eff0ecc1SMasatake YAMATO 			++*cp;
132eff0ecc1SMasatake YAMATO 			rubySkipWhitespace (cp);
133eff0ecc1SMasatake YAMATO 			vString *vstr_rest = readRest (cp);
134eff0ecc1SMasatake YAMATO 			if (vstr_rest)
135eff0ecc1SMasatake YAMATO 			{
136eff0ecc1SMasatake YAMATO 				vStringPut (vstr, ' ');
137eff0ecc1SMasatake YAMATO 				vStringCat (vstr, vstr_rest);
138eff0ecc1SMasatake YAMATO 				vStringDelete (vstr_rest);
139eff0ecc1SMasatake YAMATO 			}
140eff0ecc1SMasatake YAMATO 		}
141eff0ecc1SMasatake YAMATO 	}
142eff0ecc1SMasatake YAMATO 
143eff0ecc1SMasatake YAMATO 	return vstr;
144eff0ecc1SMasatake YAMATO }
145eff0ecc1SMasatake YAMATO 
146a3baf5deSMasatake YAMATO struct caseType {
147a3baf5deSMasatake YAMATO 	const char *keyword;
148a3baf5deSMasatake YAMATO 	rspecKind    kind;
149a3baf5deSMasatake YAMATO };
150a3baf5deSMasatake YAMATO 
lineNotify(rubySubparser * s,const unsigned char ** cp)151eff0ecc1SMasatake YAMATO static int lineNotify (rubySubparser *s, const unsigned char **cp)
152eff0ecc1SMasatake YAMATO {
153a3baf5deSMasatake YAMATO 	struct caseType caseTypes [] = {
154a3baf5deSMasatake YAMATO 		{ "describe", K_DESCRIBE },
155a3baf5deSMasatake YAMATO 		{ "RSpec.describe", K_DESCRIBE },
156a3baf5deSMasatake YAMATO 		{ "context", K_CONTEXT },
157*e8daccd0SMasatake YAMATO 		{ "it", K_IT },
158a3baf5deSMasatake YAMATO 	};
159a3baf5deSMasatake YAMATO 
160a3baf5deSMasatake YAMATO 	for (int i = 0; i < ARRAY_SIZE(caseTypes); i++)
161a3baf5deSMasatake YAMATO 	{
162a3baf5deSMasatake YAMATO 		if (rubyCanMatchKeywordWithAssign(cp, caseTypes[i].keyword))
163eff0ecc1SMasatake YAMATO 		{
164eff0ecc1SMasatake YAMATO 			vString *vstr = NULL;
165eff0ecc1SMasatake YAMATO 			rubySkipWhitespace (cp);
166eff0ecc1SMasatake YAMATO 			vstr = readRest (cp);
167eff0ecc1SMasatake YAMATO 			if (vstr)
168eff0ecc1SMasatake YAMATO 			{
169a3baf5deSMasatake YAMATO 				int r = makeSimpleRSpecTag (vstr, caseTypes[i].kind, s);
170eff0ecc1SMasatake YAMATO 				vStringDelete (vstr);
171eff0ecc1SMasatake YAMATO 				return r;
172eff0ecc1SMasatake YAMATO 			}
173eff0ecc1SMasatake YAMATO 		}
174eff0ecc1SMasatake YAMATO 	}
175a3baf5deSMasatake YAMATO 
176eff0ecc1SMasatake YAMATO 	return CORK_NIL;
177eff0ecc1SMasatake YAMATO }
178eff0ecc1SMasatake YAMATO 
enterBlockNotify(rubySubparser * s,int corkIndex)179eff0ecc1SMasatake YAMATO static void enterBlockNotify (rubySubparser *s, int corkIndex)
180eff0ecc1SMasatake YAMATO {
181eff0ecc1SMasatake YAMATO 	struct sRSpecSubparser *rspec = (struct sRSpecSubparser *)s;
182eff0ecc1SMasatake YAMATO 	rspec->scope = corkIndex;
183eff0ecc1SMasatake YAMATO }
184eff0ecc1SMasatake YAMATO 
leaveBlockNotify(rubySubparser * s,int corkIndex)185eff0ecc1SMasatake YAMATO static void leaveBlockNotify (rubySubparser *s, int corkIndex)
186eff0ecc1SMasatake YAMATO {
187eff0ecc1SMasatake YAMATO 	tagEntryInfo *e = getEntryInCorkQueue (corkIndex);
188eff0ecc1SMasatake YAMATO 	if (e)
189eff0ecc1SMasatake YAMATO 	{
190eff0ecc1SMasatake YAMATO 		struct sRSpecSubparser *rspec = (struct sRSpecSubparser *)s;
191eff0ecc1SMasatake YAMATO 		rspec->scope = e->extensionFields.scopeIndex;
192eff0ecc1SMasatake YAMATO 	}
193eff0ecc1SMasatake YAMATO }
194eff0ecc1SMasatake YAMATO 
findRSpecTags(void)195eff0ecc1SMasatake YAMATO static void findRSpecTags (void)
196eff0ecc1SMasatake YAMATO {
197eff0ecc1SMasatake YAMATO 	scheduleRunningBaseparser (RUN_DEFAULT_SUBPARSERS);
198eff0ecc1SMasatake YAMATO }
199eff0ecc1SMasatake YAMATO 
inputStart(subparser * s)200049011caSMasatake YAMATO static void inputStart (subparser *s)
201049011caSMasatake YAMATO {
202049011caSMasatake YAMATO 	struct sRSpecSubparser *rspec = (struct sRSpecSubparser *)s;
203049011caSMasatake YAMATO 	rspec->scope = CORK_NIL;
204049011caSMasatake YAMATO }
205049011caSMasatake YAMATO 
RSpecParser(void)206eff0ecc1SMasatake YAMATO extern parserDefinition* RSpecParser (void)
207eff0ecc1SMasatake YAMATO {
208eff0ecc1SMasatake YAMATO 	static struct sRSpecSubparser rspecSubparser = {
209eff0ecc1SMasatake YAMATO 		.ruby = {
210eff0ecc1SMasatake YAMATO 			.subparser = {
211eff0ecc1SMasatake YAMATO 				.direction = SUBPARSER_BASE_RUNS_SUB,
212049011caSMasatake YAMATO 				.inputStart = inputStart,
213eff0ecc1SMasatake YAMATO 			},
214eff0ecc1SMasatake YAMATO 			.lineNotify = lineNotify,
215eff0ecc1SMasatake YAMATO 			.enterBlockNotify = enterBlockNotify,
216eff0ecc1SMasatake YAMATO 			.leaveBlockNotify = leaveBlockNotify,
217eff0ecc1SMasatake YAMATO 		},
218eff0ecc1SMasatake YAMATO 		.scope = CORK_NIL,
219eff0ecc1SMasatake YAMATO 	};
220eff0ecc1SMasatake YAMATO 
221eff0ecc1SMasatake YAMATO 	static parserDependency dependencies [] = {
222eff0ecc1SMasatake YAMATO 		[0] = { DEPTYPE_SUBPARSER, "Ruby", &rspecSubparser },
223eff0ecc1SMasatake YAMATO 	};
224eff0ecc1SMasatake YAMATO 
225eff0ecc1SMasatake YAMATO 	parserDefinition* const def = parserNew ("RSpec");
226eff0ecc1SMasatake YAMATO 
227eff0ecc1SMasatake YAMATO 	def->dependencies = dependencies;
228eff0ecc1SMasatake YAMATO 	def->dependencyCount = ARRAY_SIZE(dependencies);
229eff0ecc1SMasatake YAMATO 	def->kindTable  = RSpecKinds;
230eff0ecc1SMasatake YAMATO 	def->kindCount  = ARRAY_SIZE (RSpecKinds);
231eff0ecc1SMasatake YAMATO 	def->parser     = findRSpecTags;
232eff0ecc1SMasatake YAMATO 	def->useCork    = CORK_QUEUE;
233eff0ecc1SMasatake YAMATO 
234eff0ecc1SMasatake YAMATO 	return def;
235eff0ecc1SMasatake YAMATO }
236