/**
 * @file    L3Parser.ypp
 * @brief   Definition of the SBML Level 3 infix-to-ASTNode parser.
 * @author  Lucian Smith
 * 
 * <!--------------------------------------------------------------------------
 * This file is part of libSBML.  Please visit http://sbml.org for more
 * information about SBML, and the latest version of libSBML.
 *
 * Copyright (C) 2019 jointly by the following organizations:
 *     1. California Institute of Technology, Pasadena, CA, USA
 *     2. University of Heidelberg, Heidelberg, Germany
 *
 * Copyright (C) 2013-2018 jointly by the following organizations:
 *     1. California Institute of Technology, Pasadena, CA, USA
 *     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
 *     3. University of Heidelberg, Heidelberg, Germany
 *
 * Copyright (C) 2009-2013 jointly by the following organizations: 
 *     1. California Institute of Technology, Pasadena, CA, USA
 *     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
 *  
 * Copyright (C) 2006-2008 by the California Institute of Technology,
 *     Pasadena, CA, USA 
 *  
 * Copyright (C) 2002-2005 jointly by the following organizations: 
 *     1. California Institute of Technology, Pasadena, CA, USA
 *     2. Japan Science and Technology Agency, Japan
 * 
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation.  A copy of the license agreement is provided
 * in the file named "LICENSE.txt" included with this software distribution and
 * also available online as http://sbml.org/software/libsbml/license.html
 * ---------------------------------------------------------------------- -->*/

/**
 * This file is designed to be compiled by the 'bison' program with the
 * command:
 *
 * bison -v L3Parser.ypp -o L3Parser.cpp
 *
 * The '-v' creates a file called 'L3Parser.output' which can be examined
 * to piece together the explicit logic of the parser, and the -o specifies
 * the output file (L3Parser.cpp instead of the default L3Parser.tab.cpp)
 *
 * This command produces the file 'L3Parser.cpp' which is included
 * explicitly in the libsbml distribution.  Automatic creation of this
 * file is not yet incorporated into the CMake system; it must be
 * run by hand before change here will make it into the actual libsbml
 * library.
 *
 * Bison v2.4.2 was used to create the .cpp file when this file was
 * first created.  It does not rely on odd features of the bison
 * parser, so other versions should work fine.
 *
 * This file currently compiles with zero reduce/reduce errors and zero
 * shift/reduce warnings.
 */

%code top {

 /** @cond doxygenLibsbmlInternal */
}

