#!/usr/bin/perl

# follow javadoc
# @see http://java.sun.com/j2se/javadoc/writingdoccomments/

use strict;

=cut

這個程式可以從 C 的程式碼中，列出所有 statis/non-static 的 functions。

每個 function 前面可以有 javadoc-style 的註解。範例如下：

/**
 * Function of the function func.
 * @param x variable, if there are more than one @-style description, it
 *        will end with the next @-style description.
 * @return void
 */
void func(int x)
{
    ...
}

如果有註解之後沒有接 function 的話，會原封不動丟出來。

=cut

my $content;

foreach my $f (@ARGV) {
    makedoc($f);
}


sub grep_desc
{
    my @buffer = ();
    my $name = '\b\w+\b';
    my $type = '\b (?: (?:struct|unsigned) \s+)? \w+\b (?: \s*\*\s* | \s+)';
    my $sentence = '.*';

    my $one_desc = "$name\\s+$sentence";
    my $desc_head = "\\/ \\* \\* \n";
    my $desc_tail = "\\s* \\* \\/ \n";
    my $desc_line = "\\s* \\* .* \n";
    my $paramdesc = "\@param\\s+ $one_desc (?:\n$desc_line)*";
    my $returndesc = "\@return\\s+ $sentence (?:\n$desc_line)*";
    my $seedesc = "\@see\\s+ $sentence (?:\n$desc_line)*";
    my $desc = "$desc_head(?:$desc_line)*$desc_tail";

    my $modifier = '(?: static | inline)\s+';
    my $one_param = "$type \\s* $name";
    my $more_param = ",\\s* $one_param";
    my $param = "(?: $one_param(?:$more_param)* | void )?";
    my $func_proto = "(?:$modifier)* $type \\s* $name\\($param\\)";

    my $pattern = "(?: ($desc)|($func_proto)[\n\\s]*{ )";
    my $out;

    if ($content =~ s/^([.\n]*)$pattern//mox) {
	$1 and push @buffer, {type => 'garbage', data => undef};
	if ($2) {
	    $out = $2;
	    $out =~ s#^/\*\*##;
	    $out =~ s#\s*\*/$##;
	    $out =~ s/^\s*\* ?/  /mg;
	    push @buffer, { type => 'comment', data => $out };
	}
	elsif ($3) {
	    $out = $3;
	    $out =~ s/\n/ /g;
	    $out =~ s/\s+/ /g;
	    $out .= ";\n";
	    push @buffer, { type => 'function', data => $out };
	}
	else {
	    die;
	}
    }
    return @buffer;
}

sub makedoc
{
    my $file = shift @_;
    open SRC, "<$file";
    $content = join "",<SRC>;
    close SRC;

    # just to break them up to avoid vim's misunderstanding
    print "// vim".":ft=c\n\n";

    $content =~ s#/\*[^*].*?\*/##sg;
    my @buffer = ();

    while (my @b = grep_desc()) {
	push @buffer, @b;
	shift @buffer while ($buffer[0]->{type} eq 'garbage');
	last unless @buffer;

	if (@buffer == 3) {
	    if ($buffer[1]->{type} eq 'garbage') {
		$_ = shift @buffer;
		shift @buffer;
		unshift @buffer, $_;
	    }
	}
	if (@buffer == 2) {
	    if ($buffer[0]->{type} eq 'function') {
		$_ = shift @buffer;
		print $/, $_->{data};
	    }
	    elsif ($buffer[0]->{type} eq 'comment') {
		if ($buffer[1]->{type} eq 'comment') {
		    $_ = shift @buffer;
		    print $_->{data}, $/;
		}
		else {
		    print $/, $buffer[1]->{data}, $buffer[0]->{data}, $/;
		    undef @buffer;
		}
	    }
	}
	if (@buffer == 1) {
	    if ($buffer[0]->{type} eq 'function') {
		$_ = shift @buffer;
		print $/, $_->{data};
	    }
	}

    }
}
