line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
############################################################################### |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# LaTeX::TOM::Parser |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# The parsing class |
6
|
|
|
|
|
|
|
# |
7
|
|
|
|
|
|
|
############################################################################### |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
package LaTeX::TOM::Parser; |
10
|
|
|
|
|
|
|
|
11
|
10
|
|
|
10
|
|
80
|
use strict; |
|
10
|
|
|
|
|
20
|
|
|
10
|
|
|
|
|
364
|
|
12
|
10
|
|
|
|
|
4306
|
use base qw( |
13
|
|
|
|
|
|
|
LaTeX::TOM::Node |
14
|
|
|
|
|
|
|
LaTeX::TOM::Tree |
15
|
10
|
|
|
10
|
|
60
|
); |
|
10
|
|
|
|
|
20
|
|
16
|
10
|
|
|
10
|
|
73
|
use constant true => 1; |
|
10
|
|
|
|
|
23
|
|
|
10
|
|
|
|
|
570
|
|
17
|
10
|
|
|
10
|
|
61
|
use constant false => 0; |
|
10
|
|
|
|
|
20
|
|
|
10
|
|
|
|
|
458
|
|
18
|
|
|
|
|
|
|
|
19
|
10
|
|
|
10
|
|
57
|
use Carp qw(carp croak); |
|
10
|
|
|
|
|
20
|
|
|
10
|
|
|
|
|
533
|
|
20
|
10
|
|
|
10
|
|
61
|
use File::Basename qw(fileparse); |
|
10
|
|
|
|
|
19
|
|
|
10
|
|
|
|
|
1891
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $VERSION = '0.12'; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
my %error_handlers = ( |
25
|
|
|
|
|
|
|
0 => sub { warn "parse error: $_[0].\n" }, |
26
|
|
|
|
|
|
|
1 => sub { die "parse error: $_[0].\n" }, |
27
|
|
|
|
|
|
|
2 => sub {}, |
28
|
|
|
|
|
|
|
); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
# Constructor |
31
|
|
|
|
|
|
|
# |
32
|
|
|
|
|
|
|
sub _new { |
33
|
17
|
|
|
17
|
|
41
|
my $class = shift; |
34
|
|
|
|
|
|
|
|
35
|
10
|
|
|
10
|
|
73
|
no strict 'refs'; |
|
10
|
|
|
|
|
22
|
|
|
10
|
|
|
|
|
78502
|
|
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
my $self = bless { |
38
|
|
|
|
|
|
|
config => { |
39
|
17
|
|
|
|
|
63
|
BRACELESS => \%{'LaTeX::TOM::BRACELESS'}, |
40
|
17
|
|
|
|
|
45
|
INNERCMDS => \%{'LaTeX::TOM::INNERCMDS'}, |
41
|
17
|
|
|
|
|
46
|
MATHENVS => \%{'LaTeX::TOM::MATHENVS'}, |
42
|
17
|
|
|
|
|
45
|
MATHBRACKETS => \%{'LaTeX::TOM::MATHBRACKETS'}, |
43
|
17
|
|
|
|
|
43
|
PARSE_ERRORS_FATAL => ${'LaTeX::TOM::PARSE_ERRORS_FATAL'}, |
44
|
17
|
|
|
|
|
37
|
TEXTENVS => \%{'LaTeX::TOM::TEXTENVS'}, |
|
17
|
|
|
|
|
100
|
|
45
|
|
|
|
|
|
|
}, |
46
|
|
|
|
|
|
|
}; |
47
|
|
|
|
|
|
|
|
48
|
17
|
|
|
|
|
73
|
$self->_init(@_); |
49
|
|
|
|
|
|
|
|
50
|
17
|
|
|
|
|
47
|
return $self; |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
sub new { |
54
|
|
|
|
|
|
|
# XXX deprecated as of 2023-01-30 |
55
|
0
|
|
|
0
|
0
|
0
|
carp 'Direct use of LaTeX::TOM::Parser constructor is deprecated and will be removed in future version'; |
56
|
0
|
|
|
|
|
0
|
shift->_new(@_); |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# Set/reset "globals" |
60
|
|
|
|
|
|
|
# |
61
|
|
|
|
|
|
|
sub _init { |
62
|
17
|
|
|
17
|
|
32
|
my $parser = shift; |
63
|
17
|
|
|
|
|
45
|
my ($parse_errors_fatal, $readinputs, $applymappings) = @_; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
my $retrieve_opt_default = sub |
66
|
|
|
|
|
|
|
{ |
67
|
51
|
|
|
51
|
|
89
|
my ($opt, $default) = @_; |
68
|
51
|
100
|
|
|
|
160
|
return $opt if defined $opt; |
69
|
23
|
|
|
|
|
85
|
return $default; |
70
|
17
|
|
|
|
|
84
|
}; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# set user options |
73
|
|
|
|
|
|
|
# |
74
|
17
|
|
|
|
|
51
|
$parser->{readinputs} = $retrieve_opt_default->($readinputs, 0); |
75
|
17
|
|
|
|
|
52
|
$parser->{applymappings} = $retrieve_opt_default->($applymappings, 0); |
76
|
17
|
|
|
|
|
49
|
$parser->{PARSE_ERRORS_FATAL} = $retrieve_opt_default->($parse_errors_fatal, $parser->{config}{PARSE_ERRORS_FATAL}); |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# init internal stuff |
79
|
|
|
|
|
|
|
# |
80
|
17
|
|
|
|
|
48
|
$parser->{MATHBRACKETS} = $parser->{config}{MATHBRACKETS}; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# this will hold a running list/hash of commands that have been remapped |
83
|
17
|
|
|
|
|
35
|
$parser->{MAPPEDCMDS} = {}; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# this will hold a running list/hash of commands that have been used. We dont |
86
|
|
|
|
|
|
|
# bother apply mappings except to commands that have been used. |
87
|
17
|
|
|
|
|
36
|
$parser->{USED_COMMANDS} = {}; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# no file yet |
90
|
17
|
|
|
|
|
87
|
$parser->{file} = undef; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# Parse a LaTeX file, return a tree. You probably want this method. |
94
|
|
|
|
|
|
|
# |
95
|
|
|
|
|
|
|
sub parseFile { |
96
|
9
|
|
|
9
|
0
|
560
|
my $parser = shift; |
97
|
9
|
|
|
|
|
18
|
my $filename = shift; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# init variables |
100
|
|
|
|
|
|
|
# |
101
|
9
|
|
|
|
|
20
|
$parser->{file} = $filename; # file name member data |
102
|
9
|
|
|
|
|
19
|
my $tree = {}; # init output tree |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# read in text from file or bomb out |
105
|
|
|
|
|
|
|
# |
106
|
9
|
|
|
|
|
29
|
my $text = _readFile($filename, true); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# do the parse |
109
|
|
|
|
|
|
|
# |
110
|
9
|
|
|
|
|
40
|
$tree = $parser->parse($text); |
111
|
|
|
|
|
|
|
|
112
|
9
|
|
|
|
|
31
|
return $tree; |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# main parsing entrypoint |
116
|
|
|
|
|
|
|
# |
117
|
|
|
|
|
|
|
sub parse { |
118
|
19
|
|
|
19
|
0
|
4363
|
my $parser = shift; |
119
|
19
|
|
|
|
|
52
|
my ($text) = @_; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# first half of parsing (goes up to finding commands, reading inputs) |
122
|
|
|
|
|
|
|
# |
123
|
19
|
|
|
|
|
64
|
my ($tree, $bracehash) = $parser->_parseA($text); |
124
|
|
|
|
|
|
|
_debug( |
125
|
|
|
|
|
|
|
'done with _parseA', |
126
|
0
|
|
|
0
|
|
0
|
sub { $tree->_warn() }, |
127
|
19
|
|
|
|
|
140
|
); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# handle mappings |
130
|
|
|
|
|
|
|
# |
131
|
19
|
100
|
|
|
|
98
|
$parser->_applyMappings($tree) if $parser->{applymappings}; |
132
|
|
|
|
|
|
|
_debug( |
133
|
|
|
|
|
|
|
'done with _applyMappings', |
134
|
0
|
|
|
0
|
|
0
|
sub { $tree->_warn() }, |
135
|
19
|
|
|
|
|
112
|
); |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
# second half of parsing (environments) |
138
|
|
|
|
|
|
|
# |
139
|
19
|
|
|
|
|
73
|
$parser->_parseB($tree); |
140
|
|
|
|
|
|
|
_debug( |
141
|
|
|
|
|
|
|
'done with _parseB', |
142
|
0
|
|
|
0
|
|
0
|
sub { $tree->_warn() }, |
143
|
19
|
|
|
|
|
144
|
); |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# once all the above is done we can propegate math/plaintext modes down |
146
|
|
|
|
|
|
|
# |
147
|
19
|
|
|
|
|
100
|
$parser->_propegateModes($tree, 0, 0); # math = 0, plaintext = 0 |
148
|
|
|
|
|
|
|
_debug( |
149
|
|
|
|
|
|
|
'done with _propegateModes', |
150
|
0
|
|
|
0
|
|
0
|
sub { $tree->_warn() }, |
151
|
19
|
|
|
|
|
108
|
); |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# handle kooky \[ \] math mode |
154
|
|
|
|
|
|
|
# |
155
|
19
|
50
|
|
|
|
81
|
if (not exists $parser->{MAPPEDCMDS}->{'\\['}) { |
156
|
|
|
|
|
|
|
# math mode (\[ \], \( \)) |
157
|
19
|
|
|
|
|
130
|
$parser->_stage5($tree, {'\\[' => '\\]', '\\(' => '\\)'}, 1); |
158
|
19
|
|
|
|
|
91
|
$parser->_propegateModes($tree, 0, 0); # have to do this again of course |
159
|
19
|
|
|
|
|
44
|
$parser->{MATHBRACKETS}->{'\\['} = '\\]'; # put back in brackets list for |
160
|
19
|
|
|
|
|
46
|
$parser->{MATHBRACKETS}->{'\\('} = '\\)'; # printing purposes. |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
_debug( |
163
|
|
|
|
|
|
|
undef, |
164
|
0
|
|
|
0
|
|
0
|
sub { $tree->_warn() }, |
165
|
19
|
|
|
|
|
117
|
); |
166
|
|
|
|
|
|
|
|
167
|
19
|
|
|
|
|
122
|
$tree->listify; # add linked-list stuff |
168
|
|
|
|
|
|
|
|
169
|
19
|
|
|
|
|
98
|
return $tree; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# Parsing with no mappings and no externally accessible parser object. |
173
|
|
|
|
|
|
|
# |
174
|
|
|
|
|
|
|
sub _basicparse { |
175
|
8
|
|
|
8
|
|
20
|
my $parser = shift; # @_ would break code |
176
|
8
|
|
|
|
|
15
|
my $text = shift; |
177
|
|
|
|
|
|
|
|
178
|
8
|
50
|
|
|
|
24
|
my $parse_errors_fatal = (defined $_[0] ? $_[0] : $parser->{config}{PARSE_ERRORS_FATAL}); |
179
|
8
|
100
|
|
|
|
29
|
my $readinputs = (defined $_[1] ? $_[1] : 1); |
180
|
|
|
|
|
|
|
|
181
|
8
|
|
|
|
|
39
|
$parser = LaTeX::TOM::Parser->_new($parse_errors_fatal, $readinputs); |
182
|
8
|
|
|
|
|
27
|
my ($tree, $bracehash) = $parser->_parseA($text); |
183
|
|
|
|
|
|
|
|
184
|
8
|
|
|
|
|
27
|
$parser->_parseB($tree); |
185
|
|
|
|
|
|
|
|
186
|
8
|
|
|
|
|
42
|
$tree->listify; # add linked-list stuff |
187
|
|
|
|
|
|
|
|
188
|
8
|
|
|
|
|
63
|
return ($tree, $bracehash); |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# start the tree. separate out comment and text nodes. |
192
|
|
|
|
|
|
|
# |
193
|
|
|
|
|
|
|
sub _stage1 { |
194
|
27
|
|
|
27
|
|
58
|
my $parser = shift; |
195
|
27
|
|
|
|
|
39
|
my $text = shift; |
196
|
|
|
|
|
|
|
|
197
|
27
|
|
|
|
|
81
|
my @nodes = _getTextAndCommentNodes($text, 0, length($text)); |
198
|
|
|
|
|
|
|
|
199
|
27
|
|
|
|
|
134
|
return LaTeX::TOM::Tree->_new([@nodes]); |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# this stage parses the braces ({}) and adds the corresponding structure to |
203
|
|
|
|
|
|
|
# the tree. |
204
|
|
|
|
|
|
|
# |
205
|
|
|
|
|
|
|
sub _stage2 { |
206
|
146
|
|
|
146
|
|
210
|
my $parser = shift; |
207
|
|
|
|
|
|
|
|
208
|
146
|
|
|
|
|
195
|
my $tree = shift; |
209
|
146
|
|
100
|
|
|
321
|
my $bracehash = shift || undef; |
210
|
146
|
|
50
|
|
|
419
|
my $startidx = shift || 0; # last two params for starting at some specific |
211
|
146
|
|
50
|
|
|
366
|
my $startpos = shift || 0; # node and offset. |
212
|
|
|
|
|
|
|
|
213
|
146
|
|
|
|
|
195
|
my %blankhash; |
214
|
|
|
|
|
|
|
|
215
|
146
|
100
|
|
|
|
281
|
if (not defined $bracehash) { |
216
|
27
|
|
|
|
|
64
|
$bracehash = {%blankhash}; |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
146
|
|
|
|
|
203
|
my $leftidx = -1; |
220
|
146
|
|
|
|
|
233
|
my $leftpos = -1; |
221
|
146
|
|
|
|
|
197
|
my $leftcount = 0; |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# loop through the nodes |
224
|
146
|
|
|
|
|
231
|
for (my $i = $startidx; $i < @{$tree->{nodes}}; $i++) { |
|
410
|
|
|
|
|
868
|
|
225
|
264
|
|
|
|
|
380
|
my $node = $tree->{nodes}[$i]; |
226
|
264
|
|
|
|
|
370
|
my $spos = $node->{start}; # get text start position |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# set position placeholder within the text block |
229
|
264
|
100
|
|
|
|
462
|
my $pos = ($i == $startidx) ? $startpos : 0; |
230
|
|
|
|
|
|
|
|
231
|
264
|
50
|
|
|
|
617
|
if ($node->{type} eq 'TEXT') { |
232
|
|
|
|
|
|
|
|
233
|
264
|
|
|
|
|
835
|
_debug("parseStage2: looking at text node: [$node->{content}]", undef); |
234
|
|
|
|
|
|
|
|
235
|
264
|
|
|
|
|
530
|
my ($nextpos, $brace) = _findbrace($node->{content}, $pos); |
236
|
264
|
|
|
|
|
601
|
while ($nextpos != -1) { |
237
|
|
|
|
|
|
|
|
238
|
244
|
|
|
|
|
312
|
$pos = $nextpos + 1; # update position pointer |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# handle left brace |
241
|
244
|
100
|
|
|
|
551
|
if ($brace eq '{') { |
|
|
50
|
|
|
|
|
|
242
|
122
|
|
|
|
|
377
|
_debug("found '{' at position $nextpos, leftcount is $leftcount", undef); |
243
|
122
|
100
|
|
|
|
247
|
if ($leftcount == 0) { |
244
|
119
|
|
|
|
|
170
|
$leftpos = $nextpos; |
245
|
119
|
|
|
|
|
162
|
$leftidx = $i |
246
|
|
|
|
|
|
|
} |
247
|
122
|
|
|
|
|
185
|
$leftcount++; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# handle right brance |
251
|
|
|
|
|
|
|
elsif ($brace eq '}') { |
252
|
|
|
|
|
|
|
|
253
|
122
|
|
|
|
|
346
|
_debug("found '}' at position $nextpos, leftcount is $leftcount", undef); |
254
|
122
|
|
|
|
|
183
|
my $rightpos = $nextpos; |
255
|
122
|
|
|
|
|
184
|
$leftcount--; |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
# found the corresponding right brace to our starting left brace |
258
|
122
|
100
|
|
|
|
228
|
if ($leftcount == 0) { |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# see if we have to split the text node into 3 parts |
261
|
|
|
|
|
|
|
# |
262
|
119
|
50
|
|
|
|
217
|
if ($leftidx == $i) { |
263
|
|
|
|
|
|
|
|
264
|
119
|
|
|
|
|
325
|
my ($leftside, $textnode3) = $node->split($rightpos, $rightpos); |
265
|
119
|
|
|
|
|
322
|
my ($textnode1, $textnode2) = $leftside->split($leftpos, $leftpos); |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# make the new GROUP node |
268
|
|
|
|
|
|
|
my $groupnode = LaTeX::TOM::Node->_new( |
269
|
|
|
|
|
|
|
{type => 'GROUP', |
270
|
|
|
|
|
|
|
start => $textnode2->{start} - 1, |
271
|
119
|
|
|
|
|
426
|
end => $textnode2->{end} + 1, |
272
|
|
|
|
|
|
|
children => LaTeX::TOM::Tree->_new([$textnode2]), |
273
|
|
|
|
|
|
|
}); |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# splice the new subtree into the old location |
276
|
119
|
|
|
|
|
190
|
splice @{$tree->{nodes}}, $i, 1, $textnode1, $groupnode, $textnode3; |
|
119
|
|
|
|
|
365
|
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# add to the brace-pair lookup table |
279
|
119
|
|
|
|
|
350
|
$bracehash->{$groupnode->{start}} = $groupnode->{end}; |
280
|
119
|
|
|
|
|
363
|
$bracehash->{$groupnode->{end}} = $groupnode->{start}; |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
# recur into new child node |
283
|
119
|
|
|
|
|
405
|
$parser->_stage2($groupnode->{children}, $bracehash); |
284
|
|
|
|
|
|
|
|
285
|
119
|
|
|
|
|
345
|
$i++; # skip to textnode3 for further processing |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# split across nodes |
289
|
|
|
|
|
|
|
# |
290
|
|
|
|
|
|
|
else { |
291
|
|
|
|
|
|
|
|
292
|
0
|
|
|
|
|
0
|
my ($textnode1, $textnode2) = $tree->{nodes}[$leftidx]->split($leftpos, $leftpos); |
293
|
0
|
|
|
|
|
0
|
my ($textnode3, $textnode4) = $node->split($rightpos, $rightpos); |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# remove nodes in between the node we found '{' in and the node |
296
|
|
|
|
|
|
|
# we found '}' in |
297
|
|
|
|
|
|
|
# |
298
|
0
|
|
|
|
|
0
|
my @removed = splice @{$tree->{nodes}}, $leftidx+1, $i-$leftidx-1; |
|
0
|
|
|
|
|
0
|
|
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# create a group node that contains the text after the left brace, |
301
|
|
|
|
|
|
|
# then all the nodes up until the next text node, then the text |
302
|
|
|
|
|
|
|
# before the right brace. |
303
|
|
|
|
|
|
|
# |
304
|
|
|
|
|
|
|
my $groupnode = LaTeX::TOM::Node->_new( |
305
|
|
|
|
|
|
|
{type => 'GROUP', |
306
|
|
|
|
|
|
|
start => $textnode2->{start} - 1, |
307
|
0
|
|
|
|
|
0
|
end => $textnode3->{end} + 1, |
308
|
|
|
|
|
|
|
children => LaTeX::TOM::Tree->_new( |
309
|
|
|
|
|
|
|
[$textnode2, |
310
|
|
|
|
|
|
|
@removed, |
311
|
|
|
|
|
|
|
$textnode3]), |
312
|
|
|
|
|
|
|
}); |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# replace the two original text nodes with the leftover left and |
315
|
|
|
|
|
|
|
# right portions, as well as the group node with everything in |
316
|
|
|
|
|
|
|
# the middle. |
317
|
|
|
|
|
|
|
# |
318
|
0
|
|
|
|
|
0
|
splice @{$tree->{nodes}}, $leftidx, 2, $textnode1, $groupnode, $textnode4; |
|
0
|
|
|
|
|
0
|
|
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# add to the brace-pair lookup table |
321
|
0
|
|
|
|
|
0
|
$bracehash->{$groupnode->{start}} = $groupnode->{end}; |
322
|
0
|
|
|
|
|
0
|
$bracehash->{$groupnode->{end}} = $groupnode->{start}; |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# recur into new child nodes |
325
|
0
|
|
|
|
|
0
|
$parser->_stage2($groupnode->{children}, $bracehash); |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# step back to textnode4 on this level for further processing |
328
|
0
|
|
|
|
|
0
|
$i -= scalar @removed; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
119
|
|
|
|
|
168
|
$leftpos = -1; # reset left data |
332
|
119
|
|
|
|
|
155
|
$leftidx = -1; |
333
|
119
|
|
|
|
|
279
|
last; |
334
|
|
|
|
|
|
|
} # $leftcount == 0 |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# check for '}'-based error |
337
|
|
|
|
|
|
|
# |
338
|
3
|
50
|
|
|
|
8
|
if ($leftcount < 0) { |
339
|
0
|
|
|
|
|
0
|
$error_handlers{$parser->{PARSE_ERRORS_FATAL}}->("'}' before '{' at " . ($spos + $rightpos)); |
340
|
0
|
|
|
|
|
0
|
$leftcount = 0; # reset and continue |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
} # right brace |
343
|
|
|
|
|
|
|
|
344
|
125
|
|
|
|
|
247
|
($nextpos, $brace) = _findbrace($node->{content}, $pos); |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
} # while (braces left) |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
} # if TEXT |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
} # loop over all nodes |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# check for extra '{' parse error |
353
|
|
|
|
|
|
|
# |
354
|
146
|
50
|
|
|
|
290
|
if ($leftcount > 0) { |
355
|
0
|
|
|
|
|
0
|
my $spos = $tree->{nodes}[$leftidx]->{start}; # get text start position |
356
|
0
|
|
|
|
|
0
|
$error_handlers{$parser->{PARSE_ERRORS_FATAL}}->("unmatched '{' at " . ($spos + $leftpos)); |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# try to continue on, after the offending brace |
359
|
0
|
|
|
|
|
0
|
$parser->_stage2($tree, $bracehash, $leftidx, $leftpos + 1); |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
|
362
|
146
|
|
|
|
|
316
|
return $bracehash; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# this stage finds LaTeX commands and accordingly turns GROUP nodes into |
366
|
|
|
|
|
|
|
# command nodes, labeled with the command |
367
|
|
|
|
|
|
|
# |
368
|
|
|
|
|
|
|
sub _stage3 { |
369
|
146
|
|
|
146
|
|
200
|
my $parser = shift; |
370
|
|
|
|
|
|
|
|
371
|
146
|
|
|
|
|
192
|
my $tree = shift; |
372
|
146
|
|
|
|
|
191
|
my $parent = shift; |
373
|
|
|
|
|
|
|
|
374
|
146
|
|
|
|
|
208
|
for (my $i = 0; $i< @{$tree->{nodes}}; $i++) { |
|
531
|
|
|
|
|
1057
|
|
375
|
|
|
|
|
|
|
|
376
|
385
|
|
|
|
|
508
|
my $node = $tree->{nodes}[$i]; |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
# check text node for command tag |
379
|
385
|
100
|
|
|
|
691
|
if ($node->{type} eq 'TEXT') { |
380
|
266
|
|
|
|
|
387
|
my $text = $node->{content}; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# inner command (such as {\command text text}). our regexp checks to see |
383
|
|
|
|
|
|
|
# if this text chunk begins with \command, since that would be the case |
384
|
|
|
|
|
|
|
# due to the previous parsing stages. if found, the parent node is |
385
|
|
|
|
|
|
|
# promoted to a command. |
386
|
|
|
|
|
|
|
# |
387
|
266
|
100
|
100
|
|
|
946
|
if ($text =~ /^\s*\\(\w+\*?)/ && defined $parent && $parser->{config}{INNERCMDS}->{$1}) { |
|
|
|
100
|
|
|
|
|
388
|
2
|
|
|
|
|
4
|
my $command = $1; |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
# if the parent is already a command node, we have to make a new |
391
|
|
|
|
|
|
|
# nested command node |
392
|
|
|
|
|
|
|
# |
393
|
2
|
50
|
|
|
|
10
|
if ($parent->{type} eq 'COMMAND') { |
|
|
50
|
|
|
|
|
|
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
# make a new command node |
396
|
|
|
|
|
|
|
my $newnode = LaTeX::TOM::Node->_new( |
397
|
|
|
|
|
|
|
{type => 'COMMAND', |
398
|
|
|
|
|
|
|
command => $command, |
399
|
|
|
|
|
|
|
start => $parent->{start}, |
400
|
|
|
|
|
|
|
end => $parent->{end}, |
401
|
|
|
|
|
|
|
position => 'inner', |
402
|
0
|
|
|
|
|
0
|
children => $parent->{children} }); |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# point parent to it |
405
|
0
|
|
|
|
|
0
|
$parent->{children} = LaTeX::TOM::Tree->_new([$newnode]); |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# start over at this level (get additional inner commands) |
408
|
0
|
|
|
|
|
0
|
$parent = $newnode; |
409
|
0
|
|
|
|
|
0
|
$i = -1; |
410
|
|
|
|
|
|
|
|
411
|
0
|
|
|
|
|
0
|
$parser->{USED_COMMANDS}->{$newnode->{command}} = 1; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# parent is a naked group, we can make it into a command node |
415
|
|
|
|
|
|
|
# |
416
|
|
|
|
|
|
|
elsif ($parent->{type} eq 'GROUP') { |
417
|
2
|
|
|
|
|
5
|
$parent->{type} = 'COMMAND'; |
418
|
2
|
|
|
|
|
4
|
$parent->{command} = $command; |
419
|
2
|
|
|
|
|
4
|
$parent->{position} = 'inner'; |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# start over at this level |
422
|
2
|
|
|
|
|
3
|
$i = -1; |
423
|
|
|
|
|
|
|
|
424
|
2
|
|
|
|
|
4
|
$parser->{USED_COMMANDS}->{$parent->{command}} = 1; |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
2
|
|
|
|
|
21
|
$node->{content} =~ s/^\s*\\(?:\w+\*?)//o; |
428
|
|
|
|
|
|
|
} |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# outer command (such as \command{parameters}). our regexp checks to |
431
|
|
|
|
|
|
|
# see if this text chunk ends in \command, since that would be the case |
432
|
|
|
|
|
|
|
# due to the previous parsing stages. |
433
|
|
|
|
|
|
|
# |
434
|
266
|
100
|
100
|
|
|
1291
|
if ($text =~ /(?:^|[^\\])(\\\w+\*?(\s*\[.*?\])?)\s*$/os && |
|
|
|
66
|
|
|
|
|
435
|
|
|
|
|
|
|
defined $tree->{nodes}[$i+1] && |
436
|
|
|
|
|
|
|
$tree->{nodes}[$i+1]->{type} eq 'GROUP') { |
437
|
|
|
|
|
|
|
|
438
|
112
|
|
|
|
|
293
|
my $tag = $1; |
439
|
|
|
|
|
|
|
|
440
|
112
|
|
|
|
|
384
|
_debug("found text node [$text] with command tag [$tag]", undef); |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
# remove the text |
443
|
112
|
|
|
|
|
611
|
$node->{content} =~ s/\\\w+\*?\s*(?:\[.*?\])?\s*$//os; |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# parse it for command and ops |
446
|
112
|
|
|
|
|
348
|
$tag =~ /^\\(\w+\*?)\s*(?:\[(.*?)\])?$/os; |
447
|
|
|
|
|
|
|
|
448
|
112
|
|
|
|
|
234
|
my $command = $1; |
449
|
112
|
|
|
|
|
186
|
my $opts = $2; |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
# make the next node a command node with the above data |
452
|
112
|
|
|
|
|
187
|
my $next = $tree->{nodes}[$i+1]; |
453
|
|
|
|
|
|
|
|
454
|
112
|
|
|
|
|
208
|
$next->{type} = 'COMMAND'; |
455
|
112
|
|
|
|
|
233
|
$next->{command} = $command; |
456
|
112
|
|
|
|
|
177
|
$next->{opts} = $opts; |
457
|
112
|
|
|
|
|
190
|
$next->{position} = 'outer'; |
458
|
|
|
|
|
|
|
|
459
|
112
|
|
|
|
|
262
|
$parser->{USED_COMMANDS}->{$next->{command}} = 1; |
460
|
|
|
|
|
|
|
} |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
# recognize braceless commands |
463
|
|
|
|
|
|
|
# |
464
|
266
|
100
|
66
|
|
|
1333
|
if ($text =~ /(\\(\w+\*?)[ \t]+(\S+))/gso || $text =~ /(\\(\w+)(\d+))/gso) { |
465
|
2
|
|
|
|
|
6
|
my $all = $1; |
466
|
2
|
|
|
|
|
6
|
my $command = $2; |
467
|
2
|
|
|
|
|
6
|
my $param = $3; |
468
|
|
|
|
|
|
|
|
469
|
2
|
50
|
|
|
|
8
|
if ($parser->{config}{BRACELESS}->{$command}) { |
470
|
|
|
|
|
|
|
# warn "found braceless command $command with param $param"; |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
# get location to split from node text |
473
|
0
|
|
|
|
|
0
|
my $a = index $node->{content}, $all, 0; |
474
|
0
|
|
|
|
|
0
|
my $b = $a + length($all) - 1; |
475
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
# make all the new nodes |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
# new left and right text nodes |
479
|
0
|
|
|
|
|
0
|
my ($leftnode, $rightnode) = $node->split($a, $b); |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# param contents node |
482
|
0
|
|
|
|
|
0
|
my $pstart = index $node->{content}, $param, $a; |
483
|
|
|
|
|
|
|
my $newchild = LaTeX::TOM::Node->_new( |
484
|
|
|
|
|
|
|
{type => 'TEXT', |
485
|
|
|
|
|
|
|
start => $node->{start} + $pstart, |
486
|
0
|
|
|
|
|
0
|
end => $node->{start} + $pstart + length($param) - 1, |
487
|
|
|
|
|
|
|
content => $param }); |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
# new command node |
490
|
|
|
|
|
|
|
my $commandnode = LaTeX::TOM::Node->_new( |
491
|
|
|
|
|
|
|
{type => 'COMMAND', |
492
|
|
|
|
|
|
|
braces => 0, |
493
|
|
|
|
|
|
|
command => $command, |
494
|
|
|
|
|
|
|
start => $node->{start} + $a, |
495
|
0
|
|
|
|
|
0
|
end => $node->{start} + $b, |
496
|
|
|
|
|
|
|
children => LaTeX::TOM::Tree->_new([$newchild]), |
497
|
|
|
|
|
|
|
}); |
498
|
|
|
|
|
|
|
|
499
|
0
|
|
|
|
|
0
|
$parser->{USED_COMMANDS}->{$commandnode->{command}} = 1; |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
# splice these all into the original array |
502
|
0
|
|
|
|
|
0
|
splice @{$tree->{nodes}}, $i, 1, $leftnode, $commandnode, $rightnode; |
|
0
|
|
|
|
|
0
|
|
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# make the rightnode the node we're currently analyzing |
505
|
0
|
|
|
|
|
0
|
$node = $rightnode; |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
# make sure outer loop will continue parsing *after* rightnode |
508
|
0
|
|
|
|
|
0
|
$i += 2; |
509
|
|
|
|
|
|
|
} |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
} |
512
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
# recur |
514
|
385
|
100
|
100
|
|
|
1250
|
if ($node->{type} eq 'GROUP' || |
515
|
|
|
|
|
|
|
$node->{type} eq 'COMMAND') { |
516
|
|
|
|
|
|
|
|
517
|
119
|
|
|
|
|
316
|
$parser->_stage3($node->{children}, $node); |
518
|
|
|
|
|
|
|
} |
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
} |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
# this stage finds \begin{x} \end{x} environments and shoves their contents |
523
|
|
|
|
|
|
|
# down into a new child node, with a parent node of ENVIRONMENT type. |
524
|
|
|
|
|
|
|
# |
525
|
|
|
|
|
|
|
# this has the effect of making the tree deeper, since much of the structure |
526
|
|
|
|
|
|
|
# is in environment tags and will now be picked up. |
527
|
|
|
|
|
|
|
# |
528
|
|
|
|
|
|
|
# for ENVIRONMENTs, "start" means the ending } on the \begin tag, |
529
|
|
|
|
|
|
|
# "end" means the starting \ on the \end tag, |
530
|
|
|
|
|
|
|
# "ostart" is the starting \ on the "begin" tag, |
531
|
|
|
|
|
|
|
# "oend" is the ending } on the "end" tag, and |
532
|
|
|
|
|
|
|
# and "class" is the "x" from above. |
533
|
|
|
|
|
|
|
# |
534
|
|
|
|
|
|
|
sub _stage4 { |
535
|
144
|
|
|
144
|
|
214
|
my $parser = shift; |
536
|
144
|
|
|
|
|
179
|
my $tree = shift; |
537
|
|
|
|
|
|
|
|
538
|
144
|
|
|
|
|
185
|
my $bcount = 0; # \begin "stack count" |
539
|
144
|
|
|
|
|
185
|
my $class = ""; # environment class |
540
|
144
|
|
|
|
|
171
|
my $bidx = 0; # \begin array index. |
541
|
|
|
|
|
|
|
|
542
|
144
|
|
|
|
|
199
|
for (my $i = 0; $i < @{$tree->{nodes}}; $i++) { |
|
604
|
|
|
|
|
1155
|
|
543
|
460
|
|
|
|
|
592
|
my $node = $tree->{nodes}->[$i]; |
544
|
|
|
|
|
|
|
|
545
|
|
|
|
|
|
|
# see if this is a "\begin" command node |
546
|
460
|
100
|
100
|
|
|
1879
|
if ($node->{type} eq 'COMMAND' && $node->{command} eq 'begin') { |
|
|
100
|
100
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
547
|
|
|
|
|
|
|
|
548
|
32
|
|
|
|
|
150
|
_debug("parseStage4: found a begin COMMAND node, $node->{children}->{nodes}[0]->{content} @ $node->{start}", undef); |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
# start a new "stack" |
551
|
32
|
100
|
|
|
|
95
|
if ($bcount == 0) { |
|
|
50
|
|
|
|
|
|
552
|
28
|
|
|
|
|
52
|
$bidx = $i; |
553
|
28
|
|
|
|
|
37
|
$bcount++; |
554
|
28
|
|
|
|
|
69
|
$class = $node->{children}->{nodes}->[0]->{content}; |
555
|
28
|
|
|
|
|
74
|
_debug("parseStage4: opening environment tag found, class = $class", undef); |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
# add to the "stack" |
559
|
|
|
|
|
|
|
elsif ($node->{children}->{nodes}->[0]->{content} eq $class) { |
560
|
0
|
|
|
|
|
0
|
$bcount++; |
561
|
0
|
|
|
|
|
0
|
_debug("parseStage4: incrementing tag count for $class", undef); |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
} |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
# handle "\end" command nodes |
566
|
|
|
|
|
|
|
elsif ($node->{type} eq 'COMMAND' && |
567
|
|
|
|
|
|
|
$node->{command} eq 'end' && |
568
|
|
|
|
|
|
|
$node->{children}->{nodes}->[0]->{content} eq $class) { |
569
|
|
|
|
|
|
|
|
570
|
27
|
|
|
|
|
66
|
$bcount--; |
571
|
27
|
|
|
|
|
94
|
_debug("parseStage4: decrementing tag count for $class", undef); |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
# we found our closing "\end" tag. replace everything with the proper |
574
|
|
|
|
|
|
|
# ENVIRONMENT tag and subtree. |
575
|
|
|
|
|
|
|
# |
576
|
27
|
50
|
|
|
|
65
|
if ($bcount == 0) { |
577
|
|
|
|
|
|
|
|
578
|
27
|
|
|
|
|
79
|
_debug("parseStage4: closing environment $class", undef); |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
# first we must take everything between the "\begin" and "\end" |
581
|
|
|
|
|
|
|
# nodes and put them in a new array, removing them from the old one |
582
|
27
|
|
|
|
|
46
|
my @newarray = splice @{$tree->{nodes}}, $bidx+1, $i - ($bidx + 1); |
|
27
|
|
|
|
|
106
|
|
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
# make the ENVIRONMENT node |
585
|
27
|
|
|
|
|
67
|
my $start = $tree->{nodes}[$bidx]->{end}; |
586
|
27
|
|
|
|
|
52
|
my $end = $node->{start}; |
587
|
27
|
|
|
|
|
159
|
my $envnode = LaTeX::TOM::Node->_new( |
588
|
|
|
|
|
|
|
{type => 'ENVIRONMENT', |
589
|
|
|
|
|
|
|
class => $class, |
590
|
|
|
|
|
|
|
start => $start, # "inner" start and end |
591
|
|
|
|
|
|
|
end => $end, |
592
|
|
|
|
|
|
|
ostart => $start - length('begin') - length($class) - 2, |
593
|
|
|
|
|
|
|
oend => $end + length('end') + length($class) + 2, |
594
|
|
|
|
|
|
|
children => LaTeX::TOM::Tree->_new([@newarray]), |
595
|
|
|
|
|
|
|
}); |
596
|
|
|
|
|
|
|
|
597
|
27
|
50
|
|
|
|
136
|
if ($parser->{config}{MATHENVS}->{$envnode->{class}}) { |
598
|
0
|
|
|
|
|
0
|
$envnode->{math} = 1; |
599
|
|
|
|
|
|
|
} |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
# replace the \begin and \end COMMAND nodes with the single |
602
|
|
|
|
|
|
|
# environment node |
603
|
27
|
|
|
|
|
48
|
splice @{$tree->{nodes}}, $bidx, 2, $envnode; |
|
27
|
|
|
|
|
166
|
|
604
|
|
|
|
|
|
|
|
605
|
27
|
|
|
|
|
57
|
$class = ""; # reset class. |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
# i is going to change by however many nodes we removed |
608
|
27
|
|
|
|
|
47
|
$i -= scalar @newarray; |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
# recur into the children |
611
|
27
|
|
|
|
|
75
|
$parser->_stage4($envnode->{children}); |
612
|
|
|
|
|
|
|
} |
613
|
|
|
|
|
|
|
} |
614
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
# recur in general |
616
|
|
|
|
|
|
|
elsif ($node->{children}) { |
617
|
90
|
|
|
|
|
296
|
$parser->_stage4($node->{children}); |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
# parse error if we're missing an "\end" tag. |
622
|
144
|
100
|
|
|
|
409
|
if ($bcount > 0) { |
623
|
1
|
|
|
|
|
10
|
$error_handlers{$parser->{PARSE_ERRORS_FATAL}}->( |
624
|
|
|
|
|
|
|
"missing \\end{$class} for \\begin{$class} at position $tree->{nodes}[$bidx]->{end}" |
625
|
|
|
|
|
|
|
); |
626
|
|
|
|
|
|
|
} |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
# This is the "math" stage: here we grab simple-delimiter math modes from |
630
|
|
|
|
|
|
|
# the text they are embedded in, and turn those into new groupings, with the |
631
|
|
|
|
|
|
|
# "math" flag set. |
632
|
|
|
|
|
|
|
# |
633
|
|
|
|
|
|
|
# having this top level to go over all the bracket types prevents some pretty |
634
|
|
|
|
|
|
|
# bad combinatorial explosion |
635
|
|
|
|
|
|
|
# |
636
|
|
|
|
|
|
|
sub _stage5 { |
637
|
46
|
|
|
46
|
|
70
|
my $parser = shift; |
638
|
|
|
|
|
|
|
|
639
|
46
|
|
|
|
|
68
|
my $tree = shift; |
640
|
46
|
|
100
|
|
|
145
|
my $caremath = shift || 0; |
641
|
|
|
|
|
|
|
|
642
|
46
|
|
|
|
|
78
|
my $brackets = $parser->{MATHBRACKETS}; |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
# loop through all the different math mode bracket types |
645
|
46
|
|
|
|
|
230
|
foreach my $left (sort {length($b) <=> length($a)} keys %$brackets) { |
|
125
|
|
|
|
|
278
|
|
646
|
140
|
|
|
|
|
252
|
my $right = $brackets->{$left}; |
647
|
|
|
|
|
|
|
|
648
|
140
|
|
|
|
|
289
|
$parser->_stage5_r($tree, $left, $right, $caremath); |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
} |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
# recursive meat of above |
653
|
|
|
|
|
|
|
# |
654
|
|
|
|
|
|
|
sub _stage5_r { |
655
|
554
|
|
|
554
|
|
740
|
my $parser = shift; |
656
|
|
|
|
|
|
|
|
657
|
554
|
|
|
|
|
685
|
my $tree = shift; |
658
|
554
|
|
|
|
|
694
|
my $left = shift; |
659
|
554
|
|
|
|
|
671
|
my $right = shift; |
660
|
554
|
|
100
|
|
|
1139
|
my $caremath = shift || 0; # do we care if we're already in math mode? |
661
|
|
|
|
|
|
|
# this matters for \( \), \[ \] |
662
|
|
|
|
|
|
|
|
663
|
554
|
|
|
|
|
690
|
my $leftpos = -1; # no text pos for found left brace yet. |
664
|
554
|
|
|
|
|
624
|
my $leftidx = -1; # no array index for found left brace yet. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
# loop through the nodes |
667
|
554
|
|
|
|
|
726
|
for (my $i = 0; $i < scalar @{$tree->{nodes}}; $i++) { |
|
2120
|
|
|
|
|
3870
|
|
668
|
1566
|
|
|
|
|
2062
|
my $node = $tree->{nodes}[$i]; |
669
|
1566
|
|
|
|
|
1908
|
my $pos = 0; # position placeholder within the text block |
670
|
1566
|
|
|
|
|
2146
|
my $spos = $node->{start}; # get text start position |
671
|
|
|
|
|
|
|
|
672
|
1566
|
100
|
66
|
|
|
6957
|
if ($node->{type} eq 'TEXT' && |
|
|
100
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
673
|
|
|
|
|
|
|
(!$caremath || (!$node->{math} && $caremath))) { |
674
|
|
|
|
|
|
|
|
675
|
|
|
|
|
|
|
# search for left brace if we haven't started a pair yet |
676
|
1114
|
50
|
|
|
|
1817
|
if ($leftidx == -1) { |
677
|
1114
|
|
|
|
|
1775
|
$leftpos = _findsymbol($node->{content}, $left, $pos); |
678
|
|
|
|
|
|
|
|
679
|
1114
|
50
|
|
|
|
2010
|
if ($leftpos != -1) { |
680
|
0
|
|
|
|
|
0
|
_debug("found (left) $left in [$node->{content}]", undef); |
681
|
0
|
|
|
|
|
0
|
$leftidx = $i; |
682
|
0
|
|
|
|
|
0
|
$pos = $leftpos + 1; # next pos to search from |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
# search for a right brace |
687
|
1114
|
50
|
|
|
|
1685
|
if ($leftpos != -1) { |
688
|
0
|
|
|
|
|
0
|
my $rightpos = _findsymbol($node->{content}, $right, $pos); |
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
# found |
691
|
0
|
0
|
|
|
|
0
|
if ($rightpos != -1) { |
692
|
|
|
|
|
|
|
|
693
|
|
|
|
|
|
|
# we have to split the text node into 3 parts |
694
|
0
|
0
|
|
|
|
0
|
if ($leftidx == $i) { |
695
|
0
|
|
|
|
|
0
|
_debug("splitwithin: found (right) $right in [$node->{content}]", undef); |
696
|
|
|
|
|
|
|
|
697
|
0
|
|
|
|
|
0
|
my ($leftnode, $textnode3) = $node->split($rightpos, $rightpos + length($right) - 1); |
698
|
0
|
|
|
|
|
0
|
my ($textnode1, $textnode2) = $leftnode->split($leftpos, $leftpos + length($left) - 1); |
699
|
|
|
|
|
|
|
|
700
|
0
|
|
|
|
|
0
|
my $startpos = $spos; # get text start position |
701
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
# make the math ENVIRONMENT node |
703
|
0
|
|
|
|
|
0
|
my $mathnode = LaTeX::TOM::Node->_new( |
704
|
|
|
|
|
|
|
{type => 'ENVIRONMENT', |
705
|
|
|
|
|
|
|
class => $left, # use left delim as class |
706
|
|
|
|
|
|
|
math => 1, |
707
|
|
|
|
|
|
|
start => $startpos + $leftpos, |
708
|
|
|
|
|
|
|
ostart => $startpos + $leftpos - length($left) + 1, |
709
|
|
|
|
|
|
|
end => $startpos + $rightpos, |
710
|
|
|
|
|
|
|
oend => $startpos + $rightpos + length($right) - 1, |
711
|
|
|
|
|
|
|
children => LaTeX::TOM::Tree->_new([$textnode2]), |
712
|
|
|
|
|
|
|
}); |
713
|
|
|
|
|
|
|
|
714
|
0
|
|
|
|
|
0
|
splice @{$tree->{nodes}}, $i, 1, $textnode1, $mathnode, $textnode3; |
|
0
|
|
|
|
|
0
|
|
715
|
|
|
|
|
|
|
|
716
|
0
|
|
|
|
|
0
|
$i++; # skip ahead two nodes, so we'll be parsing textnode3 |
717
|
|
|
|
|
|
|
} |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
# split across nodes |
720
|
|
|
|
|
|
|
else { |
721
|
|
|
|
|
|
|
|
722
|
0
|
|
|
|
|
0
|
_debug("splitacross: found (right) $right in [$node->{content}]", undef); |
723
|
|
|
|
|
|
|
|
724
|
|
|
|
|
|
|
# create new set of 4 smaller text nodes from the original two |
725
|
|
|
|
|
|
|
# that contain the left and right delimiters |
726
|
|
|
|
|
|
|
# |
727
|
0
|
|
|
|
|
0
|
my ($textnode1, $textnode2) = $tree->{nodes}[$leftidx]->split($leftpos, $leftpos + length($left) - 1); |
728
|
0
|
|
|
|
|
0
|
my ($textnode3, $textnode4) = $tree->{nodes}[$i]->split($rightpos, $rightpos + length($right) - 1); |
729
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
# nodes to remove "from the middle" (between the left and right |
731
|
|
|
|
|
|
|
# text nodes which contain the delimiters) |
732
|
|
|
|
|
|
|
# |
733
|
0
|
|
|
|
|
0
|
my @remnodes = splice @{$tree->{nodes}}, $leftidx+1, $i - $leftidx - 1; |
|
0
|
|
|
|
|
0
|
|
734
|
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
# create a math node that contains the text after the left brace, |
736
|
|
|
|
|
|
|
# then all the nodes up until the next text node, then the text |
737
|
|
|
|
|
|
|
# before the right brace. |
738
|
|
|
|
|
|
|
# |
739
|
|
|
|
|
|
|
my $mathnode = LaTeX::TOM::Node->_new( |
740
|
|
|
|
|
|
|
{type => 'ENVIRONMENT', |
741
|
|
|
|
|
|
|
class => $left, |
742
|
|
|
|
|
|
|
math => 1, |
743
|
|
|
|
|
|
|
start => $textnode2->{start} - 1, |
744
|
|
|
|
|
|
|
end => $textnode3->{end} + 1, |
745
|
|
|
|
|
|
|
ostart => $textnode2->{start} - 1 - length($left) + 1, |
746
|
0
|
|
|
|
|
0
|
oend => $textnode3->{end} + 1 + length($right) - 1, |
747
|
|
|
|
|
|
|
children => LaTeX::TOM::Tree->_new( |
748
|
|
|
|
|
|
|
[$textnode2, |
749
|
|
|
|
|
|
|
@remnodes, |
750
|
|
|
|
|
|
|
$textnode3]), |
751
|
|
|
|
|
|
|
}); |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
# replace (TEXT_A, ... , TEXT_B) with the mathnode created above |
754
|
0
|
|
|
|
|
0
|
splice @{$tree->{nodes}}, $leftidx, 2, $textnode1, $mathnode, $textnode4; |
|
0
|
|
|
|
|
0
|
|
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
# do all nodes again but the very leftmost |
757
|
|
|
|
|
|
|
# |
758
|
0
|
|
|
|
|
0
|
$i = $leftidx; |
759
|
|
|
|
|
|
|
} |
760
|
|
|
|
|
|
|
|
761
|
0
|
|
|
|
|
0
|
$leftpos = -1; # reset left data |
762
|
0
|
|
|
|
|
0
|
$leftidx = -1; |
763
|
|
|
|
|
|
|
} # right brace |
764
|
|
|
|
|
|
|
} # left brace |
765
|
|
|
|
|
|
|
else { |
766
|
|
|
|
|
|
|
|
767
|
1114
|
|
|
|
|
1732
|
my $rightpos = _findsymbol($node->{content}, $right, $pos); |
768
|
|
|
|
|
|
|
|
769
|
1114
|
50
|
|
|
|
2211
|
if ($rightpos != -1) { |
770
|
0
|
|
|
|
|
0
|
my $startpos = $node->{start}; # get text start position |
771
|
0
|
|
|
|
|
0
|
$error_handlers{$parser->{PARSE_ERRORS_FATAL}}->("unmatched '$right' at " . ($startpos + $rightpos)); |
772
|
|
|
|
|
|
|
} |
773
|
|
|
|
|
|
|
} |
774
|
|
|
|
|
|
|
} # if TEXT |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
# recur, but not into verbatim environments! |
777
|
|
|
|
|
|
|
# |
778
|
|
|
|
|
|
|
elsif ($node->{children} && |
779
|
|
|
|
|
|
|
!( |
780
|
|
|
|
|
|
|
($node->{type} eq 'COMMAND' && $node->{command} =~ /^verb/) || |
781
|
|
|
|
|
|
|
($node->{type} eq 'ENVIRONMENT' && $node->{class} =~ /^verbatim/))) { |
782
|
|
|
|
|
|
|
|
783
|
414
|
50
|
|
|
|
746
|
if ($LaTeX::TOM::DEBUG) { |
784
|
0
|
|
|
|
|
0
|
my $message = "Recurring into $node->{type} node "; |
785
|
0
|
0
|
|
|
|
0
|
$message .= $node->{command} if ($node->{type} eq 'COMMAND'); |
786
|
0
|
0
|
|
|
|
0
|
$message .= $node->{class} if ($node->{type} eq 'ENVIRONMENT'); |
787
|
0
|
|
|
|
|
0
|
_debug($message, undef); |
788
|
|
|
|
|
|
|
} |
789
|
|
|
|
|
|
|
|
790
|
414
|
|
|
|
|
998
|
$parser->_stage5_r($node->{children}, $left, $right, $caremath); |
791
|
|
|
|
|
|
|
} |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
} # loop over text blocks |
794
|
|
|
|
|
|
|
|
795
|
554
|
50
|
|
|
|
1244
|
if ($leftpos != -1) { |
796
|
0
|
|
|
|
|
0
|
my $startpos = $tree->{nodes}[$leftidx]->{start}; # get text start position |
797
|
0
|
|
|
|
|
0
|
$error_handlers{$parser->{PARSE_ERRORS_FATAL}}->("unmatched '$left' at " . ($startpos + $leftpos)); |
798
|
|
|
|
|
|
|
} |
799
|
|
|
|
|
|
|
} |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
# This stage propegates the math mode flag and plaintext flags downward. |
802
|
|
|
|
|
|
|
# |
803
|
|
|
|
|
|
|
# After this is done, we can make the claim that only text nodes marked with |
804
|
|
|
|
|
|
|
# the plaintext flag should be printed. math nodes will have the "math" flag, |
805
|
|
|
|
|
|
|
# and also plantext = 0. |
806
|
|
|
|
|
|
|
# |
807
|
|
|
|
|
|
|
sub _propegateModes { |
808
|
190
|
|
|
190
|
|
264
|
my $parser = shift; |
809
|
|
|
|
|
|
|
|
810
|
190
|
|
|
|
|
241
|
my $tree = shift; |
811
|
190
|
|
|
|
|
232
|
my $math = shift; # most likely want to call this with 0 |
812
|
190
|
|
|
|
|
231
|
my $plaintext = shift; # ditto this-- default to nothing visible. |
813
|
|
|
|
|
|
|
|
814
|
190
|
|
|
|
|
233
|
foreach my $node (@{$tree->{nodes}}) { |
|
190
|
|
|
|
|
338
|
|
815
|
|
|
|
|
|
|
|
816
|
|
|
|
|
|
|
# handle text nodes on this level. set flags. |
817
|
|
|
|
|
|
|
# |
818
|
536
|
100
|
|
|
|
948
|
if ($node->{type} eq 'TEXT') { |
|
|
50
|
|
|
|
|
|
819
|
384
|
|
|
|
|
546
|
$node->{math} = $math; |
820
|
384
|
|
|
|
|
642
|
$node->{plaintext} = $plaintext; |
821
|
|
|
|
|
|
|
} |
822
|
|
|
|
|
|
|
|
823
|
|
|
|
|
|
|
# propegate flags downward, possibly modified |
824
|
|
|
|
|
|
|
# |
825
|
|
|
|
|
|
|
elsif (defined $node->{children}) { |
826
|
|
|
|
|
|
|
|
827
|
152
|
|
|
|
|
194
|
my $mathflag = $math; # math propegates down by default |
828
|
152
|
|
|
|
|
196
|
my $plaintextflag = 0; # plaintext flag does NOT propegate by default |
829
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
# handle math or plain text forcing envs |
831
|
|
|
|
|
|
|
# |
832
|
152
|
100
|
100
|
|
|
486
|
if ($node->{type} eq 'ENVIRONMENT' || $node->{type} eq 'COMMAND') { |
|
|
50
|
|
|
|
|
|
833
|
148
|
50
|
33
|
|
|
1280
|
if (defined $node->{class} && ( |
|
|
100
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
834
|
|
|
|
|
|
|
$parser->{config}{MATHENVS}->{$node->{class}} || |
835
|
|
|
|
|
|
|
$parser->{config}{MATHENVS}->{"$node->{class}*"}) |
836
|
|
|
|
|
|
|
) |
837
|
|
|
|
|
|
|
{ |
838
|
0
|
|
|
|
|
0
|
$mathflag = 1; |
839
|
0
|
|
|
|
|
0
|
$plaintextflag = 0; |
840
|
|
|
|
|
|
|
} |
841
|
|
|
|
|
|
|
elsif (($node->{type} eq 'COMMAND' && |
842
|
|
|
|
|
|
|
($parser->{config}{TEXTENVS}->{$node->{command}} || |
843
|
|
|
|
|
|
|
$parser->{config}{TEXTENVS}->{"$node->{command}*"})) || |
844
|
|
|
|
|
|
|
($node->{type} eq 'ENVIRONMENT' && |
845
|
|
|
|
|
|
|
($parser->{config}{TEXTENVS}->{$node->{class}} || |
846
|
|
|
|
|
|
|
$parser->{config}{TEXTENVS}{"$node->{command}*"})) |
847
|
|
|
|
|
|
|
) { |
848
|
|
|
|
|
|
|
|
849
|
80
|
|
|
|
|
124
|
$mathflag = 0; |
850
|
80
|
|
|
|
|
103
|
$plaintextflag = 1; |
851
|
|
|
|
|
|
|
} |
852
|
|
|
|
|
|
|
} |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
# groupings change nothing |
855
|
|
|
|
|
|
|
# |
856
|
|
|
|
|
|
|
elsif ($node->{type} eq 'GROUP') { |
857
|
4
|
|
|
|
|
6
|
$mathflag = $math; |
858
|
4
|
|
|
|
|
6
|
$plaintextflag = $plaintext; |
859
|
|
|
|
|
|
|
} |
860
|
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
# recur |
862
|
152
|
|
|
|
|
427
|
$parser->_propegateModes($node->{children}, $mathflag, $plaintextflag); |
863
|
|
|
|
|
|
|
} |
864
|
|
|
|
|
|
|
} |
865
|
|
|
|
|
|
|
} |
866
|
|
|
|
|
|
|
|
867
|
|
|
|
|
|
|
# apply a mapping to text nodes in a tree |
868
|
|
|
|
|
|
|
# |
869
|
|
|
|
|
|
|
# for newcommands and defs: mapping is a hash: |
870
|
|
|
|
|
|
|
# |
871
|
|
|
|
|
|
|
# {name, nparams, template, type} |
872
|
|
|
|
|
|
|
# |
873
|
|
|
|
|
|
|
# name is a string |
874
|
|
|
|
|
|
|
# nparams is an integer |
875
|
|
|
|
|
|
|
# template is a tree fragement containing text nodes with #x flags, where |
876
|
|
|
|
|
|
|
# parameters will be replaced. |
877
|
|
|
|
|
|
|
# type is "command" |
878
|
|
|
|
|
|
|
# |
879
|
|
|
|
|
|
|
# for newenvironments: |
880
|
|
|
|
|
|
|
# |
881
|
|
|
|
|
|
|
# {name, nparams, btemplate, etemplate, type} |
882
|
|
|
|
|
|
|
# |
883
|
|
|
|
|
|
|
# same as above, except type is "environment" and there are two templates, |
884
|
|
|
|
|
|
|
# btemplate and etemplate. |
885
|
|
|
|
|
|
|
# |
886
|
|
|
|
|
|
|
sub _applyMapping { |
887
|
12
|
|
|
12
|
|
18
|
my $parser = shift; |
888
|
|
|
|
|
|
|
|
889
|
12
|
|
|
|
|
16
|
my $tree = shift; |
890
|
12
|
|
|
|
|
18
|
my $mapping = shift; |
891
|
12
|
|
100
|
|
|
29
|
my $i = shift || 0; # index to start with, in tree. |
892
|
|
|
|
|
|
|
|
893
|
12
|
|
|
|
|
13
|
my $applications = 0; # keep track of # of applications |
894
|
|
|
|
|
|
|
|
895
|
12
|
|
|
|
|
16
|
for (; $i < @{$tree->{nodes}}; $i++) { |
|
54
|
|
|
|
|
109
|
|
896
|
|
|
|
|
|
|
|
897
|
42
|
|
|
|
|
54
|
my $node = $tree->{nodes}[$i]; |
898
|
|
|
|
|
|
|
|
899
|
|
|
|
|
|
|
# begin environment nodes |
900
|
|
|
|
|
|
|
# |
901
|
42
|
100
|
100
|
|
|
333
|
if ($node->{type} eq 'COMMAND' |
|
|
100
|
100
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
902
|
|
|
|
|
|
|
&& $node->{command} eq 'begin' |
903
|
|
|
|
|
|
|
&& $node->{children}->{nodes}[0]->{content} eq $mapping->{name} |
904
|
|
|
|
|
|
|
) { |
905
|
|
|
|
|
|
|
# grab the nparams next group nodes as parameters |
906
|
|
|
|
|
|
|
# |
907
|
1
|
|
|
|
|
3
|
my @params = (); |
908
|
|
|
|
|
|
|
|
909
|
1
|
|
|
|
|
2
|
my $remain = $mapping->{nparams}; |
910
|
1
|
|
|
|
|
2
|
my $j = 1; |
911
|
1
|
|
33
|
|
|
20
|
while ($remain > 0 && ($i + $j) < scalar @{$tree->{nodes}}) { |
|
0
|
|
|
|
|
0
|
|
912
|
|
|
|
|
|
|
|
913
|
0
|
|
|
|
|
0
|
my $node = $tree->{nodes}[$i + $j]; |
914
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
# grab group node |
916
|
0
|
0
|
|
|
|
0
|
if ($node->{type} eq 'GROUP') { |
917
|
0
|
|
|
|
|
0
|
push @params, $node->{children}; |
918
|
0
|
|
|
|
|
0
|
$remain--; |
919
|
|
|
|
|
|
|
} |
920
|
|
|
|
|
|
|
|
921
|
0
|
|
|
|
|
0
|
$j++; |
922
|
|
|
|
|
|
|
} |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
# if we didn't get enough group nodes, bomb out |
925
|
1
|
50
|
|
|
|
7
|
next if $remain; |
926
|
|
|
|
|
|
|
|
927
|
|
|
|
|
|
|
# otherwise make new subtree |
928
|
1
|
|
|
|
|
4
|
my $applied = _applyParamsToTemplate($mapping->{btemplate}, @params); |
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
# splice in the result |
931
|
1
|
|
|
|
|
3
|
splice @{$tree->{nodes}}, $i, $j, @{$applied->{nodes}}; |
|
1
|
|
|
|
|
9
|
|
|
1
|
|
|
|
|
4
|
|
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
# skip past all the new stuff |
934
|
1
|
|
|
|
|
3
|
$i += scalar @{$applied->{nodes}} - 1; |
|
1
|
|
|
|
|
6
|
|
935
|
|
|
|
|
|
|
} |
936
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
# end environment nodes |
938
|
|
|
|
|
|
|
# |
939
|
|
|
|
|
|
|
elsif ($node->{type} eq 'COMMAND' |
940
|
|
|
|
|
|
|
&& $node->{command} eq 'end' |
941
|
|
|
|
|
|
|
&& $node->{children}->{nodes}[0]->{content} eq $mapping->{name} |
942
|
|
|
|
|
|
|
) { |
943
|
|
|
|
|
|
|
# make new subtree (no params) |
944
|
1
|
|
|
|
|
4
|
my $applied = $mapping->{etemplate}->copy(); |
945
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
# splice in the result |
947
|
1
|
|
|
|
|
3
|
splice @{$tree->{nodes}}, $i, 1, @{$applied->{nodes}}; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
13
|
|
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
# skip past all the new stuff |
950
|
1
|
|
|
|
|
3
|
$i += scalar @{$applied->{nodes}} - 1; |
|
1
|
|
|
|
|
4
|
|
951
|
|
|
|
|
|
|
|
952
|
1
|
|
|
|
|
4
|
$applications++; # only count end environment nodes |
953
|
|
|
|
|
|
|
} |
954
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
# newcommand nodes |
956
|
|
|
|
|
|
|
# |
957
|
|
|
|
|
|
|
elsif ($node->{type} eq 'COMMAND' |
958
|
|
|
|
|
|
|
&& $node->{command} eq $mapping->{name} |
959
|
|
|
|
|
|
|
&& $mapping->{nparams} |
960
|
|
|
|
|
|
|
) { |
961
|
1
|
|
|
|
|
4
|
my @params = (); |
962
|
|
|
|
|
|
|
|
963
|
|
|
|
|
|
|
# children of COMMAND node will be first parameter |
964
|
1
|
|
|
|
|
2
|
push @params, $node->{children}; |
965
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
# find next nparams GROUP nodes and push their children onto @params |
967
|
1
|
|
|
|
|
3
|
my $remain = $mapping->{nparams} - 1; |
968
|
1
|
|
|
|
|
1
|
my $j = 1; |
969
|
1
|
|
33
|
|
|
5
|
while ($remain > 0 && ($i + $j) < scalar @{$tree->{nodes}}) { |
|
0
|
|
|
|
|
0
|
|
970
|
|
|
|
|
|
|
|
971
|
0
|
|
|
|
|
0
|
my $node = $tree->{nodes}[$i + $j]; |
972
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
# grab group node |
974
|
0
|
0
|
|
|
|
0
|
if ($node->{type} eq 'GROUP') { |
975
|
0
|
|
|
|
|
0
|
push @params, $node->{children}; |
976
|
0
|
|
|
|
|
0
|
$remain--; |
977
|
|
|
|
|
|
|
} |
978
|
|
|
|
|
|
|
|
979
|
0
|
|
|
|
|
0
|
$j++; |
980
|
|
|
|
|
|
|
} |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
# if we didn't get enough group nodes, bomb out |
983
|
1
|
50
|
|
|
|
5
|
next if ($remain > 0); |
984
|
|
|
|
|
|
|
|
985
|
|
|
|
|
|
|
# apply the params to the template |
986
|
1
|
|
|
|
|
3
|
my $applied = _applyParamsToTemplate($mapping->{template}, @params); |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
# splice in the result |
989
|
1
|
|
|
|
|
3
|
splice @{$tree->{nodes}}, $i, $j, @{$applied->{nodes}}; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
2
|
|
990
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
# skip past all the new stuff |
992
|
1
|
|
|
|
|
2
|
$i += scalar @{$applied->{nodes}} - 1; |
|
1
|
|
|
|
|
2
|
|
993
|
|
|
|
|
|
|
|
994
|
1
|
|
|
|
|
4
|
$applications++; |
995
|
|
|
|
|
|
|
} |
996
|
|
|
|
|
|
|
|
997
|
|
|
|
|
|
|
# find 0-param mappings |
998
|
|
|
|
|
|
|
elsif ($node->{type} eq 'TEXT' && !$mapping->{nparams}) { |
999
|
|
|
|
|
|
|
|
1000
|
15
|
|
|
|
|
26
|
my $text = $node->{content}; |
1001
|
15
|
|
|
|
|
22
|
my $command = $mapping->{name}; |
1002
|
|
|
|
|
|
|
|
1003
|
|
|
|
|
|
|
# find occurrences of the mapping command |
1004
|
|
|
|
|
|
|
# |
1005
|
15
|
50
|
|
|
|
43
|
my $wordend = ($command =~ /\w$/ ? 1 : 0); |
1006
|
15
|
|
33
|
|
|
128
|
while (($wordend && $text =~ /\\\Q$command\E(\W|$)/g) || |
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
1007
|
|
|
|
|
|
|
(!$wordend && $text =~ /\\\Q$command\E/g)) { |
1008
|
|
|
|
|
|
|
|
1009
|
0
|
|
|
|
|
0
|
_debug("found occurrence of mapping $command", undef); |
1010
|
|
|
|
|
|
|
|
1011
|
0
|
|
|
|
|
0
|
my $idx = index $node->{content}, '\\' . $command, 0; |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
# split the text node at that command |
1014
|
0
|
|
|
|
|
0
|
my ($leftnode, $rightnode) = $node->split($idx, $idx + length($command)); |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
# copy the mapping template |
1017
|
0
|
|
|
|
|
0
|
my $applied = $mapping->{template}->copy(); |
1018
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
# splice the new nodes in |
1020
|
0
|
|
|
|
|
0
|
splice @{$tree->{nodes}}, $i, 1, $leftnode, @{$applied->{nodes}}, $rightnode; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
1021
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
# adjust i so we end up on rightnode when we're done |
1023
|
0
|
|
|
|
|
0
|
$i += scalar @{$applied->{nodes}} + 1; |
|
0
|
|
|
|
|
0
|
|
1024
|
|
|
|
|
|
|
|
1025
|
|
|
|
|
|
|
# get the next node |
1026
|
0
|
|
|
|
|
0
|
$node = $tree->{nodes}[$i]; |
1027
|
|
|
|
|
|
|
|
1028
|
|
|
|
|
|
|
# count application |
1029
|
0
|
|
|
|
|
0
|
$applications++; |
1030
|
|
|
|
|
|
|
} |
1031
|
|
|
|
|
|
|
} |
1032
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
# recur |
1034
|
|
|
|
|
|
|
elsif ($node->{children}) { |
1035
|
|
|
|
|
|
|
|
1036
|
10
|
|
|
|
|
43
|
$applications += $parser->_applyMapping($node->{children}, $mapping); |
1037
|
|
|
|
|
|
|
} |
1038
|
|
|
|
|
|
|
} |
1039
|
|
|
|
|
|
|
|
1040
|
12
|
|
|
|
|
26
|
return $applications; |
1041
|
|
|
|
|
|
|
} |
1042
|
|
|
|
|
|
|
|
1043
|
|
|
|
|
|
|
# find and apply all mappings in the tree, progressively and recursively. |
1044
|
|
|
|
|
|
|
# a mapping applies to the entire tree and subtree consisting of nodes AFTER |
1045
|
|
|
|
|
|
|
# itself in the level array. |
1046
|
|
|
|
|
|
|
# |
1047
|
|
|
|
|
|
|
sub _applyMappings { |
1048
|
14
|
|
|
14
|
|
21
|
my $parser = shift; |
1049
|
|
|
|
|
|
|
|
1050
|
14
|
|
|
|
|
18
|
my $tree = shift; |
1051
|
|
|
|
|
|
|
|
1052
|
14
|
|
|
|
|
29
|
for (my $i = 0; $i < @{$tree->{nodes}}; $i++) { |
|
63
|
|
|
|
|
116
|
|
1053
|
|
|
|
|
|
|
|
1054
|
49
|
|
|
|
|
71
|
my $prev = $tree->{nodes}[$i-1]; |
1055
|
49
|
|
|
|
|
61
|
my $node = $tree->{nodes}[$i]; |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
# find newcommands |
1058
|
49
|
100
|
100
|
|
|
298
|
if ($node->{type} eq 'COMMAND' && |
|
|
100
|
100
|
|
|
|
|
|
|
50
|
66
|
|
|
|
|
|
|
100
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
1059
|
|
|
|
|
|
|
$node->{command} =~ /^(re)?newcommand$/) { |
1060
|
|
|
|
|
|
|
|
1061
|
1
|
|
|
|
|
4
|
my $mapping = _makeMapping($tree, $i); |
1062
|
1
|
50
|
|
|
|
5
|
next if (!$mapping->{name}); # skip fragged commands |
1063
|
|
|
|
|
|
|
|
1064
|
1
|
50
|
|
|
|
4
|
if ($parser->{USED_COMMANDS}->{$mapping->{name}}) { |
1065
|
1
|
|
|
|
|
6
|
_debug("applying (nc) mapping $mapping->{name}", undef); |
1066
|
|
|
|
|
|
|
} else { |
1067
|
0
|
|
|
|
|
0
|
_debug("NOT applying (nc) mapping $mapping->{name}", undef); |
1068
|
0
|
|
|
|
|
0
|
next; |
1069
|
|
|
|
|
|
|
} |
1070
|
|
|
|
|
|
|
|
1071
|
|
|
|
|
|
|
# add to mappings list |
1072
|
|
|
|
|
|
|
# |
1073
|
1
|
|
|
|
|
5
|
$parser->{MAPPEDCMDS}->{"\\$mapping->{name}"} = 1; |
1074
|
|
|
|
|
|
|
|
1075
|
1
|
|
|
|
|
4
|
_debug("found a mapping with name $mapping->{name}, $mapping->{nparams} params", undef); |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
# remove the mapping declaration |
1078
|
|
|
|
|
|
|
# |
1079
|
1
|
|
|
|
|
3
|
splice @{$tree->{nodes}}, $i, $mapping->{skip} + 1; |
|
1
|
|
|
|
|
5
|
|
1080
|
|
|
|
|
|
|
|
1081
|
|
|
|
|
|
|
# apply the mapping |
1082
|
1
|
|
|
|
|
4
|
my $count = $parser->_applyMapping($tree, $mapping, $i); |
1083
|
|
|
|
|
|
|
|
1084
|
1
|
50
|
|
|
|
5
|
if ($count > 0) { |
1085
|
1
|
|
|
0
|
|
6
|
_debug("printing altered subtree", sub { $tree->_warn() }); |
|
0
|
|
|
|
|
0
|
|
1086
|
|
|
|
|
|
|
} |
1087
|
|
|
|
|
|
|
|
1088
|
1
|
|
|
|
|
7
|
$i--; # since we removed the cmd node, check this index again |
1089
|
|
|
|
|
|
|
} |
1090
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
# handle "\newenvironment" mappings |
1092
|
|
|
|
|
|
|
elsif ($node->{type} eq 'COMMAND' && |
1093
|
|
|
|
|
|
|
$node->{command} =~ /^(re)?newenvironment$/) { |
1094
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
# make a mapping hash |
1096
|
|
|
|
|
|
|
# |
1097
|
1
|
|
|
|
|
6
|
my $mapping = $parser->_makeEnvMapping($tree, $i); |
1098
|
1
|
50
|
|
|
|
4
|
next if (!$mapping->{name}); # skip fragged commands. |
1099
|
|
|
|
|
|
|
|
1100
|
1
|
|
|
|
|
13
|
_debug("applying (ne) mapping $mapping->{name}", undef); |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
# remove the mapping declaration |
1103
|
|
|
|
|
|
|
# |
1104
|
1
|
|
|
|
|
5
|
splice @{$tree->{nodes}}, $i, $mapping->{skip} + 1; |
|
1
|
|
|
|
|
8
|
|
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
# apply the mapping |
1107
|
|
|
|
|
|
|
# |
1108
|
1
|
|
|
|
|
5
|
my $count = $parser->_applyMapping($tree, $mapping, $i); |
1109
|
|
|
|
|
|
|
} |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
# handle "\def" stype commands. |
1112
|
|
|
|
|
|
|
elsif ($node->{type} eq 'COMMAND' && |
1113
|
|
|
|
|
|
|
defined $prev && |
1114
|
|
|
|
|
|
|
$prev->{type} eq 'TEXT' && |
1115
|
|
|
|
|
|
|
$prev->{content} =~ /\\def\s*$/o) { |
1116
|
|
|
|
|
|
|
|
1117
|
0
|
|
|
|
|
0
|
_debug("found def style mapping $node->{command}", undef); |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
# remove the \def |
1120
|
0
|
|
|
|
|
0
|
$prev->{content} =~ s/\\def\s*$//o; |
1121
|
|
|
|
|
|
|
|
1122
|
|
|
|
|
|
|
# make the mapping |
1123
|
|
|
|
|
|
|
my $mapping = {name => $node->{command}, |
1124
|
|
|
|
|
|
|
nparams => 0, |
1125
|
0
|
|
|
|
|
0
|
template => $node->{children}->copy(), |
1126
|
|
|
|
|
|
|
type => 'command'}; |
1127
|
|
|
|
|
|
|
|
1128
|
0
|
0
|
|
|
|
0
|
next if (!$mapping->{name}); # skip fragged commands |
1129
|
|
|
|
|
|
|
|
1130
|
0
|
0
|
|
|
|
0
|
if ($parser->{USED_COMMANDS}->{$mapping->{name}}) { |
1131
|
0
|
|
|
|
|
0
|
_debug("applying (def) mapping $mapping->{name}", undef); |
1132
|
|
|
|
|
|
|
} else { |
1133
|
0
|
|
|
|
|
0
|
_debug("NOT applying (def) mapping $mapping->{name}", undef); |
1134
|
0
|
|
|
|
|
0
|
next; |
1135
|
|
|
|
|
|
|
} |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
# add to mappings list |
1138
|
|
|
|
|
|
|
# |
1139
|
0
|
|
|
|
|
0
|
$parser->{MAPPEDCMDS}->{"\\$mapping->{name}"} = 1; |
1140
|
|
|
|
|
|
|
|
1141
|
0
|
|
|
0
|
|
0
|
_debug("template is", sub { $mapping->{template}->_warn() }); |
|
0
|
|
|
|
|
0
|
|
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
# remove the command node |
1144
|
0
|
|
|
|
|
0
|
splice @{$tree->{nodes}}, $i, 1; |
|
0
|
|
|
|
|
0
|
|
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
# apply the mapping |
1147
|
0
|
|
|
|
|
0
|
my $count = $parser->_applyMapping($tree, $mapping, $i); |
1148
|
|
|
|
|
|
|
|
1149
|
0
|
|
|
|
|
0
|
$i--; # check this index again |
1150
|
|
|
|
|
|
|
} |
1151
|
|
|
|
|
|
|
|
1152
|
|
|
|
|
|
|
# recur |
1153
|
|
|
|
|
|
|
elsif ($node->{children}) { |
1154
|
|
|
|
|
|
|
|
1155
|
12
|
|
|
|
|
27
|
$parser->_applyMappings($node->{children}); |
1156
|
|
|
|
|
|
|
} |
1157
|
|
|
|
|
|
|
} |
1158
|
|
|
|
|
|
|
} |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
# read files from \input commands and place into the tree, parsed |
1161
|
|
|
|
|
|
|
# |
1162
|
|
|
|
|
|
|
# also include bibliographies |
1163
|
|
|
|
|
|
|
# |
1164
|
|
|
|
|
|
|
sub _addInputs { |
1165
|
89
|
|
|
89
|
|
123
|
my $parser = shift; |
1166
|
|
|
|
|
|
|
|
1167
|
89
|
|
|
|
|
118
|
my $tree = shift; |
1168
|
|
|
|
|
|
|
|
1169
|
89
|
|
|
|
|
130
|
for (my $i = 0; $i < @{$tree->{nodes}}; $i++) { |
|
326
|
|
|
|
|
616
|
|
1170
|
|
|
|
|
|
|
|
1171
|
237
|
|
|
|
|
304
|
my $node = $tree->{nodes}[$i]; |
1172
|
|
|
|
|
|
|
|
1173
|
237
|
100
|
100
|
|
|
816
|
if ($node->{type} eq 'COMMAND' |
|
|
100
|
100
|
|
|
|
|
1174
|
|
|
|
|
|
|
&& $node->{command} eq 'input' |
1175
|
|
|
|
|
|
|
) { |
1176
|
7
|
|
|
|
|
15
|
my $file = $node->{children}->{nodes}[0]->{content}; |
1177
|
7
|
100
|
|
|
|
24
|
next if $file =~ /pstex/; # ignore pstex images |
1178
|
|
|
|
|
|
|
|
1179
|
6
|
|
|
|
|
23
|
_debug("reading input file $file", undef); |
1180
|
|
|
|
|
|
|
|
1181
|
6
|
|
|
|
|
8
|
my $contents; |
1182
|
6
|
|
|
|
|
140
|
my $filename = fileparse($file); |
1183
|
6
|
|
|
|
|
41
|
my $has_extension = qr/\.\S+$/; |
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
# read in contents of file |
1186
|
6
|
100
|
66
|
|
|
191
|
if (-e $file && $filename =~ $has_extension) { |
|
|
50
|
|
|
|
|
|
1187
|
5
|
|
|
|
|
21
|
$contents = _readFile($file); |
1188
|
|
|
|
|
|
|
} |
1189
|
|
|
|
|
|
|
elsif ($filename !~ $has_extension) { |
1190
|
1
|
|
|
|
|
5
|
$file = "$file.tex"; |
1191
|
1
|
50
|
|
|
|
17
|
$contents = _readFile($file) if -e $file; |
1192
|
|
|
|
|
|
|
} |
1193
|
|
|
|
|
|
|
|
1194
|
|
|
|
|
|
|
# dump Psfig/TeX files, they aren't useful to us and have |
1195
|
|
|
|
|
|
|
# nonconforming syntax. Use declaration line as our heuristic. |
1196
|
|
|
|
|
|
|
# |
1197
|
6
|
100
|
66
|
|
|
43
|
if (defined $contents |
1198
|
|
|
|
|
|
|
&& $contents =~ m!^ \% \s*? Psfig/TeX \s* $!mx |
1199
|
|
|
|
|
|
|
) { |
1200
|
1
|
|
|
|
|
3
|
undef $contents; |
1201
|
1
|
|
|
|
|
244
|
carp "ignoring Psfig input `$file'"; |
1202
|
|
|
|
|
|
|
} |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
# actually do the parse of the sub-content |
1205
|
|
|
|
|
|
|
# |
1206
|
6
|
100
|
|
|
|
95
|
if (defined $contents) { |
1207
|
|
|
|
|
|
|
# parse into a tree |
1208
|
5
|
|
|
|
|
20
|
my ($subtree,) = $parser->_basicparse($contents, $parser->{PARSE_ERRORS_FATAL}); |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
# replace \input command node with subtree |
1211
|
5
|
|
|
|
|
14
|
splice @{$tree->{nodes}}, $i, 1, @{$subtree->{nodes}}; |
|
5
|
|
|
|
|
12
|
|
|
5
|
|
|
|
|
12
|
|
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
# step back |
1214
|
5
|
|
|
|
|
21
|
$i--; |
1215
|
|
|
|
|
|
|
} |
1216
|
|
|
|
|
|
|
} |
1217
|
|
|
|
|
|
|
elsif ($node->{type} eq 'COMMAND' |
1218
|
|
|
|
|
|
|
&& $node->{command} eq 'bibliography' |
1219
|
|
|
|
|
|
|
) { |
1220
|
|
|
|
|
|
|
# try to find a .bbl file |
1221
|
|
|
|
|
|
|
# |
1222
|
1
|
|
|
|
|
96
|
foreach my $file (<*.bbl>) { |
1223
|
|
|
|
|
|
|
|
1224
|
1
|
|
|
|
|
6
|
my $contents = _readFile($file); |
1225
|
|
|
|
|
|
|
|
1226
|
1
|
50
|
|
|
|
6
|
if (defined $contents) { |
1227
|
|
|
|
|
|
|
|
1228
|
1
|
|
|
|
|
9
|
my ($subtree,) = $parser->_basicparse($contents, $parser->{PARSE_ERRORS_FATAL}); |
1229
|
1
|
|
|
|
|
4
|
splice @{$tree->{nodes}}, $i, 1, @{$subtree->{nodes}}; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
4
|
|
1230
|
1
|
|
|
|
|
3
|
$i--; |
1231
|
|
|
|
|
|
|
} |
1232
|
|
|
|
|
|
|
} |
1233
|
|
|
|
|
|
|
} |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
# recur |
1236
|
236
|
100
|
|
|
|
421
|
if ($node->{children}) { |
1237
|
71
|
|
|
|
|
158
|
$parser->_addInputs($node->{children}); |
1238
|
|
|
|
|
|
|
} |
1239
|
|
|
|
|
|
|
} |
1240
|
|
|
|
|
|
|
} |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
# do pre-mapping parsing |
1243
|
|
|
|
|
|
|
# |
1244
|
|
|
|
|
|
|
sub _parseA { |
1245
|
27
|
|
|
27
|
|
49
|
my $parser = shift; |
1246
|
27
|
|
|
|
|
46
|
my $text = shift; |
1247
|
|
|
|
|
|
|
|
1248
|
27
|
|
|
|
|
100
|
my $tree = $parser->_stage1($text); |
1249
|
27
|
|
|
|
|
77
|
my $bracehash = $parser->_stage2($tree); |
1250
|
|
|
|
|
|
|
|
1251
|
27
|
|
|
|
|
90
|
$parser->_stage3($tree); |
1252
|
|
|
|
|
|
|
|
1253
|
27
|
100
|
|
|
|
114
|
$parser->_addInputs($tree) if $parser->{readinputs}; |
1254
|
|
|
|
|
|
|
|
1255
|
27
|
|
|
|
|
72
|
return ($tree, $bracehash); |
1256
|
|
|
|
|
|
|
} |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
# do post-mapping parsing (make environments) |
1259
|
|
|
|
|
|
|
# |
1260
|
|
|
|
|
|
|
sub _parseB { |
1261
|
27
|
|
|
27
|
|
48
|
my $parser = shift; |
1262
|
27
|
|
|
|
|
61
|
my $tree = shift; |
1263
|
|
|
|
|
|
|
|
1264
|
27
|
|
|
|
|
77
|
$parser->_stage4($tree); |
1265
|
|
|
|
|
|
|
|
1266
|
27
|
|
|
|
|
74
|
_debug("done with parseStage4", undef); |
1267
|
|
|
|
|
|
|
|
1268
|
27
|
|
|
|
|
89
|
$parser->_stage5($tree, 0); |
1269
|
|
|
|
|
|
|
|
1270
|
27
|
|
|
|
|
80
|
_debug("done with parseStage5", undef); |
1271
|
|
|
|
|
|
|
} |
1272
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
############################################################################### |
1274
|
|
|
|
|
|
|
# |
1275
|
|
|
|
|
|
|
# Parser "Static" Subroutines |
1276
|
|
|
|
|
|
|
# |
1277
|
|
|
|
|
|
|
############################################################################### |
1278
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
# find next unescaped char in some text |
1280
|
|
|
|
|
|
|
# |
1281
|
|
|
|
|
|
|
sub _uindex { |
1282
|
0
|
|
|
0
|
|
0
|
my $text = shift; |
1283
|
0
|
|
|
|
|
0
|
my $char = shift; |
1284
|
0
|
|
|
|
|
0
|
my $pos = shift; |
1285
|
|
|
|
|
|
|
|
1286
|
0
|
|
|
|
|
0
|
my $realbrace = 0; |
1287
|
0
|
|
|
|
|
0
|
my $idx = -1; |
1288
|
|
|
|
|
|
|
|
1289
|
|
|
|
|
|
|
# get next opening brace |
1290
|
0
|
|
|
|
|
0
|
do { |
1291
|
0
|
|
|
|
|
0
|
$realbrace = 1; |
1292
|
0
|
|
|
|
|
0
|
$idx = index $text, $char, $pos; |
1293
|
|
|
|
|
|
|
|
1294
|
0
|
0
|
|
|
|
0
|
if ($idx != -1) { |
1295
|
0
|
|
|
|
|
0
|
$pos = $idx + 1; |
1296
|
0
|
|
|
|
|
0
|
my $prevchar = substr $text, $idx - 1, 1; |
1297
|
0
|
0
|
|
|
|
0
|
if ($prevchar eq '\\') { |
1298
|
0
|
|
|
|
|
0
|
$realbrace = 0; |
1299
|
0
|
|
|
|
|
0
|
$idx = -1; |
1300
|
|
|
|
|
|
|
} |
1301
|
|
|
|
|
|
|
} |
1302
|
|
|
|
|
|
|
} while (!$realbrace); |
1303
|
|
|
|
|
|
|
|
1304
|
0
|
|
|
|
|
0
|
return $idx; |
1305
|
|
|
|
|
|
|
} |
1306
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
sub _find { |
1308
|
3006
|
|
|
3006
|
|
4827
|
my ($text, $symbol, $pos) = @_; |
1309
|
|
|
|
|
|
|
|
1310
|
3006
|
|
|
|
|
3714
|
my ($found, $index); |
1311
|
|
|
|
|
|
|
|
1312
|
|
|
|
|
|
|
# get next occurrence of the symbol |
1313
|
3006
|
|
|
|
|
3534
|
do { |
1314
|
3006
|
|
|
|
|
3554
|
$found = true; |
1315
|
3006
|
|
|
|
|
4113
|
$index = index $text, $symbol, $pos; |
1316
|
|
|
|
|
|
|
|
1317
|
3006
|
50
|
100
|
|
|
6015
|
if ($symbol eq '}' && $index - 1 >= 0 && substr($text, $index - 1, 1) eq ' ') { |
|
|
|
66
|
|
|
|
|
1318
|
|
|
|
|
|
|
#$pos = $index + 1; |
1319
|
0
|
|
|
|
|
0
|
$index = -1; |
1320
|
|
|
|
|
|
|
} |
1321
|
|
|
|
|
|
|
|
1322
|
3006
|
100
|
|
|
|
5955
|
if ($index != -1) { |
1323
|
460
|
|
|
|
|
567
|
$pos = $index + 1; |
1324
|
|
|
|
|
|
|
|
1325
|
|
|
|
|
|
|
# make sure this occurrence isn't escaped. this is imperfect. |
1326
|
460
|
100
|
|
|
|
862
|
my $prev_char = ($index - 1 >= 0) ? (substr $text, $index - 1, 1) : ''; |
1327
|
460
|
100
|
|
|
|
789
|
my $pprev_char = ($index - 2 >= 0) ? (substr $text, $index - 2, 1) : ''; |
1328
|
|
|
|
|
|
|
|
1329
|
460
|
50
|
33
|
|
|
1288
|
if ($prev_char eq '\\' && $pprev_char ne '\\') { |
1330
|
0
|
|
|
|
|
0
|
$found = false; |
1331
|
|
|
|
|
|
|
} |
1332
|
|
|
|
|
|
|
} |
1333
|
|
|
|
|
|
|
} until ($found); |
1334
|
|
|
|
|
|
|
|
1335
|
3006
|
|
|
|
|
4669
|
return $index; |
1336
|
|
|
|
|
|
|
} |
1337
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
# support function: find the next occurrence of some symbol which is |
1339
|
|
|
|
|
|
|
# not escaped. |
1340
|
|
|
|
|
|
|
# |
1341
|
|
|
|
|
|
|
sub _findsymbol { |
1342
|
2228
|
|
|
2228
|
|
3282
|
return _find(@_); |
1343
|
|
|
|
|
|
|
} |
1344
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
# support function: find the earliest next brace in some (flat) text |
1346
|
|
|
|
|
|
|
# |
1347
|
|
|
|
|
|
|
sub _findbrace { |
1348
|
389
|
|
|
389
|
|
649
|
my ($text, $pos) = @_; |
1349
|
|
|
|
|
|
|
|
1350
|
389
|
|
|
|
|
650
|
my $index_o = _find($text, '{', $pos); |
1351
|
389
|
|
|
|
|
615
|
my $index_c = _find($text, '}', $pos); |
1352
|
|
|
|
|
|
|
|
1353
|
|
|
|
|
|
|
# handle all find cases |
1354
|
389
|
100
|
100
|
|
|
1865
|
if ($index_o == -1 && $index_c == -1) { |
|
|
100
|
100
|
|
|
|
|
|
|
50
|
33
|
|
|
|
|
|
|
|
66
|
|
|
|
|
1355
|
145
|
|
|
|
|
340
|
return (-1, ''); |
1356
|
|
|
|
|
|
|
} |
1357
|
|
|
|
|
|
|
elsif ($index_c == -1 || ($index_o != -1 && $index_o < $index_c)) { |
1358
|
122
|
|
|
|
|
311
|
return ($index_o, '{'); |
1359
|
|
|
|
|
|
|
} |
1360
|
|
|
|
|
|
|
elsif ($index_o == -1 || $index_c < $index_o) { |
1361
|
122
|
|
|
|
|
406
|
return ($index_c, '}'); |
1362
|
|
|
|
|
|
|
} |
1363
|
|
|
|
|
|
|
} |
1364
|
|
|
|
|
|
|
|
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
# skip "blank nodes" in a tree, starting at some position. will finish |
1367
|
|
|
|
|
|
|
# at the first non-blank node. (ie, not a comment or whitespace TEXT node. |
1368
|
|
|
|
|
|
|
# |
1369
|
|
|
|
|
|
|
sub _skipBlankNodes { |
1370
|
2
|
|
|
2
|
|
4
|
my ($tree, $i) = @_; |
1371
|
|
|
|
|
|
|
|
1372
|
2
|
|
|
|
|
4
|
my $node = $tree->{nodes}[$$i]; |
1373
|
|
|
|
|
|
|
|
1374
|
2
|
|
66
|
|
|
18
|
while ($node->{type} eq 'COMMENT' |
|
|
|
66
|
|
|
|
|
1375
|
|
|
|
|
|
|
|| ($node->{type} eq 'TEXT' |
1376
|
|
|
|
|
|
|
&& $node->{content} =~ /^\s*$/s) |
1377
|
|
|
|
|
|
|
) { |
1378
|
1
|
|
|
|
|
5
|
$node = $tree->{nodes}[++$$i]; |
1379
|
|
|
|
|
|
|
} |
1380
|
|
|
|
|
|
|
} |
1381
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
# is the passed-in node a valid parameter node? for this to be true, it must |
1383
|
|
|
|
|
|
|
# either be a GROUP or a position = inner command. |
1384
|
|
|
|
|
|
|
# |
1385
|
|
|
|
|
|
|
sub _validParamNode { |
1386
|
2
|
|
|
2
|
|
4
|
my ($node) = @_; |
1387
|
|
|
|
|
|
|
|
1388
|
2
|
50
|
0
|
|
|
7
|
if ($node->{type} eq 'GROUP' |
|
|
|
33
|
|
|
|
|
1389
|
|
|
|
|
|
|
|| ($node->{type} eq 'COMMAND' && $node->{position} eq 'inner')) { |
1390
|
2
|
|
|
|
|
5
|
return true; |
1391
|
|
|
|
|
|
|
} |
1392
|
0
|
|
|
|
|
0
|
return false; |
1393
|
|
|
|
|
|
|
} |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
# duplicate a valid param node. This means for a group, copy the child tree. |
1396
|
|
|
|
|
|
|
# for a command, make a new tree with just the command node and its child tree. |
1397
|
|
|
|
|
|
|
# |
1398
|
|
|
|
|
|
|
sub _duplicateParam { |
1399
|
2
|
|
|
2
|
|
4
|
my $parser = shift; |
1400
|
2
|
|
|
|
|
3
|
my $node = shift; |
1401
|
|
|
|
|
|
|
|
1402
|
2
|
50
|
|
|
|
5
|
if ($node->{type} eq 'GROUP') { |
|
|
0
|
|
|
|
|
|
1403
|
2
|
|
|
|
|
16
|
return $node->{children}->copy(); |
1404
|
|
|
|
|
|
|
} |
1405
|
|
|
|
|
|
|
elsif ($node->{type} eq 'COMMAND') { |
1406
|
|
|
|
|
|
|
|
1407
|
0
|
|
|
|
|
0
|
my $subtree = $node->{children}->copy(); # copy child subtree |
1408
|
0
|
|
|
|
|
0
|
my $nodecopy = $node->copy(); # make a new node with old data |
1409
|
0
|
|
|
|
|
0
|
$nodecopy->{children} = $subtree; # set the child pointer to new subtree |
1410
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
# return a new tree with the new node (subtree) as its only element |
1412
|
0
|
|
|
|
|
0
|
return LaTeX::TOM::Tree->_new([$nodecopy]); |
1413
|
|
|
|
|
|
|
} |
1414
|
|
|
|
|
|
|
|
1415
|
0
|
|
|
|
|
0
|
return undef; |
1416
|
|
|
|
|
|
|
} |
1417
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
sub _getMapping { |
1419
|
2
|
|
|
2
|
|
6
|
my ($type, $tree, $i) = @_; |
1420
|
|
|
|
|
|
|
|
1421
|
2
|
|
|
|
|
5
|
my $node = $tree->{nodes}[$$i]; |
1422
|
|
|
|
|
|
|
|
1423
|
2
|
50
|
33
|
|
|
12
|
if ($node->{type} ne 'COMMAND' |
|
|
|
33
|
|
|
|
|
1424
|
|
|
|
|
|
|
|| ($node->{command} ne "new$type" |
1425
|
|
|
|
|
|
|
&& $node->{command} ne "renew$type") |
1426
|
|
|
|
|
|
|
) { |
1427
|
0
|
|
|
|
|
0
|
return (); |
1428
|
|
|
|
|
|
|
} |
1429
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
# figure out command (first child, text node) |
1431
|
2
|
|
|
|
|
5
|
my $command = $node->{children}->{nodes}[0]->{content}; |
1432
|
2
|
100
|
|
|
|
10
|
if ($command =~ /^\s* \\(\S+) \s*$/x) { |
1433
|
1
|
|
|
|
|
3
|
$command = $1; |
1434
|
|
|
|
|
|
|
} |
1435
|
|
|
|
|
|
|
|
1436
|
2
|
|
|
|
|
4
|
$node = $tree->{nodes}[++$$i]; |
1437
|
|
|
|
|
|
|
|
1438
|
|
|
|
|
|
|
# figure out number of params |
1439
|
2
|
|
|
|
|
6
|
my $nparams = 0; |
1440
|
2
|
50
|
|
|
|
5
|
if ($node->{type} eq 'TEXT') { |
1441
|
2
|
|
|
|
|
4
|
my $text = $node->{content}; |
1442
|
|
|
|
|
|
|
|
1443
|
2
|
100
|
|
|
|
11
|
if ($text =~ /^\s* \[\s* ([0-9]+) \s*\] \s*$/x) { |
1444
|
1
|
|
|
|
|
2
|
$nparams = $1; |
1445
|
|
|
|
|
|
|
} |
1446
|
|
|
|
|
|
|
|
1447
|
2
|
|
|
|
|
5
|
$$i++; |
1448
|
|
|
|
|
|
|
} |
1449
|
|
|
|
|
|
|
|
1450
|
2
|
|
|
|
|
19
|
return ($command, $nparams); |
1451
|
|
|
|
|
|
|
} |
1452
|
|
|
|
|
|
|
|
1453
|
|
|
|
|
|
|
# make a mapping from a newenvironment fragment |
1454
|
|
|
|
|
|
|
# |
1455
|
|
|
|
|
|
|
# newenvironments have the following syntax: |
1456
|
|
|
|
|
|
|
# |
1457
|
|
|
|
|
|
|
# \newenvironment{name}[nparams]?{beginTeX}{endTeX} |
1458
|
|
|
|
|
|
|
# |
1459
|
|
|
|
|
|
|
sub _makeEnvMapping { |
1460
|
1
|
|
|
1
|
|
2
|
my $parser = shift; |
1461
|
1
|
|
|
|
|
11
|
my ($tree, $index) = @_; |
1462
|
|
|
|
|
|
|
|
1463
|
1
|
|
|
|
|
3
|
my $i = $index; |
1464
|
|
|
|
|
|
|
|
1465
|
1
|
50
|
|
|
|
4
|
my ($command, $nparams) = _getMapping('environment', $tree, \$i) or return undef; |
1466
|
|
|
|
|
|
|
|
1467
|
|
|
|
|
|
|
# default templates-- just repeat the declarations |
1468
|
|
|
|
|
|
|
# |
1469
|
1
|
|
|
|
|
8
|
my ($btemplate) = $parser->_basicparse("\\begin{$command}", 2, 0); |
1470
|
1
|
|
|
|
|
7
|
my ($etemplate) = $parser->_basicparse("\\end{$command}", 2, 0); |
1471
|
|
|
|
|
|
|
|
1472
|
1
|
|
|
|
|
8
|
my $end_pos = $i; |
1473
|
|
|
|
|
|
|
|
1474
|
|
|
|
|
|
|
# get two group subtrees... one for the begin and one for the end |
1475
|
|
|
|
|
|
|
# templates. we only ignore whitespace TEXT nodes and comments |
1476
|
|
|
|
|
|
|
# |
1477
|
1
|
|
|
|
|
6
|
_skipBlankNodes($tree, \$i); |
1478
|
1
|
|
|
|
|
3
|
my $node = $tree->{nodes}[$i]; |
1479
|
|
|
|
|
|
|
|
1480
|
1
|
50
|
|
|
|
6
|
if (_validParamNode($node)) { |
1481
|
1
|
|
|
|
|
5
|
$btemplate = $parser->_duplicateParam($node); |
1482
|
|
|
|
|
|
|
|
1483
|
1
|
|
|
|
|
5
|
$i++; |
1484
|
1
|
|
|
|
|
4
|
_skipBlankNodes($tree, \$i); |
1485
|
1
|
|
|
|
|
4
|
$node = $tree->{nodes}[$i]; |
1486
|
|
|
|
|
|
|
|
1487
|
1
|
50
|
|
|
|
3
|
if (_validParamNode($node)) { |
1488
|
1
|
|
|
|
|
5
|
$etemplate = $parser->_duplicateParam($node); |
1489
|
1
|
|
|
|
|
3
|
$end_pos = $i; |
1490
|
|
|
|
|
|
|
} |
1491
|
|
|
|
|
|
|
} |
1492
|
|
|
|
|
|
|
|
1493
|
|
|
|
|
|
|
# build and return the mapping hash |
1494
|
|
|
|
|
|
|
# |
1495
|
|
|
|
|
|
|
return { |
1496
|
1
|
|
|
|
|
15
|
type => 'environment', |
1497
|
|
|
|
|
|
|
name => $command, |
1498
|
|
|
|
|
|
|
nparams => $nparams, |
1499
|
|
|
|
|
|
|
btemplate => $btemplate, # begin template |
1500
|
|
|
|
|
|
|
etemplate => $etemplate, # end template |
1501
|
|
|
|
|
|
|
skip => $end_pos - $index, |
1502
|
|
|
|
|
|
|
}; |
1503
|
|
|
|
|
|
|
} |
1504
|
|
|
|
|
|
|
|
1505
|
|
|
|
|
|
|
# make a mapping from a newcommand fragment |
1506
|
|
|
|
|
|
|
# takes tree pointer and index of command node |
1507
|
|
|
|
|
|
|
# |
1508
|
|
|
|
|
|
|
# newcommands have the following syntax: |
1509
|
|
|
|
|
|
|
# |
1510
|
|
|
|
|
|
|
# \newcommand{\name}[nparams]?{anyTeX} |
1511
|
|
|
|
|
|
|
# |
1512
|
|
|
|
|
|
|
sub _makeMapping { |
1513
|
1
|
|
|
1
|
|
5
|
my ($tree, $index) = @_; |
1514
|
|
|
|
|
|
|
|
1515
|
1
|
|
|
|
|
2
|
my $i = $index; |
1516
|
|
|
|
|
|
|
|
1517
|
1
|
50
|
|
|
|
4
|
my ($command, $nparams) = _getMapping('command', $tree, \$i) or return undef; |
1518
|
|
|
|
|
|
|
|
1519
|
|
|
|
|
|
|
# grab subtree template (array ref) |
1520
|
|
|
|
|
|
|
# |
1521
|
1
|
|
|
|
|
3
|
my $node = $tree->{nodes}[$i]; |
1522
|
1
|
|
|
|
|
1
|
my $template; |
1523
|
|
|
|
|
|
|
|
1524
|
1
|
50
|
|
|
|
3
|
if ($node->{type} eq 'GROUP') { |
1525
|
1
|
|
|
|
|
6
|
$template = $node->{children}->copy(); |
1526
|
|
|
|
|
|
|
} |
1527
|
|
|
|
|
|
|
else { |
1528
|
0
|
|
|
|
|
0
|
return undef; |
1529
|
|
|
|
|
|
|
} |
1530
|
|
|
|
|
|
|
|
1531
|
|
|
|
|
|
|
# build and return the mapping hash |
1532
|
|
|
|
|
|
|
# |
1533
|
|
|
|
|
|
|
return { |
1534
|
1
|
|
|
|
|
7
|
type => 'command', |
1535
|
|
|
|
|
|
|
name => $command, |
1536
|
|
|
|
|
|
|
nparams => $nparams, |
1537
|
|
|
|
|
|
|
template => $template, |
1538
|
|
|
|
|
|
|
skip => $i - $index, |
1539
|
|
|
|
|
|
|
}; |
1540
|
|
|
|
|
|
|
} |
1541
|
|
|
|
|
|
|
|
1542
|
|
|
|
|
|
|
# this sub is the main entry point for the sub that actually takes a set of |
1543
|
|
|
|
|
|
|
# parameter trees and inserts them into a template tree. the return result, |
1544
|
|
|
|
|
|
|
# newly allocated, should be plopped back into the original tree where the |
1545
|
|
|
|
|
|
|
# parameters (along with the initial command invocation) |
1546
|
|
|
|
|
|
|
# |
1547
|
|
|
|
|
|
|
sub _applyParamsToTemplate { |
1548
|
2
|
|
|
2
|
|
5
|
my $template = shift; |
1549
|
2
|
|
|
|
|
4
|
my @params = @_; |
1550
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
# have to copy the template to a freshly allocated tree |
1552
|
|
|
|
|
|
|
# |
1553
|
2
|
|
|
|
|
6
|
my $applied = $template->copy(); |
1554
|
|
|
|
|
|
|
|
1555
|
|
|
|
|
|
|
# now recursively apply the params. |
1556
|
|
|
|
|
|
|
# |
1557
|
2
|
|
|
|
|
80
|
_applyParamsToTemplate_r($applied, @params); |
1558
|
|
|
|
|
|
|
|
1559
|
2
|
|
|
|
|
6
|
return $applied; |
1560
|
|
|
|
|
|
|
} |
1561
|
|
|
|
|
|
|
|
1562
|
|
|
|
|
|
|
# recursive helper for above |
1563
|
|
|
|
|
|
|
# |
1564
|
|
|
|
|
|
|
sub _applyParamsToTemplate_r { |
1565
|
4
|
|
|
4
|
|
7
|
my $template = shift; |
1566
|
4
|
|
|
|
|
9
|
my @params = @_; |
1567
|
|
|
|
|
|
|
|
1568
|
4
|
|
|
|
|
5
|
for (my $i = 0; $i < @{$template->{nodes}}; $i++) { |
|
13
|
|
|
|
|
32
|
|
1569
|
|
|
|
|
|
|
|
1570
|
9
|
|
|
|
|
14
|
my $node = $template->{nodes}[$i]; |
1571
|
|
|
|
|
|
|
|
1572
|
9
|
100
|
|
|
|
26
|
if ($node->{type} eq 'TEXT') { |
|
|
50
|
|
|
|
|
|
1573
|
|
|
|
|
|
|
|
1574
|
7
|
|
|
|
|
11
|
my $text = $node->{content}; |
1575
|
|
|
|
|
|
|
|
1576
|
|
|
|
|
|
|
# find occurrences of the parameter flags |
1577
|
|
|
|
|
|
|
# |
1578
|
7
|
100
|
|
|
|
20
|
if ($text =~ /(#([0-9]+))/) { |
1579
|
|
|
|
|
|
|
|
1580
|
1
|
|
|
|
|
3
|
my $all = $1; |
1581
|
1
|
|
|
|
|
3
|
my $num = $2; |
1582
|
|
|
|
|
|
|
|
1583
|
|
|
|
|
|
|
# get the index of the flag we just found |
1584
|
|
|
|
|
|
|
# |
1585
|
1
|
|
|
|
|
3
|
my $idx = index $text, $all, 0; |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
# split the node on the location of the flag |
1588
|
|
|
|
|
|
|
# |
1589
|
1
|
|
|
|
|
5
|
my ($leftnode, $rightnode) = $node->split($idx, $idx + length($all) - 1); |
1590
|
|
|
|
|
|
|
|
1591
|
|
|
|
|
|
|
# make a copy of the param we want |
1592
|
|
|
|
|
|
|
# |
1593
|
1
|
|
|
|
|
16
|
my $param = $params[$num - 1]->copy(); |
1594
|
|
|
|
|
|
|
|
1595
|
|
|
|
|
|
|
# splice the new text nodes, along with the parameter subtree, into |
1596
|
|
|
|
|
|
|
# the old location |
1597
|
|
|
|
|
|
|
# |
1598
|
1
|
|
|
|
|
3
|
splice @{$template->{nodes}}, $i, 1, $leftnode, @{$param->{nodes}}, $rightnode; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
3
|
|
1599
|
|
|
|
|
|
|
|
1600
|
|
|
|
|
|
|
# skip forward to where $rightnode is in $template on next iteration |
1601
|
|
|
|
|
|
|
# |
1602
|
1
|
|
|
|
|
2
|
$i += scalar @{$param->{nodes}}; |
|
1
|
|
|
|
|
5
|
|
1603
|
|
|
|
|
|
|
} |
1604
|
|
|
|
|
|
|
} |
1605
|
|
|
|
|
|
|
|
1606
|
|
|
|
|
|
|
# recur |
1607
|
|
|
|
|
|
|
elsif (defined $node->{children}) { |
1608
|
|
|
|
|
|
|
|
1609
|
2
|
|
|
|
|
9
|
_applyParamsToTemplate_r($node->{children}, @params); |
1610
|
|
|
|
|
|
|
} |
1611
|
|
|
|
|
|
|
} |
1612
|
|
|
|
|
|
|
} |
1613
|
|
|
|
|
|
|
|
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
# This sub takes a chunk of the document text between two points and makes |
1616
|
|
|
|
|
|
|
# it into a list of TEXT nodes and COMMENT nodes, as we would expect from |
1617
|
|
|
|
|
|
|
# '%' prefixed LaTeX comment lines |
1618
|
|
|
|
|
|
|
# |
1619
|
|
|
|
|
|
|
sub _getTextAndCommentNodes { |
1620
|
27
|
|
|
27
|
|
65
|
my ($text, $begins, $ends) = @_; |
1621
|
|
|
|
|
|
|
|
1622
|
27
|
|
|
|
|
80
|
my $node_text = substr $text, $begins, $ends - $begins; |
1623
|
|
|
|
|
|
|
|
1624
|
27
|
|
|
|
|
117
|
_debug("getTextAndCommentNodes: looking at [$node_text]", undef); |
1625
|
|
|
|
|
|
|
|
1626
|
|
|
|
|
|
|
my $make_node = sub { |
1627
|
26
|
|
|
26
|
|
66
|
my ($mode_type, $begins, $start_pos, $output) = @_; |
1628
|
|
|
|
|
|
|
|
1629
|
26
|
|
|
|
|
273
|
return LaTeX::TOM::Node->_new({ |
1630
|
|
|
|
|
|
|
type => uc $mode_type, |
1631
|
|
|
|
|
|
|
start => $begins + $start_pos, |
1632
|
|
|
|
|
|
|
end => $begins + $start_pos + length($output) - 1, |
1633
|
|
|
|
|
|
|
content => $output, |
1634
|
|
|
|
|
|
|
}); |
1635
|
27
|
|
|
|
|
135
|
}; |
1636
|
|
|
|
|
|
|
|
1637
|
27
|
|
|
|
|
108
|
my @lines = split (/( |
1638
|
|
|
|
|
|
|
(?:\s* # whitespace |
1639
|
|
|
|
|
|
|
(?
|
1640
|
|
|
|
|
|
|
\%[^\n]* # comment |
1641
|
|
|
|
|
|
|
\n)+ # newline |
1642
|
|
|
|
|
|
|
)/mx, $node_text); |
1643
|
|
|
|
|
|
|
|
1644
|
27
|
|
|
|
|
52
|
my @nodes; |
1645
|
|
|
|
|
|
|
|
1646
|
27
|
|
|
|
|
49
|
my $start_pos = 0; |
1647
|
27
|
|
|
|
|
48
|
my $output; |
1648
|
|
|
|
|
|
|
my $mode_type; |
1649
|
27
|
|
|
|
|
42
|
my $first = true; |
1650
|
|
|
|
|
|
|
|
1651
|
27
|
|
|
|
|
64
|
foreach my $line (@lines) { |
1652
|
|
|
|
|
|
|
|
1653
|
26
|
50
|
33
|
|
|
123
|
my $line_type = ( |
1654
|
|
|
|
|
|
|
$line =~ /^\s*\%/ |
1655
|
|
|
|
|
|
|
&& $node_text !~ / |
1656
|
|
|
|
|
|
|
\\begin\{verbatim\} |
1657
|
|
|
|
|
|
|
.* \Q$line\E .* |
1658
|
|
|
|
|
|
|
\\end\{verbatim\} |
1659
|
|
|
|
|
|
|
/sx |
1660
|
|
|
|
|
|
|
) ? 'comment' : 'text'; |
1661
|
|
|
|
|
|
|
|
1662
|
|
|
|
|
|
|
# if type stays the same, add to output and do nothing |
1663
|
26
|
50
|
33
|
|
|
88
|
if ($first || $line_type eq $mode_type) { |
1664
|
|
|
|
|
|
|
|
1665
|
26
|
|
|
|
|
58
|
$output .= $line; |
1666
|
|
|
|
|
|
|
|
1667
|
|
|
|
|
|
|
# handle turning off initialization stuff |
1668
|
26
|
|
50
|
|
|
133
|
$first &&= false; |
1669
|
26
|
|
33
|
|
|
123
|
$mode_type ||= $line_type; |
1670
|
|
|
|
|
|
|
} |
1671
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
# if type changes, make new node from current chunk, change mode type |
1673
|
|
|
|
|
|
|
# and start a new chunk |
1674
|
|
|
|
|
|
|
else { |
1675
|
0
|
|
|
|
|
0
|
push @nodes, $make_node->($mode_type, $begins, $start_pos, $output); |
1676
|
|
|
|
|
|
|
|
1677
|
0
|
|
|
|
|
0
|
$start_pos += length($output); # update start position |
1678
|
0
|
|
|
|
|
0
|
$output = $line; |
1679
|
|
|
|
|
|
|
|
1680
|
0
|
|
|
|
|
0
|
$mode_type = $line_type; |
1681
|
|
|
|
|
|
|
} |
1682
|
|
|
|
|
|
|
} |
1683
|
|
|
|
|
|
|
|
1684
|
27
|
100
|
|
|
|
126
|
push @nodes, $make_node->($mode_type, $begins, $start_pos, $output) if defined $output; |
1685
|
|
|
|
|
|
|
|
1686
|
27
|
|
|
|
|
157
|
return @nodes; |
1687
|
|
|
|
|
|
|
} |
1688
|
|
|
|
|
|
|
|
1689
|
|
|
|
|
|
|
# Read in the contents of a text file on disk. Return in string scalar. |
1690
|
|
|
|
|
|
|
# |
1691
|
|
|
|
|
|
|
sub _readFile { |
1692
|
16
|
|
|
16
|
|
45
|
my ($file, $raise_error) = @_; |
1693
|
|
|
|
|
|
|
|
1694
|
16
|
|
100
|
|
|
76
|
$raise_error ||= false; |
1695
|
|
|
|
|
|
|
|
1696
|
16
|
|
|
|
|
658
|
my $opened = open(my $fh, '<', $file); |
1697
|
|
|
|
|
|
|
|
1698
|
16
|
50
|
|
|
|
83
|
unless ($opened) { |
1699
|
0
|
0
|
|
|
|
0
|
croak "Cannot open `$file': $!" if $raise_error; |
1700
|
0
|
|
|
|
|
0
|
return undef; |
1701
|
|
|
|
|
|
|
} |
1702
|
|
|
|
|
|
|
|
1703
|
16
|
|
|
|
|
30
|
my $contents = do { local $/; <$fh> }; |
|
16
|
|
|
|
|
73
|
|
|
16
|
|
|
|
|
661
|
|
1704
|
16
|
|
|
|
|
196
|
close($fh); |
1705
|
|
|
|
|
|
|
|
1706
|
16
|
|
|
|
|
101
|
return $contents; |
1707
|
|
|
|
|
|
|
} |
1708
|
|
|
|
|
|
|
|
1709
|
|
|
|
|
|
|
sub _debug { |
1710
|
920
|
|
|
920
|
|
1564
|
my ($message, $code) = @_; |
1711
|
|
|
|
|
|
|
|
1712
|
920
|
|
|
|
|
1204
|
my $DEBUG = $LaTeX::TOM::DEBUG; |
1713
|
|
|
|
|
|
|
|
1714
|
920
|
50
|
33
|
|
|
2059
|
return unless $DEBUG >= 1 && $DEBUG <= 2; |
1715
|
|
|
|
|
|
|
|
1716
|
0
|
|
|
|
|
|
my ($filename, $line) = (caller)[1,2]; |
1717
|
0
|
|
|
|
|
|
my $caller = join ':', (fileparse($filename))[0], $line; |
1718
|
|
|
|
|
|
|
|
1719
|
0
|
0
|
0
|
|
|
|
warn "$caller: $message\n" if $DEBUG >= 1 && defined $message; |
1720
|
0
|
0
|
0
|
|
|
|
$code->() if $DEBUG == 2 && defined $code; |
1721
|
|
|
|
|
|
|
} |
1722
|
|
|
|
|
|
|
|
1723
|
|
|
|
|
|
|
1; |