Fix #6894 - debugging generator
[roojspacker] / roojspacker / DocTag.vala
1
2
3 namespace JSDOC 
4 {
5         public enum DocTagTitle
6         {
7                 NO_VALUE,
8                 PARAM,
9                 PROPERTY,
10                 CFG,
11                 EXAMPLE,
12                 SINGLETON,
13                 AUTHOR,
14                 METHOD,
15                 DESC,
16                 OVERVIEW,
17                 SINCE,
18                 CONSTANT,
19                 VERSION,
20                 DEPRECATED,
21  
22                 SEE,
23                 CLASS,
24                 NAMESPACE,
25                 CONSTRUCTOR,
26                 STATIC,
27  
28                 
29                 INNER,
30                 FIELD,
31                 FUNCTION,
32                 EVENT,
33                 NAME,
34                 RETURN,
35                 THROWS,
36                 REQUIRES,
37                 TYPE,
38                 PRIVATE,
39                 IGNORE,
40                 ARGUMENTS,
41                 EXTENDS,
42                 DEFAULT,
43                 MEMBEROF,
44                 PUBLIC,
45                 SCOPE,
46                 SCOPEALIAS
47   
48         }
49         
50         errordomain DocTagException {
51                 NO_TITLE,
52                 INVALID_TITLE,
53                 INVALID_NAME,
54                 INVALID_TYPE
55         }
56
57
58         public class DocTag : Object 
59         {
60
61                 public DocTagTitle title = DocTagTitle.NO_VALUE;
62                 public string type = "";  // eg.. boolean / string etc..., may be xxxx|bbbb - eg. optional types
63                 public string name = ""; // eg. "title" << a property name etc...
64                 public bool isOptional = false;
65                 public string defaultValue = "";
66                 public string desc = "";
67                 public Gee.ArrayList<string> optvalues;
68                 public string memberOf = ""; // set by add addMember..
69
70                 public string asString()
71                 {
72                         return "DocTag: title=%s name=%s type=%s  desc=%s".printf(
73                                 this.title.to_string(),
74                                 this.name,
75                                 this.type,
76                                 this.desc
77                         );
78                 }
79         
80                 public Json.Object toJson()
81                 {
82                         var ret = new Json.Object();
83                         ret.set_string_member("title", this.title.to_string());
84                         ret.set_string_member("type", this.type);
85                         ret.set_string_member("name", this.name);
86                         ret.set_string_member("defaultValue", this.defaultValue);
87                         ret.set_string_member("desc", this.desc);
88                         ret.set_string_member("memberOf", this.memberOf);
89                         ret.set_boolean_member("isOptional", this.isOptional);
90                         var ar = new Json.Array();
91                         foreach(var ov in this.optvalues) {
92                                 ar.add_string_element(ov);
93                         }
94                         ret.set_array_member("optvalues", ar);
95                         return ret;
96                 }
97         
98         
99                 public DocTag (string in_src)
100                 {
101                     
102                     GLib.debug("Parsing Tag: %s", in_src);
103                     
104                      
105                     
106                     
107                     this.optvalues = new Gee.ArrayList<string>();
108                     
109                     var src = in_src;
110                         
111             try {
112                 src = this.nibbleTitle(src);
113                 
114                 src = this.nibbleType(src);
115                 
116
117                 // only some tags are allowed to have names.
118                 if (
119                         this.title == DocTagTitle.PARAM ||
120                         this.title == DocTagTitle.PROPERTY || 
121                         this.title == DocTagTitle.CFG) { // @config is deprecated
122                     src = this.nibbleName(src);
123                 }
124             }
125             catch(DocTagException e) {
126                 GLib.debug("Failed to parse tag: '%s' = error = %s", in_src, e.message);
127                 // only throw if in 'strict'??
128                 //throw e;
129                 return;
130             }
131             
132             // if type == @cfg, and matches (|....|...)
133             
134             src = src.strip();
135  
136             MatchInfo mi = null;
137             
138             if (this.title ==  DocTagTitle.CFG && /^\([^)]+\)/.match_all(src, 0, out mi )) {
139                     
140                                 var ms = mi.fetch(0);
141                                 GLib.debug("Got Opt list: %s", ms);
142                                 
143                                 ms = ms.substring(1,ms.length-2);
144                                 if (ms.contains("|")) {
145                                         var ar = ms.split("|");
146                                         for (var i =0 ; i < ar.length;i++) {
147                                                 optvalues.add(ar[i].strip());
148                                         }
149                                         src = src.substring(ms.length, src.length - ms.length);                   
150                     
151                 } 
152                 
153             }
154             
155             
156             this.desc = src; // whatever is left
157             
158             // example tags need to have whitespace preserved
159             if (this.title != DocTagTitle.EXAMPLE) {
160                         this.desc = this.desc.strip();
161                 }
162             
163
164                 
165         
166
167                 }
168         
169         
170                 /**
171                     Find and shift off the title of a tag.
172                     @param {string} src
173                     @return src
174                  */
175                 private string nibbleTitle (string src) throws DocTagException
176                 {
177                     //GLib.debug("nibbleTitle: %s", src);
178                     MatchInfo mi;
179                      
180                     if(! /^\s*(\S+)\s*(?:\s([\s\S]*))?$/.match_full(src, src.length, 0, 0, out mi) || 
181                             mi.get_match_count() < 2)  {
182                                 throw new DocTagException.NO_TITLE("missing title");
183                                 return src;
184                     }
185                     
186                     //GLib.debug("nibbleTitle: regexmatches %d : %s",
187                     //           mi.get_match_count(), 
188                     //           mi.fetch(1).up());
189                     
190                     EnumClass enumc = (EnumClass) typeof (DocTagTitle).class_ref ();
191
192                     unowned EnumValue? eval = enumc.get_value_by_name ( "JSDOC_DOC_TAG_TITLE_"+  mi.fetch(1).up());
193                     if (eval == null) {
194                                 throw new DocTagException.INVALID_TITLE("title not supported ??");
195                                 return src;
196                     }
197                     this.title = (DocTagTitle) eval.value;
198                     return mi.get_match_count() > 2 ? mi.fetch(2) : "";
199
200                 }
201                  
202                   /**
203             Find and shift off the type of a tag.
204             @requires frame/String.js
205             @param {string} src
206             @return src
207          */
208         private string nibbleType(string src) 
209         {
210                     MatchInfo mi;
211             if(! /^\s*\{/.match_all(src, 0, out mi)) {
212                    return src;
213             }
214             int start;
215             int stop;
216               
217                         this.balance(src,'{', '}', out start, out stop);
218                         //GLib.debug("nibble type: %s %d, %d", src, start,stop);
219             if (stop == -1) {
220                 throw new DocTagException.INVALID_TYPE("Malformed comment tag ignored. Tag type requires an opening { and a closing }: ") ;
221                 return src;
222             }
223             this.type = src.substring(start+1,stop-1).strip();
224             this.type = this.type.replace(",", "|"); // multiples can be separated by , or |
225             return src.substring(stop+1, -1);
226             
227         }
228          
229          
230          
231         /**
232             Find and shift off the name of a tag.
233             @requires frame/String.js
234             @param {string} src
235             @return src
236          */
237                 private string nibbleName( string in_src) throws DocTagException
238         {
239
240            
241             var src = in_src.strip();
242             //GLib.debug("nibbleName: %s", in_src);
243             
244             // is optional?
245             if (src.get(0) == '[') {
246                         int start, stop;
247                  this.balance(src,'[', ']', out start, out stop);
248                 if (stop == -1) {
249                     throw new  DocTagException.INVALID_NAME("Malformed comment tag ignored. Tag optional name requires an opening [ and a closing ]: ");
250                     return src;
251                 }
252                 this.name = src.substring(start+1, stop).strip();
253                 this.isOptional = true;
254                 
255                 src = src.substring(stop+1);
256                 
257                 // has default value?
258                 var nameAndValue = this.name.split("=");
259                 if (nameAndValue.length > 1) {
260                         var oname = this.name;
261                     this.name = nameAndValue[0].strip();
262
263                     this.defaultValue = oname.substring( nameAndValue[0].length + 1 , nameAndValue[0].length + 1 - oname.length); /// what about
264                 }
265                 GLib.debug("got name %s", this.name);                
266                 return src.substring(stop+1, stop+1-src.length);
267             }
268                         // not encased with [ ]
269
270                     MatchInfo mi;
271
272             if (/^(\S+)(?:\s([\s\S]*))?$/.match_full(src, src.length, 0, 0,  out mi)) {
273                         this.name = mi.fetch(1);
274                         GLib.debug("got name %s", this.name);
275                                 return mi.get_match_count() > 2 ? mi.fetch(2) : "";
276             }
277                 
278
279             return src;
280         }
281          
282          
283         private void balance(string str, char open, char close, out int start, out int stop) {
284             start = 0;
285             stop  =-1;
286             while (str.get(start) != open) {
287                 if (start == str.length) {
288                         return;
289                         }
290                 start++;
291             }
292             
293             stop = start +1;
294             var balance = 1;
295             while (stop < str.length) {
296                 if (str.get(stop) == open) balance++;
297                 if (str.get(stop) == close) balance--;
298                 if (balance == 0) break;
299                 stop++;
300                 if (stop == str.length) {
301                         stop = -1;
302                         return;
303                         }
304             }
305             
306
307                 }
308                 
309                 public Json.Array optvalue_as_json_array()
310                 {
311                         var ret = new Json.Array();
312                         foreach (var str in this.optvalues ) {
313                                 ret.add_string_element(str);
314                         }
315                         return ret;
316                         
317                         
318                 }
319                 
320         }
321 }
322         
323