xref: /Universal-ctags/parsers/gemspec.c (revision 197e8d50b7068db538f51c52f1cd77b6694e0874)
1*197e8d50SMasatake YAMATO /*
2*197e8d50SMasatake YAMATO * A parser for RUBYGEM's specification
3*197e8d50SMasatake YAMATO *
4*197e8d50SMasatake YAMATO * Copyright (c) 2021, Red Hat, Inc.
5*197e8d50SMasatake YAMATO * Copyright (c) 2021, Masatake YAMATO
6*197e8d50SMasatake YAMATO *
7*197e8d50SMasatake YAMATO * Author: Masatake YAMATO <yamato@redhat.com>
8*197e8d50SMasatake YAMATO *
9*197e8d50SMasatake YAMATO * This program is free software; you can redistribute it and/or
10*197e8d50SMasatake YAMATO * modify it under the terms of the GNU General Public License
11*197e8d50SMasatake YAMATO * as published by the Free Software Foundation; either version 2
12*197e8d50SMasatake YAMATO * of the License, or (at your option) any later version.
13*197e8d50SMasatake YAMATO *
14*197e8d50SMasatake YAMATO * This program is distributed in the hope that it will be useful,
15*197e8d50SMasatake YAMATO * but WITHOUT ANY WARRANTY; without even the implied warranty of
16*197e8d50SMasatake YAMATO * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17*197e8d50SMasatake YAMATO * GNU General Public License for more details.
18*197e8d50SMasatake YAMATO *
19*197e8d50SMasatake YAMATO * You should have received a copy of the GNU General Public License
20*197e8d50SMasatake YAMATO * along with this program; if not, write to the Free Software
21*197e8d50SMasatake YAMATO * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22*197e8d50SMasatake YAMATO * USA.
23*197e8d50SMasatake YAMATO *
24*197e8d50SMasatake YAMATO * Reference:
25*197e8d50SMasatake YAMATO * - https://guides.rubygems.org/specification-reference/
26*197e8d50SMasatake YAMATO *
27*197e8d50SMasatake YAMATO * See also:
28*197e8d50SMasatake YAMATO * - https://yehudakatz.com/2010/12/16/clarifying-the-roles-of-the-gemspec-and-gemfile/
29*197e8d50SMasatake YAMATO */
30*197e8d50SMasatake YAMATO 
31*197e8d50SMasatake YAMATO /*
32*197e8d50SMasatake YAMATO *   INCLUDE FILES
33*197e8d50SMasatake YAMATO */
34*197e8d50SMasatake YAMATO #include "general.h"  /* must always come first */
35*197e8d50SMasatake YAMATO 
36*197e8d50SMasatake YAMATO #include "entry.h"
37*197e8d50SMasatake YAMATO #include "parse.h"
38*197e8d50SMasatake YAMATO #include "subparser.h"
39*197e8d50SMasatake YAMATO 
40*197e8d50SMasatake YAMATO #include "ruby.h"
41*197e8d50SMasatake YAMATO 
42*197e8d50SMasatake YAMATO #include <string.h>
43*197e8d50SMasatake YAMATO 
44*197e8d50SMasatake YAMATO /*
45*197e8d50SMasatake YAMATO * DATA STRUCTURES
46*197e8d50SMasatake YAMATO */
47*197e8d50SMasatake YAMATO struct sGemSpecSubparser {
48*197e8d50SMasatake YAMATO 	rubySubparser ruby;
49*197e8d50SMasatake YAMATO 	vString *var_name;
50*197e8d50SMasatake YAMATO };
51*197e8d50SMasatake YAMATO 
52*197e8d50SMasatake YAMATO typedef enum {
53*197e8d50SMasatake YAMATO 	K_GEM,
54*197e8d50SMasatake YAMATO } gemspecKind;
55*197e8d50SMasatake YAMATO 
56*197e8d50SMasatake YAMATO typedef enum {
57*197e8d50SMasatake YAMATO 	R_GEM_RUNTIME_DEP,
58*197e8d50SMasatake YAMATO 	R_GEM_DEVEL_DEP,
59*197e8d50SMasatake YAMATO } gemspecGemRole;
60*197e8d50SMasatake YAMATO 
61*197e8d50SMasatake YAMATO static roleDefinition GemSpecGemRoles [] = {
62*197e8d50SMasatake YAMATO 	{ true, "runtimeDep", "specifying runtime dependency" },
63*197e8d50SMasatake YAMATO 	{ true, "develDep",   "specifying development dependency" },
64*197e8d50SMasatake YAMATO };
65*197e8d50SMasatake YAMATO 
66*197e8d50SMasatake YAMATO static kindDefinition GemSpecKinds [] = {
67*197e8d50SMasatake YAMATO 	{ true, 'g', "gem", "gems",
68*197e8d50SMasatake YAMATO 	  .referenceOnly = false, ATTACH_ROLES(GemSpecGemRoles)},
69*197e8d50SMasatake YAMATO };
70*197e8d50SMasatake YAMATO 
71*197e8d50SMasatake YAMATO /*
72*197e8d50SMasatake YAMATO * FUNCTIONS
73*197e8d50SMasatake YAMATO */
findGemSpecTags(void)74*197e8d50SMasatake YAMATO static void findGemSpecTags (void)
75*197e8d50SMasatake YAMATO {
76*197e8d50SMasatake YAMATO 	scheduleRunningBaseparser (0);
77*197e8d50SMasatake YAMATO }
78*197e8d50SMasatake YAMATO 
lineNotify(rubySubparser * s,const unsigned char ** cp)79*197e8d50SMasatake YAMATO static int lineNotify (rubySubparser *s, const unsigned char **cp)
80*197e8d50SMasatake YAMATO {
81*197e8d50SMasatake YAMATO 	struct sGemSpecSubparser *gemspec = (struct sGemSpecSubparser *)s;
82*197e8d50SMasatake YAMATO 	const unsigned char *p = *cp;
83*197e8d50SMasatake YAMATO 
84*197e8d50SMasatake YAMATO 	if (vStringLength (gemspec->var_name) > 0
85*197e8d50SMasatake YAMATO 		&& (strncmp ((char *)p, vStringValue (gemspec->var_name),
86*197e8d50SMasatake YAMATO 					 vStringLength (gemspec->var_name)) == 0))
87*197e8d50SMasatake YAMATO 	{
88*197e8d50SMasatake YAMATO 		int kind = K_GEM;
89*197e8d50SMasatake YAMATO 		int role = ROLE_DEFINITION_INDEX;
90*197e8d50SMasatake YAMATO 		bool is_attr = true;
91*197e8d50SMasatake YAMATO 
92*197e8d50SMasatake YAMATO 		p += vStringLength (gemspec->var_name);
93*197e8d50SMasatake YAMATO 
94*197e8d50SMasatake YAMATO 		if (strncmp ((const char *)p, "name", 4) == 0)
95*197e8d50SMasatake YAMATO 			p += 4;
96*197e8d50SMasatake YAMATO 		else if (strncmp ((const char *)p, "add_runtime_dependency", 22) == 0)
97*197e8d50SMasatake YAMATO 		{
98*197e8d50SMasatake YAMATO 			p += 22;
99*197e8d50SMasatake YAMATO 			role = R_GEM_RUNTIME_DEP;
100*197e8d50SMasatake YAMATO 			is_attr = false;
101*197e8d50SMasatake YAMATO 		}
102*197e8d50SMasatake YAMATO 		else if (strncmp ((const char *)p, "add_development_dependency", 26) == 0)
103*197e8d50SMasatake YAMATO 		{
104*197e8d50SMasatake YAMATO 			p += 26;
105*197e8d50SMasatake YAMATO 			role = R_GEM_DEVEL_DEP;
106*197e8d50SMasatake YAMATO 			is_attr = false;
107*197e8d50SMasatake YAMATO 		}
108*197e8d50SMasatake YAMATO 		else
109*197e8d50SMasatake YAMATO 			p = NULL;
110*197e8d50SMasatake YAMATO 
111*197e8d50SMasatake YAMATO 		if (p)
112*197e8d50SMasatake YAMATO 		{
113*197e8d50SMasatake YAMATO 			rubySkipWhitespace (&p);
114*197e8d50SMasatake YAMATO 			if ((!is_attr) || *p == '=')
115*197e8d50SMasatake YAMATO 			{
116*197e8d50SMasatake YAMATO 				if (is_attr)
117*197e8d50SMasatake YAMATO 				{
118*197e8d50SMasatake YAMATO 					p++;
119*197e8d50SMasatake YAMATO 					rubySkipWhitespace (&p);
120*197e8d50SMasatake YAMATO 				}
121*197e8d50SMasatake YAMATO 				if (*p == '"' || *p == '\'')
122*197e8d50SMasatake YAMATO 				{
123*197e8d50SMasatake YAMATO 					unsigned char b = *p;
124*197e8d50SMasatake YAMATO 					vString *gem = vStringNew ();
125*197e8d50SMasatake YAMATO 					p++;
126*197e8d50SMasatake YAMATO 					if (rubyParseString (&p, b, gem))
127*197e8d50SMasatake YAMATO 					{
128*197e8d50SMasatake YAMATO 						if (role == ROLE_DEFINITION_INDEX)
129*197e8d50SMasatake YAMATO 							makeSimpleTag (gem, kind);
130*197e8d50SMasatake YAMATO 						else
131*197e8d50SMasatake YAMATO 							makeSimpleRefTag (gem, kind, role);
132*197e8d50SMasatake YAMATO 					}
133*197e8d50SMasatake YAMATO 					vStringDelete (gem);
134*197e8d50SMasatake YAMATO 				}
135*197e8d50SMasatake YAMATO 			}
136*197e8d50SMasatake YAMATO 		}
137*197e8d50SMasatake YAMATO 	}
138*197e8d50SMasatake YAMATO 	else if (rubyCanMatchKeywordWithAssign (&p, "Gem::Specification.new"))
139*197e8d50SMasatake YAMATO 	{
140*197e8d50SMasatake YAMATO 		vString *vstr = vStringNew ();
141*197e8d50SMasatake YAMATO 		bool curly_bracket = false;
142*197e8d50SMasatake YAMATO 
143*197e8d50SMasatake YAMATO 		rubySkipWhitespace (&p);
144*197e8d50SMasatake YAMATO 
145*197e8d50SMasatake YAMATO 		curly_bracket = (*p == '{');
146*197e8d50SMasatake YAMATO 		if (curly_bracket
147*197e8d50SMasatake YAMATO 			|| (rubyParseMethodName (&p, vstr)
148*197e8d50SMasatake YAMATO 				&& vStringLength (vstr) == 2
149*197e8d50SMasatake YAMATO 				&& strcmp (vStringValue(vstr), "do") == 0))
150*197e8d50SMasatake YAMATO 		{
151*197e8d50SMasatake YAMATO 			if (curly_bracket)
152*197e8d50SMasatake YAMATO 				p++;
153*197e8d50SMasatake YAMATO 
154*197e8d50SMasatake YAMATO 			rubySkipWhitespace (&p);
155*197e8d50SMasatake YAMATO 			if (*p == '|')
156*197e8d50SMasatake YAMATO 			{
157*197e8d50SMasatake YAMATO 				p++;
158*197e8d50SMasatake YAMATO 				rubySkipWhitespace (&p);
159*197e8d50SMasatake YAMATO 				vStringClear (vstr);
160*197e8d50SMasatake YAMATO 				if (rubyParseMethodName (&p, vstr))
161*197e8d50SMasatake YAMATO 				{
162*197e8d50SMasatake YAMATO 					vStringPut (vstr, '.');
163*197e8d50SMasatake YAMATO 					vStringCopy(gemspec->var_name, vstr);
164*197e8d50SMasatake YAMATO 				}
165*197e8d50SMasatake YAMATO 			}
166*197e8d50SMasatake YAMATO 		}
167*197e8d50SMasatake YAMATO 		vStringDelete (vstr);
168*197e8d50SMasatake YAMATO 	}
169*197e8d50SMasatake YAMATO 	return CORK_NIL;
170*197e8d50SMasatake YAMATO }
171*197e8d50SMasatake YAMATO 
inputStart(subparser * s)172*197e8d50SMasatake YAMATO static void inputStart (subparser *s)
173*197e8d50SMasatake YAMATO {
174*197e8d50SMasatake YAMATO 	struct sGemSpecSubparser *gemspec = (struct sGemSpecSubparser *)s;
175*197e8d50SMasatake YAMATO 
176*197e8d50SMasatake YAMATO 	gemspec->var_name = vStringNew ();
177*197e8d50SMasatake YAMATO }
178*197e8d50SMasatake YAMATO 
inputEnd(subparser * s)179*197e8d50SMasatake YAMATO static void inputEnd (subparser *s)
180*197e8d50SMasatake YAMATO {
181*197e8d50SMasatake YAMATO 	struct sGemSpecSubparser *gemspec = (struct sGemSpecSubparser *)s;
182*197e8d50SMasatake YAMATO 
183*197e8d50SMasatake YAMATO 	vStringDelete (gemspec->var_name); /* NULL is acceptable. */
184*197e8d50SMasatake YAMATO 	gemspec->var_name = NULL;
185*197e8d50SMasatake YAMATO }
186*197e8d50SMasatake YAMATO 
GemSpecParser(void)187*197e8d50SMasatake YAMATO extern parserDefinition* GemSpecParser (void)
188*197e8d50SMasatake YAMATO {
189*197e8d50SMasatake YAMATO 	static const char *const extensions [] = { "gemspec", NULL };
190*197e8d50SMasatake YAMATO 	static struct sGemSpecSubparser gemspecSubparser = {
191*197e8d50SMasatake YAMATO 		.ruby = {
192*197e8d50SMasatake YAMATO 			.subparser = {
193*197e8d50SMasatake YAMATO 				.direction = SUBPARSER_SUB_RUNS_BASE,
194*197e8d50SMasatake YAMATO 				.inputStart = inputStart,
195*197e8d50SMasatake YAMATO 				.inputEnd = inputEnd,
196*197e8d50SMasatake YAMATO 			},
197*197e8d50SMasatake YAMATO 			.lineNotify = lineNotify,
198*197e8d50SMasatake YAMATO 		},
199*197e8d50SMasatake YAMATO 		.var_name = NULL,
200*197e8d50SMasatake YAMATO 	};
201*197e8d50SMasatake YAMATO 
202*197e8d50SMasatake YAMATO 	static parserDependency dependencies [] = {
203*197e8d50SMasatake YAMATO 		[0] = { DEPTYPE_SUBPARSER, "Ruby", &gemspecSubparser },
204*197e8d50SMasatake YAMATO 	};
205*197e8d50SMasatake YAMATO 
206*197e8d50SMasatake YAMATO 	parserDefinition* const def = parserNew ("GemSpec");
207*197e8d50SMasatake YAMATO 
208*197e8d50SMasatake YAMATO 	def->dependencies = dependencies;
209*197e8d50SMasatake YAMATO 	def->dependencyCount = ARRAY_SIZE(dependencies);
210*197e8d50SMasatake YAMATO 	def->kindTable  = GemSpecKinds;
211*197e8d50SMasatake YAMATO 	def->kindCount  = ARRAY_SIZE (GemSpecKinds);
212*197e8d50SMasatake YAMATO 	def->extensions = extensions;
213*197e8d50SMasatake YAMATO 	def->parser     = findGemSpecTags;
214*197e8d50SMasatake YAMATO 	def->useCork    = CORK_QUEUE;
215*197e8d50SMasatake YAMATO 
216*197e8d50SMasatake YAMATO 	return def;
217*197e8d50SMasatake YAMATO }
218