Latex Equation Compiler Code
Brought to you by:
jrheinlaender
/***************************************************************************
ltxfileparse.yy - rules for reading a Latex file with eqc commands
- parser generation file for bison
-------------------
begin : Sun Oct 21 2001
copyright : (C) 2001 by Jan Rheinlaender
email : jrheinlaender@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
%{
#define YYDEBUG 1
#include <stdexcept>
#include <stack>
#include <map> // *** added in 1.1
//#include <cln/cln.h> // *** added in 1.1, removed in 1.4.1
#include <cmath> // *** changed in 1.0
//#include "../config/config.h" // *** added in 1.1
#include <sstream>
#include "ltxfilelex.h"
#include "eqc.h"
#include "unit.h"
#include "func.h" // *** added in 0.5
#include "printing.h"
#include "msgdriver.h" // *** added in 1.3.1
#include <ginac/ginac.h> // *** added in 1.1
/// Variables ----------------------------------------------------------------
// Input and output files
#define YYPARSE_PARAM inputfile
std::ofstream output;
// Variables for following \input files
std::stack<int> linenumbers;
std::stack<std::string> inputfnames;
std::string inputfname = ""; // TODO: is this necessary? Why not use the stack?
// Variables for saving and restoring options
// Note: This does not handle nesting levels yet, all options are accumulated within an equation
// line and then removed
bool save_opts = false;
std::vector<option_name> optnames;
eqc compiler; // For storing away the parsing results
unsigned nesting_level = 0; // *** added in 0.8
equation last_printed; // *** added in 0.9
std::string nextlabel = ""; // Label for the next equation *** added in 1.1
// Brackets *** Moved here from bracketstack.cpp in 0.8
#define BRACKETTYPES 10
#define BRACKETSIZES 5
const std::string lbtypes[BRACKETTYPES] = {"{", "\\{", "\\lbrace", "(", "[", "|", "\\lbrack", "\\lfloor", "\\lceil", "\\langle"};
const std::string rbtypes[BRACKETTYPES] = {"}", "\\}", "\\rbrace", ")", "]", "|", "\\rbrack", "\\rfloor", "\\rceil", "\\rangle"};
const std::string lbsizes[BRACKETSIZES] = {"\\left", "\\bigl", "\\Bigl", "\\biggl", "\\Biggl"};
const std::string rbsizes[BRACKETSIZES] = {"\\right", "\\bigr", "\\Bigr", "\\biggr", "\\Biggr"};
static std::map<std::string, std::string> bracketmap; // A map giving the corresponding left bracket for any right bracket
bool initialized = false;
// Helper functions -----------------------------------------------------------
// *** Added code to handle new parameter ltxrepr in 0.4
// *** removed parameter label in 0.6, parameter ltxrepr in 0.9
void printeq(const equation &eq, const std::string &asterisk) {
if (asterisk == "") { // *** added equation chaining in 0.9
// TODO: Is there a more elegant way of doing this inside class equation?
if (optstack::options->get(o_eqchain).boolean) {
if ((last_printed.gettype() != empty) &&
(eq.lhs() == last_printed.lhs())) {
output << "{}";
eq.do_print_ltx(print_latex(output));
} else {
optstack::options->set(o_eqchain, false);
output << "{}";
eq.do_print_ltx(print_latex(output));
optstack::options->set(o_eqchain, true);
}
} else {
output << "{}" << latex << ex(eq) << dflt;
}
if (compiler.is_label(compiler.getprevious()))
last_printed = compiler[compiler.getprevious()]; // *** added in 0.9
}
if (!eq.is_temporary() && (asterisk == "")) { // *** changed to is_temporary() in 0.7
output << "\\label{" << eq.getlabel() << "}";
}
} // printeq()
void printerr(const std::string &text) { // *** added in 0.6, changed in 0.8
// TODO: implement error positioning in an expression
//msg_error.prio(0) << "Error in expression: '" << input << "'. Ignoring." << endline
// << " "
// << std::string(input.size() - to_scan - 2, ' ') << "<--" << endline; // *** changed ^ to <-- in 0.6
msg::error(0) << inputfname << ":" << linenumber << ": " << text << endline;
output << "{\\mbox{" << ltx_label(text) << "}}";
} // printerr()
// Note that result is no reference because of ex_to<> casting!
// *** replaced argument label by global nextlabel in 1.1
// *** Moved checking for a label in a nested equation elsewhere
expression *register_and_print(equation result, const expression &orig, const std::string &asterisk) {
// Note that we assume orig to be an equation!!!
if (nesting_level > 0) { // *** added in 0.8
MSG_INFO(10) << "Not printing equation, nesting level is " << nesting_level << endline;
compiler.deltempeq(orig, expression()); // *** changed in 0.9
return new expression(result);
} else {
try {
result.setlabel(nextlabel);
compiler.check_and_register(result, ex_to<equation>(orig));
printeq(compiler[compiler.getprevious()], asterisk);
} catch (std::exception &e) {
printerr(e.what()); // *** changed in 0.8
}
nextlabel = ""; // Clear the label
return new expression(compiler[compiler.getprevious()]); // *** added in 0.8
}
} // register_and_print
expression *register_and_print(const expression& result, const expression &orig, const std::string &asterisk) {
if (!is_a<equation>(result)) {
MSG_ERROR(0) << "Internal error: register_and_print() called with non-equation argument for: " << result << endline;
return new expression(numeric(1));
}
// Transform to equation, preserving the latex representation
equation eq = ex_to<equation>(result);
eq.setltxrepr(result.getltxrepr());
return register_and_print(eq, orig, asterisk); // *** changed in 1.1
} // register_and_print()
void checkbrackets(const std::string &left, const std::string &right) { // *** added in 0.8
MSG_INFO(10) << "Checking brackets '" << left << "' and '" << right << "'" << endline;
if (bracketmap[right] != left)
msg::warn(0) << "Warning: Bracket mismatch for '" << right << "'" << endline;
} // checkbrackets()
void restore_options() { // *** added in 1.1
if (nesting_level > 0) return; // *** added in 1.2
for (std::vector<option_name>::const_iterator c = optnames.begin(); c != optnames.end(); c++) {
optstack::options->restore(*c);
}
optnames.clear();
save_opts = false;
} // restore_options()
// Return type declarations ----------------------------------------------
%}
%union {
std::string *str; // For parsing strings
bool boolean; // For parsing booleans *** changed *boolean to boolean in 1.2, pointer not required here
expression *expr; // For parsing expressions *** changed ex to class expression in 0.4
exprvector *exlist; // For parsing expression lists *** changed to vector in 1.0
std::vector<std::string> *strlist; // For parsing STL <std::string> lists
unsigned uint; // For parsing flag arguments *** added in 0.6
option_name opt; // For parsing option name arguments *** added in 1.2
aligntype align; // For parsing alignment names *** added in 1.2
}
%defines
%start init
%{
// Token declarations -------------------------------------------------------
// *** added NUMER and DENOM in 0.8, SQUARE and CUBIC removed
// *** added EQSOLVE in 0.8
// *** made EQSUBST type str in 0.9
// *** removed AUTOALIGN and NOAUTOALIGN, replaced by printoptions in 0.9
// *** removed expstr, eq, equation, equationstm, equals, explist, substlist and vector in 0.9
// *** made simplifications strlist in 0.9
// *** added EQEVAL, EQEVQLP, PRINTV and TSERIES in 1.0
// *** made unitlist type exlist and IMPMUL type str in 1.0
// *** added TRUNC, path, BOOL, ?OPT_*, O_LABEL, SAVE, RESTORE, optword in 1.1
// *** removed EQCPATH SCLIMITS PRECISIONTYPE PRECISION UNITS LANG ALIGN in 1.1
// *** added VALUEWITH, ALIGN, OPT_A. EOPT_QS, COPT_QS, LABEL became QSTRING in 1.2
// *** simplified option processing in 1.2
// *** removed ROUND in 1.2
// *** added INTEGRAL in 1.3.1
// *** added MINDEX and MATRIX in 1.4.3
// *** added NUMERIC, EXPONENT, MBOX in 1.4.4
%}
%token <str> BOPEN BCLOSE DIGIT SUBSCRIPT /* Basic tokens */
%token <boolean> BOOL
%token <str> NAME WORD QSTRING MACRO STRING UNIT FUNC INTEGRAL MINDEX SYMBOL /* String tokens */
%token <expr> NUMERIC
%token <str> KWUNIT FRAC VALUE VALUEWITH LHSRHS NUMER DENOM EQEVAL EQEVALP TSERIES /* Expression statements */
%token CONSTANT FUNCTION MATRIX DEFFUNC DEFUNIT MBOX /* Keywords for declaring things at top level */
%token EQUATION EQOP EQSUBST EQREV EQSIMPF EQSOLVE /* equation statements */
%token PRINT PRINTV DELETE /* Operations on equations */
%token USEPACKAGE INPUT RENEWCOMMAND ENDFILE BEGINENV ENDENV DUMP /* File management keywords */
%token SAVE RESTORE O_LABEL /* Keywords for options */
%token <opt> OPT_E OPT_S OPT_B OPT_L OPT_A
%token <align> ALIGN
%token CLEAREQUATIONS EQCOPTIONS /* Keywords for changing global settings */
%type <str> EQOP EQSUBST ENDFILE IMPMUL
%type <str> integer float number
%type <str> asterisk word optword wordlist optpackageargs bopen bclose
%type <expr> ex exp exponent expressionstm
%token <uint> EXPONENT
%type <exlist> unitlist
%type <strlist> labellist simplifications
%type <uint> opthints hints statement
%type <opt> opt
%{
// Operator declarations ----------------------------------------------
%}
%left ';' /* *** added in 0.8 */
%left OVER
%left '=' /* *** added in 0.8 */
%left ':' /* *** added in 1.0 */
%left '-' '+'
%left '*' '/' IMPMUL TIMES /* *** added IMPMUL in 0.5, removed MUL in 0.9 */
%nonassoc NEG /* negation (unary minus) */
%right FUNC INTEGRAL /* changed order of NEG and FUNC in 0.5, added INTEGRAL in 1.3.1 */
%right '^' /* exponentiation */
%nonassoc EXPONENT /* exponentiation */
%right '!' /* faculty *** added in 1.0 */
%left '&' /* added in 0.9 */
%%
init: { // This rule is necessary to ensure that the input buffer and the func class
// is initialised before parsing starts
if (msg::info().checkprio(11)) yydebug = 1; // turn on bison debugging
inputfname = *((std::string *)inputfile);
if (!new_input_buffer(inputfname)) {
msg::error(0) << "Can't open " << inputfname << endline;
return (EXIT_FAILURE);
}
// Construct the output file name and open the file
std::string outname(inputfname);
int pos = outname.rfind(".");
outname.erase(pos, outname.size() - pos);
outname = outname + ".eqc";
try {
output.open(outname.c_str());
} catch (std::exception &e) {
msg::error(0) << "Can't write to " << outname << endline;
pop_input_buffer();
return EXIT_FAILURE;
}
// Initialize map of brackets *** moved here from bracketstack.cpp in 0.8
if (!initialized) {
for (unsigned i = 0; i < BRACKETTYPES; i++) {
bracketmap[rbtypes[i]] = lbtypes[i];
for (unsigned j = 0; j < BRACKETSIZES; j++)
bracketmap[rbsizes[j]+rbtypes[i]] = lbsizes[j]+lbtypes[i];
}
if (msg::info().checkprio(10)) {
msg::info() << "Brackets:" << endline;
std::map<std::string,std::string>::iterator variable = bracketmap.begin();
while (variable != bracketmap.end()) {
msg::info() << variable->first << " : " << variable->second << endline;
variable++;
}
}
initialized = true;
}
enter_state(scan_stm); // Start scanning for a macro that begins a statement
} input
;
// File management and scanning for the next keyword -----------------------------------------
input: { /* empty */ }
| input STRING {
//if ((*$2 == "\n") || (*$2 == "%")) {
// std::cout << "Removed empty line" << std::endl;
//} else {
output << *$2; delete ($2);
//}
}
| input MACRO { // *** replaced scan_arg(scan_stm) with exit_state() here and in following rules in 0.8
output << *$2; delete ($2);
exit_state(); // return to scan_stm
}
| input statement {
if (get_state() != verbatim) { // *** added in 0.8
if (get_asterisk_statement()) {
exit_state();
set_asterisk_statement(true); // Propagate to this state!
} else {
exit_state(); // Leave keyword processing state, return to statement scanning
set_asterisk_statement(false);
}
}
}
| input error { // *** moved here in 0.8
// Handle errors in statements by skipping to the next keyword
msg::error(0) << " before '" << yytext << "'. Skipping to next keyword." << endline;
clear_state(); // *** added in 0.8
enter_state(scan_stm); // *** added in 0.8
yyclearin;
yyerrok;
}
| input ENDFILE {
output << *$2; delete ($2); // *** added in 0.6
if (inputfnames.empty()) {
if (msg::info().checkprio(3)) {
std::ostringstream os;
compiler.print(os);
Unit::print(os);
msg::info() << os.str();
}
compiler.clearall();
linenumber = 1;
nesting_level = 0; // *** added in 0.8
msg::info(0) << "Finished with input file " << inputfname << "." << endline;
pop_input_buffer();
output.close();
exit_state(); // *** added in 0.8, state stack should be empty after this
clear_state(); // *** added in 0.8, prints warning if stack is not empty
return (EXIT_SUCCESS); // continue with next file on command line
} else {
// Switch back to the file that had the \input statement
msg::info(0) << "Switching input back to file " << inputfnames.top() << "." << endline;
pop_input_buffer();
linenumber = linenumbers.top();
linenumbers.pop();
inputfname = inputfnames.top();
inputfnames.pop();
exit_state(); // *** added in 0.6
enter_state(scan_stm); // *** added in 0.8, net result is to clear 'argument' in the scanner
}
}
;
word: WORD //*** Definition added in version 0.5
| '{' WORD '}' { $$ = $2; }
;
// Processing of EQC statements
statement: expressionstm { $$ = 0; /* Just to remove the default argument (equation) *** added in 0.8 */ }
// Operations on equations -------------------------------------------------------------
| PRINT '{' exp '}' { // *** added in 0.6, changed to use exp in 0.9
if (!is_a<equation>(*$3)) {
printerr("Equation expected");
} else {
// If we don't set the label to "", a latex \label will be created!
equation eq = ex_to<equation>(*$3);
printeq(eq.setlabel(""), "");
}
delete ($3);
}
| PRINTV '{' exp '}' { // *** added in 1.0, print a matrix in pstricks format
if (!is_a<matrix>(*$3))
printerr("Matrix expected");
else if (ex_to<matrix>(*$3).cols() != 2)
printerr ("The matrix must have two columns");
else {
matrix m = ex_to<matrix>(*$3);
const option_name opts[5] = {o_precision, o_fixeddigits, o_lowsclimit, o_highsclimit, o_lang};
for (unsigned i = 0; i < 5; i++) optstack::options->save(opts[i]);
option_value o;
o.expr = new ex(10);
optstack::options->set(o_precision, o);
optstack::options->set(o_fixeddigits, false);
o.expr = new ex(0.0000000001);
optstack::options->set(o_lowsclimit, o); // **** changed in 1.2
o.expr = new ex(999999999);
optstack::options->set(o_highsclimit, o);
optstack::options->set(o_lang, std::string("english"));
for (unsigned i = 0; i < m.rows(); i++) {
// prevent things like 1e-5 from appearing in the output
output << "(";
print_ltx(m(i, 0), output);
output << ", ";
print_ltx(m(i, 1), output);
output << ")";
}
for (unsigned i = 0; i < 5; i++) optstack::options->restore(opts[i]);
}
}
| DELETE '{' { enter_state(scan_ex); } labellist '}' { // *** added in 0.6
set_asterisk_statement(true); // *** added in 1.4.3
for (std::vector<std::string>::const_iterator i = $4->begin(); i != $4->end(); i++) {
try {
compiler.deleq(*i);
} catch (std::exception &e) {
printerr(e.what()); // *** changed in 0.8
}
}
delete($4);
}
// *** Keyword align now handled by option eqalign
// Declaration of things at top level ---------------------------------------------------
| CONSTANT asterisk '{' exp '}' {
// *** removed token SPACE in 0.6, changed to use exp in 0.9
if (!is_a<equation>(*$4)) {
printerr("Equation expected");
} else {
equation eq = ex_to<equation>(*$4);
if (eq.gettype() != original)
printerr("Warning: Registering a constant that was already defined in an equation");
try {
compiler.register_constant(eq);
} catch (std::exception &e) {
printerr(e.what()); // *** changed in 0.8
}
compiler.lastequation(eq); // *** changed in 0.6
equation last = compiler[compiler.getprevious()];
last.setltxrepr($4->getltxrepr());
printeq(last, *$2);
}
delete ($2); delete ($4);
}
| FUNCTION opthints '{' exp '}' '{' exp '}' {
set_asterisk_statement(true); // *** added in 1.4.3
// *** added in 0.5, added hints in 0.6
// *** removed token SPACE in 0.6
if (!is_a<symbol>(*$4)) {
printerr("Symbol expected for first argument of \\function"); // *** changed in 0.8
} else { // *** changed to register_function in 0.7
compiler.register_function(ex_to<symbol>(*$4).get_name(), make_lst_from_matrix(*$7), $2); // *** changed in 1.3.0
}
delete ($4); delete ($7);
}
| MATRIX '{' WORD '}' { // *** added in 1.4.3
set_asterisk_statement(true); // *** added in 1.4.3
compiler.getsym(*$3, *$3, true);
//compiler.getsym(*$3).print(print_tree(std::cout));
MSG_INFO(0) << "Created matrix " << *$3 << endline;
delete ($3);
}
| DEFUNIT asterisk optword '{' MACRO '}' '{' exp '}' {
// *** Rewrite in 0.4 using class expression
// *** removed token SPACE in 0.6
std::string name(*$5, 1, $5->size()); // erase the backslash *** made more compact in 0.8
Unit::add_conversion(name, *$8);
Unit::add_unitname(*$5);
if (*$2 == "") {
output << "\\defunit{" << *$5 << "}{" << ((*$3 == "") ? $8->getltxrepr() : *$3) << "}";
} else {
set_asterisk_statement(true); // *** added in 1.4.3
}
delete ($2); delete($3); delete ($5); delete ($8);
}
| DEFFUNC asterisk '{' exp '}' '{' exp '}' { // *** added in 0.5
// *** removed token SPACE in 0.6
if (!is_a<func>(*$4)) {
printerr("Function name expected for \\deffunc"); // *** changed in 0.8
} else {
func::define(ex_to<func>(*$4).get_name(), *$7);
// *** moved autoalign functionality to class equation in 0.9
printeq(equation(*$4, *$7), *$2);
}
delete ($2); delete ($4); delete ($7);
}
// Changing global settings ------------------------------------------------------
| EQCOPTIONS '{' keyvallist '}' { // *** PRINTOPTIONS replaced by EQCOPTIONS in 1.1
set_asterisk_statement(true); // *** added in 1.4.3
// All the work is done by 'keyvalllist'
}
// *** Moved EQLANG, PRECISION, PRECISION_TYPE, SCIENTIFIC_LIMITS, EQCPATH, PREFERRED_UNITS
// *** to EQCOPTIONS in 1.1
| CLEAREQUATIONS {
set_asterisk_statement(true); // *** added in 1.4.3
msg::info(1) << "Clearing equations..." << endline;
if (msg::info().checkprio(3)) {
std::ostringstream os;
compiler.print(os);
Unit::print(os);
}
compiler.clear();
}
// File management ----------------------------------------------------------------------
| DUMP { // *** added in 0.8
set_asterisk_statement(true); // *** added in 1.4.3
compiler.dumpeq(output);
}
| USEPACKAGE optpackageargs '{' WORD '}' {
// *** removed token SPACE in 0.6
output << "\\usepackage" << ((*$2 == "") ? "" : "[" + *$2 + "]")
<< '{' << *$4 << '}';
if ((*$4 == "eqc") && (*$2 != "")) {
optstack::options->set(o_lang, *$2); // *** changed in 1.1
}
delete ($2); delete ($4);
}
| BEGINENV '{' WORD asterisk '}' { // *** added in 0.8
set_asterisk_statement(false); // *** added in 1.4.3
output << "\\begin{" << *$3 << *$4 << "}";
option_value o;
if (*$3 == "verbatim") {
exit_state(); // finish processing keyword
enter_state(verbatim);
} else if (*$3 == "eqnarray") {
o.align = both;
optstack::options->set(o_eqalign, o); // *** changed in 0.9, in 1.1
last_printed = equation(); // No equation chaining between environments *** added in 0.9
} else if ((*$3 == "align") || (*$3 == "alignat") ||
(*$3 == "aligned") || (*$3 == "alignedat") || (*$3 == "flalign")) {
o.align = onlyleft;
optstack::options->set(o_eqalign, o); // *** changed in 0.9, in 1.1
last_printed = equation(); // No equation chaining between environments *** added in 0.9
}
MSG_INFO(2) << "Found environment " << *$3 << *$4 << endline;
delete ($3); delete ($4);
}
| ENDENV '{' WORD asterisk '}' { // *** added in 0.8
set_asterisk_statement(false); // *** added in 1.4.3
output << "\\end{" << *$3 << *$4 << "}";
if ((*$3 == "eqnarray") || (*$3 == "align") || (*$3 == "alignat") ||
(*$3 == "aligned") || (*$3 == "alignedat") || (*$3 == "flalign")) {
option_value o;
o.align = none;
optstack::options->set(o_eqalign, o); // *** changed in 0.9
last_printed = equation(); // No equation chaining between environments *** added in 0.9
} else if (*$3 == "document") {
// TODO: Stop parsing at \end{document}
}
MSG_INFO(2) << "End of environment " << *$3 << *$4 << endline;
delete ($3); delete ($4);
}
| INPUT word { //*** changed WORD to word in 0.5
// *** removed token SPACE in 0.6
inputfnames.push(inputfname);
inputfname = *$2;
bool success; //*** added in 0.5
if (!(success = new_input_buffer(inputfname))) {
inputfname += ".tex"; //*** added in 0.5
if (!(success = new_input_buffer(inputfname))) {
inputfname = inputfnames.top();
inputfnames.pop();
msg::error(0) << inputfname << ":" << linenumber << ": Can't open either "
<< *$2 << " or " << *$2 << ".tex. Skipping." << endline;
output << "\\input " << *$2; // TODO: Any brackets that might have been used are lost! Problem?
}
}
if (success) {
msg::info(0) << "Switching input to file " << inputfname << "." << endline;
linenumbers.push(linenumber);
linenumber = 1;
}
delete ($2);
}
| RENEWCOMMAND '{' { enter_state(scan_str); } STRING '}' optword
'{' { enter_state(scan_str); } STRING '}' {
// *** changed ARGUMENT to STRING in 0.8
// *** changed optarg to optword in 1.1
output << "\\renewcommand{" << *$4 << "}" << ((*$6 != "") ? ("[" + *$6 + "]") : "")
<< "{" << *$9 << "}";
delete ($4); delete ($6); delete ($9);
}
;
// Note that the units in this list are NOT canonicalized!!!
unitlist: MACRO {
$$ = new exprvector;
$$->push_back(Unit($1->erase(0,1))); // *** changed to vector in 1.0
delete ($1);
}
| unitlist ';' MACRO {
$1->push_back(Unit($3->erase(0,1))); // remove backslash! *** changed to push_back in 1.0
$$ = $1;
delete ($3);
}
;
// *** renamed to labellist, changed ARGUMENT to LABEL in 0.8, changed to vector in 0.9, to QSTRING in 1.2
labellist: QSTRING { $$ = new std::vector<std::string>; $$->push_back(*$1); delete($1); }
| labellist ';' QSTRING {
// *** enter_state(scan_ex) is not necessary in between list items since 0.8
$1->push_back(*$3);
$$ = $1;
delete ($3);
}
;
opthints: /* no argument */ { $$ = 0; }
| '[' hints ']' { $$ = $2; }
;
hints: WORD { $$ = func::hint(*$1); delete($1); }
| hints ';' WORD {
$$ = $1 | func::hint(*$3);
delete($3);
}
;
// *** keyvalllist and keyvalpair added in 1.1
// Note: EOPT_E = exp does NOT work, because it is ambiguous: An exp can be ex ';' ex !!
keyvallist: keyvalpair
| keyvallist ';' keyvalpair
;
keyvalpair: OPT_L '=' '{' unitlist '}' { // Unit options: units
if (save_opts) {
optnames.push_back($1);
optstack::options->save($1);
}
option_value o;
o.vec = Unit::create_conversions(*$4);
optstack::options->set($1, o);
}
| OPT_E '=' exp { // precision, lowsclimit, highsclimit *** added in 1.2, two shift-reduce conflicts??
if (save_opts) {
optnames.push_back($1);
optstack::options->save($1);
}
try {
// std::cout << "Setting option " << $1 << " to value " << *$3 << std::endl;
option_value o;
o.expr = $3;
optstack::options->set($1, o);
} catch (std::exception &e) {
printerr(e.what());
}
if ($1 == o_debug) msg::info().setlevel(optstack::options->get(o_debug).integer); // *** added in 1.4.3 TODO: Does not honour push/pop yet
delete ($3);
}
| OPT_A '=' ALIGN { // eqalign
if (save_opts) {
optnames.push_back($1);
optstack::options->save($1);
}
option_value o;
o.align = $3;
optstack::options->set($1, o);
}
| OPT_S '=' QSTRING { // lang, path, eqsplittext
if (save_opts) {
optnames.push_back($1);
optstack::options->save($1);
}
optstack::options->set($1, *$3);
delete ($3);
}
| OPT_B '=' BOOL { // eqraw, eqparse, eqchain, fixeddigits
if (save_opts) {
optnames.push_back($1);
optstack::options->save($1);
}
optstack::options->set($1, $3);
if ($1 == o_tan) func::set_tangent(optstack::options->get(o_tan).boolean); // *** added in 1.4.3 TODO: Does not honour push/pop yet
}
| O_LABEL '=' QSTRING {
if (nesting_level > 0) {
msg::warn(0) << "Warning: Assigning the label " << *$3
<< " to a nested equation will loose the label." << endline;
} else {
nextlabel = *$3;
}
nextlabel = *$3;
delete($3);
}
| SAVE '=' opt { // Save a compiler option
optstack::options->save($3);
}
| RESTORE '=' opt { // Restore a compiler option
optstack::options->restore($3);
}
;
opt: OPT_E | OPT_S | OPT_A | OPT_B | OPT_L
;
// *** printflags added in 0.9, moved functionality to keyvalllist in 1.1.
// equation statements ----------------------------------------------------------------------
// *** moved here from statement in 0.6
expressionstm: EQUATION asterisk optarg '{' exp '}' { // *** changed to use exp in 0.9
if (!is_a<equation>(*$5)) {
printerr("Equation expected");
$$ = new expression();
} else {
if ((ex_to<equation>(*$5).gettype() == derived) && (*$2 == "")) {
msg::warn(0) << inputfname << ":" << linenumber << ": "
<< "Warning: Creating duplicate equation! Use \\printeq?" << endline;
}
$$ = register_and_print(*$5, equation(), *$2); // *** changed in 1.1
}
restore_options(); // *** added in 1.1
delete ($2); delete($5);
}
| EQOP asterisk optarg '{' exp '}' '{' exp '}' { // *** changed to use exp in 0.9
// Note: The Latex representation is lost here
if (!is_a<equation>(*$5)) {
printerr("Equation expected");
$$ = new expression();
} else { // Perform the requested operation on the equation
expression result = equation();
try { // *** added try-catch block in 0.6 for division by zero etc.
if (*$1 == "add") result = *$5 + *$8;
else if (*$1 == "sub") result = *$5 - *$8;
else if (*$1 == "mul") result = *$5 * *$8;
else if (*$1 == "div") result = *$5 / *$8;
else if (*$1 == "pow") result = pow(*$5, *$8); // *** added in 0.6
else if (*$1 == "func") result = ex_to<equation>(*$5).apply_func(*$8); // *** added in 0.9
else if (*$1 == "diff") result = ex_to<equation>(*$5).diff(*$8);
else printerr("Unknown operator '\\eq" + *$1 + "'."); // *** changed in 0.8
} catch (std::exception &e) {
printerr(e.what()); // *** changed in 0.8
}
MSG_INFO(1) << "Result: " << result << endline;
$$ = register_and_print(result, *$5, *$2); // *** changed in 0.9
}
restore_options(); // *** added in 1.1
delete ($1); delete ($2); delete($5); delete($8);
}
| EQSUBST asterisk optarg '{' exp '}' '{' exp '}' {
// Note: The Latex representation is lost here
if (!is_a<equation>(*$5)) {
try { // *** changed to accept expressions in 1.0
if (*$1 == "subst") {
$$ = new expression($5->subs(make_lst_from_matrix(*$8), subs_options::algebraic)); // *** changed in 1.3
} else if (*$1 == "substc") {
$$ = new expression($5->csubs(make_lst_from_matrix(*$8), subs_options::algebraic)); // *** changed in 1.3
} else {
throw std::invalid_argument("Substitution type " + *$1 + " does not exist.");
}
} catch (std::exception &e) { // *** added try/catch in 0.8
printerr(e.what());
$$ = new expression();
}
} else {
expression result;
try {
if (*$1 == "subst") { // *** added distinction between subst and substc in 0.9
result = ex_to<equation>(*$5).subs(make_lst_from_matrix(*$8), subs_options::algebraic); // *** changed in 1.3
} else if (*$1 == "substc") {
result = ex_to<equation>(*$5).csubs(make_lst_from_matrix(*$8), subs_options::algebraic); // *** changed in 1.3
} else {
throw std::invalid_argument("Substitution type " + *$1 + " does not exist.");
}
} catch (std::exception &e) { // *** added try/catch in 0.8
printerr(e.what());
}
$$ = register_and_print(result, *$5, *$2);
}
restore_options(); // *** added in 1.1
delete($2); delete($5); delete($8);
}
| EQREV asterisk optarg '{' exp '}' { // *** made to use exp in 0.9
if (!is_a<equation>(*$5)) {
printerr("Equation expected");
$$ = new expression();
} else {
equation eq = ex_to<equation>(*$5);
eq.setltxrepr($5->getltxrepr());
eq = eq.reverse();
$$ = register_and_print(eq, *$5, *$2);
}
restore_options(); // *** added in 1.1
delete($2); delete($5);
}
| EQSIMPF asterisk optarg '{' exp '}' '{' simplifications '}' {
// Note: The Latex representation is lost here
// *** added in 0.6, made to use exp in 0.9
if (!is_a<equation>(*$5)) {
printerr("Equation expected");
$$ = new expression();
} else {
try { // *** changed in 0.9
equation eq = ex_to<equation>(*$5); // This is necessary because expression::simplify introduces relationals instead of equations!
$$ = register_and_print(eq.simplify(*$8), *$5, *$2);
} catch (std::exception &e) { // *** added try/catch in 0.8
printerr(e.what());
$$ = new expression();
}
}
restore_options(); // *** added in 1.1
delete($2); delete($5);
}
| EQSOLVE asterisk optarg '{' exp '}' '{' exp '}' '{' exp '}' {
// Note: The Latex representation is lost here
// *** added in 0.8, made to use exp in 0.9
if (!is_a<equation>(*$5)) {
printerr("Equation expected");
$$ = new expression();
} else {
try { // *** changed in 0.9
equation result = ex_to<equation>(*$5).solve(*$8, *$11);
MSG_INFO(0) << "Solution (rounded): " << result.evalf() << endline;
$$ = register_and_print(result, *$5, *$2);
} catch (std::exception &e) {
printerr(e.what());
$$ = new expression();
}
}
restore_options(); // *** added in 1.1
delete($2); delete($5); delete($8); delete($11);
}
// Expression statements
| VALUE optvalarg '{' exp '}' %prec FUNC { // *** merged with ltxeqparse in 0.8
// Note: The Latex representation is lost here
// Print the value of this expression
symbol var;
if (!is_a<symbol>(*$4)) {
var = ex_to<symbol>(compiler.getsym(VALSYM)); // *** added ex_to<symbol> in 0.9
try { // *** changed derived to original in 0.7
compiler.check_and_register(equation(var, *$4, relational::equal, none, original, VALLABEL));
} catch (std::exception &e) { // this should never happen...
printerr(e.what()); // *** changed in 0.8
}
} else {
var = ex_to<symbol>(*$4);
}
expression value;
std::ostringstream v; // *** added in 1.0
try {
if (*$1 == "\\val") value = compiler.find_value_of(var);
else if (*$1 == "\\quantity") value = compiler.find_quantity_of(var);
else if (*$1 == "\\numval") value = expression(compiler.find_numval_of(var));
else if (*$1 == "\\units") value = compiler.find_units_of(var);
print_ltx(Unit::subst_units(value, *optstack::options->get(o_units).vec).evalf(), v, true);
if (nesting_level > 0) { // *** added in 0.8
MSG_INFO(10) << "Not printing expression, nesting level is " << nesting_level << endline;
} else {
output << v.str(); // *** changed in 1.0
}
MSG_INFO(1) << *$1 << " of " << *$4 << " : " << v.str() << endline;
} catch (std::exception &e) {
printerr(e.what());
output << "\\mbox{[" << $1->erase(0,1) << "]}";
value = var; // leave everything as it is
}
$$ = new expression(value, v.str()); // *** changed in 1.0, now adds a ltxrepr
restore_options();
delete ($1); delete ($4);
}
| VALUEWITH optvalarg '{' exp '}' '{' exp '}' %prec FUNC { // *** added in 1.2
// Note: The Latex representation is lost here
// Print the value of this expression using an optional list of assignments to find it
symbol var;
if (!is_a<symbol>(*$4)) {
var = ex_to<symbol>(compiler.getsym(VALSYM)); // *** added ex_to<symbol> in 0.9
try { // *** changed derived to original in 0.7
compiler.check_and_register(equation(var, *$4, relational::equal, none, original, VALLABEL));
} catch (std::exception &e) { // this should never happen...
printerr(e.what()); // *** changed in 0.8
}
} else {
var = ex_to<symbol>(*$4);
}
expression value;
std::ostringstream v; // *** added in 1.0
try {
if (*$1 == "\\val") value = compiler.find_value_of(var, make_lst_from_matrix(*$7)); // *** changed in 1.3.0
else if (*$1 == "\\quantity") value = compiler.find_quantity_of(var, make_lst_from_matrix(*$7)); // *** changed in 1.3.0
else if (*$1 == "\\numval") value = expression(compiler.find_numval_of(var, make_lst_from_matrix(*$7))); // *** changed in 1.3.0
else if (*$1 == "\\units") value = compiler.find_units_of(var, make_lst_from_matrix(*$7)); // *** changed in 1.3.0
print_ltx(Unit::subst_units(value, *optstack::options->get(o_units).vec).evalf(), v, true); // *** changed in 1.0, in 1.1, added optstack::options->get in 1.4.1
if (nesting_level > 0) { // *** added in 0.8
MSG_INFO(10) << "Not printing expression, nesting level is " << nesting_level << endline;
} else {
output << v.str(); // *** changed in 1.0
}
MSG_INFO(1) << *$1 << " of " << *$4 << " using " << *$7 << " : " << v.str() << endline;
} catch (std::exception &e) {
// TODO: use printerr(e.what()) ?
printerr(e.what());
output << "\\mbox{[" << $1->erase(0,1) << "]}";
value = var; // leave everything as it is
}
$$ = new expression(value, v.str()); // *** changed in 1.0, now adds a ltxrepr
restore_options();
delete ($1); delete ($4); delete ($7);
}
| EQEVAL '{' exp '}' '{' exp '}' { // *** added in 1.0
if (!is_a<equation>(*$3) || !is_a<equation>(*$6))
printerr("Two equations expected as arguments");
else {
expression f = ex_to<equation>(*$3).rhs(); // The function to evaluate
expression var = ex_to<equation>(*$6).lhs(); // The independent variable
expression values = ex_to<equation>(*$6).rhs(); // The values of the variable
if (!is_a<symbol>(var))
printerr("The independent variable must be a symbol");
else if (!is_a<matrix>(values))
printerr ("The values of the independent variable must be a vector");
else if (ex_to<matrix>(values).cols() != 1)
printerr ("The vector of values may have only one column");
else {
matrix vals = ex_to<matrix>(values);
matrix result(vals.rows(), 2);
for (unsigned i = 0; i < vals.rows(); i++) {
result(i, 0) = vals(i, 0);
try {
result(i, 1) = f.subs(var == vals(i, 0));
} catch (std::exception &e) {
printerr(e.what());
result(i, 1) = 0;
}
}
MSG_INFO(1) << "Calculated " << *$3 << " for " << var << " = " << values
<< ": " << result << endline;
$$ = new expression(result);
}
}
}
| EQEVALP '{' exp '}' '{' exp '}' '{' exp '}' { // *** added in 1.0
if (!is_a<equation>(*$3) || !is_a<equation>(*$6) || !is_a<equation>(*$9))
printerr("Three equations expected as arguments");
else {
expression x = ex_to<equation>(*$3).rhs(); // The x-function to evaluate
expression y = ex_to<equation>(*$6).rhs(); // The y-function to evaluate
expression var = ex_to<equation>(*$9).lhs(); // The independent variable
expression values = ex_to<equation>(*$9).rhs(); // The values of the variable
if (!is_a<symbol>(var))
printerr("The independent variable must be a symbol");
else if (!is_a<matrix>(values))
printerr ("The values of the independent variable must be a vector");
else if (ex_to<matrix>(values).cols() != 1)
printerr ("The vector of values may have only one column");
else {
matrix vals = ex_to<matrix>(values);
matrix result(vals.rows(), 2);
for (unsigned i = 0; i < vals.rows(); i++) {
try {
result(i, 0) = x.subs(var == vals(i, 0));
result(i, 1) = y.subs(var == vals(i, 0));
} catch (std::exception &e) {
printerr(e.what());
result(i, 0) = 0;
result(i, 1) = 0;
}
}
MSG_INFO(1) << "Calculated " << *$3 << " , " << *$6 << " for " << var << " = " << values << ": " << result << endline;
$$ = new expression(result);
}
}
}
| LHSRHS '{' exp '}' {
// Note: The Latex representation is lost here
// *** merged versions from ltxfileparse and ltxeqparse in 0.8
// *** included handling of equations in 0.9
if (!is_a<equation>(*$3)) {
printerr("Equation expected");
$$ = new expression();
} else {
$$ = new expression((*$1 == "\\lhs") ? $3->lhs() : $3->rhs());
if (nesting_level > 0) { // *** added in 0.8
MSG_INFO(10) << "Not printing expression, nesting level is " << nesting_level << endline;
} else {
print_ltx(*$$, output, true);
}
}
delete ($1); delete ($3);
}
| TSERIES '{' exp '}' '{' exp '}' '{' exp '}' { // *** added in 1.0
try {
if (!$9->info(info_flags::nonnegint))
throw std::invalid_argument("Third argument of \\tseries must be a positive integer");
$$ = new expression(
series_to_poly($3->series(*$6, ex_to<numeric>(*$9).to_int())));
//if (($3->testltxrepr()) && ($6->testltxrepr()) && ($9->testltxrepr()))
// $$->setltxrepr(*$1 + "{" + $3->getltxrepr() + "}{" + $6->getltxrepr() + "}"
// + "{" + $9->getltxrepr() + "}");
if (nesting_level > 0) {
MSG_INFO(10) << "Not printing expression, nesting level is " << nesting_level << endline;
} else {
print_ltx(*$$, output, true);
}
MSG_INFO(2) << "Taylor series of " << *$3 << ": " << *$$ << endline;
} catch (std::exception &e) {
printerr(e.what());
$$ = new expression;
}
}
| FRAC '{' exp '}' '{' exp '}' { // *** moved here from definition of ex in 0.8
$$ = new expression(*$3 / *$6);
//if (($3->testltxrepr()) && ($6->testltxrepr()))
// $$->setltxrepr(*$1 + "{" + $3->getltxrepr() + "}{" + $6->getltxrepr() + "}");
delete ($1); delete ($3); delete($6);
}
// | MBOX '{' { enter_state(scan_str); } STRING '}' { /* *** added in 1.4.4 */ }
| NUMER '{' exp '}' { // *** added in 0.8
$$ = new expression($3->numer());
delete ($3);
}
| DENOM '{' exp '}' { // *** added in 0.8
$$ = new expression($3->denom());
delete ($3);
}
;
exp: { enter_state(scan_ex); } ex { // *** added evalm() in 1.0
try {
$$ = new expression($2->evalm(), noraw ? std::string("") : argument); // *** changed in 1.4.4, was $2->getltxrepr()
} catch (std::exception& e) { // *** added in 1.4.3 because evalm() throws an exception on an ncmul with non-commutative symbols
$$ = new expression(*$2, noraw ? std::string("") : argument);
}
MSG_INFO(2) << "Set latex representation to '" << (noraw ? std::string("") : argument) << "'" << endline;
}
;
ex: SYMBOL {
$$ = new expression(compiler.getsym(*$1, *$1), *$1);
delete ($1);
}
| NUMERIC // *** added in 1.4.4
| number { // *** added n to constructor to set the ltxrepr in 0.9
//std::string n = *$1; // Save the original string for the ltxrepr
unsigned pos = $1->find(',');
if (pos <= $1->size()) $1->replace(pos, 1, ".");
$$ = new expression(numeric($1->c_str())); //, n);
delete($1);
}
| UNIT { // *** added *$1 to constructor to set the ltxrepr in 0.9
$$ = new expression(Unit::canonicalize_from(*$1), "\\" + *$1);
delete($1);
}
| ex ';' ex {
matrix *result;
matrix op1;
if (is_a<matrix>(*$1)) { // Construct a matrix. TODO: This is very inefficient
op1 = ex_to<matrix>(*$1);
unsigned rows = op1.rows();
if (is_a<matrix>(*$3)) { // A: We are adding a new row to the matrix
matrix op2 = ex_to<matrix>(*$3);
if (op2.cols() != op1.cols()) {
printerr("New row for matrix has different number of elements. Ignoring it.");
result = &op1;
} else {
rows++;
result = new matrix(rows, op1.cols());
for (unsigned r = 0; r < rows - 1; r++)
for (unsigned c = 0; c < op1.cols(); c++)
(*result)(r, c) = op1(r, c); // Copy the old matrix TODO: Here is the inefficiency
for (unsigned c = 0; c < op1.cols(); c++)
(*result)(rows - 1, c) = op2(0, c);
}
} else { // B: We are just adding a new element to a linear (one-row) matrix
result = new matrix(rows, op1.nops() + 1);
for (unsigned i = 0; i < op1.cols(); i++)
(*result)(0, i) = op1(0, i);
(*result)(0, op1.cols()) = *$3;
}
} else { // C: Construct matrix from two elements
result = new matrix(1, 2);
(*result) = *$1, *$3;
}
if (is_a<equation>(*$1)) { // We don't test *$3 because mixed lists do not occur
$$ = new expression(*result); // Lists of equations have no ltxrepr
} else {
$$ = new expression(*result);
//if (($1->testltxrepr()) && ($3->testltxrepr()))
// $$->setltxrepr($1->getltxrepr() + "; " + $3->getltxrepr());
}
delete($1); delete($3); delete(result);
}
| bopen ex bclose { // *** moved bracket checking here in 0.8
checkbrackets(*$1, *$3);
//if ($2->testltxrepr()) $2->setltxrepr(*$1 + $2->getltxrepr() + *$3);
$$ = $2;
delete($1); delete($3);
}
| FUNC { // a function may be used without arguments
if (*$1 == "\\wild") // *** added in 1.0
$$ = new expression(wild());
else
$$ = new expression(func(*$1), *$1);
delete ($1);
}
| FUNC ex { // *** changed in 0.5
if (*$1 == "\\wild") { // *** added in 1.0
if (is_a<numeric>(*$2) && ex_to<numeric>(*$2).info(info_flags::nonnegint)) {
$$ = new expression(wild(ex_to<numeric>(*$2).to_int()));
} else {
printerr("Argument of \\wild must be an integer");
$$ = new expression(wild());
}
} else {
if (is_a<matrix>(*$2) && (func(*$1).get_numargs() == 1)) { // *** added special handling in 1.4.3
MSG_INFO(0) << "Assuming that list of arguments is meant to be a matrix for " << *$1 << "(" << *$2 << ")" << endline;
$$ = new expression(func(*$1, *$2)); // We assume that the user meant to pass a matrix as the argument
} else {
$$ = new expression(func(*$1, make_lst_from_matrix(*$2))); // *** changed in 1.3.0
}
//if ($2->testltxrepr())
// if (!is_a<matrix>(*$2)) // *** added in 1.0, for functions with more than one argument, changed in 1.3.0
// $$->setltxrepr(*$1 + " " + $2->getltxrepr());
}
delete ($1); delete($2);
}
| INTEGRAL ex { // *** added in 1.3.1
if (!is_a<matrix>(*$2)) {
printerr("Argument of \\integral must be a list of three elements");
$$ = new expression();
} else {
matrix args = ex_to<matrix>(*$2);
if (!((args.rows() == 1) && (args.cols() == 3))) {
printerr("Argument of \\integral must be a list of three elements");
$$ = new expression();
} else if (!is_a<relational>(args(0,0))) {
printerr("First argument of \\integral must be an equation");
$$ = new expression();
} else {
expression var = ex_to<equation>(args(0,0)).lhs();
expression lowbound = ex_to<equation>(args(0,0)).rhs();
if (!is_a<symbol>(var)) {
printerr("First argument of \\integral must have form 'symbol = expression'");
$$ = new expression();
} else {
$$ = new expression(integral(var, lowbound, args(0,1), args(0,2)));
}
}
}
delete($2);
}
| ex '!' { // *** added in 1.0
$$ = new expression(factorial(*$1));
//if ($1->testltxrepr()) $$->setltxrepr($1->getltxrepr() + "!");
compiler.deltempeq(*$1, expression());
delete($1);
}
| '-' ex %prec NEG { // *** included handling of equations in 0.9
$$ = new expression(-*$2);
//if ($2->testltxrepr()) $$->setltxrepr("-" + $2->getltxrepr());
compiler.deltempeq(*$2, expression());
delete($2);
}
| '+' ex %prec NEG {
//if ($2->testltxrepr()) $2->setltxrepr("+" + $2->getltxrepr());
$$ = $2;
}
| ex '+' ex { // *** included handling of equations in 0.9
$$ = new expression(*$1 + *$3);
//if (($1->testltxrepr()) && ($3->testltxrepr()))
// $$->setltxrepr($1->getltxrepr() + " + " + $3->getltxrepr());
compiler.deltempeq(*$1, *$3); // Note that the nesting level will always be > 0
delete ($1); delete($3);
}
| ex '-' ex { // *** included handling of equations in 0.9
$$ = new expression(*$1 - *$3);
//if (($1->testltxrepr()) && ($3->testltxrepr()))
// $$->setltxrepr($1->getltxrepr() + " - " + $3->getltxrepr());
compiler.deltempeq(*$1, *$3);
delete ($1); delete($3);
}
| ex '*' ex { // *** included handling of equations in 0.9, added try catch
try {
$$ = new expression(*$1 * *$3);
//if (($1->testltxrepr()) && ($3->testltxrepr()))
// $$->setltxrepr($1->getltxrepr() + " * " + $3->getltxrepr());
} catch (std::exception &e) {
printerr(e.what());
$$ = new expression();
}
compiler.deltempeq(*$1, *$3);
delete ($1); delete($3);
}
| ex IMPMUL ex { // *** included handling of equations in 0.9
try {
$$ = new expression(*$1 * *$3);
//if (($1->testltxrepr()) && ($3->testltxrepr()))
// $$->setltxrepr($1->getltxrepr() + *$2 + $3->getltxrepr()); // *** added *$2 in 1.0
} catch (std::exception &e) { printerr(e.what()); $$ = new expression(); }
compiler.deltempeq(*$1, *$3);
delete ($1); delete ($2); delete ($3);
}
| ex TIMES ex {
try {
$$ = new expression(func("\\vecprod", vecprod(*$1, *$3)));
} catch (std::exception &e) { printerr(e.what()); $$ = new expression(); }
compiler.deltempeq(*$1, *$3);
delete ($1); delete ($3);
}
| ex '/' ex { // *** included handling of equations in 0.9
try {
$$ = new expression(*$1 / *$3);
//if (($1->testltxrepr()) && ($3->testltxrepr()))
// $$->setltxrepr($1->getltxrepr() + " / " + $3->getltxrepr());
} catch (std::exception &e) { printerr(e.what()); $$ = new expression(); }
compiler.deltempeq(*$1, *$3);
delete ($1); delete($3);
}
| ex OVER ex { // *** included handling of equations in 0.9
// Note: Cases like { x + 3 \over y + 4 } are treated as {(x+3) \over (y+4)} by Latex! And so does eqc...
try {
$$ = new expression(*$1 / *$3);
//if (($1->testltxrepr()) && ($3->testltxrepr()))
// $$->setltxrepr($1->getltxrepr() + "\\over " + $3->getltxrepr());
} catch (std::exception &e) { printerr(e.what()); $$ = new expression(); }
compiler.deltempeq(*$1, *$3);
delete ($1); delete($3);
}
| NAME '^' exponent { // *** changed symbol to NAME in 0.5
// This case needs a special rule because the next rule won't handle it. Bison will
// try to match the one after, which fails
$$ = new expression(pow (compiler.getsym(*$1, *$1), *$3));
//if ($3->testltxrepr()) $$->setltxrepr(*$1 + "^" + $3->getltxrepr());
delete($1); delete($3);
}
| ex EXPONENT { // *** added in 1.4.4
$$ = new expression(pow (*$1, numeric($2)));
compiler.deltempeq(*$1, expression());
delete($1);
}
| ex '^' exponent { // *** included handling of equations in 0.9
// One shift/reduce conflict with the "NAME ^ exponent" rule.
// TODO: How do we prevent double superscripts, as these are not allowed by latex?
$$ = new expression(pow (*$1, *$3));
//if (($1->testltxrepr()) && ($3->testltxrepr()))
// $$->setltxrepr($1->getltxrepr() + "^" + $3->getltxrepr());
compiler.deltempeq(*$1, *$3);
delete ($1); delete ($3);
}
| NAME '^' exponent SUBSCRIPT { // Handle a tiresome quirk of Latex syntax
$1->append(*$4);
$$ = new expression(pow (compiler.getsym(*$1, *$1), *$3));
//if ($3->testltxrepr()) $$->setltxrepr(*$1 + "^" + $3->getltxrepr());
delete ($1); delete($3); delete($4);
}
| NAME { // *** added in 0.5
// handle a NAME which has neither super- nor subscript
$$ = new expression(compiler.getsym(*$1, *$1), *$1);
delete ($1);
}
| '&' ex { // *** added in 0.9
//if ($2->testltxrepr()) $2->setltxrepr("&" + $2->getltxrepr());
if (is_a<equation>(*$2)) {
equation eq = ex_to<equation>(*$2);
$$ = new expression(eq.addalign(left));
delete($2);
} else {
$2->setampersand();
$$ = $2;
}
}
| ex '&' { // *** added in 0.9
//if ($1->testltxrepr()) $1->setltxrepr($1->getltxrepr() + "&");
if (is_a<equation>(*$1)) {
equation eq = ex_to<equation>(*$1);
$$ = new expression(eq.addalign(right));
delete($1);
} else {
$1->setampersand();
$$ = $1;
}
}
| ex ':' ex ':' ex { // *** added in 1.0
expression esize;
expression step;
if (*$5 == 0) { // autostep *** added in 1.2
esize = numeric(optstack::options->get(o_vecautosize).uinteger);
step = expression(*$3 - *$1) / expression(esize - 1);
} else {
step = *$5;
esize = (expression(*$3 - *$1) / step + 1).evalf();
}
if (!esize.info(info_flags::positive)) {
printerr("Error: Vector must have positive integer size");
$$ = new expression;
} else {
//MSG_INFO(2) << "Vector size: " << esize << endline;
unsigned size = numeric_to_uint(ex_to<numeric>(esize)); // *** changed in 1.0
matrix m(size, 1);
for (unsigned i = 0; i < size; i++) m(i, 0) = *$1 + expression(step * i);
$$ = new expression(m);
MSG_INFO(2) << "Created vector " << step << endline;
}
}
| ex '=' ex { // *** changed in 0.8, moved here in 0.9
try {
MSG_INFO(1) << "equation: " << *$1 << " = " << *$3 << endline;
$$ = new expression(equation(*$1, *$3, relational::equal, none, original));
} catch (std::exception &e) {
printerr(e.what());
$$ = new expression(equation()); // *** changed in 0.9
}
delete ($1); delete($3);
}
| QSTRING { // An equation label
// *** changed ARGUMENT to LABEL in 0.8, the scanner now returns the label without ", changed name to QSTRING in 1.2
try {
equation result = compiler[*$1]; // *** changed in 0.9
if (!compiler.is_lib(*$1)) // *** added in 1.2
result.settype(derived); // This equation is a duplicate *** added derived in 0.6
//result.setltxrepr(""); // *** added in 0.9
// *** moved autoalign functionality to class equation in 0.9
MSG_INFO(1) << "Label " << *$1 << " references equation " << result << endline;
$$ = new expression(result);
} catch (std::exception &e) {
printerr(e.what()); // *** changed in 0.8
$$ = new expression(equation()); // *** arguments 0,0 not necessary any more in 0.8
}
delete ($1);
}
| { nesting_level++; } expressionstm { // *** added in 0.8
$$ = $2;
exit_state(); // Leave the nested keyword processing state
nesting_level--;
}
;
// *** added in 0.6, changed to a strlist in 0.9
simplifications: WORD { $$ = new std::vector<std::string>; $$->push_back(*$1); delete($1); }
| simplifications ';' WORD {
// *** enter_state(scan_ex) is not necessary in between list items since 0.8
$1->push_back(*$3);
$$ = $1;
delete ($3);
}
;
// *** changed in 1.1, old syntax still valid for brevity
optvalarg: /* no argument to \val statement */
| '[' WORD ']' {
optnames.push_back(o_precision);
optstack::options->save(o_precision);
try {
option_value o;
o.expr = new ex(atoi($2->c_str()));
optstack::options->set(o_precision, o);
} catch (std::exception &e) {
printerr(e.what());
}
delete($2);
}
| '[' MACRO ']' { // *** changed unitlist to MACRO in 1.1
optnames.push_back(o_units);
exprvector u;
u.push_back(Unit($2->erase(0,1)));
optstack::options->save(o_units);
option_value o;
o.vec = Unit::create_conversions(u);
try {
optstack::options->set(o_units, o);
} catch (std::exception &e) {
printerr(e.what());
}
delete($2);
}
| '[' WORD ']' '[' MACRO ']' {
optnames.push_back(o_precision);
optstack::options->save(o_precision);
try {
option_value o;
o.expr = new ex(atoi($2->c_str()));
optstack::options->set(o_precision, o);
optnames.push_back(o_units);
exprvector u;
u.push_back(Unit($5->erase(0,1)));
o.vec = Unit::create_conversions(u);
optstack::options->save(o_units);
optstack::options->set(o_units, o);
} catch (std::exception &e) {
printerr(e.what());
}
delete($2); delete($5);
}
| '[' { save_opts = true; } keyvallist ']'
;
bopen: BOPEN { $$ = $1; }
| '{' { $$ = new std::string("{"); }
;
bclose: BCLOSE { $$ = $1; }
| '}' { $$ = new std::string("}"); }
;
exponent: DIGIT { // Beware: Writing x = 10^10 will result in x = 1 and a parsing error!
$$ = new expression(numeric($1->c_str()), *$1); delete ($1); }
| NAME {
$$ = new expression(compiler.getsym(*$1, *$1), *$1); delete ($1);
}
| bopen exp bclose { // *** changed '{' '}' to bopen bclose in 0.9
// Note that saying only exp here introduces 15 shift/reduce conflicts
checkbrackets(*$1, *$3);
//$2->setltxrepr(*$1 + $2->getltxrepr() + *$3);
$$ = $2;
delete($1); delete($3);
}
;
number: integer
| float
;
integer: DIGIT
| integer DIGIT { $1->append(*$2); $$ = $1; delete ($2); }
;
float: integer ',' integer {
if (*optstack::options->get(o_lang).str != "german") // *** variable lang moved to class eqc in 0.4
printerr("Wrong decimal marker in '" + *$1 + "," + *$3 + "'"); // *** changed in 0.8
$1->append("," + *$3); // *** changed to ',' in 0.9, this is corrected further up
$$ = $1;
delete ($3);
}
| integer '.' integer {
if (*optstack::options->get(o_lang).str == "german") // *** variable lang moved to class eqc in 0.4
printerr("Wrong decimal marker in '" + *$1 + "." + *$3 + "'"); // *** changed in 0.8
$1->append("." + *$3);
$$ = $1;
delete ($3);
}
;
// General definitions
// *** added keyvallist in 1.1
optarg: /* no argument */
| '[' WORD ']' { // *** changed in 1.1
if (nesting_level > 0) {
msg::warn(0) << "Warning: Assigning the label " << *$2
<< " to a nested equation will loose the label." << endline;
} else {
nextlabel = *$2;
}
delete($2);
}
| '[' { save_opts = true; } keyvallist ']'
;
asterisk: /* no asterisk */ {
$$ = new std::string("");
set_asterisk_statement(false); // *** added in 1.4.3
}
| '*' {
$$ = new std::string("*");
set_asterisk_statement(nesting_level == 0); // *** added in 1.4.3
}
;
// *** added optword in 1.1
optword: /* no argument */ { $$ = new std::string(""); }
| '[' WORD ']' { $$ = $2; }
;
// Added to parse \usepackage[left=3cm]{...} syntax. Note that ',' is not returned in this parsing mode
wordlist: WORD { $$ = $1; }
| wordlist WORD { $1->append(*$2); $$ = $1; delete($2); }
| wordlist '=' WORD { $1->append("="); $1->append(*$3); $$ = $1; delete($3); }
;
optpackageargs: /* no arguments */ { $$ = new std::string(""); }
| '[' wordlist ']' { $$ = $2; }
;
%%
int yyerror(const char *s) /* Called by ltxfileparse on error, *** changed to yyerror in 1.0 */
{
msg::error(0) << inputfname << ":" << linenumber << ": " << s;
return 0;
}