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