File Coverage

blib/lib/SpeL/Parser/Chunk.pm
Criterion Covered Total %
statement 104 147 70.7
branch 16 32 50.0
condition 2 6 33.3
subroutine 14 16 87.5
pod 4 4 100.0
total 140 205 68.2


line stmt bran cond sub pod time code
1             # -*- cperl -*-
2             # ABSTRACT: LaTeX file parser
3              
4              
5 39     39   2531 use strict;
  39         123  
  39         1594  
6 39     39   204 use warnings;
  39         75  
  39         3042  
7             package SpeL::Parser::Chunk;
8              
9 39     39   253 use parent 'Exporter';
  39         72  
  39         374  
10 39     39   2667 use Carp;
  39         102  
  39         2692  
11              
12 39     39   213 use IO::File;
  39         75  
  39         5772  
13 39     39   245 use File::Basename;
  39         76  
  39         2571  
14              
15 39     39   16148 use SpeL::Object::Document;
  39         175  
  39         1905  
16 39     39   252 use SpeL::Object::ElementList;
  39         84  
  39         5447  
17             #use Data::Dumper;
18              
19             our $prepmacrolist = [];
20             our $prepenvlist = [];
21              
22             our $gobblematcher = qr/verbatim|listing/i;
23              
24             our $elements = do {
25 39     39   1678 use Regexp::Grammars;
  39         17197  
  39         417  
26             qr{
27             #
28              
29            
30              
31            
32              
33             #
34              
35              
36             # & not \& \,\quad\qquad whitespace comment
37             # (?: (?
38            
39             (?: \\, | \\quad | \\qquad | ~ | \\\s | [\s\n] | (\%[^\n]*\n) )* |
40             \\hskip \s* (:?\d+)?(?:\.\d+)? \s* |
41             \\relax
42              
43            
44             pt | mm | cm | in | ex | em | mu | sp
45              
46            
47             <[Element]>*
48              
49            
50            
51             (?: |
52             |
53             |
54             |
55             |
56             |
57             |
58             |
59             |
60             |
61             |
62             |
63             |
64             |
65             |
66             |
67             |
68             \| \| |
69             )
70            
71              
72            
73            
74             (?: |
75             |
76             |
77             |
78             |
79             |
80             |
81             )
82              
83            
84            
85             |
86             # never triggerd,
87             # had to hide this in Relation
88              
89              
90            
91             <[RelationChain]>* |
92             <[RelationChain]>*
93              
94            
95             ?
96              
97              
98            
99             = |
100             \\approx |
101             \\equiv |
102             \\ne\b |
103             \> |
104             \< |
105             \\gt\b |
106             \\gg\b |
107             \\lt\b |
108             \\ll\b |
109             \\ge\b |
110             \\le\b |
111             \\in\b |
112             \\[Ll]eftarrow |
113             \\[Rr]ightarrow |
114             \\[Ll]eftrightarrow |
115             \\[Ll]ongleftarrow |
116             \\[Rr]ongrightarrow |
117             \\[Ll]ongleftrightarrow |
118             \\[Uu]parrow |
119             \\[Dd]ownarrow |
120             \\[Uu]pdownarrow |
121             \\(?:long)?mapsto |
122             \\(?:leads)?to |
123             (?
124              
125            
126            
127            
128             ( ? |
129             |
130             |
131             |
132             |
133             |
134             ? |
135             ? |
136             ? |
137             ? |
138             ? |
139             ? |
140             |
141             |
142             |
143             |
144             |
145             |
146             ? )
147              
148            
149             |
150             |
151             |
152             |
153             |
154            
155              
156            
157             |
158             |
159            
160            
161            
162             \\left <.ws> (?:\(|\[|\\\{|\||\\\|) |
163             \\left <.ws> \.
164              
165            
166             (?:\\right) <.ws> (?:\)|\]|\\\}|\||\\\|) |
167             \\right <.ws> \.
168              
169            
170             \\frac \{ (?: | ) \}
171             \{ (?: | ) \}
172              
173            
174             |
175             |
176             |
177             |
178             |
179             |
180             |
181             ?
182              
183            
184             <.ws> \{ <.ws> <.ws> \} |
185             |
186             |
187            
188              
189            
190             |
191             |
192            
193              
194            
195             \{ \} |
196            
197              
198            
199             |
200             |
201             |
202             |
203             |
204             |
205             |
206             |
207             |
208             |
209            
210            
211            
212             \\ num <.ws> \{ \}
213              
214            
215             \\ qty <.ws> \{ \} <.ws>
216              
217            
218             \\ unit <.ws>
219              
220            
221             (?
222             \{
223             (?: (?> [^{}]+ ) | (?&braceunit))*
224             \}
225             )
226            
227            
228             |
229             |
230            
231              
232            
233             \!
234            
235            
236             \\sqrt <.ws> \[ \] <.ws> \{ \} |
237             \\sqrt <.ws> \{ \}
238              
239            
240            
241             \\ ( (?!\\)(?!end)(?!begin)(?!par)(?!item)(?!left)(?!right)(?!hskip) ) ? <[Args]>*
242              
243            
244            
245             \\ <[Args]>* |
246            
247             \\ ( (?!\\)(?!end)(?!begin)(?!par)(?!item)(?!left)(?!right)(?!hskip)(?!underbrace)(?!overbrace) ) ? <[Args=Mathargs]>*
248            
249            
250            
251             \\ <[Args]>
252              
253              
254            
255             \\ begin <.ws> \{ \}
256            
257             \\ end <.ws> \{ <.everbatimtag( :verbatimtag )> \}
258              
259            
260             \\ begin <.ws> \{ \} ? <[Args]>*
261             <.ws>
262             \\ end <.ws> \{ <.etag( :tag )> \}
263             |
264             \\ begin <.ws> \{ \} ? <[Args]>*
265             <.ws>
266            
267              
268            
269             \\\[
270            
271             \\\]
272              
273            
274             \\ begin <.ws> \{ \} ? ? <[Args]>*
275             <[MathUnit]>+ % <.eol> <.ws>
276             \\ end <.ws> \{ <.mathetag( :mathtag )> \}
277             |
278             \\ begin <.ws> \{ \} ? ? <[Args]>*
279             <[MathUnit]>+ % <.eol> <.ws>
280            
281            
282            
283             \\ begin <.ws> \{ \} ? <[Args]>*
284             <[MathUnit]>+ % <.eol> <.ws>
285             \\ end <.ws> \{ <.mathetaginner( :mathtaginner )> \}
286             |
287             \\ begin <.ws> \{ \} ? <[Args]>*
288             <[MathUnit]>+ % <.eol> <.ws>
289            
290              
291            
292             \\ begin <.ws> \{ \} ? <[Args]>*
293             <[MathUnit]>+ % <.eol> <.ws>
294             \\ end <.ws> \{ <.matrixetag( :matrixtag )> \}
295             |
296             \\ begin <.ws> \{ \} ? <[Args]>*
297             <[MathUnit]>+ % <.eol> <.ws>
298            
299              
300            
301            
302             \{ \}
303              
304            
305             \{ \}
306              
307            
308             [\$] [\$] \s* |
309             \\[\(] \\[\)] \s*
310            
311            
312             \\ item ? <[Args]>* ( <[Element]> )*
313              
314             ,
315              
316             ;
317              
318             ,
319              
320            
321             \\ begin <.ws> \{ <\:env> \}
322              
323             \\ end <.ws> \{ <\:env> \}
324              
325             \{
326              
327             \}
328              
329             =
330              
331             \\ transpose
332            
333             \\ end \{
334              
335             \\ begin <.ws> \{ \}
336              
337             [^][\$&%#_{}~^\s]++
338              
339            
340              
341             array\*? | aligned\*?
342              
343             (??{quotemeta $ARG{mathtaginner}})
344              
345             (?:[bBpvV]|small)?matrix\*?
346              
347             (??{quotemeta $ARG{matrixtag}})
348              
349             equation\*? | eqnarray\*? | alignat\*? | align\*? | gather\*?
350              
351             (??{quotemeta $ARG{mathtag}})
352              
353             \\\{ | \\\} | \\\[ | \\\]
354              
355             \^
356              
357             \_
358              
359             \&
360              
361             \\,
362              
363             \\\\(?:\[[^]]+\])?
364              
365             (??{quotemeta $ARG{tag}})
366              
367             (??{quotemeta $ARG{verbatimtag}})
368              
369             <.ws> \[ <[MATCH=Option]>+ % (,) \]
370            
371             <.ws> \{ \}
372              
373             <.ws> \{ \}
374              
375            
376             ( =)? ?
377              
378             [^][|\$%#_{}~^,=\s\\-]++
379              
380             [a-zA-Z@]++\*?
381              
382             [#]+ \d+
383              
384            
385             <.sub> <.sup> |
386             <.sup> <.sub> |
387             <.sup> |
388             <.sub> |
389            
390              
391            
392             |
393             |
394             |
395             |
396            
397              
398            
399             \\ int |
400             \\ sum |
401             \\ lim |
402             \\ max |
403             \\ min
404              
405            
406             [^][|\$%&#_{}^\\]++
407              
408            
409             ( (?: [^\$%&#_{}|^\\] |
410             \\ \& |
411             \\ \" |
412             \\ \, |
413             \\ \' |
414             \\ \` |
415             \\ \{ |
416             \\ \} |
417             \\ \$ |
418             \\ % |
419             \\ _ )+ )
420              
421              
422            
423             |
424             ? <.ws> |
425             ? (? |
426             ? <.ws> |
427             ? (? |
428             ? <.ws>
429              
430              
431            
432             \\
433             arcsin | asin | arccos | acos | arctan | atan | arccot | acot |
434             sinh | cosh | tanh | coth)>
435              
436            
437             \\ (?: <.sub> )?
438              
439            
440             \\ underbrace
441              
442            
443             \\ overbrace
444            
445            
446             ( | ) ?
447              
448            
449             [a-zA-Z]
450              
451            
452             \\(?:mit|mup)?[aA]lpha |
453             \\(?:mit|mup)?[bB]eta |
454             \\(?:mit|mup)?[gG]amma |
455             \\(?:mit|mup)?[dD]elta |
456             \\(?:mit|mup)?(?:var)?epsilon |
457             \\(?:mit|mup)?[zZ]eta |
458             \\(?:mit|mup)?[eE]ta |
459             \\(?:mit|mup)?[tT]heta |
460             \\(?:mit|mup)?vartheta |
461             \\(?:mit|mup)?iota |
462             \\(?:mit|mup)?[kK]appa
463             \\(?:mit|mup)?[lL]ambda |
464             \\(?:mit|mup)?[mM]u |
465             \\(?:mit|mup)?[nN]u |
466             \\(?:mit|mup)?[xX]i |
467             \\(?:mit|mup)?[pP]i |
468             \\(?:var)?rho |
469             \\(?:mit|mup)?[sS]igma |
470             \\(?:mit|mup)?[tT]au |
471             \\(?:mit|mup)?[uU]psilon |
472             \\(?:mit|mup)?[pP]hi |
473             \\(?:mit|mup)?varphi |
474             \\(?:mit|mup)?[cC]hi |
475             \\(?:mit|mup)?[pP]si |
476             \\(?:mit|mup)?[oO]mega
477              
478            
479             ([+-]) |
480             \\pm
481              
482            
483             ? |
484            
485              
486             j | i
487              
488            
489            
490              
491            
492            
493             | ((?:\.|,)\d+)
494             | \\ pi ({})?
495             | \\ infty ({})? )>
496              
497            
498             (?:
499             | ((?:\.|,)\d+) )> )?
500             (?: [eE] )?
501              
502            
503             \w | \\_
504              
505             .*?
506              
507             }xs
508             };
509              
510              
511             our $chunk = do {
512 39     39   473 use Regexp::Grammars;
  39         111  
  39         303  
513             qr{
514             #
515             #
516            
517             <.ws>?
518              
519            
520              
521            
522              
523            
524            
525              
526             \\ endinput
527             }xs
528             };
529              
530             # to debug:
531             #
532             #
533              
534              
535              
536             sub new {
537 39     39 1 340 my $class = shift;
538              
539 39         95 my $self = {};
540 39 50       160 $class = (ref $class ? ref $class : $class );
541 39         112 bless $self, $class;
542              
543 39         285 $self->{lines} = [];
544 39         170 $self->{lineinfo} = [];
545 39         149 return $self;
546             }
547              
548              
549             sub parseDocumentString {
550 67     67 1 146 my $self = shift;
551 67         277 my ( $document, $filename, $debug ) = @_;
552              
553             # preprocess macros and environments that are in the list
554             # say STDERR "Before cleansing:\n" . $document;
555             # Final prep to accomdate for docstrip '{Change History}'
556              
557 67 50       382 $debug = 0 unless( defined( $debug ) );
558            
559 67 50       275 if ( $debug ) {
560 0         0 say STDERR "=== Original ======================================================";
561 0         0 say STDERR $document;
562             }
563            
564 67         303 foreach my $entry (@$prepmacrolist) {
565 15         309 my $mac_regexp = qr/ ( \\ $entry->{macro} ) /x;
566 15         54 my $optarg_regexp = qr/ (
567             \[
568             [^]]*
569             \]
570             )? /x;
571 15         44 my $mandarg_regexp = qr/ (?
572             \{
573             (?:
574             (?> [^{}]+ )
575             |
576             (?&braceunit)
577             )*
578             \}
579             )
580             /x;
581              
582 15         30 my $regexp = $mac_regexp;
583 15         28 my $i = 1;
584 15         25 my $optargcount = 0;
585 15 100 66     100 if ( $entry->{argc}
586             ne '-NoValue-'
587             and
588             $entry->{argc} > 0 ) {
589 7 100       33 if ( $entry->{optarg} ne '-NoValue-' ) {
590 1         4 $regexp .= qr/ \s* /x . $optarg_regexp;
591 1         3 $optargcount = 1;
592 1         2 ++$i;
593             }
594 7         134 for( ; $i <= $entry->{argc}; ++$i ) {
595 8         66 $regexp .= qr/ \s* /x . $mandarg_regexp;
596             }
597             }
598              
599 15         25 while( 1 ) {
600 30         726 my @matches = $document =~ /$regexp/;
601 30 100       151 last unless( scalar @matches );
602            
603 15         42 my $replacement = $entry->{replacement};
604 15         26 my $m = 1;
605 15         149 foreach( $m = 1; $m < @matches; ++$m ) {
606 16 100       217 if ( defined $matches[$m] ) {
607 15         33 my $r = $matches[$m];
608 15         149 $r = substr( $r, 1, length( $r ) -2 );
609 15         284 $replacement =~ s/##$m/$r/;
610             }
611             else {
612             # this only occurs for $m = 1, i.e. when the optional
613             # argument was not specified in the text, therefore,
614             # we replace it by the default
615 1         9 $replacement =~ s/##$m/$entry->{optarg}/;
616             }
617             }
618 15         686 $document =~ s/$regexp/$replacement/;
619             }
620             # $document =~ s/$arr->[0]/$2/g;
621             # }
622             # else {
623             # $document =~ s/$arr->[0]//g;
624             # }
625             }
626              
627            
628 67         213 foreach my $entry (@$prepenvlist) {
629            
630 0         0 my $envbegin_regexp = qr/ \\ begin \{ $entry->{env} \} /x;
631 0         0 my $envend_regexp = qr/ \\ end \{ $entry->{env} \} /x;
632 0         0 my $optarg_regexp = qr/ (
633             \[
634             [^]]*
635             \]
636             )?
637             /x;
638 0         0 my $mandarg_regexp = qr/ (?
639             \{
640             (?:
641             (?> [^{}]+ )
642             |
643             (?&braceunit)
644             )*
645             \}
646             )
647             /x;
648 0         0 my $envcontent_regexp = qr/ ( .* ) /sx;
649            
650 0         0 my $regexp = $envbegin_regexp;
651 0         0 my $i = 1;
652 0         0 my $optargcount = 0;
653 0 0 0     0 if ( $entry->{argc}
654             ne '-NoValue-'
655             and
656             $entry->{argc} > 0 ) {
657 0 0       0 if ( $entry->{optarg} ne '-NoValue-' ) {
658 0         0 $regexp .= qr/ \s* /x . $optarg_regexp;
659 0         0 $optargcount = 1;
660 0         0 ++$i;
661             }
662 0         0 for( ; $i <= $entry->{argc}; ++$i ) {
663 0         0 $regexp .= qr/ \s* /x . $mandarg_regexp;
664             }
665             }
666 0         0 $regexp .= $envcontent_regexp;
667 0         0 $regexp .= $envend_regexp;
668            
669 0         0 while( 1 ) {
670 0         0 my @matches = $document =~ /$regexp/;
671 0 0       0 last unless( scalar @matches );
672            
673 0         0 my $replacement = $entry->{replacement};
674 0         0 my $m = 1;
675 0         0 foreach( $m = 1; $m < @matches; ++$m ) {
676 0 0       0 if ( defined $matches[$m] ) {
677 0         0 my $r = $matches[$m];
678 0         0 $r = substr( $r, 1, length( $r ) -2 );
679 0         0 $replacement =~ s/##$m/$r/;
680             }
681             else {
682             # this only occurs for $m = 1, i.e. when the optional
683             # argument was not specified in the text, therefore,
684             # we replace it by the default
685 0         0 $replacement =~ s/##$m/$entry->{optarg}/;
686             }
687             }
688 0         0 $document =~ s/$regexp/$replacement/;
689             }
690             }
691 67 50       247 if ( $debug ) {
692 0         0 say STDERR "=== Prepped ======================================================";
693 0         0 say STDERR $document;
694             }
695              
696            
697             # Parse the document
698 67         752 my $doc = SpeL::Object::Document->new();
699 67         629 $doc->{ElementList} = SpeL::Object::ElementList->new();
700 67         324 $doc->{ElementList}->{Element} = [];
701              
702 67         151 my $result;
703 67 50       4498 if ( $result = ( $document . "\n\\endinput" ) =~ $SpeL::Parser::Chunk::chunk ) {
704 67 50       616 if( exists $/{Document}->{ElementList}->{Element} ) {
705 67         271 push @{$doc->{ElementList}->{Element}},
706 67         165 @{$/{Document}->{ElementList}->{Element}};
  67         345  
707             }
708             }
709             else {
710 0         0 $![0] =~ /^(.*)__(\d+),(\d+)__(.*)$/;
711 0         0 $![0] = $1 . $self->_errloc( $3 ) . $4;
712 0         0 die( "Error: failed to parse $filename\n" .
713             "=> " . join( "\n ", @! ) . "\n" );
714             }
715            
716 67 50       789 if ( $debug ) {
717 0         0 say STDERR "=== Parsed ======================================================";
718 0         0 say STDERR Data::Dumper->Dump( [ $doc ], [ 'tree' ] );
719             }
720              
721 67         2645 return $doc;
722             }
723              
724              
725              
726             sub parseDocument {
727 67     67 1 151471 my $self = shift;
728 67         640 my ( $filename, $debug ) = @_;
729              
730 67         5971 $self->{path} = dirname( $filename );
731              
732 67         680 my $file = IO::File->new();
733 67 50       3205 $file->open( "<$filename" )
734             or croak( "Error: canot open '$filename' for reading\n" );
735 67         15159 @{$self->{lines}} = <$file>;
  67         438  
736              
737             # setup lineposition bookkeeping
738 67         200 my $firstlineindex = 0;
739 67         224 @{$self->{lineinfo}} =
740 223         345 map{ my $retval = $firstlineindex;
741 223         392 $firstlineindex += length( $_ );
742 223         506 $retval
743 67         221 } @{$self->{lines}};
  67         238  
744 67         187 push @{$self->{lineinfo}}, $self->{lineinfo}->[-1] + 1;
  67         343  
745              
746             # parse
747 67         164 my $document = join( '', @{$self->{lines}} );
  67         348  
748 67         333 $document =~ s/^\{(.*)\}$/$1/s; # solve docstrip's problem of {Change History}
749 67         399 $self->{tree} = $self->parseDocumentString( $document, $filename, $debug );
750              
751 67         378 delete $self->{lines};
752 67         2967 delete $self->{lineinfo};
753             }
754              
755              
756             sub object {
757 104     104 1 12917 my $self = shift;
758 104         1043 return $self;
759             }
760              
761              
762             sub _report {
763 0     0     my ( $match ) = @_;
764 0           return "__$match->{matchpos},$match->{matchline}__";
765             }
766              
767              
768             sub _errloc {
769 0     0     my $self = shift;
770 0           my ( $matchline ) = @_;
771 0           return "line $matchline";
772             }
773              
774              
775             1;
776              
777             __END__