%{
/**
 *
 * This file is AUTOMATICALLY GENERATED by the 'bison' program and
 * should not be modified directly.  It is created with the command
 *
 * bison -v L3Parser.ypp -o L3Parser.cpp
 *
 * from the L3Parser.ypp file.  Any modifications should be performed on
 * that file directly.
 *
 * This file defines an entire class, 'L3Parser', that does not appear
 * in any header file, as it is only useful in the context of the C functions
 * generated by the bison program.  Future versions of this parser may
 * use a version of bison that generates a parser object instead of C
 * functions, but for now, the only interface with the library is defined
 * in the L3Parser.h file.
 */

#include <sbml/common/libsbml-namespace.h>
#include <sbml/Model.h>
#include <sbml/math/L3Parser.h>
#include <sbml/math/L3ParserSettings.h>
#include <sbml/extension/ASTBasePlugin.h>
#include <sbml/util/util.h>

#include <sstream>
#include <set>
#ifdef LIBSBML_WITH_THREADSAFE_PARSER
#include <mutex>
#endif

LIBSBML_CPP_NAMESPACE_USE
LIBSBML_CPP_NAMESPACE_BEGIN
class SBMLVisitor;
class FormulaUnitsData;

#ifndef SWIG //Hide the entire class from SWIG
/**
 * @class L3Parser
 * @ingroup core
 * @brief Class providing functionality for the bison-generated parser.
 *
 * The L3Parser class is an internal class designed to hold the guts of the bison parser, plus
 * the lexer.  It is designed to be a global singleton object, because that's the cleanest way
 * I could find to incorporate functions into the bison framework.
 *
 * The functions declared in this file are defined in the file L3Parser.ypp, which
 * must be compiled by bison to create L3Parser.cpp, the file included in
 * libsbml.  For more details, see the L3Parser.ypp file.
 *
 * Within the various 'sbml_yylex*' functions that bison creates, functions
 * from the global 'l3p' object (of the L3Parser class) are used to calculate
 * necessary information for the parsing of the string, and to determine appropriate
 * error messages when things go wrong.
 * @internal
 */
class LIBSBML_EXTERN L3Parser
{
public:

  std::stringstream input;
  ASTNode* outputNode;
  std::string error;
  std::map<std::string, std::string*> words;
  long exponent;
  long denominator;

  //settings:
  L3ParserSettings defaultL3ParserSettings;
  const Model* model;
  ParseLogType_t parselog;
  bool collapseminus;
  bool parseunits;
  bool avocsymbol;
  const L3ParserSettings* currentSettings;
  bool strCmpIsCaseSensitive;
  bool modulol3v2;

  L3Parser();
  virtual ~L3Parser();

  //Functions needed for the parser and the lexer:
  /**
   * Compares 'name' against a list of known constants, and returns the particular
   * constant type (AST_CONSTANT_TRUE, AST_CONSTANT_PI, AST_NAME_TIME) associated
   * with that string (with case ignored).  For the mathematical constants
   * infinity and notanumber, AST_REAL is returned, and the parser must then
   * examine the string again to discover what to do with the ASTNode.
   */
  ASTNodeType_t getSymbolFor(const std::string& name) const;
  /**
   * Compares 'name' against a list of known functions allowed in the MathML
   * of SBML Level 2 and 3.  Multiple mappings of string->type are present,
   * so that (for example) both the strings 'acos' and 'arccos' return the
   * type AST_FUNCTION_ARCCOS.  "log" returns AST_FUNCTION_LOG, so when 
   * user preference is taken into consideration, 'name' must once again
   * be checked.
   */
  ASTNodeType_t getFunctionFor(const std::string& name) const;
  /**
   * This function creates an ASTNode that is a 'piecewise' function that
   * mimics the 'modulo' function 'x % y'.  It was modified from the
   * function of the same name in Copasi in its  CEvaluationNodeOperator 
   * class, which itself had modifications submitted by Frank Bergmann.  
   */
  ASTNode*      createModuloTree(ASTNode* x, ASTNode* y) const;
  /**
   * Sets the member variable 'collapseminus' to the provided boolean value.  Used in parsing
   * unary minuses.
   */
  void setCollapseMinus(bool collapse);
  /**
   * Sets the member variable 'parselog' to the provided enum.  Used in parsing
   * strings with the function "log" with a single argument.
   */
  void setParseLog(ParseLogType_t parseas);
  /**
   * Sets the member variable 'parseunits' to the provided boolean.  Used in
   * deciding whether strings that assign units to numbers (like "10 mL") 
   * are parsed correctly, or as errors.
   */
  void setParseUnits(bool units);
  /**
   * Sets the member variable 'avocsymbol' to the provided boolean. Used in
   * deciding whether to parse the string 'avogadro' as an ASTNode of type
   * AST_NAME_AVOGADRO or AST_NAME with the name 'avogadro'.
   */
  void setAvoCsymbol(bool avo);
  /**
   * Sets the member variable 'strCmpIsCaseSensitive' to the provided boolean. Used in
   * deciding whether to parse the built-in functions and variables caseless
   * or not.
   */
  void setComparisonCaseSensitivity(bool strcmp);
  /**
   * Compares the two strings, and returns 'true' if they are equivalent,
   * ignoring case.  Used in the parser and in the 'getSymbolFor' and 
   * 'getFunctionFor' functions.
   */
  bool l3StrCmp(const std::string& lhs, const std::string& rhs) const;
  /**
   * Sets the member variable 'modulol3v2' to the provided boolean value.  Used in parsing
   * the '%' symbol.
   */
  void setModuloL3v2(bool ml3v2);
  /**
   * Sets the member variable 'l3v2functions' to the provided boolean value.  Used in parsing
   * the functions added to the SBML spec in l3v2.
   */
  void setInput(const char* c);
  /**
   * Sets the error string so that it can be retrieved by the function 
   * 'SBML_getLastParseL3Error'.
   */
  void setError(const char* c);
  /**
   * Sets the error string so that it can be retrieved by the function 
   * 'SBML_getLastParseL3Error'.
   */
  void setError(const std::string& c);
  /**
   * Resets the L3Parser object, removing any error or input strings,
   * setting the output ASTNode to NULL, and resetting all parser settings
   * to that stored in the 'defaultL3ParserSettings' member variable.
   */
  void clear();
  /**
   * Returns the 'error' member variable, which is either empty or contains
   * the error message set from a 'setError' function.
   */
  std::string getError();
  
  /**
   * The bison parser needs string pointers to pass around from function to
   * function.  In order to not create too many of these objects, and to
   * ensure that they are properly deleted, the lexer calls this function
   * when it encounters a valid ID string to get a stable pointer that can 
   * be passed to the parser functions.
   * 
   * In this function, 'word' is looked up in a hash map of strings to 
   * string pointers; if it is not found, a new string pointer containing
   * the string is created, added to the hash, and returned.  If it is
   * found, the previously-created pointer is returned.
   */
  std::string* addWord(const std::string& word);
  
  /**
   * This function checks the provided ASTNode function to see if it is a 
   * known function with the wrong number of arguments.  If so, an error is set
   * (using the 'setError' function) and 'true' is returned.  If the
   * correct number of arguments is provided, 'false' is returned.
   */
  bool checkNumArguments(const ASTNode* function);

  /**
   * This function takes a 'lambda' function and changes any arguments
   * that might match predefined constants, and changes them back to normal
   * AST_NAME children, and changes any corresponding constants in the final
   * math function to also be AST_NAME. 
   */
  void fixLambdaArguments(const ASTNode* function);

  /**
   * This function checks the provided ASTNode function to see if it is a 
   * known function with the wrong number of arguments.  If so, an error is set
   * (using the 'setError' function) and 'true' is returned.  If the
   * correct number of arguments is provided, 'false' is returned.  It is used
   * for ASTNodes created from packages.
   */
  bool checkNumArgumentsForPackage(const ASTNode* function);

  /**
   * This function takes the given left and right ASTNodes, and combines them
   * with the given relational type, returning the combined node.
   */
  ASTNode* combineRelationalElements(ASTNode* left, ASTNode* right, ASTNodeType_t type);

  /**
   * Provides a copy of the default parser settings member variable.
   */
  L3ParserSettings getDefaultL3ParserSettings();

  /**
   * Change the default settings for this parser to the settings provided.  All subsequent
   * calls to parseL3Formula with no 'settings' argument will use these settings
   * instead of the defaults.
   */
  void setDefaultSettings(L3ParserSettings settings);

  /**
   * The generic parsing function for grammar lines that packages recognize, but not core.
   * When a package recognizes the 'type', it will parse and return the correct ASTNode.
   * If it does not recognize the 'type', or if the arguments are incorrect, NULL is returend.
   */
  virtual ASTNode* parsePackageInfix(L3ParserGrammarLineType_t type, 
    std::vector<ASTNode*> *nodeList = NULL, std::vector<std::string*> *stringList = NULL,
    std::vector<double> *doubleList = NULL) const;


  /**
   * If the either versions of the function @see SBML_parseL3Formula() returns NULL, an error 
   * is set internally which is accessible via this function.  The returned error will 
   * report the string it was trying to parse, which character it had parsed when it 
   * encountered the error, and what the error was.
   * 
   */
  static char* getLastParseL3Error();
};
#endif //The entire class should be invisible to SWIG, too.

LIBSBML_CPP_NAMESPACE_END

  using namespace std;

  int sbml_yylex(void);
  L3Parser* l3p = NULL;
#ifdef LIBSBML_WITH_THREADSAFE_PARSER
  std::mutex l3p_mutex;
#endif


LIBSBML_CPP_NAMESPACE_BEGIN
L3Parser* L3Parser_getInstance()
{
  if (l3p == NULL)
  {
    l3p = new L3Parser();
    std::atexit(SBML_deleteL3Parser);
  }
  return l3p;
}
LIBSBML_CPP_NAMESPACE_END

  void sbml_yyerror(char const *);
  int sbml_yylloc_first_position = 1;
  int sbml_yylloc_last_position = 1;

#ifdef __BORLANDC__
#undef DOUBLE
#endif

%}

/*Bison declarations */
%union {
  ASTNode* astnode;
  char character;
  const string* word;
  double numdouble;
  long   numlong;
  double mantissa;
  long   rational;
}


%type <astnode> node nodelist nodesemicolonlist number

/* Ensure ASTNode objects allocated with new are cleaned up if parsing is unsuccessful */
%destructor { delete($$); } node
%destructor { delete($$); } nodelist
%destructor { delete($$); } nodesemicolonlist
%destructor { delete($$); } number

%left '&' '|'         /* Boolean functions and, or */
%left '<' '>' '=' '!' /* Boolean comparison functions (gt, lt, eq, etc.) */
%left '-' '+'
%left '*' '/' '%'
%left NOT NEG UPLUS   /* Boolean negation (unary not), regular negation (unary minus), and unary plus (a no-op) */
%right '^'            /* exponentiation        */
%left '['

%token YYEOF 0 "end of string"
%token  <numdouble> DOUBLE "number"
%token  <numlong> INTEGER "integer"
%token  <mantissa> E_NOTATION "number in e-notation form"
%token  <rational> RATIONAL "number in rational notation"
%token  <word> SYMBOL "element name"

%define api.prefix {sbml_yy}
%debug
%error-verbose
%% /* The grammar: */

input:          /* empty */
        |       node {l3p->outputNode = $1;}
        |       error {}
        |       node error {delete $1;}
        ;

