# -*- mode: Perl -*-
# /=====================================================================\ #
# | calc.sty | #
# | Implementation for LaTeXML | #
# |=====================================================================| #
# | 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::Package::Pool;
use strict;
use warnings;
use LaTeXML::Package;
# \setcounter{<ctr>}{<integer expression>}
DefPrimitive('\setcounter{}{}', sub {
my ($stomach, $ctr, $arg) = @_;
SetCounter($ctr, readExpression($stomach->getGullet, 'Number', $arg));
return; });
# \addtocounter{<ctr>}{<integer expression>}
DefPrimitive('\addtocounter{}{}', sub {
my ($stomach, $ctr, $arg) = @_;
AddToCounter($ctr, readExpression($stomach->getGullet, 'Number', $arg));
return; });
DefPrimitive('\setlength{Variable}{}', sub {
my ($stomach, $var, $arg) = @_;
my ($defn, @args) = @$var;
$defn->setValue(readExpression($stomach->getGullet, 'Glue', $arg), @args); });
DefPrimitive('\addtolength{Variable}{}', sub {
my ($stomach, $var, $arg) = @_;
my ($defn, @args) = @$var;
$defn->setValue($defn->valueOf(@args)->add(readExpression($stomach->getGullet, 'Glue', $arg)),
@args); });
DefPrimitive('\settowidth{Variable}{}', sub {
my ($stomach, $var, $arg) = @_;
my ($defn, @args) = @$var;
my $box = Digest($arg);
$defn->setValue($box->getWidth, @args); });
DefPrimitive('\settoheight{Variable}{}', sub {
my ($stomach, $var, $arg) = @_;
my ($defn, @args) = @$var;
my $box = Digest($arg);
$defn->setValue($box->getHeight, @args); });
DefPrimitive('\settodepth{Variable}{}', sub {
my ($stomach, $var, $arg) = @_;
my ($defn, @args) = @$var;
my $box = Digest($arg);
$defn->setValue($box->getDepth, @args); });
DefPrimitive('\settototalheight{Variable}{}', sub {
my ($stomach, $var, $arg) = @_;
my ($defn, @args) = @$var;
my $box = Digest($arg);
$defn->setValue($box->getHeight + $box->getDepth, @args); });
#======================================================================
# Grammar as specified in calc.sty
# type = integer | dimen | glue
# <type expression> -> <type term>
# | <type expression><plus or minus><type term>
# <type term> -> <type term><type scan stop>
# | <type factor>
# | <type term><multiply or divide><integer>
# | <type term><multiply or divide><real number>
# | <type term><multiply or divide><max or min integer>
# <type scan stop> -> <empty>
# | <optional space>
# | \relax
# <type factor> -> <type>
# | <text dimen factor>
# | <max or min type>
# | ( <type expression> )
# <integer> -> <number>
# <max or min type> -> <max or min command>{<type expression>}{<type expression>}
# <max or min command> -> \maxof | \minof
# <text dimen factor> -> <text dimen command>{<text>}
# <text dimen command> -> \widthof | \heightof | \depthof | \totalheightof
# <multiply or divide> -> * | /
# <real number> -> \ratio{<dimen expression>}{<dimen expression>}
# | \real{<optional signs><decimal constant>}
# <plus or minus> -> + | -
# <decimal constant> -> . | , | <digit><decimal constant> |<decimal constant><digit>
# <digit> -> 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
# <optional signs> -> <optional spaces>
# | <optional signs><plus or minus><optional spaces>
#======================================================================
# LaTeXML implementation, not exactly the same grammar as above
# (but I'm not convinced that grammar is what the latex version does)
# Here, type = Number, Glue
sub readExpression {
my ($ogullet, $type, $tokens) = @_;
return $ogullet->readingFromMouth(LaTeXML::Core::Mouth->new(), sub {
my ($gullet) = @_;
$gullet->unread($tokens); # Really so weird?
my $term = readTerm($gullet, $type);
while (my $op = $gullet->readKeyword('+', '-')) {
my $term2 = readTerm($gullet, $type);
$term = ($op eq '+' ? $term->add($term2) : $term->subtract($term2)); }
return $term; }); }
sub readTerm {
my ($gullet, $type) = @_;
$gullet->skipSpaces;
# my $factor = readTextDimenFactor($gullet) # but only if type is dimen!
# || readReal($gullet) # ? only sometimes
# || readMinMax($gullet,$type)
# || readParenthesized($gullet,$type)
# || $gullet->readValue($type);
my $factor = readValue($gullet, $type);
while (my $op = $gullet->readKeyword('*', '/')) {
# my $factor2 = readReal($gullet)
# || readMinMax($gullet,'Number')
# || readParenthesized($gullet,'Number')
# || $gullet->readNumber();
my $factor2 = readValue($gullet, 'Number');
$factor = ($op eq '*'
? $factor->multiply($factor2)
# : $term->divide($div)); }
: (ref $factor)->new(int($factor->valueOf / $factor2->valueOf))); } # ????
return $factor; }
sub readValue {
my ($gullet, $type) = @_;
$gullet->skipSpaces;
my $peek = $gullet->readXToken();
# Evaluate <Text Dimen Factors>
if (Equals($peek, T_CS('\widthof'))) {
my $box = Digest($gullet->readArg);
Error('unexpected', $peek, $gullet, "\\widthof not expected here (reading $type)")
if $type eq 'Number';
return $box->getWidth; }
elsif (Equals($peek, T_CS('\heightof'))) {
my $box = Digest($gullet->readArg);
Error('unexpected', $peek, $gullet, "\\heightof not expected here (reading $type)")
if $type eq 'Number';
return $box->getHeight; }
elsif (Equals($peek, T_CS('\depthof'))) {
my $box = Digest($gullet->readArg);
Error('unexpected', $peek, $gullet, "\\depthof not expected here (reading $type)")
if $type eq 'Number';
return $box->getDepth; }
elsif (Equals($peek, T_CS('\totalheightof'))) {
my $box = Digest($gullet->readArg);
Error('unexpected', $peek, $gullet, "\\totalheightof not expected here (reading $type)")
if $type eq 'Number';
return $box->getHeight->add($box->getDepth); }
# Evaluate <Real> values
elsif (Equals($peek, T_CS('\real'))) {
my $arg = $gullet->readArg;
return $gullet->readingFromMouth(LaTeXML::Core::Mouth->new(), sub {
my ($igullet) = @_;
$igullet->unread($arg); # Really so weird?
return $igullet->readFloat(); }); }
elsif (Equals($peek, T_CS('\ratio'))) {
my $x = readExpression($gullet, 'Glue', $gullet->readArg);
my $y = readExpression($gullet, 'Glue', $gullet->readArg);
return Float($x->ptValue / $y->ptValue); }
# Evaluate MinMax values
elsif (Equals($peek, T_CS('\minof'))) {
my $x = readExpression($gullet, $type, $gullet->readArg);
my $y = readExpression($gullet, $type, $gullet->readArg);
return $x->smaller($y); }
elsif (Equals($peek, T_CS('\maxof'))) {
my $x = readExpression($gullet, $type, $gullet->readArg);
my $y = readExpression($gullet, $type, $gullet->readArg);
return $x->larger($y); }
# Read & Evaluate parenthesized subexpressions
elsif (Equals($peek, T_OTHER('('))) {
return readExpression($gullet, $type, $gullet->readUntil(T_OTHER(')'))); }
# Else read literal values.
else {
$gullet->unread($peek);
if ($type eq 'Number') {
return $gullet->readNumber(); }
else { # Really should read glue, but downgrade to dimension????
return $gullet->readGlue(); } } }
sub readMinMax {
my ($gullet, $type) = @_;
$gullet->skipSpaces;
my $peek = $gullet->readXToken();
if (Equals($peek, T_CS('\minof'))) {
my $x = readExpression($gullet, $type, $gullet->readArg);
my $y = readExpression($gullet, $type, $gullet->readArg);
return $x->smaller($y); }
elsif (Equals($peek, T_CS('\maxof'))) {
my $x = readExpression($gullet, $type, $gullet->readArg);
my $y = readExpression($gullet, $type, $gullet->readArg);
return $x->larger($y); }
else {
$gullet->unread($peek);
return; } }
sub readTextDimenFactor {
my ($gullet) = @_;
$gullet->skipSpaces;
my $peek = $gullet->readXToken();
if (Equals($peek, T_CS('\widthof'))) {
my $box = Digest($gullet->readArg);
return Dimension(0); }
elsif (Equals($peek, T_CS('\heightof'))) {
my $box = Digest($gullet->readArg);
return Dimension(0); }
elsif (Equals($peek, T_CS('\depthof'))) {
my $box = Digest($gullet->readArg);
return Dimension(0); }
elsif (Equals($peek, T_CS('\totalheightof'))) {
my $box = Digest($gullet->readArg);
return Dimension(0); }
else {
$gullet->unread($peek);
return; } }
sub readParenthesized {
my ($gullet, $type) = @_;
$gullet->skipSpaces;
if (my $op = $gullet->readKeyword('(')) {
return readExpression($gullet, $type, $gullet->readUntil(T_OTHER(')'))); }
else {
return; } }
sub readReal {
my ($gullet) = @_;
$gullet->skipSpaces;
my $peek = $gullet->readXToken();
if (Equals($peek, T_CS('\real'))) {
my $arg = $gullet->readArg;
return $gullet->readingFromMouth(LaTeXML::Core::Mouth->new(), sub {
my ($igullet) = @_;
$igullet->unread($arg); # Really so weird?
return $igullet->readFloat(); }); }
elsif (Equals($peek, T_CS('\ratio'))) {
my $x = readExpression($gullet, 'Glue', $gullet->readArg);
my $y = readExpression($gullet, 'Glue', $gullet->readArg);
return Float($x->ptValue / $y->ptValue); }
else {
$gullet->unread($peek);
return; } }
#**********************************************************************
1;