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