node:           number {$$ = $1;}
        |       SYMBOL {
                   $$ = new ASTNode();
                   string name(*$1);
                   $$->setName(name.c_str());
                   if (l3p->model == NULL ||
                       (l3p->model->getSpecies(name) == NULL
                        && l3p->model->getCompartment(name) == NULL
                        && l3p->model->getReaction(name) == NULL
                        && l3p->model->getParameter(name) == NULL
                        && l3p->model->getSpeciesReference(name) == NULL)) {
                     //The symbol is not used in any other mathematical context in the SBML model, so we can see if it matches a list of pre-defined names
                     ASTNodeType_t type = l3p->getSymbolFor(name);
                     if (type != AST_UNKNOWN) $$->setType(type);
                     if (type==AST_REAL) {
                       if (l3p->l3StrCmp(name, "inf"))          $$->setValue(numeric_limits<double>::infinity());
                       if (l3p->l3StrCmp(name, "infinity"))     $$->setValue(numeric_limits<double>::infinity());
                       if (l3p->l3StrCmp(name, "nan"))          $$->setValue(numeric_limits<double>::quiet_NaN());
                       if (l3p->l3StrCmp(name, "notanumber"))   $$->setValue(numeric_limits<double>::quiet_NaN());
                     }
                     if (!l3p->avocsymbol && type==AST_NAME_AVOGADRO) {
                       //Don't parse 'avogadro' as a csymbol.
                       $$->setName(name.c_str());
                       $$->setType(AST_NAME);
                     }
                   }
        }
        |       '(' node ')'  {$$ = $2;}
        |       node '^' node {$$ = new ASTNode(AST_POWER); $$->addChild($1); $$->addChild($3);}
        |       node '*' node {
                  if ($1->getType()==AST_TIMES) {
                    $$ = $1;
                    $$->addChild($3);
                  }
                  else {
                    $$ = new ASTNode(AST_TIMES);
                    $$->addChild($1);
                    $$->addChild($3);
                  }
                }
        |       node '+' node {
                  if ($1->getType()==AST_PLUS) {
                    $$ = $1;
                    $$->addChild($3);
                  }
                  else {
                    $$ = new ASTNode(AST_PLUS);
                    $$->addChild($1);
                    $$->addChild($3);
                  }
                }
        |       node '/' node {$$ = new ASTNode(AST_DIVIDE); $$->addChild($1); $$->addChild($3);}
        |       node '-' node {$$ = new ASTNode(AST_MINUS); $$->addChild($1); $$->addChild($3);}
        |       node '%' node {
                  if (l3p->modulol3v2) {
                    $$ = new ASTNode(AST_FUNCTION_REM);
                    $$->addChild($1);
                    $$->addChild($3);
                  }
                  else {
                    $$ = l3p->createModuloTree($1, $3);
                  }
                }
        |       '+' node %prec UPLUS {$$ = $2;}
        |       '-' node %prec NEG {
                  if (l3p->collapseminus) {
                    if ($2->getType()==AST_REAL) {
                      $$ = $2;
                      $$->setValue(-$$->getReal());
                    }
                    else if ($2->getType()==AST_INTEGER) {
                      $$ = $2;
                      $$->setValue(-$$->getInteger());
                    }
                    else if ($2->getType()==AST_REAL_E) {
                      $$ = $2;
                      $$->setValue(-$$->getMantissa(), $$->getExponent());
                    }
                    else if ($2->getType()==AST_RATIONAL) {
                      $$ = $2;
                      $$->setValue(-$$->getNumerator(), $$->getDenominator());
                    }
                    else if ($2->getType()==AST_MINUS && $2->getNumChildren()==1) {
                      $$ = $2->getChild(0);
                      $2->removeChild(0);
                      delete $2;
                    }
                    else {
                      $$ = new ASTNode(AST_MINUS);    
                      $$->addChild($2);
                    }
                  }
                  else {
                    $$ = new ASTNode(AST_MINUS);
                    $$->addChild($2);
                  }
                }
        |       node '>' node {$$ = l3p->combineRelationalElements($1, $3, AST_RELATIONAL_GT);}
        |       node '<' node {$$ = l3p->combineRelationalElements($1, $3, AST_RELATIONAL_LT);}
        |       node '>' '=' node {$$ = l3p->combineRelationalElements($1, $4, AST_RELATIONAL_GEQ);}
        |       node '<' '=' node {$$ = l3p->combineRelationalElements($1, $4, AST_RELATIONAL_LEQ);}
        |       node '=' '=' node {$$ = l3p->combineRelationalElements($1, $4, AST_RELATIONAL_EQ);}
        |       node '!' '=' node {$$ = l3p->combineRelationalElements($1, $4, AST_RELATIONAL_NEQ);}
        |       node '<' '>' node {$$ = l3p->combineRelationalElements($1, $4, AST_RELATIONAL_NEQ);}
        |       node '>' '<' node {$$ = l3p->combineRelationalElements($1, $4, AST_RELATIONAL_NEQ);}
        |       node '&' '&' node {
                  if ($1->getType()==AST_LOGICAL_AND) {
                    $$ = $1;
                    $$->addChild($4);
                  }
                  else {
                    $$ = new ASTNode(AST_LOGICAL_AND);
                    $$->addChild($1);
                    $$->addChild($4);
                  }
                }
        |       node '|' '|' node {
                  if ($1->getType()==AST_LOGICAL_OR) {
                    $$ = $1;
                    $$->addChild($4);
                  }
                  else {
                    $$ = new ASTNode(AST_LOGICAL_OR);
                    $$->addChild($1);
                    $$->addChild($4);
                  }
                }
        |       '!' node %prec NOT {$$ = new ASTNode(AST_LOGICAL_NOT); $$->addChild($2);}
        |       SYMBOL '(' ')' {
                   $$ = new ASTNode(AST_FUNCTION);
                   string name(*$1);
                   $$->setName($1->c_str());
                   if (l3p->model == NULL || l3p->model->getFunctionDefinition(name) == NULL) {
                     //The symbol is not used in any other mathematical context in the SBML model, so we can see if it matches a list of pre-defined names
                     ASTNodeType_t type = l3p->getFunctionFor(name);
                     if (type != AST_UNKNOWN) $$->setType(type);
                     if (type == AST_LAMBDA) l3p->fixLambdaArguments($$);
                     if (l3p->checkNumArguments($$)) YYABORT;
                   }
        }
        |       SYMBOL '(' nodelist ')' {
                   $$ = $3;
                   string name(*$1);
                   $$->setName($1->c_str());
                   $$->setType(AST_FUNCTION);
                   ASTNodeType_t type = l3p->getFunctionFor(name);
                   if (l3p->model == NULL || l3p->model->getFunctionDefinition(name) == NULL) {
                     //The symbol is not used in any other mathematical context in the SBML model, so we can see if it matches a list of pre-defined names
                     if (type != AST_UNKNOWN) $$->setType(type);
                     if (type==AST_FUNCTION_ROOT && l3p->l3StrCmp(name, "sqrt")) {
                       //If the number of arguments is wrong, set an error now instead of waiting for later.
                       if ($$->getNumChildren() != 1) {
                         l3p->setError("The function 'sqrt' takes exactly one argument.");
                         delete $$;
                         YYABORT;
                       }
                       //Add a '2' node before the existing child.
                       ASTNode* int2 = new ASTNode(AST_INTEGER);
                       int2->setValue(2);
                       $$->prependChild(int2);
                     }
                     if (type==AST_FUNCTION_POWER && l3p->l3StrCmp(name, "sqr")) {
                       //Add a '2' node after the existing child.
                       ASTNode* int2 = new ASTNode(AST_INTEGER);
                       int2->setValue(2);
                       $$->addChild(int2);
                     }
                     if (type==AST_FUNCTION_LOG && l3p->l3StrCmp(name, "log10")) {
                       //Add a '10' node before the existing child.
                       ASTNode* int10 = new ASTNode(AST_INTEGER);
                       int10->setValue(10);
                       $$->prependChild(int10);
                     }
                     if (type==AST_FUNCTION_LOG && l3p->l3StrCmp(name, "log")) {
                       //If there is exactly one argument, change it to log10, ln, or give a special error message, depending on a user setting (by default, parse to log10).
                       if ($$->getNumChildren() == 1) {
                         ASTNode* int10;
                         switch(l3p->parselog) {
                         case L3P_PARSE_LOG_AS_LOG10:
                           int10 = new ASTNode(AST_INTEGER);
                           int10->setValue(10);
                           $$->prependChild(int10);
                           break;
                         case L3P_PARSE_LOG_AS_LN:
                           $$->setType(AST_FUNCTION_LN);
                           break;
                         case L3P_PARSE_LOG_AS_ERROR:
                           l3p->setError("Writing a function as 'log(x)' was legal in the L1 parser, but translated as the natural log, not the base-10 log.  This construct is disallowed entirely as being ambiguous, and you are encouraged instead to use 'ln(x)', 'log10(x)', or 'log(base, x)'.");
                           delete $$;
                           YYABORT;
                         }
                       }
                     }
                   }
                   if (type == AST_LAMBDA) l3p->fixLambdaArguments($$);
                   if (l3p->checkNumArguments($$)) YYABORT;
        }
        |       node '[' nodelist ']' {
                  vector<ASTNode*> allnodes;
                  allnodes.push_back($1);
                  allnodes.push_back($3);
                  $$ = l3p->parsePackageInfix(INFIX_SYNTAX_NAMED_SQUARE_BRACKETS, &allnodes);
                  if ($$ == NULL) {
                    l3p->setError("No package is enabled that can interpret vectors, so formulas of the form 'x[y]' are disallowed.");
                    delete($1);
                    delete($3);
                    YYERROR;
                  }
                  if (l3p->checkNumArgumentsForPackage($$)) YYABORT;
                }
        |       node '[' ']' {
                  vector<ASTNode*> allnodes;
                  allnodes.push_back($1);
                  $$ = l3p->parsePackageInfix(INFIX_SYNTAX_NAMED_SQUARE_BRACKETS, &allnodes);
                  if ($$ == NULL) {
                    l3p->setError("No package is enabled that can interpret vectors, so formulas of the form 'x[]' are disallowed.");
                    delete ($1);
                    YYERROR;
                  }
                  if (l3p->checkNumArgumentsForPackage($$)) YYABORT;
                }
        |       '{' nodelist '}'  {
                  vector<ASTNode*> allnodes;
                  allnodes.push_back($2);
                  $$ = l3p->parsePackageInfix(INFIX_SYNTAX_CURLY_BRACES, &allnodes);
                  if ($$ == NULL) {
                    l3p->setError("No package is enabled that can interpret curly braces, so formulas of the form '{x, y}' are disallowed.");
                    delete($2);
                    YYERROR;
                  }
                  if (l3p->checkNumArgumentsForPackage($$)) YYABORT;
                }
        |       '{' nodesemicolonlist '}'  {
                  vector<ASTNode*> allnodes;
                  allnodes.push_back($2);
                  $$ = l3p->parsePackageInfix(INFIX_SYNTAX_CURLY_BRACES_SEMICOLON, &allnodes);
                  if ($$ == NULL) {
                    l3p->setError("No package is enabled that can interpret curly braces with semicolon-delimited lists, so formulas of the form '{x, y; p, q}' are disallowed.");
                    delete($2);
                    YYERROR;
                  }
                  if (l3p->checkNumArgumentsForPackage($$)) YYABORT;
                }
        |       '{' '}'  {
                  $$ = l3p->parsePackageInfix(INFIX_SYNTAX_CURLY_BRACES);
                  if ($$ == NULL) {
                    l3p->setError("No package is enabled that can interpret empty curly braces, so formulas of the form '{}' are disallowed.");
                    YYERROR;
                  }
                  if (l3p->checkNumArgumentsForPackage($$)) YYABORT;
                }
        ;

