]> git.mxchange.org Git - flightgear.git/blob - scripts/python/nasal_api_doc.py
For stable versions, download data tarball directly
[flightgear.git] / scripts / python / nasal_api_doc.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # Copyright (C) 2012 Adrian Musceac
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19
20 import os, sys, glob
21 import io
22 import re, string
23
24 """Script which generates an API documentation file for Nasal libraries
25 located inside $FGROOT/Nasal/
26 Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]
27 Or configure the local path below, and ommit the path in the console.
28 The API doc in HTML format is generated in the current working directory"""
29
30 ########### Local $FGROOT/Nasal/ path ##########
31 NASAL_PATH="../fgfs/fgdata/Nasal/"
32
33
34 def get_files(nasal_dir):
35         if nasal_dir[-1]!='/':
36                 nasal_dir+='/'
37         try:
38                 os.stat(nasal_dir)
39         except:
40                 print "The path does not exist"
41                 sys.exit()
42         fgroot_dir = nasal_dir.rstrip('/').replace('Nasal','')
43         f_version = open(fgroot_dir+'version','rb')
44         version = f_version.read(256).rstrip('\n')
45         top_level = []
46         modules = []
47         top_namespaces = []
48         files_list = os.listdir(nasal_dir)
49         for f in files_list:
50                 if f.find(".nas")!=-1:
51                         top_level.append(f)
52                         continue
53                 if os.path.isdir(nasal_dir + f):
54                         modules.append(f)
55         top_level.sort()
56         modules.sort()
57         if len(top_level) ==0:
58                 print "This does not look like the correct $FGROOT/Nasal path"
59                 sys.exit()
60         if len(modules)==0:
61                 print "Warning: could not find any submodules"
62         for f in top_level:
63                 namespace=f.replace(".nas","")
64                 functions=parse_file(nasal_dir + f)
65                 top_namespaces.append([namespace,functions])
66         for m in modules:
67                 files=glob.glob(nasal_dir+m+"/*.nas")
68                 for f in files:
69                         functions=parse_file(f)
70                         top_namespaces.append([m,functions])
71
72         output_text(top_namespaces,modules,version)
73
74
75 def output_text(top_namespaces,modules,version):
76         fw=open('./nasal_api_doc.html','wb')
77         buf='<html><head>\
78         <title>Nasal API</title>\
79         <style>\n\
80         a.main_module_link {margin-left:30px;display:block;float:left;}\
81         div.container {background-color:#eee;clear:left;margin-top:20px;}\
82         h2.namespace_title {padding-left:20px;color:#fff;background-color:#8888AC}\
83         h4.class_function {padding-left:20px;background-color:#eee;color:#000033}\
84         h4.class_definition {padding-left:20px;background-color:#eee;color:#000033}\
85         h4.function {padding-left:20px;background-color:#eee;color:#000033}\
86         hr {margin-left:30px;margin-right:30px;}\
87         div.comments {padding-left:40px;display:inline;font-size:12px;}\
88         </style>\n\
89         </head><body style="width:1024px;">'
90
91         buf+='<h1 style="padding-left:20px;display:block;color:#fff;background-color:#555588;">\
92         Nasal $FGROOT Library<br/><span style="font-size:12px;">Flightgear version: '+version+'\
93         <br/>This file is generated automatically by scripts/python/nasal_api_doc.py\
94         </span></h1>\
95         <br/><a href="http://plausible.org/nasal">Nasal documentation</a>&nbsp;&nbsp;\
96         <a href="http://wiki.flightgear.org/Nasal_scripting_language">Flightgear Nasal documentation</a>\n<div style="float:right;">&nbsp;'
97         buf+='<h2 style="font-size:14px;height:450px;width:250px;overflow:scroll;display:block;position:fixed;top:20px;right:20px;background-color:#8888AC;border:1px solid black;">\n'
98         done=[]
99         for namespace in top_namespaces:
100                 color='0000cc'
101                 if namespace[0] in modules:
102                         color='cc0000'
103                 if namespace[0] not in done:
104                         buf+='<a class="main_module_link" style="color:'+color+'" href="#'+namespace[0]+'">'+namespace[0]+'</a>&nbsp;<br/>\n'
105                         done.append(namespace[0])
106         buf+='</h2></div>\n'
107         done2=[]
108         for namespace in top_namespaces:
109                 if namespace[0] not in done2:
110                         buf+='<div class="container" style="">\n'
111                         buf += '<h2 class="namespace_title"><a name="'+namespace[0]+'">'+namespace[0]+'</a></h2>\n'
112                         done2.append(namespace[0])
113                 for functions in namespace[1]:
114                         class_func=functions[0].split('.')
115                         if len(class_func)>1:
116                                 f_name=''
117                                 if class_func[1].find('_')==0:
118                                         f_name='<font color="#0000cc">'+class_func[1]+'</font>'
119                                 else:
120                                         f_name=class_func[1]
121                                 if class_func[1]!='':
122                                         buf+= '<div><h4 class="class_function"><b>'\
123                                                 +namespace[0]+'</b>'+ "." + '<b><i>'+class_func[0]+'</i></b>'+'<b>.'+f_name+'</b>'+' ( <font color="#cc0000">'+ functions[1]+ '</font> )' +'</h4>\n'
124                                 else:
125                                         buf+= '<div><h4 class="class_definition"><b>'\
126                                                 +namespace[0]+'</b>'+ "." + '<b><i><u><font color="#000000">'+class_func[0]+'</font></u></i></b>' +'</h4>\n'
127                         else:
128                                 if functions[0].find('_')==0:
129                                         f_name='<font color="#0000cc">'+functions[0]+'</font>'
130                                 else:
131                                         f_name=functions[0]
132                                 buf+= '<div><h4 class="function"><b>'\
133                                         +namespace[0]+'</b>'+ "." + '<b>'+f_name+'</b>'+ ' ( <font color="#cc0000">'+ functions[1]+ '</font> )' +'</h4>\n'
134                         for comment in functions[2]:
135                                 if comment.find('=====')!=-1:
136                                         buf+='<hr/>'
137                                 else:
138                                         tempComment = comment.replace('#','').replace('<','&lt;').replace('>','&gt;')
139                                         if string.strip(tempComment)!="":
140                                                 buf+= '<div class="comments">'+tempComment+'</div><br/>\n'
141                         buf+='</div>\n'
142                 if namespace[0] not in done2:
143                         buf+='</div>\n'
144         buf+='</body></html>'
145         fw.write(buf)
146         fw.close()
147
148 def parse_file(filename):
149         fr=open(filename,'rb')
150         content=fr.readlines()
151         i=0
152         retval=[]
153         classname=""
154         for line in content:
155                 match=re.search('^var\s+([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line)
156                 if match!=None:
157                         func_name=match.group(1)
158                         comments=[]
159                         param=match.group(2)
160                         if(line.find(')')==-1 and line.find('(')!=-1):
161                                 k=i+1
162                                 while(content[k].find(')')==-1):
163                                         param+=content[k].rstrip('\n')
164                                         k+=1
165                                 param+=content[k].split(')')[0]
166                         j=i-1
167                         count=0
168                         while ( j>i-35 and j>-1):
169                                 if count>3:
170                                         break
171                                 if len(content[j])<2:
172                                         j-=1
173                                         count+=1
174                                         continue
175                                 if re.search('^\s*#',content[j])!=None:
176                                         comments.append(content[j].rstrip('\n'))
177                                         j-=1
178                                 else:
179                                         break
180                         if(len(comments)>1):
181                                 comments.reverse()
182                         retval.append((func_name, param,comments))
183                         i+=1
184                         continue
185
186                 match3=re.search('^var\s*([A-Za-z0-9_-]+)\s*=\s*{\s*(\n|})',line)
187                 if match3!=None:
188                         classname=match3.group(1)
189
190                         comments=[]
191
192                         j=i-1
193                         count=0
194                         while ( j>i-35 and j>-1):
195                                 if count>3:
196                                         break
197                                 if len(content[j])<2:
198                                         j-=1
199                                         count+=1
200                                         continue
201                                 if re.search('^\s*#',content[j])!=None:
202                                         comments.append(content[j].rstrip('\n'))
203                                         j-=1
204                                 else:
205                                         break
206                         if(len(comments)>1):
207                                 comments.reverse()
208                         retval.append((classname+'.', '',comments))
209                         i+=1
210                         continue
211
212                 match2=re.search('^\s*([A-Za-z0-9_-]+)\s*:\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line)
213                 if match2!=None:
214                         func_name=match2.group(1)
215                         comments=[]
216                         param=match2.group(2)
217                         if(line.find(')')==-1 and line.find('(')!=-1):
218                                 k=i+1
219                                 while(content[k].find(')')==-1):
220                                         param+=content[k].rstrip('\n')
221                                         k+=1
222                                 param+=content[k].split(')')[0]
223                         j=i-1
224                         count=0
225                         while ( j>i-35 and j>-1):
226                                 if count>3:
227                                         break
228                                 if len(content[j])<2:
229                                         j-=1
230                                         count+=1
231                                         continue
232                                 if re.search('^\s*#',content[j])!=None:
233                                         comments.append(content[j].rstrip('\n'))
234                                         j-=1
235                                 else:
236                                         break
237                         if(len(comments)>1):
238                                 comments.reverse()
239                         if classname =='':
240                                 continue
241                         retval.append((classname+'.'+func_name, param,comments))
242                         i+=1
243                         continue
244
245                 match4=re.search('^([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=\n.-]*)\)?',line)
246                 if match4!=None:
247                         classname=match4.group(1)
248                         func_name=match4.group(2)
249                         comments=[]
250                         param=match4.group(3)
251                         if(line.find(')')==-1 and line.find('(')!=-1):
252                                 k=i+1
253                                 while(content[k].find(')')==-1):
254                                         param+=content[k].rstrip('\n')
255                                         k+=1
256                                 param+=content[k].split(')')[0]
257                         j=i-1
258                         count=0
259                         while ( j>i-35 and j>-1):
260                                 if count>3:
261                                         break
262                                 if len(content[j])<2:
263                                         j-=1
264                                         count+=1
265                                         continue
266                                 if re.search('^\s*#',content[j])!=None:
267                                         comments.append(content[j].rstrip('\n'))
268                                         j-=1
269                                 else:
270                                         break
271                         if(len(comments)>1):
272                                 comments.reverse()
273                         retval.append((classname+'.'+func_name, param,comments))
274                         i+=1
275                         continue
276
277                 i+=1
278         return retval
279
280
281
282 if __name__ == "__main__":
283         if len(sys.argv) <2:
284                 print 'Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]'
285                 sys.exit()
286         else:
287                 if sys.argv[1]=='parse':
288                         if len(sys.argv) <3:
289                                 nasal_path=NASAL_PATH
290                         else:
291                                 nasal_path=sys.argv[2]
292                         get_files(nasal_path)
293                 else:
294                         print 'Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]'
295                         sys.exit()