File Coverage

blib/lib/PerlPoint/Tags.pm
Criterion Covered Total %
statement 34 52 65.3
branch 4 24 16.6
condition 6 9 66.6
subroutine 6 8 75.0
pod 2 2 100.0
total 52 95 54.7


line stmt bran cond sub pod time code
1            
2            
3             # = HISTORY SECTION =====================================================================
4            
5             # ---------------------------------------------------------------------------------------
6             # version | date | author | changes
7             # ---------------------------------------------------------------------------------------
8             # 0.05 |05.06.2006| JSTENZEL | documented the new "standalone" configuration;
9             # 0.04 |15.12.2005| JSTENZEL | added POD for public methods to avoid Pod::Coverage
10             # | | | complaints (behaviour was already documented before);
11             # 0.03 |11.10.2001| JSTENZEL | slight POD fix in "NAME" section;
12             # | | JSTENZEL | hooks now receive the body array by reference;
13             # | | JSTENZEL | now hook paramter: anchor object;
14             # |12.10.2001| JSTENZEL | documented new finish hook interface;
15             # |14.10.2001| JSTENZEL | added call();
16             # 0.02 |22.07.2001| JSTENZEL | To make PerlPoint available under perl 5.005 which
17             # | | | is still widely used, I added a hint to include this
18             # | | | module under perl 5.005 (perl 5.6 loads parent modules
19             # | | | automatically). Further more, because explicit loading
20             # | | | causes a perl warning, code was added to suppress these
21             # | | | messages by additional but useless assignments.
22             # 0.01 |19.03.2001| JSTENZEL | new.
23             # ---------------------------------------------------------------------------------------
24            
25             # = POD SECTION =========================================================================
26            
27             =head1 NAME
28            
29             B - processes PerlPoint tag declarations
30            
31             =head1 VERSION
32            
33             This manual describes version B<0.05>.
34            
35             =head1 SYNOPSIS
36            
37             # declare a tag declaration package
38             package PerlPoint::Tags::New;
39            
40             # declare base "class"
41             use base qw(PerlPoint::Tags);
42            
43            
44             =head1 DESCRIPTION
45            
46             PerlPoint is built a modularized way. The base packages provide parsing
47             and stream processing for I translators into target formats and are
48             therefore intended to be as general as possible. That's why they not even
49             define tags, because every translator author may wish to provide own tags
50             special to the addressed target projector (or format, respectively). On
51             the other hand, the parser I to know about tags to recognize them
52             correctly. That is where this module comes in. It serves as a base of
53             tag declaration modules by providing a general C method to be
54             inherited by them. This method scans the invoking module for certain data
55             structures containing tag declarations and imports these data into a
56             structure in its own namespace. The parser knows about this B
57             collection and makes it the base of its tag handling.
58            
59             It is recommended to have a "top level" tag declaration module for each
60             PerlPoint translator, so there could be a C, a
61             C, C, a C
62             and so on. (These modules of course may simply invoke lower level declarations.)
63            
64             Note: We are speaking in terms of "classes" here but of course we are actually
65             only using the mechanism of C together with inheritance to provide
66             an intuitive and easy to use way of declaration.
67            
68             As an additional feature, the module provides a method B to
69             allow translator users to declare tags additionally. See below for details.
70            
71            
72             =head2 Tag declaration by subclasses
73            
74             So to declare tags, just write a module in the B namespace
75             and make it a subclass of B:
76            
77             # declare a tag declaration package
78             package PerlPoint::Tags::New;
79            
80             # declare base "class"
81             use base qw(PerlPoint::Tags);
82            
83             Now the tags can be declared. Tag declarations are expected in a global hash
84             named B<%tags>. Each key is the name of a tag, while descriptions are nested
85             structures stored as values.
86            
87             # pragmata
88             use strict;
89             use vars qw(%tags %sets);
90            
91             # tag declarations
92             %tags=(
93             EMPHASIZE => {
94             # options
95             options => TAGS_OPTIONAL,
96            
97             # don't miss the body!
98             body => TAGS_MANDATORY,
99             },
100            
101             COLORIZE => {...},
102            
103             FONTIFY => {...},
104            
105             ...
106             );
107            
108             This looks complicated but is easy to understand. Each option is decribed by a
109             hash. The I slot just expresses if the body is obsolete,
110             optional or mandatory. This is done by using constants provided by
111             B. Obsolete bodies will not be recognized by the parser.
112            
113             The I slot may be omitted. This means the body is I.
114            
115             There are the same choices for I in general: they may be obsolete,
116             optional or mandatory. If the slot is omitted this means that the tag does
117             not need any options. The parser will I accept a tag option part in this
118             case.
119            
120             To sum it up, options and body of a tag can be declared as mandatory by
121             C, optional by C, or obsolete by
122             C.
123            
124             If you need further checks you can hook into the parser by using the
125             C<"hook"> key:
126            
127             %tags=(
128             EMPHASIZE => {
129             # options
130             options => TAGS_OPTIONAL,
131            
132             # perform special checks
133             hook => sub {
134             # get parameters
135             my ($tagname, $options, $body, $anchor)=@_;
136            
137             # checks
138             $rc=...
139            
140             reply results
141             $rc;
142             }
143             },
144            
145             COLORIZE => {...},
146            
147             FONTIFY => {...},
148            
149             ...
150             );
151            
152             An option hook function receives the tag name, a reference to a hash of
153             option name / value pairs to check, a body array reference and an
154             anchor object. Using the option hash reference, I
155             options>. The passed body array is I of the body part of the stream.
156             The hook therefore cannot modify the body part on parsers side.
157             The anchor object can be used to store new anchors or query anchors already
158             known, see \C for details of this objects interface.
159            
160             The following return codes are defined:
161            
162             =over 4
163            
164             =item PARSING_COMPLETED
165            
166             Parsing will be stopped successfully.
167            
168             =item PARSING_ERASE
169            
170             The parser will throw away the tag I.
171            
172             =item PARSING_ERROR
173            
174             A semantic error occurred. This error will be counted, but parsing will be
175             continued to possibly detect even more errors.
176            
177             =item PARSING_FAILED
178            
179             A syntactic error occured. Parsing will be stopped immediately.
180            
181             =item PARSING_IGNORE
182            
183             The parser will ignore the tag, but stream the body. The result
184             is similar to a source omitting the tag.
185            
186             =item PARSING_OK
187            
188             The checked object is declared to be OK, parsing will be continued.
189            
190             =back
191            
192             Hooks are an interesting way to extend document parsing, but
193             please take into consideration that tag hooks might be called quite often.
194             So, if checks have to be performed, users will be glad if they are
195             performed quickly.
196            
197             And there is another hook interface. It might happen that several
198             operations need parsing to be completed before they can start, like
199             checking an referenced anchor which might be defined I the
200             referencing tag. To handle such situations, a subroutine can be
201             declared as value of key C. The parser will invoke this
202             code when parsing is done I the tag was parsed successfully.
203             (So if a C function returned an error code, the C
204             hook will be ignored.)
205            
206             Here is an example (from an implementation of the basic tag \REF):
207            
208             # afterburner
209             finish => sub
210             {
211             # declare and init variable
212             my $ok=PARSING_OK;
213            
214             # take parameters
215             my ($options, $anchors)=@_;
216            
217             # check link for being valid
218             unless (my $anchor=$anchors->query($options->{name}))
219             {
220             $ok=PARSING_FAILED,
221             warn qq(\n\n[Error] Unknown link address "$options->{name}."\n);
222             }
223             else
224             {
225             # link ok, get value (there is only one key/value pair
226             # in the received hash)
227             ($options->{__value__})=(values %$anchor);
228             }
229            
230             # supply status
231             $ok;
232             },
233            
234            
235             Because several informations are no longer available after parsing,
236             finish hooks have a different interface. They receive options and
237             anchors like parsing hooks, but no line number and no body information.
238            
239             Options can be modified as well as in parsing hooks. Return codes are
240             the same, but are evaluated slightly different according to the
241             invokation time:
242            
243             =over 4
244            
245             =item PARSING_COMPLETED
246            
247             All right. This code is accepted for reasons of convenience,
248             it is recommended to use C instead.
249            
250             =item PARSING_ERASE
251            
252             The backend will ignore the tag and all its contents
253             (which means its body).
254            
255             =item PARSING_ERROR
256            
257             A semantic error occurred. This error will be counted.
258            
259             =item PARSING_FAILED
260            
261             An error occured. Because parsing is already finished,
262             this will be counted as an I error.
263            
264             This code is accepted for reasons of convenience,
265             it is recommended to use C instead.
266            
267             =item PARSING_IGNORE
268            
269             The backend will ignore the tag, but process its body.
270             This simply means that the tag takes no effect.
271            
272             =item PARSING_OK
273            
274             All right.
275            
276             =back
277            
278             The order of finish hook invokation can I from the order
279             of tag usage. Do not depend on it.
280            
281             A finish hook is I invoked unless the tag was processed
282             and streamed successfully at parsing time. This simply means
283             if the parsing hook returned C, or if there was
284             no parsing hook at all.
285            
286            
287             =head2 Marking tags that can act standalone
288            
289             A tag can be part of various paragraphs. A single tag in a
290             paragraph with no prefix produces a text paragraph containing
291             just this tag. This can be intended, but there are other cases
292             when the tag should stand for its own.
293            
294             The I attribute instructs the parser to strip off
295             the wrapping paragraph from a handle that is used as its only
296             content. If there is more content in the paragraph the
297             paragraph wrapper will not be removed.
298            
299             The flag should be set to a true value to activate the
300             occasional paragraph stripping.
301            
302             Example:
303            
304             standalone => 1,
305            
306            
307             =head2 Using other tag definitions
308            
309             One can invoke hooks of I. This is
310             powerful, but dangerous. Nevertheless, it helps to emulate
311             other tags, for example if an old interface (tag and option
312             names) shall be preserved but the new functionality shall
313             be used (without being copied between tag modules). To invoke
314             a foreign hook, call \C (fully
315             qualified function name) with tag name, hook type and
316             parameters, like so:
317            
318             $rc=PerlPoint::Tags::call('TAG', 'hook', @_);
319            
320             Valid hook types are "hook" and "finish" (currently). If the
321             tag is not registered, or has no hook of the specified type,
322             an undefined value is supplied, otherwise the return code of
323             the invoked function.
324            
325             It is not checked if you call a "hook" function from a "finish"
326             hook or vice versa. Take care!
327            
328             This feature is made available to interchange hooks between
329             several tag definition modules. If you want to share hook
330             functions between tags declared by the I tag module, it
331             is recommended to use common Perl techniques.
332            
333            
334             =head2 Tag activation
335            
336             Now, in a translator software where a parser object should be built, tag
337             declarations can be accessed by simply loading the declaration modules,
338             just as usual (there is no need to load B directly there,
339             unless the converter should run under perl 5.005 which I this
340             parent module to be loaded explicitly (while perl 5.6 does is implicitly)):
341            
342             # declare all the tags to recognize
343             use PerlPoint::Tags::New;
344            
345             This updates a structure in the B namespace. The parser
346             knows about this structure and will automatically evaluate it.
347            
348             Several declaration modules can be loaded subsequently. Each I tag is
349             added to the internal structure, while I tags are overwritten
350             by new declarations.
351            
352             # declare all the tags to recognize
353             use PerlPoint::Tags::Basic;
354             use PerlPoint::Tags::HTML;
355             use PerlPoint::Tags::SDF;
356             use PerlPoint::Tags::New;
357            
358            
359             =head2 Activating certain tags
360            
361             Certain translators might only want to support I of tags declared in a
362             B submodule. This is possible as well, similar to the usual
363             importing mechanism:
364            
365             # declare all the tags to recognize
366             use PerlPoint::Tags::New qw(COLORIZE);
367            
368             This does only declare the C tag, but ignores C and C.
369            
370            
371             =head2 Tag sets
372            
373             To simplify activation of certain but numerous tags a declaration module can group
374             them by setting up a global hash named C<%sets>.
375            
376             %sets=(
377             pointto => [qw(EMPHASIZE COLORIZE)],
378             );
379            
380             This allows a translator autor to activate C and C at once:
381            
382             # declare all the tags to recognize
383             use PerlPoint::Tags::New qw(:pointto);
384            
385             The syntax is borrowed from the usual import mechanism.
386            
387             Tag sets can overlap:
388            
389             %sets=(
390             pointto => [qw(EMPHASIZE COLORIZE)],
391             set2 => [qw(COLORIZE FONTIFY)],
392             );
393            
394             And of course they can be nested:
395            
396             %sets=(
397             pointto => [qw(EMPHASIZE COLORIZE)],
398             all => [(':pointto', qw(FONTIFY))],
399             );
400            
401            
402             =head2 Allowing translator users to import foreign tag declarations
403            
404             As PerlPoint provides a flexible way to write translators, PerlPoint documents might
405             be written with tags for a certain translator and later then be processed by another
406             translator which does not support all the original tags. Of course, the second
407             translator does not need to handle these tags, but the parser needs to know they
408             should be recognized. On the other hand, it cannot know this from the declarations
409             made by the second translator itself, because they of course do not contain the
410             tags of the first translator.
411            
412             The problem could be solved if there would be a way to inform the parser about the
413             tags initially used. That's why this module provides B, a method that
414             imports foreign declarations at run time. Suppose a translator provides an option
415             C<-tagset> to let a user specify which tag sets the document was initially written
416             for. Then the following code makes them known to the parser, addtionally to the
417             declarations the translator itself already made as usual (see above):
418            
419             # load module to access the function
420             use PerlPoint::Tags;
421            
422             # get options
423             ...
424            
425             # import foreign tags
426             PerlPoint::Tags::addTagSets(@{$options{tagset}})
427             if exists $options{tagset};
428            
429             (Note: this example is based on the B option access interface.
430             Other interfaces might need adaptations.)
431            
432             Tags imported via C do I overwrite original definitions.
433            
434             A "tag set", in this context, is the set of tag declarations a certain translator
435             makes. So, the attributes to B are expected to be target languages
436             corresponding to the translators name, making usage easy for the user. So, pp2I
437             is expected to provide a "tag set" declaration module PerlPoint::Tags::B,
438             pp2html PerlPoint::Tags::B, pp2xml PerlPoint::Tags::B and so on.
439            
440             If all translators provide this same interface, usage should be easy. A user
441             who wrote a document with C in mind, passing it to C which provides
442             significantly less tags, only has to add the option C<"-tagset HTML"> to the
443             C call to make his document pass the PerlPoint parser.
444            
445             =head1 METHODS
446            
447             =cut
448            
449            
450            
451            
452             # check perl version
453             require 5.00503;
454            
455             # = PACKAGE SECTION (internal helper package) ==========================================
456            
457             # declare package
458             package PerlPoint::Tags;
459            
460             # declare package version (as a STRING!!)
461             $VERSION="0.05";
462            
463            
464             # = PRAGMA SECTION =======================================================================
465            
466             # set pragmata
467 8     8   63774 use strict;
  8         18  
  8         371  
