758d2c600a2be32416e4b319fc6facd2ed3d21a6
[ardour.git] / tools / fmt-luadoc.php
1 #!/usr/bin/php
2 <?php
3 ## USAGE
4 #
5 # ./waf configure --luadoc ....
6 # ./waf
7 # ./gtk2_ardour/arluadoc > doc/luadoc.json
8 #
9 # php tools/fmt-luadoc.php > /tmp/luadoc.html
10 #
11
12 ################################################################################
13 ################################################################################
14
15 $json = file_get_contents (dirname (__FILE__).'/../doc/luadoc.json');
16 $doc = array ();
17 foreach (json_decode ($json, true) as $b) {
18         if (!isset ($b['type'])) { continue; }
19         $doc[] = $b;
20 }
21
22 if (count ($doc) == 0) {
23         fwrite (STDERR, "Failed to read luadoc.json\n");
24         exit (1);
25 }
26
27 ################################################################################
28 ## Global result variables
29 ################################################################################
30
31 $classlist = array ();
32 $funclist = array ();
33 $constlist = array ();
34
35
36 ################################################################################
37 ## Pre-process the data, collect functions, parse arguments, cross reference
38 ################################################################################
39
40
41 ################################################################################
42 # some internal helper functions first
43
44 $classes = array ();
45 $consts = array ();
46
47 function my_die ($msg) {
48         fwrite (STDERR, $msg."\n");
49         exit (1);
50 }
51
52 ##function ptr_strip ($ctype) {
53 #       # boost::shared_ptr<std::list<boost::shared_ptr<ARDOUR::Route>> > >
54 #       # -> std::list<ARDOUR::Route>
55 #       $ctype = preg_replace ('/boost::shared_ptr<([^>]*)[ ]*>/', '$1', $ctype);
56 #       return preg_replace ('/boost::shared_ptr<([^>]*)[ ]*>/', '$1', $ctype);
57 #}
58
59 function arg2lua ($argtype) {
60         global $classes;
61         global $consts;
62
63         # LuaBridge abstracts C++ references
64         $flags = preg_match ('/&$/', $argtype);
65         $arg = preg_replace ('/&$/', '', $argtype);
66
67         # filter out basic types
68         $builtin = array ('float', 'double', 'bool', 'std::string', 'int', 'long', 'unsigned long', 'unsigned int', 'unsigned char', 'char', 'void', 'char*', 'unsigned char*', 'void*');
69         if (in_array ($arg, $builtin)) {
70                 return array ($arg => $flags);
71         }
72
73         # check Class declarations first
74         foreach (array_merge ($classes, $consts) as $b) {
75                 if ($b['decl'] == $arg) {
76                         return array ($b['lua'] => $flags);
77                 }
78         }
79
80         # strip class pointers -- TODO Check C'tor for given class
81         $arg = preg_replace ('/[&*]*$/', '', $argtype);
82         foreach (array_merge ($classes, $consts) as $b) {
83                 if ($b['decl'] == $arg) {
84                         return array ($b['lua'] => $flags);
85                 }
86         }
87         return array ('--MISSING (' . $argtype . ')--' => ($flags | 4));
88 }
89
90 function stripclass ($classname, $name) {
91         $classname .= ':';
92         if (strpos ($name, $classname) !== 0) {
93                 my_die ('invalid class prefix: ' .$classname. ' -- '. $name);
94         }
95         return substr ($name, strlen ($classname));
96 }
97
98 function datatype ($decl) {
99         # TODO handle spaces in type. Works because
100         # we don't yet have templated types (with_space <here >)
101         return substr ($decl, 0, strpos ($decl, ' '));
102 }
103
104 function luafn2class ($lua) {
105         return substr ($lua, 0, strrpos ($lua, ':'));
106 }
107
108 function checkclass ($b) {
109         global $classlist;
110         if (!isset ($classlist[luafn2class ($b['lua'])])) {
111                 my_die ('MISSING CLASS FOR '. print_r ($b['lua'], true));
112         }
113 }
114
115 # parse functions argument list to lua-names
116 function decl2args ($decl) {
117         $start = strrpos ($decl, '(');
118         $end = strrpos ($decl, ')');
119         $args = substr ($decl, $start + 1, $end - $start - 1);
120         $arglist = preg_split ('/, */', $args);
121         $rv = array ();
122         foreach ($arglist as $a) {
123                 if (empty ($a)) { continue; }
124                 $rv[] = arg2lua ($a);
125         }
126         return $rv;
127 }
128
129 ################################################################################
130 # step 1: build class indices
131
132 foreach ($doc as $b) {
133         if (strpos ($b['type'], "[C] ") === 0) {
134                 $classes[] = $b;
135                 $classlist[$b['lua']] = $b;
136                 if (strpos ($b['type'], 'Pointer Class') === false) {
137                         $classdecl[$b['decl']] = $b;
138                 }
139         }
140 }
141
142 foreach ($classes as $c) {
143         if (strpos ($c['type'], 'Pointer Class') !== false) { continue; }
144         if (isset ($c['parent'])) {
145                 if (isset ($classdecl[$c['parent']])) {
146                         $classlist[$c['lua']]['luaparent'][] = $classdecl[$c['parent']]['lua'];
147                 } else {
148                         my_die ('unknown parent class: ' . print_r ($c, true));
149                 }
150         }
151 }
152
153 # step 2: extract constants/enum
154 foreach ($doc as $b) {
155         switch ($b['type']) {
156         case "Constant/Enum":
157         case "Constant/Enum Member":
158                 if (strpos ($b['decl'], '::') === false) {
159                         # for extern c enums, use the Lua Namespace
160                         $b['decl'] = str_replace (':', '::', luafn2class ($b['lua']));
161                 }
162                 $ns = str_replace ('::', ':', $b['decl']);
163                 $constlist[$ns][] = $b;
164                 # arg2lua lookup
165                 $b['lua'] = $ns;
166                 $consts[] = $b;
167                 break;
168         default:
169                 break;
170         }
171 }
172
173 # step 3: process functions
174 foreach ($doc as $b) {
175         switch ($b['type']) {
176         case "Constructor":
177         case "Weak/Shared Pointer Constructor":
178                 checkclass ($b);
179                 $classlist[luafn2class ($b['lua'])]['ctor'][] = array (
180                         'name' => luafn2class ($b['lua']),
181                         'args' => decl2args ($b['decl']),
182                 );
183                 break;
184         case "Data Member":
185                 checkclass ($b);
186                 $classlist[luafn2class ($b['lua'])]['data'][] = array (
187                         'name' => $b['lua'],
188                         'ret'  => arg2lua (datatype ($b['decl']))
189                 );
190                 break;
191         case "C Function":
192                 # we required C functions to be in a class namespace
193         case "Ext C Function":
194                 checkclass ($b);
195                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
196                         'name' => $b['lua'],
197                         'args' => array (array ('--custom--' => 0)), // XXX
198                         'ret' => array ('...' => 0), // XXX
199                         'ref'  => true,
200                         'ext'  => true
201                 );
202                 break;
203         case "Free Function":
204         case "Free Function RefReturn":
205                 $funclist[luafn2class ($b['lua'])][] = array (
206                         'name' => $b['lua'],
207                         'args' => decl2args ($b['decl']),
208                         'ret'  => arg2lua ($b['ret']),
209                         'ref'  => (strpos ($b['type'], "RefReturn") !== false)
210                 );
211                 break;
212         case "Member Function":
213         case "Member Function RefReturn":
214         case "Member Pointer Function":
215         case "Weak/Shared Pointer Function":
216         case "Weak/Shared Pointer Function RefReturn":
217         case "Weak/Shared Null Check":
218         case "Weak/Shared Pointer Cast":
219         case "Static Member Function":
220                 checkclass ($b);
221                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
222                         'name' => $b['lua'],
223                         'args' => decl2args ($b['decl']),
224                         'ret'  => arg2lua ($b['ret']),
225                         'ref'  => (strpos ($b['type'], "RefReturn") !== false)
226                 );
227                 break;
228         case "Constant/Enum":
229         case "Constant/Enum Member":
230                 # already handled -> $consts
231                 break;
232         default:
233                 if (strpos ($b['type'], "[C] ") !== 0) {
234                         my_die ('unhandled type: ' . $b['type']);
235                 }
236                 break;
237         }
238 }
239
240
241 # step 4: collect/group/sort
242
243 # step 4a: unify weak/shared Ptr classes
244 foreach ($classlist as $ns => $cl) {
245         if (strpos ($cl['type'], ' Array') !== false) {
246                 $classlist[$ns]['arr'] = true;
247                 continue;
248         }
249         foreach ($classes as $c) {
250                 if ($c['lua'] == $ns) {
251                         if (strpos ($c['type'], 'Pointer Class') !== false) {
252                                 $classlist[$ns]['ptr'] = true;
253                                 $classlist[$ns]['decl'] = 'boost::shared_ptr< '.$c['decl']. ' >, boost::weak_ptr< '.$c['decl']. ' >';
254                                 break;
255                         }
256                 }
257         }
258 }
259
260 # step4b: sanity check
261 foreach ($classlist as $ns => $cl) {
262         if (isset ($classes[$ns]['parent']) && !isset ($classlist[$ns]['luaparent'])) {
263                 my_die ('missing parent class: ' . print_r ($cl, true));
264         }
265 }
266
267 # step 4c: merge free functions into classlist
268 foreach ($funclist as $ns => $fl) {
269         if (isset ($classlist[$ns])) {
270                 my_die ('Free Funcion in existing namespace: '.$ns.' '. print_r ($ns, true));
271         }
272         $classlist[$ns]['func'] = $fl;
273         $classlist[$ns]['free'] = true;
274 }
275
276 # step 4d: order to chaos
277 # no array_multisort() here, sub-types are sorted after merging parents
278 ksort ($classlist);
279
280
281 ################################################################################
282 # OUTPUT
283 ################################################################################
284
285
286 ################################################################################
287 # Helper functions
288 define ('NL', "\n");
289
290 function ctorname ($name) {
291         return htmlentities (str_replace (':', '.', $name));
292 }
293
294 function varname ($a) {
295         return array_keys ($a)[0];
296 }
297
298 function name_sort_cb ($a, $b) {
299         return strcmp ($a['name'], $b['name']);
300 }
301
302 function typelink ($a, $linkcls = '', $argcls = '') {
303         global $classlist;
304         global $constlist;
305         if (in_array ($a, array_keys ($classlist))) {
306                 return '<a class="'.$linkcls.'" href="#'.$a.'">'.$a.'</a>';
307         } else if (in_array ($a, array_keys ($constlist))) {
308                 return '<a class="'.$linkcls.'" href="#'.ctorname ($a).'">'.ctorname ($a).'</a>';
309         } else {
310                 return '<span class="'.$argcls.'">'.$a.'</span>';
311         }
312 }
313
314 function format_args ($args) {
315         $rv = '<span class="functionargs"> (';
316         $first = true;
317         foreach ($args as $a) {
318                 if (!$first) { $rv .= ', '; }; $first = false;
319                 $flags = $a[varname ($a)];
320                 if ($flags & 1) {
321                         $rv .= typelink (varname ($a).'&amp;', '', 'em');
322                 } else {
323                         $rv .= typelink (varname ($a), '', 'em');
324                 }
325         }
326         $rv .= ')</span>';
327         return $rv;
328 }
329
330 function format_class_members ($ns, $cl, &$dups) {
331         $rv = '';
332         if (isset ($cl['ctor'])) {
333                 usort ($cl['ctor'], 'name_sort_cb');
334                 $rv.= ' <tr><th colspan="3">Constructor</th></tr>'.NL;
335                 foreach ($cl['ctor'] as $f) {
336                         $rv.= ' <tr><td class="def">&Copf;</td><td class="decl">';
337                         $rv.= '<span class="functionname">'.ctorname ($f['name']).'</span>';
338                         $rv.= format_args ($f['args']);
339                         $rv.= '</td><td class="fill"></td></tr>'.NL;
340                 }
341         }
342         $nondups = array ();
343         if (isset ($cl['func'])) {
344                 foreach ($cl['func'] as $f) {
345                         if (in_array (stripclass ($ns, $f['name']), $dups)) { continue; }
346                         $nondups[] = $f;
347                 }
348         }
349         if (count ($nondups) > 0) {
350                 usort ($nondups, 'name_sort_cb');
351                 $rv.= ' <tr><th colspan="3">Methods</th></tr>'.NL;
352                 foreach ($nondups as $f) {
353                         $dups[] = stripclass ($ns, $f['name']);
354                         $rv.= ' <tr><td class="def">';
355                         if ($f['ref'] && isset ($f['ext'])) {
356                                 # external C functions
357                                 $rv.= '<em>LuaTable</em>';
358                         } elseif ($f['ref'] && varname ($f['ret']) == 'void') {
359                                 # functions with reference args return args
360                                 $rv.= '<em>LuaTable</em>(...)';
361                         } elseif ($f['ref']) {
362                                 $rv.= '<em>LuaTable</em>('.typelink (varname ($f['ret'])).', ...)';
363                         } else {
364                                 $rv.= typelink (varname ($f['ret']));
365                         }
366                         $rv.= '</td><td class="decl">';
367                         $rv.= '<span class="functionname">'.stripclass ($ns, $f['name']).'</span>';
368                         $rv.= format_args ($f['args']);
369                         $rv.= '</td><td class="fill"></td></tr>'.NL;
370 }
371         }
372         if (isset ($cl['data'])) {
373                 usort ($cl['data'], 'name_sort_cb');
374                 $rv.= ' <tr><th colspan="3">Data Members</th></tr>'.NL;
375                 foreach ($cl['data'] as $f) {
376                         $rv.= ' <tr><td class="def">'.typelink (array_keys ($f['ret'])[0]).'</td><td class="decl">';
377                         $rv.= '<span class="functionname">'.stripclass ($ns, $f['name']).'</span>';
378                         $rv.= '</td><td class="fill"></td></tr>'.NL;
379                 }
380         }
381         return $rv;
382 }
383
384
385 ################################################################################
386 # Start Output
387 ?><!DOCTYPE html>
388 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
389 <head>
390 <title>Ardour Lua Bindings</title>
391 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
392 <style type="text/css">
393 div.content        { max-width:60em; margin: 1em auto; }
394 h1                 { margin:2em 0 0 0; padding:0em; border-bottom: 1px solid black;}
395 h2.cls             { margin:2em 0 0 0; padding-left:1em; border: 1px dashed #6666ee;}
396 h2.cls abbr        { text-decoration:none; cursor:default;}
397 h3.cls             { margin:1em 0 0 0;}
398 h2.class           { background-color: #aaee66; }
399 h2.enum            { background-color: #aaaaaa; }
400 h2.pointerclass    { background-color: #eeaa66; }
401 h2.array           { background-color: #66aaee; }
402 h2.opaque          { background-color: #6666aa; }
403 p.cdecl            { text-align: right; float:right; font-size:90%; margin:0; padding: 0 0 0 1em;}
404 ul.classlist       { columns: 2; -webkit-columns: 2; -moz-columns: 2; }
405 div.clear          { clear:both; }
406 table.classmembers { width: 100%; }
407 table.classmembers th      { text-align:left; border-bottom:1px solid black; padding-top:1em; }
408 table.classmembers td.def  { text-align:right; padding-right:.5em;  white-space: nowrap;}
409 table.classmembers td.decl { text-align:left; padding-left:.5em; white-space: nowrap; }
410 table.classmembers td.fill { width: 99%;}
411 table.classmembers span.em { font-style: italic;}
412 div.header         {text-align:center;}
413 div.header h1      {margin:0;}
414 div.header p       {margin:.25em;}
415 </style>
416 </head>
417 <body>
418 <div class="header">
419 <h1>Ardour Lua Bindings</h1>
420 <p>
421 <a href="#h_classes">Class Documentation</a>
422 &nbsp;|&nbsp;
423 <a href="#h_enum">Enum/Constants</a>
424 &nbsp;|&nbsp;
425 <a href="#h_index">Index</a>
426 </p>
427 </div>
428 <div class="content">
429
430 <?php
431 echo '<h1 id="h_classes">Class Documentation</h1>'.NL;
432
433 foreach ($classlist as $ns => $cl) {
434         $dups = array ();
435         $tbl =  format_class_members ($ns, $cl, $dups);
436
437         if (empty ($tbl)) {
438                 # place-holder class (maybe collect at bottom??)
439                 echo '<h2 id="'.$ns.'" class="cls opaque"><abbr title="Opaque Object">&empty;</abbr>&nbsp;'.$ns.'</h2>'.NL;
440         }
441         else if (isset ($classlist[$ns]['free'])) {
442                 echo '<h2 id="'.$ns.'" class="cls freeclass"><abbr title="Namespace">&Nopf;</abbr>&nbsp;'.$ns.'</h2>'.NL;
443         }
444         else if (isset ($classlist[$ns]['arr'])) {
445                 echo '<h2 id="'.$ns.'" class="cls array"><abbr title="C Array">&ctdot;</abbr>&nbsp;'.$ns.'</h2>'.NL;
446         }
447         else if (isset ($classlist[$ns]['ptr'])) {
448                 echo '<h2 id="'.$ns.'" class="cls pointerclass"><abbr title="Pointer Class">&Rarr;</abbr>&nbsp;'.$ns.'</h2>'.NL;
449         } else {
450                 echo '<h2 id="'.htmlentities ($ns).'" class="cls class"><abbr title="Class">&comp;</abbr>&nbsp;'.htmlentities ($ns).'</h2>'.NL;
451         }
452         if (isset ($cl['decl'])) {
453                 echo '<p class="cdecl"><em>C&#8225;</em>: '.htmlentities ($cl['decl']).'</p>'.NL;
454         }
455
456         $inherited = array ();
457         if (isset ($classlist[$ns]['luaparent'])) {
458                 $parents = array_unique ($classlist[$ns]['luaparent']);
459                 asort ($parents);
460                 echo ' <p>is-a: ';
461                 $first = true;
462                 foreach ($parents as $p) {
463                         if (!$first) { echo ', '; }; $first = false;
464                         echo typelink ($p);
465                         $inherited[$p] = $classlist[$p];
466                 }
467                 echo '</p>'.NL;
468         }
469         echo '<div class="clear"></div>'.NL;
470
471         if (empty ($tbl)) {
472                 echo '<p>This class object is only used indirectly as return-value and function-parameter.</p>'.NL;
473         } else {
474                 echo '<table class="classmembers">'.NL;
475                 echo $tbl;
476                 echo ' </table>'.NL;
477         }
478
479         // traverse all parent classes..
480         foreach ($inherited as $pns => $pcl) {
481                 $tbl = format_class_members ($pns, $pcl, $dups);
482                 if (!empty ($tbl)) {
483                         echo '<h3 class="cls">Inherited from '.$pns.'</h3>'.NL;
484                         echo '<table class="classmembers">'.NL;
485                         echo $tbl;
486                         echo '</table>'.NL;
487                 }
488         }
489 }
490
491 echo '<h1 id="h_enum">Enum/Constants</h1>'.NL;
492 foreach ($constlist as $ns => $cs) {
493         echo '<h2 id="'.ctorname ($ns).'" class="cls enum"><abbr title="Enum">&isin;</abbr>&nbsp;'.ctorname ($ns).'</h2>'.NL;
494         echo '<ul class="enum">'.NL;
495         foreach ($cs as $c) {
496                 echo '<li class="const">'.ctorname ($c['lua']).'</li>'.NL;
497         }
498         echo '</ul>'.NL;
499 }
500
501 echo '<h1 id="h_index" >Class Index</h1>'.NL;
502 echo '<ul class="classlist">'.NL;
503 foreach ($classlist as $ns => $cl) {
504         echo '<li><a href="#'.$ns.'">'.$ns.'</a></li>'.NL;
505 }
506 echo '</ul>'.NL;
507
508
509
510 ?>
511 </div>
512 </body>
513 </html>