number:         DOUBLE {
                  $$ = new ASTNode(); 
                  $$->setValue($1); 
//                  if(l3p->useDimensionless) {
//                    $$->setUnits("dimensionless");
//                  }
                } 
        |       E_NOTATION {
                  $$ = new ASTNode();
                  $$->setValue($1, l3p->exponent); 
//                  if(l3p->useDimensionless) {
//                    $$->setUnits("dimensionless");
//                  }
                }
        |       INTEGER {
                  $$ = new ASTNode(); 
                  $$->setValue($1); 
//                  if(l3p->useDimensionless) {
//                    $$->setUnits("dimensionless");
//                  }
                }
        |       RATIONAL {
                  $$ = new ASTNode(); 
                  $$->setValue($1, l3p->denominator);
//                  if(l3p->useDimensionless) {
//                    $$->setUnits("dimensionless");
//                  }
                }
        |       number SYMBOL {
                  $$ = $1;
                  if ($$->getUnits() != "") {
                    l3p->setError("Can't use " + *$2 + " as a unit, because the unit is already set for this number.");
                    delete $$;
                    YYERROR;
                  }
                  if (!l3p->parseunits) {
                    l3p->setError("The ability to associate units with numbers has been disabled.");
                    delete $$;
                    YYERROR;
                  }
                  $$->setUnits(*$2);
               }
        ;

nodelist:       node {$$ = new ASTNode(AST_FUNCTION); $$->addChild($1);}
        |       nodelist ',' node {$$ = $1;  $$->addChild($3);}
        ;

nodesemicolonlist: nodelist ';' nodelist {$$ = new ASTNode(AST_FUNCTION); $$->addChild($1); $$->addChild($3);}
        |       nodesemicolonlist ';' nodelist {$$ = $1;  $$->addChild($3);}
        ;

%%


void sbml_yyerror(char const *s)
{
  l3p->setError(s);
}

