xref: /Universal-ctags/parsers/robot.c (revision e852ee0e939802331dc3117fd85a31b243c3d04f)
1 /*
2  *   Copyright (c) 2017, Daniel Riechers
3  *
4  *   This source code is released for free distribution under the terms of the
5  *   GNU General Public License version 2 or (at your option) any later version.
6  *
7  *   Parser from Robot Framework http://robotframework.org
8  */
9 
10 #include "general.h"
11 
12 #include <string.h>
13 
14 #include "entry.h"
15 #include "parse.h"
16 #include "vstring.h"
17 #include "routines.h"
18 
19 typedef enum {
20         K_TESTCASE,
21         K_KEYWORD,
22         K_VARIABLE,
23         COUNT_KIND
24 } RobotKind;
25 
26 static RobotKind section = -1;
27 
28 static kindDefinition RobotKinds[COUNT_KIND] = {
29 	{true, 't', "testcase",   "testcases"},
30 	{true, 'k', "keyword",    "keywords"},
31 	{true, 'v', "variable",   "variables"},
32 };
33 
34 typedef enum {
35 	X_WHITESPACE_SWAPPED,
36 } robotXtag;
37 
38 static xtagDefinition RobotXtags [] = {
39 	{
40 		.enabled = true,
41 		.name = "whitespaceSwapped",
42 		.description = "Include tags swapping whitespace and underscore chars",
43 	},
44 };
45 
46 
findRobotTags(void)47 static void findRobotTags (void)
48 {
49 	findRegexTags ();
50 }
51 
whitespaceSwap(vString * const s)52 static bool whitespaceSwap (vString *const s)
53 {
54         char replaceWith = '_';
55         char toReplace = ' ';
56 		char changed = false;
57 
58         if(strchr(vStringValue (s), '_'))
59         {
60             replaceWith = ' ';
61             toReplace = '_';
62         }
63 
64         for(unsigned int i=0; i < vStringLength(s); i++)
65             if(vStringChar(s, i) == toReplace)
66 			{
67                 vStringChar (s, i) = replaceWith;
68 				changed = true;
69 			}
70 
71 		return changed;
72 }
73 
changeSection(const char * const line,const regexMatch * const matches,const unsigned int count CTAGS_ATTR_UNUSED,void * data CTAGS_ATTR_UNUSED)74 static bool changeSection (const char *const line, const regexMatch *const matches,
75                                const unsigned int count CTAGS_ATTR_UNUSED, void *data CTAGS_ATTR_UNUSED)
76 {
77     const char * const matchedSection = line + matches[1].start;
78 
79     if(strncasecmp(matchedSection, "test cases", matches[1].length) == 0)
80     {
81         section = K_TESTCASE;
82     }
83     else if(strncasecmp(matchedSection, "keywords", matches[1].length) == 0)
84     {
85         section = K_KEYWORD;
86     }
87     else if(strncasecmp(matchedSection, "variables", matches[1].length) == 0)
88     {
89         section = K_VARIABLE;
90     }
91 	return true;
92 }
93 
makeSimpleXTag(const vString * const name,const int kind,unsigned int xtagType)94 static void makeSimpleXTag (const vString* const name, const int kind,
95 							unsigned int xtagType)
96 {
97 	tagEntryInfo e;
98 
99 	initTagEntry (&e, vStringValue(name), kind);
100 	markTagExtraBit (&e, xtagType);
101 	makeTagEntry (&e);
102 }
103 
tagKeywordsAndTestCases(const char * const line,const regexMatch * const matches,const unsigned int count,void * data CTAGS_ATTR_UNUSED)104 static bool tagKeywordsAndTestCases (const char *const line, const regexMatch *const matches,
105                                const unsigned int count, void *data CTAGS_ATTR_UNUSED)
106 {
107     if (count > 1 && ( section == K_KEYWORD || section == K_TESTCASE) )
108     {
109         vString *const name = vStringNew ();
110         vStringNCopyS (name, line + matches [1].start, matches [1].length);
111         makeSimpleTag (name, section);
112         if (isXtagEnabled (RobotXtags[X_WHITESPACE_SWAPPED].xtype)
113 			&& whitespaceSwap(name))
114 			makeSimpleXTag (name, section,
115 							RobotXtags[X_WHITESPACE_SWAPPED].xtype);
116         vStringDelete (name);
117 		return true;
118     }
119 	return false;
120 }
121 
tagVariables(const char * const line,const regexMatch * const matches,const unsigned int count,void * data CTAGS_ATTR_UNUSED)122 static bool tagVariables (const char *const line, const regexMatch *const matches,
123                                const unsigned int count, void *data CTAGS_ATTR_UNUSED)
124 {
125     if (count > 1 && section == K_VARIABLE)
126     {
127         vString *const name = vStringNew ();
128         vStringNCopyS (name, line + matches [1].start, matches [1].length);
129         makeSimpleTag (name, K_VARIABLE);
130         if (isXtagEnabled (RobotXtags[X_WHITESPACE_SWAPPED].xtype)
131 			&& whitespaceSwap(name))
132 			makeSimpleXTag (name, K_VARIABLE,
133 							RobotXtags[X_WHITESPACE_SWAPPED].xtype);
134         vStringDelete (name);
135 		return true;
136     }
137 	return false;
138 }
139 
initialize(const langType language)140 static void initialize (const langType language)
141 {
142     addLanguageCallbackRegex (language, "^\\*+ *([^* ].+[^* ]) *\\*+$",
143             "{exclusive}", changeSection, NULL, NULL);
144 
145     addLanguageCallbackRegex (
146 		language,
147 		"(^([A-Za-z0-9]+|\\$\\{[_A-Za-z0-9][' _A-Za-z0-9]*(:([^}]|\\\\)+)*\\})([${}' _]([-_$A-Za-z0-9]+|\\{[_A-Za-z0-9][' _A-Za-z0-9]*(:([^}]|\\\\)+)*\\})+)*)",
148 		"{exclusive}", tagKeywordsAndTestCases, NULL, NULL);
149 
150     addLanguageCallbackRegex (language, "^[$@]\\{([_A-Za-z0-9][' _A-Za-z0-9]+)\\}  [ ]*.+",
151             "{exclusive}", tagVariables, NULL, NULL);
152 }
153 
RobotParser(void)154 extern parserDefinition* RobotParser (void)
155 {
156 	static const char *const extensions[] = { "robot", NULL };
157 	parserDefinition *def = parserNew ("Robot");
158     def->kindTable      = RobotKinds;
159     def->kindCount  = COUNT_KIND;
160 	def->extensions = extensions;
161 	def->initialize = initialize;
162     def->parser     = findRobotTags;
163 	def->xtagTable = RobotXtags;
164 	def->xtagCount = ARRAY_SIZE (RobotXtags);
165 	return def;
166 }
167