1 #include <Rcpp.h>
2 using namespace Rcpp;
3 
4 // From http://developer.r-project.org/parseRd.pdf:  The characters \, %, {,
5 // and } have special meaning in almost all parts of an Rd file. In code,
6 // strings must also match, except in comments.
7 
8 // The two functions are very similar, so we use a common
9 // implementation and select the functionality via the
10 // mode argument:
11 // mode == 0: rdComplete
12 // mode == 1: findEndOfTag
13 
roxygen_parse_tag(std::string string,bool is_code=false,int mode=0)14 int roxygen_parse_tag(std::string string, bool is_code = false,
15 		      int mode = 0) {
16   int n = string.length();
17 
18   char in_string = '\0';
19   bool in_escape = false;
20   bool in_r_comment = false;
21   bool in_latex_comment = false;
22   int braces = 0, r_braces = 0;
23 
24   for(int i = 0; i < n; i++) {
25     char cur = string[i];
26 
27     if (in_escape) {
28       // Swallow escaped characters
29       in_escape = false;
30     } else if (in_string != '\0') {
31       // Look for end of string
32       if (cur == in_string) {
33         in_string = false;
34       } else if (cur == '\\') {
35         in_escape = true;
36       }
37     } else if (in_r_comment) {
38       // Inside R comments, braces must match.
39       // R comments are terminated by newline or } not matched by {
40       if (cur == '\n') {
41         in_r_comment = false;
42         r_braces = 0;
43       } else if (cur == '{') {
44         braces++;
45         r_braces++;
46       } else if (cur == '}') {
47         braces--;
48         r_braces--;
49         if (r_braces == 0)
50           in_r_comment = false;
51       }
52     } else if (in_latex_comment) {
53       if (cur == '\n') {
54         in_latex_comment = false;
55       }
56     } else {
57       switch(cur) {
58       case '{':  braces++; break;
59       case '}':  braces--; break;
60       case '\\': in_escape = true; break;
61       case '#':  if (is_code) in_r_comment = true; break;
62       case '%':  in_latex_comment = true; break;
63       case '\'': if (is_code) in_string = '\''; break;
64       case '"':  if (is_code) in_string = '"'; break;
65       }
66     }
67 
68     if (mode == 1) {
69       bool complete = braces == 0 && !in_escape && !in_string;
70       if (complete && i + 1 < n && string[i + 1] != '{') {
71 	return i;
72       }
73     }
74 
75   }
76 
77   bool complete = braces == 0 && !in_escape && !in_string;
78 
79   if (mode == 0) {
80     if (complete) return 1; else return 0;
81 
82   } else {
83     if (complete) return n - 1; else return -1;
84   }
85 }
86 
87 // [[Rcpp::export]]
findEndOfTag(std::string string,bool is_code=false)88 int findEndOfTag(std::string string, bool is_code = false) {
89   return roxygen_parse_tag(string, is_code, 1);
90 }
91 
92 // [[Rcpp::export]]
rdComplete(std::string string,bool is_code=false)93 bool rdComplete(std::string string, bool is_code = false) {
94   return roxygen_parse_tag(string, is_code, 0) == 1 ? true : false;
95 }
96