int sbml_yylex(void)
{
  char cc = 0;
  l3p->input.get(cc);

  //If we're done with the input:
  if (l3p->input.eof()) {
    return 0;
  }
  //If something else went wrong:
  if (!l3p->input.good()) {
    l3p->setError("The input is no longer good, for an unknown reason.");
    return -1;
  }

  // Skip white space.
  while ((cc == ' ' || cc == '\t' || cc == '\n' || cc == '\r')
         && !l3p->input.eof()) {
    l3p->input.get(cc);
  }
  if (l3p->input.eof()) return 0;

  // Parse words
  if (isalpha(cc) || cc == '_') {
    string word;
    while ((isalpha(cc) || isdigit(cc) || cc == '_') && !l3p->input.eof()) {
      word += cc;
      l3p->input.get(cc);
    }
    if (!l3p->input.eof()) {
      l3p->input.unget();
    }
    sbml_yylval.word = l3p->addWord(word);
    //cout << "\tRead word '" << word << "'." << endl;
    return SYMBOL;
  }

  // Parse numbers
  if (isdigit(cc) || (cc=='.' && isdigit(l3p->input.peek()))) {
    double number;
    long numlong;
    l3p->input.unget();
    streampos numbegin = l3p->input.tellg();
    l3p->input >> numlong;
    l3p->input.clear();
    l3p->input.seekg(numbegin);
    l3p->input >> number;
    if (l3p->input.fail()) {
      //The only reason I know of for this to happen is that there was a number followed by an 'e'.
      l3p->input.clear(); //clear the error.
      l3p->input.seekg(numbegin);
      string failnum = "";
      cc = l3p->input.get();
      while (l3p->input.good() && (isdigit(cc) || cc=='.')) {
        failnum += cc;
        cc = l3p->input.get();
      }
      if (cc=='e' || cc=='E') {
        l3p->input.unget();
        //We're going to call yylex recursively here, so we need to swap out l3p->input
        streampos numend = l3p->input.tellg();
        string tempinput = l3p->input.str();
        l3p->input.str(failnum);
        int ret = sbml_yylex();
        l3p->input.str(tempinput);
        l3p->input.clear();
        l3p->input.seekg(numend);
        return ret;
      }
      //Something weird went wrong; give up.
      return -1;
    }
    streampos numend = l3p->input.tellg();
    l3p->input.clear();
    l3p->input.seekg(numbegin);
    bool decimal = false;
    bool e = false;
    bool last_e = false;
    while (l3p->input.tellg() != numend) {
      cc = l3p->input.get();
      if (cc=='.') {
        decimal = true;
      }
      if (cc=='e' || cc=='E') {
        e = true;
        last_e = true;
      }
      else {
        last_e = false;
      }
    }
    if (last_e) {
       e = false;
       l3p->input.unget();
    }
    if (!decimal && !e && number == static_cast<double>(numlong) && numlong <= SBML_INT_MAX) {
      sbml_yylval.numlong = numlong;
      return INTEGER;
    }
    if (!e) {
      sbml_yylval.numdouble = number;
      return DOUBLE;
    }
    l3p->input.clear();
    l3p->input.seekg(numbegin);
    cc = l3p->input.get();
    string mantissa = "";
    while (l3p->input.tellg() != numend && (isdigit(cc) || cc=='.')) {
      mantissa += cc;
      cc = l3p->input.get();
    }
    if (cc=='e' || cc=='E') {
      if (l3p->input.peek()=='+') {
        cc = l3p->input.get();
      }
      bool isneg = false;
      if (l3p->input.peek()=='-') {
        cc = l3p->input.get();
        isneg = true;
      }
      l3p->input >> numlong;
      if (isneg) {
        numlong = -numlong;
      }
      stringstream mantissastr;
      mantissastr.str(mantissa);
      mantissastr >> number;
      l3p->exponent = numlong;
      sbml_yylval.mantissa = number;
      return E_NOTATION;
    }
    else {
      assert(false); //How did this happen?
      //This is an error condition, but parsing the value as a double should be sufficient.
      sbml_yylval.numdouble = number;
      return DOUBLE;
    }
  }
  else if (cc=='(') {
    streampos rationalbegin = l3p->input.tellg();
    cc = l3p->input.get();
    if ((cc== '-' && isdigit(l3p->input.peek())) || isdigit(cc)) {
      l3p->input.unget();
      long numerator;
      l3p->input >> numerator;
      cc = l3p->input.get();
      if (cc=='/') {
        cc = l3p->input.get();
        if ((cc== '-' && isdigit(l3p->input.peek())) || isdigit(cc)) {
          l3p->input.unget();
          long denominator;
          l3p->input >> denominator; 
          cc = l3p->input.get();
          if (cc==')') {
            //Actually a rational number!
            sbml_yylval.rational = numerator;
            l3p->denominator = denominator;
            return RATIONAL;
          }
        }
      }
    }
    l3p->input.clear();
    l3p->input.seekg(rationalbegin, ios::beg); // Not actually a rational number.
    return '(';
  }

  //Otherwise, return the character we are looking at (like '*', '+', etc.
  return cc;
}

L3Parser::L3Parser()
  : input()
  , outputNode(NULL)
  , error()
  , words()
  , exponent(0)
  , denominator(0)
  , defaultL3ParserSettings()
  , model(NULL)
  , parselog(L3P_PARSE_LOG_AS_LOG10)
  , collapseminus(L3P_EXPAND_UNARY_MINUS)
  , parseunits(L3P_PARSE_UNITS)
  , avocsymbol(L3P_AVOGADRO_IS_CSYMBOL)
  , strCmpIsCaseSensitive(L3P_COMPARE_BUILTINS_CASE_SENSITIVE)
  , modulol3v2(L3P_MODULO_IS_PIECEWISE)
{
}

L3Parser::~L3Parser ()
{
  for (map<string, string*>::iterator word = words.begin(); word != words.end(); word++) {
    delete (*word).second;
  }
}

ASTNodeType_t L3Parser::getSymbolFor(const string& name) const
{
  if (l3StrCmp(name, "true"))         return AST_CONSTANT_TRUE;
  if (l3StrCmp(name, "false"))        return AST_CONSTANT_FALSE;
  if (l3StrCmp(name, "pi"))           return AST_CONSTANT_PI;
  if (l3StrCmp(name, "exponentiale")) return AST_CONSTANT_E;
  if (l3StrCmp(name, "avogadro"))     return AST_NAME_AVOGADRO;
  if (l3StrCmp(name, "time"))         return AST_NAME_TIME;
  if (l3StrCmp(name, "inf"))          return AST_REAL;
  if (l3StrCmp(name, "infinity"))     return AST_REAL;
  if (l3StrCmp(name, "nan"))          return AST_REAL;
  if (l3StrCmp(name, "notanumber"))   return AST_REAL;
 
  return currentSettings->getPackageSymbolFor(name);
}

