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