#!/usr/bin/env perl use strict; use warnings; use Data::Dumper; use Storable qw/dclone/; use constant { STATE_READ_SKIP => 0, STATE_READ_CONTENT => 1, }; my $state = STATE_READ_SKIP; my $content; my %functions = (); my %modules = (); my $cur_module; sub print_markdown { while (my ($mname, $m) = each %modules) { print <{'data'} EOD if ($m->{'example'}) { print <{'example'} ~~~ EOD } print "##Methods\n\nThe module defines the following methods.\n\n"; while (my ($fname, $f) = each %{$m->{'functions'}}) { print <{'data'} EOD print "\n*Parameters*\n"; foreach (@{$f->{'params'}}) { if ($_->{'type'}) { print "\t`$_->{'name'} \{$_->{'type'}\}` $_->{'description'}\n"; } else { print "\t`$_->{'name'}` $_->{'description'}\n"; } } print "\n*Returns*\n"; if ($f->{'return'} && $f->{'return'}->{'description'}) { $_ = $f->{'return'}; if ($_->{'type'}) { print "\t`\{$_->{'type'}\}` $_->{'description'}\n"; } else { print "\t$_->{'description'}\n"; } } else { print "\tnothing\n"; } if ($f->{'example'}) { print <{'example'} ~~~ EOD } print "\nBack to [module description](#mod_$mname).\n"; } print "\nBack to [top](#).\n"; } } sub parse_function { my ($func, @data) = @_; my ($name) = ($func =~ /^\@function\s*(.+)\s*$/); $functions{$name} = {}; my $f = $functions{$name}; my $example = 0; foreach(@data) { if (/^\@param\s*(?:\{([a-zA-Z])\})?\s*(\S+)\s*(.+)?\s*$/) { my $p = { name => $2, type => $1, description => $3}; push @{$f->{'params'}}, $p; } elsif (/^\@return\s*(?:\{([a-zA-Z])\})?\s*(.+)?\s*$/) { my $r = { type => $1, description => $2 }; $f->{'return'} = $r; } elsif (/^\@example$/) { $example = 1; } elsif ($_ ne $func) { if ($example) { $f->{'example'} .= $_; } else { $f->{'data'} .= $_; } } } if ($f->{'data'}) { chomp $f->{'data'}; } if ($f->{'example'}) { chomp $f->{'example'}; } } sub parse_module { my ($module, @data) = @_; my ($name) = ($module =~ /^\@module\s*(.+)\s*$/); $modules{$name} = { functions => dclone(\%functions) }; %functions = (); my $f = $modules{$name}; my $example = 0; foreach(@data) { if (/^\@example$/) { $example = 1; } elsif ($_ ne $module) { if ($example) { $f->{'example'} .= $_; } else { $f->{'data'} .= $_; } } } if ($f->{'data'}) { chomp $f->{'data'}; } if ($f->{'example'}) { chomp $f->{'example'}; } $cur_module = $f; } sub parse_content { my @func = grep /^\@function.+$/, @_; if (scalar @func > 0) { parse_function($func[0], @_); } else { my @module = grep /^\@module.+$/, @_; if (scalar @module > 0) { parse_module($module[0], @_); } } } while(<>) { if ($state == STATE_READ_SKIP) { if ($_ =~ /^\s*\/\*\*\*$/) { $state = STATE_READ_CONTENT; $content = ""; } } elsif ($state == STATE_READ_CONTENT) { if ($_ =~ /^\s*\*\/$/) { $state = STATE_READ_SKIP; parse_content(split /^/, $content); $content = ""; } else { my ($line) = ($_ =~ /^\s*(?:\*\s?)(.+)\s*$/); if ($line) { $content .= $line . "\n"; } else { # Preserve empty lines $content .= "\n"; } } } } $cur_module->{'functions'} = dclone(\%functions); print_markdown;