ASTNodeType_t L3Parser::getFunctionFor(const string& name) const
{
  if (l3StrCmp(name, "abs"))      return AST_FUNCTION_ABS;
  if (l3StrCmp(name, "acos"))     return AST_FUNCTION_ARCCOS;
  if (l3StrCmp(name, "arccos"))   return AST_FUNCTION_ARCCOS;
  if (l3StrCmp(name, "acosh"))    return AST_FUNCTION_ARCCOSH;
  if (l3StrCmp(name, "arccosh"))  return AST_FUNCTION_ARCCOSH;
  if (l3StrCmp(name, "acot"))     return AST_FUNCTION_ARCCOT;
  if (l3StrCmp(name, "arccot"))   return AST_FUNCTION_ARCCOT;
  if (l3StrCmp(name, "acoth"))    return AST_FUNCTION_ARCCOTH;
  if (l3StrCmp(name, "arccoth"))  return AST_FUNCTION_ARCCOTH;
  if (l3StrCmp(name, "acsc"))     return AST_FUNCTION_ARCCSC;
  if (l3StrCmp(name, "arccsc"))   return AST_FUNCTION_ARCCSC;
  if (l3StrCmp(name, "acsch"))    return AST_FUNCTION_ARCCSCH;
  if (l3StrCmp(name, "arccsch"))  return AST_FUNCTION_ARCCSCH;
  if (l3StrCmp(name, "asec"))     return AST_FUNCTION_ARCSEC;
  if (l3StrCmp(name, "arcsec"))   return AST_FUNCTION_ARCSEC;
  if (l3StrCmp(name, "asech"))    return AST_FUNCTION_ARCSECH;
  if (l3StrCmp(name, "arcsech"))  return AST_FUNCTION_ARCSECH;
  if (l3StrCmp(name, "asin"))     return AST_FUNCTION_ARCSIN;
  if (l3StrCmp(name, "arcsin"))   return AST_FUNCTION_ARCSIN;
  if (l3StrCmp(name, "asinh"))    return AST_FUNCTION_ARCSINH;
  if (l3StrCmp(name, "arcsinh"))  return AST_FUNCTION_ARCSINH;
  if (l3StrCmp(name, "atan"))     return AST_FUNCTION_ARCTAN;
  if (l3StrCmp(name, "arctan"))   return AST_FUNCTION_ARCTAN;
  if (l3StrCmp(name, "atanh"))    return AST_FUNCTION_ARCTANH;
  if (l3StrCmp(name, "arctanh"))  return AST_FUNCTION_ARCTANH;
  if (l3StrCmp(name, "ceil"))     return AST_FUNCTION_CEILING;
  if (l3StrCmp(name, "ceiling"))  return AST_FUNCTION_CEILING;
  if (l3StrCmp(name, "cos"))      return AST_FUNCTION_COS;
  if (l3StrCmp(name, "cosh"))     return AST_FUNCTION_COSH;
  if (l3StrCmp(name, "cot"))      return AST_FUNCTION_COT;
  if (l3StrCmp(name, "coth"))     return AST_FUNCTION_COTH;
  if (l3StrCmp(name, "csc"))      return AST_FUNCTION_CSC;
  if (l3StrCmp(name, "csch"))     return AST_FUNCTION_CSCH;
  if (l3StrCmp(name, "delay"))    return AST_FUNCTION_DELAY;
  if (l3StrCmp(name, "exp"))      return AST_FUNCTION_EXP;
  if (l3StrCmp(name, "factorial")) return AST_FUNCTION_FACTORIAL;
  if (l3StrCmp(name, "floor"))    return AST_FUNCTION_FLOOR;
  if (l3StrCmp(name, "lambda"))   return AST_LAMBDA;
  if (l3StrCmp(name, "log"))      return AST_FUNCTION_LOG;
  if (l3StrCmp(name, "ln"))       return AST_FUNCTION_LN;
  if (l3StrCmp(name, "log10"))    return AST_FUNCTION_LOG;
  if (l3StrCmp(name, "piecewise")) return AST_FUNCTION_PIECEWISE;
  if (l3StrCmp(name, "power"))    return AST_POWER;
  if (l3StrCmp(name, "pow"))      return AST_POWER;
  if (l3StrCmp(name, "sqr"))      return AST_FUNCTION_POWER;
  if (l3StrCmp(name, "sqrt"))     return AST_FUNCTION_ROOT;
  if (l3StrCmp(name, "root"))     return AST_FUNCTION_ROOT;
  if (l3StrCmp(name, "sec"))      return AST_FUNCTION_SEC;
  if (l3StrCmp(name, "sech"))     return AST_FUNCTION_SECH;
  if (l3StrCmp(name, "sin"))      return AST_FUNCTION_SIN;
  if (l3StrCmp(name, "sinh"))     return AST_FUNCTION_SINH;
  if (l3StrCmp(name, "tan"))      return AST_FUNCTION_TAN;
  if (l3StrCmp(name, "tanh"))     return AST_FUNCTION_TANH;
  if (l3StrCmp(name, "and"))      return AST_LOGICAL_AND;
  if (l3StrCmp(name, "not"))      return AST_LOGICAL_NOT;
  if (l3StrCmp(name, "or"))       return AST_LOGICAL_OR;
  if (l3StrCmp(name, "xor"))      return AST_LOGICAL_XOR;
  if (l3StrCmp(name, "eq"))       return AST_RELATIONAL_EQ;
  if (l3StrCmp(name, "equals"))   return AST_RELATIONAL_EQ;
  if (l3StrCmp(name, "geq"))      return AST_RELATIONAL_GEQ;
  if (l3StrCmp(name, "gt"))       return AST_RELATIONAL_GT;
  if (l3StrCmp(name, "leq"))      return AST_RELATIONAL_LEQ;
  if (l3StrCmp(name, "lt"))       return AST_RELATIONAL_LT;
  if (l3StrCmp(name, "neq"))      return AST_RELATIONAL_NEQ;
  if (l3StrCmp(name, "divide"))   return AST_DIVIDE;
  if (l3StrCmp(name, "minus"))    return AST_MINUS;
  if (l3StrCmp(name, "plus"))     return AST_PLUS;
  if (l3StrCmp(name, "times"))    return AST_TIMES;

  return currentSettings->getPackageFunctionFor(name);
}

ASTNode* L3Parser::createModuloTree(ASTNode* x, ASTNode* y) const
{
  if (x==NULL || y==NULL) return NULL;
  ASTNode* pASTNode = new ASTNode(); 
  //The following code was lifted wholesale from Copasi's  CEvaluationNodeOperator::createModuloTree.  Thanks, Copasi!

  // Frank noticed that this should actually be implemented as a
  // piecewise function because if one of the arguments is
  // negative, the definition is different
  pASTNode->setType(AST_FUNCTION_PIECEWISE);
  // the first child is the true branch
  // x%y -> x-ceil(x/y)*y
  ASTNode* pASTNodeTrue = new ASTNode();
  pASTNodeTrue->setType(AST_MINUS);
  ASTNode* tmpASTNode = new ASTNode(AST_DIVIDE);
  tmpASTNode->addChild(x);
  tmpASTNode->addChild(y);
  ASTNode* tmpASTNode2 = new ASTNode(AST_FUNCTION_CEILING);
  tmpASTNode2->addChild(tmpASTNode);
  tmpASTNode = new ASTNode(AST_TIMES);
  tmpASTNode->addChild(new ASTNode(*y));
  tmpASTNode->addChild(tmpASTNode2);
  pASTNodeTrue->addChild(new ASTNode(*x));
  pASTNodeTrue->addChild(tmpASTNode);
  pASTNode->addChild(pASTNodeTrue);
  // now comes the condition
  // if exactly one of the arguments to modulo is a negative number
  // we use the ceil branch, else we use the floor branch
  // x < 0 xor y < 0
  // xor
  ASTNode* pASTNodeCondition = new ASTNode();
  pASTNodeCondition->setType(AST_LOGICAL_XOR);
  // x < 0
  // <
  tmpASTNode = new ASTNode(AST_RELATIONAL_LT);
  // x
  tmpASTNode->addChild(new ASTNode(*x));
  // 0
  tmpASTNode2 = new ASTNode(AST_INTEGER);
  tmpASTNode2->setValue(0);
  tmpASTNode->addChild(tmpASTNode2);
  pASTNodeCondition->addChild(tmpASTNode);
  // y < 0
  // <
  tmpASTNode = new ASTNode(AST_RELATIONAL_LT);
  // y
  tmpASTNode->addChild(new ASTNode(*y));
  // 0
  tmpASTNode2 = new ASTNode(AST_INTEGER);
  tmpASTNode2->setValue(0);
  tmpASTNode->addChild(tmpASTNode2);
  pASTNodeCondition->addChild(tmpASTNode);
  pASTNode->addChild(pASTNodeCondition);
  // last is the false branch
  // x%y -> x-floor(x/y)*y
  ASTNode* pASTNodeFalse = new ASTNode();
  pASTNodeFalse->setType(AST_MINUS);
  tmpASTNode = new ASTNode(AST_DIVIDE);
  tmpASTNode->addChild(new ASTNode(*x));
  tmpASTNode->addChild(new ASTNode(*y));
  tmpASTNode2 = new ASTNode(AST_FUNCTION_FLOOR);
  tmpASTNode2->addChild(tmpASTNode);
  tmpASTNode = new ASTNode(AST_TIMES);
  tmpASTNode->addChild(new ASTNode(*y));
  tmpASTNode->addChild(tmpASTNode2);
  pASTNodeFalse->addChild(new ASTNode(*x));
  pASTNodeFalse->addChild(tmpASTNode);
  pASTNode->addChild(pASTNodeFalse);
  return pASTNode;
}

