[go: up one dir, main page]

Menu

[r11]: / src / ltxfileparse.yy  Maximize  Restore  History

Download this file

1423 lines (1378 with data), 66.4 kB

/***************************************************************************
    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

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
  std::vector<expression> *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
%}
%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 <str> KWUNIT FRAC VALUE VALUEWITH LHSRHS NUMER DENOM EQEVAL EQEVALP TSERIES /* Expression statements */
%token CONSTANT FUNCTION MATRIX DEFFUNC DEFUNIT /* 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 bopen bclose
%type <expr>    ex exp exponent expressionstm
%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 /* *** 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        */
%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
              printeq(compiler[compiler.getprevious()], *$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 optword '{' WORD '}' {
            // TODO: multiple package arguments are not recognized!
            // *** 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 std::vector<expression>;
            $$->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(ex_to<equation>(*$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(ex_to<equation>(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());
              }
              // *** changed in 0.9
              // Note we set the type to original so that *$5 is not removed from the compiler!
              // *** changed that in 1.0 (was register_and_print(eq.settype(original), ...)
              equation eq = ex_to<equation>(result);
              $$ = register_and_print(eq, *$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
            // Note: The Latex representation is lost here
            if (!is_a<equation>(*$5)) {
              printerr("Equation expected");
              $$ = new expression();
            } else { // *** changed in 0.9
              $$ = register_and_print(ex_to<equation>(*$5).reverse(), *$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
                $$ = register_and_print(ex_to<equation>(*$5).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);
              //value.print(print_tree(std::cout));

              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);
          }
          | KWUNIT '{' exp '}' '{' exp '}' { // *** added in 1.4.3 to preserve latex representation
            $$ = new expression(*$3 * *$6);
            if (($3->testltxrepr()) && ($6->testltxrepr()))
                $$->setltxrepr(*$1 + "{" + $3->getltxrepr() + "}{" + $6->getltxrepr() + "}");
            delete($1); delete($3); delete($6);
          }
          | NUMER '{' exp '}' { // *** added in 0.8
            // Note: The Latex representation is lost here
            $$ = new expression($3->numer());
            delete ($3);
          }
          | DENOM '{' exp '}' { // *** added in 0.8
            // Note: The Latex representation is lost here
            $$ = new expression($3->denom());
            delete ($3);
          } // *** ROUND became a hard-coded function in 1.2
;
exp:      { enter_state(scan_ex); } ex { // *** added evalm() in 1.0
            try {
              $$ = new expression($2->evalm(), $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, $2->getltxrepr());
            }
          }
;
ex:       SYMBOL {
            $$ = new expression(compiler.getsym(*$1, *$1), *$1);
            delete ($1);
          }
          | 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 { // *** defined as an operator in 0.9
	    // *** changed to use matrices in 1.3.0
	    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 {
              /*if (is_a<ncsymbol>(*$1) || is_a<ncsymbol>(*$3)) { // *** added in 1.4.3
                MSG_INFO(0) << "Matrix multiplication" << endline;
                $$ = new expression(ncmul(*$1, *$3));
              } else  {
		MSG_INFO(0) << "Non-Matrix multiplication" << endline;*/
              $$ = 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);
            //$$->print(print_tree(std::cout));
          }
          | 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 '/' 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 { // *** included handling of equations in 0.9
            // One shift/reduce conflict with the preceding 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));
              /*$1->print(print_tree(std::cout));
              std::cout << " = ";
              $3->print(print_tree(std::cout));
              std::cout << std::endl;*/
              // *** added original in 0.6, changed equals definition in 0.9
              // *** moved autoalign functionality to class equation in 0.9
            } 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);
                std::vector<expression> 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);
                  std::vector<expression> 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; }
;

%%
int yyerror(const char *s)  /* Called by ltxfileparse on error, *** changed to yyerror in 1.0 */
     {
       msg::error(0) << inputfname << ":" << linenumber << ": " << s;
       return 0;
     }