| blib/lib/LaTeX/Replicase.pm | |||
|---|---|---|---|
| Criterion | Covered | Total | % |
| statement | 506 | 533 | 94.9 |
| branch | 364 | 434 | 83.8 |
| condition | 174 | 237 | 73.4 |
| subroutine | 21 | 21 | 100.0 |
| pod | 2 | 2 | 100.0 |
| total | 1067 | 1227 | 86.9 |
| line | stmt | bran | cond | sub | pod | time | code |
|---|---|---|---|---|---|---|---|
| 1 | package LaTeX::Replicase; | ||||||
| 2 | |||||||
| 3 | 2 | 2 | 173671 | use 5.010; | |||
| 2 | 4 | ||||||
| 4 | 2 | 2 | 8 | use strict; | |||
| 2 | 6 | ||||||
| 2 | 39 | ||||||
| 5 | 2 | 2 | 9 | use warnings; | |||
| 2 | 2 | ||||||
| 2 | 105 | ||||||
| 6 | 2 | 2 | 7 | use utf8; | |||
| 2 | 4 | ||||||
| 2 | 10 | ||||||
| 7 | |||||||
| 8 | 2 | 2 | 64 | use File::Basename qw(fileparse); | |||
| 2 | 3 | ||||||
| 2 | 163 | ||||||
| 9 | 2 | 2 | 8 | use File::Path qw(make_path); | |||
| 2 | 2 | ||||||
| 2 | 85 | ||||||
| 10 | 2 | 2 | 786 | use File::Compare; | |||
| 2 | 1444 | ||||||
| 2 | 95 | ||||||
| 11 | 2 | 2 | 13 | use Carp; | |||
| 2 | 3 | ||||||
| 2 | 13118 | ||||||
| 12 | |||||||
| 13 | require Exporter; | ||||||
| 14 | |||||||
| 15 | our @ISA = qw(Exporter); | ||||||
| 16 | |||||||
| 17 | our %EXPORT_TAGS = ('all' => [ qw( | ||||||
| 18 | replication | ||||||
| 19 | tex_escape | ||||||
| 20 | ) ], | ||||||
| 21 | ); | ||||||
| 22 | |||||||
| 23 | our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); | ||||||
| 24 | our @EXPORT = qw( ); | ||||||
| 25 | |||||||
| 26 | our $VERSION = '0.705'; | ||||||
| 27 | our $DEBUG; $DEBUG = 0 unless defined $DEBUG; | ||||||
| 28 | our @logs; | ||||||
| 29 | our $nlo = 1; # Number Line Output, start of 1 | ||||||
| 30 | |||||||
| 31 | sub tex_escape { | ||||||
| 32 | 15 | 100 | 66 | 15 | 1 | 209678 | return if ! $_[0] or $_[0] =~/^[a-zA-Z0-9,=:;!\.\s\+\-\*]+$/ or $_[0] =~s/^%%%://; |
| 100 | |||||||
| 33 | |||||||
| 34 | 11 | 33 | for( $_[0] ) { | ||||
| 35 | 11 | 28 | s/\\/\\textbackslash\{\}/g; | ||||
| 36 | 11 | 81 | s/([%}{_&\$\#])/\\$1/g; # masking active symbols | ||||
| 37 | 11 | 57 | s/\^/\\$&\{\}/g; # ^ --> \^{} | ||||
| 38 | |||||||
| 39 | 11 | 100 | 66 | 68 | s/~/\\texttt\{\\~\{\}\}/g if $_[1] && $_[1] =~/~/; # tilde (~) --> \texttt{\~{}} | ||
| 40 | } | ||||||
| 41 | } | ||||||
| 42 | |||||||
| 43 | |||||||
| 44 | sub replication { | ||||||
| 45 | 23 | 23 | 1 | 278749 | my( $source, $info, %op ) = @_; | ||
| 46 | |||||||
| 47 | 23 | 100 | 51 | our $DEBUG; $DEBUG = $op{debug} if defined $op{debug}; | |||
| 23 | 130 | ||||||
| 48 | 23 | 52 | $DEBUG += 0; | ||||
| 49 | 23 | 85 | our @logs = (); | ||||
| 50 | |||||||
| 51 | 23 | 100 | 66 | 133 | if( defined( $source ) && length( $source ) ) { | ||
| 52 | |||||||
| 53 | 22 | 100 | 90 | if(ref \$source eq 'SCALAR') { | |||
| 100 | |||||||
| 54 | 19 | 42 | for( $source ) { | ||||
| 55 | 19 | 93 | s/^\s+//; | ||||
| 56 | 19 | 53 | s/\s+.*//s; | ||||
| 57 | |||||||
| 58 | 19 | 50 | 914 | $_ = (glob)[0] if $^O =~/(?:linux|bsd|darwin|solaris|sunos)/; | |||
| 59 | } | ||||||
| 60 | } | ||||||
| 61 | elsif(ref $source ne 'ARRAY') { | ||||||
| 62 | 1 | 3 | $_ = "!!! ERROR#6: invalid FILE or ARRAY input!"; | ||||
| 63 | 1 | 50 | 4 | $op{silent} or carp $_; | |||
| 64 | |||||||
| 65 | 1 | 3 | push @logs, $_; | ||||
| 66 | 1 | 4 | return \@logs; | ||||
| 67 | } | ||||||
| 68 | |||||||
| 69 | } | ||||||
| 70 | else { | ||||||
| 71 | 1 | 5 | $_ = "!!! ERROR#0: undefined input FILE or ARRAY!"; | ||||
| 72 | 1 | 50 | 5 | $op{silent} or carp $_; | |||
| 73 | |||||||
| 74 | 1 | 3 | push @logs, $_; | ||||
| 75 | 1 | 8 | return \@logs; | ||||
| 76 | } | ||||||
| 77 | |||||||
| 78 | 21 | 100 | 82 | push @logs, "--> Checking source data: '$source'" if $DEBUG; | |||
| 79 | |||||||
| 80 | 21 | 100 | 100 | 368 | if((ref \$source eq 'SCALAR' and ! -s $source) or (ref $source eq 'ARRAY' and ! @$source)) { | ||
| 66 | |||||||
| 66 | |||||||
| 81 | 1 | 4 | $_ = "!!! ERROR#1: source ('$source') does NOT exist or is EMPTY!"; | ||||
| 82 | 1 | 50 | 5 | $op{silent} or carp $_; | |||
| 83 | |||||||
| 84 | 1 | 3 | push @logs, $_; | ||||
| 85 | 1 | 5 | return \@logs; | ||||
| 86 | } | ||||||
| 87 | |||||||
| 88 | # global data of TeX file | ||||||
| 89 | 20 | 100 | 100 | 170 | unless( $info | ||
| 66 | |||||||
| 90 | and (( ref $info eq 'HASH' and %$info ) or (ref $info eq 'ARRAY' and @$info )) | ||||||
| 91 | ) { | ||||||
| 92 | 2 | 5 | $_= "!!! ERROR#2: EMPTY or WRONG data!"; | ||||
| 93 | 2 | 50 | 9 | $op{silent} or carp $_; | |||
| 94 | |||||||
| 95 | 2 | 5 | push @logs, $_; | ||||
| 96 | 2 | 11 | return \@logs; | ||||
| 97 | } | ||||||
| 98 | |||||||
| 99 | # environments: global for %%%V:, %%%VAR: ; and local for %%%VAR: | ||||||
| 100 | 18 | 40 | my $data = my $vardata = $info; | ||||
| 101 | |||||||
| 102 | 18 | 51 | my( $filename, $dir ); | ||||
| 103 | 18 | 100 | 53 | if( ref \$source eq 'SCALAR') { | |||
| 104 | 16 | 476 | ( $filename, $dir, my($ext)) = fileparse( $source ); | ||||
| 105 | } | ||||||
| 106 | else { # for ARRAY input | ||||||
| 107 | 2 | 4 | $filename = 'ready.tex'; | ||||
| 108 | 2 | 5 | $dir = '.'; | ||||
| 109 | } | ||||||
| 110 | |||||||
| 111 | 18 | 45 | my( $fh, $ofile ); | ||||
| 112 | 18 | 100 | 66 | 100 | if( defined( $_ = $op{ofile} ) && length ) { | ||
| 113 | 17 | 100 | 64 | if(/::STDOUT$/) { | |||
| 114 | 1 | 3 | $fh = $ofile = $_; | ||||
| 115 | } | ||||||
| 116 | else { | ||||||
| 117 | 16 | 48 | s/^\s+//; | ||||
| 118 | 16 | 53 | s/\s+.*//s; | ||||
| 119 | 16 | 50 | 691 | $ofile = ( $^O =~/(?:linux|bsd|darwin|solaris|sunos)/ ) ? (glob)[0] : $_; | |||
| 120 | } | ||||||
| 121 | } | ||||||
| 122 | else { | ||||||
| 123 | 1 | 33 | 6 | my $outdir = $op{outdir} // "$dir/$$"; # Target dir for ready TeX file | |||
| 124 | 1 | 50 | 4 | if( length $outdir ) { | |||
| 125 | 1 | 4 | for( $outdir ) { | ||||
| 126 | 1 | 4 | s/^\s+//; | ||||
| 127 | 1 | 4 | s/\s+.*//s; | ||||
| 128 | |||||||
| 129 | 1 | 50 | 50 | $_ = (glob)[0] if $^O =~/(?:linux|bsd|darwin|solaris|sunos)/; | |||
| 130 | } | ||||||
| 131 | } | ||||||
| 132 | else { # for $outdir = '' | ||||||
| 133 | 0 | 0 | $outdir = "./$$"; | ||||
| 134 | } | ||||||
| 135 | |||||||
| 136 | 1 | 50 | 13 | unless( -d $outdir ) { | |||
| 137 | 1 | 333 | make_path( $outdir, {error => \my $err} ); | ||||
| 138 | |||||||
| 139 | 1 | 50 | 33 | 13 | if ($err && @$err) { | ||
| 140 | |||||||
| 141 | 0 | 0 | for my $diag (@$err) { | ||||
| 142 | 0 | 0 | my( $path, $message ) = %$diag; | ||||
| 143 | 0 | 0 | 0 | 0 | $_ = ( $path && length( $path ) ) ? | ||
| 144 | "!!! ERROR#7: ('$path' creation problem) $message" : | ||||||
| 145 | "!!! ERROR#8: (general error) $message"; | ||||||
| 146 | 0 | 0 | 0 | $op{silent} or carp $_; | |||
| 147 | 0 | 0 | push @logs, $_; | ||||
| 148 | } | ||||||
| 149 | |||||||
| 150 | 0 | 0 | return \@logs; | ||||
| 151 | } | ||||||
| 152 | |||||||
| 153 | } | ||||||
| 154 | |||||||
| 155 | 1 | 4 | $ofile = "$outdir/$filename"; | ||||
| 156 | } | ||||||
| 157 | |||||||
| 158 | 18 | 100 | 69 | push @logs, "--> Using '$ofile' file as output" if $DEBUG; | |||
| 159 | |||||||
| 160 | # new file must be different | ||||||
| 161 | 18 | 100 | 66 | 323 | if( -s $ofile and ref \$source eq 'SCALAR' | ||
| 66 | |||||||
| 66 | |||||||
| 162 | and ( | ||||||
| 163 | ( $source eq $ofile and compare( $source, $ofile ) == 0 ) | ||||||
| 164 | or | ||||||
| 165 | ( join(',', stat $source) eq join(',', stat $ofile) ) | ||||||
| 166 | ) | ||||||
| 167 | ) { | ||||||
| 168 | 1 | 192 | $_= "!!! ERROR#3: Input (template) & output files match. Can't overwrite template file!"; | ||||
| 169 | 1 | 50 | 6 | $op{silent} or carp $_; | |||
| 170 | |||||||
| 171 | 1 | 4 | push @logs, $_; | ||||
| 172 | 1 | 6 | return \@logs; | ||||
| 173 | } | ||||||
| 174 | |||||||
| 175 | 17 | 44 | my $TEMPLATE; | ||||
| 176 | 17 | 100 | 64 | if( ref \$source eq 'SCALAR') { | |||
| 177 | 15 | 100 | 48 | my $mode = $op{utf8} ? ':utf8' : ''; | |||
| 178 | |||||||
| 179 | 15 | 100 | 38 | push @logs, "--> Open '$source'" if $DEBUG; | |||
| 180 | |||||||
| 181 | 15 | 50 | 742 | open $TEMPLATE, "<:raw$mode", $source or do{ | |||
| 182 | 0 | 0 | $_= "!!! ERROR#4: $!"; | ||||
| 183 | 0 | 0 | 0 | $op{silent} or carp $_; | |||
| 184 | |||||||
| 185 | 0 | 0 | push @logs, $_; | ||||
| 186 | 0 | 0 | return \@logs; | ||||
| 187 | }; | ||||||
| 188 | } | ||||||
| 189 | |||||||
| 190 | 17 | 100 | 72 | unless( $fh ) { # it's not "::STDOUT" | |||
| 191 | 16 | 100 | 57 | my $mode = $op{utf8} ? ':encoding(utf8)' : ''; | |||
| 192 | |||||||
| 193 | 16 | 100 | 52 | push @logs, "--> Open '$ofile'" if $DEBUG; | |||
| 194 | |||||||
| 195 | 1 | 50 | 1 | 1072 | open $fh, ">$mode", $ofile or do{ | ||
| 1 | 18 | ||||||
| 1 | 6 | ||||||
| 16 | 2344 | ||||||
| 196 | 0 | 0 | $_= "!!! ERROR#5: $!"; | ||||
| 197 | 0 | 0 | 0 | $op{silent} or carp $_; | |||
| 198 | |||||||
| 199 | 0 | 0 | push @logs, $_; | ||||
| 200 | 0 | 0 | return \@logs; | ||||
| 201 | }; | ||||||
| 202 | } | ||||||
| 203 | |||||||
| 204 | 17 | 1600 | $nlo = 1; | ||||
| 205 | 17 | 27 | my $chkVAR = 0; # check %%%VAR for ARRAY|HASH|SCALAR|REF->SCALAR type | ||||
| 206 | 17 | 81 | my $key; | ||||
| 207 | my $tdz; # flag of The Dead Zone | ||||||
| 208 | 17 | 0 | my @columns; | ||||
| 209 | 17 | 31 | my $end = 0; | ||||
| 210 | |||||||
| 211 | =for comment | ||||||
| 212 | =begin comment | ||||||
| 213 | @columns: | ||||||
| 214 | [...]: -- table columns | ||||||
| 215 | [...]{...} -- descriptions (properties) of table columns: | ||||||
| 216 | {ki} -- name (key || index ) of a variable from $data->{ $key } | ||||||
| 217 | {%} -- NO \par | ||||||
| 218 | {v} -- to paste by default text (located in template) if variable =~/^\x{001}$/ | ||||||
| 219 | {p} -- to paste text on right | ||||||
| 220 | {head}[...] -- TeX strings before %%%V: | ||||||
| 221 | {tail}[...] -- TeX strings after %%%V: | ||||||
| 222 | {eX}[...] -- indices of {head} that eXcept for the first and last elements and rows of %%%VAR: | ||||||
| 223 | =end comment | ||||||
| 224 | =cut | ||||||
| 225 | |||||||
| 226 | 17 | 100 | 52 | if( $TEMPLATE ) { | |||
| 227 | 15 | 506 | while( my $z = <$TEMPLATE> ) { | ||||
| 228 | |||||||
| 229 | 925 | 2268 | $end = &_line_decryption( $fh, $info, \$z, \$data, \$vardata, \$chkVAR, \$key, \$tdz, \@columns, \%op ); | ||||
| 230 | |||||||
| 231 | 925 | 100 | 2375 | if( $end ) { | |||
| 232 | 12 | 100 | 27 | print { $fh } <$TEMPLATE> if $end != 3; # NOT ( \endinput AND \end{document} ) | |||
| 9 | 163 | ||||||
| 233 | |||||||
| 234 | 12 | 31 | last; #--> Exit template | ||||
| 235 | } | ||||||
| 236 | 913 | 2914 | undef $z; | ||||
| 237 | |||||||
| 238 | } | ||||||
| 239 | 15 | 252 | close $TEMPLATE; | ||||
| 240 | } | ||||||
| 241 | else { | ||||||
| 242 | 2 | 8 | for my $z ( @$source ) { | ||||
| 243 | |||||||
| 244 | 264 | 50 | 415 | if( $end ) { | |||
| 245 | 0 | 0 | print { $fh } $z; | ||||
| 0 | 0 | ||||||
| 246 | } | ||||||
| 247 | else { | ||||||
| 248 | 264 | 583 | $end = &_line_decryption( $fh, $info, \$z, \$data, \$vardata, \$chkVAR, \$key, \$tdz, \@columns, \%op ); | ||||
| 249 | |||||||
| 250 | 264 | 100 | 582 | last if $end == 3; # \endinput OR \end{document} | |||
| 251 | } | ||||||
| 252 | } | ||||||
| 253 | } | ||||||
| 254 | |||||||
| 255 | 17 | 100 | 100 | 61 | if( ! $end and defined( $key ) ) { | ||
| 256 | 2 | 8 | &_var_output( $fh, $key, $vardata, \@columns, \%op ); | ||||
| 257 | |||||||
| 258 | 2 | 5 | $_ = "~~> l.EOF. WARNING#1: Missing '%%%ENDx' tag for '$key'"; | ||||
| 259 | 2 | 100 | 23 | $op{silent} or carp $_; | |||
| 260 | 2 | 396 | push @logs, $_; | ||||
| 261 | } | ||||||
| 262 | |||||||
| 263 | 17 | 100 | 1187 | $ofile =~/::STDOUT$/ or close $fh; | |||
| 264 | |||||||
| 265 | 17 | 100 | 136 | @logs or return; | |||
| 266 | 10 | 118 | return \@logs; | ||||
| 267 | } | ||||||
| 268 | |||||||
| 269 | #--------------------- | ||||||
| 270 | # Internal function(s) | ||||||
| 271 | |||||||
| 272 | sub _line_decryption { | ||||||
| 273 | 1189 | 1189 | 2621 | my( $fh, $info, $z, $data, $vardata, $chkVAR, $key, $tdz, $columns, $op ) = @_; | |||
| 274 | |||||||
| 275 | 1189 | 1486 | our $DEBUG; | ||||
| 276 | 1189 | 1437 | our @logs; | ||||
| 277 | 1189 | 1455 | our $nlo; | ||||
| 278 | |||||||
| 279 | 1189 | 100 | 2854 | if( defined $$key ) { # We are in VAR-structure | |||
| 100 | |||||||
| 280 | |||||||
| 281 | 533 | 100 | 2150 | return 0 unless $$z =~/%%%[AETV]\S*:/; # Nope control tags --> drop TeX line | |||
| 282 | |||||||
| 283 | 411 | 100 | 66 | 2802 | if( $$z =~/%%%(?:END(? |
||
| 50 | 100 | ||||||
| 0 | |||||||
| 33 | |||||||
| 66 | |||||||
| 284 | 80 | 518 | my $t = $+{t}; | ||||
| 285 | |||||||
| 286 | 80 | 284 | my $end = &_var_output( $fh, $$key, $$vardata, $columns, $op ); | ||||
| 287 | |||||||
| 288 | # Clear the VAR-structure for the next external VARiable | ||||||
| 289 | 80 | 126 | $$chkVAR = 0; | ||||
| 290 | 80 | 140 | undef $$key; | ||||
| 291 | 80 | 369 | @$columns = (); | ||||
| 292 | |||||||
| 293 | 80 | 100 | 151 | return $end if $end; | |||
| 294 | |||||||
| 295 | 78 | 100 | 100 | 196 | return 1 if $t && $t eq 'T'; # END of Template area --> output everything to the end of template without substitution | ||
| 296 | |||||||
| 297 | 75 | 100 | 66 | 163 | undef $$tdz if $t && $t eq 'Z'; | ||
| 298 | |||||||
| 299 | 75 | 100 | 482 | return 0 if $$z =~/%%%ENDZ?:/; # end of %%%VAR: tag | |||
| 300 | |||||||
| 301 | 11 | 100 | 69 | if( $$z =~/%%%+TDZ:/) { # The Dead Zone | |||
| 302 | 7 | 14 | $$tdz = 1; | ||||
| 303 | 7 | 21 | return 0; | ||||
| 304 | } | ||||||
| 305 | |||||||
| 306 | } | ||||||
| 307 | elsif( (ref $$vardata eq 'HASH' and ( | ||||||
| 308 | ref $$vardata->{ $$key } eq 'HASH' | ||||||
| 309 | or ref $$vardata->{ $$key } eq 'ARRAY' | ||||||
| 310 | or ref \$$vardata->{ $$key } eq 'SCALAR' | ||||||
| 311 | or ref $$vardata->{ $$key } eq 'SCALAR' # REF->SCALAR | ||||||
| 312 | ) | ||||||
| 313 | ) | ||||||
| 314 | or (ref $$vardata eq 'ARRAY' and ( | ||||||
| 315 | ref $$vardata->[ $$key ] eq 'HASH' | ||||||
| 316 | or ref $$vardata->[ $$key ] eq 'ARRAY' | ||||||
| 317 | or ref \$$vardata->[ $$key ] eq 'SCALAR' | ||||||
| 318 | or ref $$vardata->[ $$key ] eq 'SCALAR' # REF->SCALAR | ||||||
| 319 | ) | ||||||
| 320 | ) | ||||||
| 321 | ) { | ||||||
| 322 | 331 | 100 | 808 | my $vk = ref $$vardata eq 'HASH' ? $$vardata->{ $$key } : $$vardata->[ $$key ]; | |||
| 323 | 331 | 100 | 100 | 1060 | my $sclr = (ref \$vk eq 'SCALAR' or ref $vk eq 'SCALAR') ? 1 : 0; | ||
| 324 | |||||||
| 325 | # Index of column in target table | ||||||
| 326 | 331 | 100 | 100 | 1193 | my $j = ( @$columns && exists( $columns->[-1]{ki} ) ) ? | ||
| 50 | |||||||
| 327 | @$columns : | ||||||
| 328 | ($#$columns // 0); | ||||||
| 329 | 331 | 100 | 100 | 1007 | $j = 0 if $j < 0 or $sclr; | ||
| 330 | |||||||
| 331 | 331 | 100 | 100 | 3758 | if( ! $sclr and $$z =~/^(.*?)\s?%%%+V:\s*([^\s:%#]+)(%?)\s?(.*)/) { | ||
| 100 | 100 | ||||||
| 332 | # the non-SCALAR V-variable is nested in a VAR-structure | ||||||
| 333 | 126 | 307 | my $dV = $1; # Value, by default | ||||
| 334 | 126 | 280 | my $ki = $2; # name (key or index) of V-variable | ||||
| 335 | 126 | 233 | my $Np = $3; # NO \par | ||||
| 336 | 126 | 222 | my $paste = $4; # on right | ||||
| 337 | |||||||
| 338 | 126 | 100 | 100 | 664 | if( $$chkVAR == 0b0001) { # V-variable is in {HASH|ARRAY}.ARRAY of VAR-structure | ||
| 100 | 66 | ||||||
| 100 | 66 | ||||||
| 100 | 66 | ||||||
| 339 | |||||||
| 340 | 32 | 100 | 66 | 266 | if( $ki eq '@') { | ||
| 100 | 66 | ||||||
| 50 | |||||||
| 341 | 1 | 4 | $ki = '0-'; # ALL elements | ||||
| 342 | 1 | 4 | $columns->[$j]{ki} = $ki; # starting index (unnamed meaning) | ||||
| 343 | } | ||||||
| 344 | elsif( $ki =~/^\-*(\d+)$/ && ($1 < @$vk or ($ki < 0 && $1 == @$vk)) ) { | ||||||
| 345 | # specific indices, e.g.: 0 or 3 or -1 | ||||||
| 346 | 16 | 64 | $columns->[$j]{ki} = $ki; | ||||
| 347 | } | ||||||
| 348 | elsif( $ki =~/^[\d,\-]+$/) { | ||||||
| 349 | # mixed indexes, e.g.: 1-3,6-7-9,-,4,-5,0,7- or 3- (i.e. 3..arr_end) or 0-5 (0..5) or -1- (-1,-2,..arr_start) | ||||||
| 350 | 15 | 34 | for( $ki ) { | ||||
| 351 | 15 | 49 | s/\-+/-/g; | ||||
| 352 | 15 | 43 | s/,+/,/g; | ||||
| 353 | } | ||||||
| 354 | 15 | 42 | $columns->[$j]{ki} = $ki; | ||||
| 355 | } | ||||||
| 356 | else { | ||||||
| 357 | 0 | 0 | 0 | 0 | push @logs, "~~> l.$. WARNING#8: ARRAY index is not numeric in %%%V:". $ki if $DEBUG or ! $op->{ignore}; | ||
| 358 | } | ||||||
| 359 | |||||||
| 360 | } | ||||||
| 361 | elsif( $$chkVAR == 0b0010) { # V-variable is in ARRAY.HASH of VAR-structure | ||||||
| 362 | |||||||
| 363 | 38 | 87 | for my $d ( @$vk ) { | ||||
| 364 | 38 | 50 | 107 | if( exists $d->{$ki} ) { | |||
| 365 | 38 | 105 | $columns->[$j]{ki} = $ki; # save variable name in j-th column | ||||
| 366 | 38 | 63 | last; | ||||
| 367 | } | ||||||
| 368 | } | ||||||
| 369 | } | ||||||
| 370 | elsif( $$chkVAR == 0b0100 or $$chkVAR == 0b01000 ) { # V-variable is SCALAR (or REF->SCALAR) in regular ARRAY of VAR-structure | ||||||
| 371 | |||||||
| 372 | 17 | 100 | 33 | 113 | if( $ki eq '@') { | ||
| 100 | 66 | ||||||
| 100 | |||||||
| 373 | 5 | 11 | $ki = '0-'; # ALL elements | ||||
| 374 | 5 | 15 | $columns->[$j]{ki} = $ki; # starting index (unnamed meaning) | ||||
| 375 | } | ||||||
| 376 | elsif( $ki =~/^\-*(\d+)$/ && ($1 < @$vk or ($ki < 0 && $1 == @$vk)) ) { | ||||||
| 377 | # specific indices, e.g.: 0 or 3 or -1 | ||||||
| 378 | 4 | 14 | $columns->[$j]{ki} = $ki; | ||||
| 379 | } | ||||||
| 380 | elsif( $ki =~/^[\d,\-]+$/) { | ||||||
| 381 | # mixed indexes, e.g.: 1-3,6-7-9,-,4,-5,0,7- or 3- (i.e. 3..arr_end) or 0-5 (0..5) or -1- (-1,-2,..arr_start) | ||||||
| 382 | 7 | 28 | for( $ki ) { | ||||
| 383 | 7 | 51 | s/\-+/-/g; | ||||
| 384 | 7 | 29 | s/,+/,/g; | ||||
| 385 | } | ||||||
| 386 | 7 | 22 | $columns->[$j]{ki} = $ki; | ||||
| 387 | } | ||||||
| 388 | |||||||
| 389 | } | ||||||
| 390 | elsif( ref $vk eq 'HASH' | ||||||
| 391 | and exists( $vk->{$ki} ) | ||||||
| 392 | and ( ref \$vk->{$ki} eq 'SCALAR' | ||||||
| 393 | or ref $vk->{$ki} eq 'SCALAR' | ||||||
| 394 | or ( $ki eq '@' | ||||||
| 395 | and ref $vk->{$ki} eq 'ARRAY' | ||||||
| 396 | ) | ||||||
| 397 | ) | ||||||
| 398 | ) { | ||||||
| 399 | 33 | 76 | $columns->[$j]{ki} = $ki; # save variable key in j-th element | ||||
| 400 | } | ||||||
| 401 | |||||||
| 402 | 126 | 100 | 416 | &_set_column( $dV, $Np, $paste, $columns->[$j] ) if exists $columns->[$j]{ki}; | |||
| 403 | } | ||||||
| 404 | elsif( $$z =~/(? %?)/ |
||||||
| 405 | or $$z =~/^\s*%%%+ADD(? %?)\s?(? |
||||||
| 406 | ) { | ||||||
| 407 | 200 | 1273 | my $s = $+{s}; | ||||
| 408 | |||||||
| 409 | 200 | 100 | 826 | if( $+{p} ) { | |||
| 410 | 51 | 100 | 121 | length($s) or return 0; | |||
| 411 | } | ||||||
| 412 | else { | ||||||
| 413 | 149 | 258 | $s .= "\n"; | ||||
| 414 | } | ||||||
| 415 | |||||||
| 416 | 199 | 100 | 1137 | if( $+{t} eq 'A') { # %%%ADDA: | |||
| 100 | |||||||
| 417 | 3 | 7 | push @{ $columns->[$j]{head} }, $s; | ||||
| 3 | 11 | ||||||
| 418 | 3 | 9 | $columns->[$j]{ki} = '%%%ADDA'; # phantom | ||||
| 419 | 3 | 9 | $columns->[$j]{'%'} = 1; | ||||
| 420 | } | ||||||
| 421 | elsif( $+{t} eq 'E') { # %%%ADDE: | ||||||
| 422 | 30 | 50 | 66 | 165 | if( @$columns && ( $sclr or (exists( $columns->[-1]{ki} ) && ! $columns->[$j]) ) ) { | ||
| 33 | |||||||
| 423 | 30 | 40 | push @{ $columns->[-1]{tail} }, $s; | ||||
| 30 | 106 | ||||||
| 424 | } | ||||||
| 425 | else { | ||||||
| 426 | 0 | 0 | push @{ $columns->[$j]{head} }, $s; | ||||
| 0 | 0 | ||||||
| 427 | } | ||||||
| 428 | } | ||||||
| 429 | else { # %%%ADD[X]: | ||||||
| 430 | 166 | 277 | push @{ $columns->[$j]{head} }, $s; | ||||
| 166 | 666 | ||||||
| 431 | 166 | 100 | 100 | 899 | $columns->[$j]{eX}{ $#{ $columns->[$j]{head} } } = undef if ! $sclr and $+{t} eq 'X'; # $$chkVAR && ... %%%ADDX: | ||
| 33 | 132 | ||||||
| 432 | } | ||||||
| 433 | } | ||||||
| 434 | |||||||
| 435 | 330 | 1000 | return 0; | ||||
| 436 | } | ||||||
| 437 | else { | ||||||
| 438 | 0 | 0 | return 0; | ||||
| 439 | } | ||||||
| 440 | |||||||
| 441 | } | ||||||
| 442 | elsif( $$z =~/%%%+END(? |
||||||
| 443 | |||||||
| 444 | # Clear the VAR-structure for the next external variable | ||||||
| 445 | 39 | 70 | $$chkVAR = 0; | ||||
| 446 | 39 | 65 | undef $$key; | ||||
| 447 | 39 | 67 | @$columns = (); | ||||
| 448 | |||||||
| 449 | 39 | 100 | 366 | return 1 if $+{t} eq 'T'; # END of Template area --> output everything to the end of template without substitution | |||
| 450 | |||||||
| 451 | 33 | 100 | 167 | undef $$tdz if $+{t} eq 'Z'; # End of TDZ | |||
| 452 | 33 | 85 | return 0; | ||||
| 453 | } | ||||||
| 454 | |||||||
| 455 | 621 | 100 | 1436 | $$tdz = 1 if $$z =~s/^\s*%%%+TDZ:\s?[\r\n]*//; # The Dead Zone | |||
| 456 | |||||||
| 457 | 621 | 100 | 1166 | if( $$tdz ) { # The Dead Zone is ON | |||
| 458 | 124 | 100 | 290 | if( length $$z ) {# Output TeX | |||
| 459 | 116 | 155 | print { $fh } $$z; | ||||
| 116 | 347 | ||||||
| 460 | 116 | 180 | ++$nlo; | ||||
| 461 | } | ||||||
| 462 | 124 | 287 | return 0; | ||||
| 463 | } | ||||||
| 464 | |||||||
| 465 | 497 | 100 | 2683 | if( $$z =~/(.*?)\s?%%%+VAR:\s*([^\s:%#]+)(%?)\s?(.*)/) { | |||
| 100 | |||||||
| 100 | |||||||
| 466 | 99 | 290 | my $before = $1; | ||||
| 467 | 99 | 202 | my $k = $2; # name (key) | ||||
| 468 | 99 | 220 | my $Np = $3; # NO \par | ||||
| 469 | 99 | 204 | my $paste = $4; # on right text for SCALAR only | ||||
| 470 | |||||||
| 471 | # root or global structure (environment) | ||||||
| 472 | 99 | 100 | 240 | my $vd = ( $k =~s/^\/+//) ? $info : $$data; | |||
| 473 | |||||||
| 474 | 99 | 164 | my $x; # for unknown/undefined sub-key | ||||
| 475 | |||||||
| 476 | # Search nested sub-keys | ||||||
| 477 | 99 | 302 | for my $sk ( split '/', $k ) { | ||||
| 478 | 100 | 157 | $$vardata = $vd; | ||||
| 479 | 100 | 50 | 195 | length( $sk ) or next; | |||
| 480 | |||||||
| 481 | 100 | 100 | 66 | 658 | if( $sk =~/^\d+$/ && ref $vd eq 'ARRAY' and defined( $vd->[$sk] )) { | ||
| 100 | 66 | ||||||
| 66 | |||||||
| 482 | 3 | 100 | 8 | last if &_data_redef( $sk, $vd->[$sk], \$k, \$vd, \$x ); | |||
| 483 | } | ||||||
| 484 | elsif( ref $vd eq 'HASH' and exists( $vd->{$sk} )) { | ||||||
| 485 | 82 | 100 | 244 | last if &_data_redef( $sk, $vd->{$sk}, \$k, \$vd, \$x ); | |||
| 486 | } | ||||||
| 487 | else { | ||||||
| 488 | 15 | 26 | $x = $sk; | ||||
| 489 | 15 | 24 | last; | ||||
| 490 | } | ||||||
| 491 | } | ||||||
| 492 | |||||||
| 493 | # Clear the VAR-structure for a new variable | ||||||
| 494 | 99 | 183 | $$chkVAR = 0; | ||||
| 495 | 99 | 153 | undef $$key; | ||||
| 496 | 99 | 180 | @$columns = (); | ||||
| 497 | |||||||
| 498 | 99 | 100 | 194 | if( $x ) { | |||
| 499 | 16 | 100 | 100 | 101 | push @logs, "~~> l.$. WARNING#2: unknown or undef ARRAY|HASH|SCALAR|REF.SCALAR of sub-key '$x' in %%%VAR:". $k if $DEBUG or ! $op->{ignore}; | ||
| 500 | |||||||
| 501 | 16 | 27 | $$vardata = $$data; | ||||
| 502 | 16 | 22 | print { $fh } $$z; | ||||
| 16 | 46 | ||||||
| 503 | 16 | 24 | ++$nlo; | ||||
| 504 | 16 | 41 | return 0; | ||||
| 505 | } | ||||||
| 506 | |||||||
| 507 | # key or sub-...sub-key is found | ||||||
| 508 | 83 | 100 | 219 | push @logs, "--> l.$. Found %%%VAR:". $k if $DEBUG; | |||
| 509 | |||||||
| 510 | 83 | 100 | 245 | my $vk = ref $$vardata eq 'HASH' ? $$vardata->{$k} : $$vardata->[$k]; | |||
| 511 | |||||||
| 512 | 83 | 100 | 100 | 210 | push @logs, "~~> l.$. NOT defined key in %%%VAR:". $k if ! defined($vk) && $DEBUG; | ||
| 513 | |||||||
| 514 | 83 | 100 | 205 | return 0 if &_chk_var( $fh, $k, $vk, $Np, \$paste, \$before, $chkVAR, $columns, $z, $op ); | |||
| 515 | |||||||
| 516 | # push @logs, "--> l.$. Remember key = '$k' (chkVAR=$$chkVAR), type: ".ref($vk) if $DEBUG; ###AG | ||||||
| 517 | |||||||
| 518 | 82 | 194 | $$key = $k; # save key name | ||||
| 519 | 82 | 220 | return 0; | ||||
| 520 | |||||||
| 521 | } | ||||||
| 522 | elsif( $$z =~/%%%+V:\s*=(def|esc|ignore|silent|debug)=\s*(\S*)/) { # setting up facultative options | ||||||
| 523 | 8 | 100 | 51 | $op->{$1} = $2 || 0; | |||
| 524 | 8 | 100 | 100 | 40 | $DEBUG = $2 || 0 if $1 eq 'debug'; | ||
| 525 | 8 | 19 | return 0; | ||||
| 526 | } | ||||||
| 527 | elsif( $$z =~/^(? %?)\s?(? |
||||||
| 528 | 54 | 346 | my $k = $+{k}; | ||||
| 529 | |||||||
| 530 | 54 | 138 | my %el; | ||||
| 531 | 54 | 291 | &_set_column( $+{v}, $+{p}, $+{s}, \%el ); | ||||
| 532 | |||||||
| 533 | 54 | 141 | my $inidata = $$data; # save initial environment | ||||
| 534 | |||||||
| 535 | 54 | 100 | 148 | if( $k =~s/^\/+//) { | |||
| 536 | 7 | 13 | $$data = $info; # reset to root environment | ||||
| 537 | |||||||
| 538 | 7 | 100 | 26 | length($k) or return 0; | |||
| 539 | } | ||||||
| 540 | |||||||
| 541 | # Search nested sub-keys | ||||||
| 542 | 50 | 73 | my $x = 0; # for unknown sub-key | ||||
| 543 | 50 | 150 | for my $sk ( split '/', $k ) { | ||||
| 544 | 57 | 50 | 131 | length( $sk ) or next; | |||
| 545 | |||||||
| 546 | 57 | 92 | my $d; | ||||
| 547 | 57 | 100 | 100 | 481 | if( $sk =~/^\d+$/ && ref $$data eq 'ARRAY' && defined( $$data->[$sk] ) ) { | ||
| 100 | 66 | ||||||
| 66 | |||||||
| 548 | 6 | 13 | $d = $$data->[$sk]; | ||||
| 549 | } | ||||||
| 550 | elsif( ref $$data eq 'HASH' && exists( $$data->{$sk} )) { | ||||||
| 551 | 21 | 54 | $d = $$data->{$sk}; | ||||
| 552 | } | ||||||
| 553 | else { | ||||||
| 554 | 30 | 100 | 100 | 207 | push @logs, "~~> l.$. WARNING#3: unknown sub-key '$sk' in %%%V:". $k if $DEBUG or ! $op->{ignore}; | ||
| 555 | |||||||
| 556 | 30 | 45 | print { $fh } $$z; | ||||
| 30 | 65 | ||||||
| 557 | 30 | 62 | ++$nlo; | ||||
| 558 | |||||||
| 559 | 30 | 42 | $x = 1; | ||||
| 560 | 30 | 58 | last; | ||||
| 561 | } | ||||||
| 562 | |||||||
| 563 | # Check type | ||||||
| 564 | 27 | 100 | 100 | 116 | if( (ref $d eq 'ARRAY' or ref $d eq 'HASH') ) { | ||
| 565 | 9 | 16 | $$data = $d; # sub-key (path) found: redefined | ||||
| 566 | 9 | 69 | next; | ||||
| 567 | } | ||||||
| 568 | |||||||
| 569 | 18 | 25 | my $v; | ||||
| 570 | 18 | 100 | 57 | if( ref \$d eq 'SCALAR') { | |||
| 100 | |||||||
| 571 | 16 | 34 | $v = $d; | ||||
| 572 | } | ||||||
| 573 | elsif( ref $d eq 'SCALAR') { # REF->SCALAR | ||||||
| 574 | 1 | 4 | $v = $$d; | ||||
| 575 | } | ||||||
| 576 | else { | ||||||
| 577 | 1 | 50 | 33 | 14 | push @logs, "~~> l.$. WARNING#4: wrong type (not SCALAR|ARRAY|HASH) of '$sk' in %%%V:". $k if $DEBUG or ! $op->{ignore}; | ||
| 578 | |||||||
| 579 | 1 | 3 | print { $fh } $$z; | ||||
| 1 | 3 | ||||||
| 580 | 1 | 2 | ++$nlo; | ||||
| 581 | |||||||
| 582 | 1 | 2 | $x = 1; | ||||
| 583 | 1 | 3 | last; | ||||
| 584 | } | ||||||
| 585 | |||||||
| 586 | 17 | 100 | 55 | $_ = &_v_print( $fh, $k, $v, \%el, $op ) and return $_; | |||
| 587 | |||||||
| 588 | 14 | 26 | $x = 1; | ||||
| 589 | 14 | 34 | last; | ||||
| 590 | } | ||||||
| 591 | |||||||
| 592 | 47 | 100 | 160 | $$data = $inidata if $x; # value found or unknown sub-key: reset to initial environment | |||
| 593 | |||||||
| 594 | 47 | 148 | return 0; | ||||
| 595 | } | ||||||
| 596 | |||||||
| 597 | 336 | 483 | print { $fh } $$z; | ||||
| 336 | 968 | ||||||
| 598 | 336 | 491 | ++$nlo; | ||||
| 599 | |||||||
| 600 | 336 | 644 | return 0; | ||||
| 601 | } | ||||||
| 602 | |||||||
| 603 | |||||||
| 604 | sub _set_column { | ||||||
| 605 | 208 | 208 | 721 | my( $dV, $Np, $paste, $column ) = @_; | |||
| 606 | |||||||
| 607 | 208 | 100 | 623 | $column->{v} = $dV if length $dV; | |||
| 608 | 208 | 100 | 400 | $column->{'%'} = 1 if $Np; | |||
| 609 | 208 | 100 | 509 | $column->{p} = $paste if length $paste; | |||
| 610 | } | ||||||
| 611 | |||||||
| 612 | |||||||
| 613 | sub _data_redef { | ||||||
| 614 | 85 | 85 | 205 | my( $sk, $d, $k, $data, $x ) = @_; | |||
| 615 | |||||||
| 616 | 85 | 100 | 100 | 287 | if( ref $d eq 'ARRAY' or ref $d eq 'HASH') { | ||
| 617 | 49 | 77 | $$data = $d; # redefined for %%%VAR: | ||||
| 618 | 49 | 172 | return 0; | ||||
| 619 | } | ||||||
| 620 | |||||||
| 621 | 36 | 100 | 100 | 108 | if( ref \$d eq 'SCALAR' or ref $d eq 'SCALAR') { | ||
| 622 | 35 | 57 | $$k = $sk; | ||||
| 623 | } | ||||||
| 624 | else { | ||||||
| 625 | 1 | 3 | $$x = $sk; | ||||
| 626 | } | ||||||
| 627 | 36 | 113 | return 1; | ||||
| 628 | } | ||||||
| 629 | |||||||
| 630 | sub _chk_var { | ||||||
| 631 | 83 | 83 | 237 | my( $fh, $k, $vk, $Np, $paste, $before, $chkVAR, $columns, $z, $op ) = @_; | |||
| 632 | |||||||
| 633 | 83 | 132 | our $DEBUG; | ||||
| 634 | 83 | 116 | our @logs; | ||||
| 635 | 83 | 97 | our $nlo; | ||||
| 636 | |||||||
| 637 | 83 | 100 | 100 | 267 | if( ref $vk eq 'ARRAY') { | ||
| 100 | |||||||
| 638 | |||||||
| 639 | 38 | 100 | 50 | if( @{ $vk } ) { | |||
| 38 | 82 | ||||||
| 640 | # Check ARRAY.{ARRAY|HASH|SCALAR[.REF]} | ||||||
| 641 | |||||||
| 642 | 37 | 59 | for my $d ( @{ $vk } ) { | ||||
| 37 | 104 | ||||||
| 643 | 135 | 100 | 329 | if(ref $d eq 'ARRAY'){ | |||
| 100 | |||||||
| 100 | |||||||
| 50 | |||||||
| 644 | 35 | 63 | $$chkVAR |= 0b00001; | ||||
| 645 | } | ||||||
| 646 | elsif(ref $d eq 'HASH') { | ||||||
| 647 | 22 | 40 | $$chkVAR |= 0b00010; | ||||
| 648 | } | ||||||
| 649 | elsif(ref \$d eq 'SCALAR') { | ||||||
| 650 | 74 | 122 | $$chkVAR |= 0b00100; | ||||
| 651 | } | ||||||
| 652 | elsif(ref $d eq 'SCALAR') { # REF->SCALAR | ||||||
| 653 | 4 | 10 | $$chkVAR |= 0b01000; | ||||
| 654 | } | ||||||
| 655 | else { | ||||||
| 656 | 0 | 0 | $$chkVAR |= 0b10000; | ||||
| 657 | } | ||||||
| 658 | } | ||||||
| 659 | } | ||||||
| 660 | else { | ||||||
| 661 | 1 | 4 | $$chkVAR |= 0b00100; # by default, SCALAR | ||||
| 662 | } | ||||||
| 663 | |||||||
| 664 | 38 | 100 | 66 | 248 | if( ! $$chkVAR or $$chkVAR > 0b01000 or ($$chkVAR & ($$chkVAR - 1)) ) { | ||
| 66 | |||||||
| 665 | 1 | 50 | 33 | 17 | push @logs, "~~> l.$. WARNING#6: mixed types (ARRAY with HASH with SCALAR or other) of %%%VAR:". $k if $DEBUG or ! $op->{ignore}; | ||
| 666 | |||||||
| 667 | 1 | 2 | print { $fh } $$z; | ||||
| 1 | 19 | ||||||
| 668 | 1 | 4 | ++$nlo; | ||||
| 669 | 1 | 7 | return 1; | ||||
| 670 | } | ||||||
| 671 | } | ||||||
| 672 | elsif( ref \$vk eq 'SCALAR' or ref $vk eq 'SCALAR') { | ||||||
| 673 | 35 | 104 | $columns->[0]{ki} = $k; | ||||
| 674 | 35 | 80 | &_set_column('', $Np, $$paste, $columns->[0] ); | ||||
| 675 | } | ||||||
| 676 | |||||||
| 677 | 82 | 100 | 172 | if( $$before ) {# Output prefix TeX | |||
| 678 | 13 | 24 | print { $fh } $$before; | ||||
| 13 | 42 | ||||||
| 679 | # ++$nlo; | ||||||
| 680 | } | ||||||
| 681 | |||||||
| 682 | 82 | 225 | return 0; | ||||
| 683 | } | ||||||
| 684 | |||||||
| 685 | # VALUE output | ||||||
| 686 | sub _v_print { | ||||||
| 687 | 438 | 438 | 962 | my( $fh, $k, $v, $el, $op ) = @_; | |||
| 688 | 438 | 100 | 854 | $v = $$v if ref $v eq 'SCALAR'; | |||
| 689 | |||||||
| 690 | 438 | 558 | our $DEBUG; | ||||
| 691 | 438 | 532 | our @logs; | ||||
| 692 | 438 | 513 | our $nlo; | ||||
| 693 | |||||||
| 694 | 438 | 100 | 812 | unless( defined $v ) { | |||
| 695 | 40 | 100 | 90 | if( $op->{def} ) { | |||
| 696 | 39 | 100 | 85 | push @logs, "~~> l.$.".' NOT defined %%%V[AR]:'. $k if $DEBUG; | |||
| 697 | 39 | 95 | return 0; | ||||
| 698 | } | ||||||
| 699 | 1 | 3 | $v = ''; | ||||
| 700 | } | ||||||
| 701 | |||||||
| 702 | 399 | 100 | 920 | if( $v =~/^\x{001}[\x{003}\x{004}]?$/) { # by default text from template | |||
| 703 | 4 | 100 | 14 | if( exists $el->{v} ) { | |||
| 704 | 3 | 7 | print { $fh } $el->{v}; | ||||
| 3 | 20 | ||||||
| 705 | |||||||
| 706 | 3 | 50 | 16 | push @logs, "--> l.$.>$nlo".' Insert text by default %%%V[AR]:'. $k .'= '. $el->{v} if $DEBUG; | |||
| 707 | } | ||||||
| 708 | 4 | 14 | $v =~s/^\x{001}//; | ||||
| 709 | } | ||||||
| 710 | |||||||
| 711 | 399 | 100 | 1333 | if( $v =~/^\x{003}$/) { # END of INPUT template, similar to \endinput | |||
| 100 | |||||||
| 100 | |||||||
| 712 | 1 | 2 | say { $fh } '\endinput'; | ||||
| 1 | 2 | ||||||
| 713 | 1 | 1 | ++$nlo; | ||||
| 714 | 1 | 4 | return 3; | ||||
| 715 | } | ||||||
| 716 | elsif( $v =~/^\x{004}$/) { # END of INPUT template, similar to \endinput | ||||||
| 717 | 4 | 7 | say { $fh } '\end{document}'; | ||||
| 4 | 21 | ||||||
| 718 | 4 | 8 | ++$nlo; | ||||
| 719 | 4 | 21 | return 3; | ||||
| 720 | } | ||||||
| 721 | elsif( length $v ) { | ||||||
| 722 | 385 | 100 | 816 | tex_escape( $v, $op->{esc} ) if $op->{esc}; | |||
| 723 | |||||||
| 724 | 385 | 100 | 848 | push @logs, "--> l.$.>$nlo".' Insert %%%V[AR]:'. $k .'= '. $v if $DEBUG; | |||
| 725 | |||||||
| 726 | 385 | 523 | print { $fh } $v; | ||||
| 385 | 1157 | ||||||
| 727 | 385 | 1034 | ++$nlo while $v =~/\n/g; | ||||
| 728 | |||||||
| 729 | 385 | 100 | 877 | print { $fh } $el->{p} if exists $el->{p}; | |||
| 10 | 41 | ||||||
| 730 | } | ||||||
| 731 | |||||||
| 732 | 394 | 100 | 775 | unless( $el->{'%'} ) { | |||
| 733 | 260 | 387 | print { $fh } "\n"; # NO:YES \par | ||||
| 260 | 578 | ||||||
| 734 | 260 | 420 | ++$nlo; | ||||
| 735 | } | ||||||
| 736 | |||||||
| 737 | 394 | 971 | return 0; | ||||
| 738 | } | ||||||
| 739 | |||||||
| 740 | # HEAD-TAIL output | ||||||
| 741 | sub _ht_print { | ||||||
| 742 | 840 | 840 | 1580 | my( $fh, $el, $ht, $border ) = @_; | |||
| 743 | |||||||
| 744 | 840 | 100 | 1875 | $el->{$ht} or return; | |||
| 745 | |||||||
| 746 | 407 | 551 | our $DEBUG; | ||||
| 747 | 407 | 530 | our @logs; | ||||
| 748 | 407 | 514 | our $nlo; | ||||
| 749 | |||||||
| 750 | 407 | 595 | my $i = 0; | ||||
| 751 | 407 | 535 | foreach( @{ $el->{$ht} } ) { | ||||
| 407 | 910 | ||||||
| 752 | 496 | 100 | 100 | 1647 | next if $ht eq 'head' and $border && exists( $el->{eX} ) && exists( $el->{eX}{$i} ); | ||
| 100 | |||||||
| 100 | |||||||
| 753 | |||||||
| 754 | 463 | 100 | 1077 | push @logs, "-->\tl.$.>$nlo Insert $ht: ". $_ if $DEBUG; | |||
| 755 | |||||||
| 756 | 463 | 615 | print { $fh } $_; | ||||
| 463 | 1577 | ||||||
| 757 | 463 | 744 | ++$nlo; | ||||
| 758 | } | ||||||
| 759 | continue { | ||||||
| 760 | 496 | 996 | ++$i; | ||||
| 761 | } | ||||||
| 762 | |||||||
| 763 | } | ||||||
| 764 | |||||||
| 765 | # HEAD-VALUE-TAIL output | ||||||
| 766 | sub _hvt_print { | ||||||
| 767 | 437 | 437 | 1011 | my( $fh, $ki, $val, $el, $op, $border ) = @_; | |||
| 768 | |||||||
| 769 | 437 | 573 | our $DEBUG; | ||||
| 770 | 437 | 553 | our @logs; | ||||
| 771 | 437 | 514 | our $nlo; | ||||
| 772 | |||||||
| 773 | 437 | 100 | 100 | 1357 | if( length($ki) and ! defined $val ) { | ||
| 774 | 16 | 50 | 43 | if( $op->{def} ) { | |||
| 775 | 16 | 100 | 58 | push @logs, "~~> l.$.".' NOT defined %%%V:'. $ki if $DEBUG; | |||
| 776 | 16 | 43 | return 0; | ||||
| 777 | } | ||||||
| 778 | |||||||
| 779 | 0 | 0 | $val = ''; | ||||
| 780 | } | ||||||
| 781 | |||||||
| 782 | # output head of variable | ||||||
| 783 | 421 | 955 | &_ht_print( $fh, $el, 'head', $border ); | ||||
| 784 | |||||||
| 785 | # output value of variable ( $fh, $ki, $val, $el, $op ) | ||||||
| 786 | 421 | 100 | 758 | $_ = &_v_print and return $_; | |||
| 787 | |||||||
| 788 | # output tail of variable | ||||||
| 789 | 419 | 980 | &_ht_print( $fh, $el, 'tail', 0); | ||||
| 790 | |||||||
| 791 | 419 | 932 | return 0; | ||||
| 792 | } | ||||||
| 793 | |||||||
| 794 | |||||||
| 795 | sub _s_a_prn { | ||||||
| 796 | 203 | 203 | 428 | my( $fh, $i, $values, $el, $op, $border, $col ) = @_; | |||
| 797 | |||||||
| 798 | 203 | 375 | my $val = $values->[$i]; | ||||
| 799 | 203 | 100 | 433 | $val = $$val if ref $val eq 'SCALAR'; | |||
| 800 | |||||||
| 801 | 203 | 290 | my $end = 0; | ||||
| 802 | |||||||
| 803 | 203 | 100 | 403 | if( ref \$val eq 'SCALAR') { | |||
| 50 | |||||||
| 804 | 201 | 400 | $end = &_hvt_print( $fh, $i, $val, $el, $op, $$border ); | ||||
| 805 | |||||||
| 806 | 201 | 325 | ++$$col; | ||||
| 807 | 201 | 298 | $$border = 0; | ||||
| 808 | } | ||||||
| 809 | elsif( ref $val eq 'ARRAY') { # [...].ARRAY.ARRAY | ||||||
| 810 | |||||||
| 811 | 2 | 3 | for( @$val ) { | ||||
| 812 | 17 | 50 | 25 | next if ref \$_ ne 'SCALAR'; | |||
| 813 | |||||||
| 814 | 17 | 43 | $end = &_hvt_print( $fh, $i, $_, $el, $op, $$border ); | ||||
| 815 | |||||||
| 816 | 17 | 16 | ++$$col; | ||||
| 817 | 17 | 13 | $$border = 0; | ||||
| 818 | |||||||
| 819 | 17 | 50 | 25 | last if $end; | |||
| 820 | } | ||||||
| 821 | |||||||
| 822 | } | ||||||
| 823 | |||||||
| 824 | 203 | 652 | return $end; | ||||
| 825 | } | ||||||
| 826 | |||||||
| 827 | |||||||
| 828 | sub _mixed_indices { | ||||||
| 829 | 125 | 125 | 289 | my( $fh, $ki, $values, $el, $op, $border ) = @_; | |||
| 830 | |||||||
| 831 | 125 | 206 | my $nd = @$values; | ||||
| 832 | 125 | 190 | my $col = my $end = 0; | ||||
| 833 | |||||||
| 834 | 125 | 403 | for my $ii ( split ',', $ki ) { # e.g. -1-,1-3,6-7-9,-,4,-5,0,7- | ||||
| 835 | 142 | 100 | 277 | next if $ii eq '-'; | |||
| 836 | |||||||
| 837 | 141 | 100 | 341 | if( $ii =~/^(\-[1-9]\d*)\-(\d*)$/) { # -1- i.e. reverse: -1,-2,..-@arr (i.e. arr_start) | |||
| 838 | 12 | 31 | my $s = $1; | ||||
| 839 | 12 | 66 | 95 | my $e = -1*($2 || $nd); | |||
| 840 | 12 | 100 | 41 | $s = -1*$nd if abs($s) > $nd; | |||
| 841 | 12 | 100 | 26 | $e = -1*$nd if abs($e) > $nd; | |||
| 842 | 12 | 100 | 29 | ($s, $e) = ($e, $s) if $e > $s; | |||
| 843 | |||||||
| 844 | 12 | 33 | for( my $i = $s; $i >= $e; --$i ) { | ||||
| 845 | 38 | 50 | 81 | $end = &_s_a_prn( $fh, $i, $values, $el, $op, \$border, \$col ) and last; | |||
| 846 | } | ||||||
| 847 | |||||||
| 848 | 12 | 50 | 38 | $end ? last : next; | |||
| 849 | } | ||||||
| 850 | |||||||
| 851 | 129 | 100 | 274 | if( $ii =~/^\-[0-9]+$/ ) { # -5 | |||
| 852 | 1 | 5 | my $i = $ii+0; | ||||
| 853 | |||||||
| 854 | 1 | 50 | 5 | if( abs($i) <= $nd ) { | |||
| 855 | 1 | 50 | 4 | $end = &_s_a_prn( $fh, $i, $values, $el, $op, \$border, \$col ) and last; | |||
| 856 | } | ||||||
| 857 | |||||||
| 858 | 1 | 22 | next; | ||||
| 859 | } | ||||||
| 860 | |||||||
| 861 | 128 | 403 | my @n = grep{length} sort{$a <=> $b} split '-', $ii; | ||||
| 131 | 391 | ||||||
| 3 | 11 | ||||||
| 862 | |||||||
| 863 | 128 | 100 | 66 | 518 | if( @n < 2 and $n[0] < $nd ) { # e.g. 4 || 0 || 7(-) | ||
| 864 | 125 | 100 | 311 | if( $ii =~/\-$/) { # 7(-) | |||
| 865 | |||||||
| 866 | 17 | 47 | for( my $i = $n[0]; $i < $nd; ++$i ) { | ||||
| 867 | 49 | 50 | 107 | $end = &_s_a_prn( $fh, $i, $values, $el, $op, \$border, \$col ) and last; | |||
| 868 | } | ||||||
| 869 | |||||||
| 870 | } | ||||||
| 871 | else { # 4 || 0 | ||||||
| 872 | 108 | 50 | 312 | $end = &_s_a_prn( $fh, $n[0], $values, $el, $op, \$border, \$col ) and last; | |||
| 873 | } | ||||||
| 874 | |||||||
| 875 | } | ||||||
| 876 | else { # 1-3 ->(1..3) || 6-7-9 ->(6..9) | ||||||
| 877 | 3 | 8 | for( my $i = $n[0]; $i <= $n[-1]; ++$i ) { | ||||
| 878 | 7 | 100 | 12 | $end = &_s_a_prn( $fh, $i, $values, $el, $op, \$border, \$col ) and last; | |||
| 879 | } | ||||||
| 880 | } | ||||||
| 881 | |||||||
| 882 | 128 | 100 | 353 | last if $end; | |||
| 883 | } | ||||||
| 884 | |||||||
| 885 | 125 | 338 | return( $col, $end ); | ||||
| 886 | } | ||||||
| 887 | |||||||
| 888 | |||||||
| 889 | sub _var_output { | ||||||
| 890 | 82 | 82 | 198 | my( $fh, $key, $vardata, $columns, $op ) = @_; | |||
| 891 | 82 | 100 | 264 | my $values = (ref $vardata eq 'HASH') ? $vardata->{ $key } : $vardata->[ $key ]; | |||
| 892 | |||||||
| 893 | 82 | 50 | 165 | @$columns or return; | |||
| 894 | |||||||
| 895 | 82 | 121 | our $DEBUG; | ||||
| 896 | 82 | 114 | our @logs; | ||||
| 897 | 82 | 101 | our $nlo; | ||||
| 898 | |||||||
| 899 | 82 | 147 | my $end = 0; | ||||
| 900 | |||||||
| 901 | 82 | 100 | 100 | 290 | if( ref \$values eq 'SCALAR' or ref $values eq 'SCALAR') { # key => SCALAR | ||
| 902 | 35 | 107 | return &_hvt_print( $fh, $key, $values, $columns->[0], $op ); | ||||
| 903 | } | ||||||
| 904 | |||||||
| 905 | 47 | 100 | 114 | if( ref $values eq 'ARRAY') { # key => ARRAY | |||
| 50 | |||||||
| 906 | |||||||
| 907 | 37 | 100 | 94 | unless( @$values ) { | |||
| 908 | 1 | 50 | 33 | 31 | push @logs, "~~> l.$. WARNING#7: empty ARRAY of %%%VAR:". $key if $DEBUG or ! $op->{ignore}; | ||
| 909 | 1 | 5 | return 0; | ||||
| 910 | } | ||||||
| 911 | |||||||
| 912 | # Forming a table | ||||||
| 913 | 36 | 62 | my $row = 0; | ||||
| 914 | 36 | 59 | my $nd = @$values; | ||||
| 915 | |||||||
| 916 | _var_output_M0: | ||||||
| 917 | 36 | 80 | foreach my $d ( @$values ) { # loop through table rows | ||||
| 918 | |||||||
| 919 | 92 | 100 | 209 | push @logs, '--> Table row = '. $row if $DEBUG; | |||
| 920 | |||||||
| 921 | 92 | 134 | my $col = 0; | ||||
| 922 | 92 | 157 | foreach my $el ( @$columns ) { # loop through table columns (for ARRAY.HASH) or rows (for ARRAY.SCALAR) | ||||
| 923 | |||||||
| 924 | 269 | 515 | my $ki = $el->{ki}; | ||||
| 925 | 269 | 100 | 100 | 808 | my $border = ((! $row and ! $col) or ($row >= $#{ $values } and (!defined( $ki ) or !length( $ki )) ) ) ? 1 : 0; | ||
| 926 | |||||||
| 927 | 269 | 444 | my $val; | ||||
| 928 | 269 | 100 | 446 | if( defined $ki ) { | |||
| 929 | 232 | 50 | 100 | 1077 | if( $ki eq '%%%ADDA') { | ||
| 100 | |||||||
| 100 | |||||||
| 50 | |||||||
| 0 | |||||||
| 930 | 0 | 0 | $val = ''; | ||||
| 931 | } | ||||||
| 932 | elsif( ref \$d eq 'SCALAR' or ref $d eq 'SCALAR') { # (ARRAY.SCALAR or ARRAY.REF->SCALAR) in regular vector | ||||||
| 933 | |||||||
| 934 | 28 | 50 | 142 | if( $ki =~/^[\d,\-]+$/) { | |||
| 935 | # mixed indices, e.g.: 1-3,6-7-9,-,4,-5,0,7- or 3- (i.e. 3..arr_end) or 0-5 (0..5) or -1- (-1,-2,..arr_start) | ||||||
| 936 | 28 | 100 | 81 | last _var_output_M0 if $row; | |||
| 937 | |||||||
| 938 | 16 | 74 | ($_, $end) = &_mixed_indices( $fh, $ki, $values, $el, $op, $border ); | ||||
| 939 | 16 | 50 | 67 | $col += $_ - 1 if $_; | |||
| 940 | } | ||||||
| 941 | |||||||
| 942 | 16 | 100 | 44 | $end ? last _var_output_M0 : next; | |||
| 943 | } | ||||||
| 944 | elsif( ref $d eq 'HASH') { # ARRAY.HASH | ||||||
| 945 | 95 | 192 | $val = $d->{$ki}; | ||||
| 946 | |||||||
| 947 | 95 | 100 | 100 | 483 | if( defined( $val ) && ref $val eq 'ARRAY') { # ARRAY.HASH.ARRAY | ||
| 50 | 33 | ||||||
| 948 | 2 | 7 | for my $vv ( @$val ) { | ||||
| 949 | 14 | 50 | 34 | next unless ref \$vv eq 'SCALAR'; | |||
| 950 | |||||||
| 951 | 14 | 50 | 25 | $end = &_hvt_print( $fh, $ki, $vv, $el, $op, $border ) and last _var_output_M0; | |||
| 952 | 14 | 20 | ++$col; | ||||
| 953 | } | ||||||
| 954 | |||||||
| 955 | 2 | 50 | 10 | $end ? last _var_output_M0 : next; | |||
| 956 | } | ||||||
| 957 | elsif( ref \$val ne 'SCALAR' and ref $val ne 'SCALAR') { | ||||||
| 958 | 0 | 0 | next; | ||||
| 959 | } | ||||||
| 960 | } | ||||||
| 961 | elsif( ref $d eq 'ARRAY') { # ARRAY.ARRAY | ||||||
| 962 | |||||||
| 963 | 109 | 50 | 482 | if( $ki =~/^[\d,\-]+$/) { | |||
| 964 | # mixed indices, e.g.: 1-3,6-7-9,-,4,-5,0,7- or 3- (i.e. 3..arr_end) or 0-5 (0..5) or -1- (-1,-2,..arr_start) | ||||||
| 965 | 109 | 272 | ($_, $end) = &_mixed_indices( $fh, $ki, $d, $el, $op, $border ); | ||||
| 966 | 109 | 50 | 277 | $col += $_ - 1 if $_; | |||
| 967 | } | ||||||
| 968 | |||||||
| 969 | 109 | 50 | 299 | $end ? last _var_output_M0 : next; | |||
| 970 | } | ||||||
| 971 | elsif( $op->{def} ) { | ||||||
| 972 | |||||||
| 973 | 0 | 0 | 0 | push @logs, "~~> l.$. NOT defined %%%V:". $ki if $DEBUG; | |||
| 974 | |||||||
| 975 | 0 | 0 | next; | ||||
| 976 | } | ||||||
| 977 | } | ||||||
| 978 | else { | ||||||
| 979 | # empty parameter -- at the very end of the columns (parameters) | ||||||
| 980 | 37 | 101 | $ki = ''; | ||||
| 981 | } | ||||||
| 982 | |||||||
| 983 | 130 | 50 | 263 | $end = &_hvt_print( $fh, $ki, $val, $el, $op, $border ) and last _var_output_M0; | |||
| 984 | } | ||||||
| 985 | continue { | ||||||
| 986 | 256 | 530 | ++$col; | ||||
| 987 | } | ||||||
| 988 | |||||||
| 989 | 79 | 50 | 156 | last if $end; | |||
| 990 | } | ||||||
| 991 | continue { | ||||||
| 992 | 79 | 186 | ++$row; | ||||
| 993 | } | ||||||
| 994 | |||||||
| 995 | } | ||||||
| 996 | elsif( ref $values eq 'HASH') { | ||||||
| 997 | |||||||
| 998 | 10 | 17 | my $col = 0; | ||||
| 999 | 10 | 22 | foreach my $el ( @$columns ) { # loop through parameters of %%%VAR-structure | ||||
| 1000 | |||||||
| 1001 | 39 | 80 | my $ki = $el->{ki}; | ||||
| 1002 | 39 | 100 | 100 | 101 | my $border = ( ! $col or ($col >= $#{ $columns } and (!defined( $ki ) or !length( $ki )) )) ? 1 : 0; | ||
| 1003 | |||||||
| 1004 | 39 | 64 | my $val; | ||||
| 1005 | 39 | 100 | 70 | if( defined $ki ) { | |||
| 1006 | 36 | 100 | 100 | 270 | if( $ki eq '%%%ADDA') { | ||
| 100 | 66 | ||||||
| 100 | 33 | ||||||
| 100 | 66 | ||||||
| 50 | 66 | ||||||
| 1007 | 3 | 5 | $val = ''; | ||||
| 1008 | } | ||||||
| 1009 | elsif( ref \$values->{$ki} eq 'SCALAR' and ( !$op->{def} or defined( $values->{$ki} ) ) ) { # HASH.SCALAR | ||||||
| 1010 | 24 | 42 | $val = $values->{$ki}; | ||||
| 1011 | } | ||||||
| 1012 | elsif( ref $values->{$ki} eq 'SCALAR' and ( !$op->{def} or defined( ${ $values->{$ki} } ) ) ) { # HASH.REF->SCALAR | ||||||
| 1013 | 1 | 3 | $val = ${ $values->{$ki} }; | ||||
| 1 | 3 | ||||||
| 1014 | } | ||||||
| 1015 | elsif( $ki eq '@' and ref $values->{'@'} eq 'ARRAY') { | ||||||
| 1016 | 2 | 4 | for my $k ( @{ $values->{'@'} } ) { | ||||
| 2 | 5 | ||||||
| 1017 | 11 | 100 | 66 | 48 | next unless defined($k) && exists( $values->{$k} ); | ||
| 1018 | |||||||
| 1019 | 10 | 16 | my $v; | ||||
| 1020 | 10 | 100 | 38 | if( ref \$values->{$k} eq 'SCALAR') { | |||
| 100 | |||||||
| 50 | |||||||
| 1021 | 8 | 15 | $v = $values->{$k}; | ||||
| 1022 | } | ||||||
| 1023 | elsif( ref $values->{$k} eq 'SCALAR') { | ||||||
| 1024 | 1 | 3 | $v = ${ $values->{$k} }; | ||||
| 1 | 19 | ||||||
| 1025 | } | ||||||
| 1026 | elsif( $op->{def} ) { | ||||||
| 1027 | 1 | 50 | 5 | push @logs, "-->\tl.$. ". 'NOT HASH.ARRAY.SCALAR %%%V:@->{'.$k."} in %%%VAR:". $key if $DEBUG; | |||
| 1028 | |||||||
| 1029 | 1 | 4 | next; | ||||
| 1030 | } | ||||||
| 1031 | |||||||
| 1032 | 9 | 50 | 37 | $end = &_hvt_print( $fh, $k, $v, $el, $op, $border ) and last _var_output_M0; | |||
| 1033 | 9 | 22 | $border = 0; | ||||
| 1034 | } | ||||||
| 1035 | |||||||
| 1036 | 2 | 50 | 9 | $end ? last : next; | |||
| 1037 | } | ||||||
| 1038 | elsif( $op->{def} ) { | ||||||
| 1039 | 6 | 50 | 14 | push @logs, "~~> l.$. NOT HASH.SCALAR or NOT defined %%%V:". $ki if $DEBUG; | |||
| 1040 | |||||||
| 1041 | 6 | 12 | next; | ||||
| 1042 | } | ||||||
| 1043 | } | ||||||
| 1044 | else { | ||||||
| 1045 | # empty parameter -- at the very end of the columns (parameters) | ||||||
| 1046 | 3 | 6 | $ki = ''; | ||||
| 1047 | } | ||||||
| 1048 | |||||||
| 1049 | 31 | 100 | 66 | $end = &_hvt_print( $fh, $ki, $val, $el, $op, $border ) and last; | |||
| 1050 | } | ||||||
| 1051 | continue { | ||||||
| 1052 | 38 | 75 | ++$col; | ||||
| 1053 | } | ||||||
| 1054 | } | ||||||
| 1055 | |||||||
| 1056 | 46 | 105 | return $end; | ||||
| 1057 | } | ||||||
| 1058 | |||||||
| 1059 | 1; | ||||||
| 1060 | |||||||
| 1061 | __END__ |