Tweak Lua-doc processing:
[ardour.git] / tools / fmt-luadoc.php
1 #!/usr/bin/php
2 <?php
3 ## USAGE
4 #
5 ## generate doc/luadoc.json.gz (lua binding doc)
6 # ./waf configure --luadoc ....
7 # ./waf
8 # ./gtk2_ardour/arluadoc > doc/luadoc.json.gz
9 #
10 ## generate doc/ardourapi.json.gz (ardour header doxygen doc)
11 # cd ../../tools/doxy2json
12 # ./ardourdoc.sh
13 # cd -
14 #
15 ## format HTML (using this scripterl)
16 # php tools/fmt-luadoc.php > /tmp/luadoc.html
17 #
18
19 $options = getopt("m");
20 if (isset ($options['m'])) {
21         $HTMLOUTPUT = false; ## set to false to output ardour-manual
22 } else {
23         $HTMLOUTPUT = true; ## set to false to output ardour-manual
24 }
25
26 ################################################################################
27 ################################################################################
28
29 $json = gzdecode (file_get_contents (dirname (__FILE__).'/../doc/luadoc.json.gz'));
30 $doc = array ();
31 $ardourversion = '';
32 foreach (json_decode ($json, true) as $b) {
33         if (!isset ($b['type'])) {
34                 if (isset ($b['version'])) { $ardourversion = $b['version']; }
35                 continue;
36         }
37         # reserved lua words
38         $b ['lua'] = preg_replace ('/:_end/', ':end', $b ['lua']);
39         $b ['lua'] = preg_replace ('/:_type/', ':type', $b ['lua']);
40         $b ['ldec'] = preg_replace ('/ const/', '', preg_replace ('/ const&/', '', $b['decl']));
41         $b ['ldec'] = preg_replace ('/_VampHost::/', '', $b['ldec']);
42         $b ['decl'] = preg_replace ('/_VampHost::/', '', $b['decl']);
43         if (isset ($b['ret'])) {
44                 $b['ret'] = preg_replace ('/ const/', '', preg_replace ('/ const&/', '', $b['ret']));
45                 $b['ret'] = preg_replace ('/_VampHost::/', '', $b['ret']);
46         }
47         if (isset ($b['parent'])) {
48                 $b ['parent'] = preg_replace ('/_VampHost::/', '', $b['parent']);
49         }
50         $doc[] = $b;
51 }
52
53 if (count ($doc) == 0) {
54         fwrite (STDERR, "Failed to read luadoc.json\n");
55         exit (EXIT_FAILURE);
56 }
57
58 ################################################################################
59 ## Global result variables
60 ################################################################################
61
62 $classlist = array ();
63 $constlist = array ();
64
65
66 ################################################################################
67 ## Pre-process the data, collect functions, parse arguments, cross reference
68 ################################################################################
69
70
71 ################################################################################
72 # some internal helper functions first
73
74 $funclist = array ();
75 $classes = array ();
76 $consts = array ();
77
78 function my_die ($msg) {
79         fwrite (STDERR, $msg."\n");
80         exit (1);
81 }
82
83 ##function ptr_strip ($ctype) {
84 #       # boost::shared_ptr<std::list<boost::shared_ptr<ARDOUR::Route>> > >
85 #       # -> std::list<ARDOUR::Route>
86 #       $ctype = preg_replace ('/boost::shared_ptr<([^>]*)[ ]*>/', '$1', $ctype);
87 #       return preg_replace ('/boost::shared_ptr<([^>]*)[ ]*>/', '$1', $ctype);
88 #}
89
90 function arg2lua ($argtype, $flags = 0) {
91         global $classes;
92         global $consts;
93
94         # LuaBridge abstracts C++ references
95         $flags |= preg_match ('/&$/', $argtype);
96         $arg = preg_replace ('/&$/', '', $argtype);
97         $arg = preg_replace ('/ $/', '', $arg);
98
99         # filter out basic types
100         $builtin = array ('float', 'double', 'bool', 'std::string', 'int', 'short', 'long', 'unsigned int', 'unsigned short', 'unsigned long', 'unsigned char', 'char', 'void', 'char*', 'unsigned char*', 'void*');
101         if (in_array ($arg, $builtin)) {
102                 return array ($arg => $flags);
103         }
104
105         if ($arg == 'luabridge::LuaRef') {
106                 return array ('Lua-Function' => $flags | 4);
107         }
108         if ($arg == 'LTC_TV_STANDARD') {
109                 $arg = 'ARDOUR::DSP::LTC_TV_STANDARD';
110         }
111
112         # check Class declarations first
113         foreach (array_merge ($classes, $consts) as $b) {
114                 if ($b['ldec'] == $arg) {
115                         return array ($b['lua'] => $flags);
116                 }
117         }
118
119         # strip class pointers -- TODO Check C'tor for given class
120         $arg = preg_replace ('/[&*]*$/', '', $argtype);
121         foreach (array_merge ($classes, $consts) as $b) {
122                 if ($b['ldec'] == $arg) {
123                         return array ($b['lua'] => $flags);
124                 }
125         }
126         if ($flags & 2) {
127                 return array ($argtype => ($flags | 4));
128         } else {
129                 return array ('--MISSING (' . $argtype . ')--' => ($flags | 4));
130         }
131 }
132
133 function stripclass ($classname, $name) {
134         $classname .= ':';
135         if (strpos ($name, $classname) !== 0) {
136                 my_die ('invalid class prefix: ' .$classname. ' -- '. $name);
137         }
138         return substr ($name, strlen ($classname));
139 }
140
141 function datatype ($decl) {
142         # TODO handle spaces in type. Works because
143         # we don't yet have templated types (with_space <here >)
144         return substr ($decl, 0, strrpos ($decl, ' '));
145 }
146
147 function luafn2class ($lua) {
148         return substr ($lua, 0, strrpos ($lua, ':'));
149 }
150
151 function luafn2name ($lua) {
152         $fn = strrpos ($lua, ':');
153         if ($fn !== 0 && strlen($lua) > $fn + 1) {
154                 return substr ($lua, $fn + 1);
155         }
156         my_die ('invalid class prefix: '. $name);
157 }
158
159
160 function checkclass ($b) {
161         global $classlist;
162         if (!isset ($classlist[luafn2class ($b['lua'])])) {
163                 my_die ('MISSING CLASS FOR '. print_r ($b['lua'], true));
164         }
165 }
166
167 # parse functions argument list to lua-names
168 function decl2args ($decl) {
169         $start = strrpos ($decl, '(');
170         $end = strrpos ($decl, ')');
171         $args = substr ($decl, $start + 1, $end - $start - 1);
172         $arglist = preg_split ('/, */', $args);
173         $rv = array ();
174         foreach ($arglist as $a) {
175                 if (empty ($a)) { continue; }
176                 $rv[] = arg2lua ($a);
177         }
178         return $rv;
179 }
180
181 function canonical_ctor ($b) {
182         $rv = '';
183         if (preg_match('/[^(]*\(([^)*]*)\*\)(\(.*\))/', $b['decl'], $matches)) {
184                 $lc = luafn2class ($b['lua']);
185                 $cn = str_replace (':', '::', $lc);
186                 $fn = substr ($lc, 1 + strrpos ($lc, ':'));
187                 $rv = $cn . '::'. $fn . $matches[2];
188         }
189         return $rv;
190 }
191
192 function canonical_decl ($b) {
193         $rv = '';
194         $pfx = '';
195         # match clang's declatation format
196         if (preg_match('/[^(]*\(([^)*]*)\*\)\((.*)\)/', $b['decl'], $matches)) {
197                 if (strpos ($b['type'], 'Free Function') !== false) {
198                         $pfx = str_replace (':', '::', luafn2class ($b['lua'])) . '::';
199                 }
200                 $fn = substr ($b['lua'], 1 + strrpos ($b['lua'], ':'));
201                 $rv = $matches[1] . $fn . '(';
202                 $arglist = preg_split ('/, */', $matches[2]);
203                 $first = true;
204                 foreach ($arglist as $a) {
205                         if (!$first) { $rv .= ', '; }; $first = false;
206                         if (empty ($a)) { continue; }
207                         $a = preg_replace ('/([^>]) >/', '$1>', $a);
208                         $a = preg_replace ('/^Cairo::/', '', $a); // special case cairo enums
209                         $a = preg_replace ('/([^ ])&/', '$1 &', $a);
210                         $a = preg_replace ('/std::vector<([^>]*)> const/', 'const std::vector<$1>', $a);
211                         $a = str_replace ('std::vector', 'vector', $a);
212                         $a = str_replace ('vector', 'std::vector', $a);
213                         $a = str_replace ('std::string', 'string', $a);
214                         $a = str_replace ('string const', 'const string', $a);
215                         $a = str_replace ('string', 'std::string', $a);
216                         $rv .= $a;
217                 }
218                 $rv .= ')';
219         }
220         return $pfx . $rv;
221 }
222
223 ################################################################################
224 # step 1: build class indices
225
226 foreach ($doc as $b) {
227         if (strpos ($b['type'], "[C] ") === 0) {
228                 $classes[] = $b;
229                 $classlist[$b['lua']] = $b;
230                 if (strpos ($b['type'], 'Pointer Class') === false) {
231                         $classdecl[$b['ldec']] = $b;
232                 }
233         }
234 }
235
236 foreach ($classes as $c) {
237         if (strpos ($c['type'], 'Pointer Class') !== false) { continue; }
238         if (isset ($c['parent'])) {
239                 if (isset ($classdecl[$c['parent']])) {
240                         $classlist[$c['lua']]['luaparent'][] = $classdecl[$c['parent']]['lua'];
241                 } else {
242                         my_die ('unknown parent class: ' . print_r ($c, true));
243                 }
244         }
245 }
246
247 # step 2: extract constants/enum
248 foreach ($doc as $b) {
249         switch ($b['type']) {
250         case "Constant/Enum":
251         case "Constant/Enum Member":
252                 if (strpos ($b['ldec'], '::') === false) {
253                         # for extern c enums, use the Lua Namespace
254                         $b['ldec'] = str_replace (':', '::', luafn2class ($b['lua']));
255                 }
256                 $ns = str_replace ('::', ':', $b['ldec']);
257                 $constlist[$ns][] = $b;
258                 # arg2lua lookup
259                 $b['lua'] = $ns;
260                 $consts[] = $b;
261                 break;
262         default:
263                 break;
264         }
265 }
266
267 # step 3: process functions
268 foreach ($doc as $b) {
269         switch ($b['type']) {
270         case "Constructor":
271         case "Weak/Shared Pointer Constructor":
272                 checkclass ($b);
273                 $classlist[luafn2class ($b['lua'])]['ctor'][] = array (
274                         'name' => luafn2class ($b['lua']),
275                         'args' => decl2args ($b['ldec']),
276                         'cand' => canonical_ctor ($b),
277                         'nil' => false
278                 );
279                 break;
280         case "Weak/Shared Pointer NIL Constructor":
281                 checkclass ($b);
282                 $classlist[luafn2class ($b['lua'])]['ctor'][] = array (
283                         'name' => luafn2class ($b['lua']),
284                         'args' => decl2args ($b['ldec']),
285                         'cand' => canonical_ctor ($b),
286                         'nil' => true
287                 );
288                 break;
289         case "Property":
290                 checkclass ($b);
291                 $classlist[luafn2class ($b['lua'])]['props'][] = array (
292                         'name' => $b['lua'],
293                         'ret'  => arg2lua (datatype ($b['ldec']))
294                 );
295                 break;
296         case "Data Member":
297                 checkclass ($b);
298                 $classlist[luafn2class ($b['lua'])]['data'][] = array (
299                         'name' => $b['lua'],
300                         'ret'  => arg2lua (datatype ($b['ldec']))
301                 );
302                 break;
303         case "Static C Function":
304                 checkclass ($b);
305                 if (strpos ($b['lua'], 'ARDOUR:DataType:') === 0) {
306                         # special case ARDOUR:DataType convenience c'tor
307                         $args = array ();
308                         $ret = array (luafn2class ($b['lua']) => 0);
309                         $canon = 'ARDOUR::LuaAPI::datatype_ctor_'.strtolower (luafn2name ($b['lua'])).'(lua_State*)';
310                 } else {
311                         my_die ('unhandled Static C: ' . print_r($b, true));
312                 }
313                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
314                         'bind' => $b,
315                         'name' => $b['lua'],
316                         'args' => $args,
317                         'ret'  => $ret,
318                         'ref'  => false,
319                         'ext'  => false,
320                         'cand' => $canon
321                 );
322                 break;
323         case "C Function":
324                 # we required C functions to be in a class namespace
325         case "Ext C Function":
326                 checkclass ($b);
327                 $args = array (array ('--lua--' => 0));
328                 $ret = array ('...' => 0);
329                 $ns = luafn2class ($b['lua']);
330                 $cls = $classlist[$ns];
331                 if (preg_match ('/.*<([^>]*)[ ]*>/', $cls['ldec'], $templ)) {
332                         # std::vector, std::list types
333                         switch (stripclass($ns, $b['lua'])) {
334                         case 'add':
335                                 #$args = array (array ('LuaTable {'.$templ[1].'}' => 0));
336                                 $args = array (arg2lua ($templ[1], 2));
337                                 $ret = array ('LuaTable' => 0);
338                                 break;
339                         case 'iter':
340                                 $args = array ();
341                                 $ret = array ('LuaIter' => 0);
342                                 break;
343                         case 'table':
344                                 $args = array ();
345                                 $ret = array ('LuaTable' => 0);
346                                 break;
347                         default:
348                                 break;
349                         }
350                 } else if (strpos ($cls['type'], ' Array') !== false) {
351                         # catches  C:FloatArray, C:IntArray
352                         $templ = preg_replace ('/[&*]*$/', '', $cls['ldec']);
353                         switch (stripclass($ns, $b['lua'])) {
354                         case 'array':
355                                 $args = array ();
356                                 $ret = array ('LuaMetaTable' => 0);
357                                 break;
358                         case 'get_table':
359                                 $args = array ();
360                                 $ret = array ('LuaTable' => 0);
361                                 break;
362                         case 'set_table':
363                                 $args = array (array ('LuaTable {'.$templ.'}' => 0));
364                                 $ret = array ('void' => 0);
365                                 break;
366                         default:
367                                 break;
368                         }
369                 }
370                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
371                         'bind' => $b,
372                         'name' => $b['lua'],
373                         'args' => $args,
374                         'ret'  => $ret,
375                         'ref'  => true,
376                         'ext'  => true,
377                         'cand' => canonical_decl ($b)
378                 );
379                 break;
380         case "Free C Function":
381                 $funclist[luafn2class ($b['lua'])][] = array (
382                         'bind' => $b,
383                         'name' => $b['lua'],
384                         'args' => $args,
385                         'ret'  => $ret,
386                         'ref'  => false,
387                         'ext'  => true,
388                         'cand' => str_replace (':', '::', $b['lua']).'(lua_State*)'
389                 );
390                 break;
391         case "Free Function":
392         case "Free Function RefReturn":
393                 $funclist[luafn2class ($b['lua'])][] = array (
394                         'bind' => $b,
395                         'name' => $b['lua'],
396                         'args' => decl2args ($b['ldec']),
397                         'ret'  => arg2lua ($b['ret']),
398                         'ref'  => (strpos ($b['type'], "RefReturn") !== false),
399                         'cand' => canonical_decl ($b)
400                 );
401                 break;
402         case "Member Function":
403         case "Member Function RefReturn":
404         case "Member Pointer Function":
405         case "Weak/Shared Pointer Function":
406         case "Weak/Shared Pointer Function RefReturn":
407         case "Weak/Shared Null Check":
408         case "Static Member Function":
409                 checkclass ($b);
410                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
411                         'bind' => $b,
412                         'name' => $b['lua'],
413                         'args' => decl2args ($b['ldec']),
414                         'ret'  => arg2lua ($b['ret']),
415                         'ref'  => (strpos ($b['type'], "RefReturn") !== false),
416                         'cand' => canonical_decl ($b)
417                 );
418                 break;
419         case "Cast":
420         case "Weak/Shared Pointer Cast":
421                 checkclass ($b);
422                 $classlist[luafn2class ($b['lua'])]['cast'][] = array (
423                         'bind' => $b,
424                         'name' => $b['lua'],
425                         'args' => decl2args ($b['ldec']),
426                         'ret'  => arg2lua ($b['ret']),
427                         'ref'  => (strpos ($b['type'], "RefReturn") !== false),
428                         'cand' => canonical_decl ($b)
429                 );
430                 break;
431         case "Constant/Enum":
432         case "Constant/Enum Member":
433                 # already handled -> $consts
434                 break;
435         default:
436                 if (strpos ($b['type'], "[C] ") !== 0) {
437                         my_die ('unhandled type: ' . $b['type']);
438                 }
439                 break;
440         }
441 }
442
443
444 # step 4: collect/group/sort
445
446 # step 4a: unify weak/shared Ptr classes
447 foreach ($classlist as $ns => $cl) {
448         if (strpos ($cl['type'], ' Array') !== false) {
449                 $classlist[$ns]['arr'] = true;
450                 $classlist[$ns]['cdecl'] = $cl['decl'];
451                 continue;
452         }
453         foreach ($classes as $c) {
454                 if ($c['lua'] == $ns) {
455                         if (strpos ($c['type'], 'Pointer Class') !== false) {
456                                 $classlist[$ns]['ptr'] = true;
457                                 $classlist[$ns]['cdecl'] = 'boost::shared_ptr< '.$c['decl']. ' >, boost::weak_ptr< '.$c['decl']. ' >';
458                                 break;
459                         } else {
460                                 $classlist[$ns]['cdecl'] = $c['decl'];
461                         }
462                 }
463         }
464 }
465
466 # step4b: sanity check
467 foreach ($classlist as $ns => $cl) {
468         if (isset ($classes[$ns]['parent']) && !isset ($classlist[$ns]['luaparent'])) {
469                 my_die ('missing parent class: ' . print_r ($cl, true));
470         }
471 }
472
473 # step 4c: merge free functions into classlist
474 foreach ($funclist as $ns => $fl) {
475         if (isset ($classlist[$ns])) {
476                 my_die ('Free Funcion in existing namespace: '.$ns.' '. print_r ($ns, true));
477         }
478         $classlist[$ns]['func'] = $fl;
479         $classlist[$ns]['free'] = true;
480 }
481
482 # step 4d: order to chaos
483 # no array_multisort() here, sub-types are sorted after merging parents
484 ksort ($classlist);
485
486
487 ################################################################################
488 ################################################################################
489 ################################################################################
490
491
492 #### -- split here --  ####
493
494 # from here on, only $classlist and $constlist arrays are relevant.
495 # we also pull in C++ header annotation from doxygen to $api
496
497
498 # read documentation from doxygen
499 $json = gzdecode (file_get_contents (dirname (__FILE__).'/../doc/ardourapi.json.gz'));
500 $api = array ();
501 foreach (json_decode ($json, true) as $a) {
502         if (!isset ($a['decl'])) { continue; }
503         if (empty ($a['decl'])) { continue; }
504         $canon = str_replace (' *', '*', $a['decl']);
505         $api[$canon] = $a;
506 }
507
508 # keep track of found/missing doc
509 $dox_found = 0;
510 $dox_miss = 0;
511
512 # retrive a value from $api
513 function doxydoc ($canonical_declaration) {
514         global $api;
515         global $dox_found;
516         global $dox_miss;
517         if (isset ($api[$canonical_declaration])) {
518                 $dox_found++;
519                 return $api[$canonical_declaration]['doc'];
520         }
521         // remove template namespace e.g.
522         //  "ARDOUR::Track::bounceable(boost::shared_ptr<ARDOUR::Processor>"
523         //  "ARDOUR::Track::bounceable(boost::shared_ptr<Processor>"
524         $cn = preg_replace ('/<[^>]*::([^>]*)>/', '<$1>', $canonical_declaration);
525         if (isset ($api[$cn])) {
526                 $dox_found++;
527                 return $api[$cn]['doc'];
528         }
529         #fwrite (STDERR, $canonical_declaration."\n"); # XXX DEBUG
530
531         $dox_miss++;
532         return '';
533 }
534
535 ################################################################################
536 # OUTPUT
537 ################################################################################
538
539
540 ################################################################################
541 # Helper functions
542 define ('NL', "\n");
543
544 # constructors, enums (constants) use a dot.  (e.g. "LuaOSC.Address" -> "LuaOSC.Address" )
545 function ctorname ($name) {
546         return htmlentities (str_replace (':', '.', $name));
547 }
548
549 # strip class prefix (e.g "Evoral:MidiEvent:channel"  -> "channel")
550 function shortname ($name) {
551         return htmlentities (substr ($name, strrpos ($name, ':') + 1));
552 }
553
554 # retrieve variable name from    array["VARNAME"] => FLAGS
555 function varname ($a) {
556         return array_keys ($a)[0];
557 }
558
559 # recusively collect class parents (derived classes)
560 function traverse_parent ($ns, &$inherited) {
561         global $classlist;
562         $rv = '';
563         if (isset ($classlist[$ns]['luaparent'])) {
564                 $parents = array_unique ($classlist[$ns]['luaparent']);
565                 asort ($parents);
566                 foreach ($parents as $p) {
567                         if (!empty ($rv)) { $rv .= ', '; }
568                         if ($p == $ns) { continue; }
569                         $rv .= typelink ($p);
570                         $inherited[$p] = $classlist[$p];
571                         traverse_parent ($p, $inherited);
572                 }
573         }
574         return $rv;
575 }
576
577 # create a cross-reference to a type (class or enum)
578 # *all* <a> links are generated here, currently anchors on a single page.
579 function typelink ($a, $short = false, $argcls = '', $linkcls = '', $suffix = '') {
580         global $classlist;
581         global $constlist;
582         if (isset($classlist[$a]['free'])) {
583                 return '<a class="'.$linkcls.'" href="#'.htmlentities ($a).'">'.($short ? shortname($a) : ctorname($a)).$suffix.'</a>';
584         } else if (in_array ($a, array_keys ($classlist))) {
585                 return '<a class="'.$linkcls.'" href="#'.htmlentities($a).'">'.($short ? shortname($a) : htmlentities($a)).$suffix.'</a>';
586         } else if (in_array ($a, array_keys ($constlist))) {
587                 return '<a class="'.$linkcls.'" href="#'.ctorname ($a).'">'.($short ? shortname($a) : ctorname($a)).$suffix.'</a>';
588         } else {
589                 return '<span class="'.$argcls.'">'.htmlentities($a).$suffix.'</span>';
590         }
591 }
592
593 # output format function arguments
594 function format_args ($args) {
595         $rv = '<span class="functionargs"> (';
596         $first = true;
597         foreach ($args as $a) {
598                 if (!$first) { $rv .= ', '; }; $first = false;
599                 $flags = $a[varname ($a)];
600                 if ($flags & 4) {
601                         $rv .= '<span>'.varname ($a).'</span>';
602                 }
603                 else if ($flags & 2) {
604                         $rv .= '<em>LuaTable</em> {'.typelink (varname ($a), true, 'em').'}';
605                 }
606                 elseif ($flags & 1) {
607                         $rv .= typelink (varname ($a), true, 'em', '', '&amp;');
608                 }
609                 else {
610                         $rv .= typelink (varname ($a), true, 'em');
611                 }
612         }
613         $rv .= ')</span>';
614         return $rv;
615 }
616
617 # format doxygen documentation for class-definition
618 function format_doxyclass ($cl) {
619         $rv = '';
620         if (isset ($cl['decl'])) {
621                 $doc = doxydoc ($cl['decl']);
622                 if (!empty ($doc)) {
623                         $rv.= '<div class="classdox">'.$doc.'</div>'.NL;
624                 }
625         }
626         return $rv;
627 }
628
629 # format doxygen documentation for class-members
630 function format_doxydoc ($f) {
631         $rv = '';
632         if (isset ($f['cand'])) {
633                 $doc = doxydoc ($f['cand']);
634                 if (!empty ($doc)) {
635                         $rv.= '<tr><td></td><td class="doc" colspan="2"><div class="dox">'.$doc;
636                         $rv.= '</div></td></tr>'.NL;
637                 } else if (0) { # debug
638                         $rv.= '<tr><td></td><td class="doc" colspan="2"><p>'.htmlentities($f['cand']).'</p>';
639                         $rv.= '</td></tr>'.NL;
640                 }
641         }
642         return $rv;
643 }
644
645 # usort() callback for class-members
646 function name_sort_cb ($a, $b) {
647         return strcmp ($a['name'], $b['name']);
648 }
649
650 # main output function for every class
651 function format_class_members ($ns, $cl, &$dups) {
652         $rv = '';
653         # print contructor - if any
654         if (isset ($cl['ctor'])) {
655                 usort ($cl['ctor'], 'name_sort_cb');
656                 $rv.= ' <tr><th colspan="3">Constructor</th></tr>'.NL;
657                 foreach ($cl['ctor'] as $f) {
658                         $rv.= ' <tr>';
659                         if ($f['nil']) {
660                                 $rv.= '<td class="def"><abbr title="Nil Pointer Constructor">&alefsym;</abbr></td>';
661                         } else {
662                                 $rv.= '<td class="def">&Copf;</td>';
663                         }
664                         $rv.= '<td class="decl">';
665                         $rv.= '<span class="functionname">'.ctorname ($f['name']).'</span>';
666                         $rv.= format_args ($f['args']);
667                         $rv.= '</td><td class="fill"></td></tr>'.NL;
668                         # doxygen documentation (may be empty)
669                         $rv.= format_doxydoc($f);
670                 }
671         }
672
673         # strip duplicates (inherited or derived methods)
674         # e.g  AudioTrack -> Track -> Route -> SessionObject -> Stateful
675         # all 5 have "isnil()"
676         $nondups = array ();
677         if (isset ($cl['func'])) {
678                 foreach ($cl['func'] as $f) {
679                         if (in_array (stripclass ($ns, $f['name']), $dups)) { continue; }
680                         $nondups[] = $f;
681                 }
682         }
683
684         # print methods - if any
685         if (count ($nondups) > 0) {
686                 usort ($nondups, 'name_sort_cb');
687                 $rv.= ' <tr><th colspan="3">Methods</th></tr>'.NL;
688                 foreach ($nondups as $f) {
689                         $dups[] = stripclass ($ns, $f['name']);
690                         # return value/type
691                         $rv.= ' <tr><td class="def">';
692                         if ($f['ref'] && isset ($f['ext'])) {
693                                 # external C functions
694                                 $rv.= '<em>'.varname ($f['ret']).'</em>';
695                         } elseif ($f['ref'] && varname ($f['ret']) == 'void') {
696                                 # void functions with reference args
697                                 $rv.= '<em>LuaTable</em>(...)';
698                         } elseif ($f['ref']) {
699                                 # functions with reference args and return value
700                                 $rv.= '<em>LuaTable</em>('.typelink (varname ($f['ret']), true, 'em').', ...)';
701                         } else {
702                                 # normal class members
703                                 $rv.= typelink (varname ($f['ret']), true, 'em');
704                         }
705                         # function declaration and arguments
706                         $rv.= '</td><td class="decl">';
707                         $rv.= '<span class="functionname"><abbr title="'.htmlentities($f['bind']['decl']).'">'.stripclass ($ns, $f['name']).'</abbr></span>';
708                         $rv.= format_args ($f['args']);
709                         $rv.= '</td><td class="fill"></td></tr>'.NL;
710                         # doxygen documentation (may be empty)
711                         $rv.= format_doxydoc($f);
712                 }
713         }
714         # print cast - if any
715         if (isset ($cl['cast'])) {
716                 usort ($cl['cast'], 'name_sort_cb');
717                 $rv.= ' <tr><th colspan="3">Cast</th></tr>'.NL;
718                 foreach ($cl['cast'] as $f) {
719                         $rv.= ' <tr><td class="def">';
720                         $rv.= typelink (varname ($f['ret']), true, 'em');
721                         # function declaration and arguments
722                         $rv.= '</td><td class="decl">';
723                         $rv.= '<span class="functionname"><abbr title="'.htmlentities($f['bind']['decl']).'">'.stripclass ($ns, $f['name']).'</abbr></span>';
724                         $rv.= format_args ($f['args']);
725                         $rv.= '</td><td class="fill"></td></tr>'.NL;
726                         # doxygen documentation (may be empty)
727                         $rv.= format_doxydoc($f);
728                 }
729         }
730
731         # print properties - if any
732         if (isset ($cl['props'])) {
733                 usort ($cl['props'], 'name_sort_cb');
734                 $rv.= ' <tr><th colspan="3">Properties</th></tr>'.NL;
735                 foreach ($cl['props'] as $f) {
736                         $rv.= ' <tr><td class="def">'.typelink (array_keys ($f['ret'])[0], false, 'em').'</td><td class="decl">';
737                         $rv.= '<span class="functionname">'.stripclass ($ns, $f['name']).'</span>';
738                         $rv.= '</td><td class="fill"></td></tr>'.NL;
739                 }
740         }
741
742         # print data members - if any
743         if (isset ($cl['data'])) {
744                 usort ($cl['data'], 'name_sort_cb');
745                 $rv.= ' <tr><th colspan="3">Data Members</th></tr>'.NL;
746                 foreach ($cl['data'] as $f) {
747                         $rv.= ' <tr><td class="def">'.typelink (array_keys ($f['ret'])[0], false, 'em').'</td><td class="decl">';
748                         $rv.= '<span class="functionname">'.stripclass ($ns, $f['name']).'</span>';
749                         $rv.= '</td><td class="fill"></td></tr>'.NL;
750                         $f['cand'] = str_replace (':', '::', $f['name']);
751                         $rv.= format_doxydoc($f);
752                 }
753         }
754         return $rv;
755 }
756
757
758 ################################################################################
759 # Start Output
760
761 if ($HTMLOUTPUT) {
762
763 ?><!DOCTYPE html>
764 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
765 <head>
766 <title>Ardour Lua Bindings</title>
767 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
768 <style type="text/css">
769 div.header         { text-align:center; }
770 div.header h2      { margin:0; }
771 div.header p       { margin:.25em; text-align:center; }
772 div.luafooter      { text-align:center; font-size:80%; color: #888; margin: 2em 0; }
773 #luaref            { max-width:60em; margin: 1em auto; }
774
775 #luaref h2                 { margin:2em 0 0 0; padding:0em; border-bottom: 1px solid black; }
776 #luaref h3.cls             { margin:2em 0 0 0; padding: 0 0 0 1em; border: 1px dashed #6666ee; }
777 #luaref h3.cls abbr        { text-decoration:none; cursor:default; }
778 #luaref h4.cls             { margin:1em 0 0 0; }
779 #luaref h3.class           { background-color: #aaee66; }
780 #luaref h3.enum            { background-color: #aaaaaa; }
781 #luaref h3.pointerclass    { background-color: #eeaa66; }
782 #luaref h3.array           { background-color: #66aaee; }
783 #luaref h3.opaque          { background-color: #6666aa; }
784 #luaref p                  { text-align: justify; }
785 #luaref p.cdecl            { text-align: right; float:right; font-size:90%; margin:0; padding: 0 0 0 1em; }
786 #luaref ul.classindex      { columns: 2; -webkit-columns: 2; -moz-columns: 2; }
787 #luaref div.clear          { clear:both; }
788 #luaref p.classinfo        { margin: .25em 0; }
789 #luaref div.code           { width:80%; margin:.5em auto; }
790 #luaref div.code div       { width:45%; }
791 #luaref div.code pre       { line-height: 1.2em; margin: .25em 0; }
792 #luaref div.code samp      { color: green; font-weight: bold; background-color: #eee; }
793 #luaref div.classdox       { padding: .1em 1em; }
794 #luaref div.classdox p     { margin: .5em 0 .5em .6em; }
795 #luaref div.classdox p     { margin: .5em 0 .5em .6em; }
796 #luaref div.classdox       { padding: .1em 1em; }
797 #luaref div.classdox p     { margin: .5em 0 .5em .6em; }
798 #luaref table.classmembers { width: 100%; }
799 #luaref table.classmembers th      { text-align:left; border-bottom:1px solid black; padding-top:1em; }
800 #luaref table.classmembers td.def  { text-align:right; padding-right:.5em;  white-space: nowrap; }
801 #luaref table.classmembers td.decl { text-align:left; padding-left:.5em; white-space: nowrap; }
802 #luaref table.classmembers td.doc  { text-align:left; padding-left:.6em; line-height: 1.2em; font-size:80%; }
803 #luaref table.classmembers td.doc div.dox {background-color:#eee; padding: .1em 1em; }
804 #luaref table.classmembers td.doc p { margin: .5em 0; }
805 #luaref table.classmembers td.doc p.para-brief { font-size:120%; }
806 #luaref table.classmembers td.doc p.para-returns { font-size:120%; }
807 #luaref table.classmembers td.doc dl { font-size:120%; line-height: 1.3em; }
808 #luaref table.classmembers td.doc dt { font-style: italic; }
809 #luaref table.classmembers td.fill { width: 99%; }
810 #luaref table.classmembers span.em { font-style: italic; }
811 #luaref span.functionname abbr     { text-decoration:none; cursor:default; }
812 #luaref table.classmembers td.def abbr { text-decoration:none; cursor:default; }
813 </style>
814 </head>
815 <body>
816 <div class="header">
817 <h2>Ardour Lua Bindings</h2>
818 <p>
819 <a href="#h_classes">Class Documentation</a>
820 &nbsp;|&nbsp;
821 <a href="#h_enum">Enum/Constants</a>
822 &nbsp;|&nbsp;
823 <a href="#h_index">Index</a>
824 </p>
825 </div>
826
827 <!-- #### SNIP #### !-->
828
829 <?php
830
831 } else {
832
833 ?>
834
835 <p class="warning">
836 This documentation is far from complete may be inaccurate and subject to change.
837 </p>
838
839 <?php
840 }
841 ?>
842
843 <div id="luaref">
844
845 <?php
846
847 ################################################################################
848 # some general documentation -- should really go elsehere
849
850 ?>
851
852 <h2 id="h_intro">Overview</h2>
853 <p>
854 The top-level entry point are <?=typelink('ARDOUR:Session')?> and <?=typelink('ArdourUI:Editor')?>.
855 Most other Classes are used indirectly starting with a Session function. e.g. Session:get_routes().
856 </p>
857 <p>
858 A few classes are dedicated to certain script types, e.g. Lua DSP processors have exclusive access to
859 <?=typelink('ARDOUR:DSP')?> and <?=typelink('ARDOUR:ChanMapping')?>. Action Hooks Scripts to
860 <?=typelink('LuaSignal:Set')?> etc.
861 </p>
862 <p>
863 Detailed documentation (parameter names, method description) is not yet available. Please stay tuned.
864 </p>
865 <h3>Short introduction to Ardour classes</h3>
866 <p>
867 Ardour's structure is object oriented. The main object is the Session. A Session contains Audio Tracks, Midi Tracks and Busses.
868 Audio and Midi tracks are derived from a more general "Track" Object,  which in turn is derived from a "Route" (aka Bus).
869 (We say "An Audio Track <em>is-a</em> Track <em>is-a</em> Route").
870 Tracks contain specifics. For Example a track <em>has-a</em> diskstream (for file i/o).
871 </p>
872 <p>
873 Operations are performed on objects. One gets a reference to an object and then calls a method.
874 e.g <code>obj = Session:route_by_name("Audio")   obj:set_name("Guitar")</code>.
875 </p>
876 <p>
877 Lua automatically follows C++ class inheritance. e.g one can directly call all SessionObject and Route methods on Track object. However lua does not automatically promote objects. A Route object which just happens to be a Track needs to be explicitly cast to a Track. Methods for casts are provided with each class. Note that the cast may fail and return a <em>nil</em> reference.
878 </p>
879 <p>
880 Likewise multiple inheritance is a <a href="http://www.lua.org/pil/16.3.html">non-trivial issue</a> in Lua. To avoid performance penalties involved with lookups, explicit casts are required in this case. One example is <?=typelink('ARDOUR:SessionObject')?> which is-a StatefulDestructible which inherits from both Stateful and Destructible.
881 </p>
882 <p>
883 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:
884 you cannot simply remove a track that is currently processing audio. There are various <em>factory</em> methods for object creation or removal.
885 </p>
886 <h3>Pass by Reference</h3>
887 <p>
888 Since Lua functions are closures, C++ methods that pass arguments by reference cannot be used as-is.
889 All parameters passed to a C++ method which uses references are returned as Lua Table.
890 If the C++ method also returns a value it is prefixed. Two parameters are returned: the value and a Lua Table holding the parameters.
891 </p>
892
893 <div class="code">
894         <div style="float:left;">C++
895
896 <pre><code class="cxx">void set_ref (int&amp; var, long&amp; val)
897 {
898         printf ("%d %ld\n", var, val);
899         var = 5;
900         val = 7;
901 }
902 </code></pre>
903
904         </div>
905         <div style="float:right;">Lua
906
907 <pre><code class="lua">local var = 0;
908 ref = set_ref (var, 2);
909 -- output from C++ printf()
910 </code><samp class="lua">0 2</samp><code>
911 -- var is still 0 here
912 print (ref[1], ref[2])
913 </code><samp class="lua">5 7</samp></pre>
914
915         </div>
916 </div>
917 <div class="clear"></div>
918 <div class="code">
919         <div style="float:left;">
920
921 <pre><code class="cxx">int set_ref2 (int &amp;var, std::string unused)
922 {
923         var = 5;
924         return 3;
925 }
926 </code></pre>
927
928         </div>
929         <div style="float:right;">
930 <pre><code class="lua">rv, ref = set_ref2 (0, "hello");
931 print (rv, ref[1], ref[2])
932 </code><samp class="lua">3 5 hello</samp></pre>
933         </div>
934 </div>
935 <div class="clear"></div>
936
937 <h3>Pointer Classes</h3>
938 <p>
939 Libardour makes extensive use of reference counted <code>boost::shared_ptr</code> to manage lifetimes.
940 The Lua bindings provide a complete abstraction of this. There are no pointers in Lua.
941 For example a <?=typelink('ARDOUR:Route')?> is a pointer in C++, but Lua functions operate on it like it was a class instance.
942 </p>
943 <p>
944 <code>shared_ptr</code> are reference counted. Once assigned to a Lua variable, the C++ object will be kept and remains valid.
945 It is good practice to assign references to Lua <code>local</code> variables or reset the variable to <code>nil</code> to drop the ref.
946 </p>
947 <p>
948 All pointer classes have a <code>isnil ()</code> method. This is for two cases:
949 Construction may fail. e.g. <code><?=typelink('ARDOUR:LuaAPI')?>.newplugin()</code>
950 may not be able to find the given plugin and hence cannot create an object.
951 </p>
952 <p>
953 The second case if for <code>boost::weak_ptr</code>. As opposed to <code>boost::shared_ptr</code> weak-pointers are not reference counted.
954 The object may vanish at any time.
955 If Lua code calls a method on a nil object, the interpreter will raise an exception and the script will not continue.
956 This is not unlike <code>a = nil a:test()</code> which results in en error "<em>attempt to index a nil value</em>".
957 </p>
958 <p>
959 From the Lua side of things there is no distinction between weak and shared pointers. They behave identically.
960 Below they're indicated in orange and have an arrow to indicate the pointer type.
961 Pointer Classes cannot be created in Lua scripts. It always requires a call to C++ to create the Object and obtain a reference to it.
962 </p>
963
964
965 <?php
966
967 #################################
968 # Main output function -- Classes
969
970 echo '<h2 id="h_classes">Class Documentation</h2>'.NL;
971 foreach ($classlist as $ns => $cl) {
972         $dups = array ();
973         $tbl =  format_class_members ($ns, $cl, $dups);
974
975         # format class title - depending on type
976         if (empty ($tbl)) {
977                 # classes with no members (no ctor, no methods, no data)
978                 echo '<h3 id="'.htmlentities ($ns).'" class="cls opaque"><abbr title="Opaque Object">&empty;</abbr>&nbsp;'.htmlentities ($ns).'</h3>'.NL;
979         }
980         else if (isset ($classlist[$ns]['free'])) {
981                 # free functions (no class)
982                 echo '<h3 id="'.htmlentities ($ns).'" class="cls freeclass"><abbr title="Namespace">&Nopf;</abbr>&nbsp;'.ctorname($ns).'</h3>'.NL;
983         }
984         else if (isset ($classlist[$ns]['arr'])) {
985                 # C Arrays
986                 echo '<h3 id="'.htmlentities ($ns).'" class="cls array"><abbr title="C Array">&ctdot;</abbr>&nbsp;'.htmlentities ($ns).'</h3>'.NL;
987         }
988         else if (isset ($classlist[$ns]['ptr'])) {
989                 # Pointer Classes
990                 echo '<h3 id="'.htmlentities ($ns).'" class="cls pointerclass"><abbr title="Pointer Class">&Rarr;</abbr>&nbsp;'. htmlentities ($ns).'</h3>'.NL;
991         }
992         else {
993                 # Normal Class
994                 echo '<h3 id="'.htmlentities ($ns).'" class="cls class"><abbr title="Class">&comp;</abbr>&nbsp;'.htmlentities ($ns).'</h3>'.NL;
995         }
996
997         # show original C++ declaration
998         if (isset ($cl['cdecl'])) {
999                 echo '<p class="cdecl"><em>C&#8225;</em>: '.htmlentities ($cl['cdecl']).'</p>'.NL;
1000         }
1001
1002         # print class inheritance (direct parent *name* only)
1003         $inherited = array ();
1004         $isa = traverse_parent ($ns, $inherited);
1005         if (!empty ($isa)) {
1006                 echo ' <p class="classinfo">is-a: '.$isa.'</p>'.NL;
1007         }
1008         echo '<div class="clear"></div>'.NL;
1009
1010
1011         # class documentation (if any)
1012         echo format_doxyclass ($cl);
1013
1014         # member documentation
1015         if (empty ($tbl)) {
1016                 echo '<p class="classinfo">This class object is only used indirectly as return-value and function-parameter. It provides no methods by itself.</p>'.NL;
1017         } else {
1018                 echo '<table class="classmembers">'.NL;
1019                 echo $tbl;
1020                 echo ' </table>'.NL;
1021         }
1022
1023         # traverse parent classes (all inherited members)
1024         foreach ($inherited as $pns => $pcl) {
1025                 $tbl = format_class_members ($pns, $pcl, $dups);
1026                 if (!empty ($tbl)) {
1027                         echo '<h4 class="cls">Inherited from '.$pns.'</h4>'.NL;
1028                         echo '<table class="classmembers">'.NL;
1029                         echo $tbl;
1030                         echo '</table>'.NL;
1031                 }
1032         }
1033 }
1034
1035 ####################
1036 # Enum and Constants
1037
1038 echo '<h2 id="h_enum">Enum/Constants</h2>'.NL;
1039 foreach ($constlist as $ns => $cs) {
1040         echo '<h3 id="'.ctorname ($ns).'" class="cls enum"><abbr title="Enum">&isin;</abbr>&nbsp;'.ctorname ($ns).'</h3>'.NL;
1041         echo '<ul class="enum">'.NL;
1042         foreach ($cs as $c) {
1043                 echo '<li class="const">'.ctorname ($c['lua']).'</li>'.NL;
1044         }
1045         echo '</ul>'.NL;
1046 }
1047
1048 ######################
1049 # Index of all classes
1050
1051 echo '<h2 id="h_index" >Class Index</h2>'.NL;
1052 echo '<ul class="classindex">'.NL;
1053 foreach ($classlist as $ns => $cl) {
1054         echo '<li>'.typelink($ns).'</li>'.NL;
1055 }
1056 echo '</ul>'.NL;
1057
1058
1059 # see how far there is still to go...
1060 fwrite (STDERR, "Found $dox_found annotations. missing: $dox_miss\n");
1061 echo '<!-- '.$dox_found.' / '.$dox_miss.' !-->'.NL;
1062
1063 ?>
1064 </div>
1065 <div class="luafooter">Ardour <?=$ardourversion?> &nbsp;-&nbsp; <?=date('r')?></div>
1066 <?php
1067
1068 if ($HTMLOUTPUT) {
1069         echo '<!-- #### SNIP #### !-->'.NL;
1070         echo '</body>'.NL;
1071         echo '</html>'.NL;
1072 }