2 # -*- coding: utf-8 -*-
4 # Copyright (C) 2012 Adrian Musceac
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.
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.
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/>.
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"""
30 ########### Local $FGROOT/Nasal/ path ##########
31 NASAL_PATH="../fgfs/fgdata/Nasal/"
34 def get_files(nasal_dir):
35 if nasal_dir[-1]!='/':
40 print "The path does not exist"
42 fgroot_dir = nasal_dir.rstrip('/').replace('Nasal','')
43 f_version = open(fgroot_dir+'version','rb')
44 version = f_version.read(256).rstrip('\n')
48 files_list = os.listdir(nasal_dir)
50 if f.find(".nas")!=-1:
53 if os.path.isdir(nasal_dir + f):
57 if len(top_level) ==0:
58 print "This does not look like the correct $FGROOT/Nasal path"
61 print "Warning: could not find any submodules"
63 namespace=f.replace(".nas","")
64 functions=parse_file(nasal_dir + f)
65 top_namespaces.append([namespace,functions])
67 files=glob.glob(nasal_dir+m+"/*.nas")
69 functions=parse_file(f)
70 top_namespaces.append([m,functions])
72 output_text(top_namespaces,modules,version)
75 def output_text(top_namespaces,modules,version):
76 fw=open('./nasal_api_doc.html','wb')
78 <title>Nasal API</title>\
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;}\
89 </head><body style="width:1024px;">'
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\
95 <br/><a href="http://plausible.org/nasal">Nasal documentation</a> \
96 <a href="http://wiki.flightgear.org/Nasal_scripting_language">Flightgear Nasal documentation</a>\n<div style="float:right;"> '
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'
99 for namespace in top_namespaces:
101 if namespace[0] in modules:
103 if namespace[0] not in done:
104 buf+='<a class="main_module_link" style="color:'+color+'" href="#'+namespace[0]+'">'+namespace[0]+'</a> <br/>\n'
105 done.append(namespace[0])
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:
117 if class_func[1].find('_')==0:
118 f_name='<font color="#0000cc">'+class_func[1]+'</font>'
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'
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'
128 if functions[0].find('_')==0:
129 f_name='<font color="#0000cc">'+functions[0]+'</font>'
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:
138 tempComment = comment.replace('#','').replace('<','<').replace('>','>')
139 if string.strip(tempComment)!="":
140 buf+= '<div class="comments">'+tempComment+'</div><br/>\n'
142 if namespace[0] not in done2:
144 buf+='</body></html>'
148 def parse_file(filename):
149 fr=open(filename,'rb')
150 content=fr.readlines()
155 match=re.search('^var\s+([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line)
157 func_name=match.group(1)
160 if(line.find(')')==-1 and line.find('(')!=-1):
162 while(content[k].find(')')==-1):
163 param+=content[k].rstrip('\n')
165 param+=content[k].split(')')[0]
168 while ( j>i-35 and j>-1):
171 if len(content[j])<2:
175 if re.search('^\s*#',content[j])!=None:
176 comments.append(content[j].rstrip('\n'))
182 retval.append((func_name, param,comments))
186 match3=re.search('^var\s*([A-Za-z0-9_-]+)\s*=\s*{\s*(\n|})',line)
188 classname=match3.group(1)
194 while ( j>i-35 and j>-1):
197 if len(content[j])<2:
201 if re.search('^\s*#',content[j])!=None:
202 comments.append(content[j].rstrip('\n'))
208 retval.append((classname+'.', '',comments))
212 match2=re.search('^\s*([A-Za-z0-9_-]+)\s*:\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line)
214 func_name=match2.group(1)
216 param=match2.group(2)
217 if(line.find(')')==-1 and line.find('(')!=-1):
219 while(content[k].find(')')==-1):
220 param+=content[k].rstrip('\n')
222 param+=content[k].split(')')[0]
225 while ( j>i-35 and j>-1):
228 if len(content[j])<2:
232 if re.search('^\s*#',content[j])!=None:
233 comments.append(content[j].rstrip('\n'))
241 retval.append((classname+'.'+func_name, param,comments))
245 match4=re.search('^([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=\n.-]*)\)?',line)
247 classname=match4.group(1)
248 func_name=match4.group(2)
250 param=match4.group(3)
251 if(line.find(')')==-1 and line.find('(')!=-1):
253 while(content[k].find(')')==-1):
254 param+=content[k].rstrip('\n')
256 param+=content[k].split(')')[0]
259 while ( j>i-35 and j>-1):
262 if len(content[j])<2:
266 if re.search('^\s*#',content[j])!=None:
267 comments.append(content[j].rstrip('\n'))
273 retval.append((classname+'.'+func_name, param,comments))
282 if __name__ == "__main__":
284 print 'Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]'
287 if sys.argv[1]=='parse':
289 nasal_path=NASAL_PATH
291 nasal_path=sys.argv[2]
292 get_files(nasal_path)
294 print 'Usage: nasal_api_doc.py parse [path to $FGROOT/Nasal/]'