/[svn]/linuxsampler/trunk/scripts/generate_lscp_shell_reference.pl
ViewVC logotype

Contents of /linuxsampler/trunk/scripts/generate_lscp_shell_reference.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2534 - (show annotations) (download)
Sun Mar 9 21:34:03 2014 UTC (10 years, 1 month ago) by schoenebeck
File MIME type: text/plain
File size: 10414 byte(s)
* LSCP shell (WIP): Added initial support for built-in LSCP reference
  documentation, which will automatically show the relevant LSCP reference
  section on screen as soon as one specific LSCP command was detected while
  typing on the command line.
* Bumped version (1.0.0.svn37).

1 #!/usr/bin/perl -w
2
3 # Updates the built-in LSCP documentation reference of the LSCP shell.
4 #
5 # Copyright (c) 2014 Christian Schoenebeck
6 #
7 # Extracts all sections from Documentation/lscp.xml marked with our magic
8 # XML attribute "lscp_cmd=true" (and uses the section's "anchor" XML attribute
9 # for *knowing* the respective exact LSCP command of the section).
10 # Then src/network/lscp_shell_reference.cpp is generated by this script with
11 # the documentation for each individual LSCP command extracted.
12 #
13 # Usage: generate_lscp_shell_reference.pl
14
15 use XML::Parser;
16 use Data::Dumper; # just for debugging
17 use Storable qw(dclone);
18
19 my $YACC_FILE = "../Documentation/lscp.xml";
20 my $REFERENCE_CPP_FILE = "../src/network/lscp_shell_reference.cpp";
21
22 ###########################################################################
23 # class MyDOM
24 #
25 # Wraps the data model returned by XML::Parser and provides convenient methods
26 # to access the model in DOM style. Because the tree model provided by
27 # XML::Parser uses a very inconvenient layout, which would require a lot of
28 # hard readable and error prone code if accessed directly.
29
30 package MyDOM;
31
32 # $dom = MyDOM->new($doc);
33 sub new {
34 my ($class, $self) = @_;
35 my $data = {
36 'type' => 0,
37 'attr' => 0,
38 'content' => $self
39 };
40 # print ::Dumper($self) . "\n";
41 return bless $data, $class;
42 }
43
44 # $element = $dom->element($name, [$index = 0]);
45 sub element {
46 my $self = shift @_;
47 my $name = shift @_;
48 my $nr = (@_) ? shift @_ : 0;
49 my $content = $self->{content};
50 my $i = 0;
51 my $k = 0;
52 CYCLE: while ($i + 1 < @$content) {
53 my $type = $content->[$i++];
54 my $subContent = $content->[$i++];
55
56 next CYCLE if ($type ne $name);
57
58 if ($k == $nr) {
59 my $attr = ($type && @$subContent) ? $subContent->[0] : 0;
60 my $subContentClone = ($type) ? ::dclone($subContent) : $subContent; # clone it, since we will modify it next
61 if ($attr && $type) { shift @$subContentClone; } # drop first element, which contains attributes
62 my $data = {
63 'type' => $type,
64 'attr' => $attr,
65 'content' => $subContentClone
66 };
67 return bless $data, 'MyDOM';
68 }
69 $k++;
70 }
71 return 0;
72 }
73
74 # $element = $dom->elementNr(4);
75 sub elementNr {
76 my $self = shift @_;
77 my $nr = shift @_;
78 my $content = $self->{content};
79 my $i = 0;
80 my $k = 0;
81 while ($i + 1 < @$content) {
82 my $type = $content->[$i++];
83 my $subContent = $content->[$i++];
84 if ($k == $nr) {
85 my $attr = ($type && @$subContent) ? $subContent->[0] : 0;
86 # print "type $type\n";
87 # print ::Dumper($subContent) . "\n";
88 my $subContentClone = ($type) ? ::dclone($subContent) : $subContent; # clone it, since we will modify it next
89 if ($attr && $type) { shift @$subContentClone; } # drop first element, which contains attributes
90 my $data = {
91 'type' => $type,
92 'attr' => $attr,
93 'content' => $subContentClone
94 };
95 return bless $data, 'MyDOM';
96 }
97 $k++;
98 }
99 return 0;
100 }
101
102 # $s = $element->name();
103 sub name {
104 my $self = shift @_;
105 return $self->{type};
106 }
107
108 # $s = $element->attr("anchor");
109 sub attr {
110 my $self = shift @_;
111 my $name = shift @_;
112 if (!$self->{attr} || !exists $self->{attr}->{$name}) {
113 return 0;
114 }
115 return $self->{attr}->{$name};
116 }
117
118 # $s = $element->body();
119 sub body {
120 my $self = shift @_;
121 $s = "";
122 if (!$self->{type}) {
123 return $self->{content};
124 }
125 for (my $i = 0; $self->elementNr($i); $i++) {
126 $e = $self->elementNr($i);
127 if (!$e->name()) {
128 $s .= $e->{content};
129 }
130 }
131 return $s;
132 }
133
134 # $element->dumpMe();
135 sub dumpMe {
136 my $self = shift @_;
137 print "[dumpME()]: " . ::Dumper($self->{content}) . "\n";
138 }
139
140 ###########################################################################
141 # main app
142
143 package main;
144
145 # parse command line argument(s)
146 my $g_debug_xml_extract = 0;
147 if (defined($ARGV[0]) and $ARGV[0] eq "--debug-xml-extract") {
148 $g_debug_xml_extract = 1;
149 }
150
151 # will be populated by collectCommands()
152 my $g_cmds = { };
153
154 # collectCommands($dom);
155 sub collectCommands {
156 my $dom = shift @_;
157 for (my $i = 0; $dom->element("section", $i); $i++) {
158 my $section = $dom->element("section", $i);
159 if ($section->attr("lscp_cmd")) {
160 if (!$section->attr("anchor")) {
161 die "ERROR: Section deteced with 'lscp_cmd' attribute, but without 'anchor' attribute.";
162 }
163 my $name = $section->attr("anchor");
164 if (exists $g_cmds->{$name}) {
165 die "ERROR: Multiple occurence of LSCP command detected: $name";
166 }
167 $g_cmds->{$name} = $section;
168 } else {
169 collectCommands($section);
170 }
171 }
172 }
173
174 # removes redundant white spaces
175 sub trimAll {
176 my $s = shift;
177 # replace tabs by space
178 $s =~ s/\t/ /g;
179 # replace occurences of more than one space character by only one space
180 # character (including new line character)
181 $s =~ s/\s+/ /g;
182 # remove leading white spaces
183 $s =~ s/^\s+//g;
184 # remove trailing white spaces
185 $s =~ s/\s+$//g;
186 return $s;
187 }
188
189 # creates an optional space intended to be appended to the given string
190 sub wordSepFor {
191 my $s = shift;
192 if ($s eq '') { return ""; }
193 if ($s =~ /\n$/) { return ""; }
194 return " ";
195 }
196
197 # $s = encodeXref($xref);
198 sub encodeXref {
199 my $xref = shift;
200 return trimAll($xref->body());
201 }
202
203 # $s = encodeT($t);
204 sub encodeT {
205 my $t = shift;
206 my $s = "";
207 for (my $i = 0; $t->elementNr($i); $i++) {
208 $e = $t->elementNr($i);
209 $type = $e->name();
210 if (!$type) {
211 $s .= wordSepFor($s);
212 $s .= trimAll($e->body());
213 } elsif ($type eq "t") {
214 $s .= wordSepFor($s);
215 $s .= encodeT($e);
216 } elsif ($type eq "list") {
217 $s .= wordSepFor($s);
218 $s .= encodeSection($e);
219 } elsif ($type eq "xref") {
220 $s .= wordSepFor($s);
221 $s .= encodeXref($e);
222 }
223 }
224 if (!($s =~ /\n\n$/)) { $s .= "\n\n"; }
225 return $s;
226 }
227
228 # $s = encodeSection($section);
229 sub encodeSection {
230 my $section = shift;
231 my $s = "";
232 for (my $i = 0; $section->elementNr($i); $i++) {
233 $e = $section->elementNr($i);
234 $type = $e->name();
235 if (!$type) {
236 # nothing here for now
237 } elsif ($type eq "t") {
238 $s .= wordSepFor($s);
239 $s .= encodeT($e);
240 } elsif ($type eq "list") {
241 $s .= wordSepFor($s);
242 $s .= encodeSection($e);
243 } elsif ($type eq "xref") {
244 $s .= wordSepFor($s);
245 $s .= encodeXref($e);
246 }
247 }
248 return $s;
249 }
250
251 # open and parse lscp.xml
252 my $parser = XML::Parser->new(Style => 'Tree');
253 my $doc = $parser->parsefile($YACC_FILE);
254 my $dom = MyDOM->new($doc);
255 my $middle = $dom->element("rfc")->element("middle");
256
257 # extract all sections from the document with the individual LSCP commands
258 collectCommands($middle);
259
260 # if --debug-xml-extract is supplied, just show the result of XML parsing and exit
261 if ($g_debug_xml_extract) {
262 while (my ($name, $section) = each(%$g_cmds)) {
263 print "-> " . $name . "\n";
264 print encodeSection($section);
265 }
266 exit(0);
267 }
268
269 # start generating lscp_shell_reference.cpp ...
270 open(OUT, ">", $REFERENCE_CPP_FILE) || die "Can't open LSCP shell doc reference C++ file for output";
271 print OUT <<EOF_BLOCK;
272 /*****************************************************************************
273 * *
274 * LSCP documentation reference built into LSCP shell. *
275 * *
276 * Copyright (c) 2014 Christian Schoenebeck *
277 * *
278 * This program is part of LinuxSampler and released under the same terms. *
279 * *
280 * This source file is auto generated by 'generate_lscp_shell_reference.pl' *
281 * from 'lscp.xml'. Thus do not modify this C++ file directly! *
282 * *
283 *****************************************************************************/
284
285 /*
286 This C++ file should automatically be re-generated if lscp.xml was
287 modified, if not, you may call "make parser" explicitly.
288 */
289
290 #include "lscp_shell_reference.h"
291 #include <string.h>
292
293 static lscp_ref_entry_t lscp_reference[] = {
294 EOF_BLOCK
295 while (my ($name, $section) = each(%$g_cmds)) {
296 # convert reference string block into C-style string format
297 my $s = encodeSection($section);
298 $s =~ s/\n/\\n/g;
299 $s =~ s/\"/\\\"/g;
300 # split reference string into equal length chunks, so we can distribute
301 # them over lines, in order to not let them float behind 80 chars per line
302 my @lines = unpack("(A70)*", $s);
303 my $backSlashWrap = 0;
304 print OUT " { \"$name\",\n";
305 foreach my $line (@lines) {
306 if ($backSlashWrap) { $line = "\\" . $line; }
307 $backSlashWrap = ($line =~ /\\$/);
308 if ($backSlashWrap) { chop $line; }
309 print OUT " \"$line\"\n";
310
311 }
312 print OUT " },\n";
313 }
314 print OUT <<EOF_BLOCK;
315 };
316
317 lscp_ref_entry_t* lscp_reference_for_command(const char* cmd) {
318 const int n1 = strlen(cmd);
319 if (!n1) return NULL;
320 int foundLength = 0;
321 lscp_ref_entry_t* foundEntry = NULL;
322 for (int i = 0; i < sizeof(lscp_reference) / sizeof(lscp_ref_entry_t); ++i) {
323 const int n2 = strlen(lscp_reference[i].name);
324 const int n = n1 < n2 ? n1 : n2;
325 if (!strncmp(cmd, lscp_reference[i].name, n)) {
326 if (foundEntry) {
327 if (n1 < foundLength && n1 < n2) return NULL;
328 if (n2 == foundLength) return NULL;
329 if (n2 < foundLength) continue;
330 }
331 foundEntry = &lscp_reference[i];
332 foundLength = n2;
333 }
334 }
335 return foundEntry;
336 }
337 EOF_BLOCK
338 close(OUT);
339 exit(0); # all done, success

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC