2 # Task: convert gnumeric's function documentation into a valid DocBook XML
5 # Input format: as produced by "sstest --dump-func-defs=file", i.e.
6 # a series of chunks documenting functions. The chunks consist of a number
7 # of lines. Lines are either
9 # - @{parameter name}: text
10 # - a continuation of the previous line
11 # Chunks are separated by empty lines, but note that lines in a chunk may
12 # be empty, so empty lines are not always chunk separators.
13 # Chunks may contain multiple @KEYWORD=value lines for the same keyword.
14 # The various keywords have their own enum value in GnmFuncHelpType in
20 use open IO
=> ':utf8';
21 binmode STDOUT
, ':utf8';
24 # Global state that we need to track
27 # On the input side (parser state):
28 my $curcat = undef; # Current category
29 my $curfunc = undef; # Current function name
30 my $curkeyword = undef; # Current input marker (keyword)
33 my @tagstack = (); # Closing tags that still need to be output at some
34 # future point for proper balance.
41 # Escape/quote the characters which are special in XML: ampersand,
42 # less than sign and greater than sign.
45 # Let's do this one first...
46 $str =~ s/\&/\&/gu;
56 $str = "e_stuff
($str);
57 $str =~ s/\b$curfunc\b/<function>$curfunc<\/function
>/gu
;
58 $str =~ s/\@\{([^}]*)\}/<parameter>$1<\/parameter
>/gu
;
59 $str =~ s/\@(\w*)\b/<parameter>$1<\/parameter
>/gu
;
63 sub close_including
($) {
66 my $element = pop @tagstack;
67 last unless defined $element;
68 print " " x
scalar(@tagstack), $element, "\n";
75 my $element = pop @tagstack;
76 last unless defined $element;
77 if ($element eq $tag) {
78 push @tagstack, $element;
81 print " " x
scalar(@tagstack), $element, "\n";
86 # Functions to process specific keywords
89 sub processnotimplemented
($) {
90 die("Sorry, no code has been implemented yet to handle the $curkeyword keyword"),
93 sub process_category
($) {
97 my $ws = " " x
scalar(@tagstack);
99 if ((not defined $curcat) or ($curcat ne $cat)) {
100 # Start of a new category.
102 # Finish up the old one (if there is one)
103 close_including
('</sect1>');
105 # And start on the new one
106 my $cat_id = "CATEGORY_" . $cat;
107 $cat_id =~ s/\s+/_/gg;
108 $cat_id =~ s/[^A-Za-z_]//gu;
109 print $ws, "<sect1 id=\"$cat_id\">\n";
110 print $ws, " <title>", "e_stuff
($cat), "</title>\n";
111 push @tagstack, ('</sect1>');
115 sub process_function
($) {
118 my $ws = " " x
scalar(@tagstack);
119 print $ws, "<refentry id=\"gnumeric-function-$func\">\n";
120 print $ws, " <refmeta>\n";
121 print $ws, " <refentrytitle><function>$func</function></refentrytitle>\n";
122 print $ws, " </refmeta>\n";
123 print $ws, " <refnamediv>\n";
124 print $ws, " <refname><function>$func</function></refname>\n";
125 push @tagstack, ('</refentry>');
128 sub process_short_desc
($) {
131 my $ws = " " x
scalar(@tagstack);
132 print $ws, " <refpurpose>\n";
133 print $ws, " ", &markup_stuff
($desc), "\n";
134 print $ws, " </refpurpose>\n";
135 print $ws, "</refnamediv>\n";
138 sub process_description
($) {
140 my $ws = " " x
scalar(@tagstack);
141 print $ws, "<refsect1>\n";
142 print $ws, " <title>Description</title>\n";
143 my $haveparameters = 0;
144 foreach my $l (split(/\n/, $text)) {
145 if (!$haveparameters && $l =~ m/^\@\{/) {
148 print $ws," <para>", &markup_stuff
($l), "</para>\n";
150 print $ws, "</refsect1>\n";
153 sub process_argumentdescription
($) {
155 my $ws = " " x
scalar(@tagstack);
156 print $ws, "<refsect1>\n";
157 print $ws, " <title>Arguments</title>\n";
158 my $haveparameters = 0;
159 foreach my $l (split(/\n/, $text)) {
160 if (!$haveparameters && $l =~ m/^\@\{/) {
163 print $ws," <para>", &markup_stuff
($l), "</para>\n";
165 print $ws, "</refsect1>\n";
168 sub process_note
($) {
170 my $ws = " " x
scalar(@tagstack);
171 print $ws, "<refsect1>\n";
172 print $ws, " <title>Note</title>\n";
173 foreach my $l (split(/\n/, $text)) {
174 print $ws," <para>", &markup_stuff
($l), "</para>\n";
176 print $ws, "</refsect1>\n";
179 sub process_excel
($) {
181 my $ws = " " x
scalar(@tagstack);
182 print $ws, "<refsect1>\n";
183 print $ws, " <title>Microsoft Excel Compatibility</title>\n";
184 foreach my $l (split(/\n/, $text)) {
185 print $ws," <para>", &markup_stuff
($l), "</para>\n";
187 print $ws, "</refsect1>\n";
192 my $ws = " " x
scalar(@tagstack);
193 print $ws, "<refsect1>\n";
194 print $ws, " <title>OpenDocument Format (ODF) Compatibility</title>\n";
195 foreach my $l (split(/\n/, $text)) {
196 print $ws," <para>", &markup_stuff
($l), "</para>\n";
198 print $ws, "</refsect1>\n";
201 sub process_syntax
($) {
203 my $ws = " " x
scalar(@tagstack);
204 $str = &markup_stuff
($str);
205 $str =~ s/([\(\,])(\w*)/$1<parameter>$2<\/parameter
>/gu
;
206 print $ws, "<refsynopsisdiv>\n";
207 print $ws, " <synopsis>$str</synopsis>\n";
208 print $ws, "</refsynopsisdiv>\n";
211 sub process_examples
($) {
213 my $ws = " " x
scalar(@tagstack);
214 print $ws, "<refsect1>\n";
215 print $ws, " <title>Examples</title>\n";
216 print $ws, " <para>", &markup_stuff
($text), "</para>\n";
217 push @tagstack, ('</refsect1>');
220 sub process_seealso
($) {
224 $linktxt =~ s/\s//gu;
225 $linktxt =~ s/\.$//u;
226 my @links = split (/,/, $linktxt);
228 my $ws = " " x
scalar(@tagstack);
229 print $ws, "<refsect1>\n";
230 print $ws, " <title>See also</title>\n";
232 print $ws, " <para>\n";
233 foreach my $link (@links) {
234 push @a, $ws . " <link linkend=\"gnumeric-function-$link\"><function>$link</function></link>";
236 if (scalar(@a) > 0) {
237 print join (",\n", @a), ".\n";
239 print $ws, " </para>\n";
240 push @tagstack, ('</refsect1>');
244 'CATEGORY' => \
&process_category
,
245 'FUNCTION' => \
&process_function
,
246 'SHORTDESC' => \
&process_short_desc
,
247 'SYNTAX' => \
&process_syntax
,
248 'ARGUMENTDESCRIPTION' => \
&process_argumentdescription
,
249 'DESCRIPTION' => \
&process_description
,
250 'SEEALSO' => \
&process_seealso
,
251 'NOTE' => \
&process_note
,
252 'EXCEL' => \
&process_excel
,
253 'ODF' => \
&process_odf
,
256 sub process_chunk
(@
) {
258 return unless scalar(@chunk) > 0;
260 # Trim off any trailing empty lines
261 while (scalar(@chunk) > 0) {
262 last unless $chunk[$#chunk] =~ /^\s*$/;
267 my $in_description = 0;
271 for my $i (0..$#chunk) {
272 my $chunk = $chunk[$i];
275 if ($chunk =~ m/^\@(\w+)=(.*)/) {
276 my ($key, $val) = (uc($1), $2);
278 $cat = $val if ($key eq 'CATEGORY');
279 $curfunc = $val if ($key eq 'FUNCTION');
281 if (defined($processor{$key})) {
282 if (defined($curkeyword)) {
283 # Process the previous tag for which
284 # all lines have been gathered now.
285 &{$processor{$curkeyword}}($lines);
291 die("Unrecognised keyword: $key\n");
294 $lines .= "\n" . $chunk;
296 &{$processor{$curkeyword}}($lines);
300 close_upto
('</sect1>'); # Closing tag of a category.
307 if ($line =~ m/^\@CATEGORY=/) {
308 # We're at the start of a new chunk of function
310 process_chunk
(@chunk);
317 process_chunk
(@chunk);
318 while (my $el = pop @tagstack) {
319 print " " x
scalar(@tagstack), $el, "\n";