void L3Parser::setParseLog(ParseLogType_t parseas)
{
  parselog = parseas;
}

void L3Parser::setCollapseMinus(bool collapse)
{
  collapseminus = collapse;
}

void L3Parser::setParseUnits(bool units)
{
  parseunits = units;
}

void L3Parser::setAvoCsymbol(bool avo)
{
  avocsymbol = avo;
}

void L3Parser::setComparisonCaseSensitivity(bool strcmp)
{
  strCmpIsCaseSensitive = strcmp;
}

void L3Parser::setModuloL3v2(bool ml3v2)
{
  modulol3v2 = ml3v2;
}

bool L3Parser::l3StrCmp(const string& lhs, const string& rhs) const
{
  if (strCmpIsCaseSensitive) {
    return lhs==rhs;
  }
  if (lhs.size() != rhs.size()) return false;

  for (size_t i = 0; i < lhs.size(); ++i) {
    if (toupper(lhs[i]) != toupper(rhs[i])) return false;
  }
  return true;

}

void L3Parser::setInput(const char* c)
{
  input.clear();
  if (c == NULL) 
  {
    setError("NULL input.");
    return;
  }
  input.str(c);
}

void L3Parser::setError(const char* c)
{
  if (c == NULL) return;
  string err = c;
  setError(err);
}

void L3Parser::setError(const string& c)
{
  stringstream err;
  streampos position = input.tellg();
  if (position== (streampos)-1) {
    position = input.str().size();
  }
  err << "Error when parsing input '" << input.str() << "' at position " << position << ":  " << c;
  error = err.str();
}

void L3Parser::clear()
{
  input.clear();
  input.str("");
  outputNode = NULL;
  error = "";
  model = defaultL3ParserSettings.getModel();
  parselog = defaultL3ParserSettings.getParseLog();
  collapseminus = defaultL3ParserSettings.getParseCollapseMinus();
  parseunits = defaultL3ParserSettings.getParseUnits();
  avocsymbol = defaultL3ParserSettings.getParseAvogadroCsymbol();
  strCmpIsCaseSensitive = defaultL3ParserSettings.getComparisonCaseSensitivity();
  modulol3v2 = defaultL3ParserSettings.getParseModuloL3v2();
}

string L3Parser::getError()
{
  return error;
}
    
string* L3Parser::addWord(const std::string& word)
{
  //cout << "The word is: " << word << endl;
  map<string, string*>::iterator found = words.find(word);
  if (found != words.end()) {
    return (*found).second;
  }
  else {
    string* savedword = new string(word);
    words.insert(make_pair(word, savedword));
    return savedword;
  }
}

bool L3Parser::checkNumArguments(const ASTNode* function)
{
  unsigned int children = function->getNumChildren();
  stringstream error;
  string name = "";
  if (function->isOperator()) {
    name = function->getOperatorName();
  }
  else {
    name = function->getName();
  }
  error << "The function '" << name << "' takes ";
  string errmsg = "";
  ASTNodeType_t type = function->getType(); 
  switch(type) 
  {
  case AST_FUNCTION_ABS:
  case AST_FUNCTION_ARCCOS:
  case AST_FUNCTION_ARCCOSH:
  case AST_FUNCTION_ARCCOT:
  case AST_FUNCTION_ARCCOTH:
  case AST_FUNCTION_ARCCSC:
  case AST_FUNCTION_ARCCSCH:
  case AST_FUNCTION_ARCSEC:
  case AST_FUNCTION_ARCSECH:
  case AST_FUNCTION_ARCSIN:
  case AST_FUNCTION_ARCSINH:
  case AST_FUNCTION_ARCTAN:
  case AST_FUNCTION_ARCTANH:
  case AST_FUNCTION_CEILING:
  case AST_FUNCTION_COS:
  case AST_FUNCTION_COSH:
  case AST_FUNCTION_COT:
  case AST_FUNCTION_COTH:
  case AST_FUNCTION_CSC:
  case AST_FUNCTION_CSCH:
  case AST_FUNCTION_EXP:
  case AST_FUNCTION_FACTORIAL:
  case AST_FUNCTION_FLOOR:
  case AST_FUNCTION_LN:
  case AST_FUNCTION_SEC:
  case AST_FUNCTION_SECH:
  case AST_FUNCTION_SIN:
  case AST_FUNCTION_SINH:
  case AST_FUNCTION_TAN:
  case AST_FUNCTION_TANH:
  case AST_LOGICAL_NOT:

    if (children != 1) {
      error << "exactly one argument, but " << children << " were found.";
      l3p->setError(error.str());
      delete function;
      return true;
    }
    return false;
  case AST_DIVIDE:
  case AST_FUNCTION_DELAY:
  case AST_FUNCTION_LOG:       // a log ASTNode has a child for base
  case AST_FUNCTION_POWER:
  case AST_POWER:
  case AST_RELATIONAL_NEQ:

    if (children != 2) {
      error << "exactly two arguments, but " << children << " were found.";
      l3p->setError(error.str());
      delete function;
      return true;
    }
    return false;

  case AST_RELATIONAL_EQ:
  case AST_RELATIONAL_GEQ:
  case AST_RELATIONAL_GT:
  case AST_RELATIONAL_LEQ:
  case AST_RELATIONAL_LT:
    if (children <= 1) {
      error << "at least two arguments, but " << children << " were found.";
      l3p->setError(error.str());
      delete function;
      return true;
    }
    return false;

  case AST_FUNCTION_PIECEWISE:
  case AST_LAMBDA:
    if (children == 0) {
      error << "at least one argument, but none were found.";
      l3p->setError(error.str());
      delete function;
      return true;
    }
    return false;

  case AST_FUNCTION_ROOT:
  case AST_MINUS:
    if (children < 1 || children > 2) {
      error << "exactly one or two arguments, but " << children << " were found.";
      l3p->setError(error.str());
      delete function;
      return true;
    }
    return false;
      
  case AST_TIMES:
  case AST_PLUS:
  case AST_LOGICAL_AND:
  case AST_LOGICAL_OR:
  case AST_LOGICAL_XOR:
    return false;
  default:
    {
      error.str("");
      bool ret = currentSettings->checkNumArgumentsForPackage(function, error);
      if (ret)
      {
        l3p->setError(error.str());
        delete function;
      }
      return ret;
    }
    //Otherwise, we'll assume that the function is n-ary and takes 0 or more arguments
    return false;
  }
}

void makeConstantIntoName(ASTNodeType_t type, ASTNode* function)
{
  if (function->getType() == type) {
    function->setType(AST_NAME);
    function->setDefinitionURL("");
    switch(type) {
    case AST_CONSTANT_TRUE:
      function->setName("true");
      break;
    case AST_CONSTANT_FALSE:
      function->setName("false");
      break;
    case AST_CONSTANT_PI:
      function->setName("pi");
      break;
    case AST_CONSTANT_E:
      function->setName("exponentiale");
      break;
    default:
      break;
    }
  }
  for (unsigned int c=0; c<function->getNumChildren(); c++) {
    makeConstantIntoName(type, function->getChild(c));
  }
}

