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