root/trunk/idldoc/src/parser/docparidldocformatparser__define.pro @ 462

Revision 462, 21.8 KB (checked in by mgalloy, 5 years ago)

Fixed typo "held properties" not "help properties".

Line 
1; docformat = 'rst'
2
3;+
4; Handles parsing of IDLdoc format comment blocks.
5;-
6
7
8;+
9; Removes leading blank lines from string arrays.
10;
11; :Params:
12;    lines : in, out, required, type=strarr
13;       line from which to remove leading blank lines
14;-
15pro docparidldocformatparser::_removeSpace, lines
16  compile_opt strictarr
17
18  ; line is all space
19  re = '^[[:space:]]*$'
20 
21  ; stop at first line that is not all space
22  i = 0
23  while (i lt n_elements(lines) && stregex(lines[i], re, /boolean) eq 1) do i++
24 
25  ; return empty string if no lines left
26  lines = i lt n_elements(lines) ? lines[i:*] : ''
27end
28
29
30;+
31; Parse the lines from a tag.
32;
33; :Returns: strarr
34;
35; :Params:
36;    lines : in, out, required, type=strarr
37;
38; :Keywords:
39;    has_argument : in, optional, type=boolean
40;       set to indicate that this tag has an argument
41;    tag : out, optional, type=string
42;       set to a named variable to return the name of the tag
43;    argument : out, optional, type=string
44;       set to a named variable to return the argument
45;    n_attributes : out, optional, type=long
46;       set to a named variable to return the number of attributes in curly
47;       braces
48;    attribute_names : out, optional, type=strarr
49;       set to a named variable to return an array of attribute names
50;    attribute_values : out, optional, type=strarr
51;       set to a named variable to return an array of attribute values (value
52;       will be '' if the attribute has no value)
53;-
54function docparidldocformatparser::_parseTag, lines, $
55                                              has_argument=hasArgument, $
56                                              tag=tag, argument=argument, $
57                                              n_attributes=nAttributes, $
58                                              attribute_names=attributeNames, $
59                                              attribute_values=attributeValues
60  compile_opt strictarr
61 
62  myLines = lines
63 
64  ; find tag
65  re = '^[[:space:]]*@([[:alpha:]_]+)'
66  tagStart = stregex(myLines[0], re, length=tagLength, /subexpr)
67  if (tagStart[0] lt 0) then begin
68    self.system->warning, 'invalid syntax: ' + myLines[0]
69    return, ''
70  endif
71  tag = strmid(myLines[0], tagStart[1], tagLength[1])
72  myLines[0] = strmid(myLines[0], tagStart[1] + tagLength[1])
73 
74  if (~keyword_set(hasArgument)) then return, myLines
75 
76  ; find argument
77 
78  self->_removeSpace, myLines
79 
80  re = '^[[:space:]]*([[:alnum:]_$]+)'
81  argStart = stregex(myLines[0], re, length=argLength, /subexpr)
82  ; if argStart[0] eq -1 then ERROR
83  argument = strmid(myLines[0], argStart[1], argLength[1])
84  myLines[0] = strmid(myLines[0], argStart[1] + argLength[1])
85 
86  ; find attributes
87
88  attributeNamesList = obj_new('MGcoArrayList', type=7, block_size=10)
89  attributeValuesList = obj_new('MGcoArrayList', type=7, block_size=10)
90 
91  re = '^[[:space:]]*{([^}]*)}.*'
92  starts = 0
93  while (starts[0] ge 0) do begin
94    self->_removeSpace, myLines
95    starts = stregex(myLines[0], re, /subexpr, length=lengths)
96    attribute = strmid(myLines[0], starts[1], lengths[1])
97    myLines[0] = strmid(myLines[0], starts[1] + lengths[1] + 1L)
98    if (starts[0] ge 0) then begin
99      equalPos = strpos(attribute, '=')
100      if (equalPos ge 0) then begin
101        attributeNamesList->add, strmid(attribute, 0L, equalPos)
102        attributeValuesList->add, strmid(attribute, equalPos + 1L)
103      endif else begin
104        attributeNamesList->add, attribute
105        attributeValuesList->add, ''
106      endelse
107    endif
108  endwhile
109 
110  ; return attribute information
111  nAttributes = attributeNamesList->count()
112  if (nAttributes gt 0) then begin
113    attributeNames = attributeNamesList->get(/all)
114    attributeValues = attributeValuesList->get(/all)   
115  endif
116 
117  obj_destroy, [attributeNamesList, attributeValuesList]
118 
119  return, myLines
120end
121
122
123;+
124; Handles a tag with attributes (i.e. {} enclosed arguments like in param or
125; keyword).
126;
127; :Params:
128;    lines : in, required, type=strarr
129;       lines of raw text for that tag
130;
131; :Keywords:
132;    routine : in, required, type=object
133;       routine tree object
134;    markup_parser : in, required, type=object
135;       markup parser object
136;-
137pro docparidldocformatparser::_handleArgumentTag, lines, $
138                                                  routine=routine, $
139                                                  markup_parser=markupParser
140  compile_opt strictarr
141 
142  lines = self->_parseTag(lines, /has_argument, $
143                          tag=tag, argument=argument, $
144                          n_attributes=nAttributes, $
145                          attribute_names=attributeNames, $
146                          attribute_values=attributeValues)
147 
148  case strlowcase(tag) of
149    'param': arg = routine->getParameter(argument, found=found)
150    'keyword': arg = routine->getKeyword(argument, found=found)
151    else:   ; shouldn't happen
152  endcase
153 
154  routine->getProperty, name=routineName
155  if (~found) then begin
156    self.system->warning, strlowcase(tag) + ' ' + argument $
157                            + ' not found in ' + routineName
158    return
159  endif
160
161  for i = 0L, nAttributes - 1L do begin
162    case strlowcase(attributeNames[i]) of
163      'in': arg->setProperty, is_input=1
164      'out': arg->setProperty, is_output=1
165      'optional': arg->setProperty, is_optional=1
166      'required': arg->setProperty, is_required=1
167      'private': arg->setProperty, is_private=1
168      'hidden': arg->setProperty, is_hidden=1
169      'obsolete': arg->setProperty, is_obsolete=1
170
171      'type': arg->setProperty, type=attributeValues[i]
172      'default': arg->setProperty, default_value=attributeValues[i]
173      else: begin
174          self.system->warning, $
175            'unknown argument attribute "' + attributeNames[i] $
176              + '" for argument' + argument + ' in ' + routineName           
177        end
178    endcase
179  endfor
180   
181  arg->setProperty, comments=markupParser->parse(lines)
182end
183
184
185;+
186; Handles one tag in a routine's comments.
187;
188; :Params:
189;    tag : in, required, type=string
190;       rst tag, i.e. returns, params, keywords, etc.
191;    lines : in, required, type=strarr
192;       lines of raw text for that tag
193;
194; :Keywords:
195;    routine : in, required, type=object
196;       routine tree object
197;    markup_parser : in, required, type=object
198;       markup parser object
199;-
200pro docparidldocformatparser::_handleRoutineTag, tag, lines, $
201                                                 routine=routine,  $
202                                                 markup_parser=markupParser
203  compile_opt strictarr
204 
205  case strlowcase(tag) of
206    'abstract': routine->setProperty, is_abstract=1B
207    'author': routine->setProperty, author=markupParser->parse(self->_parseTag(lines))
208    'bugs': begin
209        routine->setProperty, bugs=markupParser->parse(self->_parseTag(lines))
210        self.system->createBugEntry, routine
211      end     
212    'categories': begin
213        comments = self->_parseTag(lines)
214        categories = strtrim(strsplit(strjoin(comments), ',', /extract), 2)
215        for i = 0L, n_elements(categories) - 1L do begin
216          if (categories[i] ne '') then begin
217            routine->addCategory, categories[i]
218            self.system->createCategoryEntry, categories[i], routine
219          endif
220        endfor
221      end
222    'copyright': routine->setProperty, copyright=markupParser->parse(self->_parseTag(lines))
223    'customer_id': routine->setProperty, customer_id=markupParser->parse(self->_parseTag(lines))
224    'examples': routine->setProperty, examples=markupParser->parse(self->_parseTag(lines))
225    'field': begin
226        ; fields are only allowed in routine named "classname__define"
227        routine->getProperty, file=file, name=name
228        classname = strmid(name, 0, strlen(name) - 8)       
229        if (strlowcase(strmid(name, 7, /reverse_offset)) ne '__define') then begin
230          self.system->warning, 'field not allowed in non-class definition routine'
231          break
232        endif
233   
234        ; parse
235        comments = self->_parseTag(lines, /has_argument, $
236                                   argument=fieldName, $
237                                   n_attributes=nAttributes, $
238                                   attribute_names=attributeNames, $
239                                   attribute_values=attributeValues)                                       
240
241        ; get the class tree object
242        class = file->getClass(classname)
243       
244        ; add the field
245        field = class->addField(fieldName, /get_only)
246        if (obj_valid(field)) then begin       
247          field->setProperty, name=fieldName, $
248                              comments=markupParser->parse(comments)
249        endif else begin
250          self.system->warning, 'invalid field ' + fieldName
251        endelse
252      end
253    'file_comments': begin
254        routine->getProperty, file=file
255        file->setProperty, comments=markupParser->parse(self->_parseTag(lines))
256      end
257    'hidden': routine->setProperty, is_hidden=1
258    'hidden_file': begin
259        routine->getProperty, file=file
260        file->setProperty, is_hidden=1B
261      end
262    'history': routine->setProperty, history=markupParser->parse(self->_parseTag(lines))
263    'inherits': begin
264        routine->getProperty, name=name
265        msg = '(%"obsolete tag ''%s'' at routine level in %s")'
266        self.system->warning, string(format=msg, tag, name)
267      end   
268    'keyword': self->_handleArgumentTag, lines, routine=routine, markup_parser=markupParser
269    'obsolete': begin
270        routine->setProperty, is_obsolete=1B
271        self.system->createObsoleteEntry, routine
272      end
273    'param': self->_handleArgumentTag, lines, routine=routine, markup_parser=markupParser
274    'post': routine->setProperty, post=markupParser->parse(self->_parseTag(lines))
275    'pre': routine->setProperty, pre=markupParser->parse(self->_parseTag(lines))
276    'private': routine->setProperty, is_private=1B
277    'private_file': begin
278        routine->getProperty, file=file
279        file->setProperty, is_private=1B
280      end
281    'properties': begin
282        routine->getProperty, name=name
283        msg = '(%"properties tag at routine level in %s")'
284        self.system->warning, string(format=msg, tag, name)       
285      end     
286    'requires': begin       
287        requires = self->_parseTag(lines)
288       
289        ; look for an IDL version
290        for i = 0L, n_elements(requires) - 1L do begin
291          version = stregex(lines[i], '[[:digit:].]+', /extract)
292          if (version ne '') then break
293        endfor
294         
295        ; if you have a real version then check in with system
296        if (version ne '') then begin
297          self.system->checkRequiredVersion, version, routine
298        endif
299       
300        routine->setProperty, requires=markupParser->parse(requires)
301      end
302    'restrictions': routine->setProperty, restrictions=markupParser->parse(self->_parseTag(lines))
303    'returns': routine->setProperty, returns=markupParser->parse(self->_parseTag(lines))
304    'todo': begin
305        routine->setProperty, todo=markupParser->parse(self->_parseTag(lines))
306        self.system->createTodoEntry, routine
307      end
308    'uses': routine->setProperty, uses=markupParser->parse(self->_parseTag(lines))
309    'version': routine->setProperty, version=markupParser->parse(self->_parseTag(lines))
310    else: begin
311        routine->getProperty, name=name
312        msg = '(%"unknown tag ''%s'' at routine level in %s")'
313        self.system->warning, string(format=msg, tag, name)
314      end
315  endcase
316end
317
318
319;+
320; Handles one tag in a file's comments.
321;
322; :Params:
323;    tag : in, required, type=string
324;       rst tag, i.e. returns, params, keywords, etc.
325;    lines : in, required, type=strarr
326;       lines of raw text for that tag
327;
328; :Keywords:
329;    file : in, required, type=object
330;       file tree object
331;    markup_parser : in, required, type=object
332;       markup parser object
333;-
334pro docparidldocformatparser::_handleFileTag, tag, lines, $
335                                              file=file, $
336                                              markup_parser=markupParser
337  compile_opt strictarr
338 
339  case strlowcase(tag) of
340    'file_comments': begin
341        file->setProperty, comments=markupParser->parse(self->_parseTag(lines))   
342      end
343    'property': begin
344        file->getProperty, is_class=isClass, class=class
345        if (~isClass) then begin
346          self.system->warning, 'property not allowed non-class definition file'
347        endif
348   
349        comments = self->_parseTag(lines, /has_argument, $
350                                   argument=propertyName, $
351                                   n_attributes=nAttributes, $
352                                   attribute_names=attributeNames, $
353                                   attribute_values=attributeValues)                                       
354
355        property = self->_addToHeldProperties(propertyName)       
356        property->setProperty, comments=markupParser->parse(comments)
357      end
358   
359    'hidden': file->setProperty, is_hidden=1B
360    'private': file->setProperty, is_private=1B
361   
362    'examples': file->setProperty, examples=markupParser->parse(self->_parseTag(lines))
363   
364    'author': file->setProperty, author=markupParser->parse(self->_parseTag(lines))
365    'copyright': file->setProperty, copyright=markupParser->parse(self->_parseTag(lines))
366    'history': file->setProperty, history=markupParser->parse(self->_parseTag(lines))
367    'version': file->setProperty, version=markupParser->parse(self->_parseTag(lines))
368   
369    'abstract': file->setProperty, is_abstract=1B
370    'bugs': begin
371        self.system->createBugEntry, file
372        file->setProperty, bugs=markupParser->parse(self->_parseTag(lines))
373      end
374    'categories': begin
375        comments = self->_parseTag(lines)
376        categories = strtrim(strsplit(strjoin(comments), ',', /extract), 2)
377        for i = 0L, n_elements(categories) - 1L do begin
378          if (categories[i] ne '') then begin
379            file->addCategory, categories[i]
380            self.system->createCategoryEntry, categories[i], file
381          endif
382        endfor
383      end
384    'customer_id': file->setProperty, customer_id=markupParser->parse(self->_parseTag(lines))
385    'obsolete': begin
386        self.system->createObsoleteEntry, file
387        file->setProperty, is_obsolete=1B
388      end
389    'requires': begin
390        requires = self->_parseTag(lines)
391       
392        ; look for an IDL version
393        for i = 0L, n_elements(requires) - 1L do begin
394          version = stregex(lines[i], '[[:digit:].]+', /extract)
395          if (version ne '') then break
396        endfor
397         
398        ; if you have a real version then check in with system
399        if (version ne '') then begin
400          self.system->checkRequiredVersion, version, file
401        endif
402           
403        file->setProperty, requires=markupParser->parse(requires)
404      end
405    'restrictions': file->setProperty, restrictions=markupParser->parse(self->_parseTag(lines))
406    'todo': begin
407        file->setProperty, todo=markupParser->parse(self->_parseTag(lines))
408        self.system->createTodoEntry, file
409      end
410    'uses': file->setProperty, uses=markupParser->parse(self->_parseTag(lines))
411
412    'inherits': begin
413        file->getProperty, basename=basename
414        msg = '(%"obsolete tag ''%s'' at file level in %s")'
415        self.system->warning, string(format=msg, tag, basename)
416      end         
417    'field': begin
418        file->getProperty, basename=basename
419        msg = '(%"routine level tag ''%s'' at file level in %s")'
420        self.system->warning, string(format=msg, tag, basename)   
421      end
422    'post': begin
423        file->getProperty, basename=basename
424        msg = '(%"routine level tag ''%s'' at file level in %s")'
425        self.system->warning, string(format=msg, tag, basename)   
426      end
427    'pre': begin
428        file->getProperty, basename=basename
429        msg = '(%"routine level tag ''%s'' at file level in %s")'
430        self.system->warning, string(format=msg, tag, basename)   
431      end
432    'param': begin
433        file->getProperty, basename=basename
434        msg = '(%"routine level tag ''%s'' at file level in %s")'
435        self.system->warning, string(format=msg, tag, basename)   
436      end
437    'keyword': begin
438        file->getProperty, basename=basename
439        msg = '(%"routine level tag ''%s'' at file level in %s")'
440        self.system->warning, string(format=msg, tag, basename)   
441      end
442    'returns': begin
443        file->getProperty, basename=basename
444        msg = '(%"routine level tag ''%s'' at file level in %s")'
445        self.system->warning, string(format=msg, tag, basename)       
446      end
447   
448    else: begin
449        file->getProperty, basename=basename
450        msg = '(%"unknown tag ''%s'' at file level in %s")'
451        self.system->warning, string(format=msg, tag, basename)
452      end
453  endcase
454end
455
456
457;+
458; Handles parsing of a comment block associated with a routine using IDLdoc
459; syntax.
460;
461; :Params:
462;    lines : in, required, type=strarr
463;       all lines of the comment block
464; :Keywords:
465;    routine : in, required, type=object
466;       routine tree object
467;    markup_parser : in, required, type=object
468;       markup parser object
469;-
470pro docparidldocformatparser::parseRoutineComments, lines, routine=routine, $
471                                                    markup_parser=markupParser
472  compile_opt strictarr
473 
474  ; find @ symbols that are the first non-whitespace character on the line
475  tagLocations = where(stregex(lines, '^[[:space:]]*@') ne -1, nTags)
476 
477  ; parse normal comments
478  tagsStart = nTags gt 0 ? tagLocations[0] : n_elements(lines)
479  if (tagsStart ne 0) then begin
480    comments = markupParser->parse(lines[0:tagsStart - 1L])
481    routine->setProperty, comments=comments
482  endif
483
484  ; go through each tag
485  for t = 0L, nTags - 1L do begin
486    tagStart = tagLocations[t]
487    tag = strmid(strtrim(stregex(lines[tagStart], '^[ ]*@[[:alpha:]_]+', /extract), 1), 1)
488    tagEnd = t eq nTags - 1L $
489               ? n_elements(lines) - 1L $
490               : tagLocations[t + 1L] - 1L
491    self->_handleRoutineTag, tag, lines[tagStart:tagEnd], $
492                             routine=routine, markup_parser=markupParser
493  endfor
494end
495
496
497;+
498; Handles parsing of a comment block associated with a file.
499;
500; :Params:
501;    lines : in, required, type=strarr
502;       all lines of the comment block
503;
504; :Keywords:
505;    file : in, required, type=object
506;       file tree object
507;    markup_parser : in, required, type=object
508;       markup parser object
509;-
510pro docparidldocformatparser::parseFileComments, lines, file=file, $
511                                                 markup_parser=markupParser                         
512  compile_opt strictarr
513 
514  ; find @ symbols that are the first non-whitespace character on the line
515  tagLocations = where(stregex(lines, '^[[:space:]]*@') ne -1L, nTags)
516 
517  ; parse normal comments
518  tagsStart = nTags gt 0 ? tagLocations[0] : n_elements(lines)
519  if (tagsStart ne 0) then begin
520    comments = markupParser->parse(lines[0:tagsStart - 1L])
521    file->setProperty, comments=comments
522  endif
523
524  ; go through each tag
525  for t = 0L, nTags - 1L do begin
526    tagStart = tagLocations[t]
527    tag = strmid(stregex(lines[tagStart], '@[[:alpha:]_]+', /extract), 1)
528    tagEnd = t eq nTags - 1L $
529               ? n_elements(lines) - 1L $
530               : tagLocations[t + 1L] - 1L
531    self->_handleFileTag, tag, lines[tagStart:tagEnd], $
532                          file=file, markup_parser=markupParser
533  endfor
534end
535
536
537;+
538; Handles parsing of a comment block in the overview file using IDLdoc syntax.
539;
540; :Params:
541;    lines : in, required, type=strarr
542;       all lines of the comment block
543;
544; :Keywords:
545;    system : in, required, type=object
546;       system object
547;    markup_parser : in, required, type=object
548;       markup parser object
549;-
550pro docparidldocformatparser::parseOverviewComments, lines, system=system, $
551                                                     markup_parser=markupParser
552  compile_opt strictarr
553
554  ; find @ symbols that are the first non-whitespace character on the line
555  tagLocations = where(stregex(lines, '^[[:space:]]*@') ne -1, nTags)
556 
557  ; parse normal comments
558  tagsStart = nTags gt 0 ? tagLocations[0] : n_elements(lines)
559  if (tagsStart ne 0) then begin
560    comments = markupParser->parse(lines[0:tagsStart - 1L])
561    system->setProperty, overview_comments=comments
562  endif
563
564  system->getProperty, directories=directories
565
566  ; go through each tag
567  for t = 0L, nTags - 1L do begin
568    tagStart = tagLocations[t]
569    tag = strmid(stregex(lines[tagStart], '@[[:alpha:]_]+', /extract), 1)
570    tagEnd = t eq nTags - 1L $
571               ? n_elements(lines) - 1L $
572               : tagLocations[t + 1L] - 1L
573    tagLines = self->_parseTag(lines[tagStart:tagEnd])
574   
575    case strlowcase(tag) of
576      'dir': begin
577          re = '^[[:space:]]*([[:alpha:]._$\-\/]+)[[:space:]]+'
578          argStart = stregex(tagLines[0], re, /subexpr, length=argLength)
579          if (argStart[0] eq -1L) then begin
580            system->getProperty, overview=overview
581            system->warning, 'directory argument not present for dir tag in overview file ' + overview
582            break           
583          endif
584         
585          dirName = strmid(tagLines[0], argStart[1], argLength[1])
586          if (strmid(dirName, 0, /reverse_offset) ne path_sep()) then begin
587            dirName += path_sep()
588          endif
589         
590          tagLines[0] = strmid(tagLines[0], argStart[1] + argLength[1])
591                   
592          for d = 0L, directories->count() - 1L do begin
593            dir = directories->get(position=d)
594            dir->getProperty, location=location
595            if (dirName eq location) then begin
596              tree = markupParser->parse(tagLines)
597              dir->setProperty, overview_comments=tree
598              break
599            endif
600          endfor
601        end
602      else: begin
603          system->getProperty, overview=overview
604          system->warning, 'unknown tag "' + tag + '" in overview file ' + overview
605        end
606    endcase
607  endfor
608end
609
610
611;+
612; Define instance variables.
613;-
614pro docparidldocformatparser__define
615  compile_opt strictarr
616
617  define = { DOCparIDLdocFormatParser, inherits DOCparFormatParser }
618end
Note: See TracBrowser for help on using the browser.