line
stmt
bran
cond
sub
pod
time
code
1
2
# ATTENTION:
3
#
4
# This is NO module and NO script, but a GRAMMAR. To build the module,
5
# call "yapp -[s]m PerlPoint::Parser -o PP/Parser.pm ". This builds
6
# a module "Parser.pm" in the subdirectory "PP".
7
#
8
# The "yapp" script mentioned above comes with Parse::Yapp.
9
10
%{
11
12
# = HISTORY SECTION =====================================================================
13
14
# ---------------------------------------------------------------------------------------
15
# version | date | author | changes
16
# ---------------------------------------------------------------------------------------
17
# 0.452 |10.10.2007| JSTENZEL | just for the release;
18
# 0.451 |06.10.2007| JSTENZEL | just for the release;
19
# 0.45 |03.12.2006| JSTENZEL | just for the release;
20
# 0.44 |15.06.2006| JSTENZEL | new type "parsedexample" for \INCLUDE;
21
# |06.08.2006| JSTENZEL | bugfix in parameter check of _evalTagCondition(): using
22
# | | | "if defined $par" instead of "if $par";
23
# |27.11.2006| JSTENZEL | better definition of flagSet() etc., old implementation
24
# | | | was buggy;
25
# 0.43 |09.04.2006| JSTENZEL | slight code optimizations in file embedding;
26
# | | JSTENZEL | INCLUDE now has an "import" option with module API;
27
# | | JSTENZEL | included file type and embedded language now have
28
# | | | default "pp";
29
# | | JSTENZEL | run(): new configuration parameter "importMapping";
30
# 0.42 |05.03.2006| JSTENZEL | non kernel tags now can be configured to be standalone,
31
# | | | in which case a wrapping paragraph is removed from the
32
# | | | stream (IMAGE and LOCALTOC configuration moved to tag
33
# | | | definition);
34
# | | | area, the wrapping paragraph is removed;
35
# |07.03.2006| JSTENZEL | dummy tokens inserted by the parser now are special
36
# | | | strings that can be filtered out by the backend module;
37
# |10.03.2006| JSTENZEL | bugfix: statistics for list shifters did not work;
38
# | | JSTENZEL | Macro default parameters were not documented! Added.
39
# 0.41 |15.12.2005| JSTENZEL | almost all routines are internal, to avoid Pod::Coverage
40
# | | | complaints they now begin with an underscore;
41
# 0.40 |12.06.2003| JSTENZEL | bugfix: delayed tokens were not reparsed when reinserted,
42
# | | | this could cause trouble when the paragraph (special
43
# | | | characters) context changed between the point the
44
# | | | token was detected first and delayed, and the point
45
# | | | the token is reinserted into the stream (especially
46
# | | | important after file inclusion, when the stacked token
47
# | | | is a newline, has to be evaluated in STATE_DEFAULT
48
# | | | but was stacked in a paragraph where newlines are not
49
# | | | ignored;
50
# | | JSTENZEL | additionally, "empty paragraphs" (*skipped* paragraphs)
51
# | | | now are not only *really* empty paragraphs but all
52
# | | | paragraphs containing of whitespaces only;
53
# |21.06.2003| JSTENZEL | headlines provide additional data: their numerical, full
54
# | | | and shortcut pathes;
55
# |22.06.2003| JSTENZEL | _normalizeTableRows() now supplies number of columns both
56
# | | | in title row and the maximum value;
57
# | | JSTENZEL | new warning if the maximum columns number is detected
58
# | | | in another line than the first table line (which is the
59
# | | | base of normalization);
60
# |10.08.2003| JSTENZEL | new helper function _semerr() to report semantic errors;
61
# | | JSTENZEL | new option -criticalSemanticErrors;
62
# |14.08.2003| JSTENZEL | input filters can access the source file by a variable
63
# | | | $main::_ifilterFile now;
64
# | | JSTENZEL | fixed an "undefined value" warning;
65
# |17.08.2003| JSTENZEL | bugfix: docstream "main" was ignored like any other docstream
66
# | | | if working in the "docstream ignore" mode;
67
# |10.09.2003| JSTENZEL | definition list explanations ("texts" now have an own
68
# | | | enveloping directive (DIRECTIVE_DPOINT_TEXT);
69
# |11.09.2003| JSTENZEL | LOCALTOC added to the list of standalone tags (which are
70
# | | | stripped of of an enveloping text paragraph if they are its
71
# | | | only contents);
72
# |05.05.2004| JSTENZEL | anchors now take the number of the page they are defined in;
73
# | | JSTENZEL | tag hooks now take an additional parameter: the number of
74
# | | | the page the tag is used on;
75
# | | JSTENZEL | bugfix: numerical pathes were built incorrectly: when entering
76
# | | | a new sublevel, the counter was not reset to 1;
77
# | | JSTENZEL | added anchors();
78
# |11.07.2004| JSTENZEL | headlines now provide a path of absolute page numbers as well
79
# | | | and a variable snapshot;
80
# | | JSTENZEL | a reset variable is removed now (as a side effect, it is no
81
# | | | longer possible to build variables containing spaces only);
82
# |24.07.2004| JSTENZEL | added -skipcomments;
83
# |10.09.2004| JSTENZEL | bugfix: words looking like symbolic variables (but not defined
84
# | | | as such) were restored without their braces ("{}");
85
# |27.12.2004| JSTENZEL | bugfix: skipped headline levels were filled with previous
86
# | | | headline strings of those levels;
87
# |28.12.2004| JSTENZEL | text paragraphs now have their own special character, but
88
# | | | optional: a dot;
89
# |24.02.2005| JSTENZEL | acceleration: the lexer built some data very often;
90
# |27.02.2005| JSTENZEL | bugfix: backslashes before variables were handled incorrectly,
91
# | | | now variables are no longer "boosted" but handled like macros
92
# | | | - which has a performance drawback, unfortunately ...;
93
# |16.05.2005| JSTENZEL | backslashes in tag options are no longer ignored but can be
94
# | | | used to guard characters;
95
# |23.08.2005| JSTENZEL | first chapter is checked for a headline now;
96
# 0.39 |01.02.2003| JSTENZEL | passing directive id chain of the current chapter
97
# | | | headline to tag hook functions now;
98
# |07.03.2003| JSTENZEL | several variable patterns were used explicitly instead
99
# | | | if the precompiled ones from %lexerPatterns;
100
# | | JSTENZEL | bugfix: guarded variables were expanded;
101
# | | JSTENZEL | now it is documented that list indentation is reset
102
# | | | automatically by a subsequent non list paragraph;
103
# |26.04.2003| JSTENZEL | added "no utf8" to avoid errors under perl 5.8;
104
# |01.05.2003| JSTENZEL | adding *all* composite anchors for headlines, not only
105
# | | | for the full path;
106
# 0.38 |07.06.2002| JSTENZEL | restoring doubled backslashes in filtered paragraphs,
107
# | | | restoring ">" characters as if they were guarded;
108
# |04.07.2002| JSTENZEL | simplified several array field access codes;
109
# | | JSTENZEL | bugfix: empty headlines caused an infinite loop
110
# | | | when trailing whitespaces should be removed;
111
# | | JSTENZEL | bugfix: empty headlines caused a failure when headline
112
# | | | anchors should be stored, skipping them now;
113
# |20.08.2002| JSTENZEL | improved tag streaming: stream now contains a body hint;
114
# | | JSTENZEL | bugfix: paragraph filters restored tag bodies even if
115
# | | | there was no body;
116
# | | JSTENZEL | old caches need to be updated - adapted compatibility hint;
117
# |27.08.2002| JSTENZEL | started to use precompiled lexer patterns;
118
# |31.08.2002| JSTENZEL | \INCLUDE, \EMBED and \TABLE now support the _cnd_ option,
119
# | | | like tags defined externally;
120
# |04.12.2002| JSTENZEL | bugfix in pfilter retranslation: backslash reinsertion was
121
# | | | not performed multiply;
122
# | | JSTENZEL | pfilter retranslation: backslash reinsertion now suppressed
123
# | | | in verbatim blocks;
124
# |01.01.2003| JSTENZEL | added input filter support to \EMBED, via option "ifilter";
125
# |02.01.2003| JSTENZEL | added input filter support to \INCLUDE, same interface;
126
# 0.37 |up to | JSTENZEL | flagSet() now takes a list of flag names;
127
# |14.04.2002| JSTENZEL | names of included files are resolved to avoid trouble
128
# | | | with links (and to avoid error messages);
129
# | | JSTENZEL | \INCLUDE searches pathes specified in environment
130
# | | | variable PERLPOINTLIB (like perl, shells, linkers etc.);
131
# | | JSTENZEL | if tags with finish hooks are used, a paragraph will
132
# | | | not be cached because it becomes potentially dynamic;
133
# | | JSTENZEL | anchors defined by a cached paragraph are cached now
134
# | | | as well - and restored after a cache hit (updated cache
135
# | | | format);
136
# | | JSTENZEL | \INCLUDE additionally searches pathes specified in an
137
# | | | array passed to method run() via new parameter "libpath";
138
# | | JSTENZEL | Filtered paragraphs that need a parser lookahead into
139
# | | | the next paragraph to be completely detected could cause
140
# | | | trouble because the reinserted result was grammatically
141
# | | | placed before the already parsed start token of the
142
# | | | subsequent paragraph. Fixed by introducing a virtual,
143
# | | | empty "Word" token supplied by the lexer in such cases
144
# | | | (look for $flags{virtualParagraphStart} and
145
# | | | $lexerFlags{cbell}). (By the way, this outdated an
146
# | | | earlier solution using a virtual text paragraph startup
147
# | | | and a delayed token - this former solution caused trouble
148
# | | | when the paragraph following the filtered one was not
149
# | | | a pure text (so even filtered texts did not work)).
150
# | | JSTENZEL | Filtered paragraphs are no longer cached - the filter
151
# | | | makes them dynamical. Note that for combined paragraphs
152
# | | | like compound blocks and lists this is true for the first
153
# | | | part only, because subsequent parts can be cached in
154
# | | | their original form (the filter will be applied when the
155
# | | | parts will have been combined).
156
# | | JSTENZEL | paragraph filters: added retranslation of headlines and
157
# | | | verbatim blocks;
158
# | | JSTENZEL | passing original paragraph type to filters by new variable
159
# | | | $main::_pfilterType;
160
# | | JSTENZEL | generalized paragraph type constant to string translation;
161
# | | JSTENZEL | lexer delays to flag the end of the document source
162
# | | | when a paragraph filter still needs to be applied
163
# | | | (otherwise, the parser would not request more tokens
164
# | | | because from his point of view the source was already
165
# | | | parsed completely, so the filtering result (and the
166
# | | | original block) would disappear from the result - it would
167
# | | | not be reparsed);
168
# | | JSTENZEL | empty text paragraphs are no longer made part of the stream;
169
# | | JSTENZEL | blocks were streamed with a final newline, improved;
170
# | | JSTENZEL | added headline shortcuts;
171
# | | JSTENZEL | added document stream entry points;
172
# |15.04.2002| JSTENZEL | added chapter docstream hints to headline stream data;
173
# 0.36 |10.08.2001| JSTENZEL | the stream became a more complex data structure to
174
# | | | allow converter authors to act according to a documents
175
# | | | structure (getting headlines without having to process
176
# | | | all tokens, moving between chapters) - basically, it
177
# | | | *remained* a stream (with additional structure info);
178
# |29.09.2001| JSTENZEL | adapted stream initialization to intermediately
179
# | | | modified design;
180
# | | JSTENZEL | bugfixes in _normalizeTableRows(): standalone single "0"
181
# | | | in table cells was removed;
182
# |07.10.2001| JSTENZEL | improved error messages provide an error pointer;
183
# |11.10.2001| JSTENZEL | removed unused "use fields" directive;
184
# | | JSTENZEL | storing headline anchors now, depending on new
185
# | | | flag headlineLinks;
186
# | | JSTENZEL | modified tag hook interface, tag body array is now
187
# | | | passed by *reference*;
188
# | | JSTENZEL | passing anchor object to tag hooks;
189
# |12.10.2001| JSTENZEL | added tag finish hook interface;
190
# |13.10.2001| JSTENZEL | list shifts are no longer flagged by DIRECTIVE_START
191
# | | | *and* DIRECTIVE_COMPLETED, no just by DIRECTIVE_START;
192
# | | JSTENZEL | headline start directives in the stream now provide
193
# | | | the full (plain) headline;
194
# | | JSTENZEL | added tag conditions;
195
# |14.10.2001| JSTENZEL | bugfix: passed tag options to parsing hooks instead
196
# | | | of tag body;
197
# | | JSTENZEL | using new stream directive index constants;
198
# | | JSTENZEL | stream directives now begin with a hash reference to
199
# | | | pass backend hints;
200
# |17.10.2001| JSTENZEL | new directive format results in modified cache format,
201
# | | | adapted automatic update;
202
# |27.10.2001| JSTENZEL | list directives now contain hints about predecessing
203
# | | | or following list shifts;
204
# |29.10.2001| JSTENZEL | added paragraph filters (in a first version for verb. blocks);
205
# |16.11.2001| JSTENZEL | improved _Error();
206
# | | JSTENZEL | improved lexer traces (did hide lines in verb. blocks and
207
# | | | comments;
208
# | | JSTENZEL | Heredoc close sequence detection is no longer restricted
209
# | | | to original source lines but also active for lines gotten
210
# | | | from stack - this became possible because verbatim block
211
# | | | lines are scanned in *completely* since version 0.34.
212
# | | | As a result, it is possible now to generate verbatim
213
# | | | blocks via active contents, but it is still impossible
214
# | | | to do this for blocks and text paragraphs beginning with
215
# | | | a tag or macro.
216
# |17.11.2001| JSTENZEL | implemented a more general paragraph filter approach
217
# | | | (still incomplete: needs to be extended for lists, needs
218
# | | | retranslation of paragraph stream into text);
219
# |18.11.2001| JSTENZEL | slightly improved _stackInput() (initially empty lines
220
# | | | buffers would have been stacked, and a final buffer value
221
# | | | of "0" would have been ignored);
222
# | | JSTENZEL | detection of block starts and text paragraphs beginning
223
# | | | with a line now take stacked lines into consideration
224
# | | | - this was suppressed because stacked input can begin
225
# | | | anywhere in a real line and not just at the beginning,
226
# | | | but now it is checked if there was a trailing \n in the
227
# | | | previous stack entry (we do not have to check previous
228
# | | | non stacked lines because there is no way to produce
229
# | | | a beginning paragraph on the stack without a leading
230
# | | | (and therefore stacked) empty line);
231
# | | JSTENZEL | text passed to paragraph filters is now retranslated from
232
# | | | the paragraphs streams (implementation still incomplete);
233
# |21.11.2001| JSTENZEL | macro definitions can now optionally take option defaults;
234
# |22.11.2001| JSTENZEL | bugfix in macro definition tag option handling: no boost!;
235
# |01.12.2001| JSTENZEL | tables can be filtered now;
236
# | | JSTENZEL | Compound paragraphs can be filtered now!
237
# | | JSTENZEL | lists can be filtered now, added retranslation parts;
238
# | | JSTENZEL | slightly restructered lexer parts: new _lineStartResearch();
239
# |02.12.2002| JSTENZEL | slightly restructered lexer parts: new _refLexed()
240
# | | | (to detect streamed parts placed in the input line, must
241
# | | | have beed happened before as well??);
242
# 0.35 |16.06.2001| JSTENZEL | text paragraphs containing an image only are now
243
# | | | transformed into just the image;
244
# |22.07.2001| JSTENZEL | in order to make it run under 5.005 again, a pseudo
245
# | | | hash was replaced by a pure and simple standard hash;
246
# |22.07.2001| JSTENZEL | improved the "specials" pattern in lexer() by guarding "-";
247
# |23.07.2001| JSTENZEL | opening input files in binmode() for Windows compatibility;
248
# 0.34 |14.03.2001| JSTENZEL | added parsing time report;
249
# | | JSTENZEL | slight code optimizations;
250
# |20.03.2001| JSTENZEL | introduced tag templates declared via PerlPoint::Tags:
251
# |22.03.2001| JSTENZEL | bugfix: macros could not contain "0":
252
# | | JSTENZEL | comments are now read at once, no longer lexed and parsed,
253
# | | | likewise, verbatim block lines are handled as one word;
254
# |25.03.2001| JSTENZEL | special character activation in tags is now nearer to the
255
# | | | related grammatical constructs, so "<" is no longer a
256
# | | | special after the tag body is opened;
257
# | | JSTENZEL | completed tag template interface by checks of mandatory
258
# | | | parts and hooks into the parser to check options and body;
259
# |01.04.2001| JSTENZEL | paragraphs using macros or variables are cached now -
260
# | | | they can be reused unless macro/variable settings change;
261
# | | JSTENZEL | cache structure now stores parser version for compatibility
262
# | | | checks;
263
# |08.04.2001| JSTENZEL | removed ACCEPT_ALL support;
264
# | | JSTENZEL | improved special character handling in tag recognition
265
# | | | furtherly: "=" is now very locally specialized;
266
# | | JSTENZEL | tag option and body hooks now take the tag occurence line
267
# | | | number as their first argument, not the tag name which is
268
# | | | of course already known to the hook function author;
269
# | | JSTENZEL | The new macro caching feature allowed to improve the cache
270
# | | | another way: constructions looking like a tag or macro but
271
# | | | being none of them were streamed and cached like strings
272
# | | | (because they *were* strings). If later on somebody declared
273
# | | | such a macro, the cache still found the paragraph unchanged
274
# | | | (same checksum) and reused the old stream instead of building
275
# | | | a new stream on base of the resolved macro. Now, if something
276
# | | | looks like a macro, the macro cache checksum feature is
277
# | | | activated, so every later macro definition will prevent the
278
# | | | cached string representation of being reused. Instead of
279
# | | | this, the new macro will be resolved, and the new resulting
280
# | | | paragraph stream will be cached. This is by far more
281
# | | | transparent and intuitive.
282
# |11.04.2001| JSTENZEL | added predeclared variables;
283
# |19.04.2001| JSTENZEL | embedded Perl code offering no code is ignored now;
284
# |21.04.2001| JSTENZEL | replaced call to Parse::Yapps parser object method YYData()
285
# | | | by direct access to its built in hash entry USER as suggested
286
# | | | by the Parse::Yapp manual for reasons of efficiency;
287
# | | JSTENZEL | bugfix: all parts restored from @inputStack were handled as
288
# | | | new lines which caused several unnecessay operations including
289
# | | | line number updates, cache paragraph checksumming and
290
# | | | removal of "leading" whitespaces (tokens recognized as Ils
291
# | | | while we were still in a formerly started line) - this fix
292
# | | | should accelerate processing of documents using numerous
293
# | | | macros (when cached) and of course avoid invalid token removals;
294
# | | JSTENZEL | tables are now "normalized": if a table row contains less
295
# | | | columns than the headline row, the missed columns are
296
# | | | automatically added (this helps converters to detect empty columns);
297
# | | JSTENZEL | bugfix: internal table flags were not all reset if a table
298
# | | | was completed, thus causing streams for subsequent tables
299
# | | | being built with additional, incorrect elements;
300
# | | JSTENZEL | adapted macro handling to the new tag handling: if now options or
301
# | | | or body was declared in the macro definition, options or body are
302
# | | | not evaluated
303
# |22.04.2001| JSTENZEL | the first bugfix yesterday was too common, improved;
304
# |24.04.2001| JSTENZEL | bugfix: conditions were handled in headline state, causing
305
# | | | backslashes to be removed; new state STATE_CONDITION added;
306
# | | JSTENZEL | added first function (flagSet()) of a simplified condition
307
# | | | interface (SCI) which is intended to allow non (Perl) programmers
308
# | | | to easily understand and perform common checks;
309
# |27.04.2001| JSTENZEL | $^W is a global variable - no need to switch to the Safe
310
# | | | compartment to modify it;
311
# | | JSTENZEL | added next function (varValue()) of a the SCI;
312
# |29.04.2001| JSTENZEL | now the parser predeclares variables as well: first one is
313
# | | | $_STARTDIR to flag where processing started;
314
# |21.05.2001| JSTENZEL | bugfix in table handling: one column tables were not handled
315
# | | | correctly, modified table handling partly by the way si that
316
# | | | in the future it might become possible to have nested tables;
317
# |22.05.2001| JSTENZEL | source nesting level is now reported by an internal variable _SOURCE_LEVEL;
318
# |23.05.2001| JSTENZEL | table fields are trimmed now: beginning and trailing whitespaces are removed;
319
# |24.05.2001| JSTENZEL | text paragraphs containing only a table become just a table now;
320
# |24.05.2001| JSTENZEL | text paragraphs now longer contain a final whitespace (made from the
321
# | | | final carriage return;
322
# |25.05.2001| JSTENZEL | completed support for the new \TABLE flag option "rowseparator" which
323
# | | | allows you to separate table columns by a string of your choice enabling
324
# | | | streamed tables like in
325
# | | | "Look: \TABLE{rowseparator="+++"} c1 | c2 +++ row 2, 1 | row 2, 2 \END_TABLE";
326
# | | JSTENZEL | slightly reorganized the way tag build table streams are completed,
327
# | | | enabling a more common detection of prebuild stream parts - in fact, if
328
# | | | this description makes no sense to you, this enables to place \END_TABLE
329
# | | | even *in* the final table line instead of in a new line (as usually done
330
# | | | and documented);
331
# |26.05.2001| JSTENZEL | added new parser option "nestedTables" which enables table nesting if set
332
# | | | to a true value. made nesting finally possible;
333
# | | JSTENZEL | to help converters handling nested tables, tables now provide their
334
# | | | nesting level by the new internal table option "__nestingLevel__";
335
# |27.05.2001| JSTENZEL | cache hits are no longer mentioned in the list of expected tokens displayed
336
# | | | by _Error(), because the message is intended to be read by humans who
337
# | | | cannot insert cache hits into a document;
338
# |28.05.2001| JSTENZEL | new predeclared variable _PARSER_VERSION;
339
# | | JSTENZEL | new \INCLUDE option "localize";
340
# |31.05.2001| JSTENZEL | new headline level offset keyword "base_level";
341
# |01.06.2001| JSTENZEL | performance boost by lexing words no longer as real words or even
342
# | | | characters but as the longest strings until the next special character;
343
# |02.06.2001| JSTENZEL | improved table field trimming in _normalizeTableRows();
344
# |05.06.2001| JSTENZEL | the last line in a source file is now lexed the optimized way as well;
345
# |06.06.2001| JSTENZEL | cache structure now stores constant declarations version for compatability
346
# | | | checks;
347
# |09.06.2001| JSTENZEL | bugfix: headlines could not begin with a character that can start a
348
# | | | paragraph - fixed by introducing new state STATE_HEADLINE_LEVEL;
349
# | | JSTENZEL | variable names can contain umlauts now;
350
# | | JSTENZEL | updated inlined module documentation (POD);
351
# | | JSTENZEL | used Storable version is now stored in cache, cache is rebuilt
352
# | | | automatically if a different Storable version is detected;
353
# |10.06.2001| JSTENZEL | added code execution by eval() (on users request);
354
# |12.06.2001| JSTENZEL | code executed by eval() or do() is no started with "no strict" settings
355
# | | | to enable unlimited access to functions, like under Safe control
356
# | | | (also this is by no means optimal so it might be improved later);
357
# | | JSTENZEL | tag hooks can reply various values now;
358
# |15.06.2001| JSTENZEL | tags take exactly *one* hook into consideration now: this simplifies
359
# | | | and accelerates the interface *and* allows hooks for tags neither
360
# | | | owning option nor nody;
361
# 0.33 |22.02.2001| JSTENZEL | slightly improved PerlPoint::Parser::DelayedToken;
362
# |25.02.2001| JSTENZEL | variable values can now begin with every character;
363
# |13.03.2001| JSTENZEL | bugfix in handling cache hits for continued ordered lists:
364
# | | | list numbering is updated now;
365
# |14.03.2001| JSTENZEL | added mailing list hint to POD;
366
# | | JSTENZEL | undefined return values of embedded Perl are no longer
367
# | | | tried to be parsed, this is for example useful to
368
# | | | predeclare functions;
369
# | | JSTENZEL | slight bugfix in internal ordered list counting which
370
# | | | takes effect if an ordered list is *started* by "##";
371
# 0.32 |07.02.2001| JSTENZEL | bugfix: bodyless macros can now be used without moving
372
# | | | subsequent tokens before the macro replacement;
373
# |10.02.2001| JSTENZEL | added new special type "example" to \INCLUDE to relieve
374
# | | | people who want to include files just as examples;
375
# 0.31 |30.01.2001| JSTENZEL | ordered lists now provide the entry level number
376
# | | | (additionally to the first list point which already
377
# | | | did this if the list was continued);
378
# |01.02.2001| JSTENZEL | made POD more readable to pod2man;
379
# | | JSTENZEL | bugfix: if a headline is restored from cache, internal
380
# | | | headline level flags need to be restored as well to
381
# | | | make \INCLUDE{headlinebase=CURRENT_LEVEL} work when
382
# | | | it is the first thing in a document except of headlines
383
# | | | which are all restored from cache;
384
# | | JSTENZEL | new "smart" option of \INCLUDE tag suppresses inclusion
385
# | | | if the file was already loaded, which is useful for alias
386
# | | | definitions used both in a nested and the base source;
387
# |02.02.2001| JSTENZEL | bugfix: circular source nesting was supressed too hard:
388
# | | | a source could never be loaded twice, but this may be
389
# | | | really useful to reuse files multiply - now only the
390
# | | | currently nested sources are taken into account;
391
# |03.02.2001| JSTENZEL | bugfix: continued lists did not work as expected yet,
392
# | | | now they do (bug was detected by Lorenz), improved by
393
# | | | the way: continued list points not really continuing
394
# | | | are streamed now as usual list points (no level hint);
395
# 0.30 |05.01.2001| JSTENZEL | slight lexer improvement (removed obsolete code);
396
# | | JSTENZEL | modified the grammar a way that shift/reduce conflicts
397
# | | | were reduced (slightly) and, more important, the grammar
398
# | | | now passes yacc/bison (just a preparation);
399
# |20.01.2001| JSTENZEL | variable settings are now propagated into the stream;
400
# | | JSTENZEL | improved syntactical error messages;
401
# |23.01.2001| JSTENZEL | bugfix: embedding into tags failed because not all
402
# | | | special settings were restored correctly, especially
403
# | | | for ">" which completes a tag body;
404
# |27.01.2001| JSTENZEL | fixed "unintialized value" warning (cache statistics);
405
# | | JSTENZEL | tag implementation is now more restrictive: according
406
# | | | to the language definition tag and macro names now *have*
407
# | | | to be built from capitals and underscores, thus reducing
408
# | | | potential tag recognition confusion with ACCEPT_ALL;
409
# | | JSTENZEL | lowercased alias names in alias definitions are now
410
# | | | automatically converted into capitals because of the
411
# | | | modfied tag/macro recognition just mentioned before;
412
# | | JSTENZEL | POD: added a warning to the POD section that the cache
413
# | | | should be cleansed after introducing new macros which
414
# | | | could possibly be used as simple text before;
415
# 0.29 |21.12.2000| JSTENZEL | direct setting of $VERSION variable to enable CPAN to
416
# | | | detect and display the parser module version;
417
# | | JSTENZEL | introduced base settings for active contents provided
418
# | | | in %$PerlPoint - a new common way to pass things like
419
# | | | the current target language;
420
# |27.12.2000| JSTENZEL | closing angle brackets are to be guarded only *once*
421
# | | | now - in former versions each macro level added the need
422
# | | | of yet another backslash;
423
# |28.12.2000| JSTENZEL | macro bodies are no longer reparsed which accelerates
424
# | | | procesing of nested macros drastically (and avoids the
425
# | | | overhead and dangers of rebuilding a source string and
426
# | | | parsing it again - this way, parsing becomes easier to
427
# | | | maintain in case of syntax extensions (nevertheless, the
428
# | | | old code worked well!);
429
# 0.28 |14.12.2000| JSTENZEL | made it finally backward compatible to perl 5.005 again;
430
# 0.27 |07.12.2000| JSTENZEL | moved package namespace from "PP" to "PerlPoint";
431
# 0.26 |30.11.2000| JSTENZEL | "Perl Point" => "PerlPoint";
432
# |02.12.2000| JSTENZEL | bugfix in _stackInput() which could remove input lines;
433
# | | JSTENZEL | new headline level offset keyword "current_level";
434
# |03.12.2000| JSTENZEL | the parser now changes into a sourcefiles directory thus
435
# | | | getting able to follow relative paths in nested sources;
436
# | | JSTENZEL | bugfix in input stack: must be multi levelled - we need
437
# | | | one input stack per processed source!;
438
# | | JSTENZEL | cache data now contains headline level informations;
439
# 0.25 |22.11.2000| JSTENZEL | added notes about Storable updates;
440
# |24.11.2000| JSTENZEL | bugfix in caching of embedded parts including empty lines;
441
# | | JSTENZEL | bugfix in modified ordered point intro handling;
442
# |27.11.2000| JSTENZEL | bugfix in progress visualization;
443
# | | JSTENZEL | improved progress visualization;
444
# | | JSTENZEL | new experimental tag setting "\ACCEPT_ALL";
445
# 0.24 |10.11.2000| JSTENZEL | added incremental parsing ("caching");
446
# |18.11.2000| JSTENZEL | slightly simplified the code;
447
# | | JSTENZEL | added ordered list continuations;
448
# 0.23 |28.10.2000| JSTENZEL | bugfix: indentation in embedded code was not accepted;
449
# | | JSTENZEL | using an input stack now for improved embedding;
450
# | | JSTENZEL | tracing active contents now;
451
# 0.22 |21.10.2000| JSTENZEL | new \INCLUDE headline offset parameter;
452
# |25.10.2000| JSTENZEL | bugfixes in trace code;
453
# | | JSTENZEL | modified implementation of included file handling:
454
# | | | reopening a handle did not work in all cases with perl5.6;
455
# 0.21 |11.10.2000| JSTENZEL | improved table paragraphs;
456
# |14.10.2000| JSTENZEL | added alias/macro feature;
457
# 0.20 |10.10.2000| JSTENZEL | added table paragraphs;
458
# 0.19 |08.10.2000| JSTENZEL | added condition paragraphs;
459
# |09.10.2000| JSTENZEL | bugfix in table handling: generated stream was wrong;
460
# 0.18 |05.10.2000| JSTENZEL | embedded Perl code is evaluated now, method run() takes
461
# | | | a Safe object;
462
# |07.10.2000| JSTENZEL | Perl code can now be included as well as embedded;
463
# | | JSTENZEL | variable values are now accessible by embedded and
464
# | | | included Perl code;
465
# | | JSTENZEL | PerlPoint can now be embedded as well as included;
466
# 0.17 |04.10.2000| JSTENZEL | bugfix in documentation: colons have not to be guarded
467
# | | | in definition texts;
468
# | | JSTENZEL | bugfixes in special token handling;
469
# 0.16 |30.09.2000| JSTENZEL | updated documentation;
470
# | | JSTENZEL | bugfix in special token handling;
471
# |03.10.2000| JSTENZEL | definition list items can contain tags now;
472
# |04.10.2000| JSTENZEL | added new target language filter feature;
473
# 0.15 |06.06.2000| JSTENZEL | there were still 5.6 specific operations, using
474
# | | | IO::File now as an emulation under perl 5.005;
475
# 0.14 |03.06.2000| JSTENZEL | improved handling of special tag characters to simplify
476
# | | | PP writing;
477
# | | JSTENZEL | bugfixes: stream contained trailing whitespaces for
478
# | | | list points and headlines;
479
# | | JSTENZEL | bugfix: empty lines in verbatim blocks were not
480
# | | | streamed;
481
# | | JSTENZEL | bugfix: stream contained leading newline for verbatim
482
# | | | blocks;
483
# |05.06.2000| JSTENZEL | switched back to 5.005 open() syntax to become compatible;
484
# 0.13 |01.06.2000| JSTENZEL | made it 5.003 compatible again;
485
# 0.12 |27.05.2000| JSTENZEL | leading spaces in list point lines are suppressed now;
486
# | | JSTENZEL | bugfix in run(): did not supply correct a return code;
487
# | | JSTENZEL | bugfix: last semantic action must be a true value to
488
# | | | flag success (to the parser);
489
# 0.11 |20.05.2000| JSTENZEL | completed embedding feature;
490
# |21.05.2000| JSTENZEL | bugfix in semantic error counting;
491
# | | JSTENZEL | added include feature;
492
# |27.05.2000| JSTENZEL | added table feature (first version);
493
# 0.10 |17.04.2000| JSTENZEL | still incomplete embedding code added;
494
# |03.05.2000| JSTENZEL | bugfix: verbatim block opener was added to stream
495
# | | | because of the modified syntax (not completely impl.);
496
# 0.09 |11.04.2000| JSTENZEL | reorganized verbatim block start: spaces between "&"
497
# | | | and "<
498
# | | | paragraphs with a startup "&" character are allowed now;
499
# | | JSTENZEL | added new paragraph type "definition list point";
500
# |14.04.2000| JSTENZEL | streamed lists are embedded into list directives now;
501
# |15.04.2000| JSTENZEL | modified syntax of verbatim blocks;
502
# | | JSTENZEL | added variables;
503
# | | JSTENZEL | modified tag syntax into "\TAG[{parlist}][]";
504
# 0.08 |04.04.2000| JSTENZEL | started to implement the new pp2xy concept;
505
# |07.04.2000| JSTENZEL | headlines are terminated by a REAL empty line now;
506
# | | JSTENZEL | old "points" became "unordered list points";
507
# | | JSTENZEL | added new paragraph type "ordered list point";
508
# |08.04.2000| JSTENZEL | built in list shifting;
509
# |09.04.2000| JSTENZEL | bugfix in text paragraph rule;
510
# |10.04.2000| JSTENZEL | blocks are combined now automatically unless there is an
511
# | | | intermediate control paragraph;
512
# 0.07 |25.03.2000| JSTENZEL | tag length is now 1 to 8 characters (instead of 1 to 3);
513
# | | JSTENZEL | POD fixes;
514
# | | JSTENZEL | using CPAN id's in HOC now;
515
# 0.06 |24.02.2000| JSTENZEL | trailing whitespaces in input lines are now removed
516
# | | | (except of newlines!);
517
# 0.05 |11.10.1999| JSTENZEL | bugfix: paragraphs generated array references;
518
# | | JSTENZEL | PP::Parser::Constants became PP::Constants;
519
# | | JSTENZEL | adapted POD to pod2text (needs more blank lines);
520
# 0.04 |09.10.1999| JSTENZEL | moved certain constants into PP::Parser::Constants;
521
# | | JSTENZEL | completed POD;
522
# 0.03 |08.10.1999| JSTENZEL | started to generate intermediate data;
523
# | | JSTENZEL | simplified array access;
524
# | | JSTENZEL | bugfixes;
525
# |09.10.1999| JSTENZEL | added data generation;
526
# | | JSTENZEL | all messages are written in English now;
527
# | | JSTENZEL | tags are declared outside now;
528
# | | JSTENZEL | exported the script part;
529
# | | JSTENZEL | added statistics;
530
# | | JSTENZEL | added trace and display control;
531
# 0.02 |07.10.1999| JSTENZEL | added C tag;
532
# | | JSTENZEL | added comment traces;
533
# | | JSTENZEL | bugfixes;
534
# | | JSTENZEL | made it pass -w;
535
# | | JSTENZEL | new "verbatim" paragraph;
536
# 0.01 |28.09.1999| JSTENZEL | new.
537
# ---------------------------------------------------------------------------------------
538
539
# = POD SECTION =========================================================================
540
541
=head1 NAME
542
543
B - a PerlPoint Parser
544
545
=head1 VERSION
546
547
This manual describes version B<0.451>.
548
549
=head1 SYNOPSIS
550
551
# load the module:
552
use PerlPoint::Parser;
553
554
# build the parser and run it
555
# to get intermediate data in @stream
556
my ($parser)=new PerlPoint::Parser;
557
$parser->run(
558
stream => \@stream,
559
files => \@files,
560
);
561
562
563
=head1 DESCRIPTION
564
565
The PerlPoint format, initially designed by Tom Christiansen, is intended
566
to provide a simple and portable way to generate slides without the need of
567
a proprietary product. Slides can be prepared in a text editor of your choice,
568
generated on any platform where you find perl, and presented by any browser
569
which can render the chosen output format.
570
571
To sum it up,
572
I
573
This is, by tradition, usually HTML, but you may decide to use another format like
574
XML, SGML, TeX or whatever you want.
575
576
Well, this sounds fine, but how to build a translator which transforms ASCII
577
into the output format of your choice? Thats what B is made for.
578
It performs the first translation step by parsing ASCII and transforming it
579
into an intermediate stream format, which can be processed by a subsequently
580
called translator backend. By separating parsing and output generation we
581
get the flexibility to write as many backends as necessary by using the same
582
parser frontend for all translators.
583
584
B supports the complete I with exception of I
585
tags. Tags I supported the I: the parser recognizes I
586
tag which is declared by the author of a translator. This way the
587
parser can be used for various flavours of the PerlPoint language without
588
having to be modified. So, if there is a need of a certain new flag, it can
589
quickly be added without any change to B.
590
591
The following chapters describe the input format (I) and the
592
generated stream format (I). Finally, the class methods are
593
described to show you how to build a parser.
594
595
596
=head1 GRAMMAR
597
598
This chapter describes how a PerlPoint ASCII slide description has to be
599
formatted to pass B parsers.
600
601
I that the input format does I completely determine how
602
the output will be designed. The final I depends on the backend
603
which has to be called after the parser to transform its output into a
604
certain document description language. The final I depends on
605
the I behaviour.
606
607
Each PerlPoint document is made of I.
608
609
=head2 The paragraphs
610
611
All paragraphs start at the beginning of their first line. The first character
612
or string in this line determines which paragraph is recognized.
613
614
A paragraph is completed by an empty line (which may contain whitespaces).
615
Exceptions are described.
616
617
Carriage returns in paragraphs which are completed by an empty line
618
are transformed into a whitespace.
619
620
=over 4
621
622
=item Comments
623
624
start with "//" and reach until the end of the line.
625
626
627
=item Headlines
628
629
start with one or more "=" characters.
630
The number of "=" characters represents the headline level.
631
632
=First level headline
633
634
==Second level headline
635
636
===Multi
637
line
638
headline
639
example
640
641
It is possible to declare a "short version" of the headline
642
title by appending a "~" and plain strings to the headline
643
like in
644
645
=Very long headlines are expressive but may exceed the
646
available space for example in HTML navigation bars or
647
something like that ~ Long headlines
648
649
The "~" often stands for similarity, or represents the described
650
object in encyclopedias or dictionaries. So one may think of this
651
as "long title is (sometimes) similar to short title".
652
653
654
655
=item Lists
656
657
B or B start with a "*" character.
658
659
* This is a first point.
660
661
* And, I forgot,
662
there is something more to point out.
663
664
There are B as well, and I start with a hash sign ("#"):
665
666
# First, check the number of this.
667
668
# Second, don't forget the first.
669
670
The hash signs are intended to be replaced by numbers by a backend.
671
672
Because PerlPoint works on base of paragraphs, any paragraph different to
673
an ordered list point I. If you wish the list to
674
be continued use a double hash sign in case of the single one in the point
675
that reopens the list.
676
677
# Here the ordered list begins.
678
679
? $includeMore
680
681
## This is point 2 of the list that started before.
682
683
# In subsequent points, the usual single hash sign
684
works as expected again.
685
686
List continuation works list level specific (see below for level details).
687
A list cannot be continued in another chapter. Using "##" in the first
688
point of a new list takes no special effect: the list will begin as usual
689
(with number 1).
690
691
B are a third list variant. Each item starts with the
692
described phrase enclosed by a pair of colons, followed by the definition
693
text:
694
695
:first things: are usually described first,
696
697
:others: later then.
698
699
All lists can be I. A new level is introduced by
700
a special paragraph called I<"list indention"> which starts with a ">". A list level
701
can be terminated by a I<"list indention stop"> paragraph starting with a "<"
702
character. (These startup characters symbolize "level shifts".)
703
704
* First level.
705
706
* Still there.
707
708
>
709
710
* A list point of the 2nd level.
711
712
<
713
714
* Back on first level.
715
716
It is possible to shift more than one level by adding a number. There should be no whitespace between the
717
level shift character and the level number.
718
719
* First level.
720
721
>
722
723
* Second level.
724
725
>
726
727
* Third level.
728
729
<2
730
731
* Back on first level.
732
733
Level shifts are accepted between list items I.
734
735
I Any non list
736
paragraph will I list indentation, as well as the end of the source.
737
738
739
=item Texts
740
741
are paragraphs like points but begin I without a startup
742
character:
743
744
This is a simple text.
745
746
In this new text paragraph,
747
we demonstrate the multiline feature.
748
749
I, a text paragraph can be started with a special character
750
as well, which is a dot:
751
752
.This is a simple text with dot.
753
754
.In this new text paragraph,
755
we demonstrate the multiline feature.
756
757
This is intended to be used by generators which translate other formats
758
into PerlPoint, to make sure the first character of a paragraph has no
759
special meaning to the PerlPoint parser.
760
761
762
=item Blocks
763
764
are intended to contain examples or code I tag recognition.
765
This means that the parser will discover embedded tags. On the other hand,
766
it means that one may have to escape ">" characters embedded into tags. Blocks
767
begin with an I and are completed by the next empty line.
768
769
* Look at these examples:
770
771
A block.
772
773
\I block.
774
Escape ">" in tags: \C<<\>>.
775
776
Examples completed.
777
778
Subsequent blocks are joined together automatically: the intermediate empty
779
lines which would usually complete a block are translated into real empty
780
lines I the block. This makes it easier to integrate real code
781
sequences as one block, regardless of the empty lines included. However,
782
one may explicitly I to separate subsequent blocks and can do so
783
by delimiting them by a special control paragraph:
784
785
* Separated subsequent blocks:
786
787
The first block.
788
789
-
790
791
The second block.
792
793
Note that the control paragraph starts at the left margin.
794
795
796
=item Verbatim blocks
797
798
are similar to blocks in indentation but I
799
pattern recognition. That means the embedded text is I scanned for tags
800
and empty lines and may therefore remain as it was in its original place,
801
possibly a script.
802
803
These special blocks need a special syntax. They are implemented as here documents.
804
Start with a here document clause flagging which string will close the "here document":
805
806
<
807
808
PerlPoint knows various
809
tags like \B, \C and \I. # unrecognized tags
810
811
EOC
812
813
814
=item Tables
815
816
are supported as well, they start with an @ sign which is
817
followed by the column delimiter:
818
819
@|
820
column 1 | column 2 | column 3
821
aaa | bbb | ccc
822
uuu | vvvv | www
823
824
The first line is automatically marked as a "table headline". Most converters
825
emphasize such headlines by bold formatting, so there is no need to insert \B
826
tags into the document.
827
828
If a table row contains less columns than the table headline, the "missed"
829
columns are automatically added. This is,
830
831
@|
832
A | B | C
833
1
834
1 |
835
1 | 2
836
1 | 2 |
837
1 | 2 | 3
838
839
is streamed exactly like
840
841
@|
842
A | B | C
843
1 | |
844
1 | |
845
1 | 2 |
846
1 | 2 |
847
1 | 2 | 3
848
849
to make backend handling easier. (Empty HTML table cells, for example, are rendered
850
slightly obscure by certain browsers unless they are filled with invisible characters,
851
so a converter to HTML can detect such cells because of normalization and handle them
852
appropriately.)
853
854
Please note that normalization refers to the headline row. If another line contains
855
I columns than the headline, normalization does not care. If the maximum column
856
number is detected in another row, a warning is issued. (As a help for converter authors,
857
the title and maximum column number are made part of a table tag as internal options
858
C<__titleColumns__> and C<__maxColumns__>.)
859
860
In all tables, leading and trailing whitespaces of a cell are
861
automatically removed, so you can use as many of them as you want to
862
improve the readability of your source. The following table is absolutely
863
equivalent to the last example:
864
865
@|
866
A | B | C
867
1 | |
868
1 | |
869
1 | 2 |
870
1 | 2 |
871
1 | 2 | 3
872
873
There is also a more sophisticated way to describe tables, see the tag section below.
874
875
Note: Although table paragraphs cannot be nested, tables declared by tag possibly
876
I (and might be embedded into table paragraphs as well). To help converter authors
877
handling nested tables, the opening table tag provides an internal option "__nestingLevel__".
878
879
880
=item Conditions
881
882
start with a "?" character. If active contents is enabled, the paragraph text
883
is evaluated as Perl code. The (boolean) evaluation result then determines if
884
subsequent PerlPoint is read and parsed. If the result is false, all subsequent
885
paragraphs until the next condition are I.
886
887
Note that base data is made available by a global (package) hash reference
888
B<$PerlPoint>. See I for details about how to set up these data.
889
890
Conditions can be used to maintain various language versions of a presentation
891
in one source file:
892
893
? $PerlPoint->{targetLanguage} eq 'German'
894
895
Or you could enable parts of your document by date:
896
897
? time>$dateOfTalk
898
899
or by a special setting:
900
901
? flagSet('setting')
902
903
Please note that the condition code shares its variables with embedded and included
904
code.
905
906
To make usage easier and to improve readability, condition code is evaluated with
907
disabled warnings (the language variable in the example above may not even been set).
908
909
Converter authors might want to provide predefined variables such as "$language"
910
in the example.
911
912
Note: If a document uses I, be careful in intermixing docstream
913
entry points and conditions. A condition placed in a skipped document stream will
914
not e evaluated. A document stream entry point placed in a source area hidden by
915
a false condition will not be reconized.
916
917
918
=item Variable assignment paragraphs
919
920
Variables can be used in the text and will be automatically replaced by their string
921
values (if declared).
922
923
The next paragraph sets a variable.
924
925
$var=var
926
927
This variable is called $var.
928
929
All variables are made available to embedded and included Perl code as well as to
930
conditions and can be accessed there as package variables of "main::" (or whatever
931
package name the Safe object is set up to). Because a
932
variable is already replaced by the parser if possible, you have to use the fully
933
qualified name or to guard the variables "$" prefix character to do so:
934
935
\EMBED{lang=perl}join(' ', $main::var, \$var)\END_EMBED
936
937
Variable modifications by embedded or included Perl I affect the variables
938
visible to the parser. (This is true for conditions as well.) This means that
939
940
$var=10
941
\EMBED{lang=perl}$main::var*=2;\END_EMBED
942
943
causes I<$var> to be different on parser and code side - the parser will still use a
944
value of 10, while embedded code works on with a value of 20.
945
946
=item Macro or alias definitions
947
948
Sometimes certain text parts are used more than once. It would be a relieve
949
to have a shortcut instead of having to insert them again and again. The same
950
is true for tag combinations a user may prefer to use. That's what I
951
(or "macros") are designed for. They allow a presentation author to declare
952
his own shortcuts and to use them like a tag. The parser will resolve such aliases,
953
replace them by the defined replacement text and work on with this replacement.
954
955
An alias declaration starts with a "+" character followed I by the
956
alias I (without backslash prefix), optionally followed I
957
by an option default list in "{}", followed I by a colon.
958
(No additional spaces here.)
959
960
I
961
So, whereever you will use the new macro, the parser will replace it by this
962
text and I the result. This means that your macro text can contain
963
any valid constructions like tags or other macros.
964
965
The replacement text may contain strings embedded into doubled underscores like
966
C<__this__>. This is a special syntax to mark that the macro takes parameters
967
of these names (e.g. C). If a macro is used and these parameters are set,
968
their values will replace the mentioned placeholders. The special placeholder
969
"__body__" is used to mark where the macro I is to place.
970
971
If a macro is used and defined options are I, but there are defaults
972
for them in the optional default list, these defaults will be used for the
973
respective options.
974
975
Here are a few examples:
976
977
+RED:\FONT{color=red}<__body__>
978
979
+F:\FONT{color=__c__}<__body__>
980
981
+COLORED{c=blue}:\FONT{color=__c__}<__body__>
982
983
+IB:\B<\I<__body__>>
984
985
This \IB is \RED.
986
987
Defaults: first, text in \COLORED{c=red},
988
now text in \COLORED.
989
990
+TEXT:Macros can be used to abbreviate longer
991
texts as well as other tags
992
or tag combinations.
993
994
+HTML:\EMBED{lang=html}
995
996
Tags can be \RED<\I> into macros.
997
And \I<\F{c=blue}>.
998
\IB<\RED> is formatted by nested macros.
999
\HTML This is embedded HTML \END_EMBED.
1000
1001
Please note: \TEXT
1002
1003
I
1004
The same is true for the body part.
1005
I is used in the macro definition, macro bodies will not be recognized.>
1006
This means that with the definition
1007
1008
+OPTIONLESS:\B<__body__>
1009
1010
the construction
1011
1012
\OPTIONLESS{something=this}
1013
1014
is evaluated as a usage of C<\OPTIONLESS> without body, followed by the I
1015
C<{something=here}>. Likewise, the definition
1016
1017
+BODYLESS:found __something__
1018
1019
causes
1020
1021
\BODYLESS{something=this}
1022
1023
to be recognized as a usage of C<\BODYLESS> with option C, followed
1024
by the I C<>. So this will be resolved as C. Finally,
1025
1026
+JUSTTHENAME:Text phrase.
1027
1028
enforces these constructions
1029
1030
... \JUSTTHENAME, ...
1031
... \JUSTTHENAME{name=Name}, ...
1032
... \JUSTTHENAME, ...
1033
... \JUSTTHENAME{name=Name} ...
1034
1035
to be translated into
1036
1037
... Text phrase. ...
1038
... Text phrase.{name=Name} ...
1039
... Text phrase., ...
1040
... Text phrase.{name=Name} ...
1041
1042
The principle behind all this is to make macro usage I and intuative:
1043
why think of options or a body or of special characters possibly treated as
1044
option/body part openers unless the macro makes use of an option or body?
1045
1046
An I macro text I the macro (if it was already known).
1047
1048
// undeclare the IB alias
1049
+IB:
1050
1051
An alias can be used like a tag.
1052
1053
Aliases named like a tag I the tag (as long as they are defined).
1054
1055
1056
=item Document stream entry points
1057
1058
A document stream is a "document in document" and best explained by example.
1059
1060
Consider a document talking about
1061
two scripts and comparing them. A
1062
typical review of this type is
1063
structured this way: headline, notes
1064
about script 1, notes about script 2,
1065
new headline to discuss another aspect,
1066
notes about script 1, notes about
1067
script 2, and so on.
1068
1069
Everything said about item 1 is a document stream, everything about object 2
1070
as well. and a third stream is implicitly built by all parts outside these
1071
two. In slide construction, each stream can have its own area, for example
1072
1073
-------------------------------------
1074
| |
1075
| main stream |
1076
| |
1077
-------------------------------------
1078
| | |
1079
| item 1 stream | item 2 stream |
1080
| | |
1081
-------------------------------------
1082
1083
But to construct a layout like this, streams need to be distinguished, and
1084
that is what "stream entry points" are made for.
1085
1086
A stream entry point starts with a "~" character, followed by a string
1087
which is the name of the stream. This may be an internal name only, or
1088
converters may turn it into a document part as well. The C<__ALL__> string
1089
is reserved for internal purposes. It is recommended to treat C<__MAIN__>
1090
as reserved as well, although it has no special meaning yet.
1091
1092
Once an entry point was passed, all subsequent document parts belong to the
1093
declared stream, up to the next entry point or a headline which implicitly
1094
switches back to the "main stream".
1095
1096
The parser can be instructed to ignore certain streams, see I for
1097
details. If this feature is used, please be careful in intermixing stream
1098
entry points and conditions. A condition placed in a skipped document
1099
stream will not be evaluated.
1100
1101
I Certain converters
1102
may ignore them at all. As a convenient solution, the parser can be instructed
1103
to transform stream entry points into headlines (one level below the current
1104
real headline level). See I for details.
1105
1106
1107
1108
=back
1109
1110
=head2 Tags
1111
1112
Tags are directives embedded into the text stream, commanding how certain parts
1113
of the text should be interpreted. Tags are declared by using one or more modules
1114
build on base of B.
1115
1116
use PerlPoint::Tags::Basic;
1117
1118
B parsers can recognize all tags which are build of a backslash
1119
and a number of capitals and numbers.
1120
1121
\TAG
1122
1123
I are optional and follow the tag name immediately, enclosed
1124
by a pair of corresponding curly braces. Each option is a simple string
1125
assignment. The value has to be quoted if /^\w+$/ does not match it.
1126
1127
\TAG{par1=value1 par2="www.perl.com" par3="words and blanks"}
1128
1129
The I is anything you want to make the tag valid for. It is optional
1130
as well and immediately follows the optional parameters, enclosed by "<" and ">":
1131
1132
\TAG
1133
\TAG{par=value}
1134
1135
Tags can be I.
1136
1137
To provide a maximum of flexibility, tags are declared I the parser.
1138
This way a translator programmer is free to implement the tags he needs. It is
1139
recommended to always support the basic tags declared by B.
1140
On the other hand,a few tags of special meaning are reserved and cannot be declared
1141
by converter authors, because they are handled by the parser itself. These are:
1142
1143
=over 4
1144
1145
=item \INCLUDE
1146
1147
It is possible to include a file into the input stream. Have a look:
1148
1149
\INCLUDE{type=HTML file=filename}
1150
1151
This imports the file "filename". The file contents is made part of the
1152
generated stream, but not parsed. This is useful to include target language
1153
specific, preformatted parts.
1154
1155
If, however, the file type is specified as "PP", the file contents is
1156
made part of the input stream and parsed. In this case a special tag option
1157
"headlinebase" can be specified to define a headline base level used as
1158
an offset to all headlines in the included document. This makes it easier
1159
to share partial documents with others, or to build complex documents by
1160
including separately maintained parts, or to include one and the same
1161
part at different headline levels.
1162
1163
Example: If "\INCLUDE{type=PP file=file headlinebase=20}" is
1164
specified and "file" contains a one level headline
1165
like "=Main topic of special explanations"
1166
this headline is detected with a level of 21.
1167
1168
Pass the special keyword "CURRENT_LEVEL" to this tag option if you want to
1169
set just the I headline level as an offset. This results in
1170
"subchapters".
1171
1172
Example:
1173
1174
===Headline 3
1175
1176
// let included chapters start on level 4
1177
\INCLUDE{type=PP file=file headlinebase=CURRENT_LEVEL}
1178
1179
Similar to "CURRENT_LEVEL", "BASE_LEVEL" sets the current I
1180
headline level as an offset. The "base level" is the level above
1181
the current one. Using "BASE_LEVEL" results in parallel chapters.
1182
1183
Example:
1184
1185
===Headline 3
1186
1187
// let included chapters start on level 3
1188
\INCLUDE{type=PP file=file headlinebase=BASE_LEVEL}
1189
1190
A given offset is reset when the included document is parsed completely.
1191
1192
A second special option I commands the parser to include the file
1193
only unless this was already done before. This is intended for inclusion
1194
of pure alias/macro definition or variable assignment files.
1195
1196
\INCLUDE{type=PP file="common-macros.pp" smart=1}
1197
1198
Included sources may declare variables of their own, possibly overwriting
1199
already assigned values. Option "localize" works like Perls C:
1200
such changes will be reversed after the nested source will have been
1201
processed completely, so the original values will be restored. You can
1202
specify a comma separated list of variable names or the special string
1203
C<__ALL__> which flags that I current settings shall be restored.
1204
1205
\INCLUDE{type=PP file="nested.pp" localize=myVar}
1206
1207
\INCLUDE{type=PP file="nested.pp" localize="var1, var2, var3"}
1208
1209
\INCLUDE{type=PP file="nested.pp" localize=__ALL__}
1210
1211
1212
PerlPoint authors can declare an I to preprocess the
1213
included file. This is done via option I:
1214
1215
\INCLUDE{type=pp file="source.pod" ifilter="pod2pp()"}
1216
1217
An input filter is a snippet of user defined Perl code, taking the
1218
included file via C<@main::_ifilterText> and the target type via
1219
C<$main::_ifilterType>. The original filename can be accessed via
1220
C<$main::_ifilterType>. It should supply its result as an array
1221
of strings which will then be processed instead of the original file.
1222
1223
Input filters are Active Content. If Active Content is disabled,
1224
\INCLUDE tags using input filters will be ignored I.
1225
1226
1227
As a simplified option, C allows to use I
1228
import filters defined in C modules. To use
1229
such a filter do I set the C option, set C instead.
1230
C takes the name of the source format, like "POD", or a true
1231
number to indicate that the file extension should be used as the source
1232
format name. The uppercased name is used as the final part of the filter
1233
module - for "POD", the modules name would be "PerlPoint::Import::POD".
1234
If this module is installed and has a function C this
1235
function name is used like C.
1236
1237
Here are a few examples:
1238
1239
\INCLUDE{file="source.pod" import=1}
1240
1241
\INCLUDE{file="source.pod" import=pod}
1242
1243
\INCLUDE{file=source import=pod}
1244
1245
Please note that in the last example C will not work, as the
1246
source file has no extension that indicates its format is POD.
1247
1248
If C is used together with C, C is ignored.
1249
1250
1251
A PerlPoint file can be included wherever a tag is allowed, but sometimes
1252
it has to be arranged slightly: if you place the inclusion directive at
1253
the beginning of a new paragraph I your included PerlPoint starts by
1254
a paragraph of another type than text, you should begin the included file
1255
by an empty line to let the parser detect the correct paragraph type. Here
1256
is an example: if the inclusion directive is placed like
1257
1258
// include PerlPoint
1259
\INCLUDE{type=pp file="file.pp"}
1260
1261
and file.pp immediately starts with a verbatim block like
1262
1263
<
1264
verbatim
1265
VERBATIM
1266
1267
, I which is detected to
1268
be "text" (because there is no special startup character). Now in the included
1269
file, from the parsers point of view the included PerlPoint is simply a
1270
continuation of this text, because a paragraph ends with an empty line. This
1271
trouble can be avoided by beginning the included file by an empty line,
1272
so that its first paragraph can be detected correctly.
1273
1274
The second special case is a file type of "Perl". If active contents is enabled,
1275
included Perl code is read into memory and evaluated like I Perl. The
1276
results are made part of the input stream to be parsed.
1277
1278
// execute a perl script and include the results
1279
\INCLUDE{type=perl file="disk-usage.pl"}
1280
1281
As another option, files may be declared to be of type "example" or "parsedexample".
1282
This makes the file placed into the source as a verbatim block (with "example"), or
1283
a standard block (with "parsedexample"), respectively, without need to copy its contents
1284
into the source.
1285
1286
// include an external script as an example
1287
\INCLUDE{type=example file="script.csh"}
1288
1289
All lines of the example file are included as they are but can be indented on request.
1290
To do so, just set the special option "indent" to a positive numerical value equal to
1291
the number of spaces to be inserted before each line.
1292
1293
// external example source, indented by 3 spaces
1294
\INCLUDE{type=example file="script.csh" indent=3}
1295
1296
Including external scripts this way can accelerate PerlPoint authoring significantly,
1297
especially if the included files are still subject to changes.
1298
1299
It is possible to filter the file types you wish to include (with exception
1300
of "pp" and "example"), see below for details. I, the mentioned file
1301
has to exist.
1302
1303
1304
1305
=item \EMBED and \END_EMBED
1306
1307
Target format code does not necessarily need to be imported - it can be
1308
directly I as well. This means that one can write target language
1309
code within the input stream using I<\EMBED>:
1310
1311
\EMBED{lang=HTML}
1312
This is embedded HTML .
1313
The parser detects no PerlPoint
1314
tag here, except of END_EMBED .
1315
\END_EMBED
1316
1317
Because this is handled by I, not by paragraphs, it can be placed
1318
directly in a text like this:
1319
1320
These \EMBED{lang=HTML}italics \END_EMBED
1321
are formatted by HTML code.
1322
1323
Please note that the EMBED tag does not accept a tag body (to avoid
1324
ambiguities).
1325
1326
Both tag and embedded text are made part of the intermediate stream.
1327
It is the backends task to deal with it. The only exception of this rule
1328
is the embedding of I code, which is evaluated by the parser.
1329
The reply of this code is made part of the input stream and parsed as
1330
usual.
1331
1332
PerlPoint authors can declare an I to preprocess the
1333
embedded text. This is done via option I:
1334
1335
\EMBED{lang=pp ifilter="pod2pp()"}
1336
1337
=head1 POD formatted part
1338
1339
This part was written in POD.
1340
1341
\END_EMBED
1342
1343
An input filter is a snippet of user defined Perl code, taking the
1344
embedded text via C<@main::_ifilterText> and the target language via
1345
C<$main::_ifilterType>. The original filename can be accessed via
1346
C<$main::_ifilterType> (but please note that this is the source with
1347
the \EMBED tag). It should supply its result as an array of
1348
strings which will then be processed as usual.
1349
1350
Input filters are Active Contents. If Active Contents is disabled,
1351
embedded parts using input filters will be ignored I.
1352
1353
It is possible to filter the languages you wish to embed (with exception
1354
of "PP"), see below for details.
1355
1356
1357
=item \TABLE and \END_TABLE
1358
1359
It was mentioned above that tables can be built by table paragraphs.
1360
Well, there is a tag variant of this:
1361
1362
\TABLE{bg=blue separator="|" border=2}
1363
\B | \B | \B
1364
aaaa | bbbb | cccc
1365
uuuu | vvvv | wwww
1366
\END_TABLE
1367
1368
This is sligthly more powerfull than the paragraph syntax: you can set
1369
up several table features like the border width yourself, and you can
1370
format the headlines as you like.
1371
1372
As in all tables, leading and trailing whitespaces of a cell are
1373
automatically removed, so you can use as many of them as you want to
1374
improve the readability of your source.
1375
1376
The default row separator (as in the example above) is a carriage return,
1377
so that each table line can be written as a separate source line. However,
1378
PerlPoint allows you to specify another string to separate rows by option
1379
C. This allows to specify a table I into a paragraph.
1380
1381
\TABLE{bg=blue separator="|" border=2 rowseparator="+++"}
1382
\B | \B | \B +++ aaaa
1383
| bbbb | cccc +++ uuuu | vvvv| wwww \END_TABLE
1384
1385
This is exactly the same table as above.
1386
1387
If parser option I is set to a true value calling I,
1388
it is possible to I tables. To help converter authors handling this,
1389
the opening table tag provides an internal option "__nestingLevel__".
1390
1391
Tables built by tag are normalized the same way as table paragraphs are.
1392
1393
=back
1394
1395
1396
=head2 What about special formatting?
1397
1398
Earlier versions of B supported special format hints like the HTML
1399
expression ">" for the ">" character, or "ü" for "ü". B
1400
does I support this directly because such hints are specific to the
1401
I - if someone wants to translate into TeX, it might be curious
1402
for him to use HTML syntax in his ASCII text. Further more, such hints can be
1403
handled I by a backend which finds them unchanged in the produced
1404
output stream.
1405
1406
The same is true for special headers and trailers. It is a I task to
1407
add them if necessary. The parser does handle the I only.
1408
1409
1410
=head1 STREAM FORMAT
1411
1412
It is suggested to use B to evaluate the intermediate format.
1413
Nevertheless, here is the documentation of this format.
1414
1415
The generated stream is an array of tokens. Most of them are very simple,
1416
representing just their contents - words, spaces and so on. Example:
1417
1418
"These three words."
1419
1420
could be streamed into
1421
1422
"These three" + " "+ "words."
1423
1424
(This shows the principle. Actually this complete sentence would be replied as
1425
I token for reasons of effeciency.)
1426
1427
Note that the final dot I of the last token. From a document
1428
description view, this should make no difference, its just a string containing
1429
special characters or not.
1430
1431
Well, besides this "main stream", there are I. They
1432
flag the I or I of a certain logical entity - this
1433
means a whole document, a paragraph or a formatting like italicising. Almost
1434
every entity is embedded into a start I a completion directive - except
1435
of simple tokens.
1436
1437
In the current implementation, a directive is a reference to an array of mostly
1438
two fields: a directive constant showing which entity is related, and a start
1439
or completion hint which is a constant, too. The used constants are declared in
1440
B. Directives can pass additional informations by additional
1441
fields. By now, the headline directives use this feature to show the headline
1442
level, as well as the tag ones to provide tag type information and the document ones
1443
to keep the name of the original document. Further more, ordered list points I
1444
request a fix number this way.
1445
1446
# this example shows a tag directive
1447
... [DIRECTIVE_TAG, DIRECTIVE_START, "I"]
1448
+ "formatted" + " " + "strings"
1449
+ [DIRECTIVE_TAG, DIRECTIVE_COMPLETE, "I"] ...
1450
1451
To recognize whether a token is a basic or a directive, the ref() function can be
1452
used. However, this handling should be done by B transparently.
1453
The format may be subject to changes and is documented for information purposes only.
1454
1455
Original line numbers are no part of the stream but can be provided by embedded
1456
directives on request, see below for details.
1457
1458
This is the complete generator format. It is designed to be simple but powerful.
1459
1460
1461
=head1 METHODS
1462
1463
=head2 new()
1464
1465
The constructor builds and prepares a new parser object.
1466
1467
B
1468
1469
=over 4
1470
1471
=item The class name.
1472
1473
=back
1474
1475
B
1476
The new object in case of success.
1477
1478
B
1479
1480
my ($parser)=new PerlPoint::Parser;
1481
1482
=cut
1483
1484
# = CODE SECTION ========================================================================
1485
1486
# startup actions
1487
BEGIN
1488
{
1489
# declare startup helper function
1490
sub _startupGenerateConstants
1491
{
1492
# init counter
1493
my $c=0;
1494
1495
# and generate constants
1496
34
34
286
foreach my $constant (@_)
34
34
102
34
34
20505
34
34
276
34
34
100
34
34
1234
34
34
182
34
34
70
34
34
1111
34
34
267
34
34
96
34
34
1176
34
34
189
34
34
100
34
34
1083
34
34
182
34
34
14300
34
34
3406
34
34
180
34
34
81
34
34
2326
34
34
192
34
34
75
34
34
4597
34
34
183
34
34
68
34
2261
34
177
34
62
34
2441
34
204
34
64
34
1058
34
197
34
66
34
1031
34
172
34
70
34
1220
34
345
34
235
34
3061
34
168
34
245
34
5188
34
191
34
78
34
2533
34
181
34
76
34
2475
34
176
34
67
34
1036
34
172
34
69
34
1154
34
168
34
3002
34
1081
34
172
34
68
34
949
34
175
34
65
34
1183
34
183
34
62
34
1016
34
214
34
86
34
1001
34
177
34
75
34
1017
34
174
34
66
34
3025
1497
{eval "use constant $constant => $c"; $c++;}
1498
}
1499
1500
# declare internal constants: action timeout types (used as array indices, sort alphabetically!)
1501
34
34
207
_startupGenerateConstants(
1502
'LEXER_TOKEN', # reply symbols token;
1503
'LEXER_FATAL', # bug: unexpected symbol;
1504
'LEXER_IGNORE', # ignore this symbol;
1505
'LEXER_EMPTYLINE', # reply the token "Empty_line";
1506
'LEXER_SPACE', # reply the token "Space" and a simple whitespace;
1507
);
1508
1509
# state constants
1510
34
154
_startupGenerateConstants(
1511
'STATE_DEFAULT', # default;
1512
'STATE_DEFAULT_TAGMODE', # default in tag mode;
1513
1514
'STATE_BLOCK', # block;
1515
'STATE_COMMENT', # comment;
1516
'STATE_CONTROL', # control paragraph (of a single character);
1517
'STATE_DPOINT', # definition list point;
1518
'STATE_DPOINT_ITEM', # definition list point item (defined stuff);
1519
'STATE_EMBEDDING', # embedded things (HTML, Perl, ...);
1520
'STATE_PFILTER', # paragraph filter installation;
1521
'STATE_PFILTERED', # "default" state after a pfilter installation;
1522
'STATE_CONDITION', # condition;
1523
'STATE_HEADLINE_LEVEL', # headline level setting;
1524
'STATE_HEADLINE', # headline;
1525
'STATE_OPOINT', # ordered list point;
1526
'STATE_TEXT', # text;
1527
'STATE_UPOINT', # unordered list point;
1528
'STATE_VERBATIM', # verbatim block;
1529
'STATE_TABLE', # table *paragraph*;
1530
'STATE_DEFINITION', # macro definition;
1531
);
1532
1533
# declare internal constants: list shifters
1534
34
106
_startupGenerateConstants(
1535
'LIST_SHIFT_RIGHT', # shift right;
1536
'LIST_SHIFT_LEFT', # shift left;
1537
);
1538
1539
# release memory
1540
34
2138
undef &_startupGenerateConstants;
1541
}
1542
1543
# requires modern perl
1544
require 5.00503;
1545
1546
# declare module version
1547
$PerlPoint::Parser::VERSION=0.451
1548
$PerlPoint::Parser::VERSION=$PerlPoint::Parser::VERSION; # to suppress a warning of exclusive usage only;
1549
1550
# pragmata
1551
34
34
192
use strict;
34
60
34
1234
1552
1553
# load modules
1554
34
34
220
use Carp;
34
63
34
2487
1555
# use Memoize;
1556
34
34
39301
use IO::File;
34
533795
34
5561
1557
34
34
334
use File::Basename;
34
79
34
3637
1558
34
34
40504
use File::Spec::Functions;
34
45309
34
6573
1559
34
34
49612
use File::Temp qw(tempfile);
34
689778
34
3064
1560
34
34
26176
use PerlPoint::Anchors 0.03;
34
895
34
1113
1561
34
34
1062
use PerlPoint::Backend 0.10;
34
1853
34
899
1562
34
34
332
use Cwd qw(:DEFAULT abs_path);
34
111
34
6059
1563
34
34
35620
use Digest::SHA1 qw(sha1_base64);
34
38212
34
2445
1564
34
34
248
use Storable qw(:DEFAULT dclone nfreeze);
34
86
34
5271
1565
34
34
214
use PerlPoint::Constants 0.19 qw(:DEFAULT :parsing :stream :tags);
34
880
34
221263
1566
1567
# memoizations
1568
1569
# startup declarations
1570
my (
1571
%data, # the collected declaration data;
1572
%lineNrs, # the lexers line number hash, input handle specific;
1573
%specials, # special character control (may be active or not);
1574
%lexerFlags, # lexer state flags;
1575
%lexerFlagsOfPreviousState, # buffered lexer state flags of previous state;
1576
%statistics, # statistics data;
1577
%variables, # user managed variables;
1578
%flags, # various flags;
1579
%macros, # macros / aliases;
1580
%openedSourcefiles, # a hash of all source files already opened (to enable smart inclusion);
1581
%paragraphTypeStrings, # paragraph type to string translation table;
1582
1583
@nestedSourcefiles, # a list of current source file nesting (to avoid circular inclusions);
1584
@specialStack, # special state stack for temporary activations (to restore original states);
1585
@stateStack, # state stack (mostly intended for non paragraph states like STATE_EMBEDDED);
1586
@tableSeparatorStack, # the first element is the column separator string within a table, empty otherwise;
1587
@inputStack, # a stack of additional input lines and dynamically inserted parts;
1588
@inHandles, # a stack of input handles (to manage nested sources);
1589
@olistLevels, # a hint storing the last recent ordered list level number of a paragraph (hyrarchically);
1590
@inLine, # current *real* input line (the unexpanded line read from a source file);
1591
@previousStackLines, # buffer of the last lines gotten from input stack;
1592
@libraryPath, # a collection of pathes to find files for \INCLUDE in;
1593
@headlineIds, # the hierarchical values of $directiveCounter pointing to the current chapter headline;
1594
1595
$anchors, # anchor collector object;
1596
$safeObject, # an object of class Safe to evaluate Perl code embedded into PerlPoint;
1597
$sourceFile, # the source file currently read;
1598
$tagsRef, # reference to a hash of valid tag openers (strings without the "<");
1599
$resultStreamRef, # reference to a data structure to put generated stream data in;
1600
$inHandle, # the data input stream (to parse);
1601
$parserState, # the current parser state;
1602
$readCompletely, # the input file is read completely;
1603
$_semerr, # semantic error counter;
1604
$tableColumns, # counter of completed table columns;
1605
$checksums, # paragraph checksums (and associated stream parts);
1606
$macroChecksum, # the current macro checksum;
1607
$varChecksum, # the current user variables checksum;
1608
$pendingTags, # list of tags to be finished after parsing (collected using a structure);
1609
$directiveCounter, # directive counter (just to mark stream directive pairs uniquely);
1610
$retranslator, # a backend object used to restore paragraph sources to be filtered;
1611
$retranslationBuffer, # buffer used in retranslation (needs to b global to avoid closure effects with lexicals in translator routines);
1612
);
1613
1614
# ----- Startup code begins here. -----
1615
1616
# prepare main input handle (obsolete when all people will use perl 5.6)
1617
$inHandle=new IO::File;
1618
1619
# set developer data
1620
my ($developerName, $developer)=('J. Stenzel', 'perl@jochen-stenzel.de');
1621
1622
# init flag
1623
$readCompletely=0;
1624
1625
# prepare a common pattern
1626
my $patternWUmlauts=qr/[\wäöüÄÖÜß]+/;
1627
1628
# prepare lexer patterns
1629
my $patternNlbBackslash=qr/(?
1630
my %lexerPatterns=(
1631
tag => qr/$patternNlbBackslash\\([A-Z_0-9]+)/,
1632
space => qr/(\s+)/,
1633
pfilterDelimiter => qr/$patternNlbBackslash((\|){1,2})/,
1634
table => qr/$patternNlbBackslash\\(TABLE)/,
1635
endTable => qr/$patternNlbBackslash\\(END_TABLE)/,
1636
embed => qr/$patternNlbBackslash\\(EMBED)/,
1637
endEmbed => qr/$patternNlbBackslash\\(END_EMBED)/,
1638
include => qr/$patternNlbBackslash\\(INCLUDE)/,
1639
nonWhitespace => qr/$patternNlbBackslash(\S)/,
1640
colon => qr/$patternNlbBackslash(:)/,
1641
namedVarKernel => qr/\$($patternWUmlauts)/,
1642
symVarKernel => qr/\$({($patternWUmlauts)})/,
1643
);
1644
@lexerPatterns{qw(
1645
namedVar
1646
symVar
1647
)
1648
}=(
1649
qr/$patternNlbBackslash$lexerPatterns{namedVarKernel}/,
1650
qr/$patternNlbBackslash$lexerPatterns{symVarKernel}/,
1651
);
1652
1653
# declare paragraphs which are embedded
1654
my %embeddedParagraphs;
1655
@embeddedParagraphs{
1656
DIRECTIVE_UPOINT,
1657
DIRECTIVE_OPOINT,
1658
}=();
1659
1660
# declare token descriptions (to be used in error messages)
1661
my %tokenDescriptions=(
1662
EOL => 'a carriage return',
1663
Embed => 'embedded code',
1664
Embedded => 'an \END_EMBED tag',
1665
Empty_line => 'an empty line',
1666
Heredoc_close => 'a string closing the "here document"',
1667
Heredoc_open => 'a "here document" opener',
1668
Ils => 'a indentation',
1669
Include => 'an included part',
1670
Named_variable => 'a named variable',
1671
Space => 'a whitespace',
1672
StreamedPart => undef,
1673
Symbolic_variable => 'a symbolic variable',
1674
Table => 'a table',
1675
Table_separator => 'a table column separator',
1676
Tabled => 'an \END_TABLE tag',
1677
Tag_name => 'a tag name',
1678
Word => 'a word',
1679
NoToken => 'an internal dummy token that is finally ignored',
1680
);
1681
1682
%}
1683
1684
# declare tokens (not necessary for Parse::Yapp, but helpful for the reader)
1685
35
35
1
595
%token Word
1686
35
50
170
%token Empty_line
1687
%token Space
1688
%token Tag_name
1689
%token Table
1690
%token Table_separator
1691
%token Tabled
1692
%token Embed
1693
%token Embedded
1694
%token EOL
1695
%token Ils
1696
%token Include
1697
%token Heredoc_open
1698
%token Heredoc_close
1699
%token StreamedPart
1700
%token Named_variable
1701
%token Symbolic_variable
1702
1703
%expect 17
1704
1705
%%
1706
1707
# a valid document consists of paragraphs
1708
document : paragraph
1709
37
100
100
37
1145
{
33
292
1710
# skip empty "paragraphs"
1711
unless ($_[1][0]=~/^\s*$/ or not @{$_[1][0]})
1712
32
167
{
32
188
32
185
1713
# add data to the output stream
1714
push(@{$resultStreamRef->[STREAM_TOKENS]}, @{$_[1][0]});
1715
32
80
32
230
1716
# update tag finish memory
1717
_updateTagFinishMem(scalar(@{$resultStreamRef->[STREAM_TOKENS]}));
1718
32
100
33
1145
33
33
66
1719
# update checksums (unless done before for parts)
1720
_updateChecksums($_[1][0], 'Paragraph_cache_hit') unless $_[1][0][0][STREAM_DIR_TYPE]==DIRECTIVE_BLOCK
1721
or $_[1][0][0][STREAM_DIR_TYPE]==DIRECTIVE_DLIST
1722
or $_[1][0][0][STREAM_DIR_TYPE]==DIRECTIVE_OLIST
1723
or $_[1][0][0][STREAM_DIR_TYPE]==DIRECTIVE_ULIST
1724
or $_[1][0][0][STREAM_DIR_TYPE]==DIRECTIVE_HEADLINE;
1725
32
50
33
2246
1726
# update statistics, if necessary
1727
$statistics{$_[1][0][0][STREAM_DIR_TYPE]}++ unless not defined $_[1][0][0][STREAM_DIR_TYPE] or exists $embeddedParagraphs{$_[1][0][0][STREAM_DIR_TYPE]};
1728
32
100
246
50
1729
# perform special headline operations
1730
if ($_[1][0][0][STREAM_DIR_TYPE]==DIRECTIVE_HEADLINE)
1731
29
72
{
29
100
29
85
29
139
1732
# update headline stream by adding the token index of the headline
1733
push(@{$resultStreamRef->[STREAM_HEADLINES]}, @{$resultStreamRef->[STREAM_TOKENS]}-@{$_[1][0]});
1734
29
10415
1735
# add a copy of the variables valid at the end of the page
1736
$_[1][0][0][STREAM_DIR_HINTS]{vars}=dclone(\%variables);
1737
29
50
33
207
1738
# let the user know that something is going on
1739
print STDERR "\r", ' ' x length('[Info] '), '... ', $statistics{&DIRECTIVE_HEADLINE}, " chapters read."
1740
if $flags{vis}
1741
and not $statistics{&DIRECTIVE_HEADLINE} % $flags{vis};
1742
}
1743
elsif ($_[1][0][0][STREAM_DIR_TYPE]!=DIRECTIVE_COMMENT)
1744
{
1745
0
0
0
# the document starts with streamed content before the first headline,
1746
# this is considered an error except when this happens due to an import
1747
_semerr($_[0], "$sourceFile, line $_[1][1]: the first chapter needs a headline, please add one.") unless exists $flags{complainedAbout1stHeadline};
1748
0
0
0
0
0
0
1749
0
0
# update complaint flag
1750
if (exists $flags{complainedAbout1stHeadline} and $flags{complainedAbout1stHeadline} eq 'IMPORT')
1751
{delete $flags{complainedAbout1stHeadline};}
1752
else
1753
{$flags{complainedAbout1stHeadline}=1;}
1754
}
1755
32
194
1756
# this is for the parser to flag success
1757
1;
1758
}
1759
}
1760
| document paragraph
1761
470
100
100
470
3845
{
348
1798
1762
# skip empty "paragraphs"
1763
unless ($_[2][0]=~/^\s*$/ or not @{$_[2][0]})
1764
326
460
{
326
1402
326
1494
1765
# add data to the output stream, if necessary
1766
push(@{$resultStreamRef->[STREAM_TOKENS]}, @{$_[2][0]});
1767
326
607
326
1557
1768
# update tag finish memory
1769
_updateTagFinishMem(scalar(@{$resultStreamRef->[STREAM_TOKENS]}));
1770
326
100
66
7264
66
66
100
1771
# update checksums, if necessary
1772
_updateChecksums($_[2][0], 'Paragraph_cache_hit') unless $_[2][0][0][STREAM_DIR_TYPE]==DIRECTIVE_BLOCK
1773
or $_[2][0][0][STREAM_DIR_TYPE]==DIRECTIVE_DLIST
1774
or $_[2][0][0][STREAM_DIR_TYPE]==DIRECTIVE_OLIST
1775
or $_[2][0][0][STREAM_DIR_TYPE]==DIRECTIVE_ULIST
1776
or $_[2][0][0][STREAM_DIR_TYPE]==DIRECTIVE_HEADLINE;
1777
326
50
1329
1778
# update ordered list flag as necessary
1779
$flags{olist}=0 unless $_[2][0][0][STREAM_DIR_TYPE]==DIRECTIVE_OLIST;
1780
326
50
1821
1781
# update statistics, if necessary
1782
$statistics{$_[2][0][0][STREAM_DIR_TYPE]}++ unless exists $embeddedParagraphs{$_[2][0][0][STREAM_DIR_TYPE]};
1783
326
100
100
2056
100
33
66
1784
260
1955
# perform special headline operations
1785
if ($_[2][0][0][STREAM_DIR_TYPE]==DIRECTIVE_HEADLINE)
1786
59
94
{
59
138
59
258
59
1557
1787
# update headline stream by adding the token index of the headline
1788
push(@{$resultStreamRef->[STREAM_HEADLINES]}, @{$resultStreamRef->[STREAM_TOKENS]}-@{$_[2][0]});
1789
59
10258
1790
# add a copy of the variables valid at the end of the page
1791
$_[2][0][0][STREAM_DIR_HINTS]{vars}=dclone(\%variables);
1792
59
50
33
303
1793
# let the user know that something is going on, if necessary
1794
print STDERR "\r", ' ' x length('[Info] '), '... ', $statistics{&DIRECTIVE_HEADLINE}, " chapters read."
1795
if $flags{vis}
1796
and not $statistics{&DIRECTIVE_HEADLINE} % $flags{vis};
1797
}
1798
elsif (
1799
$_[2][0][0][STREAM_DIR_TYPE]!=DIRECTIVE_COMMENT
1800
and not @{$resultStreamRef->[STREAM_HEADLINES]}
1801
and (
1802
not exists $flags{complainedAbout1stHeadline}
1803
or $flags{complainedAbout1stHeadline} eq 'IMPORT'
1804
)
1805
)
1806
{
1807
1
50
17
# the document starts with streamed content before the first headline,
1808
# this is considered an error except when this happens due to an import
1809
_semerr($_[0], "$sourceFile, line $_[2][1]: the first chapter needs a headline, please add one.") unless exists $flags{complainedAbout1stHeadline};
1810
1
50
33
7
0
0
1811
1
4
# update complaint flag
1812
if (exists $flags{complainedAbout1stHeadline} and $flags{complainedAbout1stHeadline} eq 'IMPORT')
1813
{delete $flags{complainedAbout1stHeadline};}
1814
else
1815
{$flags{complainedAbout1stHeadline}=1;}
1816
}
1817
326
1499
1818
# this is for the parser to flag success
1819
1;
1820
}
1821
}
1822
;
1823
1824
1825
# paragraph filters
1826
optional_paragraph_filter : # this makes it optional
1827
| '||'
1828
8
8
53
{
1829
# switch to pfiltered mode
1830
_stateManager(STATE_PFILTER);
1831
}
1832
paragraph_filters '||'
1833
8
8
44
{
1834
# back to default mode
1835
_stateManager(STATE_PFILTERED);
1836
8
22
1837
# supply filter list
1838
$_[3];
1839
}
1840
;
1841
1842
1843
# paragraph filter
1844
paragraph_filters : Word
1845
8
8
63
{
1846
# start a new filter list
1847
[[$_[1][0]], $_[1][1]];
1848
}
1849
| paragraph_filters '|' Word
1850
0
0
0
{
0
0
1851
0
0
# append to filter list and reply updated list
1852
push(@{$_[1][0]}, $_[3][0]);
1853
[$_[1][0], $_[3][1]];
1854
}
1855
;
1856
1857
1858
1859
# paragraph (default actions work)
1860
paragraph : built_paragraph
1861
| restored_paragraph
1862
;
1863
1864
1865
# build paragraph (default actions work)
1866
built_paragraph : optional_paragraph_filter
1867
475
100
475
2429
{
1868
# filter set?
1869
if ($_[1])
1870
8
24
{
1871
# prepare an extra "token" to start the next paragraph
1872
$flags{virtualParagraphStart}=1;
1873
1874
8
43
# Disable storage of a checksum. (A filter can make the paragraph depending
1875
# on something outside the paragraph - the paragraph becomes dynamic.)
1876
$flags{checksummed}=0;
1877
}
1878
}
1879
original_paragraph
1880
{
1881
471
471
1198
# reset the "extra token" flag (it already worked when the parser
1882
# reaches this point)
1883
$flags{virtualParagraphStart}=0;
1884
471
100
2284
1885
# filters installed and active?
1886
if ($_[1])
1887
8
50
35
{
1888
# Does the caller want to evaluate code?
1889
if ($safeObject)
1890
8
50
45
{
1891
# update active contents base data, if necessary
1892
34
34
430
if ($flags{activeBaseData})
34
81
34
70102
1893
0
0
0
{
0
0
1894
no strict 'refs';
1895
${join('::', ref($safeObject) ? $safeObject->root : 'main', 'PerlPoint')}=dclone($flags{activeBaseData});
1896
}
1897
8
73
1898
# peform filter call(s)
1899
my $result=_pfilterCall($_[0], $_[1][0], $_[3][0], $_[3][1]);
1900
8
50
32
1901
# reply unmodified paragraph in case of an error
1902
return $_[3] unless defined $result;
1903
8
50
28
1904
# make the result part of the input stream, if any
1905
_stackInput($_[0], @$result) if $result;
1906
8
50
25
1907
# reset the "end of input reached" flag if necessary
1908
$readCompletely=0 if $readCompletely;
1909
8
53
1910
# supply nothing here, the result must be reparsed first
1911
['', $_[3][2]];
1912
}
1913
else
1914
0
0
0
{
1915
# filters cannot be run, inform user
1916
warn "[Warn] $sourceFile, line $_[1][1]: Active Content is disabled, paragraph cannot be filtered.\n" unless $flags{display} & DISPLAY_NOWARN;
1917
0
0
1918
# supply the unmodified paragraph
1919
$_[3];
1920
}
1921
}
1922
else
1923
463
1743
{
1924
# no filter: provide paragraph data
1925
$_[3];
1926
}
1927
}
1928
| non_filterable_paragraph
1929
;
1930
1931
# original paragraph (not composed, unmodified)
1932
original_paragraph: headline
1933
| optionally_dotted_text
1934
239
100
239
574
{
240
100
3192
1
4
1935
# remove leading dummy tokens which might have been produced by "standalone macros"
1936
splice(@{$_[1][0]}, 1, 1) while @{$_[1][0]}>1 and !ref($_[1][0][1]) and $_[1][0][1] eq DUMMY_TOKEN;
1937
1938
# check if this paragraph consists of exactly one table only
1939
239
50
100
391
# or exactly one tag which is allowed to exists standalone,
100
66
66
33
33
33
33
1940
# or exactly one embedded region
1941
if (
1942
239
3459
(
1943
# starting with a table tag or standalone tag?
1944
@{$_[1][0]}>1
1945
and ref($_[1][0][1]) eq 'ARRAY'
1946
and $_[1][0][1][STREAM_DIR_TYPE]==DIRECTIVE_TAG
1947
and (
1948
$_[1][0][1][STREAM_DIR_DATA]=~/^(TABLE)$/
1949
or (
1950
$_[1][0][1][STREAM_DIR_DATA]=~/^(\w+)$/
1951
and (
1952
(
1953
exists $tagsRef->{$1}
1954
and exists $tagsRef->{$1}{standalone}
1955
and $tagsRef->{$1}{standalone}
1956
)
1957
or $1 eq 'EMBED'
1958
)
1959
)
1960
)
1961
1962
# ending with the same tag?
1963
and ref($_[1][0][-2]) eq 'ARRAY'
1964
and $_[1][0][-2][STREAM_DIR_TYPE]==DIRECTIVE_TAG
1965
and $_[1][0][-2][STREAM_DIR_DATA] eq $1
1966
1967
# both building the same tag?
1968
and $_[1][0][-2][STREAM_DIR_DATA+1] eq $_[1][0][1][STREAM_DIR_DATA+1]
1969
)
1970
)
1971
13
24
{
13
31
1972
13
29
# remove the enclosing paragraph stuff - just return the contents (table / tag)
13
201
1973
shift(@{$_[1][0]}); # text paragraph opener
1974
pop(@{$_[1][0]}); # text paragraph trailer
1975
}
1976
239
863
1977
# pass (original or modified) data
1978
$_[1];
1979
}
1980
| verbatim
1981
| comment
1982
| dstream_entrypoint
1983
| table_paragraph
1984
| compound_block
1985
| list
1986
# variable assigments should not be filtered, but moving them
1987
# to "non_filterable_paragraphs" causes additional shift/reduce
1988
# conflicts ... hm, this should be solved
1989
| variable_assignment
1990
;
1991
1992
# non filterable paragraph (filtering control paragraphs which produce
1993
# nothing directly makes no sense)
1994
non_filterable_paragraph : Empty_line
1995
| condition
1996
| alias_definition
1997
;
1998
1999
# paragraph restored from cache (default action works)
2000
restored_paragraph : Paragraph_cache_hit
2001
;
2002
2003
# headline
2004
headline : headline_level
2005
84
84
407
{
2006
# switch to headline mode
2007
_stateManager(STATE_HEADLINE);
2008
84
258
2009
# update headline level hints
2010
$flags{headlineLevel}=$_[1][0];
2011
84
50
521
2012
# trace, if necessary
2013
warn "[Trace] $sourceFile, line $_[1][1]: Headline (of level $_[1][0]) starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2014
}
2015
basics optional_headline_shortcut Empty_line
2016
84
84
319
{
2017
# back to default mode
2018
_stateManager(STATE_DEFAULT);
2019
84
50
340
2020
# trace, if necessary
2021
warn "[Trace] $sourceFile, line $_[5][1]: Headline completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2022
84
66
160
166
2119
82
3587
2023
# remove trailing whitespaces (the final one represents the final newline)
2024
pop(@{$_[3][0]}) while @{$_[3][0]} and $_[3][0][-1]=~/^\s*$/;
2025
84
100
376
2026
# abbreviation declared?
2027
if ($_[4][0])
2028
{
2029
1
8
# remove trailing whitespaces which separated a shortcut directive from
2030
# the long headline title version, if a shortcut was specified
2031
1
5
$_[3][0][-1]=~s/\s+$//;
2032
1
10
# remove leading and trailing whitespaces from the shortcut
2033
$_[4][0]=~s/^\s+//;
2034
$_[4][0]=~s/\s+$//;
2035
}
2036
84
208
2037
# update related data
2038
@olistLevels=();
2039
84
512
2040
84
336
# update directive counter and the level hierarchy memory
2041
$#headlineIds=$flags{headlineLevel}-1;
2042
$headlineIds[$flags{headlineLevel}-1]=++$flags{headlinenr};
2043
84
856
2044
# prepare result (data part and shortcut string)
2045
my %hints=(
2046
nr => ++$directiveCounter,
2047
shortcut => $_[4][0],
2048
84
458
docstreams => {},
2049
);
2050
my $data=[
2051
# opener directive (including headline level)
2052
84
526
[\%hints, DIRECTIVE_HEADLINE, DIRECTIVE_START, $_[1][0]],
2053
# the list of enclosed literals
2054
@{$_[3][0]},
2055
# final directive (including headline level again)
2056
[\%hints, DIRECTIVE_HEADLINE, DIRECTIVE_COMPLETE, $_[1][0]]
2057
];
2058
84
621
2059
# update checksums (done here because hits need special handling)
2060
_updateChecksums($data, 'Headline_cache_hit');
2061
84
221
2062
# update pointer to the current docstream hash
2063
$flags{chapterDocstreams}=$hints{docstreams};
2064
84
595
2065
# reply data
2066
[$data, $_[5][1]];
2067
}
2068
| Headline_cache_hit
2069
0
0
0
{
2070
# update headline level hint
2071
$flags{headlineLevel}=$_[1][0][0][STREAM_DIR_DATA];
2072
0
0
2073
# reset chapter docstream hash and update the appropriate pointer
2074
$flags{chapterDocstreams}=$_[1][0][0][STREAM_DIR_HINTS]{docstreams}={};
2075
0
0
2076
# supply what you got unchanged
2077
$_[1];
2078
}
2079
;
2080
2081
# headline level
2082
headline_level : '='
2083
84
84
1283
{
2084
# switch to headline intro mode
2085
_stateManager(STATE_HEADLINE_LEVEL);
2086
84
809
2087
# start new counter and reply it
2088
[$flags{headlineLevelOffset}+1, $_[1][1]];
2089
}
2090
| headline_level '='
2091
70
70
558
{
2092
# update counter and reply it
2093
[$_[1][0]+1, $_[1][1]];
2094
}
2095
;
2096
2097
# optional headline shortcut
2098
optional_headline_shortcut : # this makes it optional
2099
83
83
633
{
2100
# nothing declared: supply an empty shortcut string
2101
['', $lineNrs{$inHandle}];
2102
}
2103
| '~' words_or_spaces
2104
1
1
4
{
1
6
2105
# reply the shortcut string
2106
[join('', @{$_[2][0]}), $lineNrs{$inHandle}];
2107
}
2108
;
2109
2110
# condition paragraph
2111
condition : '?'
2112
12
12
37
{
2113
# switch to condition mode
2114
_stateManager(STATE_CONDITION);
2115
12
50
45
2116
# trace, if necessary
2117
warn "[Trace] $sourceFile, line $_[1][1]: Condition paragraph starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2118
}
2119
basics Empty_line
2120
12
12
32
{
2121
# back to default mode
2122
_stateManager(STATE_DEFAULT);
2123
12
50
29
2124
# trace, if necessary
2125
warn "[Trace] $sourceFile, line $_[4][1]: condition completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2126
2127
12
50
33
15
# The condition is written in Perl, anything passed really?
12
159
2128
# And does the caller want to evaluate the code?
2129
if (@{$_[3][0]} and $safeObject)
2130
12
50
74
{
2131
# trace, if necessary
2132
warn "[Trace] Evaluating condition ...\n" if $flags{trace} & TRACE_SEMANTIC;
2133
12
50
31
2134
# update active contents base data, if necessary
2135
34
34
277
if ($flags{activeBaseData})
34
121
34
198017
2136
12
50
341
{
12
84
2137
no strict 'refs';
2138
${join('::', ref($safeObject) ? $safeObject->root : 'main', 'PerlPoint')}=dclone($flags{activeBaseData});
2139
}
2140
12
146
12
40
2141
12
36
# make the Perl code a string and evaluate it
2142
12
50
33
my $perl=join('', @{$_[3][0]});
2143
12
50
326
$^W=0;
2144
12
11659
warn "[Trace] $sourceFile, line $_[3][1]: Evaluating condition code:\n\n$perl\n\n\n" if $flags{trace} & TRACE_ACTIVE;
2145
my $result=ref($safeObject) ? $safeObject->reval($perl) : eval(join(' ', '{package main; no strict;', $perl, '}'));
2146
$^W=1;
2147
12
50
29
0
0
2148
# check result
2149
if ($@)
2150
{_semerr($_[0], "$sourceFile, line $_[3][1]: condition code could not be evaluated: $@.");}
2151
else
2152
12
0
0
79
{
50
33
2153
# trace, if necessary
2154
warn "[Trace] Condition is ", (defined $result and $result) ? 'true, parsing continues' : 'false, parsing is temporarily suspended', ".\n" if $flags{trace} & TRACE_ACTIVE or $flags{trace} & TRACE_SEMANTIC;
2155
12
50
33
48
2156
# success - configure parser behaviour according to result
2157
$flags{skipInput}=1 unless (defined $result and $result);
2158
}
2159
}
2160
else
2161
0
0
0
{
2162
# trace, if necessary
2163
warn "[Trace] Condition is not evaluated because of disabled active contents.\n" if $flags{trace} & TRACE_SEMANTIC;
2164
}
2165
12
56
2166
# we have to supply something, but it should be nothing (note that this is a *paragraph*, so reply a *string*)
2167
['', $_[4][1]];
2168
}
2169
;
2170
2171
# a list consists of a number of certain elements (default actions work)
2172
list : list_part
2173
| list list_part
2174
14
14
34
{
14
74
14
70
2175
14
61
# update token list and reply it
2176
push(@{$_[1][0]}, @{$_[2][0]});
2177
[$_[1][0], $_[2][1]];
2178
}
2179
| list list_shift list_part
2180
4
4
19
{
2181
# update statistics, if necessary (shifters are not passed as standalone paragraphs, so ...)
2182
$statistics{$_[2][0][0][1]}++;
2183
2184
# add shift informations to related list parts: the predecessor
2185
4
60
# gets informations about a following shift, the successor about
4
22
2186
4
11
# a predecessing shift
2187
4
10
@{$_[1][0][-1]}[STREAM_DIR_DATA+3, STREAM_DIR_DATA+4]
2188
=@{$_[3][0][ 0]}[STREAM_DIR_DATA+1, STREAM_DIR_DATA+2]
2189
=@{$_[2][0][ 0]}[STREAM_DIR_TYPE, STREAM_DIR_DATA];
2190
4
10
4
9
4
8
4
16
2191
4
17
# update token list and reply it
2192
push(@{$_[1][0]}, @{$_[2][0]}, @{$_[3][0]});
2193
[$_[1][0], $_[3][1]];
2194
}
2195
;
2196
2197
# list parts (partial lists)
2198
list_part : olist
2199
9
100
66
9
78
{
2200
# the first point may start by a certain number, check this
2201
my $start=(defined $_[1][0][0][STREAM_DIR_DATA] and $_[1][0][0][STREAM_DIR_DATA]>1) ? $_[1][0][0][STREAM_DIR_DATA] : 1;
2202
9
37
2203
# embed the points into list directives
2204
my %hints=(nr=>++$directiveCounter);
2205
[
2206
9
66
[
2207
# opener directive
2208
9
53
[\%hints, DIRECTIVE_OLIST, DIRECTIVE_START, $start, (0) x 4],
2209
# the list of enclosed literals
2210
@{$_[1][0]},
2211
# final directive
2212
[\%hints, DIRECTIVE_OLIST, DIRECTIVE_COMPLETE, $start, (0) x 4]
2213
],
2214
$_[1][1]
2215
];
2216
}
2217
| ulist
2218
9
9
29
{
2219
# reset ordered list flag
2220
$flags{olist}=0;
2221
9
36
2222
# embed the points into list directives
2223
my %hints=(nr=>++$directiveCounter);
2224
[
2225
9
108
[
2226
# opener directive
2227
9
102
[\%hints, DIRECTIVE_ULIST, DIRECTIVE_START, 0, (0) x 4],
2228
# the list of enclosed literals
2229
@{$_[1][0]},
2230
# final directive
2231
[\%hints, DIRECTIVE_ULIST, DIRECTIVE_COMPLETE, 0, (0) x 4]
2232
],
2233
$_[1][1]
2234
];
2235
}
2236
| dlist
2237
5
5
17
{
2238
# reset ordered list flag
2239
$flags{olist}=0;
2240
5
22
2241
# embed the points into list directives
2242
my %hints=(nr=>++$directiveCounter);
2243
[
2244
5
53
[
2245
# opener directive
2246
5
30
[\%hints, DIRECTIVE_DLIST, DIRECTIVE_START, 0, (0) x 4],
2247
# the list of enclosed literals
2248
@{$_[1][0]},
2249
# final directive
2250
[\%hints, DIRECTIVE_DLIST, DIRECTIVE_COMPLETE, 0, (0) x 4]
2251
],
2252
$_[1][1]
2253
];
2254
}
2255
;
2256
2257
# ordered list
2258
olist : opoint
2259
| olist opoint
2260
2
2
8
{
2
10
2
7
2261
2
10
# update token list and reply it
2262
push(@{$_[1][0]}, @{$_[2][0]});
2263
[$_[1][0], $_[2][1]];
2264
}
2265
;
2266
2267
# unordered list
2268
ulist : upoint
2269
| ulist upoint
2270
1
1
2
{
1
4
1
4
2271
1
5
# update token list and reply it
2272
push(@{$_[1][0]}, @{$_[2][0]});
2273
[$_[1][0], $_[2][1]];
2274
}
2275
;
2276
2277
# definition list
2278
dlist : dpoint
2279
| dlist dpoint
2280
1
1
3
{
1
4
1
6
2281
1
5
# update token list and reply it
2282
push(@{$_[1][0]}, @{$_[2][0]});
2283
[$_[1][0], $_[2][1]];
2284
}
2285
;
2286
2287
# ordered list point
2288
opoint : opoint_opener
2289
11
11
38
{
2290
# switch to opoint mode
2291
_stateManager(STATE_OPOINT);
2292
11
50
56
2293
# trace, if necessary
2294
warn "[Trace] $sourceFile, line $_[1][1]: Ordered list point starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2295
}
2296
text
2297
11
11
59
{
2298
# update statistics (list points are not passed as standalone paragraphs, so ...)
2299
$statistics{&DIRECTIVE_OPOINT}++;
2300
11
50
49
2301
# trace, if necessary
2302
warn "[Trace] $sourceFile, line $_[3][1]: Ordered list point completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2303
11
100
150
11
83
2304
# remove leading whitespaces from point text (it separated number wildcard and literal text part)
2305
splice(@{$_[3][0]}, 1, 1) while not ref($_[3][0][1]) and $_[3][0][1]=~/^\s*$/;
2306
11
54
2307
# reply data (they are already well prepared except that they are marked as text)
2308
$_[3][0][0][STREAM_DIR_TYPE]=$_[3][0][-1][STREAM_DIR_TYPE]=&DIRECTIVE_OPOINT;
2309
11
100
66
309
2310
# update list level hints as necessary
2311
$olistLevels[0]=(($flags{olist} or $_[1][0]) and @olistLevels) ? $olistLevels[0]+1 : 1;
2312
11
50
66
62
66
2313
# add a level hint, if necessary
2314
1
2
if ($_[1][0] and not $flags{olist} and $olistLevels[0]>1)
1
4
2315
1
3
{
1
3
2316
push(@{$_[3][0][0]}, $olistLevels[0]);
2317
push(@{$_[3][0][-1]}, $olistLevels[0]);
2318
}
2319
11
23
2320
# update ordered list flag
2321
$flags{olist}=1;
2322
11
50
45
2323
11
61
# update checksums, if possible
2324
$flags{checksummed}=0 unless $flags{virtualParagraphStart};
2325
_updateChecksums($_[3][0], 'Opoint_cache_hit');
2326
11
35
2327
# supply result
2328
$_[3];
2329
}
2330
| Opoint_cache_hit
2331
0
0
0
0
0
{
2332
# update list level hints as necessary
2333
$olistLevels[0]=($flags{olist} and @olistLevels) ? $olistLevels[0]+1 : 1;
2334
0
0
0
0
0
2335
# update continued list points
2336
$_[1][0][0][STREAM_DIR_DATA]=$olistLevels[0] if @{$_[1][0][0]}>3;
2337
0
0
2338
# update ordered list flag
2339
$flags{olist}=1;
2340
0
0
2341
# supply updated stream snippet
2342
10
10
45
$_[1];
2343
}
2344
;
2345
2346
# ordered list point opener sequence - determining if a former list should be continued
2347
# (simply reply a flag)
2348
1
1
5
opoint_opener : '#'
2349
{[0, $_[1][1]];}
2350
| '#' '#'
2351
{[1, $_[1][1]];}
2352
;
2353
2354
# unordered list point
2355
upoint : '*'
2356
10
10
41
{
2357
# switch to upoint mode
2358
_stateManager(STATE_UPOINT);
2359
10
50
49
2360
# trace, if necessary
2361
warn "[Trace] $sourceFile, line $_[1][1]: Unordered list point starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2362
}
2363
text
2364
10
10
68
{
2365
# update statistics (list points are not passed as standalone paragraphs, so ...)
2366
$statistics{&DIRECTIVE_UPOINT}++;
2367
10
50
38
2368
# trace, if necessary
2369
warn "[Trace] $sourceFile, line $_[3][1]: Unordered list point completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2370
10
100
370
10
76
2371
# remove leading whitespaces from point text (it separated bullet and literal text part)
2372
splice(@{$_[3][0]}, 1, 1) while not ref($_[3][0][1]) and $_[3][0][1]=~/^\s*$/;
2373
10
66
289
0
0
2374
# remove trailing whitespaces from point text (it represents the final newline character)
2375
splice(@{$_[3][0]}, -2, 1) while not ref($_[3][0][-2]) and $_[3][0][-2]=~/^\s*$/;
2376
10
56
2377
# reply data (they are already well prepared except that they are marked as text)
2378
$_[3][0][0][STREAM_DIR_TYPE]=$_[3][0][-1][STREAM_DIR_TYPE]=&DIRECTIVE_UPOINT;
2379
10
50
40
2380
10
130
# update checksums, if possible
2381
$flags{checksummed}=0 unless $flags{virtualParagraphStart};
2382
_updateChecksums($_[3][0], 'Upoint_cache_hit');
2383
10
35
2384
# supply result
2385
$_[3];
2386
}
2387
| Upoint_cache_hit
2388
;
2389
2390
2391
6
6
18
# definition list point
2392
dpoint : dlist_opener
2393
{
2394
}
2395
text
2396
6
6
44
{
2397
# update statistics (list points are not passed as standalone paragraphs, so ...)
2398
$statistics{&DIRECTIVE_DPOINT}++;
2399
6
50
34
2400
# trace, if necessary
2401
warn "[Trace] $sourceFile, line $_[3][1]: Definition list point completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2402
6
66
91
6
54
2403
# remove leading whitespaces from point text (it separated point introduction and literal text part)
2404
splice(@{$_[3][0]}, 1, 1) while not ref($_[3][0][1]) and $_[3][0][1]=~/^\s*$/;
2405
6
48
2406
6
28
# reply data (they are already well prepared except that they are marked as text, and that the definition item stream needs to be added)
2407
6
26
my ($hints1, $hints2, $hints3)=({nr=>++$directiveCounter}, {nr=>++$directiveCounter}, {nr=>++$directiveCounter});
2408
$_[3][0][0]=[$hints1, DIRECTIVE_DPOINT, DIRECTIVE_START];
2409
$_[3][0][-1]=[$hints1, DIRECTIVE_DPOINT, DIRECTIVE_COMPLETE];
2410
6
23
6
39
2411
# insert the definition item stream and an envelope for the explanation part
2412
6
21
splice(@{$_[3][0]}, 1, 0,
2413
[$hints2, DIRECTIVE_DPOINT_ITEM, DIRECTIVE_START],
2414
@{$_[1][0]},
2415
[$hints2, DIRECTIVE_DPOINT_ITEM, DIRECTIVE_COMPLETE],
2416
6
13
[$hints3, DIRECTIVE_DPOINT_TEXT, DIRECTIVE_START],
6
21
2417
);
2418
splice(@{$_[3][0]}, -1, 0, [$hints3, DIRECTIVE_DPOINT_TEXT, DIRECTIVE_COMPLETE]);
2419
6
50
33
2420
6
46
# update checksums, if possible
2421
$flags{checksummed}=0 unless $flags{virtualParagraphStart};
2422
_updateChecksums($_[3][0], 'Dpoint_cache_hit');
2423
6
17
2424
# supply the result
2425
$_[3];
2426
}
2427
| Dpoint_cache_hit
2428
;
2429
2430
2431
# definition list opener
2432
dlist_opener : Colon
2433
6
6
39
{
2434
# switch to dlist item mode
2435
_stateManager(STATE_DPOINT_ITEM);
2436
6
50
34
2437
# trace, if necessary
2438
warn "[Trace] $sourceFile, line $_[1][1]: Definition list point starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2439
}
2440
elements Colon
2441
6
6
30
{
2442
# switch to dlist body mode
2443
_stateManager(STATE_DPOINT);
2444
6
29
2445
# simply pass the elements
2446
[$_[3][0], $_[4][1]];
2447
}
2448
;
2449
2450
2451
# compound block
2452
compound_block : block
2453
| compound_block block
2454
{
2455
# this is tricky - to combine both blocks, we have to remove the already
2456
# embedded stop/start directives and to supply the ...
2457
[
2458
8
29
[
8
44
8
141
2459
# ... original collection WITHOUT the final directive ...
2460
@{$_[1][0]}[0..$#{$_[1][0]}-1],
2461
# insert two additional newline characters (restoring the original empty line)
2462
8
8
26
"\n\n",
8
30
2463
# ... combined with the new block, except of its INTRO directive
2464
@{$_[2][0]}[1..$#{$_[2][0]}],
2465
],
2466
$_[2][1]
2467
];
2468
}
2469
| compound_block block_flagnew compound_block
2470
1
1
11
{
2471
# update statistics (for the first part which is completed by the intermediate flag paragraph)
2472
$statistics{&DIRECTIVE_BLOCK}++;
2473
2474
# this is simply a list of both blocks
2475
[
2476
1
4
[
1
14
2477
# original collection
2478
1
2
@{$_[1][0]},
2479
# ... followed by the new block
2480
@{$_[3][0]},
2481
],
2482
$_[3][1]
2483
];
2484
}
2485
;
2486
2487
# control paragraph: block connector
2488
block_flagnew : '-'
2489
1
1
14
{
2490
# switch to control mode
2491
_stateManager(STATE_CONTROL);
2492
1
50
7
2493
# trace, if necessary
2494
warn "[Trace] $sourceFile, line $_[1][1]: New block flag starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2495
}
2496
Empty_line
2497
1
1
5
{
2498
# back to default mode
2499
_stateManager(STATE_DEFAULT);
2500
1
50
4
2501
# trace, if necessary
2502
warn "[Trace] $sourceFile, line $_[1][1]: New block flag completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2503
1
4
2504
# reply data (these are dummies because block connectors are not made part of the output stream)
2505
$_[3];
2506
}
2507
;
2508
2509
# block
2510
block : Ils
2511
25
25
105
{
2512
# switch to block mode
2513
_stateManager(STATE_BLOCK);
2514
25
50
775
2515
# trace, if necessary
2516
warn "[Trace] $sourceFile, line $_[1][1]: Block starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2517
}
2518
text
2519
25
50
25
108
{
2520
# trace, if necessary
2521
warn "[Trace] $sourceFile, line $_[3][1]: Block completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2522
2523
# reply data (they are almost perfect except that they are marked as text,
2524
25
121
# and that the initial spaces have to be inserted, and that a trailing newline
2525
25
277
# has to be removed)
25
103
2526
$_[3][0][0][STREAM_DIR_TYPE]=$_[3][0][-1][STREAM_DIR_TYPE]=DIRECTIVE_BLOCK;
2527
25
100
33
44
splice(@{$_[3][0]}, 1, 0, $_[1][0]);
24
66
54
25
366
2528
# remove the final newline made from the last carriage return, if any
2529
splice(@{$_[3][0]}, -2, 1) if @{$_[3][0]}>2 and defined $_[3][0][-2] and $_[3][0][-2] eq "\n";
2530
25
100
130
2531
25
134
# update checksums, if possible
2532
$flags{checksummed}=0 unless $flags{virtualParagraphStart};
2533
_updateChecksums($_[3][0], 'Block_cache_hit');
2534
25
72
2535
# supply result
2536
$_[3];
2537
}
2538
| Block_cache_hit
2539
;
2540
2541
# common text layer
2542
optionally_dotted_text : text
2543
| dotted_text
2544
;
2545
2546
# text
2547
text : literal
2548
363
100
100
363
7677
{
100
100
66
100
100
2549
# enter text mode - unless we are in a block (or point (which already set this mode itself))
2550
unless ( $parserState==STATE_BLOCK
2551
or $parserState==STATE_UPOINT
2552
or $parserState==STATE_OPOINT
2553
or $parserState==STATE_DPOINT
2554
or $parserState==STATE_DPOINT_ITEM
2555
or $parserState==STATE_DEFINITION
2556
or $parserState==STATE_TEXT
2557
)
2558
231
1101
{
2559
# switch to new mode
2560
_stateManager(STATE_TEXT);
2561
231
50
1702
2562
# trace, if necessary
2563
warn "[Trace] $sourceFile, line $_[1][1]: Text starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2564
}
2565
}
2566
optional_literals
2567
Empty_line
2568
363
0
33
363
1651
{
33
0
0
0
2569
# trace, if necessary
2570
warn "[Trace] $sourceFile, line $_[4][1]: Text completed.\n" unless not $flags{trace} & TRACE_PARAGRAPHS
2571
or $parserState==STATE_BLOCK
2572
or $parserState==STATE_UPOINT
2573
or $parserState==STATE_OPOINT
2574
or $parserState==STATE_DPOINT
2575
or $parserState==STATE_DPOINT_ITEM;
2576
363
1143
2577
# back to default mode
2578
_stateManager(STATE_DEFAULT);
2579
363
50
66
2912
0
0
2580
# remove the final EOL literal, if any
2581
pop(@{$_[3][0]}) if defined $_[3][0][-1] and $_[3][0][-1] eq 'EOL';
2582
363
100
100
2347
313
645
2583
# remove the final whitespace string made from the last carriage return, if any
2584
pop(@{$_[3][0]}) if defined $_[3][0][-1] and $_[3][0][-1] eq ' ';
2585
363
100
100
561
363
100
3317
30
164
2586
# reply data, if any
2587
340
1288
if ((@{$_[1][0]} and $_[1][0][0]) or @{$_[3][0]})
2588
{
2589
my %hints=(nr=>++$directiveCounter);
2590
[
2591
340
991
[
2592
# opener directive
2593
340
1244
[\%hints, DIRECTIVE_TEXT, DIRECTIVE_START],
340
2756
2594
# the list of enclosed literals
2595
@{$_[1][0]}, @{$_[3][0]},
2596
# final directive
2597
[\%hints, DIRECTIVE_TEXT, DIRECTIVE_COMPLETE],
2598
],
2599
$_[4][1],
2600
];
2601
}
2602
else
2603
23
131
{
2604
# reply nothing real
2605
[[()], $_[4][1]];
2606
}
2607
}
2608
;
2609
2610
# optionally dotted text - a helper construct to allow texts to be started by a dot
2611
dotted_text : '.'
2612
8
8
43
{
2613
# switch to new mode (to stop special handling of dots)
2614
_stateManager(STATE_TEXT);
2615
}
2616
text
2617
8
8
20
{
2618
# supply the text
2619
$_[3];
2620
}
2621
;
2622
2623
# verbatim block
2624
verbatim : Heredoc_open
2625
12
12
49
{
2626
# switch to verbatim mode
2627
_stateManager(STATE_VERBATIM);
2628
12
50
52
2629
# trace, if necessary
2630
warn "[Trace] $sourceFile, line $_[1][1]: Verbatim block starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2631
12
50
52
2632
# check close hint: should be different from "1"
2633
_semerr($_[0], "A heredoc close hint should be different from \"1\".") if $_[1][0] eq '1';
2634
12
55
2635
# store close hint
2636
$specials{heredoc}=$_[1][0];
2637
}
2638
literals_and_empty_lines
2639
Heredoc_close
2640
12
50
12
78
{
2641
# trace, if necessary
2642
warn "[Trace] $sourceFile, line $_[4][1]: Verbatim block completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2643
12
49
2644
# back to default mode
2645
_stateManager(STATE_DEFAULT);
2646
12
20
12
34
2647
# delete the initial newline (which follows the opener but is no part of the block)
2648
shift(@{$_[3][0]});
2649
12
51
2650
# reply data
2651
my %hints=(nr=>++$directiveCounter);
2652
[
2653
12
97
[
2654
# opener directive
2655
12
49
[\%hints, DIRECTIVE_VERBATIM, DIRECTIVE_START],
2656
# the list of enclosed literals
2657
@{$_[3][0]},
2658
# final directive
2659
[\%hints, DIRECTIVE_VERBATIM, DIRECTIVE_COMPLETE]
2660
],
2661
$_[4][1]
2662
];
2663
}
2664
;
2665
2666
# variable assignment
2667
variable_assignment : Named_variable '='
2668
65
65
180
{
2669
# switch to text mode to allow *all* characters starting a variable value!
2670
_stateManager(STATE_TEXT);
2671
65
50
270
2672
# trace, if necessary
2673
warn "[Trace] $sourceFile, line $_[1][1]: Variable assignment starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2674
}
2675
text
2676
65
65
91
{
65
154
2677
65
144
# remove text directives and the final space (made from the final EOL)
65
127
2678
shift(@{$_[4][0]});
2679
pop(@{$_[4][0]});
2680
65
176
65
334
2681
# make the text contents a string and store it
2682
$variables{$_[1][0]}=join('', @{$_[4][0]});
2683
65
50
366
2684
# the variable might have been reset
2685
delete($variables{$_[1][0]}) if $variables{$_[1][0]}=~/^\s*$/;
2686
65
407
2687
# update variable checksum
2688
$varChecksum=sha1_base64(nfreeze(\%variables));
2689
65
100
4343
2690
# propagate the setting to the stream, if necessary
2691
20
26
if ($flags{var2stream})
20
155
2692
{
2693
push(@{$resultStreamRef->[STREAM_TOKENS]}, [{}, DIRECTIVE_VARSET, DIRECTIVE_START, {var=>$_[1][0], value=>$variables{$_[1][0]}}]);
2694
20
35
20
76
2695
# update tag finish memory by the way
2696
_updateTagFinishMem(scalar(@{$resultStreamRef->[STREAM_TOKENS]}));
2697
}
2698
65
100
189
2699
# make the new variable setting available to embedded Perl code, if necessary
2700
34
34
457
if ($safeObject)
34
91
34
335777
2701
64
50
140
{
64
434
2702
no strict 'refs';
2703
${join('::', ref($safeObject) ? $safeObject->root : 'main', $_[1][0])}=$variables{$_[1][0]};
2704
}
2705
65
50
946
2706
# trace, if necessary
2707
warn "[Trace] $sourceFile, line $_[4][1]: Variable assignment: \$$_[1][0]=$variables{$_[1][0]}.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2708
65
262
2709
# flag this paragraph as internal
2710
['', $_[4][1]];
2711
}
2712
;
2713
2714
# comment
2715
comment : '/' '/'
2716
10
10
34
{
2717
# switch to comment mode
2718
_stateManager(STATE_COMMENT);
2719
10
50
153
2720
# trace, if necessary
2721
warn "[Trace] $sourceFile, line $_[1][1]: Comment starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2722
}
2723
optional_basics Empty_line
2724
10
10
31
{
2725
# back to default mode
2726
_stateManager(STATE_DEFAULT);
2727
10
50
28
2728
# trace, if necessary
2729
warn "[Trace] $sourceFile, line $_[5][1]: Comment completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2730
10
48
2731
10
72
# reply data, if necessary
2732
my %hints=(nr=>++$directiveCounter);
2733
$flags{skipcomments} ? [[()], $_[5][1]]
2734
: [
2735
[
2736
# opener directive
2737
10
50
60
[\%hints, DIRECTIVE_COMMENT, DIRECTIVE_START],
2738
# the list of enclosed literals
2739
@{$_[4][0]},
2740
# final directive
2741
[\%hints, DIRECTIVE_COMMENT, DIRECTIVE_COMPLETE]
2742
],
2743
$_[5][1]
2744
];
2745
}
2746
;
2747
2748
# stream entry point
2749
dstream_entrypoint : '~'
2750
{
2751
# no mode switch necessary
2752
33
50
33
178
2753
# trace, if necessary
2754
warn "[Trace] $sourceFile, line $_[1][1]: Stream entry point starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2755
}
2756
words Empty_line
2757
{
2758
# no mode switch necessary
2759
33
50
33
100
2760
# trace, if necessary
2761
warn "[Trace] $sourceFile, line $_[5][1]: Stream entry point completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2762
33
60
2763
# deactivate caching
2764
$flags{checksummed}=0;
2765
33
56
33
97
2766
33
100
66
233
# reply data as wished
2767
my $streamTitle=join('', @{$_[3][0]});
2768
unless (
2769
$flags{docstreaming}==DSTREAM_IGNORE
2770
or (
2771
$flags{docstreams2skip}
2772
and exists $flags{docstreams2skip}{$streamTitle}
2773
)
2774
)
2775
16
76
{
2776
# store stream title (both globally and locally)
2777
$resultStreamRef->[STREAM_DOCSTREAMS]{$streamTitle}=$flags{chapterDocstreams}{$streamTitle}=undef;
2778
16
100
48
2779
# special handling requested?
2780
if ($flags{docstreaming}==DSTREAM_HEADLINES)
2781
{
2782
8
55
# make this docstream entry point a headline
2783
# one level below the last real headline level
2784
my %hints=(nr=>++$directiveCounter, shortcut=>'');
2785
[
2786
8
96
[
2787
# opener directive (including headline level)
2788
[\%hints, DIRECTIVE_HEADLINE, DIRECTIVE_START, $flags{headlineLevel}+1],
2789
# the stream title becomes the "headline"
2790
$streamTitle,
2791
# final directive (including headline level again)
2792
[\%hints, DIRECTIVE_HEADLINE, DIRECTIVE_COMPLETE, $flags{headlineLevel}+1]
2793
],
2794
$_[5][1]
2795
];
2796
}
2797
# default handling
2798
8
25
else
2799
{
2800
my %hints=(nr=>++$directiveCounter);
2801
[
2802
8
58
[
2803
# directives
2804
[\%hints, DIRECTIVE_DSTREAM_ENTRYPOINT, DIRECTIVE_START, $streamTitle],
2805
],
2806
$_[5][1]
2807
];
2808
}
2809
}
2810
else
2811
{
2812
17
50
70
# configure parser to ignore eveything till the next stream entry point or headline
2813
# ... unless this is the *main* stream
2814
$flags{skipInput}=2 unless $streamTitle eq 'main';
2815
17
103
2816
# we have to supply something, but it should be nothing (note that this is a *paragraph*, so reply a *string*)
2817
['', $_[5][1]];
2818
}
2819
}
2820
;
2821
2822
# control paragraph: list shifts
2823
list_shift : list_shifter
2824
4
4
11
{
2825
4
11
# temporarily activate number detection
2826
push(@specialStack, $specials{number});
2827
$specials{number}=1;
2828
}
2829
optional_number
2830
4
4
10
{
2831
# restore previous number detection mode
2832
$specials{number}=pop(@specialStack);
2833
4
89
2834
# switch to control mode
2835
_stateManager(STATE_CONTROL);
2836
4
0
17
50
2837
# trace, if necessary
2838
warn "[Trace] $sourceFile, line $_[3][1]: List shift ", $_[1][0]==LIST_SHIFT_RIGHT ? 'right' : 'left', " starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2839
}
2840
Empty_line
2841
4
4
13
{
2842
# back to default mode
2843
_stateManager(STATE_DEFAULT);
2844
4
0
14
50
2845
# trace, if necessary
2846
warn "[Trace] $sourceFile, line $_[5][1]: List shift ", $_[1][0]==LIST_SHIFT_RIGHT ? 'right' : 'left', " completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
2847
4
100
12
2848
2
100
32
# update related data
2849
if ($_[1][0]==LIST_SHIFT_RIGHT)
2850
2
100
15
{unshift(@olistLevels, 0) for (1..(defined $_[3][0] ? $_[3][0] : 1));}
2851
else
2852
{shift(@olistLevels) for (1..(defined $_[3][0] ? $_[3][0] : 1));}
2853
4
9
2854
# reset ordered list flag
2855
$flags{olist}=0;
2856
2857
# reply data
2858
[
2859
4
100
33
[
100
2860
# opener directive (no explicit closing)
2861
[{}, $_[1][0]==LIST_SHIFT_RIGHT ? DIRECTIVE_LIST_RSHIFT : DIRECTIVE_LIST_LSHIFT, DIRECTIVE_START, defined $_[3][0] ? $_[3][0] : 1],
2862
],
2863
$_[5][1]
2864
];
2865
}
2866
;
2867
2868
# list shift characters
2869
list_shifter : '>'
2870
2
2
11
{
2871
# reply a flag
2872
[LIST_SHIFT_RIGHT, $_[1][1]];
2873
}
2874
| '<'
2875
2
2
10
{
2876
# reply a flag
2877
[LIST_SHIFT_LEFT, $_[1][1]];
2878
}
2879
;
2880
2881
# optional literals
2882
optional_literals : # this makes it optional
2883
14
14
124
{
2884
# start a new, empty list and reply it
2885
[[], $lineNrs{$inHandle}];
2886
}
2887
| literals # default action works perfectly
2888
;
2889
2890
# literals
2891
literals : literal # default action works
2892
| literals literal
2893
1332
1332
2040
{
1332
2737
1332
3015
2894
1332
5999
# update token list and reply it
2895
push(@{$_[1][0]}, @{$_[2][0]});
2896
[$_[1][0], $_[2][1]];
2897
}
2898
;
2899
2900
# optional literals and empty lines
2901
optional_literals_and_empty_lines : # this makes it optional
2902
0
0
0
{
2903
# start a new, empty list and reply it
2904
[[], $lineNrs{$inHandle}];
2905
}
2906
| literals_and_empty_lines # default action works perfectly
2907
;
2908
2909
# literals and empty lines
2910
literals_and_empty_lines : literal_or_empty_line # default action works
2911
| literals_and_empty_lines literal_or_empty_line
2912
1243
1243
1721
{
1243
2188
1243
26534
2913
1243
5096
# update token list and reply it
2914
push(@{$_[1][0]}, @{$_[2][0]});
2915
[$_[1][0], $_[2][1]];
2916
}
2917
;
2918
2919
# literal or empty line
2920
literal_or_empty_line : literal # default action works
2921
| Empty_line
2922
98
98
518
{
2923
# start a new token list and reply it
2924
[[$_[1][0]], $_[1][1]];
2925
}
2926
;
2927
2928
# literals are basics or EOL
2929
literal : basic # default action works
2930
| EOL
2931
270
270
1642
{
2932
# start a new token list and reply it
2933
[[$_[1][0]], $_[1][1]];
2934
}
2935
;
2936
2937
# optional basics
2938
optional_basics : # this makes it optional
2939
0
0
0
{
2940
# start a new, empty list and reply it
2941
[[], $lineNrs{$inHandle}];
2942
}
2943
| basics # default action works perfectly
2944
;
2945
2946
# basics
2947
basics : basic # default action works perfectly
2948
| basics basic
2949
235
235
353
{
235
529
235
763
2950
235
1040
# update token list and reply it
2951
push(@{$_[1][0]}, @{$_[2][0]});
2952
[$_[1][0], $_[2][1]];
2953
}
2954
;
2955
2956
2957
# basic (base element or table stuff)
2958
basic : element
2959
| table
2960
| table_separator
2961
;
2962
2963
2964
# elements
2965
elements : element # default action works perfectly
2966
| elements element
2967
1
1
2
{
1
4
1
3
2968
1
5
# update token list and reply it
2969
push(@{$_[1][0]}, @{$_[2][0]});
2970
[$_[1][0], $_[2][1]];
2971
}
2972
;
2973
2974
2975
# base element (numbers are no base element because they are usually words - numbers are detected very temporarily)
2976
element : Word
2977
2034
2034
9965
{
2978
# start a new token list and reply it
2979
[[$_[1][0]], $_[1][1]];
2980
}
2981
| Space
2982
955
955
6143
{
2983
# start a new token list and reply it
2984
[[$_[1][0]], $_[1][1]];
2985
}
2986
| Named_variable
2987
107
100
100
107
708
{
2988
# flag that this paragraph uses variables (a cache hit will only be useful if variable settings will be unchanged)
2989
$flags{checksummed}[4]=1 unless exists $flags{checksummed} and not $flags{checksummed};
2990
107
100
1009
2991
# start a new token list and reply it
2992
[[exists $variables{$_[1][0]} ? $variables{$_[1][0]} : join('', '$', $_[1][0])], $_[1][1]];
2993
}
2994
| Symbolic_variable
2995
43
100
66
43
290
{
2996
# flag that this paragraph uses variables (a cache hit will only be useful if variable settings will be unchanged)
2997
$flags{checksummed}[4]=1 unless exists $flags{checksummed} and not $flags{checksummed};
2998
43
50
271
2999
# start a new token list and reply it
3000
[[exists $variables{$_[1][0]} ? $variables{$_[1][0]} : join('', '$', "{$_[1][0]}")], $_[1][1]];
3001
}
3002
| StreamedPart
3003
{
3004
7
7
33
# start a new token list and reply it
3005
# (the passed stream is already a reference)
3006
2
2
12
[$_[1][0], $_[1][1]];
3007
}
3008
| tag
3009
| embedded
3010
| included
3011
;
3012
3013
3014
# optional number
3015
optional_number : # this makes it optional
3016
{[undef, $lineNrs{$inHandle}];}
3017
| Number
3018
;
3019
3020
3021
# words
3022
words : Word
3023
40
40
203
{
3024
# start a new token list and reply it
3025
[[$_[1][0]], $_[1][1]];
3026
}
3027
| words Word
3028
0
0
0
{
0
0
3029
0
0
# update token list and reply it
3030
push(@{$_[1][0]}, $_[2][0]);
3031
[$_[1][0], $_[2][1]];
3032
}
3033
;
3034
3035
# words or spaces
3036
words_or_spaces : word_or_space
3037
1
1
7
{
3038
# start a new token list and reply it
3039
[[$_[1][0]], $_[1][1]];
3040
}
3041
| words_or_spaces word_or_space
3042
2
2
3
{
2
8
3043
2
9
# update token list and reply it
3044
push(@{$_[1][0]}, $_[2][0]);
3045
[$_[1][0], $_[2][1]];
3046
}
3047
;
3048
3049
# word or space
3050
word_or_space : Word
3051
| Space
3052
;
3053
3054
3055
# tag
3056
tag : Tag_name
3057
112
50
112
672
{
3058
# trace, if necessary
3059
warn "[Trace] $sourceFile, line $_[1][1]: Tag $_[1][0] starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
3060
112
100
909
3061
# temporarily activate special "<" *as necessary*
3062
112
100
502
my $possible= (exists $macros{$_[1][0]} and $macros{$_[1][0]}->[2]) # macro: evaluate body flag;
3063
112
188
|| $tagsRef->{$_[1][0]}{__flags__}{__body__}; # tag with body;
3064
push(@specialStack, $specials{'<'}), $specials{'<'}=1 if $possible; # enable tag body, if necessary
3065
push(@specialStack, $possible); # flags what is on stack;
3066
11
114
3067
112
100
100
1020
# temporarily activate specials "{" and "}" *as necessary*
100
3068
push(@specialStack, @specials{('{', '}')}), @specials{('{', '}')}=(1) x 2
3069
if (exists $macros{$_[1][0]} and %{$macros{$_[1][0]}->[0]}) # macro: evaluate declared options;
3070
|| $tagsRef->{$_[1][0]}{__flags__}{__options__}; # tag with options;
3071
112
339
3072
# deactivate boost
3073
$flags{noboost}=1;
3074
}
3075
optional_tagpars
3076
112
112
234
{
3077
# reactivate boost
3078
$flags{noboost}=0;
3079
11
78
3080
112
100
100
963
# restore special states of "{" and "}", if necessary
100
3081
@specials{('{', '}')}=splice(@specialStack, -2, 2)
3082
if (exists $macros{$_[1][0]} and %{$macros{$_[1][0]}->[0]}) # macro: evaluate declared options;
3083
|| $tagsRef->{$_[1][0]}{__flags__}{__options__}; # tag with options;
3084
112
50
66
337
100
66
3085
112
1275
# check options in general if declared mandatory
3086
if (
3087
not @{$_[3][0]}
3088
and exists $tagsRef->{$_[1][0]}
3089
and exists $tagsRef->{$_[1][0]}{options}
3090
and $tagsRef->{$_[1][0]}{options}==&TAGS_MANDATORY
3091
)
3092
0
0
{
3093
# display error message
3094
warn "\n\n[Fatal] $sourceFile, line $_[3][1]: Missing mandatory options of tag $_[1][0]\n";
3095
0
0
3096
# this is an syntactical error, stop parsing
3097
$_[0]->YYAbort;
3098
}
3099
}
3100
optional_tagbody
3101
112
112
152
{
3102
# scopy
3103
my $ignore;
3104
112
50
307
3105
# trace, if necessary
3106
warn "[Trace] $sourceFile, line $_[5][1]: Tag $_[1][0] completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
3107
112
289
3108
112
100
129
# build parameter hash, if necessary
112
414
3109
my %pars;
3110
if (@{$_[3][0]})
3111
41
63
{
41
266
3112
# the list already consists of key/value pairs
3113
%pars=@{$_[3][0]}
3114
}
3115
112
50
295
3116
# Tag condition set?
3117
if (exists $pars{_cnd_})
3118
{
3119
0
0
0
# ok, if the condition was true or could not be evaluated, return just the body
0
0
3120
0
0
0
# (so that the tag or macro is ignored)
0
0
3121
unless (_evalTagCondition($pars{_cnd_}, $sourceFile, $_[5][1]))
3122
{return([[@{$_[5][0]} ? @{$_[5][0]} : ()], $_[5][1]]);}
3123
else
3124
0
0
{
3125
# strip off this special option before the tag or macro is furtherly processed
3126
delete $pars{_cnd_};
3127
}
3128
}
3129
112
100
318
3130
# tags require special handling
3131
unless (exists $macros{$_[1][0]})
3132
101
50
66
122
{
100
66
3133
101
7397
# check tag body in general if declared mandatory
3134
if (
3135
not @{$_[5][0]}
3136
and exists $tagsRef->{$_[1][0]}
3137
and exists $tagsRef->{$_[1][0]}{body}
3138
and $tagsRef->{$_[1][0]}{body}==&TAGS_MANDATORY
3139
)
3140
0
0
{
3141
# display error message
3142
warn "[Fatal] $sourceFile, line $_[5][1]: Missing mandatory body of tag $_[1][0]\n";
3143
0
0
3144
# this is an syntactical error, stop parsing
3145
$_[0]->YYAbort;
3146
}
3147
101
100
66
700
3148
# invoke hook function, if necessary
3149
if (exists $tagsRef->{$_[1][0]} and exists $tagsRef->{$_[1][0]}{hook})
3150
13
454
{
13
45
3151
# make an option hash
3152
my $options={@{$_[3][0]}};
3153
13
18
3154
13
26
# call hook function (use eval() to guard yourself)
13
327
13
105
3155
my $rc;
3156
eval {$rc=&{$tagsRef->{$_[1][0]}{hook}}($_[1][1], $options, dclone($_[5][0]), $anchors, join('-', @headlineIds), $flags{headlinenr})};
3157
13
50
96
3158
0
0
# check result
3159
unless ($@)
3160
{
3161
13
50
19
{
13
33
3162
# semantic error?
3163
++$_semerr, last if $rc==PARSING_ERROR;
3164
13
50
30
3165
# syntactical error?
3166
$_[0]->YYAbort, last if $rc==PARSING_FAILED;
3167
13
50
33
194
3168
# tag to ignore, or even everything covered?
3169
$ignore=$rc, last if $rc==PARSING_IGNORE or $rc==PARSING_ERASE;
3170
3171
13
37
# update options (might be modified, and checking for a difference
13
47
3172
# might take more time then just copying the replied values)
3173
@{$_[3][0]}=%$options;
3174
13
50
41
3175
# all right?
3176
if ($rc==PARSING_OK)
3177
13
100
50
{
3178
# is this a tag that will invoke a finish hook?
3179
if (exists $tagsRef->{$_[1][0]}{finish})
3180
6
20
{
3181
# update number of tags to finish in the currently built stream section, if necessary
3182
$pendingTags->[1]++;
3183
3184
6
14
# Disable storage of a checksum. (A finish hook makes the paragraph depending
3185
# on something potentially outside the paragraph - the paragraph becomes dynamic.)
3186
$flags{checksummed}=0;
3187
}
3188
13
22
3189
# well done
3190
last;
3191
}
3192
0
0
0
3193
# or even superb?
3194
$_[0]->YYAccept, last if $rc==PARSING_COMPLETED;
3195
0
0
3196
# something is wrong here
3197
warn "[Warn] Tags $_[1][0] tag hook replied unexpected result $rc, ignored.\n";
3198
}
3199
}
3200
else
3201
{warn "[Warn] Error in tags $_[1][0] tag hook: $@\n"}
3202
13
100
17
13
44
3203
# rebuild parameter hash, if necessary
3204
if (@{$_[3][0]})
3205
12
13
{
12
66
3206
# the list already consists of key/value pairs
3207
%pars=@{$_[3][0]}
3208
}
3209
}
3210
}
3211
112
100
485
3212
# this might be a macro as well as a tag - so what?
3213
unless (exists $macros{$_[1][0]})
3214
101
527
{
3215
# update statistics
3216
$statistics{&DIRECTIVE_TAG}++;
3217
101
50
575
0
0
3218
# reply tag data as necessary
3219
unless (defined $ignore)
3220
101
502
{
3221
# supply a complete tag
3222
my %hints=(nr=>++$directiveCounter);
3223
[
3224
101
362
[
101
317
3225
# opener directive
3226
77
196
[\%hints, DIRECTIVE_TAG, DIRECTIVE_START, $_[1][0], \%pars, scalar(@{$_[5][0]})],
101
759
3227
# the list of enclosed literals, if any
3228
101
100
259
@{$_[5][0]} ? @{$_[5][0]} : (),
3229
# final directive
3230
[\%hints, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, $_[1][0], \%pars, scalar(@{$_[5][0]})]
3231
],
3232
$_[5][1]
3233
];
3234
}
3235
elsif ($ignore==PARSING_IGNORE)
3236
{
3237
# supply the body, ignore the tag "envelope" ("hide" the tag)
3238
[
3239
0
0
0
[
0
0
0
0
3240
# the list of enclosed literals, if any
3241
@{$_[5][0]} ? @{$_[5][0]} : (),
3242
],
3243
$_[5][1]
3244
];
3245
0
0
}
3246
elsif ($ignore==PARSING_ERASE)
3247
0
0
{
3248
# reply nothing real
3249
[[()], $_[5][1]];
3250
}
3251
else
3252
{die "[BUG] Unhandled flag $ignore.";}
3253
}
3254
else
3255
11
100
100
78
{
3256
# flag that this paragraph uses macros (a cache hit will only be useful if macro definitions will have been unchanged)
3257
$flags{checksummed}[3]=1 unless exists $flags{checksummed} and not $flags{checksummed};
3258
11
33
3259
# this is a macro - resolve it!
3260
my $macro=$macros{$_[1][0]}->[1];
3261
11
20
11
46
3262
# fill in parameters
3263
5
50
23
foreach my $par (keys %{$macros{$_[1][0]}->[0]})
100
3264
{
3265
my $value= exists $pars{$par} ? $pars{$par}
3266
5
85
: defined $macros{$_[1][0]}->[0]{$par} ? $macros{$_[1][0]}->[0]{$par}
3267
: '';
3268
$macro=~s/__${par}__/$value/g;
3269
}
3270
3271
# Bodyless macros need special care - the parser already got the subsequent token to
3272
# recognize that the macro was complete. Now, the macro replacement is reinserted into
3273
# the stream where it will be read by the next lexer operation which is enforced when
3274
# the parser needs a token again - and this will happen after processing the already
3275
# received token which stood behind the bodyless macro. Letting the parser process the
3276
# read token this way, this token would be streamed (in most cases) *before* the macro
3277
# replacement, while it was intented to come after it. So, if we detect this case, we
3278
# move this token *behind* the macro replacement. As for the parser, we replace
3279
11
20
# this token by something streamed to "nothing", currently a special string declared
3280
11
100
14
# as "Word" token.
11
34
3281
my $delayedToken;
3282
unless (@{$_[5][0]})
3283
4
34
{
3284
# insert the current token behind the imaginary body
3285
$delayedToken=new PerlPoint::Parser::DelayedToken($_[0]->YYCurtok, $_[0]->YYCurval);
3286
3287
4
19
# set new dummy values to let the parser work on
3288
4
17
# (something without effect and valid everywhere a tag is)
3289
$_[0]->YYCurtok('Word');
3290
$_[0]->YYCurval([DUMMY_TOKEN, $_[0]->YYCurval->[1]]);
3291
}
3292
11
100
71
25
100
326
3293
# finally, pass the constructed text back to the input stream (by stack)
3294
_stackInput($_[0], (map {$_ eq '__body__' ? dclone($_[5][0]) : split(/(\n)/, $_)} split(/(__body__)/, $macro)), $delayedToken ? $delayedToken : ());
3295
11
100
33
3296
# reset the "end of input reached" flag if necessary
3297
$readCompletely=0 if $readCompletely;
3298
11
54
3299
# reply nothing real
3300
[[()], $_[5][1]];
3301
77
77
378
}
3302
}
3303
;
3304
3305
# optional tag parameters
3306
optional_tagpars : # this makes it optional
3307
{[[], $lineNrs{$inHandle}];}
3308
| used_tagpars
3309
;
3310
3311
3312
used_tagpars : '{' tagpars '}'
3313
98
98
438
{
3314
# supply the parameters
3315
[$_[2][0], $_[3][1]];
3316
}
3317
;
3318
3319
# tag parameters
3320
tagpars : tagpar
3321
| tagpars Space tagpar
3322
53
53
97
{
53
442
53
187
3323
# update parameter list
3324
push(@{$_[1][0]}, @{$_[3][0]});
3325
53
219
3326
# supply updated parameter list
3327
[$_[1][0], $_[3][1]];
3328
}
3329
;
3330
3331
# tag parameter
3332
tagpar : Word
3333
151
151
329
{
3334
151
326
# backslashes should pass in tag options
3335
push(@specialStack, $lexerFlags{backsl});
3336
$lexerFlags{backsl}=LEXER_TOKEN;
3337
3338
151
1475
# temporarily make "=" and quotes the only specials,
3339
151
2059
# but take care to reset the remaining settings defined
3340
151
982
push(@specialStack, [(%specials)], $specials{'='});
3341
@specials{keys %specials}=(0) x scalar(keys %specials);
3342
@specials{('=', '"')}=(1, 1);
3343
}
3344
'='
3345
151
151
575
{
3346
# restore special "=" setting
3347
$specials{'='}=pop(@specialStack);
3348
}
3349
tagvalue
3350
151
151
224
{
151
2582
3351
# restore special settings
3352
%specials=@{pop(@specialStack)};
3353
151
803
3354
# restore backslash flag
3355
$lexerFlags{backsl}=pop(@specialStack);
3356
151
1571
3357
# supply flag and value
3358
[[$_[1][0], $_[5][0]], $_[5][1]];
3359
}
3360
;
3361
3362
tagvalue : Word
3363
| '"' basics '"'
3364
39
39
220
{
39
346
3365
# build a string and supply it
3366
[join('', @{$_[2][0]}), $_[3][1]];
3367
}
3368
;
3369
3370
# optional tag body
3371
optional_tagbody : # this makes it optional
3372
{
3373
28
28
170
# if we are here, "<" *possibly* was marked to be a special - now it becomes what is was before
3374
28
100
87
# (take care the stack is filled correctly!)
3375
my $possible=pop(@specialStack); # was the body enabled?
3376
$specials{'<'}=pop(@specialStack) if $possible; # if so, restore the stack
3377
28
116
3378
# supply an empty result
3379
[[], $lineNrs{$inHandle}];
3380
}
3381
| '<'
3382
{
3383
84
84
169
# if we are here, "<" was marked to be a special - now it becomes what is was before
3384
84
199
# (take care the stack is filled correctly!)
3385
my $possible=pop(@specialStack); # can be ignored - surely the body was enabled!
3386
$specials{'<'}=pop(@specialStack); # restore the stack
3387
84
240
3388
84
263
# temporarily activate special ">"
3389
push(@specialStack, @specials{('>')});
3390
@specials{('>')}=1;
3391
}
3392
literals '>'
3393
84
84
196
{
3394
# reset ">" setting
3395
@specials{('>')}=pop(@specialStack);
3396
84
384
3397
# reply the literals
3398
[$_[3][0], $_[4][1]];
3399
}
3400
;
3401
3402
3403
table : Table
3404
11
50
11
54
{
3405
# trace, if necessary
3406
warn "[Trace] $sourceFile, line $_[1][1]: Table starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
3407
11
50
66
67
3408
# check nesting
3409
_semerr($_[0], "$sourceFile, line $_[3][1]: Nested tables are not supported by this parser version.")
3410
if @tableSeparatorStack and not $flags{nestedTables};
3411
11
35
3412
11
33
# temporarily activate specials "{" and "}"
3413
push(@specialStack, @specials{('{', '}')});
3414
@specials{('{', '}')}=(1, 1);
3415
11
19
3416
11
153
# empty lines have to be ignored in tables
3417
push(@specialStack, $lexerFlags{el});
3418
$lexerFlags{el}=LEXER_IGNORE;
3419
11
46
3420
# deactivate boost
3421
$flags{noboost}=1;
3422
}
3423
used_tagpars
3424
11
11
35
{
3425
# reactivate boost
3426
$flags{noboost}=0;
3427
11
32
3428
# restore previous handling of empty lines
3429
$lexerFlags{el}=pop(@specialStack);
3430
11
43
3431
# restore special state of "{" and "}"
3432
@specials{('{', '}')}=splice(@specialStack, -2, 2);
3433
11
24
11
72
3434
# read parameters and adapt them, if necessary
3435
11
100
47
my %tagpars=@{$_[3][0]};
3436
3437
4
193
if (exists $tagpars{rowseparator})
3438
4
50
16
{
3439
$tagpars{rowseparator}=quotemeta($tagpars{rowseparator});
3440
$tagpars{rowseparator}="\n" if $tagpars{rowseparator} eq '\\\\n';
3441
}
3442
11
100
66
100
50
3443
# mark table start
3444
$tableColumns=0-(
3445
exists $tagpars{gracecr} ? $tagpars{gracecr}
3446
: (not exists $tagpars{rowseparator} or $tagpars{rowseparator} eq "\n") ? 1
3447
: 0
3448
);
3449
11
100
155
100
3450
# store specified column separator (or default)
3451
unshift(@tableSeparatorStack, [
3452
exists $tagpars{separator} ? quotemeta($tagpars{separator}) : '\|',
3453
exists $tagpars{rowseparator} ? $tagpars{rowseparator} : "\n",
3454
]);
3455
}
3456
optional_literals_and_empty_lines Tabled
3457
11
11
23
{
3458
11
50
19
# build parameter hash, if necessary
11
122
3459
my %pars;
3460
if (@{$_[3][0]})
3461
11
17
{
11
87
3462
# the list already consists of key/value pairs
3463
%pars=@{$_[3][0]}
3464
}
3465
11
50
49
3466
# Tag condition set?
3467
if (exists $pars{_cnd_})
3468
{
3469
0
0
0
# ok, if the condition was true or could not be evaluated,
0
0
3470
# stop processing of this tag (there is no body, so return an empty stream)
3471
unless (_evalTagCondition($pars{_cnd_}, $sourceFile, $_[6][1]))
3472
{return([[()], $_[6][1]]);}
3473
else
3474
0
0
{
3475
# strip off this special option before the tag or macro is furtherly processed
3476
delete $pars{_cnd_};
3477
}
3478
}
3479
11
100
59
3480
11
100
46
# add row separator information unless it was defined by the user itself
3481
$pars{rowseparator}='\n' unless exists $pars{rowseparator};
3482
$pars{rowseparator}='\\\\n' if $pars{rowseparator} eq '\\n';
3483
11
33
3484
# store nesting level information
3485
$pars{__nestingLevel__}=@tableSeparatorStack;
3486
3487
# If we are here and found anything in the table, it is
3488
# possible that a final row was closed and a new one opened
3489
# (e.g. at the end of the last table line, if rows are separated
3490
# by "\n"). Because the table is completed now, these tags can
3491
6
34
# be removed to get the common case of an opened but not yet
11
123
6
146
3492
# completed table cell.
3493
11
50
66
96
splice(@{$_[5][0]}, -4, 4) if @{$_[5][0]}
66
66
33
33
3494
and ref($_[5][0][-1]) eq 'ARRAY'
3495
and @{$_[5][0][-1]}==4
3496
and $_[5][0][-1][STREAM_DIR_TYPE] eq DIRECTIVE_TAG
3497
and $_[5][0][-1][STREAM_DIR_STATE] eq DIRECTIVE_START
3498
and $_[5][0][-1][STREAM_DIR_DATA] eq 'TABLE_COL';
3499
11
72
3500
# normalize table rows (no need of auto format)
3501
($pars{__titleColumns__}, $pars{__maxColumns__})=_normalizeTableRows($_[5][0], 0);
3502
11
50
33
65
3503
# warn user in case of potential row width conflicts
3504
warn qq([Warn] $sourceFile, line $_[1][1]: The maximum cell number per row ($pars{__maxColumns__}) was not detected in the first row (which has $pars{__titleColumns__} columns).\n) if $pars{__titleColumns__}<$pars{__maxColumns__} and not ($flags{display} & DISPLAY_NOWARN);
3505
11
18
3506
11
31
# reset column separator memory, mark table completed
3507
shift(@tableSeparatorStack);
3508
$tableColumns=0;
3509
11
72
3510
# reply data in a "tag envelope" (for backends)
3511
my ($hints1, $hints2, $hints3)=({nr=>++$directiveCounter}, {nr=>++$directiveCounter}, {nr=>++$directiveCounter});
3512
[
3513
11
42
[
3514
# opener directives
3515
[$hints1, DIRECTIVE_TAG, DIRECTIVE_START, 'TABLE', \%pars],
3516
[$hints2, DIRECTIVE_TAG, DIRECTIVE_START, 'TABLE_ROW'],
3517
11
50
87
[$hints3, DIRECTIVE_TAG, DIRECTIVE_START, 'TABLE_COL'],
11
362
3518
# the list of enclosed literals reduced by the final two, if any
3519
@{$_[5][0]} ? @{$_[5][0]} : (),
3520
# final directive
3521
[$hints3, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, 'TABLE_COL'],
3522
[$hints2, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, 'TABLE_ROW'],
3523
[$hints1, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, 'TABLE', \%pars]
3524
],
3525
$_[6][1]
3526
];
3527
}
3528
;
3529
3530
# table separator: this is a simple transformation rule
3531
table_separator : Table_separator
3532
134
134
248
{
3533
# update counter of completed table columns
3534
$tableColumns++;
3535
134
424
3536
# supply a simple seperator tag
3537
my %hints=(nr=>++$directiveCounter);
3538
134
100
13223
[
3539
[
3540
[\%hints, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, 'TABLE_COL'],
3541
$_[1][0] eq 'c' ? ()
3542
: (
3543
[{}, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, 'TABLE_ROW'],
3544
[{}, DIRECTIVE_TAG, DIRECTIVE_START, 'TABLE_ROW'],
3545
),
3546
[\%hints, DIRECTIVE_TAG, DIRECTIVE_START, 'TABLE_COL'],
3547
],
3548
$_[1][1]
3549
];
3550
}
3551
;
3552
3553
3554
table_paragraph : '@'
3555
7
7
34
{
3556
# switch to condition mode
3557
_stateManager(STATE_TABLE);
3558
7
50
38
3559
# trace, if necessary
3560
warn "[Trace] $sourceFile, line $_[1][1]: Table paragraph starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
3561
}
3562
words EOL
3563
7
7
16
{
7
53
3564
# store specified column separator
3565
unshift(@tableSeparatorStack, [quotemeta(join('', @{$_[3][0]})), "\n"]);
3566
}
3567
optional_literals Empty_line
3568
7
7
39
{
3569
# back to default mode
3570
_stateManager(STATE_DEFAULT);
3571
7
50
35
3572
# trace, if necessary
3573
warn "[Trace] $sourceFile, line $_[7][1]: Table paragraph completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
3574
7
13
3575
7
94
# reset column separator memory, mark table completed
3576
shift(@tableSeparatorStack);
3577
$tableColumns=0;
3578
3579
7
58
# build parameter hash (contains level information, which is always 1,
3580
# and various retranslation hints)
3581
my %pars=(
3582
7
20
__nestingLevel__ => 1,
3583
__paragraph__ => 1,
3584
separator => join('', @{$_[3][0]}),
3585
);
3586
3587
# If we are here and found anything in the table, a final row was
3588
# closed and a new one opened at the end of the last table line.
3589
7
50
14
# Because the table is completed now, the final opener tags can
7
38
3590
# be removed. This is done *here* and by pop() for acceleration.
3591
if (@{$_[6][0]}>4)
3592
7
15
{
7
29
3593
# delete final opener directives made by the final carriage return
3594
splice(@{$_[6][0]}, -2, 2);
3595
7
561
3596
# normalize table rows and autoformat headline fields
3597
($pars{__titleColumns__}, $pars{__maxColumns__})=_normalizeTableRows($_[6][0], 1);
3598
7
50
33
44
3599
# warn user in case of potential row width conflicts
3600
warn qq([Warn] $sourceFile, line $_[1][1]: The maximum cell number per row ($pars{__maxColumns__}) was not detected in the first row (which has $pars{__titleColumns__} columns).\n) if $pars{__titleColumns__}<$pars{__maxColumns__} and not ($flags{display} & DISPLAY_NOWARN);
3601
7
94
3602
# reply data in a "tag envelope" (for backends)
3603
my %hints=(nr=>++$directiveCounter);
3604
[
3605
7
38
[
3606
# opener directives (note that first row and column are already opened by the initial carriage return stream)
3607
[\%hints, DIRECTIVE_TAG, DIRECTIVE_START, 'TABLE', \%pars],
3608
[{}, DIRECTIVE_TAG, DIRECTIVE_START, 'TABLE_ROW'],
3609
7
50
89
[{}, DIRECTIVE_TAG, DIRECTIVE_START, 'TABLE_HL'],
7
88
3610
# the list of enclosed literals reduced by the final two, if any
3611
@{$_[6][0]} ? @{$_[6][0]} : (),
3612
# final directive
3613
[\%hints, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, 'TABLE', \%pars]
3614
],
3615
$_[7][1]
3616
];
3617
}
3618
else
3619
0
0
{
3620
# empty table - reply nothing real
3621
[[()], $_[7][1]];
3622
}
3623
}
3624
;
3625
3626
3627
embedded : Embed
3628
28
28
64
{
3629
28
410
# switch to embedding mode saving the former state (including *all* special settings)
3630
28
144
push(@stateStack, $parserState);
3631
push(@specialStack, [%specials]);
3632
_stateManager(STATE_EMBEDDING);
3633
28
50
136
3634
# trace, if necessary
3635
warn "[Trace] $sourceFile, line $_[1][1]: Embedding starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
3636
3637
28
62
# Disable storage of a checksum. (Dynamic parts may change or have changed.
3638
# Static parts are static of course, but the filter settings may vary.)
3639
$flags{checksummed}=0;
3640
28
70
3641
28
64
# temporarily activate specials "{" and "}"
3642
push(@specialStack, @specials{('{', '}')});
3643
@specials{('{', '}')}=(1, 1);
3644
28
92
3645
# deactivate boost
3646
$flags{noboost}=1;
3647
}
3648
used_tagpars
3649
28
28
73
{
3650
# reactivate boost
3651
$flags{noboost}=0;
3652
28
128
3653
# restore special state of "{" and "}"
3654
@specials{('{', '}')}=splice(@specialStack, -2, 2);
3655
}
3656
optional_literals_and_empty_lines Embedded
3657
28
28
124
{
3658
28
57
# restore former parser state (including *all* special settings)
28
388
3659
_stateManager(pop(@stateStack));
3660
%specials=@{pop(@specialStack)};
3661
28
151
3662
28
50
59
# build parameter hash, if necessary
28
200
3663
my %pars;
3664
if (@{$_[3][0]})
3665
28
52
{
28
191
3666
# the list already consists of key/value pairs
3667
%pars=@{$_[3][0]}
3668
}
3669
28
50
117
3670
# set default language, if necessary
3671
$pars{lang}='pp' unless exists $pars{lang};
3672
28
50
150
3673
# Tag condition set?
3674
if (exists $pars{_cnd_})
3675
{
3676
0
0
0
# ok, if the condition was true or could not be evaluated,
0
0
3677
# stop processing of this tag (there is no body, so return an empty stream)
3678
unless (_evalTagCondition($pars{_cnd_}, $sourceFile, $_[6][1]))
3679
{return([[()], $_[6][1]]);}
3680
else
3681
0
0
{
3682
# strip off this special option before the tag or macro is furtherly processed
3683
delete $pars{_cnd_};
3684
}
3685
}
3686
28
66
453
3687
# did the user exclude files of the language the embedded source is written in?
3688
my $langExcluded=not (
3689
not $flags{filter} # no general language filter is defined, so all languages are allowed, or
3690
or lc($pars{lang}) eq 'pp' # this is a PerlPoint file, or
3691
or (
3692
exists $pars{lang} # there is a general language filter,
3693
and $pars{lang}=~/^$flags{filter}$/i # and it allows to include files of this language
3694
)
3695
);
3696
28
211
3697
# set import filter as necessary
3698
my $filterSet=_setImportFilter($_[0], \%pars);
3699
28
100
100
213
3700
# Input filter to call?
3701
if (
3702
not $langExcluded # files in the language of the embedded one are not excluded in general
3703
and exists $pars{ifilter} # and there is an import filter
3704
)
3705
1
50
28
{
3706
# Can we invoke the filter code?
3707
unless ($safeObject)
3708
{
3709
# What a pity - but probably the unfiltered source is not what the backend
3710
0
0
# expects, so we need to ignore the embedded code completely for now. As
3711
# usually, this is done without warning.
3712
return([[()], $_[6][1]]);
3713
}
3714
else
3715
{
3716
# OK, we can try to invoke the filter.
3717
1
50
11
3718
# inform user
3719
warn qq([Warn] $sourceFile, line $_[1][1]: Running input filter.\n) if $flags{trace} & TRACE_ACTIVE;
3720
1
50
11
3721
# update active contents base data, if necessary
3722
34
34
481
if ($flags{activeBaseData})
34
102
34
3222
3723
0
0
0
{
0
0
3724
no strict 'refs';
3725
${join('::', ref($safeObject) ? $safeObject->root : 'main', 'PerlPoint')}=dclone($flags{activeBaseData});
3726
}
3727
3728
# We provide the text in a special variable @main::_ifilterText,
3729
# and the target type in a special variable $main::_ifilterType,
3730
34
34
217
# as well as the filename in a special var. $main::_ifilterFile.
34
74
34
16397
1
5
3731
1
50
3
{
1
15
1
4
3732
1
50
29
no strict 'refs';
1
9
3733
1
50
10
@{join('::', ref($safeObject) ? $safeObject->root : 'main', '_ifilterText')}=@{$_[5][0]};
1
6
3734
${join('::', ref($safeObject) ? $safeObject->root : 'main', '_ifilterType')}=$pars{lang};
3735
${join('::', ref($safeObject) ? $safeObject->root : 'main', '_ifilterFile')}=$sourceFile;
3736
}
3737
1
50
28
3738
# run the filter and catch what it supplies
3739
$_[5][0]=[ref($safeObject) ? $safeObject->reval($pars{ifilter}) : eval(join(' ', '{package main; no strict;', $pars{ifilter}, '}'))];
3740
1
50
897
3741
# check result
3742
if ($@)
3743
0
0
{
3744
# inform user, if necessary
3745
_semerr($_[0], qq($sourceFile, line $_[1][1]: input filter failed: $@.));
3746
0
0
3747
# ignore this part
3748
return([[()], $_[6][1]]);
3749
}
3750
}
3751
}
3752
28
100
66
465
100
100
3753
# check if we have to stream this code
3754
if (not defined($filterSet) or $langExcluded)
3755
{
3756
2
12
# filter error occured, or the caller wants to skip the embedded code:
3757
# we have to supply something, but it should be nothing
3758
[[()], $_[6][1]];
3759
}
3760
elsif (lc($pars{lang}) eq 'pp')
3761
7
19
{
7
69
3762
# embedded PerlPoint - pass it back to the parser (by stack)
3763
_stackInput($_[0], split(/(\n)/, join('', @{$_[5][0]})));
3764
7
50
20
3765
# reset the "end of input reached" flag if necessary
3766
$readCompletely=0 if $readCompletely;
3767
7
34
3768
# we have to supply something, but it should be nothing
3769
[[()], $_[6][1]];
3770
}
3771
elsif (lc($pars{lang}) eq 'perl')
3772
{
3773
14
50
33
26
# This is embedded Perl code, anything passed really?
14
328
3774
# And does the caller want to evaluate the code?
3775
if (@{$_[5][0]} and $safeObject)
3776
14
50
59
{
3777
# update active contents base data, if necessary
3778
34
34
219
if ($flags{activeBaseData})
34
82
34
71523
3779
0
0
0
{
0
0
3780
no strict 'refs';
3781
${join('::', ref($safeObject) ? $safeObject->root : 'main', 'PerlPoint')}=dclone($flags{activeBaseData});
3782
}
3783
14
30
14
192
3784
14
50
65
# make the code a string and evaluate it
3785
my $perl=join('', @{$_[5][0]});
3786
warn "[Trace] $sourceFile, line $_[6][1]: Evaluating this code:\n\n$perl\n\n\n" if $flags{trace} & TRACE_ACTIVE;
3787
14
50
142
3788
# ignore empty code
3789
if ($perl=~/\S/)
3790
14
100
1
567
{
1
1
23
1
9
1
56
1
15
1
2
1
45
3791
# well, there is something, evaluate it
3792
my $result=ref($safeObject) ? $safeObject->reval($perl) : eval(join(' ', '{package main; no strict;', $perl, '}'));
3793
14
50
10999
0
0
3794
# check result
3795
if ($@)
3796
{_semerr($_[0], "$sourceFile, line $_[6][1]: embedded Perl code could not be evaluated: $@.");}
3797
else
3798
14
100
75
{
3799
# success - make the result part of the input stream, if any
3800
_stackInput($_[0], split(/(\n)/, $result)) if defined $result;
3801
}
3802
14
50
98
3803
# reset the "end of input reached" flag if necessary
3804
$readCompletely=0 if $readCompletely;
3805
}
3806
}
3807
14
94
3808
# we have to supply something, but it should be nothing
3809
[[()], $_[6][1]];
3810
}
3811
else
3812
5
24
{
3813
# reply data in a "tag envelope" (for backends)
3814
my %hints=(nr=>++$directiveCounter);
3815
[
3816
5
21
[
3817
# opener directive
3818
5
50
32
[\%hints, DIRECTIVE_TAG, DIRECTIVE_START, 'EMBED', \%pars],
5
64
3819
# the list of enclosed literals, if any
3820
@{$_[5][0]} ? @{$_[5][0]} : (),
3821
# final directive
3822
[\%hints, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, 'EMBED', \%pars]
3823
],
3824
$_[6][1]
3825
];
3826
}
3827
}
3828
;
3829
3830
3831
included : Include
3832
17
50
17
100
{
3833
# trace, if necessary
3834
warn "[Trace] $sourceFile, line $_[1][1]: Inclusion starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
3835
3836
# Disable storage of a checksum. (Files may change or have changed. Later on,
3837
# we could try to keep a files modification date unless it is a nested PerlPoint
3838
17
57
# source or a dynamic Perl part. For now, it seems to be sufficient that each file
3839
# is cached itself.)
3840
$flags{checksummed}=0;
3841
17
145
3842
17
50
# temporarily activate specials "{" and "}"
3843
push(@specialStack, @specials{('{', '}')});
3844
@specials{('{', '}')}=(1, 1);
3845
17
56
3846
# deactivate boost
3847
$flags{noboost}=1;
3848
}
3849
used_tagpars
3850
17
17
42
{
3851
# scopies
3852
my ($errors, $originalPath);
3853
17
50
3854
# reactivate boost
3855
$flags{noboost}=0;
3856
17
73
3857
# restore special state of "{" and "}"
3858
@specials{('{', '}')}=splice(@specialStack, -2, 2);
3859
17
36
17
173
3860
17
50
110
# check parameters: type and filename should be set at least
3861
my %tagpars=@{$_[3][0]};
3862
$errors++, _semerr($_[0], "$sourceFile, line $_[3][1]: You forgot to specify the name of your included file.") unless exists $tagpars{file};
3863
17
50
80
3864
# set default type, if necessary
3865
$tagpars{type}='pp' unless exists $tagpars{type};
3866
17
50
68
3867
# Tag condition set?
3868
if (exists $tagpars{_cnd_})
3869
{
3870
0
0
0
# ok, if the condition was true or could not be evaluated,
0
0
3871
# stop processing of this tag (there is no body, so return an empty stream)
3872
unless (_evalTagCondition($tagpars{_cnd_}, $sourceFile, $_[3][1]))
3873
{return([[()], $_[3][1]]);}
3874
else
3875
0
0
{
3876
# strip off this special option before the tag or macro is furtherly processed
3877
delete $tagpars{_cnd_};
3878
}
3879
}
3880
17
100
617
3881
# search specified directories for the file if necessary
3882
unless (-e $tagpars{file})
3883
2
11
{
3884
# pathes are stored in an already prepared array @libraryPath
3885
3
12
foreach my $path (@libraryPath)
3886
3
50
13
{
3887
3
100
74
my $newname="$path/$tagpars{file}";
3888
warn "[Trace] $sourceFile, line $_[3][1]: Trying include file name $newname for $tagpars{file}.\n" if $flags{trace} & TRACE_SEMANTIC;
3889
$tagpars{file}=$newname, last if -e $newname;
3890
}
3891
}
3892
17
50
1899
3893
# expand filename to avoid trouble by various names for the same file
3894
$tagpars{file}=catfile(abs_path(dirname($originalPath=$tagpars{file})), basename($tagpars{file}))
3895
or $errors++, semmerr("$sourceFile, line $_[3][1]: File name $tagpars{file} cannot be resolved.\n");
3896
17
0
66
711
33
33
3897
# smart inclusion?
3898
my $smart=1 if $tagpars{type}=~/^pp$/
3899
and exists $tagpars{smart} and $tagpars{smart}
3900
and exists $openedSourcefiles{$tagpars{file}};
3901
17
50
66
294
66
3902
# avoid circular source inclusion
3903
$errors++, _semerr($_[0], "$sourceFile, line $_[3][1]: Source file $originalPath was already opened before (full path: $tagpars{file}).") if $tagpars{type}=~/^pp$/
3904
and not $smart
3905
and grep($_ eq $tagpars{file}, @nestedSourcefiles);
3906
3907
17
50
100
438
100
66
3908
17
100
100
97
# PerlPoint headline offsets have to be positive numbers or certain strings
3909
17
50
66
93
$errors++, _semerr($_[0], "$sourceFile, line $_[3][1]: Invalid headline level offset $tagpars{headlinebase}, positive number or keywords BASE_LEVEL/CURRENT_LEVEL expected.") if $tagpars{type}=~/^pp$/i and exists $tagpars{headlinebase} and $tagpars{headlinebase}!~/^\d+$/ and $tagpars{headlinebase}!~/^(base|current)_level$/i;
3910
$tagpars{headlinebase}=$flags{headlineLevel} if exists $tagpars{headlinebase} and $tagpars{headlinebase}=~/^current_level$/i;
3911
$tagpars{headlinebase}=$flags{headlineLevel}-1 if exists $tagpars{headlinebase} and $tagpars{headlinebase}=~/^base_level$/i;
3912
17
50
33
159
3913
# all right?
3914
unless (defined $smart or defined $errors)
3915
17
50
682
{
3916
# check the filename
3917
if (-r $tagpars{file})
3918
{
3919
# store the files name and directory for later reference
3920
# (we could refer do that later, but using intermediate buffers
3921
17
52
# allows to keep the original values when switching the file in
3922
# background which happens with input filters)
3923
my $orgname=$tagpars{file};
3924
17
66
294
3925
# did the user exclude files of the language the embedded source is written in?
3926
my $typeExcluded=not (
3927
not $flags{filter} # no general language filter is defined, so all languages are allowed, or
3928
or lc($tagpars{type}) eq 'pp' # this is a PerlPoint file, or
3929
or (
3930
exists $tagpars{type} # there is a general language filter,
3931
and $tagpars{type}=~/^$flags{filter}$/i # and it allows to include files of this language
3932
)
3933
);
3934
17
0
33
91
33
3935
# try to set a set default import filter, if necessary
3936
if (exists $tagpars{import} and $tagpars{import} and $tagpars{import}!~/\D/)
3937
0
0
{
3938
0
0
# import format not set explicitly, scan file name for extension
3939
$tagpars{file}=~/\.(\w+)$/;
3940
$tagpars{import}=$1;
3941
0
0
0
3942
# success?
3943
delete $tagpars{import}, _semerr($_[0], qq($sourceFile, line $_[3][1]: could not determine import filter via file extension.)) unless $1;
3944
}
3945
17
214
3946
# arrange import as necessary
3947
my $filterSet=_setImportFilter($_[0], \%tagpars);
3948
17
100
100
160
3949
# Import filter to call?
3950
if (not $typeExcluded and exists $tagpars{ifilter})
3951
1
50
5
{
3952
# Can we invoke the filter code?
3953
unless ($safeObject)
3954
{
3955
# What a pity - but probably the unfiltered source is not what the backend
3956
0
0
# expects, so we need to ignore the embedded code completely for now. As
3957
# usually, this is done without warning.
3958
return([[()], $_[3][1]]);
3959
}
3960
else
3961
{
3962
# OK, we can try to invoke the filter.
3963
1
50
209
3964
# inform user
3965
warn qq([Warn] $sourceFile, line $_[1][1]: Running input filter.\n) if $flags{trace} & TRACE_ACTIVE;
3966
1
50
16
3967
# update active contents base data, if necessary
3968
34
34
280
if ($flags{activeBaseData})
34
89
34
5271
3969
0
0
0
{
0
0
3970
no strict 'refs';
3971
${join('::', ref($safeObject) ? $safeObject->root : 'main', 'PerlPoint')}=dclone($flags{activeBaseData});
3972
}
3973
1
64
3974
1
5
# open original file
3975
open(my $orgHandle, $tagpars{file});
3976
binmode($orgHandle);
3977
3978
# We provide the text in a special variable @main::_ifilterText,
3979
# and the target type in a special variable $main::_ifilterType,
3980
34
34
205
# as well as the filename in a special var. $main::_ifilterFile.
34
285
34
36305
1
2
3981
1
50
23
{
1
11
3982
1
50
20
no strict 'refs';
1
6
3983
1
50
14
@{join('::', ref($safeObject) ? $safeObject->root : 'main', '_ifilterText')}=<$orgHandle>;
1
6
3984
${join('::', ref($safeObject) ? $safeObject->root : 'main', '_ifilterType')}=$tagpars{type};
3985
${join('::', ref($safeObject) ? $safeObject->root : 'main', '_ifilterFile')}=$tagpars{file};
3986
}
3987
1
37
3988
# close original file
3989
close($orgHandle);
3990
1
13656
3991
1
502
# run the filter in the files directory and catch what it supplies
3992
my $startDir=cwd();
3993
1
50
525
chdir(dirname($orgname));
3994
1
2417
3995
my @ifiltered=ref($safeObject) ? $safeObject->reval($tagpars{ifilter}) : eval(join(' ', '{package main; no strict;', $tagpars{ifilter}, '}'));
3996
chdir($startDir);
3997
1
50
34
3998
# check result
3999
if ($@)
4000
0
0
{
4001
# inform user, if necessary
4002
_semerr($_[0], qq($sourceFile, line $_[1][1]: input filter failed: $@.));
4003
0
0
4004
# ignore this part
4005
return([[()], $_[3][1]]);
4006
}
4007
1
50
30
4008
1
1762
# ok, now "replace" the original file by a temporary one
4009
my ($tmpHandle, $tmpFilename)=tempfile(UNLINK => ($flags{trace} & TRACE_TMPFILES ? 0 : 1));
4010
1
10
$tagpars{file}=$tmpFilename;
4011
1
50
4012
print $tmpHandle @ifiltered;
4013
close($tmpHandle);
4014
}
4015
}
4016
17
50
66
349
100
100
100
4017
# check for errors
4018
if (not defined $filterSet)
4019
0
0
{
4020
# we have to supply something - but it should be nothing
4021
[[()], $_[3][1]];
4022
}
4023
# check specified file type
4024
elsif ($tagpars{type}=~/^pp$/i)
4025
11
50
{
4026
# update nesting stack
4027
push(@nestedSourcefiles, $orgname);
4028
11
109
4029
# update source file nesting level hint
4030
_predeclareVariables({_SOURCE_LEVEL=>scalar(@nestedSourcefiles)});
4031
11
50
4032
11
100
60
# build a hash of variables to "localize"
4033
my ($localizedVars, $localizeAll)=({}, 0);
4034
if (exists $tagpars{localize})
4035
2
100
12
{
4036
# special setting?
4037
if ($tagpars{localize}=~/^\s*__ALL__\s*$/)
4038
1
39
{
4039
1
3
# store a copy of all existing variables
4040
$localizedVars=dclone(\%variables);
4041
$localizeAll=1;
4042
}
4043
else
4044
1
12
{
2
7
4045
# store values of all variables to localize (passed by a comma separated list)
4046
$localizedVars={map {$_=>$variables{$_}} split(/\s*,\s*/, $tagpars{localize})};
4047
}
4048
2
100
10
4049
# the source level variable needs to be corrected
4050
$localizedVars->{_SOURCE_LEVEL}-- if exists $localizedVars->{_SOURCE_LEVEL};
4051
}
4052
4053
# we include a PerlPoint document, switch input handle
4054
11
92395
# (we intermediately have to close the original handle because of perl5.6.0 bugs)
4055
unshift(
4056
@inHandles, [
4057
tell($inHandle),
4058
$_[0]->{USER}->{INPUT},
4059
basename($sourceFile),
4060
$lineNrs{$inHandle},
4061
@flags{qw(headlineLevelOffset headlineLevel)},
4062
cwd(),
4063
$localizedVars, $localizeAll,
4064
11
496
]
4065
11
847
);
4066
11
114
close($inHandle);
4067
11
161
open($inHandle, $tagpars{file});
4068
11
119
binmode($inHandle);
4069
11
96
$_[0]->{USER}->{INPUT}='';
4070
$sourceFile=$tagpars{file};
4071
$lineNrs{$inHandle}=0;
4072
11
2389
4073
# change directory with file
4074
chdir(dirname($orgname));
4075
11
104
4076
# open a new input stack
4077
unshift(@inputStack, []);
4078
11
100
208
4079
# headline level offset declared?
4080
$flags{headlineLevelOffset}=exists $tagpars{headlinebase} ? $tagpars{headlinebase} : 0;
4081
4082
11
145
# store the filename in the list of opened sources, to avoid circular reopening
4083
# (it would be more perfect to store the complete path, is there a module for this?)
4084
$openedSourcefiles{$tagpars{file}}=1;
4085
11
564
4086
# we have to supply something, but it should be nothing
4087
[[()], $_[3][1]];
4088
}
4089
elsif ($flags{filter} and $tagpars{type}!~/^(($flags{filter})|(?:parsed)?example)$/i)
4090
{
4091
1
9
# this file does not need to be included, nevertheless
4092
# we have to supply something - but it should be nothing
4093
[[()], $_[3][1]];
4094
}
4095
elsif ($tagpars{type}=~/^perl$/i)
4096
2
50
10
{
4097
# Does the caller want to evaluate code?
4098
if ($safeObject)
4099
2
50
10
{
4100
# update active contents base data, if necessary
4101
34
34
240
if ($flags{activeBaseData})
34
174
34
6052
4102
0
0
0
{
0
0
4103
no strict 'refs';
4104
${join('::', ref($safeObject) ? $safeObject->root : 'main', 'PerlPoint')}=dclone($flags{activeBaseData});
4105
}
4106
2
50
23
4107
# evaluate the source code (for an unknown reason, we have to precede the constant by "&" here to work)
4108
warn "[Info] Evaluating included Perl code.\n" unless $flags{display} & &DISPLAY_NOINFO;
4109
2
100
27
my $result=ref($safeObject) ? $safeObject->rdo($tagpars{file})
4110
: eval
4111
{
4112
# enter user code namespace
4113
34
34
325
package main;
34
99
34
354271
4114
# disable "strict" checks
4115
1
916
no strict;
4116
# excute user code
4117
1
50
6251
my $result=do $tagpars{file};
4118
# check result ($! does not need to be checked, we checked file readability ourselves before)
4119
1
14
die $@ if $@;
4120
# reply provided result
4121
$result;
4122
};
4123
2
50
793
0
0
4124
# check result
4125
if ($@)
4126
{_semerr($_[0], "$sourceFile, line $_[3][1]: included Perl code could not be evaluated: $@.");}
4127
else
4128
2
100
66
{
4129
# success - make the result part of the input stream (by stack)
4130
_stackInput($_[0], split(/(\n)/, $result)) if defined $result;
4131
2
50
10
4132
# reset the "end of input reached" flag if necessary
4133
$readCompletely=0 if $readCompletely;
4134
}
4135
}
4136
2
48
4137
# we have to supply something, but it should be nothing
4138
[[()], $_[3][1]];
4139
}
4140
else
4141
{
4142
# we include anything else: provide the contents as it is,
4143
3
37
# declared as an "embedded" part
4144
3
291
# open(my $included, $tagpars{file});
4145
3
80
my $included=new IO::File;
4146
3
44
open($included, $tagpars{file});
4147
my @included=<$included>;
4148
close($included);
4149
4150
3
100
21
# in case the file was declared a (parsed) example, embed its contents as a (verbatim) block,
4151
# otherwise, include it as really embedded part (to be processed by a backend)
4152
if ($tagpars{type}=~/^(parsed)?example$/i)
4153
1
50
11
{
4154
# set paragraph type
4155
my $ptypeDirective=defined($1) ? DIRECTIVE_BLOCK : DIRECTIVE_VERBATIM;
4156
1
50
10
4157
# indent lines, if requested
4158
if (exists $tagpars{indent})
4159
1
50
15
{
0
0
4160
# check parameter
4161
unless ($tagpars{indent}=~/^\d+$/)
4162
{$errors++, _semerr($_[0], "$sourceFile, line $_[3][1]: Invalid indentation value of \"$tagpars{indent}\", please set up a number.");}
4163
else
4164
1
5
{
4165
1
7
# all right, indent
5
21
4166
my $indentation=' ' x $tagpars{indent};
4167
@included=map {"$indentation$_"} @included;
4168
}
4169
1
5
}
4170
4171
my %hints=(nr=>++$directiveCounter);
4172
[
4173
1
13
[
4174
# opener directive
4175
[\%hints, $ptypeDirective, DIRECTIVE_START],
4176
# the list of enclosed literals
4177
@included,
4178
# final directive
4179
[\%hints, $ptypeDirective, DIRECTIVE_COMPLETE]
4180
],
4181
$_[3][1]
4182
];
4183
}
4184
2
7
else
4185
{
4186
my %hints=(nr=>++$directiveCounter);
4187
[
4188
2
52
[
4189
# opener directive
4190
[\%hints, DIRECTIVE_TAG, DIRECTIVE_START, 'EMBED', {lang=>$tagpars{type}}],
4191
# the list of enclosed "literals", if any
4192
@included,
4193
# final directive
4194
[\%hints, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, 'EMBED', {lang=>$tagpars{type}}]
4195
],
4196
$_[3][1]
4197
];
4198
}
4199
}
4200
}
4201
else
4202
0
0
{
4203
# file missing, simply inform user
4204
$errors++, _semerr($_[0], "$sourceFile, line $_[3][1]: File $tagpars{file} does not exist or cannot be read (current directory: ", cwd(), ").");
4205
0
0
4206
# we have to supply something, but it should be nothing
4207
[[()], $_[3][1]];
4208
}
4209
}
4210
else
4211
0
0
{
4212
# we have to supply something, but it should be nothing
4213
[[()], $_[3][1]];
4214
}
4215
}
4216
;
4217
4218
4219
# macro definition
4220
alias_definition : '+'
4221
7
7
31
{
4222
# switch to definition mode
4223
_stateManager(STATE_DEFINITION);
4224
7
50
31
4225
# trace, if necessary
4226
warn "[Trace] $sourceFile, line $_[1][1]: Macro definition starts.\n" if $flags{trace} & TRACE_PARAGRAPHS;
4227
}
4228
Word
4229
7
7
29
{
4230
# deactivate boost
4231
$flags{noboost}=1;
4232
}
4233
optional_tagpars
4234
7
7
23
{
4235
# reactivate boost
4236
$flags{noboost}=0;
4237
}
4238
Colon
4239
7
7
84
{
4240
# disable all specials to get the body as a plain text
4241
@specials{keys %specials}=(0) x scalar(keys %specials);
4242
}
4243
text
4244
{
4245
# "text" already switched back to default mode (and disabled specials [{}:])
4246
7
50
7
24
4247
# trace, if necessary
4248
warn "[Trace] $sourceFile, line $_[7][1]: Macro definition completed.\n" if $flags{trace} & TRACE_PARAGRAPHS;
4249
7
50
32
4250
# check spelling (only accept capitals and underscores in alias names, just like in tags)
4251
0
0
0
if ($_[3][0]=~/[a-z]/)
4252
0
0
{
4253
warn "[Warn] $sourceFile, line $_[3][1]: Macro \"\\$_[3][0]\" is stored as ", uc("\\$_[3][0]"), ".\n" unless $flags{display} & DISPLAY_NOWARN;
4254
$_[3][0]=uc($_[3][0]);
4255
}
4256
7
13
7
18
7
13
7
14
4257
7
22
# build macro text
7
29
4258
shift(@{$_[9][0]}); pop(@{$_[9][0]});
4259
my $macro=join('', @{$_[9][0]});
4260
7
50
27
4261
# anything specified?
4262
if ($macro=~/^\s*$/)
4263
0
0
0
{
4264
# nothing defined, should this line cancel a previous definition?
4265
if (exists $macros{$_[3][0]})
4266
0
0
{
4267
# cancel macro
4268
delete $macros{$_[3][0]};
4269
0
0
0
4270
# trace, if necessary
4271
warn "[Trace] $sourceFile, line $_[7][1]: Macro \"$_[3][0]\" is cancelled.\n" if $flags{trace} & TRACE_SEMANTIC;
4272
0
0
4273
# update macro checksum
4274
$macroChecksum=sha1_base64(nfreeze(\%macros));
4275
}
4276
else
4277
0
0
0
{
4278
# trace, if necessary
4279
warn "[Trace] $sourceFile, line $_[7][1]: Empty macro \"$_[3][0]\" is ignored.\n" if $flags{trace} & TRACE_SEMANTIC;
4280
}
4281
}
4282
else
4283
7
12
{
4284
7
50
# ok, this is a new definition - get all used parameters
4285
my %pars;
4286
@pars{($macro=~/__([^_\\]+)__/g)}=();
4287
7
100
54
7
24
4288
# store default values of options, if necessary
4289
if (@{$_[5][0]})
4290
1
2
{
1
6
4291
1
33
12
# the list already consists of key/value pairs
4292
my %defaults=@{$_[5][0]};
4293
exists $pars{$_} and $pars{$_}=$defaults{$_} for keys %defaults;
4294
}
4295
7
100
21
4296
7
46
# tag body wildcard is no parameter
4297
my $bodyFlag=exists $pars{body} ? 1 : 0;
4298
delete $pars{body};
4299
7
19
4300
# make guarded underscores just underscores
4301
$macro=~s/\\_//g;
4302
4303
7
30
# store name, parameters (and their defaults, if any),
4304
# macro text and body flag
4305
$macros{$_[3][0]}=[\%pars, $macro, $bodyFlag];
4306
7
57
4307
# update macro checksum
4308
$macroChecksum=sha1_base64(nfreeze(\%macros));
4309
}
4310
4311
7
493
# we have to supply something, but it should be nothing
4312
# (this is a paragraph, so reply a plain string)
4313
35
49098
['', $_[11][1]];
4314
}
4315
;
4316
35
952
4317
4318
4319
%%
4320
4321
4322
# ------------------------------------------
4323
# Internal function: input stack management.
4324
# ------------------------------------------
4325
sub _stackInput
4326
21
21
66
{
4327
# get parameters
4328
my ($parser, @lines)=@_;
4329
21
50
4330
# declare variable
4331
my (@waiting);
4332
4333
# the current input line becomes the last line to read in this set
4334
21
100
66
196
# (this way, we arrange it that additional text is exactly placed where its generator tag or macro stood,
4335
# without Ils confusion)
4336
push(@lines, (defined $parser->{USER}->{INPUT} and $parser->{USER}->{INPUT}) ? $parser->{USER}->{INPUT} : ());
4337
4338
# combine line parts to lines completed by a trailing newline
4339
21
34
# (additionally, take into account that there might be mixed references which have to be stored unchanged)
21
171
4340
21
67
{
4341
my $lineBuffer='';
4342
58
100
134
foreach my $line (@lines)
4343
{
4344
if (ref($line))
4345
11
50
33
{
4346
# push collected string and current reference
4347
push(@waiting, length($lineBuffer) ? $lineBuffer : (), $line);
4348
11
19
4349
# reset line buffer
4350
$lineBuffer='';
4351
11
18
4352
# next turn
4353
next;
4354
}
4355
47
91
4356
# compose a string ...
4357
$lineBuffer.=$line;
4358
47
100
142
4359
# ... until a newline was found
4360
21
100
102
push(@waiting, $lineBuffer), $lineBuffer='' if $line eq "\n";
4361
}
4362
push(@waiting, $lineBuffer) if length($lineBuffer);
4363
}
4364
21
40
4365
# get next line to read
4366
my $newInputLine=shift(@waiting);
4367
21
39
21
62
4368
# update (innermost) input stack
4369
unshift(@{$inputStack[0]}, @waiting);
4370
21
105
4371
# update line memory (flag that this was an expanded line by adding a third parameter)
4372
unshift(@inLine, [length($newInputLine)+length('exp.: '), "exp.: $newInputLine", 1]);
4373
21
87
4374
# make the new top line the current input
4375
$parser->{USER}->{INPUT}=$newInputLine;
4376
}
4377
4378
4379
4380
# a pattern lookup table for certain specials, used by the lexer (should be scoped to it
4381
# but indentation of a long function takes time ...)
4382
my %specials2patterns;
4383
@specials2patterns{'colon', 'number', '-'}=(':', '0-9', '\-');
4384
4385
4386
# -----------------------------
4387
# Internal function: the lexer.
4388
# -----------------------------
4389
sub _lexer
4390
5882
5882
13459
{
4391
# get parameters
4392
my ($parser)=@_;
4393
5882
100
46510
66
66
4394
# scan for unlexed EOL´s which should be ignored
4395
while (
4396
$parser->{USER}->{INPUT}
4397
and $parser->{USER}->{INPUT}=~/^\n/
4398
and (
4399
$lexerFlags{eol}==LEXER_IGNORE
4400
or (
4401
@tableSeparatorStack
4402
and $tableSeparatorStack[0][1] eq "\n"
4403
and $tableColumns<0
4404
)
4405
)
4406
)
4407
10
50
46
{
4408
# trace, if necessary
4409
warn "[Trace] Lexer: Ignored EOL in line $lineNrs{$inHandle}.\n" if $flags{trace} & TRACE_LEXER;
4410
10
71
4411
# remove the ignored newline
4412
$parser->{USER}->{INPUT}=~s/^\n//;
4413
10
50
66
123
66
4414
# update column counter, if necessary
4415
$tableColumns++ if @tableSeparatorStack and $tableSeparatorStack[0][1] eq "\n" and $tableColumns<0;
4416
}
4417
5882
100
18087
4418
# get next symbol
4419
unless ($parser->{USER}->{INPUT})
4420
{
4421
1681
100
2202
# update line memory (removed handled lines, both original and expanded ones)
41
169
1685
7593
4422
# (use a do block to perform the operation once in any case, see perlsyn)
4423
do {shift(@inLine)} until not @inLine or @{$inLine[0]}==2;
4424
4425
1681
7841
{
2091
2514
2091
4110
4426
# will the next line be get from the input stack instead of from a real file?
4427
my $lineFromStack=scalar(@{$inputStack[0]});
4428
2091
100
6232
4429
# reset stack line buffer, if necessary
4430
undef @previousStackLines unless $lineFromStack;
4431
2091
100
50
3029
66
33
66
4432
2091
29540
# get next input line
4433
unless (
4434
(@{$inputStack[0]} and ($parser->{USER}->{INPUT}=shift(@{$inputStack[0]}) or 1))
4435
or (defined($inHandle) and $parser->{USER}->{INPUT}=<$inHandle>)
4436
)
4437
78
100
310
{
4438
# was this a nested source?
4439
unless (@inHandles)
4440
{
4441
# This was the base document: should we insert a final additional token?
4442
# (In case we recognize the end of a document source when there is still a
4443
# paragraph filter pending, supply as many "empty lines" as necessary to let
4444
67
100
33
1189
# the parser recognize the filtered paragraph is completed. Possibly a loop
66
4445
# detection should be added to avoid endless ping-pong.)
4446
$readCompletely=1,
4447
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Final Empty_line in line $lineNrs{$inHandle}.\n")),
4448
return('Empty_line', ['', $lineNrs{$inHandle}]) unless $readCompletely and not $flags{virtualParagraphStart};
4449
33
255
4450
# well done
4451
return('', [undef, -1]);
4452
}
4453
else
4454
{
4455
11
51
# we finished a nested source: close it and restore
4456
# things to continue reading of enclosing file
4457
11
129
my ($helper1, $helper2, $helper3, $localizedVars, $localizedAll);
4458
(
4459
$helper1,
4460
$parser->{USER}->{INPUT},
4461
$sourceFile,
4462
$helper2,
4463
@flags{qw(headlineLevelOffset headlineLevel)},
4464
11
35
$helper3,
4465
11
67
$localizedVars, $localizedAll,
4466
)=@{shift(@inHandles)};
4467
$lineNrs{$inHandle}=$helper2-1; # -1 to compensate the subsequent increment
4468
11
560
4469
# back to envelopes directory
4470
chdir($helper3);
4471
11
512
4472
11
486
# reopen envelope file
4473
11
1855
close($inHandle);
4474
11
65
$inHandle=new IO::File;
4475
11
80
open($inHandle, $sourceFile);
4476
binmode($inHandle);
4477
seek($inHandle, $helper1, 0);
4478
11
28
4479
# switch back to envelopes input stack
4480
shift(@inputStack);
4481
11
41
4482
# update nesting stack
4483
pop(@nestedSourcefiles);
4484
11
162
4485
# update source file nesting level hint
4486
_predeclareVariables({_SOURCE_LEVEL=>scalar(@nestedSourcefiles)});
4487
11
100
66
224
100
4488
# restore variables as necessary
4489
if ($localizedAll)
4490
1
50
8
{
4491
# Do we have to take care of the stream?
4492
if ($flags{var2stream})
4493
1
4
{
1
4
4494
# stream variable reset
4495
push(@{$resultStreamRef->[STREAM_TOKENS]}, [{}, DIRECTIVE_VARRESET, DIRECTIVE_START]);
4496
1
2
1
10
4497
# update tag finish memory
4498
_updateTagFinishMem(scalar(@{$resultStreamRef->[STREAM_TOKENS]}));
4499
4500
1
15
# restore former variables completely, overwriting current settings
4501
1
13
# (and propagating them into the stream again)
4502
undef %variables;
4503
_predeclareVariables({$_=>$localizedVars->{$_}}, 1) foreach sort keys %$localizedVars;
4504
}
4505
else
4506
0
0
{
4507
# ok, the stream does not take notice of this operation, so it can be performed quicker
4508
%variables=%$localizedVars;
4509
}
4510
}
4511
elsif (!$localizedAll and %$localizedVars)
4512
1
18
{
4513
# handle each localized variable
4514
foreach my $var (keys %$localizedVars)
4515
2
50
25
{
4516
# restore old value in parser and stream context, if necessary
4517
_predeclareVariables({$var=>$localizedVars->{$var}}, 1)
4518
if $localizedVars->{$var} ne $variables{$var};
4519
}
4520
}
4521
}
4522
}
4523
2024
100
4748
4524
# update stack line buffers, if necessary
4525
if ($lineFromStack)
4526
{
4527
# "rotate" buffers (necessary because there are various return points following,
4528
21
77
# so there will be not exactly one final point where we could save the current
4529
21
45
# line value in a scalar buffer)
4530
$previousStackLines[0]=$previousStackLines[1];
4531
$previousStackLines[1]=$parser->{USER}->{INPUT};
4532
}
4533
2024
100
100
4932
4534
# reference found on stack?
4535
if ($lineFromStack and ref($parser->{USER}->{INPUT}))
4536
11
34
{
4537
# get the reference
4538
my @refLexed=_refLexed($parser);
4539
4540
# unless the item was a newline, we can use it directly
4541
11
100
64
# (but the context for newline evaluation might have changed between the
4542
# point of delaying and now)
4543
return @refLexed unless $refLexed[0] eq 'Empty_line';
4544
4545
# ok, this has to be parsed *again* (because the paragraph/special characters
4546
1
3
# context *now* might be different from that that was present when we stacked
1
4
4547
# the item)
4548
($parser->{USER}->{INPUT}, $lineNrs{$inHandle})=@{$refLexed[1]};
4549
}
4550
2014
100
6734
4551
# update line counter, if necessary
4552
$lineNrs{$inHandle}++ unless $lineFromStack;
4553
2014
100
100
7229
4554
1995
100
100
5949
# ignore this line if wished (take conditions and docstreams into account)
4555
$parser->{USER}->{INPUT}='', redo if $flags{skipInput}==1 and $parser->{USER}->{INPUT}!~/^\?/;
4556
$parser->{USER}->{INPUT}='', redo if $flags{skipInput}==2 and $parser->{USER}->{INPUT}!~/^[~=]/;
4557
1924
3125
4558
# if we are here, we can leave the skip mode (both condition and docstream one)
4559
$flags{skipInput}=0;
4560
1924
9638
4561
# we read a new line (or something from stack)
4562
1924
100
11489
unshift(@inLine, [length($parser->{USER}->{INPUT})+length('org.: '), "org.: $parser->{USER}->{INPUT}"]);
4563
4564
unless ($lineFromStack)
4565
1913
100
4626
{
4566
# add a line update hint
4567
2
3
if ($flags{linehints})
2
25
4568
{
4569
push(@{$resultStreamRef->[STREAM_TOKENS]}, [{}, DIRECTIVE_NEW_LINE, DIRECTIVE_START, {file=>$sourceFile, line=>$lineNrs{$inHandle}}]);
4570
2
4
2
608
4571
# update tag finish memory by the way
4572
_updateTagFinishMem(scalar(@{$resultStreamRef->[STREAM_TOKENS]}));
4573
}
4574
4575
1913
9015
# remove TRAILING whitespaces, but keep newlines (if any)
1913
9240
4576
1913
14906
{
4577
1913
100
9701
my $newline=($parser->{USER}->{INPUT}=~/\n$/m);
4578
$parser->{USER}->{INPUT}=~s/\s*$//;
4579
$parser->{USER}->{INPUT}=join('', $parser->{USER}->{INPUT}, "\n") if $newline;
4580
}
4581
}
4582
1924
100
6429
4583
# scan for empty lines as necessary
4584
if ($parser->{USER}->{INPUT}=~/^$/)
4585
867
100
2969
{
4586
# update the checksum flags
4587
$flags{checksum}=1 if $flags{cache} & CACHE_ON;
4588
867
0
2441
50
4589
# trace, if necessary
4590
warn "[Trace] Lexer: Empty_line in line $lineNrs{$inHandle}", $lexerFlags{el}==LEXER_IGNORE ? ' is ignored' : '', ".\n" if $flags{trace} & TRACE_LEXER;
4591
867
3413
4592
# update input line
4593
$parser->{USER}->{INPUT}='';
4594
867
100
2940
4595
# sometimes empty lines have no special meaning
4596
shift(@inLine), redo if $lexerFlags{el}==LEXER_IGNORE;
4597
547
5033
4598
# but sometimes they are very special
4599
return('Empty_line', ["\n", $lineNrs{$inHandle}]);
4600
}
4601
else
4602
1057
100
3010
{
4603
# disable caching for embedded code containing empty lines
4604
$flags{checksummed}=0 if $specials{embedded};
4605
1057
100
100
5378
66
100
66
100
100
4606
# this may be the first line of a new paragraph to be checksummed
4607
if (
4608
($flags{cache} & CACHE_ON)
4609
and $flags{checksum}
4610
and not $lineFromStack
4611
and (not $specials{heredoc} or $specials{heredoc} eq '1')
4612
and not @tableSeparatorStack
4613
and not $specials{embedded}
4614
)
4615
104
439
{
4616
# handle $/ locally
4617
local($/);
4618
104
232
4619
# update statistics
4620
$statistics{cache}[0]++;
4621
104
100
587
4
100
19
4622
2
8
# well, switch to paragraph mode (depending on the paragraph type)!
4623
if ($parser->{USER}->{INPUT}=~/^<<(\w+)/)
4624
98
251
{$/="\n$1";}
4625
elsif ($parser->{USER}->{INPUT}=~/^(?
4626
{$/="\n\\END_TABLE";}
4627
else
4628
{$/='';}
4629
104
245
4630
# store current position
4631
my $lexerPosition=tell($inHandle);
4632
104
100
100
2269
4633
104
2914
# read *current* paragraph completely (take care - we may have read it completely yet!)
4634
104
100
100
836
seek($inHandle, $lexerPosition-length($parser->{USER}->{INPUT}), 0) unless $parser->{USER}->{INPUT}=~/^<<(\w+)/ or $parser->{USER}->{INPUT}=~/^(?
4635
my $paragraph=<$inHandle>;
4636
$paragraph=join('', $parser->{USER}->{INPUT}, $paragraph) if $parser->{USER}->{INPUT}=~/^<<(\w+)/ or $parser->{USER}->{INPUT}=~/^(?
4637
104
166
4638
104
32232
# count the lines in the paragraph read
4639
104
100
100
908
my $plines=0;
4640
$plines++ while $paragraph=~/(\n)/g;
4641
$plines-- unless $parser->{USER}->{INPUT}=~/^<<(\w+)/ or $parser->{USER}->{INPUT}=~/^(?
4642
104
4574
4643
# remove trailing whitespaces (to avoid checksumming them)
4644
$paragraph=~s/\n+$//;
4645
104
50
480
4646
# anything interesting found?
4647
if (defined $paragraph)
4648
104
50
4375
{
4649
# build checksum (of paragraph *and* headline level offset)
4650
my $checksum=sha1_base64(join('+', exists $flags{headlineLevelOffset} ? $flags{headlineLevelOffset} : 0, $paragraph));
4651
4652
104
0
100
1040
# warn "---> Searching checksum for this paragraph:\n-----\n$paragraph\n- by $checksum --\n";
33
66
0
33
4653
# check paragraph to be known
4654
if (
4655
exists $checksums->{$sourceFile}
4656
and exists $checksums->{$sourceFile}{$checksum}
4657
and (
4658
not defined $checksums->{$sourceFile}{$checksum}[3]
4659
or $checksums->{$sourceFile}{$checksum}[3] eq $macroChecksum
4660
)
4661
and (
4662
not defined $checksums->{$sourceFile}{$checksum}[4]
4663
or $checksums->{$sourceFile}{$checksum}[4] eq $varChecksum
4664
)
4665
)
4666
{
4667
# Do *not* reset the checksum flag for new checksums - we already read the
4668
0
0
# empty lines, and a new paragraph may follow! *But* deactivate the current
4669
# checksum to avoid multiple storage - we already stored it, right?
4670
$flags{checksummed}=0;
4671
4672
# reset input buffer - it is all handled (take care to remove a final newline
4673
0
0
# if the paragraph was closed by a string - this would normally be read in a
4674
0
0
0
0
# per line processing, but it remained in the file in paragraph mode)
4675
0
0
$/="\n";
4676
scalar(<$inHandle>) if $parser->{USER}->{INPUT}=~/^<<(\w+)/ or $parser->{USER}->{INPUT}=~/^(?
4677
$parser->{USER}->{INPUT}='';
4678
4679
# warn "===========> PARAGRAPH CACHE HIT!! ($lineNrs{$inHandle}/$sourceFile/$checksum) <=================\n$paragraph-----\n";
4680
# use Data::Dumper; warn Dumper($checksums->{$sourceFile}{$checksum});
4681
0
0
4682
# update statistics
4683
$statistics{cache}[1]++;
4684
4685
0
0
# update line counter
4686
# warn "----> Old line: $lineNrs{$inHandle}\n";
4687
$lineNrs{$inHandle}+=$plines;
4688
# warn "----> New line: $lineNrs{$inHandle}\n";
4689
0
0
4690
0
0
# update anchors
4691
$anchors->add($_, $checksums->{$sourceFile}{$checksum}[5]{$_}, $flags{headlinenr})
4692
foreach keys %{$checksums->{$sourceFile}{$checksum}[5]};
4693
4694
# The next steps depend - follow the provided hint. We may have to reinvoke
4695
0
0
0
# the parser to restore a state.
4696
# perl 5.6 # unless (exists $checksums->{$sourceFile}{$checksum}[2])
4697
unless (defined $checksums->{$sourceFile}{$checksum}[2])
4698
0
0
{
0
0
0
0
4699
# direct case - add the already known part directly to the stream
4700
push(@{$resultStreamRef->[STREAM_TOKENS]}, @{$checksums->{$sourceFile}{$checksum}[0]});
4701
0
0
0
0
4702
# update tag finish memory
4703
_updateTagFinishMem(scalar(@{$resultStreamRef->[STREAM_TOKENS]}));
4704
0
0
4705
0
0
# Well done this paragraph - go on!
4706
shift(@inLine);
4707
redo;
4708
}
4709
else
4710
0
0
{
4711
# more complex case - reinvoke the parser to update its states
4712
return($checksums->{$sourceFile}{$checksum}[2], [dclone($checksums->{$sourceFile}{$checksum}[0]), $lineNrs{$inHandle}]);
4713
}
4714
}
4715
104
191
104
490
4716
# flag that we are going to build an associated stream
4717
$flags{checksummed}=[$checksum, scalar(@{$resultStreamRef->[STREAM_TOKENS]}), $plines];
4718
# warn "---> Started checksumming for\n-----\n$paragraph\n---(", $plines+1, " line(s))\n";
4719
104
680
4720
# restart anchor logging
4721
$anchors->checkpoint(1);
4722
}
4723
104
1011
4724
# reset file pointer
4725
seek($inHandle, $lexerPosition, 0);
4726
}
4727
4728
1057
2392
# update the checksum flag: we are *within* a paragraph, do not checksum
4729
# until we reach the next empty line
4730
$flags{checksum}=0;
4731
}
4732
4733
1057
100
33
3587
# detect things at the beginning of a *real* line at the beginning of a paragraph
33
66
4734
# (which nevertheless might have been stacked)
4735
if ( not $lineFromStack
4736
or ( defined $previousStackLines[0]
4737
and not ref($previousStackLines[0])
4738
and $previousStackLines[0]=~/\n$/
4739
)
4740
1048
3322
)
4741
1048
100
4048
{
4742
my @rc=_lineStartResearch($parser);
4743
return(@rc) if shift(@rc);
4744
}
4745
}
4746
}
4747
5233
0
21309
50
4748
# trace, if necessary
4749
warn '[Trace] Lexing ', ref($parser->{USER}->{INPUT}) ? 'a prepared part' : qq("$parser->{USER}->{INPUT}"), ".\n" if $flags{trace} & TRACE_LEXER;
4750
5233
50
14404
4751
# Reference found? (Usually placed by _stackInput().)
4752
return _refLexed($parser) if ref($parser->{USER}->{INPUT});
4753
4754
5233
100
13985
# if the paragraph was just filtered, there might be certain operations to perform
4755
# (as usual at the beginning of a new paragraph)
4756
8
38
if ($parserState==STATE_PFILTERED)
4757
8
100
49
{
4758
my @rc=_lineStartResearch($parser);
4759
return(@rc) if shift(@rc);
4760
}
4761
5229
100
100
19164
100
4762
# scan for heredoc close hints
4763
if ($specials{heredoc} and $specials{heredoc} ne '1' and $parser->{USER}->{INPUT}=~/^($specials{heredoc})$/)
4764
12
50
66
{
4765
# trace, if necessary
4766
warn "[Trace] Lexer: Heredoc close hint $1 in line $lineNrs{$inHandle}.\n" if $flags{trace} & TRACE_LEXER;
4767
12
39
4768
# update input line
4769
$parser->{USER}->{INPUT}='';
4770
12
23
4771
# reset heredoc setting
4772
$specials{heredoc}=1;
4773
12
110
4774
# reply token
4775
return('Heredoc_close', [$1, $lineNrs{$inHandle}]);
4776
}
4777
5217
100
100
29888
100
4778
# can we take the rest of the line at *once*?
4779
if (($parserState==STATE_COMMENT or $parserState==STATE_VERBATIM) and $parser->{USER}->{INPUT} ne "\n")
4780
76
156
{
4781
76
100
157
# grab line and chomp if necessary
4782
my $line=$parser->{USER}->{INPUT};
4783
chomp($line) unless $parserState==STATE_VERBATIM;
4784
76
100
188
4785
# update input line (restore trailing newline if it will be used to detect paragraph completion)
4786
$parser->{USER}->{INPUT}=$parserState==STATE_VERBATIM ? '' : "\n";
4787
76
50
176
4788
# trace, if necessary
4789
warn qq([Trace] Lexer: word "$line" in line $lineNrs{$inHandle}.\n) if $flags{trace} & TRACE_LEXER;
4790
76
437
4791
# supply result
4792
return('Word', [$line, $lineNrs{$inHandle}]);
4793
}
4794
5141
13503
4795
# reply a token
4796
for ($parser->{USER}->{INPUT})
4797
5141
15047
{
4798
# declare scopies
4799
my ($found, $sfound);
4800
5141
100
11079
4801
# check for table separators, if necessary (these are the most common strings)
4802
if (@tableSeparatorStack)
4803
389
100
33
3479
{
4804
# check for a column separator
4805
s/^$tableSeparatorStack[0][0]//,
4806
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: table column separator in line $lineNrs{$inHandle}.\n")),
4807
return('Table_separator', ['c', $lineNrs{$inHandle}]) if /^($tableSeparatorStack[0][0])/;
4808
305
100
33
2296
4809
# check for row separator
4810
s/^$tableSeparatorStack[0][1]//,
4811
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: table row separator in line $lineNrs{$inHandle}.\n")),
4812
return('Table_separator', ['r', $lineNrs{$inHandle}]) if /^($tableSeparatorStack[0][1])/;
4813
}
4814
5007
100
12865
4815
# reply next token: EOL?
4816
889
100
9746
if (/^(\n)/)
100
50
4817
{
4818
277
693
if ($lexerFlags{eol}==LEXER_TOKEN)
4819
277
50
730
{
4820
277
1411
$found=$1;
4821
277
3956
warn("[Trace] Lexer: EOL in line $lineNrs{$inHandle}.\n") if $flags{trace} & TRACE_LEXER;
4822
s/^$1//;
4823
return('EOL', [$found, $lineNrs{$inHandle}]);
4824
}
4825
elsif ($lexerFlags{eol}==LEXER_EMPTYLINE)
4826
49
50
161
{
4827
49
309
# flag "empty line" as wished
4828
49
357
warn("[Trace] Lexer: EOL -> Empty_line in line $lineNrs{$inHandle}.\n") if $flags{trace} & TRACE_LEXER;
4829
s/^$1//;
4830
return('Empty_line', ['', $lineNrs{$inHandle}]);
4831
0
0
}
4832
elsif ($lexerFlags{eol}==LEXER_SPACE)
4833
563
50
2205
{
4834
563
3511
# flag "space" as wished and reply a simple whitespace
4835
563
8011
warn("[Trace] Lexer: EOL -> Space in line $lineNrs{$inHandle}.\n") if $flags{trace} & TRACE_LEXER;
4836
s/^$1//;
4837
return('Space', [' ', $lineNrs{$inHandle}]);
4838
}
4839
else
4840
{die "[BUG] Unhandled EOL directive $lexerFlags{eol}.";}
4841
}
4842
4118
50
0
10381
66
4843
# reply next token: scan for Ils if necessary
4844
$found=$1, s/^$1//,
4845
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Ils in line $lineNrs{$inHandle}.\n")),
4846
return('Ils', [$found, $lineNrs{$inHandle}]) if $parserState==STATE_PFILTERED and /^$lexerPatterns{space}/;
4847
4118
100
33
34253
4848
# reply next token: scan for spaces
4849
$found=$1, s/^$1//,
4850
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Space in line $lineNrs{$inHandle}.\n")),
4851
return('Space', [$found, $lineNrs{$inHandle}]) if /^$lexerPatterns{space}/;
4852
3671
100
33
25217
100
4853
# reply next token: scan for paragraph filter delimiters ("||" and "|")
4854
$found=$1, s/^\Q$1//,
4855
(($flags{trace} & TRACE_LEXER) and warn(qq([Trace] Lexer: Paragraph filter delimiter "$found" in line $lineNrs{$inHandle}.\n))),
4856
return($found, [$found, $lineNrs{$inHandle}]) if /^$lexerPatterns{pfilterDelimiter}/ and $specials{pfilter};
4857
3655
100
33
10358
66
4858
# reply next token: scan for here doc openers
4859
$found=$1, s/^<<$1//,
4860
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Heredoc opener $found in line $lineNrs{$inHandle}.\n")),
4861
return('Heredoc_open', [$found, $lineNrs{$inHandle}]) if /^<<(\w+)/ and $specials{heredoc} eq '1';
4862
3643
100
33
24653
100
4863
# reply next token: scan for SPECIAL tagnames: \TABLE
4864
$found=$1, s/^\\$1//,
4865
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Table starts in line $lineNrs{$inHandle}.\n")),
4866
return('Table', [$found, $lineNrs{$inHandle}]) if $specials{tag} and /^$lexerPatterns{table}/;
4867
3632
100
33
20279
100
4868
# reply next token: scan for SPECIAL tagnames: \END_TABLE
4869
$found=$1, s/^\\$1//,
4870
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Table completed in line $lineNrs{$inHandle}.\n")),
4871
return('Tabled', [$found, $lineNrs{$inHandle}]) if $specials{tag} and /^$lexerPatterns{endTable}/;
4872
3621
100
33
20607
100
4873
# reply next token: scan for SPECIAL tagnames: \EMBED
4874
$found=$1, s/^\\$1//,
4875
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Embedding starts in line $lineNrs{$inHandle}.\n")),
4876
return('Embed', [$found, $lineNrs{$inHandle}]) if $specials{tag} and /^$lexerPatterns{embed}/;
4877
3593
100
33
21628
100
4878
# reply next token: scan for SPECIAL tagnames: \END_EMBED
4879
$found=$1, s/^\\$1//,
4880
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Embedding completed in line $lineNrs{$inHandle}.\n")),
4881
return('Embedded', [$found, $lineNrs{$inHandle}]) if $specials{embedded} and /^$lexerPatterns{endEmbed}/;
4882
3565
100
33
21423
100
4883
# reply next token: scan for SPECIAL tagnames: \INCLUDE
4884
$found=$1, s/^\\$1//,
4885
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Including starts in line $lineNrs{$inHandle}.\n")),
4886
return('Include', [$found, $lineNrs{$inHandle}]) if $specials{tag} and /^$lexerPatterns{include}/;
4887
3548
100
33
34296
100
100
66
4888
# reply next token: scan for tagnames
4889
$found=$1, s/^\\$1//,
4890
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Tag opener $found in line $lineNrs{$inHandle}.\n")),
4891
return('Tag_name', [$found, $lineNrs{$inHandle}]) if $specials{tag} and /^$lexerPatterns{tag}/ and (exists $tagsRef->{$1} or exists $macros{$1});
4892
3436
100
33
56701
66
100
4893
# reply next token: scan for special characters
4894
$found=$1, s/^\Q$1//,
4895
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Special $found in line $lineNrs{$inHandle}.\n")),
4896
return($found, [$found, $lineNrs{$inHandle}]) if /^$patternNlbBackslash(\S)/ and exists $specials{$1} and $specials{$1};
4897
2505
100
33
10417
100
4898
# reply next token: scan for definition list items
4899
$found=$1, s/^$1//,
4900
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Colon in line $lineNrs{$inHandle}.\n")),
4901
return('Colon', [$found, $lineNrs{$inHandle}]) if $specials{colon} and /^$lexerPatterns{colon}/;
4902
4903
2486
100
33
30940
# reply next token: search for named variables (which need to be defined except at the
100
66
4904
# beginning of a new assignment paragraph)
4905
$found=$1, s/^\$$1//,
4906
(($flags{trace} & TRACE_LEXER) and warn(qq([Trace] Lexer: Named variable "$found" in line $lineNrs{$inHandle}.\n))),
4907
return('Named_variable', [$found, $lineNrs{$inHandle}])
4908
if /^$lexerPatterns{namedVar}(=?)/
4909
and (
4910
($parserState==STATE_DEFAULT and defined($2))
4911
or exists $variables{$1}
4912
);
4913
4914
2314
100
33
13793
# reply next token: search for symbolic variables (these cannot be used in assignments,
100
4915
# so handling is easier)
4916
$found=$2, s/^\$$1//,
4917
(($flags{trace} & TRACE_LEXER) and warn(qq([Trace] Lexer: Symbolic variable "$found" in line $lineNrs{$inHandle}.\n))),
4918
return('Symbolic_variable', [$found, $lineNrs{$inHandle}])
4919
if /^$lexerPatterns{symVar}/ and exists $variables{$2};
4920
4921
# flag that this paragraph *might* use macros someday, if there is still something being no tag and no
4922
2271
100
100
14355
# macro, but looking like a tag or a macro (somebody could *later* declare it a real macro, so the cache
100
100
4923
# needs to check macro definitions)
4924
$flags{checksummed}[3]=1
4925
if $specials{tag} and /^$lexerPatterns{tag}/
4926
and not (exists $flags{checksummed} and not $flags{checksummed});
4927
4928
# likewise, flag that this paragraph *might* use variables someday, if there is still something being no variable,
4929
2271
100
100
35234
# but looking like a variable (somebody could *later* declare it a real var, so the cache
100
4930
# needs to check variable definitions)
4931
$flags{checksummed}[4]=1
4932
if /($lexerPatterns{namedVarKernel})|($lexerPatterns{symVarKernel})/
4933
and not (exists $flags{checksummed} and not $flags{checksummed});
4934
2271
100
66
24242
66
100
66
100
100
4935
# remove guarding \\, if necessary
4936
s/^\\// unless $specials{heredoc}
4937
or (defined $lexerFlags{backsl} and $lexerFlags{backsl}==LEXER_TOKEN)
4938
or $parserState==STATE_EMBEDDING
4939
or $parserState==STATE_PFILTER
4940
or $parserState==STATE_CONDITION
4941
or $parserState==STATE_DEFINITION;
4942
2271
100
33
7139
66
4943
# reply next token: scan for numbers, if necessary
4944
$found=$1, s/^$1//,
4945
(($flags{trace} & TRACE_LEXER) and warn("[Trace] Lexer: Number $found in line $lineNrs{$inHandle}.\n")),
4946
2269
100
5460
return('Number', [$found, $lineNrs{$inHandle}]) if $specials{number} and /^(\d+)/;
4947
4948
unless ($flags{noboost})
4949
1871
100
66
80973
{
3058
8069
4950
1871
100
11018
# build set of characters to be special
4951
1871
100
6715
my $special=join('', '([', (map {exists $specials2patterns{$_} ? $specials2patterns{$_} : $_} grep(($specials{$_} and (length==1 or exists $specials2patterns{$_})), keys %specials)), '\n\\\\', '])');
4952
1871
111359
$special=qr($special|(\|{1,2})) if $specials{pfilter};
4953
$special=qr($special|($tableSeparatorStack[0][0])|($tableSeparatorStack[0][1])) if @tableSeparatorStack;
4954
$special=qr($special|(($lexerPatterns{namedVar})|($lexerPatterns{symVar})));
4955
4956
# reply next token: scan for word or single character (declared as "Word" as well)
4957
1871
100
33
212007
#warn("~~~~~~~~~> $special\n");
66
4958
#warn("---------> $_");
4959
$found=$1, s/^\Q$1//,
4960
#warn("=====> $found\n\n"),
4961
(($flags{trace} & TRACE_LEXER) and warn(qq([Trace] Lexer: (Boosted) word "$found" in line $lineNrs{$inHandle}.\n))),
4962
return('Word', [$found, $lineNrs{$inHandle}])
4963
if $_!~/^$special/ and /^(.+?)($special|($))/;
4964
}
4965
712
50
33
45005
66
4966
# reply next token: scan for word or single character (declared as "Word" as well)
4967
$found=$1, s/^\Q$1//,
4968
(($flags{trace} & TRACE_LEXER) and warn(qq([Trace] Lexer: Word "$found" in line $lineNrs{$inHandle}.\n))),
4969
return('Word', [$found, $lineNrs{$inHandle}]) if /^($patternWUmlauts)/ or /^(\S)/;
4970
0
0
4971
# everything should be handled - this code should never be executed!
4972
die qq([BUG] $sourceFile, line $lineNrs{$inHandle}: No symbol found in "$_"!\n);
4973
}
4974
}
4975
4976
4977
# evaluate a tag condition (can possibly be generalized: this is just a piece of code)
4978
sub _evalTagCondition
4979
0
0
0
{
4980
0
0
0
# get parameters
4981
0
0
0
my ($code, $file, $line)=@_;
4982
0
0
0
confess "[BUG] Missing code parameter.\n" unless defined $code;
4983
confess "[BUG] Missing file parameter.\n" unless defined $file;
4984
confess "[BUG] Missing line parameter.\n" unless defined $line;
4985
0
0
4986
# declare variables
4987
my ($rc);
4988
0
0
0
4989
# Does the caller want to evaluate the code?
4990
if ($safeObject)
4991
0
0
0
{
4992
# update active contents base data, if necessary
4993
34
34
488
if ($flags{activeBaseData})
34
75
34
208487
4994
0
0
0
{
0
0
4995
no strict 'refs';
4996
${join('::', ref($safeObject) ? $safeObject->root : 'main', 'PerlPoint')}=dclone($flags{activeBaseData});
4997
}
4998
0
0
0
4999
# make the code a string and evaluate it
5000
warn "[Trace] $sourceFile, line $line: Evaluating this code:\n\n$code\n\n\n" if $flags{trace} & TRACE_ACTIVE;
5001
0
0
0
5002
# invoke perl to compute the result
5003
$rc=ref($safeObject) ? $safeObject->reval($code) : eval(join(' ', '{package main; no strict;', $code, '}'));
5004
0
0
0
5005
# check result
5006
_semerr($_[0], "$file, line $line: tag condition could not be evaluated: $@.") if $@;
5007
}
5008
0
0
5009
# supply result
5010
$rc;
5011
}
5012
5013
# reference lexed: reply appropriate token
5014
sub _refLexed
5015
11
11
18
{
5016
# get parameters
5017
my ($parser)=@_;
5018
11
100
37
5019
# we got an already prepared stream part or a delayed token
5020
4
9
if (ref($parser->{USER}->{INPUT}) eq 'PerlPoint::Parser::DelayedToken')
5021
4
6
{
5022
4
14
my $delayedToken=$parser->{USER}->{INPUT};
5023
$parser->{USER}->{INPUT}='';
5024
return($delayedToken->token, $delayedToken->value);
5025
}
5026
7
17
else
5027
7
13
{
5028
7
35
my $streamedPart=$parser->{USER}->{INPUT};
5029
$parser->{USER}->{INPUT}='';
5030
return('StreamedPart', [$streamedPart, $lineNrs{$inHandle}]);
5031
}
5032
}
5033
5034
5035
sub _lineStartResearch
5036
1056
1056
1761
{
5037
# get parameters
5038
my ($parser)=@_;
5039
1056
100
15162
5040
# scan for indented lines, if necessary
5041
323
100
1516
if ($parser->{USER}->{INPUT}=~/^(\s+)/)
100
5042
{
5043
if ($lexerFlags{ils}==LEXER_TOKEN)
5044
25
50
104
{
5045
# trace, if necessary
5046
warn "[Trace] Lexer: Ils in line $lineNrs{$inHandle}.\n" if $flags{trace} & TRACE_LEXER;
5047
25
82
5048
25
512
# update input buffer and reply the token (contents is necessary as well)
5049
25
189
my $ils=$1;
5050
$parser->{USER}->{INPUT}=~s/^$1//;
5051
return(1, 'Ils', [$ils, $lineNrs{$inHandle}]);
5052
}
5053
27
50
663
elsif ($lexerFlags{ils}==LEXER_IGNORE)
5054
27
114
{
5055
warn "[Trace] Lexer: Ils in line $lineNrs{$inHandle} is ignored.\n" if $flags{trace} & TRACE_LEXER;
5056
$parser->{USER}->{INPUT}=~s/^(\s+)//;
5057
}
5058
}
5059
5060
# scan for a need of a virtual token opening a new paragraph
5061
# (to avoid parser state trouble caused by filters when the parser needs a lookahead
5062
1031
100
100
5120
# to detect the next paragraph)
66
5063
# warn "------> state $parserState (", STATE_DEFAULT, "), flag $flags{virtualParagraphStart}, $lexerFlagsOfPreviousState{cbell}\n";
5064
4
50
20
if ($parserState==STATE_DEFAULT and $flags{virtualParagraphStart} and $lexerFlagsOfPreviousState{cbell} ne LEXER_IGNORE)
5065
4
50
33
57
{
5066
warn "[Trace] Inserted virtual token to enable clean parser lookahead after pfilter invokation.\n" if $flags{trace} & TRACE_LEXER;
5067
return(1, 'Word', ['', $lineNrs{$inHandle}])
5068
if $lexerFlagsOfPreviousState{cbell} eq 'Ils'
5069
or $parser->{USER}->{INPUT}!~/^$lexerFlagsOfPreviousState{cbell}/;
5070
}
5071
1027
100
100
8067
100
5072
# scan for a new paragraph opened by a tag, if necessary
5073
if (($parserState==STATE_DEFAULT or $parserState==STATE_PFILTERED) and $parser->{USER}->{INPUT}=~/^\\/)
5074
40
165
{
5075
# remain in default state, but switch to its tag mode
5076
_stateManager(STATE_DEFAULT_TAGMODE);
5077
}
5078
1027
4098
5079
# flag that there is no token to return
5080
0;
5081
}
5082
5083
# ----------------------------------------------------------------------------------------------
5084
# Internal function: error message display.
5085
# ----------------------------------------------------------------------------------------------
5086
sub _Error
5087
4
4
10
{
5088
# get parameters
5089
my ($parser)=@_;
5090
4
11
5091
# declare base indention
5092
my $baseIndentation=' ' x length('[Error] ');
5093
5094
4
124
# use $_[0]->YYCurtok to display the recognized *token* if necessary
5095
4
19
# - for users convenience, it is suppressed in the message
4
57
5096
warn "\n\n[Error] $sourceFile, ",
5097
${$parser->YYCurval}[1] > 0 ? "line ${$parser->YYCurval}[1]" : 'all sources read',
5098
(exists $statistics{cache} and $statistics{cache}[1]) ? ' (or below because of cache hits)'
5099
4
100
17
: (),
84
100
1201
5100
': found ',
5101
defined ${$parser->YYCurval}[0] ? qq("${$parser->YYCurval}[0]") : 'nothing',
5102
", expected:\n$baseIndentation",
5103
' ' x length('or '),
5104
4
50
66
19
join("\n${baseIndentation}or ",
50
50
5105
map {
5106
exists $tokenDescriptions{$_} ? defined $tokenDescriptions{$_} ? $tokenDescriptions{$_}
5107
: ()
5108
: $_
5109
} sort grep($_!~/cache_hit$/, $parser->YYExpect)
5110
),
5111
".\n\n";
5112
4
17
5113
4
50
520
# visualize error position
4
50
17
4
357
5114
warn(
5115
(map {my $l=$_->[1]; chomp($l); "$baseIndentation$l\n"} reverse @inLine==1 ? @inLine : @inLine[0, -1]), "",
5116
$baseIndentation, ' ' x ($inLine[0][0]-length($parser->{USER}->{INPUT})-1), "^\n",
5117
$baseIndentation, '_' x ($inLine[0][0]-length($parser->{USER}->{INPUT})-1), '|', "\n\n\n"
5118
) if @inLine;
5119
}
5120
5121
# ----------------------------------------------------------------------------------------------
5122
# Internal function: state manager.
5123
# ----------------------------------------------------------------------------------------------
5124
sub _stateManager
5125
1225
1225
2313
{
5126
# get parameter
5127
my ($newState)=@_;
5128
1225
50
100
17946
100
100
100
100
100
100
100
100
100
100
100
100
100
100
100
100
66
5129
# check parameter
5130
confess "[BUG] Invalid new state $newState passed.\n" unless $newState==STATE_DEFAULT
5131
or $newState==STATE_DEFAULT_TAGMODE
5132
or $newState==STATE_TEXT
5133
or $newState==STATE_UPOINT
5134
or $newState==STATE_OPOINT
5135
or $newState==STATE_DPOINT
5136
or $newState==STATE_DPOINT_ITEM
5137
or $newState==STATE_BLOCK
5138
or $newState==STATE_VERBATIM
5139
or $newState==STATE_EMBEDDING
5140
or $newState==STATE_PFILTER
5141
or $newState==STATE_PFILTERED
5142
or $newState==STATE_CONDITION
5143
or $newState==STATE_HEADLINE_LEVEL
5144
or $newState==STATE_HEADLINE
5145
or $newState==STATE_TABLE
5146
or $newState==STATE_DEFINITION
5147
or $newState==STATE_CONTROL
5148
or $newState==STATE_COMMENT;
5149
1225
2210
5150
# store the new state
5151
$parserState=$newState;
5152
5153
1225
100
3164
# enter new state: default
5154
$newState==STATE_DEFAULT and do
5155
530
100
5577
{
5156
# buffer last states lexer flags (take care of a clean init)
5157
%lexerFlagsOfPreviousState=%lexerFlags ? %lexerFlags : (cbell => LEXER_IGNORE);
5158
530
2619
5159
# prepare lexer
5160
@lexerFlags{qw(ils eol el cbell)}=(LEXER_TOKEN, LEXER_EMPTYLINE, LEXER_IGNORE, LEXER_IGNORE);
5161
530
5008
5162
# activate special characters as necessary
5163
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1);
5164
530
50
2632
5165
# trace, if necessary
5166
warn "[Trace] Entered default state.\n" if $flags{trace} & TRACE_SEMANTIC;
5167
530
1463
5168
# well done
5169
return;
5170
};
5171
5172
695
100
1843
# enter new state: paragraph filter installation
5173
$newState==STATE_PFILTER and do
5174
8
39
{
5175
# prepare lexer
5176
@lexerFlags{qw(ils eol el cbell)}=(LEXER_TOKEN, LEXER_EMPTYLINE, LEXER_IGNORE, LEXER_IGNORE);
5177
8
72
5178
# activate special characters as necessary
5179
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1);
5180
8
50
37
5181
# trace, if necessary
5182
warn "[Trace] Entered pfilter installation state.\n" if $flags{trace} & TRACE_SEMANTIC;
5183
8
42
5184
# well done
5185
return;
5186
};
5187
5188
687
100
1795
# enter new state: paragraph filter (similar to default except for the name)
5189
$newState==STATE_PFILTERED and do
5190
8
31
{
5191
# prepare lexer
5192
@lexerFlags{qw(ils eol el cbell)}=(LEXER_TOKEN, LEXER_EMPTYLINE, LEXER_IGNORE, LEXER_IGNORE);
5193
8
54
5194
# activate special characters as necessary
5195
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1);
5196
8
50
45
5197
# trace, if necessary
5198
warn "[Trace] Entered postfilter default state.\n" if $flags{trace} & TRACE_SEMANTIC;
5199
8
19
5200
# well done
5201
return;
5202
};
5203
5204
5205
# enter new state: default in tag mode (same as default, but a paragraph starting with a tag delays switching to other
5206
679
100
1410
# modes, so we have to explicitly disable the paragraph opener specials)
5207
$newState==STATE_DEFAULT_TAGMODE and do
5208
56
219
{
5209
# prepare lexer
5210
@lexerFlags{qw(ils eol el cbell)}=(LEXER_TOKEN, LEXER_EMPTYLINE, LEXER_IGNORE, LEXER_IGNORE);
5211
56
518
5212
# activate special characters as necessary
5213
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0);
5214
56
50
277
5215
# trace, if necessary
5216
warn "[Trace] Entered default state in tag mode.\n" if $flags{trace} & TRACE_SEMANTIC;
5217
56
281
5218
# well done
5219
return;
5220
};
5221
5222
623
100
100
3149
# enter new state: headline body
5223
($newState==STATE_HEADLINE or $newState==STATE_HEADLINE_LEVEL) and do
5224
168
555
{
5225
# prepare lexer
5226
@lexerFlags{qw(ils eol el cbell)}=(LEXER_IGNORE, LEXER_SPACE, LEXER_TOKEN, LEXER_IGNORE);
5227
168
100
11048
5228
# activate special characters as necessary
5229
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, $newState==STATE_HEADLINE ? 0 : 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0);
5230
168
0
789
50
5231
# trace, if necessary
5232
warn "[Trace] Entered headline ", $newState==STATE_HEADLINE ? 'body' : 'level', " state.\n" if $flags{trace} & TRACE_SEMANTIC;
5233
168
320
5234
# well done
5235
return;
5236
};
5237
5238
455
100
1237
# enter new state: comment
5239
$newState==STATE_COMMENT and do
5240
10
29
{
5241
# prepare lexer
5242
@lexerFlags{qw(ils eol el cbell)}=(LEXER_IGNORE, LEXER_EMPTYLINE, LEXER_IGNORE, LEXER_IGNORE);
5243
10
54
5244
# activate special characters as necessary
5245
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
5246
10
50
39
5247
# trace, if necessary
5248
warn "[Trace] Entered comment state.\n" if $flags{trace} & TRACE_SEMANTIC;
5249
10
17
5250
# well done
5251
return;
5252
};
5253
5254
445
100
1153
# enter new state: text
5255
$newState==STATE_TEXT and do
5256
316
1190
{
5257
# prepare lexer
5258
@lexerFlags{qw(ils eol el cbell)}=(LEXER_IGNORE, LEXER_SPACE, LEXER_TOKEN, LEXER_IGNORE);
5259
316
1963
5260
# activate special characters as necessary
5261
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0);
5262
316
50
1111
5263
# trace, if necessary
5264
warn "[Trace] Entered text state.\n" if $flags{trace} & TRACE_SEMANTIC;
5265
316
920
5266
# well done
5267
return;
5268
};
5269
5270
129
100
450
# enter new state: text
5271
$newState==STATE_TABLE and do
5272
7
30
{
5273
# prepare lexer
5274
@lexerFlags{qw(ils eol el cbell)}=(LEXER_SPACE, LEXER_TOKEN, LEXER_TOKEN, LEXER_IGNORE);
5275
7
5817
5276
# activate special characters as necessary
5277
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0);
5278
7
50
630
5279
# trace, if necessary
5280
warn "[Trace] Entered table paragraph state.\n" if $flags{trace} & TRACE_SEMANTIC;
5281
7
18
5282
# well done
5283
return;
5284
};
5285
5286
122
100
451
# enter new state: text
5287
$newState==STATE_DEFINITION and do
5288
7
23
{
5289
# prepare lexer
5290
@lexerFlags{qw(ils eol el cbell)}=(LEXER_IGNORE, LEXER_SPACE, LEXER_TOKEN, LEXER_IGNORE);
5291
7
43
5292
# activate special characters as necessary
5293
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0);
5294
7
50
24
5295
# trace, if necessary
5296
warn "[Trace] Entered macro definition state.\n" if $flags{trace} & TRACE_SEMANTIC;
5297
7
22
5298
# well done
5299
return;
5300
};
5301
5302
115
100
288
# enter new state: unordered list point - defined item
5303
($newState==STATE_DPOINT_ITEM) and do
5304
6
24
{
5305
# prepare lexer
5306
@lexerFlags{qw(ils eol el cbell)}=(LEXER_IGNORE, LEXER_SPACE, LEXER_TOKEN, LEXER_TOKEN);
5307
6
38
5308
# activate special characters as necessary
5309
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0);
5310
6
50
31
5311
# trace, if necessary
5312
warn "[Trace] Entered definition item state.\n" if $flags{trace} & TRACE_SEMANTIC;
5313
6
14
5314
# well done
5315
return;
5316
};
5317
5318
109
100
100
1016
# enter new state: list point
100
5319
($newState==STATE_UPOINT or $newState==STATE_OPOINT or $newState==STATE_DPOINT) and do
5320
27
176
{
5321
# prepare lexer
5322
@lexerFlags{qw(ils eol el cbell)}=(LEXER_IGNORE, LEXER_SPACE, LEXER_TOKEN, qr([*#:]));
5323
27
298
5324
# activate special characters as necessary
5325
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0);
5326
27
50
159
5327
# trace, if necessary
5328
warn "[Trace] Entered point state.\n" if $flags{trace} & TRACE_SEMANTIC;
5329
27
45
5330
# well done
5331
return;
5332
};
5333
5334
82
100
243
# enter new state: block
5335
$newState==STATE_BLOCK and do
5336
25
123
{
5337
# prepare lexer
5338
@lexerFlags{qw(ils eol el cbell)}=(LEXER_SPACE, LEXER_TOKEN, LEXER_TOKEN, 'Ils');
5339
25
254
5340
# activate special characters as necessary
5341
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0);
5342
25
50
124
5343
# trace, if necessary
5344
warn "[Trace] Entered block state.\n" if $flags{trace} & TRACE_SEMANTIC;
5345
25
52
5346
# well done
5347
return;
5348
};
5349
5350
57
100
165
# enter new state: verbatim block
5351
$newState==STATE_VERBATIM and do
5352
12
46
{
5353
# prepare lexer
5354
@lexerFlags{qw(ils eol el cbell)}=(LEXER_SPACE, LEXER_TOKEN, LEXER_TOKEN, LEXER_IGNORE);
5355
12
74
5356
# activate special characters as necessary
5357
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0);
5358
12
50
65
5359
# trace, if necessary
5360
warn "[Trace] Entered verbatim state.\n" if $flags{trace} & TRACE_SEMANTIC;
5361
12
28
5362
# well done
5363
return;
5364
};
5365
5366
45
100
136
# enter new state: embedding
5367
$newState==STATE_EMBEDDING and do
5368
28
93
{
5369
# prepare lexer
5370
@lexerFlags{qw(ils eol el cbell)}=(LEXER_SPACE, LEXER_TOKEN, LEXER_TOKEN, LEXER_IGNORE);
5371
28
163
5372
# activate special characters as necessary
5373
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0);
5374
28
50
138
5375
# trace, if necessary
5376
warn "[Trace] Entered embedding state.\n" if $flags{trace} & TRACE_SEMANTIC;
5377
28
60
5378
# well done
5379
return;
5380
};
5381
5382
17
100
43
# enter new state: condition (very similar to embedding, naturally)
5383
$newState==STATE_CONDITION and do
5384
12
49
{
5385
# prepare lexer
5386
@lexerFlags{qw(ils eol el cbell)}=(LEXER_SPACE, LEXER_SPACE, LEXER_TOKEN, LEXER_IGNORE);
5387
12
67
5388
# activate special characters as necessary
5389
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
5390
12
50
36
5391
# trace, if necessary
5392
warn "[Trace] Entered condition state.\n" if $flags{trace} & TRACE_SEMANTIC;
5393
12
19
5394
# well done
5395
return;
5396
};
5397
5398
5
50
15
# enter new state: unordered list point
5399
$newState==STATE_CONTROL and do
5400
5
17
{
5401
# prepare lexer
5402
@lexerFlags{qw(ils eol el cbell)}=(LEXER_IGNORE, LEXER_IGNORE, LEXER_TOKEN, LEXER_IGNORE);
5403
5
29
5404
# activate special characters as necessary
5405
@specials{('.', '/', '*', '#', '=', '<', '>', '{', '}' , '-', '?', '@', '+', '~', 'heredoc', 'colon', 'tag', 'embedded', 'number', 'pfilter')}=(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
5406
5
50
16
5407
# trace, if necessary
5408
warn "[Trace] Entered control state.\n" if $flags{trace} & TRACE_SEMANTIC;
5409
5
16
5410
# well done
5411
return;
5412
};
5413
0
0
5414
# check yourself
5415
confess "[BUG] Unhandled state $newState.\n";
5416
}
5417
5418
5419
=pod
5420
5421
=head2 run()
5422
5423
This function starts the parser to process a number of specified files.
5424
5425
B
5426
All parameters except of the I parameter are named (pass them by hash).
5427
5428
=over 4
5429
5430
=item activeBaseData
5431
5432
This optional parameter allows to pass common data to all active contents
5433
(conditions, embedded and included Perl) by a I. By convention,
5434
a translator at least passes the target language and user settings by
5435
5436
activeBaseData => {
5437
targetLanguage => "lang",
5438
userSettings => \%userSettings,
5439
},
5440
5441
User settings are intended to allow the specification of per call settings by a
5442
user, e.g. to include special parts. By using this convention, users can easily
5443
specify such a part the following way
5444
5445
? flagSet('setting')
5446
5447
Special part.
5448
5449
? 1
5450
5451
It is up to a translator author to declare translator specific settings (and to
5452
document them). The passed values can be as complex as necessary as long as they
5453
can be duplicated by C.
5454
5455
Whenever active contents is invoked, the passed hash reference is copied
5456
(duplicated by C) into the Safe objects namespace
5457
(see I) as a global variable $PerlPoint. This way, modifications by
5458
invoked code do not effect subsequently called code snippets, base data are
5459
always fresh.
5460
5461
=item activeDataInit
5462
5463
Reserved to pass hook functions to be called preparing every active contents
5464
invokation. I
5465
5466
=item cache
5467
5468
This optional parameter controls source file paragraph caching.
5469
5470
By default, a source file is parsed completely everytime you pass it to the
5471
parser. This is no problem with tiny sources but can delay your work if you
5472
are dealing with large sources which have to be translated periodically into
5473
presentations while they are written. Typically most of the paragraphs remain
5474
unchanged from version to version, but nevertheless everything is usually
5475
reparsed which means a waste of time. Well, to improve this a paragraph
5476
cache can be activated by setting this option to B.
5477
5478
The parser caches each I individually. That means
5479
if three files are passed to the parser with activated caching, three cache
5480
files will be written. They are placed in the source file directory, named
5481
..ppcache. Please note that the paragraphs of I sources
5482
are cached in the cache file of the I document because they may have to
5483
be evaluated differently depending on inclusion context.
5484
5485
What acceleration can be expected? Well, this I
5486
depends on your source structure. Efficiency will grow with longer paragraphs,
5487
reused paragraphs and paragraph number. It will be reduced by heavy usage
5488
of active contents and embedding because every paragraph that refers
5489
to parts defined externally is not strongly determined by itself and therefore
5490
it cannot be cached. Here is a list of all reasons which cause a paragraph to
5491
be excluded from caching:
5492
5493
=over 4
5494
5495
=item Embedded parts
5496
5497
Obviously dynamic parts may change from one version to another, but even static
5498
parts could have to be interpreted differently because a user can set up new
5499
Is.
5500
5501
=item Included files
5502
5503
An \INCLUDE tag immediately disables caching for the paragraph it resides in
5504
because the loaded file may change its contents. This is not really a
5505
restriction because the included paragraphs themselves I cached if possible.
5506
5507
=item Filtered paragraphs
5508
5509
A paragraph filter can transform a source paragraph in whatever the author of
5510
a Perl function might think is useful, potentially depending on highly dynamical
5511
data. So it cannot be determined by the parser what the final translation of a
5512
certain source paragraph will be.
5513
5514
=item Document stream entry points
5515
5516
Depending on the parsers configuration, these points can be transformed into
5517
headlines or remain unchanged, so there is no fixed up mapping between a
5518
source paragraph and its streamed expression.
5519
5520
=back
5521
5522
Even with these restrictions about 70% of a real life document of more than
5523
150 paragraphs could be cached. This saved more than 60% of parsing time in
5524
subsequent translator calls.
5525
5526
New cache entries are always I which means that old entries are never
5527
replaced and a cache file tends to grow. If you ever wish to clean up a
5528
cache file completely pass B to this option.
5529
5530
To deactivate caching explicitly pass B.
5531
I
5532
5533
Settings can be combined by I.
5534
5535
# clean up the cache, then refill it
5536
cache => CACHE_CLEANUP+CACHE_ON,
5537
5538
# clean up the cache and deactivate it
5539
cache => CACHE_CLEANUP+CACHE_OFF,
5540
5541
The B value is overwritten by any other setting.
5542
5543
It is suggested to make this setting available to translator users to let
5544
them decide if a cache should be used.
5545
5546
I that there is a problem with line numbers if paragraphs are
5547
restored from cache because of the behaviour of perls paragraph mode. In this
5548
mode, the <> operator reads in any number of newlines between paragraphs but
5549
supplies only one of them. That is why I do not get the real number of lines
5550
in a paragraph and therefore cannot store them. To work around this, two
5551
strategies can be used. First, do not use more than exactly one newline
5552
between paragraphs. (This strategy is not for real life users, of course,
5553
but in this case restored numbers would be correct.) Second, remember that
5554
source line numbers are only interesting in error messages. If the parser
5555
detects an error, it therefore says: error "there or later" when a cache hit
5556
already occured. If the real number is wished the parser could be reinvoked
5557
then with deactivated cache and will report it.
5558
5559
I occurs if you parse on a UNIX
5560
system but your document (or parts of it) were written in DOS format. The
5561
paragraph mode reads such a document I. Please replace the line
5562
ending character sequences system appropriate. (If you are using C
5563
under Solaris please invoke it with option C<-ascii> to do this.)
5564
5565
More, Perls paragraph mode and PerlPoint treat whitespace lines differently.
5566
Because of the way it works, paragraph mode does not recognize them as "empty"
5567
while PerlPoint I for reasons of usability (invisible characters should
5568
not make a difference). This means that lines containing only whitespaces
5569
separate PerlPoint paragraphs but not "Perl" paragraphs, making the cache
5570
working wrong especially in examples. If paragraphs unintentionally disappear
5571
in the resulting presentation, please check the "empty lines" before them.
5572
5573
Consistent cache data depend on the versions of the parser, of constant
5574
declarations and of the module B which is used internally. If the
5575
parser detects a significant change in one of these versions, existing
5576
caches are automatically rebuilt.
5577
5578
I cache files are not locked while they are used.
5579
If you need this feature please let me know.
5580
5581
=item criticalSemanticErrors
5582
5583
If set to a true value, semantic errors will cause the parser to terminate
5584
immediately. This defaults to false: errors are accumulated and finally
5585
reported.
5586
5587
=item display
5588
5589
This parameter is optional. It controls the display of runtime messages
5590
like informations or warnings. By default, all messages are displayed. You
5591
can suppress these informations partially or completely by passing one or
5592
more of the "DISPLAY_..." variables declared in B.
5593
Constants should be combined by addition.
5594
5595
=item docstreams2skip
5596
5597
by default, all document streams are made part of the result, but by this
5598
parameter one can I certain streams (all remaining ones will be
5599
streamed as usual).
5600
5601
The list should be supplied by an array reference.
5602
5603
It is suggested to take the values of this parameter from a user option,
5604
which by convention should be named C<-skipstream>.
5605
5606
=item docstreaming
5607
5608
specifies the way the parser handles stream entry points. The value passed
5609
might be either C, C or C.
5610
5611
C instructs the parser to transform the entry points
5612
into I, one level below the current real headline level. This
5613
is an easy to implement and convenient way of docstream handling seems to
5614
make sense in most target formats.
5615
5616
C hides all streams except of the main stream. The effect
5617
is similar to a call with I set for all document streams
5618
in a source.
5619
5620
C treats the entry points as entry points and streams
5621
them as such. This is the default if the parameter is omitted.
5622
5623
Please note that filters applied by I work regardless of
5624
the I configuration which only affects the way the parser
5625
passes docstream data to a backend.
5626
5627
It is recommended to take the value of this parameter from a user option,
5628
which by convention should be named C<-docstreaming>. (A converter can
5629
define various more modes than provided by the parser and implement them
5630
itself, of course. See C for a reference implementation.)
5631
5632
5633
=item files
5634
5635
a reference to an array of files to be scanned.
5636
5637
Files are treated as PerlPoint sources except when their name has the
5638
prefix C, as in C. With this prefix, the
5639
parser tries to automatically tranform the source into PerlPoint,
5640
using a standard import filter for the format indicated by the file
5641
extension (C in our example). The filter must be installed as
5642
Cuppercased format nameE>, e.g.
5643
C.
5644
5645
=item filter
5646
5647
a regular expression describing the target language. This setting, if used,
5648
prevents all embedded or included source code of other languages than the set
5649
one from inclusion into the generated stream. This accelerates both parsing
5650
and backend handling. The pattern is evaluated case insensitively.
5651
5652
Example: pass "html|perl" to allow HTML and Perl.
5653
5654
To illustrate this, imagine a translator to PostScript. If it reads a Perl
5655
Point file which includes native HTML, this translator cannot handle such code.
5656
The backend would have to skip the HTML statements. With a "PostScript" filter,
5657
the HTML code will not appear in the stream.
5658
5659
This enables PerlPoint texts prepared for various target languages. If an
5660
author really needs plain target language code to be embedded into PerlPoint,
5661
he could provide versions for various languages. Translators using a filter
5662
will then receive exactly the code of their target language, if provided.
5663
5664
Please note that you cannot filter out PerlPoint code or example files.
5665
5666
By default, no filter is set.
5667
5668
5669
=item headlineLinks
5670
5671
this optional flag causes the parser to register all headline
5672
titles as anchors automatically. (Headlines are stored without
5673
possibly included tags which are stripped off.)
5674
5675
Registering anchors does \I mean there are anchors included
5676
to the stream, it just means that they are known to exist at
5677
parsing time because they are added to an internal C
5678
object which is passed to all tag hooks and can be evaluated there.
5679
See \C and C for details.
5680
5681
It is recommended to make use of this feature if your converter
5682
automatically makes headlines an anchor named like the headline
5683
(this feature was introduced by Lorenz Domkes C initially).
5684
(Nevertheless, usefulness may depend on dealing with the parsers
5685
anchor collection in tag hooks. See the documentations of used
5686
tag modules for details.)
5687
5688
If your converter does not support automatic headline anchors
5689
the mentioned way, it is recommended to omit this option because
5690
it could confuse tag hooks that evaluate the parsers anchor collection.
5691
5692
5693
=item libpath
5694
5695
An optional reference to an array of library pathes to be searched for
5696
files specified by \INCLUDE tags. This array is intended to be filled
5697
by directories specified via an converter option. By convention, this
5698
option is named C and should be enabled multiple times
5699
(C).
5700
5701
Please note that library pathes can be set via environment variable
5702
C as well, but directories specified via C are
5703
searched I.
5704
5705
5706
=item linehints
5707
5708
If set to a true value, the parser will embed line hints into the stream
5709
whenever a new source line begins.
5710
5711
A line hint directive is provided as
5712
5713
[
5714
DIRECTIVE_NEW_LINE, DIRECTIVE_START,
5715
{file=>filename, line=>number}
5716
]
5717
5718
and is suggested to be handled by a backend callback.
5719
5720
Please note that currently source line numbers are not guaranteed to be
5721
correct if stream parts are restored from I (see there for details).
5722
5723
The default value is 0.
5724
5725
=item nestedTables
5726
5727
This is an optional flag which is by default set to 0, indicating if the parser
5728
shall accept nested tables or not. Table nesting can produce very nice results
5729
if it is supported by the target language. HTML, for example, allows to nest
5730
tables, but other languages I. So, using this feature can really improve
5731
the results if a user is focussed on supporting certain target formats only. If I want
5732
to produce nothing but HTML, why should I take care of target formats not able
5733
to handle table nesting? On the other hand, I a document shall be translated
5734
into several formats, it might cause trouble to nest tables therein.
5735
5736
Because of this, it is suggested to let converter users decide if they want to
5737
enable table nesting or not. If the target format does not support nesting, I
5738
recommend to disable nesting completely.
5739
5740
5741
=item object
5742
5743
the parser object made by I;
5744
5745
=item safe
5746
5747
an object of the B class which comes with perl. It is used to evaluate
5748
embedded Perl code in a safe environment. By letting the caller of I
5749
provide this object, a translator author can make the level of safety fully
5750
configurable by users. Usually, the following should work
5751
5752
use Safe;
5753
...
5754
$parser->run(safe=>new Safe, ...);
5755
5756
Safe is a really good module but unfortunately limited in loading modules
5757
transparently. So if a user wants to use modules in his embedded code, he
5758
might fail to get it working in a Safe compartment. If safety does not matter,
5759
he can decide to execute it without Safe, with full Perl access. To switch
5760
on this mode, pass a true scalar value (but no reference) instead of a Safe
5761
object.
5762
5763
To make all PerlPoint converters behave similarly, it is recommended to provide
5764
two related options C<-activeContents> and C<-safeOpcode>. C<-activeContents>
5765
should flag that active contents shall be evaluated, while C<-safeOpcode>
5766
controls the level of security. A special level C should mean that all
5767
code can b executed without any restriction, while any other settings should be
5768
treated as an opcode to configure the Safe object. So, the recommended rules
5769
are: pass 0 unless C<-activeContents> is set. Pass 1 if the converter was
5770
called with C<-activeContents> I C<-safeOpcode ALL>. Pass a Safe object
5771
and configure it according to the users C<-safeOpcode> settings if
5772
C<-activeContents> is used but without C<-safeOpcode ALL>. See C
5773
for an implementation example.
5774
5775
Active Perl contents is I if this setting is omitted or if anything
5776
else than a B object is passed. (There are currently three types of active
5777
contents: embedded or included Perl and condition paragraphs.)
5778
5779
5780
=item predeclaredVars
5781
5782
Variables are usually set by assignment paragraphs. However, it may be useful
5783
for a converter to predeclare a set of them to provide certain settings to the
5784
users. Predeclared variables, as any other PerlPoint variables, can be used
5785
both in pure PerlPoint and in active contents. To help users distinguish them
5786
from user defined vars, their names will be I.
5787
5788
Just pass a hash of variable name / value pairs:
5789
5790
$parser->run(
5791
...
5792
predeclaredVars => {
5793
CONVERTER_NAME => 'pp2xy',
5794
CONVERTER_VERSION => $VERSION,
5795
...
5796
},
5797
);
5798
5799
Non capitalized variable names will be capitalized without further notice.
5800
5801
Please note that variables currently can only be scalars. Different data types
5802
will not be accepted by the parser.
5803
5804
Predeclared variables should be mentioned in the converters documentation.
5805
5806
The parser itself makes use of this feature by declaring C<_PARSER_VERSION>
5807
(the version of this module used to parse the source) and _STARTDIR (the full
5808
path of the startup directory, as reported by C).
5809
5810
C needs C to take effect.
5811
5812
5813
=item skipcomments
5814
5815
By default comments are streamed and can be converted into comments of the target language.
5816
But often they are of limited use in generated files: especially if they are intended to
5817
help the author of a document, not the reader of the source of generated results. So with
5818
this option one can suppress comments from being streamed.
5819
5820
It is suggested to get this setting via user option,
5821
which by convention should be named C<-skipcomments>.
5822
5823
=item stream
5824
5825
A reference to an array where the generated output stream should be stored in.
5826
5827
Application programmers may want to tie this array if the target ASCII
5828
texts are expected to be large (long ASCII texts can result in large stream
5829
data which may occupy a lot of memory). Because of the fact that the parser
5830
stores stream data I, memory consumption can be reduced
5831
significantly by tying the stream array.
5832
5833
It is recommended to pass an empty array. Stored data will not be overwritten,
5834
the parser I its data instead (by C).
5835
5836
=item trace
5837
5838
This parameter is optional. It is intended to activate trace code while the method
5839
runs. You may pass any of the "TRACE_..." constants declared in B,
5840
combined by addition as in the following example:
5841
5842
# show the traces of both
5843
# lexical and syntactical analysis
5844
trace => TRACE_LEXER+TRACE_PARSER,
5845
5846
If you omit this parameter or pass TRACE_NOTHING, no traces will be displayed.
5847
5848
=item var2stream
5849
5850
If set to a true value, the parser will propagate variable settings into the stream
5851
by adding additional C directives.
5852
5853
A variable propagation has the form
5854
5855
[
5856
DIRECTIVE_VARSET, DIRECTIVE_START,
5857
{var=>varname, value=>value}
5858
]
5859
5860
and is suggested to be handled by a backend callback.
5861
5862
The default value is 0.
5863
5864
=item vispro
5865
5866
activates "process visualization" which simply means that a user will see
5867
progress messages while the parser processes documents. The I
5868
value of this setting determines how often the progress message shall be
5869
updated, by a I:
5870
5871
# inform every five chapters
5872
vispro => 5,
5873
5874
Process visualization is automatically suppressed unless STDERR is
5875
connected to a terminal, if this option is omitted, I was set
5876
to C or parser Is are activated.
5877
5878
=back
5879
5880
B
5881
A "true" value in case of success, "false" otherwise. A call is performed
5882
successfully if there was neither a syntactical nor a semantic error in the
5883
parsed files.
5884
5885
B
5886
5887
$parser->run(
5888
stream => \@streamData,
5889
files => \@ARGV,
5890
filter => 'HTML',
5891
cache => CACHE_ON,
5892
trace => TRACE_PARAGRAPHS,
5893
);
5894
5895
=cut
5896
sub run
5897
36
36
1
147709
{
5898
# get parameters
5899
my ($me, @pars)=@_;
5900
36
50
215
5901
36
312
# build parameter hash
5902
confess "[BUG] The number of parameters should be even.\n" if @pars%2;
5903
my %pars=@pars;
5904
36
50
370
5905
36
50
33
391
# and check parameters
5906
36
50
187
confess "[BUG] Missing object parameter.\n" unless $me;
5907
36
50
33
353
confess "[BUG] Object parameter is no ", __PACKAGE__, " object.\n" unless ref $me and ref $me eq __PACKAGE__;
5908
36
50
170
confess "[BUG] Missing stream array reference parameter.\n" unless $pars{stream};
5909
36
50
33
384
confess "[BUG] Stream array reference parameter is no array reference.\n" unless ref $pars{stream} and ref $pars{stream} eq 'ARRAY';
5910
36
50
75
confess "[BUG] Missing file list reference parameter.\n" unless $pars{files};
36
188
5911
36
50
66
334
confess "[BUG] File list reference parameter is no array reference.\n" unless ref $pars{files} and ref $pars{files} eq 'ARRAY';
5912
36
50
33
217
confess "[BUG] You should pass at least one file to parse.\n" unless @{$pars{files}};
5913
36
50
66
247
confess "[BUG] Active base data reference is no hash reference.\n" if exists $pars{activeBaseData} and ref $pars{activeBaseData} ne 'HASH';
5914
36
100
183
confess "[BUG] Active data initializer is no code reference.\n" if exists $pars{activeDataInit} and ref $pars{activeDataInit} ne 'CODE';
5915
confess "[BUG] Document stream skip list is no array reference.\n" if exists $pars{docstreams2skip} and ref $pars{docstreams2skip} ne 'ARRAY';
5916
9
1094
if (exists $pars{filter})
5917
9
50
61
{
5918
eval "'lang'=~/$pars{filter}/";
5919
confess qq([BUG] Invalid filter expression "$pars{filter}": $@.\n) if $@;
5920
}
5921
36
124
5922
# variables
5923
my ($rc, %docHints)=(1);
5924
5925
5
95
# init internal data
5926
(
5927
$resultStreamRef, # 1
5928
$safeObject, # 2
5929
$flags{trace}, # 3
5930
$flags{display}, # 4
5931
$flags{filter}, # 5
5932
$flags{linehints}, # 6
5933
$flags{var2stream}, # 7
5934
$flags{cache}, # 8
5935
$flags{cached}, # 9
5936
$flags{vis}, # 10
5937
$flags{activeBaseData}, # 11
5938
$flags{activeDataInit}, # 12
5939
$flags{nestedTables}, # 13
5940
$flags{headlineLinks}, # 14
5941
$flags{skipcomments}, # 15
5942
$flags{docstreams2skip}, # 16
5943
$flags{docstreaming}, # 17
5944
$flags{criticalSemantics}, # 18
5945
$macroChecksum, # 19
5946
$varChecksum, # 20
5947
$anchors, # 21
5948
)=(
5949
$pars{stream}, # 1
5950
( # 2
5951
exists $pars{safe}
5952
and defined $pars{safe}
5953
) ? ref($pars{safe}) eq 'Safe' ? $pars{safe}
5954
: 1
5955
: 0,
5956
exists $pars{trace} ? $pars{trace} : TRACE_NOTHING, # 3
5957
exists $pars{display} ? $pars{display} : DISPLAY_ALL, # 4
5958
exists $pars{filter} ? $pars{filter} : '', # 5
5959
(exists $pars{linehints} and $pars{linehints}), # 6
5960
(exists $pars{var2stream} and $pars{var2stream}), # 7
5961
exists $pars{cache} ? $pars{cache} : CACHE_OFF, # 8
5962
0, # 9
5963
exists $pars{vispro} ? $pars{vispro} : 0, # 10
5964
exists $pars{activeBaseData} ? $pars{activeBaseData} : 0, # 11
5965
exists $pars{activeDataInit} ? $pars{activeDataInit} : 0, # 12
5966
exists $pars{nestedTables} ? $pars{nestedTables} : 0, # 13
5967
36
100
100
1956
exists $pars{headlineLinks} ? $pars{headlineLinks} : 0, # 14
5
100
66
17
50
66
50
33
100
100
50
100
50
100
100
100
100
50
5968
(exists $pars{skipcomments} and $pars{skipcomments}), # 15
5969
exists $pars{docstreams2skip} ? {map {($_ => undef)} @{$pars{docstreams2skip}}} : 0, # 16
5970
exists $pars{docstreaming} ? $pars{docstreaming} : DSTREAM_DEFAULT, # 17
5971
exists $pars{criticalSemanticErrors} ? $pars{criticalSemanticErrors} : 0, # 18
5972
0, # 19
5973
0, # 20
5974
PerlPoint::Anchors->new, # 21
5975
);
5976
36
100
66
367
5977
# prepare stream data structure and appropriate handlers
5978
unless (@$resultStreamRef and $resultStreamRef->[STREAM_IDENT] eq '__PerlPoint_stream__')
5979
35
513
{
5980
# empty stream
5981
35
128
@$resultStreamRef=();
35
801
5982
# initiate
5983
@{$resultStreamRef}[
5984
STREAM_IDENT,
5985
STREAM_TOKENS,
5986
STREAM_HEADLINES,
5987
]=(
5988
'__PerlPoint_stream__', # stream identifier;
5989
[], # base stream;
5990
[], # headline stream;
5991
);
5992
}
5993
36
100
324
5994
# declare helper subroutines to be used in active contents
5995
18
52
if ($safeObject)
5996
{
5997
my $code=<<'EOC';
5998
5999
unless (defined *main::flagSet{CODE})
6000
{
6001
# check if at least one of a set of flags is set
6002
# - define functions anonymously to avoid redefinition in case the condition is not matched
6003
*main::flagSet=sub
6004
{
6005
# declare and init variable
6006
my $rc=0;
6007
6008
# check flags
6009
foreach (@_)
6010
{$rc=1, last if exists $PerlPoint->{userSettings}{$_};}
6011
6012
# supply result
6013
$rc;
6014
};
6015
6016
# provide the value of a PerlPoint variable
6017
# - define function anonymously to avoid redefinition in case the condition is not matched
6018
*main::varValue=sub {${join('::', 'main', $_[0])};};
6019
}
6020
6021
# complete compartment code
6022
18
100
3
393
EOC
3
29
3
11
3
585
6023
18
50
9590
6024
ref($safeObject) ? $safeObject->reval($code) : eval("{package main; no strict; $code}");
6025
die "[BUG] Bug in function definition, please inform developer: $@" if $@;
6026
}
6027
36
294864
6028
# predeclare variables
6029
_predeclareVariables({_PARSER_VERSION=>$PerlPoint::Parser::VERSION, _STARTDIR=>cwd()});
6030
36
100
1117
6031
# store initial variables, if necessary
6032
if (exists $pars{predeclaredVars})
6033
2
50
21
{
6034
# check data format
6035
confess "[BUG] Please pass predeclared variables by a hash reference .\n" unless ref($pars{predeclaredVars}) eq 'HASH';
6036
2
14
6037
# declare
6038
_predeclareVariables($pars{predeclaredVars});
6039
}
6040
36
0
33
576
33
0
6041
# update visualization flag
6042
$flags{vis}=0 unless $flags{vis}
6043
and not $flags{display} & &DISPLAY_NOINFO
6044
and not $flags{trace}>TRACE_NOTHING
6045
and -t STDERR;
6046
36
1003
6047
36
165
# init more
6048
36
100
608
@flags{qw(skipInput headlineLevelOffset headlineLevel olist virtualParagraphStart)}=(0) x 5;
6049
delete $flags{ifilters};
6050
$statistics{cache}[1]=0 if $flags{cache} & CACHE_ON;
6051
36
3496
6052
# init even more
6053
%paragraphTypeStrings=(
6054
DIRECTIVE_HEADLINE() => 'headline',
6055
DIRECTIVE_TEXT() => 'text',
6056
DIRECTIVE_UPOINT() => 'unordered list point',
6057
DIRECTIVE_ULIST() => 'list',
6058
DIRECTIVE_OPOINT() => 'ordered list point',
6059
DIRECTIVE_OLIST() => 'list',
6060
DIRECTIVE_DPOINT() => 'definition list point',
6061
DIRECTIVE_DLIST() => 'list',
6062
DIRECTIVE_BLOCK() => 'block',
6063
DIRECTIVE_VERBATIM() => 'verbatim block',
6064
DIRECTIVE_TAG() => 'tag',
6065
DIRECTIVE_LIST_RSHIFT() => 'right list shifter',
6066
DIRECTIVE_LIST_LSHIFT() => 'left list shifter',
6067
DIRECTIVE_COMMENT() => 'comment',
6068
36
100
292
);
6069
# check tag declarations
6070
unless (ref($PerlPoint::Tags::tagdefs) eq 'HASH')
6071
28
50
303
{
6072
# warn user
6073
warn "[Warn] No tags are declared. No tags will be detected.\n" unless $flags{display} & DISPLAY_NOWARN;
6074
28
209
6075
# init shortcut pointer
6076
$tagsRef={};
6077
}
6078
else
6079
8
48
{
6080
# ok, there are tags, make a shortcut
6081
$tagsRef=$PerlPoint::Tags::tagdefs;
6082
}
6083
6084
36
50
66
724
# build an array of include pathes - specified via environment variable PERLPOINTLIB
6085
1
38
# and parameter "libpath"
6086
36
100
552
confess "[BUG] Please pass library pathes by an array reference .\n" if exists $pars{libpath} and not ref($pars{libpath}) eq 'ARRAY';
100
6087
push(@libraryPath,
6088
exists $pars{libpath} ? @{$pars{libpath}} : (),
6089
exists $ENV{PERLPOINTLIB} ? split(/\s*;\s*/, $ENV{PERLPOINTLIB}) : (),
6090
);
6091
36
50
369
6092
# welcome user
6093
0
0
unless ($flags{display} & DISPLAY_NOINFO)
6094
{
6095
34
34
394
print STDERR "[Info] The PerlPoint parser ";
34
103
34
206143
0
0
6096
0
0
{
0
0
6097
no strict 'refs';
6098
0
0
print STDERR ${join('::', __PACKAGE__, 'VERSION')};
6099
0
0
0
}
0
6100
warn " starts.\n";
6101
warn " Active contents is ", $safeObject ? ref($safeObject) ? 'safely evaluated' : 'risky evaluated' : 'ignored', ".\n";
6102
0
0
0
6103
# report cache mode
6104
warn " Paragraph cache is ", ($flags{cache} & CACHE_ON) ? '' : 'de', "activated.\n";
6105
}
6106
36
302137
6107
# save current directory
6108
my $startupDirectory=cwd();
6109
36
1455
36
1209
6110
# scan all input files
6111
foreach my $file (@{$pars{files}})
6112
37
675
{
6113
# scopies
6114
my $specifiedFile=$file;
6115
37
50
994
6116
# scan for an import directive
6117
if ($file=~/^IMPORT:(.+)/)
6118
0
0
0
{
6119
0
0
# replace the original file by a temporary one ...
6120
my ($tmpHandle, $tmpFilename)=tempfile(UNLINK => ($flags{trace} & TRACE_TMPFILES ? 0 : 1));
6121
$file=$tmpFilename;
6122
0
0
6123
0
0
# which imports the real file
6124
0
0
my $realfile=abs_path($specifiedFile=$1);
6125
print $tmpHandle qq(\n\n\\INCLUDE{import=1 file="$realfile"}\n\n);
6126
close($tmpHandle);
6127
0
0
0
0
6128
# due to the extra level chances are we have to to accept a first paragraph that is not a headline
6129
$flags{complainedAbout1stHeadline}='IMPORT' unless exists $flags{complainedAbout1stHeadline} and $flags{complainedAbout1stHeadline} eq '1';
6130
}
6131
37
50
967
6132
# inform user
6133
warn "[Info] Processing $specifiedFile ...\n" unless $flags{display} & DISPLAY_NOINFO;
6134
37
511
6135
# init input stack
6136
@inputStack=([]);
6137
37
12287
6138
# init nesting stack
6139
@nestedSourcefiles=($file);
6140
37
1666
6141
# update source file nesting level hint
6142
_predeclareVariables({_SOURCE_LEVEL=>scalar(@nestedSourcefiles)});
6143
37
246
6144
# update file hint
6145
$sourceFile=$file;
6146
37
50
5419
6147
37
309
# open file and make the new handle the parsers input
6148
open($inHandle, $file) or confess("[Fatal] Could not open input file $file.\n");
6149
binmode($inHandle);
6150
6151
37
595
# store the filename in the list of opened sources, to avoid circular reopening
6152
# (it would be more perfect to store the complete path, is there a module for this?)
6153
$openedSourcefiles{$file}=1;
6154
37
7165
6155
# change into the source directory
6156
chdir(dirname($file));
6157
37
2395
6158
37
100
100
565
# (cleanup and) read old checksums as necessary
6159
my $cachefile=sprintf(".%s.ppcache", basename($file));
6160
1
50
8
if (($flags{cache} & CACHE_CLEANUP) and -e $cachefile)
6161
1
244
{
6162
warn " Resetting paragraph cache for $specifiedFile.\n" unless $flags{display} & DISPLAY_NOINFO;
6163
37
100
100
466
unlink($cachefile);
6164
}
6165
1
185
if (($flags{cache} & CACHE_ON) and -e $cachefile)
6166
{
6167
$checksums=retrieve($cachefile) ;
6168
#use Data::Dumper; warn Dumper($checksums);
6169
1
50
33
2739
33
33
33
33
6170
# clean up old format caches
6171
unless (
6172
exists $checksums->{sha1_base64('version')}
6173
and $checksums->{sha1_base64('version')}>=0.38
6174
6175
and exists $checksums->{sha1_base64('constants')}
6176
and $checksums->{sha1_base64('constants')}==$PerlPoint::Constants::VERSION
6177
6178
and exists $checksums->{sha1_base64('Storable')}
6179
and $checksums->{sha1_base64('Storable')}==$Storable::VERSION
6180
0
0
0
)
6181
0
0
{
6182
0
0
warn " Paragraph cache for $specifiedFile is rebuilt because of an old format.\n" unless $flags{display} & DISPLAY_NOINFO;
6183
unlink($cachefile);
6184
$checksums={};
6185
}
6186
}
6187
37
100
445
6188
# store cache builder version and constant declarations version
6189
2
40
if ($flags{cache} & CACHE_ON)
6190
2
23
{
6191
2
26
$checksums->{sha1_base64('version')}=$PerlPoint::Parser::VERSION;
6192
$checksums->{sha1_base64('constants')}=$PerlPoint::Constants::VERSION;
6193
$checksums->{sha1_base64('Storable')}=$Storable::VERSION;
6194
}
6195
37
201
37
1857
6196
# store a document start directive (done here to save memory)
6197
push(@{$resultStreamRef->[STREAM_TOKENS]}, [\%docHints, DIRECTIVE_DOCUMENT, DIRECTIVE_START, basename($file)]);
6198
37
177
37
313
6199
# update tag finish memory by the way
6200
_updateTagFinishMem(scalar(@{$resultStreamRef->[STREAM_TOKENS]}));
6201
37
643
6202
# enter first (and most common) lexer state
6203
_stateManager(STATE_DEFAULT);
6204
37
100
229
6205
# flag that the next paragraph can be checksummed, if so
6206
$flags{checksum}=1 if $flags{cache} & CACHE_ON;
6207
37
50
293
6208
# set a timestamp, if helpful
6209
$flags{started}=time unless $flags{display} & DISPLAY_NOINFO;
6210
37
66
2931
6211
# parse input
6212
$rc=($rc and $me->YYParse(yylex=>\&_lexer, yyerror=>\&_Error, yydebug => ($flags{trace} & TRACE_PARSER) ? 0x1F : 0x00));
6213
37
50
308
6214
# stop time, if necessary
6215
warn "\n $specifiedFile was parsed in ", time-$flags{started}, " seconds.\n" unless $flags{display} & DISPLAY_NOINFO;
6216
37
76
37
2142
6217
# store a document completion directive (done here to save memory)
6218
push(@{$resultStreamRef->[STREAM_TOKENS]}, [\%docHints, DIRECTIVE_DOCUMENT, DIRECTIVE_COMPLETE, basename($file)]);
6219
37
108
6220
# reset the input handle and flags
6221
$readCompletely=0;
6222
37
50
66
431
66
33
6223
# store checksums, if necessary
6224
store($checksums, $cachefile) if ($flags{cache} & CACHE_ON)
6225
and $flags{cached}
6226
and defined $checksums and %$checksums;
6227
37
6969
6228
37
1090
# close the input file
6229
close($inHandle);
6230
$inHandle=new IO::File;
6231
37
6209
6232
# back to startup directory
6233
chdir($startupDirectory);
6234
}
6235
6236
36
1273
6237
# make a simple helper backend object
6238
my $helperBackend=new PerlPoint::Backend(
6239
name => 'parsers helper backend',
6240
display => DISPLAY_NOINFO+DISPLAY_NOWARN,
6241
6242
trace => TRACE_NOTHING,
6243
);
6244
36
949
6245
36
419
# get toc
6246
$helperBackend->bind($resultStreamRef);
6247
my $toc=$helperBackend->toc;
6248
36
100
100
510
6249
# store headlines as anchors, if necessary
6250
if (@$toc and $flags{headlineLinks})
6251
2
6
{
6252
# scopies
6253
2
8
my ($headlineNr, @headlinePath)=(0);
6254
6255
foreach (@$toc)
6256
7
10
{
6257
# update headline counter
6258
$headlineNr++;
6259
7
14
6260
# get data
6261
my ($level, $title)=@$_;
6262
7
50
18
6263
# skip empty headlines
6264
next unless $title;
6265
7
11
6266
# update headline path and numbers
6267
$headlinePath[$level]=$title;
6268
7
38
6269
7
50
31
# store both plain and composite headlines in the anchor object
2
11
6270
$anchors->add($title, $title, $headlineNr);
6271
$anchors->add(join('|', map {defined($_) ? $_ : ''} @headlinePath[$_..$level]), $title, $headlineNr) for (1..$level-1);
6272
}
6273
}
6274
6275
36
100
180
# add complete headline titles to streamed headline tokens,
6276
# move abbreviation, docstream and variable hints into data section
6277
if (@$toc)
6278
31
63
{
6279
# scopy
6280
31
84
my (@headlinePath, @shortcutPath, @levelPath, @pagenumPath);
120
490
6281
6282
for (my $index=0; $index<=$#{$toc}; ++$index)
6283
89
217
{
6284
# build a more readable shortcut
6285
my $ref=$resultStreamRef->[STREAM_TOKENS][$resultStreamRef->[STREAM_HEADLINES][$index]];
6286
89
335
89
220
6287
# get toc data
6288
my ($level, $title)=@{$toc->[$index]};
6289
6290
89
423
# adapt arrays to get rid of previous data - important in case someone skips several levels
6291
# (jumping from level 5 to 100 etc.)
6292
$#headlinePath=$#shortcutPath=$#levelPath=$#pagenumPath=$level;
6293
89
179
6294
89
100
332
# update headline pathes and numbers
6295
89
155
$headlinePath[$level]=$title;
6296
89
161
$shortcutPath[$level]=$ref->[0]{shortcut} ? $ref->[0]{shortcut} : $title;
6297
$levelPath[$level]++;
6298
89
232
$pagenumPath[$level]=$index+1; # real page number, no index
6299
89
305
6300
89
100
6912
my $docstreams=delete($ref->[0]{docstreams});
6301
my $variables=delete($ref->[0]{vars});
6302
push (
6303
@$ref,
6304
$toc->[$index][1],
6305
delete($ref->[0]{shortcut}),
6306
$flags{docstreaming}==DSTREAM_DEFAULT ? [sort keys %$docstreams] : {},
6307
6308
# store headline path data in the streamed token
6309
[
6310
dclone([@headlinePath[1..$level]]),
6311
dclone([@shortcutPath[1..$level]]),
6312
dclone([@levelPath[1..$level]]),
6313
dclone([@pagenumPath[1..$level]]),
6314
$variables,
6315
],
6316
);
6317
}
6318
}
6319
36
100
324
6320
# finish tags, if necessary
6321
if (@$pendingTags==3)
6322
2
5
{
2
7
6323
# get number of tokens
6324
my $lastIndex=$#{$resultStreamRef->[STREAM_TOKENS]};
6325
2
4
2
8
6326
# handle all marked sections
6327
foreach my $section (@{$pendingTags->[2]})
6328
{
6329
6
21
# scan the stream till all pending tags were handled,
6330
# begin as near as possible
6331
for (my $position=$section->[0]; $position<=$lastIndex; $position++)
6332
12
176
{
6333
# get token
6334
my $token=$resultStreamRef->[STREAM_TOKENS][$position];
6335
12
50
100
140
66
66
6336
# skip everything except tag beginners of tags with finish hooks
6337
next unless ref($token)
6338
and $token->[STREAM_DIR_TYPE]==DIRECTIVE_TAG
6339
and $token->[STREAM_DIR_STATE]==DIRECTIVE_START
6340
and exists $tagsRef->{$token->[STREAM_DIR_DATA]}{finish};
6341
6
135
6342
# make an option hash
6343
my $options=dclone($token->[STREAM_DIR_DATA+1]);
6344
6
9
6345
6
11
# call hook function (use eval() to guard yourself)
6
16
6
41
6346
my $rc;
6347
eval {$rc=&{$tagsRef->{$token->[STREAM_DIR_DATA]}{finish}}($options, $anchors, join('-', @headlineIds))};
6348
6
50
35
6349
0
0
# check result
6350
unless ($@)
6351
{
6352
6
50
33
10
{
6
41
6353
# Error? (Treat syntactic errors as semantic ones at this point to give PARSING_FAILED a meaning.)
6354
++$_semerr, last if $rc==PARSING_ERROR or $rc==PARSING_FAILED;
6355
6356
6
11
# update options (might be modified, and checking for a difference
6357
# might take more time then just copying the replied values)
6358
$token->[STREAM_DIR_DATA+1]=$options;
6359
6
50
33
24
6360
# all right? (several values just mean "ok" at this point)
6361
last if $rc==PARSING_OK or $rc==PARSING_COMPLETED;
6362
0
0
0
6363
0
0
0
# backend hints to store?
6364
$token->[STREAM_DIR_HINTS]{ignore}=1, last if $rc==PARSING_IGNORE;
6365
$token->[STREAM_DIR_HINTS]{hide}=1, last if $rc==PARSING_ERASE;
6366
0
0
6367
# something is wrong here
6368
warn "[Warn] Tags ", $token->[STREAM_DIR_DATA], " tag finish hook replied unexpected result $rc, ignored.\n";
6369
}
6370
}
6371
else
6372
{warn "[Warn] Error in tags ", $token->[STREAM_DIR_DATA], " finish hook (ignored): $@\n"}
6373
6
50
26
6374
# update counter and leave loop if all pending tags in this section were handled
6375
last unless --$section->[1];
6376
}
6377
}
6378
}
6379
36
95
6380
# clean up
6381
undef $pendingTags;
6382
6383
36
100
66
566
6384
# success?
6385
if ($rc and not $_semerr)
6386
32
50
344
{
6387
# display a summary
6388
warn <
6389
6390
[Info] Input ok.
6391
6392
0
0
Statistics:
0
0
6393
0
0
-----------
6394
0
0
${\(_statisticsHelper(DIRECTIVE_HEADLINE))},
6395
0
0
${\(_statisticsHelper(DIRECTIVE_TEXT))},
6396
0
0
${\(_statisticsHelper(DIRECTIVE_UPOINT))},
6397
0
0
${\(_statisticsHelper(DIRECTIVE_OPOINT))},
6398
0
0
${\(_statisticsHelper(DIRECTIVE_DPOINT))},
6399
0
0
${\(_statisticsHelper(DIRECTIVE_BLOCK))},
6400
0
0
${\(_statisticsHelper(DIRECTIVE_VERBATIM))},
6401
0
0
${\(_statisticsHelper(DIRECTIVE_TAG))}
6402
${\(_statisticsHelper(DIRECTIVE_LIST_RSHIFT))},
6403
${\(_statisticsHelper(DIRECTIVE_LIST_LSHIFT))},
6404
and ${\(_statisticsHelper(DIRECTIVE_COMMENT))} were detected.
6405
6406
EOM
6407
32
50
33
216
6408
# add cache informations, if necessary
6409
warn ' ' x length('[Info] '), int(100*$statistics{cache}[1]/$statistics{cache}[0]+0.5), "% of all checked paragraphs were restored from cache.\n\n" if $flags{cache} & CACHE_ON and not $flags{display} & DISPLAY_NOINFO;
6410
}
6411
else
6412
4
50
99
{
100
6413
# display a summary
6414
warn "[Info] Input contains $_semerr semantic error", $_semerr>1?'s':'', ".\n" if $_semerr;
6415
}
6416
36
50
176
6417
# inform user
6418
warn "[Info] Parsing completed.\n\n" unless $flags{display} & DISPLAY_NOINFO;
6419
36
100
1347
6420
# reply success state
6421
$rc and not $_semerr;
6422
}
6423
6424
6425
# report a semantic error, terminate process if necessary
6426
1
1
3
sub _semerr
6427
1
206
{
6428
1
50
9
my $parser=shift;
6429
warn "[Error ", ++$_semerr, "] ", @_, "\n";
6430
$parser->YYAbort if $flags{criticalSemantics};
6431
}
6432
6433
# ------------------------------------------------------
6434
# A tiny helper function intended for internal use only.
6435
# ------------------------------------------------------
6436
sub _statisticsHelper
6437
0
0
0
{
6438
0
0
0
# get and check parameters
6439
my ($type)=@_;
6440
confess "[BUG] Missing type parameter.\n" unless defined $type;
6441
0
0
0
0
6442
# declare variables
6443
my ($nr)=(exists $statistics{$type} and $statistics{$type}) ? $statistics{$type} : 0;
6444
0
0
0
6445
# reply resulting string
6446
join('', "$nr ", $paragraphTypeStrings{$type}, $nr==1 ? '' : 's');
6447
}
6448
6449
sub _updateChecksums
6450
389
389
1105
{
6451
389
50
1208
# get and check parameters
6452
389
50
1400
my ($streamPart, $parserReinvokationHint)=@_;
6453
confess "[BUG] Missing stream part parameter.\n" unless defined $streamPart;
6454
confess "[BUG] Stream part parameter is no reference.\n" unless ref($streamPart);
6455
389
100
66
2080
6456
# certain paragraph types are not cached intentionally
6457
return if not ($flags{cache} & CACHE_ON)
6458
or exists {
6459
DIRECTIVE_COMMENT() => 1,
6460
74
100
66
542
}->{$streamPart->[0][STREAM_DIR_TYPE]};
6461
6462
60
50
46084
if (exists $flags{checksummed} and $flags{checksummed})
50
100
6463
{
6464
$checksums->{$sourceFile}{$flags{checksummed}[0]}=[
6465
dclone($streamPart),
6466
$flags{checksummed}[2],
6467
$parserReinvokationHint ? $parserReinvokationHint : (),
6468
defined $flags{checksummed}[3] ? $macroChecksum : (),
6469
defined $flags{checksummed}[4] ? $varChecksum : (),
6470
$anchors->reportNew,
6471
];
6472
60
303
# use Data::Dumper;
6473
# warn Dumper($streamPart);
6474
$flags{checksummed}=undef;
6475
60
272
6476
# note that something new was cached
6477
$flags{cached}=1;
6478
}
6479
}
6480
6481
6482
# --------------------------------------------------------
6483
# Extend all table rows to the number of columns found
6484
# in the first table line ("table headline"). On request,
6485
# automatically format the first table line as "headline".
6486
# --------------------------------------------------------
6487
sub _normalizeTableRows
6488
18
18
43
{
6489
18
50
55
# get and check parameters
6490
18
50
78
my ($stream, $autoHeadline)=@_;
6491
18
50
65
confess "[BUG] Missing stream part reference parameter.\n" unless defined $stream;
6492
confess "[BUG] Stream part reference parameter is no array reference.\n" unless ref($stream) eq 'ARRAY';
6493
confess "[BUG] Missing headline mode parameter.\n" unless defined $autoHeadline;
6494
18
68
6495
# declare variables
6496
my ($refColumns, $maxColumns, $columns, $nested, @flags, @improvedStream)=(0, 0, 0.5, 0, 1);
6497
18
100
122
18
68
6498
18
50
95
# remove whitespaces at the beginning and end of the stream, if necessary
18
75
6499
shift(@$stream) if $stream->[0]=~/^\s*$/; $stream->[0]=~s/^\s+//;
6500
pop(@$stream) if $stream->[-1]=~/^\s*$/; $stream->[-1]=~s/\s+$//;
6501
18
46
6502
# process the received stream
6503
foreach (@$stream)
6504
586
100
66
4489
{
100
100
6505
# search for *embedded* tables - which are already normalized!
6506
$nested+=($_->[STREAM_DIR_STATE]==DIRECTIVE_START ? 1 : -1)
6507
if ref($_) eq 'ARRAY' and $_->[STREAM_DIR_TYPE]==DIRECTIVE_TAG and $_->[STREAM_DIR_DATA] eq 'TABLE';
6508
586
100
1227
6509
# Inside an embedded table? Just pass the stream unchanged then.
6510
push(@improvedStream, $_), next if $nested;
6511
552
66
1898
6512
552
100
1679
# check state, set flags
6513
552
100
2217
$flags[1]=(ref($_) eq 'ARRAY' and $_->[STREAM_DIR_TYPE]==DIRECTIVE_TAG);
6514
552
100
100
1888
$flags[2]=($flags[1] and $_->[STREAM_DIR_DATA] eq 'TABLE_COL');
6515
552
100
100
1752
$flags[3]=($flags[1] and $_->[STREAM_DIR_STATE]==DIRECTIVE_COMPLETE and $_->[STREAM_DIR_DATA] eq 'TABLE_ROW');
6516
$flags[4]=1 if $flags[2] and $_->[STREAM_DIR_STATE]==DIRECTIVE_START;
6517
$flags[4]=0 if $flags[2] and $_->[STREAM_DIR_STATE]==DIRECTIVE_COMPLETE;
6518
552
100
1106
6519
# update counter of current row columns
6520
$columns+=0.5 if $flags[2];
6521
552
100
100
1662
6522
# end of column reached?
6523
if ($flags[2] and not $flags[4])
6524
{
6525
128
519
# remove all trailing whitespaces in the last recent data entry,
6526
128
100
331
# remove data which becomes empty this way
6527
$improvedStream[-1]=~s/\s+$//;
6528
pop(@improvedStream) unless length($improvedStream[-1]);
6529
}
6530
552
100
100
2596
6531
# first data after opening a new column?
6532
if ($flags[4] and not $flags[2])
6533
117
146
{
6534
# reset flag
6535
$flags[4]=0;
6536
117
352
6537
117
100
313
# remove all leading whitespaces, skip data which becomes empty this way
6538
s/^\s+//;
6539
next unless length($_);
6540
}
6541
466
100
807
6542
# table headline row?
6543
if ($flags[0])
6544
142
100
100
675
{
31
146
6545
# ok: mark columns as headline parts if necessary, take other elements unchanged
6546
push(@improvedStream, ($flags[2] and $autoHeadline) ? [@{$_}[STREAM_DIR_HINTS .. STREAM_DIR_STATE], 'TABLE_HL'] : $_);
6547
# at the end of this first row, marks that it is reached, store the number
6548
142
100
380
# of its columns as a reference for the complete table, and reset the column counter
6549
# (which will be used slightly differently in the following lines)
6550
$flags[0]=0, $refColumns=$maxColumns=$columns, $columns=0 if $flags[3];
6551
}
6552
else
6553
{
6554
# this is a content row (take care to preserve the order of operations here)
6555
324
100
633
6556
# end of table row reached?
6557
if ($flags[3])
6558
{
6559
# yes: insert additional columns, if necessary
6560
push(
6561
@improvedStream,
6562
26
137
[{}, DIRECTIVE_TAG, DIRECTIVE_START, 'TABLE_COL'],
6563
[{}, DIRECTIVE_TAG, DIRECTIVE_COMPLETE, 'TABLE_COL'],
6564
) for 1 .. ($refColumns-$columns);
6565
26
45
6566
# reset column counter
6567
$columns=0;
6568
}
6569
324
50
633
6570
# update maximum number of columns, if necessary
6571
$maxColumns=$columns if $columns>$maxColumns;
6572
324
700
6573
# in any case, copy this stream part
6574
push(@improvedStream, $_);
6575
}
6576
}
6577
18
260
6578
# replace original stream by the improved variant
6579
@$stream=@improvedStream;
6580
18
166
6581
# supply the number of columns in the table row *and* the maximum number of columns
6582
($refColumns, $maxColumns);
6583
}
6584
6585
6586
# predeclare variables
6587
sub _predeclareVariables
6588
105
105
982
{
6589
105
50
1213
# get and check parameters
6590
105
50
1445
my ($declarations, $preserveNames)=@_;
6591
confess "[BUG] Missing declaration parameter.\n" unless defined $declarations;
6592
confess "[BUG] Declaration parameter is no hash reference.\n" unless ref($declarations) eq 'HASH';
6593
6594
105
722
# transform variable names, if necessary
105
674
6595
105
100
1773
{
270
100
816
270
2275
6596
my $c=0;
6597
%$declarations=map {$c++; $c%2 ? uc : $_} %$declarations unless $preserveNames;
6598
}
6599
105
1528
38
484
6600
# handle every setting (keys are sorted for test puposes only, to make the stream reproducable)
6601
foreach my $var (sort {$a cmp $b} keys %$declarations)
6602
143
50
2857
{
6603
# check data format
6604
confess "[BUG] Predeclared variable $var is no scalar.\n" if ref($declarations->{$var});
6605
143
1254
6606
# store the variable - with an uppercased name
6607
$variables{$var}=$declarations->{$var};
6608
143
100
707
20
264
6609
# propagate the setting to the stream, if necessary
6610
push(@{$resultStreamRef->[STREAM_TOKENS]}, [{}, DIRECTIVE_VARSET, DIRECTIVE_START, {var=>$var, value=>$declarations->{$var}}]) if $flags{var2stream};
6611
143
100
1310
6612
# make the new variable setting available to embedded Perl code, if necessary
6613
34
34
414
if ($safeObject)
34
87
34
16640
6614
88
100
183
{
88
2379
6615
no strict 'refs';
6616
${join('::', ref($safeObject) ? $safeObject->root : 'main', $var)}=$declarations->{$var};
6617
}
6618
}
6619
105
2566
105
1225
6620
# update tag finish memory by the way
6621
_updateTagFinishMem(scalar(@{$resultStreamRef->[STREAM_TOKENS]}));
6622
}
6623
6624
6625
# update tag finish memory
6626
sub _updateTagFinishMem
6627
523
523
1294
{
6628
523
50
2515
# get and check parameters
6629
my ($lastKnownStreamIndex)=@_;
6630
confess "[BUG] Missing last known stream index parameter.\n" unless defined $lastKnownStreamIndex;
6631
523
100
1778
6632
# update tag finish memory, if necessary
6633
if ($pendingTags->[1])
6634
6
15
{
6
11
6
17
6635
# store current collection
6636
push(@{$pendingTags->[2]}, [@{$pendingTags}[0, 1]]);
6637
6
15
6638
# reset tag counter
6639
$pendingTags->[1]=0;
6640
}
6641
523
1257
6642
# in any case, update the "last known index" memory
6643
$pendingTags->[0]=$lastKnownStreamIndex;
6644
}
6645
6646
6647
6648
# set import filter, if necessary (by setting an import function - a user function is *not* overwritten!)
6649
sub _setImportFilter
6650
45
45
106
{
6651
45
50
142
# get and check parameters
6652
45
50
139
my ($parser, $optionHash)=@_;
6653
45
50
176
confess "[BUG] Missing parser parameter.\n" unless $parser;
6654
confess "[BUG] Missing option hash parameter.\n" unless defined $optionHash;
6655
confess "[BUG] Option hash parameter is no hash reference.\n" unless ref($optionHash) eq 'HASH';
6656
45
50
66
571
0
33
6657
# anything to do?
6658
if (
6659
not exists $optionHash->{ifilter} # there is no functional import filter set yet
6660
and exists $optionHash->{import} # but there is a type specific import filter set yet
6661
and not ( # and there is not ...
6662
exists $flags{ifilters}{lc($optionHash->{import})} # ... a general import filter known yet
6663
and not defined $flags{ifilters}{lc($optionHash->{import})} # ... which flags that there is no such filter
6664
)
6665
)
6666
{
6667
0
0
0
0
# parser options allow to use input filters of other languages, if this is the case we find that the
6668
# value of the general import filter is a special string which holds the name of that language
6669
my $filterLang=lc(
6670
(
6671
exists $flags{ifilters}{lc($optionHash->{import})}
6672
and $flags{ifilters}{lc($optionHash->{import})}=~/^MAP:\s*(\w+)$/
6673
) ? $flags{ifilters}{lc($1)} : $optionHash->{import}
6674
);
6675
0
0
0
6676
# first time we need this import filter?
6677
unless (exists $flags{ifilters}{$filterLang})
6678
{
6679
0
0
# so, there is a chance to find such a filter via general modules, search for such a module
6680
eval
6681
34
34
219
{
34
96
34
67750
6682
# no strict subs
6683
no strict;
6684
0
0
6685
# build module name
6686
my $moduleName=join('::', 'PerlPoint::Import', uc($filterLang));
6687
0
0
6688
0
0
0
# try to load the module
6689
0
0
0
my $evalCode="require $moduleName;";
6690
ref($safeObject) ? $safeObject->reval($evalCode) : eval $evalCode;
6691
die $@ if $@;
6692
6693
# check for the import function specified by the API, store the code reference to the function
6694
0
0
# found or the undefined value otherwise, which will avoid repeated searches (store it both
6695
0
0
0
# for the filter language and the file language, which might differ due to filter mapping)
0
6696
$evalCode="exists \$${moduleName}::{importFilter}";
6697
$flags{ifilters}{lc($optionHash->{import})}=$flags{ifilters}{$filterLang}=join('::', $moduleName, 'importFilter()') if ref($safeObject) ? $safeObject->reval($evalCode) : eval $evalCode;
6698
};
6699
0
0
0
6700
# check success
6701
$parser->_semerr("Could not load $optionHash->{import} import filter module: $@."), return undef if $@;
6702
}
6703
0
0
0
0
6704
# now, set a functional filter, if possible
6705
$optionHash->{ifilter}=$flags{ifilters}{$filterLang}
6706
if exists $flags{ifilters}{$filterLang} and defined $flags{ifilters}{$filterLang};
6707
}
6708
45
282
6709
# flag success
6710
1;
6711
}
6712
6713
# perform paragraph filter calls
6714
sub _pfilterCall
6715
8
8
23
{
6716
8
50
33
# get and check parameters
6717
8
50
25
my ($parser, $filters, $pstream, $lineNr)=@_;
6718
8
50
50
confess "[BUG] Missing parser parameter.\n" unless $parser;
6719
8
50
33
confess "[BUG] Missing filter list.\n" unless $filters;
6720
8
50
44
confess "[BUG] Filter list is no array reference.\n" unless ref($filters) eq 'ARRAY';
6721
8
50
37
confess "[BUG] Missing paragraph stream.\n" unless $pstream;
6722
confess "[BUG] Paragraph stream is no array reference.\n" unless ref($pstream) eq 'ARRAY';
6723
confess "[BUG] Missing line number.\n" unless $lineNr;
6724
6725
8
14
6726
8
30
# declare and init variables
6727
my ($streamRef, %tableCounters);
6728
$retranslationBuffer='';
6729
8
100
32
6730
# build retranslator, if necessary
6731
unless ($retranslator)
6732
4
11
{
6733
# scopy
6734
my ($verbatimFlag)=(0);
6735
4
220
6736
# the retranslator is a backend object
6737
$retranslator=new PerlPoint::Backend(
6738
name => 'retranslator',
6739
display => DISPLAY_NOINFO+DISPLAY_NOWARN,
6740
trace => TRACE_NOTHING,
6741
);
6742
6743
# various callbacks perform the retranslation
6744
$retranslator->register(DIRECTIVE_SIMPLE, sub
6745
60
60
144
{
6746
# get parameters
6747
my ($opcode, $mode, @contents)=@_;
6748
6749
# add contents to the source text collection,
6750
60
114
# double backslashes (if they are here, they were guarded originally),
6751
60
50
183
# and restore ">" characters as if they were guarded
6752
60
207
(my $text=join('', @contents));
6753
$text=~s/([\\>])/\\$1/g unless $verbatimFlag;
6754
4
95
$retranslationBuffer.=$text;
6755
}
6756
);
6757
6758
$retranslator->register(DIRECTIVE_HEADLINE, sub
6759
8
8
21
{
6760
# get parameters
6761
my ($opcode, $mode, $level)=@_;
6762
8
100
61
6763
# add preceeding "=" characters
6764
4
56
$retranslationBuffer.=('=' x $level) if $mode==DIRECTIVE_START;
6765
}
6766
);
6767
6768
$retranslator->register(DIRECTIVE_VERBATIM, sub
6769
0
0
0
{
6770
# get parameters
6771
my ($opcode, $mode)=@_;
6772
0
0
0
6773
0
0
0
# cover contents
6774
$verbatimFlag=1, $retranslationBuffer.="<
6775
4
51
$verbatimFlag=0, $retranslationBuffer.="EOE\n" if $mode==DIRECTIVE_COMPLETE;
6776
}
6777
);
6778
6779
my $handleListPoint=sub
6780
0
0
0
{
6781
# get parameters
6782
my ($opcode, $mode, @data)=@_;
6783
0
0
0
0
0
0
0
6784
0
0
# act mode dependend
6785
if ($mode==DIRECTIVE_START)
6786
{$retranslationBuffer.=$opcode==DIRECTIVE_UPOINT ? '* ' : $opcode==DIRECTIVE_OPOINT ? '# ' : ':';}
6787
4
36
else
6788
{$retranslationBuffer.="\n\n"}
6789
};
6790
6791
my $handleDListPointItem=sub
6792
0
0
0
{
6793
# get parameters
6794
my ($opcode, $mode, @data)=@_;
6795
0
0
0
6796
4
37
# complete the item part if necessary
6797
$retranslationBuffer.=': ' if $mode==DIRECTIVE_COMPLETE;
6798
};
6799
6800
my $handleListShift=sub
6801
0
0
0
{
6802
# get parameters
6803
my ($opcode, $mode, $offset)=@_;
6804
0
0
0
0
6805
# anything to do?
6806
$retranslationBuffer.=join('',
6807
$opcode==DIRECTIVE_LIST_RSHIFT ? '>' : '<',
6808
4
25
"$offset\n\n",
6809
) if $mode==DIRECTIVE_START;
6810
4
33
};
6811
4
24
6812
4
24
$retranslator->register($_, $handleListPoint) foreach (DIRECTIVE_UPOINT, DIRECTIVE_OPOINT, DIRECTIVE_DPOINT);
6813
$retranslator->register($_, $handleListShift) foreach (DIRECTIVE_LIST_LSHIFT, DIRECTIVE_LIST_RSHIFT);
6814
$retranslator->register(DIRECTIVE_DPOINT_ITEM, $handleDListPointItem);
6815
6816
$retranslator->register(DIRECTIVE_TAG, sub
6817
0
0
0
{
6818
# get parameters
6819
my ($opcode, $mode, $tag, $settings, $bodyHint)=@_;
6820
0
0
0
6821
# table tags need special care, is it one?
6822
unless ($tag=~/^TABLE/)
6823
{
6824
0
0
# it can happen that perl complains about an undefined value here
6825
# even if no such value is to be find in debugging
6826
local($^W)=0;
6827
0
0
0
0
0
0
0
0
0
0
0
6828
# act mode dependend
6829
$retranslationBuffer.=$mode==DIRECTIVE_START ? join('', "\\$tag", (defined $settings and %$settings and grep(!/^__/, keys %$settings)) ? join('', '{', join(' ', map {qq($_="$settings->{$_}")} grep(!/^__/, keys %$settings)), '}') : (), ((defined $bodyHint and $bodyHint) ? '<' : ())) : ((defined $bodyHint and $bodyHint) ? '>' : ());
6830
}
6831
else
6832
0
0
0
0
{
0
0
0
6833
# TABLE declared by paragraph?
6834
if ($tag eq 'TABLE' and exists $settings->{__paragraph__})
6835
0
0
0
{
6836
# translate TABLE into paragraph start or completion
6837
0
0
$retranslationBuffer.=$mode==DIRECTIVE_START ? join('', '@', $settings->{separator}, "\n") : '';
6838
# init counters, store separators
6839
@tableCounters{qw(row col rowsep colsep)}=(0, 0, "\n", " $settings->{separator} ");
6840
}
6841
# TABLE declared by tags?
6842
elsif ($tag eq 'TABLE')
6843
0
0
0
{
0
0
0
0
6844
# translate TABLE into TABLE and END_TABLE
6845
0
0
0
$retranslationBuffer.=$mode==DIRECTIVE_START ? join('', '\TABLE{', join(' ', map {qq($_="$settings->{$_}")} grep(!/^__/, keys %$settings)), '}', $settings->{rowseparator} eq '\\\n' ? "\n" : '') : join('', $settings->{rowseparator} eq '\\\n' ? "\n" : '', '\END_TABLE');
0
6846
# init counters, store separators
6847
@tableCounters{qw(row col rowsep colsep)}=(0, 0, $settings->{rowseparator} eq '\\\n' ? "\n" : $settings->{rowseparator}, $settings->{separator} eq '\\\n' ? "\n" : " $settings->{separator} ");
6848
}
6849
# TABLE_ROW?
6850
elsif ($tag eq 'TABLE_ROW')
6851
0
0
0
{
6852
# we only need to act at startup
6853
if ($mode==DIRECTIVE_START)
6854
0
0
0
{
6855
# write a row separator if there was a row before
6856
0
0
$retranslationBuffer.=$tableCounters{rowsep} if $tableCounters{row};
6857
0
0
# update row counter, reset column counter
6858
$tableCounters{row}++;
6859
$tableCounters{col}=0;
6860
}
6861
}
6862
# TABLE_HL or TABLE_COL?
6863
elsif ($tag=~/TABLE_(HL|COL)/)
6864
0
0
0
{
6865
# we only need to act at startup
6866
if ($mode==DIRECTIVE_START)
6867
0
0
0
{
6868
# write a column separator if there was a column before
6869
0
0
$retranslationBuffer.=$tableCounters{colsep} if $tableCounters{col};
6870
# update row counter
6871
$tableCounters{col}++;
6872
}
6873
}
6874
4
75
}
6875
}
6876
);
6877
}
6878
8
33
8
42
6879
# embed paragraph stream into a structure looking like a complete stream
6880
@{$streamRef}[
6881
STREAM_IDENT,
6882
STREAM_TOKENS,
6883
STREAM_HEADLINES,
6884
]=(
6885
'__PerlPoint_stream__', # stream identifier;
6886
$pstream, # base stream: paragraph stream;
6887
[], # headline stream (dummy);
6888
);
6889
8
81
6890
# retranslate paragraph
6891
$retranslator->run($streamRef);
6892
6893
# init paragraph text
6894
# $retranslationBuffer=join('', @{$pstream}[1..($#{$pstream}-1)]);
6895
6896
8
27
# warn "BUFFER: $retranslationBuffer\n";
6897
6898
foreach my $perl (@$filters)
6899
{
6900
# we provide the paragraph text simply - for a general solution, we need to use an object
6901
34
34
285
# of a handy subclass of PerlPoint::Backend (still to be written)
34
108
34
13410
8
10
6902
8
50
15
{
8
92
6903
8
50
152
no strict 'refs';
8
43
6904
${join('::', ref($safeObject) ? $safeObject->root : 'main', '_pfilterText')}=$retranslationBuffer;
6905
${join('::', ref($safeObject) ? $safeObject->root : 'main', '_pfilterType')}=$paragraphTypeStrings{$pstream->[0][1]};
6906
}
6907
8
50
140
6908
# inform user
6909
warn qq([Trace] $sourceFile, line $lineNr: Running paragraph filter "$perl".\n) if $flags{trace} & TRACE_ACTIVE;
6910
8
50
63
6911
# call the filter
6912
$retranslationBuffer=ref($safeObject) ? $safeObject->reval($perl) : eval(join(' ', '{package main; no strict;', $perl, '}'));
6913
8
50
10188
6914
# check result
6915
if ($@)
6916
0
0
{
6917
# inform user, if necessary
6918
_semerr($parser, qq($sourceFile, line $lineNr: paragraph filter "$perl" could not be evaluated: $@.));
6919
0
0
6920
# stop processing, flag error
6921
return undef;
6922
}
6923
}
6924
8
50
65
6925
# success: reply result (embed it into empty lines to avoid paragraph mismatch)
6926
defined $retranslationBuffer ? [("\n") x 2, split(/(\n)/, $retranslationBuffer), ("\n") x 2] : '';
6927
}
6928
6929
=pod
6930
6931
=head2 anchors()
6932
6933
A class method that supplied all anchors collected by the parser.
6934
6935
Example:
6936
6937
my $anchors=PerlPoint::Parser::anchors;
6938
6939
0
0
1
0
=cut
6940
sub anchors
6941
{$anchors;}
6942
6943
1;
6944
6945
6946
# declare a helper package used for token "delay" after bodyless macros
6947
# (implemented the oo way to determine the data)
6948
package PerlPoint::Parser::DelayedToken;
6949
34
34
252
34
96
34
31895
6950
# even this tiny package needs modules!
6951
use Carp;
6952
6953
# make an object holding the token name and its value
6954
sub new
6955
4
4
10
{
6956
# get parameter
6957
my ($class, $token, $value)=@_;
6958
4
50
9
6959
4
50
9
# check parameters
6960
4
50
9
confess "[BUG] Missing class name.\n" unless $class;
6961
confess "[BUG] Missing token parameter.\n" unless $token;
6962
confess "[BUG] Missing token value parameter.\n" unless defined $value;
6963
4
26
6964
# build and reply object
6965
bless([$token, $value], $class);
6966
}
6967
4
4
16
6968
# reply token
6969
sub token {$_[0]->[0];}
6970
4
4
15
6971
# reply value
6972
sub value {$_[0]->[1];}
6973
6974
1;
6975
6976
6977
# = POD TRAILER SECTION =================================================================
6978
6979
=pod
6980
6981
=head1 EXAMPLE
6982
6983
The following code shows a minimal but complete parser.
6984
6985
# pragmata
6986
use strict;
6987
6988
# load modules
6989
use PerlPoint::Parser;
6990
6991
# declare variables
6992
my (@streamData);
6993
6994
# build parser
6995
my ($parser)=new PerlPoint::Parser;
6996
# and call it
6997
$parser->run(
6998
stream => \@streamData,
6999
files => \@ARGV,
7000
);
7001
7002
=head1 NOTES
7003
7004
=head2 Converter namespace
7005
7006
It is suggested to B operating in namespace B. In order to emulate
7007
the behaviour of the B module by C in case a user wishes to get
7008
full Perl access for active contents, active contents needs to be executed in
7009
this namespace. Safe does not allow to change this, so the documented default
7010
for "saved" and "not saved" active contents I to be C. This means
7011
that both the parser and active contents will pollute C. Prevent from being
7012
effected by choosing a different converter namespace. The B
7013
hyrarchy is reserved for this purpose. The recommended namespace is
7014
C>, e.g. C.
7015
7016
=head2 Format
7017
7018
The PerlPoint format was initially designed by I,
7019
who wrote an HTML slide generator for it, too.
7020
7021
I added a number of additional, useful and interesting
7022
features to the original implementation. At a certain point, we
7023
decided to redesign the tool to make it a base for slide generation
7024
not only into HTML but into various document description languages.
7025
7026
The PerlPoint format implemented by this parser version is slightly
7027
different from the original design. Presentations written for Perl
7028
Point 1.0 will I pass the parser but can simply be converted
7029
into the new format. We designed the new format as a team of
7030
I, I and me.
7031
7032
=head2 Storable updates
7033
7034
From version 0.24 on the Storable module is a prerequisite of the
7035
parser package because Storable is used to store and retrieve cache
7036
data in files. If you update your Storable installation it I
7037
happen that its internal format changes and therefore stored cache
7038
data becomes unreadable. To avoid this, the parser automatically
7039
rebuilds existing caches in case of Storable updates.
7040
7041
=head1 FILES
7042
7043
If Is are used, the parser writes cache files where the initial
7044
sources are stored. They are named ..ppcache.
7045
7046
=head1 SEE ALSO
7047
7048
=over 4
7049
7050
=item PerlPoint::Backend
7051
7052
A frame class to write backends basing on the I.
7053
7054
=item PerlPoint::Constants
7055
7056
Constants used by parser functions and in the I.
7057
7058
=item PerlPoint::Tags
7059
7060
Tag declaration base class.
7061
7062
=item pp2sdf
7063
7064
A reference implementation of a PerlPoint converter, distributed with the parser package.
7065
7066
=item pp2html
7067
7068
The inital PerlPoint tool designed and provided by Tom Christiansen. A new translator
7069
by I using B.
7070
7071
=back
7072
7073
7074
=head1 SUPPORT
7075
7076
A PerlPoint mailing list is set up to discuss usage, ideas,
7077
bugs, suggestions and translator development. To subscribe,
7078
please send an empty message to perlpoint-subscribe@perl.org.
7079
7080
If you prefer, you can contact me via perl@jochen-stenzel.de
7081
as well.
7082
7083
7084
=head1 AUTHOR
7085
7086
Copyright (c) Jochen Stenzel (perl@jochen-stenzel.de), 1999-2001.
7087
All rights reserved.
7088
7089
This module is free software, you can redistribute it and/or modify it
7090
under the terms of the Artistic License distributed with Perl version
7091
5.003 or (at your option) any later version. Please refer to the
7092
Artistic License that came with your Perl distribution for more
7093
details.
7094
7095
The Artistic License should have been included in your distribution of
7096
Perl. It resides in the file named "Artistic" at the top-level of the
7097
Perl source tree (where Perl was downloaded/unpacked - ask your
7098
system administrator if you dont know where this is). Alternatively,
7099
the current version of the Artistic License distributed with Perl can
7100
be viewed on-line on the World-Wide Web (WWW) from the following URL:
7101
http://www.perl.com/perl/misc/Artistic.html.
7102
7103
B is built using B a way that users
7104
have I to explicitly install B themselves. According
7105
to the copyright note of B I have to mention the following:
7106
7107
"The Parse::Yapp module and its related modules and shell
7108
scripts are copyright (c) 1998-1999 Francois Desarmenien,
7109
France. All rights reserved.
7110
7111
You may use and distribute them under the terms of either
7112
the GNU General Public License or the Artistic License, as
7113
specified in the Perl README file."
7114
7115
7116
=head1 DISCLAIMER
7117
7118
This software is distributed in the hope that it will be useful, but
7119
is provided "AS IS" WITHOUT WARRANTY OF ANY KIND, either expressed or
7120
implied, INCLUDING, without limitation, the implied warranties of
7121
MERCHANTABILITY and FITNESS FOR A PARTICULAR PURPOSE.
7122
7123
The ENTIRE RISK as to the quality and performance of the software
7124
IS WITH YOU (the holder of the software). Should the software prove
7125
defective, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
7126
CORRECTION.
7127
7128
IN NO EVENT WILL ANY COPYRIGHT HOLDER OR ANY OTHER PARTY WHO MAY CREATE,
7129
MODIFY, OR DISTRIBUTE THE SOFTWARE BE LIABLE OR RESPONSIBLE TO YOU OR TO
7130
ANY OTHER ENTITY FOR ANY KIND OF DAMAGES (no matter how awful - not even
7131
if they arise from known or unknown flaws in the software).
7132
7133
Please refer to the Artistic License that came with your Perl
7134
distribution for more details.
7135
7136
=cut