468 8     8   48 use vars qw($tagdefs $multiplicityIgnored);
  8         17  
  8         474  
469            
470             # = LIBRARY SECTION ======================================================================
471            
472             # load modules
473 8     8   47 use Carp;
  8         16  
  8         636  
474 8     8   950 use PerlPoint::Constants 0.13 qw(:tags);
  8         235  
  8         1481  
475            
476            
477             # = CODE SECTION =========================================================================
478            
479             sub import
480             {
481             # get class name and extract it from arguments
482 16     16   6825 my $class=shift;
483            
484             # declare variables
485 16         32 my ($tags, $sets);
486            
487             # build shortcuts
488             {
489 8     8   48 no strict qw(refs);
  8         30  
  8         9546  
  16         26  
490 16         33 $tags=\%{join('::', $class, 'tags')};
  16         98  
491 16         35 $sets=\%{join('::', $class, 'sets')};
  16         79  
492            
493             # next lines only added to avoid perl warnings (which cannot be switched off)
494 16         32 $tags=\%{join('::', $class, 'tags')};
  16         63  
495 16         27 $sets=\%{join('::', $class, 'sets')};
  16         58  
496             }
497            
498             # process *all* the tags by default
499 16 50       190 @_=sort keys %$tags unless @_;
500            
501             # process all declared symbols
502 16         319 while (my $declared=uc(shift))
503             {
504             # set?
505 92 50       243 if ($declared=~s/^:(.+)$/$1/)
506             {
507             # check if this set was declared (and declared as expected)
508 0 0       0 confess "[BUG] Use of undeclared tag set $declared in class $class" unless exists $sets->{$declared};
509 0 0       0 confess "[BUG] Set $declared of class $class was not declared by an array reference" unless ref($sets->{$declared}) eq 'ARRAY';
510            
511             # add all set tags to the list
512 0         0 unshift(@_, sort @{$sets->{$declared}});
  0         0  
513            
514             # next turn
515 0         0 next;
516             }
517            
518             # ok, this is a tag - first, check if it was declared
519 92 50       380 confess "[BUG] Use of undeclared tag $declared in class $class" unless exists $tags->{$declared};
520            
521             # does the new tag overwrite an earlier declaration?
522 92 50       206 if (exists $tagdefs->{$declared})
523             {
524             # ignore new settings, if necessary
525 0 0       0 next if $multiplicityIgnored;
526            
527             # earlier settings will be overwritten, inform user
528 0         0 carp "[Warn] Tag $declared declared multiply, replacing data!\n";
529             }
530            
531             # all right, register it
532 92         266 $tagdefs->{$declared}=$tags->{$declared};
533            
534             # immediately research it and make internal shortcuts
535 92   100     620 $tagdefs->{$declared}{__flags__}{__body__}= # body flag:
536             (
537             not exists $tagdefs->{$declared}{body} # lazy declaration: default is to expect a body;
538             or not $tagdefs->{$declared}{body} & TAGS_DISABLED # no explicitly disabled body;
539             );
540            
541            
542 92   100     104441 $tagdefs->{$declared}{__flags__}{__options__}= # option flags:
543             (
544             exists $tagdefs->{$declared}{options} # default is to skip options;
545             and not $tagdefs->{$declared}{options} & TAGS_DISABLED # no explicitly disabled options;
546             );
547             }
548             }
549            
550            
551             =pod
552            
553             =head2 addTagSets()
554            
555             Imports tagsets. See "Allowing translator users to import foreign tag declarations" for details.
556            
557             =cut
558             sub addTagSets
559             {
560             # accept multiplicity
561 0     0 1   local($multiplicityIgnored)=1;
562            
563             # process all arguments
564 0           foreach my $set (@_)
565             {
566 0           eval join('::', 'use PerlPoint::Tags', $set);
567 0 0         warn "[Warn] Could not import declarations of PerlPoint::Tags::$set.\n" if $@;
568             }
569             }
570            
571            
572             =pod
573            
574             =head2 call()
575            
576             Calls a hook function of a registered tag. See "Using other tag definitions" for details.
577            
578             =cut
579             sub call
580             {
581             # get and check parameters
582 0     0 1   my ($tag, $type, @pars)=@_;
583 0 0         confess "[BUG] Missing tag name.\n" unless $tag;
584 0 0         confess "[BUG] Missing function type.\n" unless $type;
585 0 0         confess "[BUG] Invalid function type \"$type\".\n" unless $type=~/^(finish|hook)$/;
586            
587             # flag an error in several cases, let the caller deal with it
588 0 0 0       return undef unless exists $tagdefs->{$tag}
589             and exists $tagdefs->{$tag}{$type};
590            
591             # ok, call the function
592 0           &{$tagdefs->{$tag}{$type}}(@pars);
  0            
593             }
594            
595            
596            
597             # flag successful loading
598             1;
599            
600             # = POD TRAILER SECTION =================================================================
601            
602             =pod
603            
604             =head1 NOTES
605            
606             The form of tag declaration provided by this module is designed to make
607             tag activation intuitive to write and read. Ideally, declarations are
608             written by one author, but used by several others.
609            
610             Each tag declaration module should provide a tag description in PerlPoint.
611             This allows translator authors to easily integrate tag descriptions into
612             their own documentations.
613            
614             Tag declarations have nothing to do with the way backends (translators) handle
615             recognized tags. They only enable tag detection and a few simple semantic checks
616             by the parser. A translator has still to implement its tag handling itself.
617            
618             There are no tag namespaces. Although Perl modules are used to declare the
619             tags, tags declared by various C share the same one
620             global scope. This means that different tags should be I different.
621             B displays a warning if a tag is overwritten by another one.
622            
623            
624             =head1 SEE ALSO
625            
626             =over 4
627            
628             =item B
629            
630             The parser module working on base of the declarations.
631            
632             =item B
633            
634             Various declaration modules.
635            
636             =back
637            
638            
639             =head1 SUPPORT
640            
641             A PerlPoint mailing list is set up to discuss usage, ideas,
642             bugs, suggestions and translator development. To subscribe,
643             please send an empty message to perlpoint-subscribe@perl.org.
644            
645             If you prefer, you can contact me via perl@jochen-stenzel.de
646             as well.
647            
648             =head1 AUTHOR
649            
650             Copyright (c) Jochen Stenzel (perl@jochen-stenzel.de), 1999-2001.
651             All rights reserved.
652            
653             This module is free software, you can redistribute it and/or modify it
654             under the terms of the Artistic License distributed with Perl version
655             5.003 or (at your option) any later version. Please refer to the
656             Artistic License that came with your Perl distribution for more
657             details.
658            
659             The Artistic License should have been included in your distribution of
660             Perl. It resides in the file named "Artistic" at the top-level of the
661             Perl source tree (where Perl was downloaded/unpacked - ask your
662             system administrator if you dont know where this is). Alternatively,
663             the current version of the Artistic License distributed with Perl can
664             be viewed on-line on the World-Wide Web (WWW) from the following URL:
665             http://www.perl.com/perl/misc/Artistic.html
666            
667            
668             =head1 DISCLAIMER
669            
670             This software is distributed in the hope that it will be useful, but
671             is provided "AS IS" WITHOUT WARRANTY OF ANY KIND, either expressed or
672             implied, INCLUDING, without limitation, the implied warranties of
673             MERCHANTABILITY and FITNESS FOR A PARTICULAR PURPOSE.
674            
675             The ENTIRE RISK as to the quality and performance of the software
676             IS WITH YOU (the holder of the software). Should the software prove
677             defective, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
678             CORRECTION.
679            
680             IN NO EVENT WILL ANY COPYRIGHT HOLDER OR ANY OTHER PARTY WHO MAY CREATE,
681             MODIFY, OR DISTRIBUTE THE SOFTWARE BE LIABLE OR RESPONSIBLE TO YOU OR TO
682             ANY OTHER ENTITY FOR ANY KIND OF DAMAGES (no matter how awful - not even
683             if they arise from known or unknown flaws in the software).
684            
685             Please refer to the Artistic License that came with your Perl
686             distribution for more details.
687