1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
# /=====================================================================\ #
# | LaTeXML::Core::Parameter | #
# | Representation of a single Parameter for Control Sequences | #
# |=====================================================================| #
# | Part of LaTeXML: | #
# | Public domain software, produced as part of work done by the | #
# | United States Government & not subject to copyright in the US. | #
# |---------------------------------------------------------------------| #
# | Bruce Miller <bruce.miller@nist.gov> #_# | #
# | http://dlmf.nist.gov/LaTeXML/ (o o) | #
# \=========================================================ooo==U==ooo=/ #
package LaTeXML::Core::Parameter;
use strict;
use warnings;
use LaTeXML::Global;
use LaTeXML::Common::Object;
use LaTeXML::Common::Error;
use LaTeXML::Core::Token;
use LaTeXML::Core::Tokens;
use base qw(LaTeXML::Common::Object);
# sub new {
# my ($class, $spec, %options) = @_;
# return bless { spec => $spec, %options }, $class; }
# Create a parameter reading object for a specific type.
# If either a declared entry or a function Read<Type> accessible from LaTeXML::Package::Pool
# is defined.
sub new {
my ($class, $type, $spec, %options) = @_;
my $descriptor = $STATE->lookupMapping('PARAMETER_TYPES', $type);
if (!defined $descriptor) {
if ($type =~ /^Optional(.+)$/) {
my $basetype = $1;
if ($descriptor = $STATE->lookupMapping('PARAMETER_TYPES', $basetype)) { }
elsif (my $reader = checkReaderFunction("Read$type") || checkReaderFunction("Read$basetype")) {
$descriptor = { reader => $reader }; }
$descriptor = { %$descriptor, optional => 1 } if $descriptor; }
elsif ($type =~ /^Skip(.+)$/) {
my $basetype = $1;
if ($descriptor = $STATE->lookupMapping('PARAMETER_TYPES', $basetype)) { }
elsif (my $reader = checkReaderFunction($type) || checkReaderFunction("Read$basetype")) {
$descriptor = { reader => $reader }; }
$descriptor = { %$descriptor, novalue => 1, optional => 1 } if $descriptor; }
else {
my $reader = checkReaderFunction("Read$type");
$descriptor = { reader => $reader } if $reader; } }
Fatal('misdefined', $type || 'no_type', undef, "Unrecognized parameter type in \"$spec\"") unless $descriptor;
# Convert semiverbatim to list of extra SPECIALS.
my %data = (%{$descriptor}, %options);
$data{semiverbatim} = [] if $data{semiverbatim} && (ref $data{semiverbatim} ne 'ARRAY');
return bless { spec => $spec, type => $type, %data }, $class; }
# Check whether a reader function is accessible within LaTeXML::Package::Pool
sub checkReaderFunction {
my ($function) = @_;
if (defined $LaTeXML::Package::Pool::{$function}) {
local *reader = $LaTeXML::Package::Pool::{$function};
if (defined &reader) {
return \&reader; } } }
sub stringify {
my ($self) = @_;
return $$self{spec}; }
sub setupCatcodes {
my ($self) = @_;
if ($$self{semiverbatim}) {
$STATE->beginSemiverbatim(@{ $$self{semiverbatim} }); }
return; }
sub revertCatcodes {
my ($self) = @_;
if ($$self{semiverbatim}) {
$STATE->endSemiverbatim(); }
return; }
sub read {
my ($self, $gullet, $fordefn) = @_;
# For semiverbatim, I had messed with catcodes, but there are cases
# (eg. \caption(...\label{badchars}}) where you really need to
# cleanup after the fact!
# Hmmm, seem to still need it...
if ($$self{semiverbatim}) { # Open coded setupCatcodes
$STATE->beginSemiverbatim(@{ $$self{semiverbatim} }); }
no warnings 'recursion';
my $value = &{ $$self{reader} }($gullet, @{ $$self{extra} || [] });
$value = $value->neutralize(@{ $$self{semiverbatim} }) if $$self{semiverbatim} && (ref $value)
&& $value->can('neutralize');
if ($$self{semiverbatim}) { # Open coded revertCatcodes
$STATE->endSemiverbatim(); }
if ((!defined $value) && !$$self{optional}) {
Error('expected', $self, $gullet,
"Missing argument " . Stringify($self) . " for " . Stringify($fordefn),
"Ended at " . ToString($gullet->getLocator));
$value = T_OTHER('missing'); }
elsif ($value && $$self{optional} && $$self{type} =~ /^OptionalMatch/) {
# experiment: skip spaces after a successful OptionalMatch read
$gullet->skipSpaces; }
return $value; }
# This is needed by structured parameter types like KeyVals
# where the argument may already have been tokenized before the KeyVals
# (and the parameter types for the keys) had a chance to properly parse.
# Yuck!
sub reparse {
my ($self, $gullet, $tokens) = @_;
# Needs neutralization, since the keyvals may have been tokenized already???
# perhaps a better test would involve whether $tokens is, in fact, Tokens?
if (($$self{type} eq 'Plain') || $$self{undigested}) { # Gack!
return $tokens; }
elsif ($$self{semiverbatim}) { # Needs neutralization
return $tokens->neutralize(@{ $$self{semiverbatim} }); } # but maybe specific to catcodes
else {
return $gullet->readingFromMouth(LaTeXML::Core::Mouth->new(), sub { # start with empty mouth
my ($gulletx) = @_;
my @tokens = $tokens->unlist;
if (@tokens # Strip outer braces from dimensions & friends
&& ($$self{type} =~ /^(?:Number|Dimension|Glue|MuDimension|MuGlue)$/)
&& ($tokens[0]->getCatcode == CC_BEGIN) && ($tokens[-1]->getCatcode == CC_END)) {
shift(@tokens); pop(@tokens); }
$gulletx->unread(@tokens); # but put back tokens to be read
my $value = $self->read($gulletx);
$gulletx->skipSpaces;
return $value; }); } }
sub digest {
no warnings 'recursion';
my ($self, $stomach, $value, $fordefn) = @_;
# If semiverbatim, Expand (before digest), so tokens can be neutralized; BLECH!!!!
if ($$self{semiverbatim}) {
$STATE->beginSemiverbatim(@{ $$self{semiverbatim} });
if ((ref $value eq 'LaTeXML::Core::Token') || (ref $value eq 'LaTeXML::Core::Tokens')) {
$stomach->getGullet->readingFromMouth(LaTeXML::Core::Mouth->new(), sub {
my ($igullet) = @_;
$igullet->unread($value);
my @tokens = ();
while (defined(my $token = $igullet->getPendingComment || $igullet->readXToken(1))) {
push(@tokens, $token); }
$value = Tokens(@tokens);
$value = $value->neutralize; }); } }
if (my $pre = $$self{beforeDigest}) { # Done for effect only.
&$pre($stomach); } # maybe pass extras?
$value = $value->beDigested($stomach) if (ref $value) && !$$self{undigested};
if (my $post = $$self{afterDigest}) { # Done for effect only.
&$post($stomach); } # maybe pass extras?
$STATE->endSemiverbatim() if $$self{semiverbatim}; # Corner case?
return $value; }
sub revert {
my ($self, $value) = @_;
if (my $reverter = $$self{reversion}) {
return &$reverter($value, @{ $$self{extra} || [] }); }
else {
return Revert($value); } }
#======================================================================
1;
__END__
=pod
=head1 NAME
C<LaTeXML::Core::Parameter> - a formal parameter
=head1 DESCRIPTION
Provides a representation for a single formal parameter of L<LaTeXML::Core::Definition>s:
It extends L<LaTeXML::Common::Object>.
=head1 SEE ALSO
L<LaTeXML::Core::Parameters>.
=head1 AUTHOR
Bruce Miller <bruce.miller@nist.gov>
=head1 COPYRIGHT
Public domain software, produced as part of work done by the
United States Government & not subject to copyright in the US.
=cut
|