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.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.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.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 buf+= '<div class="comments">'+comment.replace('#','').replace('<','<').replace('>','>')+'</div><br/>\n'
140 if namespace[0] not in done2:
142 buf+='</body></html>'
146 def parse_file(filename):
147 fr=open(filename,'rb')
148 content=fr.readlines()
153 match=re.search('^var\s+([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line)
155 func_name=match.group(1)
158 if(line.find(')')==-1 and line.find('(')!=-1):
160 while(content[k].find(')')==-1):
161 param+=content[k].rstrip('\n')
163 param+=content[k].split(')')[0]
166 while ( j>i-35 and j>-1):
169 if len(content[j])<2:
173 if re.search('^\s*#',content[j])!=None:
174 comments.append(content[j].rstrip('\n'))
180 retval.append((func_name, param,comments))
184 match3=re.search('^var\s*([A-Za-z0-9_-]+)\s*=\s*{\s*(\n|})',line)
186 classname=match3.group(1)
192 while ( j>i-35 and j>-1):
195 if len(content[j])<2:
199 if re.search('^\s*#',content[j])!=None:
200 comments.append(content[j].rstrip('\n'))
206 retval.append((classname+'.', '',comments))
210 match2=re.search('^\s*([A-Za-z0-9_-]+)\s*:\s*func\s*\(?([A-Za-z0-9_\s,=.\n-]*)\)?',line)
212 func_name=match2.group(1)
214 param=match2.group(2)
215 if(line.find(')')==-1 and line.find('(')!=-1):
217 while(content[k].find(')')==-1):
218 param+=content[k].rstrip('\n')
220 param+=content[k].split(')')[0]
223 while ( j>i-35 and j>-1):
226 if len(content[j])<2:
230 if re.search('^\s*#',content[j])!=None:
231 comments.append(content[j].rstrip('\n'))
239 retval.append((classname+'.'+func_name, param,comments))
243 match4=re.search('^([A-Za-z0-9_-]+)\.([A-Za-z0-9_-]+)\s*=\s*func\s*\(?([A-Za-z0-9_\s,=\n.-]*)\)?',line)
245 classname=match4.group(1)
246 func_name=match4.group(2)
248 param=match4.group(3)
249 if(line.find(')')==-1 and line.find('(')!=-1):
251 while(content[k].find(')')==-1):
252 param+=content[k].rstrip('\n')
254 param+=content[k].split(')')[0]
257 while ( j>i-35 and j>-1):
260 if len(content[j])<2:
264 if re.search('^\s*#',content[j])!=None:
265 comments.append(content[j].rstrip('\n'))
271 retval.append((classname+'.'+func_name, param,comments))
280 if __name__ == "__main__":
282 print 'Usage: nasal_api.py parse [path to $FGROOT/Nasal/]'
285 if sys.argv[1]=='parse':
287 nasal_path=NASAL_PATH
289 nasal_path=sys.argv[2]
290 get_files(nasal_path)
292 print 'Usage: nasal_api.py parse [path to $FGROOT/Nasal/]'