make-func-list.pl 7.67 KB
Newer Older
1
#!/usr/bin/perl
2 3 4
# Task: convert gnumeric's function documentation into a valid DocBook XML
# fragment.

Morten Welinder's avatar
Morten Welinder committed
5
# Input format: as produced by "sstest --dump-func-defs=file", i.e.
6 7 8 9 10 11 12 13 14 15 16
# a series of chunks documenting functions. The chunks consist of a number
# of lines. Lines are either
# - @KEYWORD=value
# - @{parameter name}: text
# - a continuation of the previous line
# Chunks are separated by empty lines, but note that lines in a chunk may
# be empty, so empty lines are not always chunk separators.
# Chunks may contain multiple @KEYWORD=value lines for the same keyword.
# The various keywords have their own enum value in GnmFuncHelpType in
# src/func.h .

Morten Welinder's avatar
Morten Welinder committed
17
use strict;
18 19
use warnings;
use diagnostics;
Morten Welinder's avatar
Morten Welinder committed
20 21
use open IO => ':utf8';
binmode STDOUT, ':utf8';
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

#
# Global state that we need to track
#

# On the input side (parser state):
my $curcat = undef;	# Current category
my $curfunc = undef;	# Current function name
my $curkeyword = undef;	# Current input marker (keyword)

# On the output side:
my @tagstack = ();	# Closing tags that still need to be output at some
			# future point for proper balance.

#
# Helper functions
#
Morten Welinder's avatar
Morten Welinder committed
39

40 41 42 43 44 45
sub quote_stuff($) {
	# Escape/quote the characters which are special in XML: ampersand,
	# less than sign and greater than sign.
	my ($str) = @_;

	# Let's do this one first...
Morten Welinder's avatar
Morten Welinder committed
46
	$str =~ s/\&/\&/gu;
47

Morten Welinder's avatar
Morten Welinder committed
48 49
	$str =~ s/</\&lt;/gu;
	$str =~ s/>/\&gt;/gu;
50 51 52 53 54 55 56
	return $str;
}

sub markup_stuff($) {
	my ($str) = @_;

	$str = &quote_stuff ($str);
Morten Welinder's avatar
Morten Welinder committed
57 58 59
	$str =~ s/\b$curfunc\b/<function>$curfunc<\/function>/gu;
	$str =~ s/\@\{([^}]*)\}/<parameter>$1<\/parameter>/gu;
	$str =~ s/\@(\w*)\b/<parameter>$1<\/parameter>/gu;
60 61
	return $str;
}
Morten Welinder's avatar
Morten Welinder committed
62

63 64 65 66 67 68
sub close_including($) {
	my ($tag) = @_;
	while (1) {
		my $element = pop @tagstack;
		last unless defined $element;
		print "  " x scalar(@tagstack), $element, "\n";
69
	}
70 71 72 73 74 75 76 77 78 79 80 81
}

sub close_upto($) {
	my ($tag) = @_;
	while (1) {
		my $element = pop @tagstack;
		last unless defined $element;
		if ($element eq $tag) {
			push @tagstack, $element;
			last;	
		}
		print "  " x scalar(@tagstack), $element, "\n";
82
	}
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
}

#
# Functions to process specific keywords
#

sub processnotimplemented($) {
	die("Sorry, no code has been implemented yet to handle the $curkeyword keyword"),
}

sub process_category($) {
	my ($cat) = @_;
	chomp($cat);

	my $ws = "  " x scalar(@tagstack);

	if ((not defined $curcat) or ($curcat ne $cat)) {
		# Start of a new category.

		# Finish up the old one (if there is one)
		close_including('</sect1>');

		# And start on the new one
		my $cat_id = "CATEGORY_" . $cat;
Morten Welinder's avatar
Morten Welinder committed
107 108
		$cat_id =~ s/\s+/_/gg;
		$cat_id =~ s/[^A-Za-z_]//gu;
109 110 111
		print $ws, "<sect1 id=\"$cat_id\">\n";
		print $ws, "  <title>", &quote_stuff ($cat), "</title>\n";
		push @tagstack, ('</sect1>');
Morten Welinder's avatar
Morten Welinder committed
112
	}
113 114 115 116 117 118
}

