Add some introductory lua-doc paragraph
[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 $constlist = array ();
33
34
35 ################################################################################
36 ## Pre-process the data, collect functions, parse arguments, cross reference
37 ################################################################################
38
39
40 ################################################################################
41 # some internal helper functions first
42
43 $funclist = array ();
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                 $args = array (array ('--custom--' => 0));
196                 $ret = array ('...' => 0);
197                 $ns = luafn2class ($b['lua']);
198                 $cls = $classlist[$ns];
199                 ## std::Vector std::List types
200                 if (preg_match ('/.*<([^>]*)[ ]*>/', $cls['decl'], $templ)) {
201                         // XXX -> move to C-source
202                         switch (stripclass($ns, $b['lua'])) {
203                         case 'add':
204                                 $args = array (array ('LuaTable {'.$templ[1].'}' => 0));
205                                 $ret = array ('LuaTable' => 0);
206                                 break;
207                         case 'iter':
208                                 $args = array ();
209                                 $ret = array ('LuaIter' => 0);
210                                 break;
211                         case 'table':
212                                 $args = array ();
213                                 $ret = array ('LuaTable' => 0);
214                                 break;
215                         default:
216                                 break;
217                         }
218                 } else if (strpos ($cls['type'], ' Array') !== false) {
219                         $templ = preg_replace ('/[&*]*$/', '', $cls['decl']);
220                         switch (stripclass($ns, $b['lua'])) {
221                         case 'array':
222                                 $args = array ();
223                                 $ret = array ('LuaMetaTable' => 0);
224                                 break;
225                         case 'get_table':
226                                 $args = array ();
227                                 $ret = array ('LuaTable' => 0);
228                                 break;
229                         case 'set_table':
230                                 $args = array (array ('LuaTable {'.$templ.'}' => 0));
231                                 $ret = array ('void' => 0);
232                                 break;
233                         default:
234                                 break;
235                         }
236                 }
237                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
238                         'bind' => $b,
239                         'name' => $b['lua'],
240                         'args' => $args,
241                         'ret'  => $ret,
242                         'ref'  => true,
243                         'ext'  => true
244                 );
245                 break;
246         case "Free Function":
247         case "Free Function RefReturn":
248                 $funclist[luafn2class ($b['lua'])][] = array (
249                         'bind' => $b,
250                         'name' => $b['lua'],
251                         'args' => decl2args ($b['decl']),
252                         'ret'  => arg2lua ($b['ret']),
253                         'ref'  => (strpos ($b['type'], "RefReturn") !== false)
254                 );
255                 break;
256         case "Member Function":
257         case "Member Function RefReturn":
258         case "Member Pointer Function":
259         case "Weak/Shared Pointer Function":
260         case "Weak/Shared Pointer Function RefReturn":
261         case "Weak/Shared Null Check":
262         case "Weak/Shared Pointer Cast":
263         case "Static Member Function":
264                 checkclass ($b);
265                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
266                         'bind' => $b,
267                         'name' => $b['lua'],
268                         'args' => decl2args ($b['decl']),
269                         'ret'  => arg2lua ($b['ret']),
270                         'ref'  => (strpos ($b['type'], "RefReturn") !== false)
271                 );
272                 break;
273         case "Constant/Enum":
274         case "Constant/Enum Member":
275                 # already handled -> $consts
276                 break;
277         default:
278                 if (strpos ($b['type'], "[C] ") !== 0) {
279                         my_die ('unhandled type: ' . $b['type']);
280                 }
281                 break;
282         }
283 }
284
285
286 # step 4: collect/group/sort
287
288 # step 4a: unify weak/shared Ptr classes
289 foreach ($classlist as $ns => $cl) {
290         if (strpos ($cl['type'], ' Array') !== false) {
291                 $classlist[$ns]['arr'] = true;
292                 continue;
293         }
294         foreach ($classes as $c) {
295                 if ($c['lua'] == $ns) {
296                         if (strpos ($c['type'], 'Pointer Class') !== false) {
297                                 $classlist[$ns]['ptr'] = true;
298                                 $classlist[$ns]['decl'] = 'boost::shared_ptr< '.$c['decl']. ' >, boost::weak_ptr< '.$c['decl']. ' >';
299                                 break;
300                         }
301                 }
302         }
303 }
304
305 # step4b: sanity check
306 foreach ($classlist as $ns => $cl) {
307         if (isset ($classes[$ns]['parent']) && !isset ($classlist[$ns]['luaparent'])) {
308                 my_die ('missing parent class: ' . print_r ($cl, true));
309         }
310 }
311
312 # step 4c: merge free functions into classlist
313 foreach ($funclist as $ns => $fl) {
314         if (isset ($classlist[$ns])) {
315                 my_die ('Free Funcion in existing namespace: '.$ns.' '. print_r ($ns, true));
316         }
317         $classlist[$ns]['func'] = $fl;
318         $classlist[$ns]['free'] = true;
319 }
320
321 # step 4d: order to chaos
322 # no array_multisort() here, sub-types are sorted after merging parents
323 ksort ($classlist);
324
325
326 ################################################################################
327 ################################################################################
328 ################################################################################
329
330
331 #### -- split here --  ####
332
333 # from here on, only $classlist and $constlist arrays are relevant.
334
335 # TODO: read function documentation from doxygen
336 # and/or reference source-code lines e.g from CSV list:
337 #   ctags -o /tmp/tags.csv --fields=+afiKkmnsSzt libs/ardour/ardour/session.h
338
339
340 ################################################################################
341 # OUTPUT
342 ################################################################################
343
344
345 ################################################################################
346 # Helper functions
347 define ('NL', "\n");
348
349 function ctorname ($name) {
350         return htmlentities (str_replace (':', '.', $name));
351 }
352
353 function shortname ($name) {
354         return htmlentities (substr ($name, strrpos ($name, ':') + 1));
355 }
356
357 function varname ($a) {
358         return array_keys ($a)[0];
359 }
360
361 function name_sort_cb ($a, $b) {
362         return strcmp ($a['name'], $b['name']);
363 }
364
365 function traverse_parent ($ns, &$inherited) {
366         global $classlist;
367         $rv = '';
368         if (isset ($classlist[$ns]['luaparent'])) {
369                 $parents = array_unique ($classlist[$ns]['luaparent']);
370                 asort ($parents);
371                 foreach ($parents as $p) {
372                         if (!empty ($rv)) { $rv .= ', '; }
373                         $rv .= typelink ($p);
374                         $inherited[$p] = $classlist[$p];
375                         traverse_parent ($p, $inherited);
376                 }
377         }
378         return $rv;
379 }
380
381 function typelink ($a, $short = false, $argcls = '', $linkcls = '', $suffix = '') {
382         global $classlist;
383         global $constlist;
384         # all cross-reference links are generated here.
385         # currently anchors on a single page.
386         if (isset($classlist[$a]['free'])) {
387                 return '<a class="'.$linkcls.'" href="#'.htmlentities ($a).'">'.($short ? shortname($a) : ctorname($a)).$suffix.'</a>';
388         } else if (in_array ($a, array_keys ($classlist))) {
389                 return '<a class="'.$linkcls.'" href="#'.htmlentities($a).'">'.($short ? shortname($a) : htmlentities($a)).$suffix.'</a>';
390         } else if (in_array ($a, array_keys ($constlist))) {
391                 return '<a class="'.$linkcls.'" href="#'.ctorname ($a).'">'.($short ? shortname($a) : ctorname($a)).$suffix.'</a>';
392         } else {
393                 return '<span class="'.$argcls.'">'.htmlentities($a).$suffix.'</span>';
394         }
395 }
396
397 function format_args ($args) {
398         $rv = '<span class="functionargs"> (';
399         $first = true;
400         foreach ($args as $a) {
401                 if (!$first) { $rv .= ', '; }; $first = false;
402                 $flags = $a[varname ($a)];
403                 if ($flags & 1) {
404                         $rv .= typelink (varname ($a), true, 'em', '', '&amp;');
405                 } else {
406                         $rv .= typelink (varname ($a), true, 'em');
407                 }
408         }
409         $rv .= ')</span>';
410         return $rv;
411 }
412
413 function format_class_members ($ns, $cl, &$dups) {
414         $rv = '';
415         if (isset ($cl['ctor'])) {
416                 usort ($cl['ctor'], 'name_sort_cb');
417                 $rv.= ' <tr><th colspan="3">Constructor</th></tr>'.NL;
418                 foreach ($cl['ctor'] as $f) {
419                         $rv.= ' <tr><td class="def">&Copf;</td><td class="decl">';
420                         $rv.= '<span class="functionname">'.ctorname ($f['name']).'</span>';
421                         $rv.= format_args ($f['args']);
422                         $rv.= '</td><td class="fill"></td></tr>'.NL;
423                 }
424         }
425         $nondups = array ();
426         if (isset ($cl['func'])) {
427                 foreach ($cl['func'] as $f) {
428                         if (in_array (stripclass ($ns, $f['name']), $dups)) { continue; }
429                         $nondups[] = $f;
430                 }
431         }
432         if (count ($nondups) > 0) {
433                 usort ($nondups, 'name_sort_cb');
434                 $rv.= ' <tr><th colspan="3">Methods</th></tr>'.NL;
435                 foreach ($nondups as $f) {
436                         $dups[] = stripclass ($ns, $f['name']);
437                         $rv.= ' <tr><td class="def">';
438                         if ($f['ref'] && isset ($f['ext'])) {
439                                 # external C functions
440                                 $rv.= '<em>'.varname ($f['ret']).'</em>';
441                         } elseif ($f['ref'] && varname ($f['ret']) == 'void') {
442                                 # functions with reference args return args
443                                 $rv.= '<em>LuaTable</em>(...)';
444                         } elseif ($f['ref']) {
445                                 $rv.= '<em>LuaTable</em>('.typelink (varname ($f['ret'], false, 'em')).', ...)';
446                         } else {
447                                 $rv.= typelink (varname ($f['ret']), true, 'em');
448                         }
449                         $rv.= '</td><td class="decl">';
450                         $rv.= '<span class="functionname"><abbr title="'.htmlentities($f['bind']['decl']).'">'.stripclass ($ns, $f['name']).'</abbr></span>';
451                         $rv.= format_args ($f['args']);
452                         $rv.= '</td><td class="fill"></td></tr>'.NL;
453                 }
454         }
455         if (isset ($cl['data'])) {
456                 usort ($cl['data'], 'name_sort_cb');
457                 $rv.= ' <tr><th colspan="3">Data Members</th></tr>'.NL;
458                 foreach ($cl['data'] as $f) {
459                         $rv.= ' <tr><td class="def">'.typelink (array_keys ($f['ret'])[0], false, 'em').'</td><td class="decl">';
460                         $rv.= '<span class="functionname">'.stripclass ($ns, $f['name']).'</span>';
461                         $rv.= '</td><td class="fill"></td></tr>'.NL;
462                 }
463         }
464         return $rv;
465 }
466
467
468 ################################################################################
469 # Start Output
470
471 ?><!DOCTYPE html>
472 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
473 <head>
474 <title>Ardour Lua Bindings</title>
475 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
476 <style type="text/css">
477 div.content        { max-width:60em; margin: 1em auto; }
478 h1                 { margin:2em 0 0 0; padding:0em; border-bottom: 1px solid black;}
479 h2.cls             { margin:2em 0 0 0; padding-left:1em; border: 1px dashed #6666ee;}
480 h2.cls abbr        { text-decoration:none; cursor:default;}
481 h3.cls             { margin:1em 0 0 0;}
482 h2.class           { background-color: #aaee66; }
483 h2.enum            { background-color: #aaaaaa; }
484 h2.pointerclass    { background-color: #eeaa66; }
485 h2.array           { background-color: #66aaee; }
486 h2.opaque          { background-color: #6666aa; }
487 p.cdecl            { text-align: right; float:right; font-size:90%; margin:0; padding: 0 0 0 1em;}
488 ul.classindex      { columns: 2; -webkit-columns: 2; -moz-columns: 2; }
489 div.clear          { clear:both; }
490 table.classmembers { width: 100%; }
491 table.classmembers th      { text-align:left; border-bottom:1px solid black; padding-top:1em; }
492 table.classmembers td.def  { text-align:right; padding-right:.5em;  white-space: nowrap;}
493 table.classmembers td.decl { text-align:left; padding-left:.5em; white-space: nowrap; }
494 table.classmembers td.fill { width: 99%;}
495 table.classmembers span.em { font-style: italic;}
496 span.functionname abbr     { text-decoration:none; cursor:default;}
497 div.header         {text-align:center;}
498 div.header h1      {margin:0;}
499 div.header p       {margin:.25em;}
500 </style>
501 </head>
502 <body>
503 <div class="header">
504 <h1>Ardour Lua Bindings</h1>
505 <p>
506 <a href="#h_classes">Class Documentation</a>
507 &nbsp;|&nbsp;
508 <a href="#h_enum">Enum/Constants</a>
509 &nbsp;|&nbsp;
510 <a href="#h_index">Index</a>
511 </p>
512 </div>
513 <div class="content">
514
515 <h1 id="h_intro">Overview</h1>
516 <p>
517 The top-level entry point are <?=typelink('ARDOUR:Session')?> and <?=typelink('ArdourUI:Editor')?>.
518 Most other Classes are used indirectly starting with a Session function. e.g. Session:get_routes().
519 </p>
520 <p>
521 A few classes are dedicated to certain script types, e.g. Lua DSP processors have exclusive access to
522 <?=typelink('ARDOUR:DSP')?> and <?=typelink('ARDOUR:ChanMapping')?>. Action Hooks Scripts to
523 <?=typelink('LuaSignal:Set')?> etc.
524 </p>
525 <p>
526 Detailed documentation (parameter names, method description) is not yet available. Please stay tuned.
527 </p>
528 <h2>Short introduction to Ardour classes</h2>
529 <p>
530 Ardour's structure is object oriented. The main object is the Session. A Session contains Audio Tracks, Midi Tracks and Busses.
531 Audio and Midi tracks are derived from a more general "Track" Object,  which in turn is derived from a "Route" (aka Bus).
532 (We say "An Audio Track <em>is-a</em> Track <em>is-a</em> Route").
533 Tracks contain specifics. For Example a track <em>has-a</em> diskstream (for file i/o).
534 </p>
535 <p>
536 Operations are performed on objects. One gets a reference to an object and then calls a method.
537 e.g   obj = Session:route_by_name("Audio")   obj:set_name("Guitar")
538 </p>
539 <p>
540 Object lifetimes are managed by the Session. Most Objects cannot be directly created, but one asks the Session to create or destroy them. This is mainly due to realtime constrains:
541 you cannot simply remove a track that is currently processing audio. There are various <em>factory</em> methods for object creation or removal.
542 </p>
543
544 <?php
545 echo '<h1 id="h_classes">Class Documentation</h1>'.NL;
546
547 foreach ($classlist as $ns => $cl) {
548         $dups = array ();
549         $tbl =  format_class_members ($ns, $cl, $dups);
550
551         # format class title
552         if (empty ($tbl)) {
553                 echo '<h2 id="'.htmlentities ($ns).'" class="cls opaque"><abbr title="Opaque Object">&empty;</abbr>&nbsp;'.htmlentities ($ns).'</h2>'.NL;
554         }
555         else if (isset ($classlist[$ns]['free'])) {
556                 echo '<h2 id="'.htmlentities ($ns).'" class="cls freeclass"><abbr title="Namespace">&Nopf;</abbr>&nbsp;'.ctorname($ns).'</h2>'.NL;
557         }
558         else if (isset ($classlist[$ns]['arr'])) {
559                 echo '<h2 id="'.htmlentities ($ns).'" class="cls array"><abbr title="C Array">&ctdot;</abbr>&nbsp;'.htmlentities ($ns).'</h2>'.NL;
560         }
561         else if (isset ($classlist[$ns]['ptr'])) {
562                 echo '<h2 id="'.htmlentities ($ns).'" class="cls pointerclass"><abbr title="Pointer Class">&Rarr;</abbr>&nbsp;'. htmlentities ($ns).'</h2>'.NL;
563         } else {
564                 echo '<h2 id="'.htmlentities ($ns).'" class="cls class"><abbr title="Class">&comp;</abbr>&nbsp;'.htmlentities ($ns).'</h2>'.NL;
565         }
566         if (isset ($cl['decl'])) {
567                 echo '<p class="cdecl"><em>C&#8225;</em>: '.htmlentities ($cl['decl']).'</p>'.NL;
568         }
569
570         # print class inheritance
571         $inherited = array ();
572         $isa = traverse_parent ($ns, $inherited);
573         if (!empty ($isa)) {
574                 echo ' <p>is-a: '.$isa.'</p>'.NL;
575         }
576         echo '<div class="clear"></div>'.NL;
577
578         # member documentation
579         if (empty ($tbl)) {
580                 echo '<p>This class object is only used indirectly as return-value and function-parameter.</p>'.NL;
581         } else {
582                 echo '<table class="classmembers">'.NL;
583                 echo $tbl;
584                 echo ' </table>'.NL;
585         }
586
587         # traverse parent classes (inherited members)
588         foreach ($inherited as $pns => $pcl) {
589                 $tbl = format_class_members ($pns, $pcl, $dups);
590                 if (!empty ($tbl)) {
591                         echo '<h3 class="cls">Inherited from '.$pns.'</h3>'.NL;
592                         echo '<table class="classmembers">'.NL;
593                         echo $tbl;
594                         echo '</table>'.NL;
595                 }
596         }
597 }
598
599 echo '<h1 id="h_enum">Enum/Constants</h1>'.NL;
600 foreach ($constlist as $ns => $cs) {
601         echo '<h2 id="'.ctorname ($ns).'" class="cls enum"><abbr title="Enum">&isin;</abbr>&nbsp;'.ctorname ($ns).'</h2>'.NL;
602         echo '<ul class="enum">'.NL;
603         foreach ($cs as $c) {
604                 echo '<li class="const">'.ctorname ($c['lua']).'</li>'.NL;
605         }
606         echo '</ul>'.NL;
607 }
608
609 echo '<h1 id="h_index" >Class Index</h1>'.NL;
610 echo '<ul class="classindex">'.NL;
611 foreach ($classlist as $ns => $cl) {
612         echo '<li>'.typelink($ns).'</li>'.NL;
613 }
614 echo '</ul>'.NL;
615
616 ?>
617 </div>
618 </body>
619 </html>