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