sub process_function($) {
	my ($func) = @_;

	my $ws = "  " x scalar(@tagstack);
119
	print $ws, "<refentry id=\"gnumeric-function-$func\">\n";
120 121 122 123 124 125
	print $ws, "  <refmeta>\n";
	print $ws, "    <refentrytitle><function>$func</function></refentrytitle>\n";
	print $ws, "  </refmeta>\n";
	print $ws, "  <refnamediv>\n";
	print $ws, "    <refname><function>$func</function></refname>\n";
	push @tagstack, ('</refentry>');
126 127 128 129 130 131 132 133 134
}

sub process_short_desc($) {
	my ($desc) = @_;

	my $ws = "  " x scalar(@tagstack);
	print $ws, "  <refpurpose>\n";
	print $ws, "    ", &markup_stuff ($desc), "\n";
	print $ws, "  </refpurpose>\n";
135
	print $ws, "</refnamediv>\n";
136 137 138 139 140 141 142 143 144 145 146 147 148
}

sub process_description($) {
	my ($text) = @_;
	my $ws = "  " x scalar(@tagstack);
	print $ws, "<refsect1>\n";
	print $ws, "  <title>Description</title>\n";
	my $haveparameters = 0;
	foreach my $l (split(/\n/, $text)) {
		if (!$haveparameters && $l =~ m/^\@\{/) {
			$haveparameters = 1;
		}
		print $ws,"    <para>", &markup_stuff($l), "</para>\n";
Morten Welinder's avatar
Morten Welinder committed
149
	}
150 151 152
	print $ws, "</refsect1>\n";
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
sub process_argumentdescription($) {
	my ($text) = @_;
	my $ws = "  " x scalar(@tagstack);
	print $ws, "<refsect1>\n";
	print $ws, "  <title>Arguments</title>\n";
	my $haveparameters = 0;
	foreach my $l (split(/\n/, $text)) {
		if (!$haveparameters && $l =~ m/^\@\{/) {
			$haveparameters = 1;
		}
		print $ws,"    <para>", &markup_stuff($l), "</para>\n";
	}
	print $ws, "</refsect1>\n";
}

168 169 170 171 172 173 174 175 176 177 178
sub process_note($) {
	my ($text) = @_;
	my $ws = "  " x scalar(@tagstack);
	print $ws, "<refsect1>\n";
	print $ws, "  <title>Note</title>\n";
	foreach my $l (split(/\n/, $text)) {
		print $ws,"    <para>", &markup_stuff($l), "</para>\n";
	}
	print $ws, "</refsect1>\n";
}

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
sub process_excel($) {
	my ($text) = @_;
	my $ws = "  " x scalar(@tagstack);
	print $ws, "<refsect1>\n";
	print $ws, "  <title>Microsoft Excel Compatibility</title>\n";
	foreach my $l (split(/\n/, $text)) {
		print $ws,"    <para>", &markup_stuff($l), "</para>\n";
	}
	print $ws, "</refsect1>\n";
}

sub process_odf($) {
	my ($text) = @_;
	my $ws = "  " x scalar(@tagstack);
	print $ws, "<refsect1>\n";
	print $ws, "  <title>OpenDocument Format (ODF) Compatibility</title>\n";
	foreach my $l (split(/\n/, $text)) {
		print $ws,"    <para>", &markup_stuff($l), "</para>\n";
	}
	print $ws, "</refsect1>\n";
}

201 202 203 204
sub process_syntax($) {
	my ($str) = @_;
	my $ws = "  " x scalar(@tagstack);
	$str = &markup_stuff ($str);
Morten Welinder's avatar
Morten Welinder committed
205
	$str =~ s/([\(\,])(\w*)/$1<parameter>$2<\/parameter>/gu;
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
	print $ws, "<refsynopsisdiv>\n";
	print $ws, "  <synopsis>$str</synopsis>\n";
	print $ws, "</refsynopsisdiv>\n";
}

sub process_examples($) {
	my ($text) = @_;
	my $ws = "  " x scalar(@tagstack);
	print $ws, "<refsect1>\n";
	print $ws, "   <title>Examples</title>\n";
	print $ws, "   <para>", &markup_stuff ($text), "</para>\n";
	push @tagstack, ('</refsect1>');
}

sub process_seealso($) {
	my ($text) = @_;

	my $linktxt = $text;
Morten Welinder's avatar
Morten Welinder committed
224 225
	$linktxt =~ s/\s//gu;
	$linktxt =~ s/\.$//u;
Morten Welinder's avatar
Morten Welinder committed
226 227
	my @links = split (/,/, $linktxt);

228 229 230
	my $ws = "  " x scalar(@tagstack);
	print $ws, "<refsect1>\n";
	print $ws, "  <title>See also</title>\n";
Morten Welinder's avatar
Morten Welinder committed
231
	my @a = ();
232
	print $ws, "  <para>\n";
Morten Welinder's avatar
Morten Welinder committed
233
	foreach my $link (@links) {
234
	    push @a, $ws . "    <link linkend=\"gnumeric-function-$link\"><function>$link</function></link>";
Morten Welinder's avatar
Morten Welinder committed
235
	}
236
	if (scalar(@a) > 0) {
Morten Welinder's avatar
Morten Welinder committed
237 238
	    print join (",\n", @a), ".\n";
	}
239 240
	print $ws, "  </para>\n";
	push @tagstack, ('</refsect1>');
Morten Welinder's avatar
Morten Welinder committed
241 242
}

243 244 245
my %processor = (
	'CATEGORY'	=> \&process_category,
	'FUNCTION'	=> \&process_function,
246
	'SHORTDESC'	=> \&process_short_desc,
247
	'SYNTAX'	=> \&process_syntax,
248
	'ARGUMENTDESCRIPTION'	=> \&process_argumentdescription,
249 250
	'DESCRIPTION'	=> \&process_description,
	'SEEALSO'	=> \&process_seealso,
251
	'NOTE'		=> \&process_note,
252 253
	'EXCEL'		=> \&process_excel,
	'ODF'		=> \&process_odf,
254
);
255

256 257 258
sub process_chunk(@) {
	my (@chunk) = @_;
	return unless scalar(@chunk) > 0;
259

260 261 262 263 264 265 266 267
	# Trim off any trailing empty lines
	while (scalar(@chunk) > 0) {
		last unless $chunk[$#chunk] =~ /^\s*$/;
		pop @chunk;
	}
	
	my $cat;
	my $in_description = 0;
268

269 270 271 272 273
	$curkeyword = undef;
	my $lines = "";
	for my $i (0..$#chunk) {
		my $chunk = $chunk[$i];
		chomp $chunk;
Morten Welinder's avatar
Morten Welinder committed
274

275 276
		if ($chunk =~ m/^\@(\w+)=(.*)/) {
			my ($key, $val) = (uc($1), $2);
Morten Welinder's avatar
Morten Welinder committed
277

278 279
			$cat = $val if ($key eq 'CATEGORY');
			$curfunc = $val if ($key eq 'FUNCTION');
Morten Welinder's avatar
Morten Welinder committed
280

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
			if (defined($processor{$key})) {
				if (defined($curkeyword)) {
					# Process the previous tag for which
					# all lines have been gathered now.
					&{$processor{$curkeyword}}($lines);
				}
				$curkeyword = $key;
				$lines = $val;
				next;
			} else {
				die("Unrecognised keyword: $key\n");
			}
		}
		$lines .= "\n" . $chunk;
	}
	&{$processor{$curkeyword}}($lines);

	$curcat = $cat;

	close_upto('</sect1>'); # Closing tag of a category.
Morten Welinder's avatar
Morten Welinder committed
301 302
}

303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
sub main() {
	my $line;
	my @chunk = ();
	while ($line = <>) {
		if ($line =~ m/^\@CATEGORY=/) {
			# We're at the start of a new chunk of function
			# documentation
			process_chunk(@chunk);
			print "\n";
			@chunk = ($line);
		} else {
			push @chunk, $line;
		}
	}
	process_chunk(@chunk);
	while (my $el = pop @tagstack) {
		print "  " x scalar(@tagstack), $el, "\n";
	}
Morten Welinder's avatar
Morten Welinder committed
321
}
322

323 324
main();
exit(0);