void L3Parser::fixLambdaArguments(const ASTNode* function)
{
  if (function->getType() != AST_LAMBDA) {
    return;
  }
  unsigned int nchildren = function->getNumChildren();
  if (nchildren==0) {
    return;
  }
  std::set<ASTNodeType_t> fixList;
  for (unsigned int c=0; c<nchildren-1; c++) {
    ASTNode* child = function->getChild(c);
    ASTNodeType_t ctype = child->getType(); 
    switch(ctype) {
    case AST_CONSTANT_TRUE:
    case AST_CONSTANT_FALSE:
    case AST_CONSTANT_PI:
    case AST_CONSTANT_E:
    case AST_NAME_AVOGADRO:
    case AST_NAME_TIME:
      child->setType(AST_NAME);
      child->setDefinitionURL("");
      switch(ctype) {
      case AST_CONSTANT_TRUE:
        child->setName("true");
        break;
      case AST_CONSTANT_FALSE:
        child->setName("false");
        break;
      case AST_CONSTANT_PI:
        child->setName("pi");
        break;
      case AST_CONSTANT_E:
        child->setName("exponentiale");
        break;
      default:
        break;
      }
      fixList.insert(ctype);
      break;
    default:
      break;
    }
  }
  for (set<ASTNodeType_t>::iterator ftype = fixList.begin(); ftype != fixList.end(); ftype++) {
    makeConstantIntoName(*ftype, function->getChild(nchildren-1));
  }
}

bool L3Parser::checkNumArgumentsForPackage(const ASTNode* function)
{
  stringstream error;
  bool ret = currentSettings->checkNumArgumentsForPackage(function, error);
  if (ret) {
    l3p->setError(error.str());
    delete function;
  }
  return ret;
}

ASTNode* L3Parser::combineRelationalElements(ASTNode* left, ASTNode* right, ASTNodeType_t type)
{
  //If 'left' is the same as 'type', add 'right' as a new argument.  (cf x < y < z -> lt(x, y, z))
  ASTNodeType_t leftType = left->getType();
  if (leftType == type && type != AST_RELATIONAL_NEQ)
  {
    left->addChild(right);
    return left;
  }
  //If 'left' is relational itself, make a new 'and' node and combine them:
  // x < y <= z  ->  and(x<y, y<=z)
  if (ASTNode_isRelational(left))
  {
    ASTNode* ret = new ASTNode(AST_LOGICAL_AND);
    ret->addChild(left);
    ASTNode* newrel = new ASTNode(type);
    newrel->addChild(left->getChild(left->getNumChildren()-1)->deepCopy());
    newrel->addChild(right);
    ret->addChild(newrel);
    return ret;
  }

  //Another option is that we've already done the above, and need to extend it:
  if (leftType == AST_LOGICAL_AND && left->getNumChildren() >= 2)
  {
    bool combine = true;
    ASTNode* leftchild = NULL;
    for (unsigned int lc=0; lc<left->getNumChildren(); lc++)
    {
      leftchild = left->getChild(lc);
      if (!ASTNode_isRelational(leftchild) ||
          leftchild->getNumChildren() < 2)
      {
        combine = false;
      }
    }
    if (combine)
    {
      if (leftchild->getType() == type && type != AST_RELATIONAL_NEQ) {
        //We can combine them by adding to the last entry (x < y <= z <= q)
        leftchild->addChild(right);
        return left;
      }
      ASTNode* newrel = new ASTNode(type);
      newrel->addChild(leftchild->getChild(leftchild->getNumChildren()-1)->deepCopy());
      newrel->addChild(right);
      left->addChild(newrel);
      return left;
    }
  }

  //Otherwise, we can't combine them:
  ASTNode* ret = new ASTNode(type);
  ret->addChild(left);
  ret->addChild(right);
  return ret;
}


ASTNode* L3Parser::parsePackageInfix(L3ParserGrammarLineType_t type, 
    vector<ASTNode*> *nodeList, vector<std::string*> *stringList,
    vector<double> *doubleList) const
{
  if (currentSettings == NULL) return NULL;
  return currentSettings->parsePackageInfix(type, nodeList, stringList, doubleList);
}

L3ParserSettings L3Parser::getDefaultL3ParserSettings()
{
  return defaultL3ParserSettings;
}

/** Everything above this point is internal. */
/** @endcond */

LIBSBML_CPP_NAMESPACE_BEGIN
#ifdef __cplusplus
BEGIN_C_DECLS

#endif /* __cplusplus */

/**
 * @if conly
 * @memberof ASTNode_t
 * @endif
 */
LIBSBML_EXTERN
ASTNode_t *
SBML_parseL3Formula (const char *formula)
{
  L3ParserSettings l3ps = L3Parser_getInstance()->getDefaultL3ParserSettings();
  return SBML_parseL3FormulaWithSettings(formula, &l3ps);
}


/**
 * @if conly
 * @memberof ASTNode_t
 * @endif
 */
LIBSBML_EXTERN
ASTNode_t *
SBML_parseL3FormulaWithModel (const char *formula, const Model_t * model)
{
  L3ParserSettings l3ps = L3Parser_getInstance()->getDefaultL3ParserSettings();
  l3ps.setModel(model);
  return SBML_parseL3FormulaWithSettings(formula, &l3ps);
}


/**
 * @if conly
 * @memberof ASTNode_t
 * @endif
 */
LIBSBML_EXTERN
ASTNode_t *
SBML_parseL3FormulaWithSettings (const char *formula, const L3ParserSettings_t * settings)
{
  l3p = L3Parser_getInstance();
  if (settings == NULL) {
    L3ParserSettings l3ps = l3p->getDefaultL3ParserSettings();
    return SBML_parseL3FormulaWithSettings(formula, &l3ps);
  }
#ifdef LIBSBML_WITH_THREADSAFE_PARSER
    const std::lock_guard<std::mutex> lock(l3p_mutex);
#endif
  l3p->clear();
  l3p->setInput(formula);
  l3p->model = settings->getModel();
  l3p->parselog = settings->getParseLog();
  l3p->collapseminus = settings->getParseCollapseMinus();
  l3p->parseunits = settings->getParseUnits();
  l3p->avocsymbol = settings->getParseAvogadroCsymbol();
  l3p->currentSettings = settings;
  l3p->strCmpIsCaseSensitive = settings->getComparisonCaseSensitivity();
  l3p->modulol3v2 = settings->getParseModuloL3v2();
  sbml_yyparse();
  return l3p->outputNode;
}


/**
 * @if conly
 * @memberof ASTNode_t
 * @endif
 */
LIBSBML_EXTERN
L3ParserSettings_t* 
SBML_getDefaultL3ParserSettings ()
{
  return new L3ParserSettings(L3Parser_getInstance()->getDefaultL3ParserSettings());
}

/**
 * @if conly
 * @memberof ASTNode_t
 * @endif
 */
LIBSBML_EXTERN
char*
SBML_getLastParseL3Error()
{
  return safe_strdup(L3Parser_getInstance()->getError().c_str());
}

/** @cond doxygenLibsbmlInternal */

LIBSBML_EXTERN
void
SBML_deleteL3Parser()
{
  if (l3p!=NULL) 
  {
    delete l3p;
    l3p = NULL;
  }
}

/** @endcond */

END_C_DECLS
LIBSBML_CPP_NAMESPACE_END
