1 /**
2  * @file    L3FormulaFormatter.cpp
3  * @brief   Formats an AST formula tree as an SBML L3 formula string.
4  * @author  Lucian Smith (from FormulaFormatter, by Ben Bornstein)
5  *
6  * @if conly
7  * This file contains the SBML_formulaToL3String() and SBML_formulaToL3StringWithSettings()
8  * functions, both associated with the ASTNode_t structure.
9  * @endif
10  *
11  * <!--------------------------------------------------------------------------
12  * This file is part of libSBML.  Please visit http://sbml.org for more
13  * information about SBML, and the latest version of libSBML.
14  *
15  * Copyright (C) 2020 jointly by the following organizations:
16  *     1. California Institute of Technology, Pasadena, CA, USA
17  *     2. University of Heidelberg, Heidelberg, Germany
18  *     3. University College London, London, UK
19  *
20  * Copyright (C) 2019 jointly by the following organizations:
21  *     1. California Institute of Technology, Pasadena, CA, USA
22  *     2. University of Heidelberg, Heidelberg, Germany
23  *
24  * Copyright (C) 2013-2018 jointly by the following organizations:
25  *     1. California Institute of Technology, Pasadena, CA, USA
26  *     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
27  *     3. University of Heidelberg, Heidelberg, Germany
28  *
29  * Copyright (C) 2009-2013 jointly by the following organizations:
30  *     1. California Institute of Technology, Pasadena, CA, USA
31  *     2. EMBL European Bioinformatics Institute (EMBL-EBI), Hinxton, UK
32  *
33  * Copyright (C) 2006-2008 by the California Institute of Technology,
34  *     Pasadena, CA, USA
35  *
36  * Copyright (C) 2002-2005 jointly by the following organizations:
37  *     1. California Institute of Technology, Pasadena, CA, USA
38  *     2. Japan Science and Technology Agency, Japan
39  *
40  * This library is free software; you can redistribute it and/or modify it
41  * under the terms of the GNU Lesser General Public License as published by
42  * the Free Software Foundation.  A copy of the license agreement is provided
43  * in the file named "LICENSE.txt" included with this software distribution and
44  * also available online as http://sbml.org/software/libsbml/license.html
45  * ---------------------------------------------------------------------- -->*/
46 
47 #include <sbml/common/common.h>
48 #include <sbml/math/ASTNodeType.h>
49 #include <sbml/math/FormulaFormatter.h>
50 #include <sbml/math/L3FormulaFormatter.h>
51 #include <sbml/math/L3ParserSettings.h>
52 #include <sbml/math/ASTNode.h>
53 #include <sbml/extension/ASTBasePlugin.h>
54 #include <assert.h>
55 
56 #include <sbml/util/util.h>
57 
58 LIBSBML_CPP_NAMESPACE_BEGIN
59 
60 int
isUnaryMinus(const ASTNode_t * node)61 isUnaryMinus (const ASTNode_t *node)
62 {
63   if (node==NULL) return 0;
64   if (ASTNode_getType(node) != AST_MINUS) return 0;
65   if (ASTNode_getNumChildren(node) != 1) return 0;
66   return 1;
67 }
68 
69 int
isUnaryNot(const ASTNode_t * node)70 isUnaryNot (const ASTNode_t *node)
71 {
72   if (node==NULL) return 0;
73   if (ASTNode_getType(node) != AST_LOGICAL_NOT) return 0;
74   if (ASTNode_getNumChildren(node) != 1) return 0;
75   return 1;
76 }
77 
78 int
79 L3FormulaFormatter_hasUnambiguousGrammar(const ASTNode_t *node,
80                                const ASTNode_t *child,
81                                const L3ParserSettings_t *settings);
82 
83 
84 /** @cond doxygenIgnored */
85 LIBSBML_EXTERN
86 char *
SBML_formulaToL3String(const ASTNode_t * tree)87 SBML_formulaToL3String (const ASTNode_t *tree)
88 {
89   L3ParserSettings_t* l3ps = L3ParserSettings_create();
90   char* ret = SBML_formulaToL3StringWithSettings(tree, l3ps);
91   L3ParserSettings_free(l3ps);
92   return ret;
93 }
94 
95 
96 LIBSBML_EXTERN
97 char *
SBML_formulaToL3StringWithSettings(const ASTNode_t * tree,const L3ParserSettings_t * settings)98 SBML_formulaToL3StringWithSettings (const ASTNode_t *tree, const L3ParserSettings_t *settings)
99 {
100   char           *s;
101   StringBuffer_t *sb;
102 
103   if (tree == NULL)
104   {
105     return NULL;
106   }
107 
108   sb = StringBuffer_create(128);
109   L3FormulaFormatter_visit(NULL, tree, sb, settings);
110   s = StringBuffer_getBuffer(sb);
111   safe_free(sb);
112   return s;
113 }
114 /** @endcond */
115 
116 
117 /**
118  * @cond doxygenLibsbmlInternal
119  * The rest of this file is internal code.
120  */
121 
122 /* function used by the isTranslatedModulo function to compare
123  * children of the piecewise that can be used to construct
124  * the modulo function
125  */
equals(const ASTNode_t * a,const ASTNode_t * b)126 int equals(const ASTNode_t* a, const ASTNode_t* b)
127 {
128   char* ach = SBML_formulaToL3String(a);
129   char* bch = SBML_formulaToL3String(b);
130   int ret = !strcmp (ach, bch);
131   free(ach);
132   free(bch);
133   return ret;
134 }
135 
136 /* Used by getL3Precedence and other functions below.
137  */
isTranslatedModulo(const ASTNode_t * node)138 int isTranslatedModulo (const ASTNode_t* node)
139 {
140   const ASTNode_t* child;
141   const ASTNode_t* c2;
142   const ASTNode_t* x;
143   const ASTNode_t* y;
144   //In l3v2 there may be an actual 'modulo' ASTNode, but for now,
145   // it's all mimicked by the piecewise function:
146   // piecewise(x - y * ceil(x / y), xor(x < 0, y < 0), x - y * floor(x / y))
147 
148   if (ASTNode_getType(node) != AST_FUNCTION_PIECEWISE) return 0;
149   if (ASTNode_getNumChildren(node) != 3) return 0;
150 
151   //x - y * ceil(x/y)
152   child = ASTNode_getChild(node, 0);
153   if (ASTNode_getType(child) != AST_MINUS) return 0;
154   if (ASTNode_getNumChildren(child) != 2) return 0;
155   x  = ASTNode_getChild(child, 0);
156 
157   c2 = ASTNode_getChild(child, 1);
158   if (ASTNode_getType(c2) != AST_TIMES) return 0;
159   if (ASTNode_getNumChildren(c2) != 2) return 0;
160   y = ASTNode_getChild(c2, 0);
161   c2 = ASTNode_getChild(c2, 1);
162   if (ASTNode_getType(c2) != AST_FUNCTION_CEILING) return 0;
163   if (ASTNode_getNumChildren(c2) != 1) return 0;
164   c2 = ASTNode_getChild(c2, 0);
165   if (ASTNode_getType(c2) != AST_DIVIDE) return 0;
166   if (ASTNode_getNumChildren(c2) != 2) return 0;
167   if (!equals(x, ASTNode_getChild(c2, 0))) return 0;
168   if (!equals(y, ASTNode_getChild(c2, 1))) return 0;
169 
170   //xor(x<0, y<0)
171   child = ASTNode_getChild(node, 1);
172   if (ASTNode_getType(child) != AST_LOGICAL_XOR) return 0;
173   if (ASTNode_getNumChildren(child) != 2) return 0;
174   c2 = ASTNode_getChild(child, 0);
175   if (ASTNode_getType(c2) != AST_RELATIONAL_LT) return 0;
176   if (ASTNode_getNumChildren(c2) != 2) return 0;
177   if (!equals(x, ASTNode_getChild(c2, 0))) return 0;
178   if (ASTNode_getType(ASTNode_getChild(c2, 1)) != AST_INTEGER) return 0;
179   if (ASTNode_getInteger(ASTNode_getChild(c2, 1)) != 0) return 0;
180   c2 = ASTNode_getChild(child, 1);
181   if (ASTNode_getType(c2) != AST_RELATIONAL_LT) return 0;
182   if (ASTNode_getNumChildren(c2) != 2) return 0;
183   if (!equals(y, ASTNode_getChild(c2, 0))) return 0;
184   if (ASTNode_getType(ASTNode_getChild(c2, 1)) != AST_INTEGER) return 0;
185   if (ASTNode_getInteger(ASTNode_getChild(c2, 1)) != 0) return 0;
186 
187   //x - y * floor(x/y)
188   child = ASTNode_getChild(node, 2);
189   if (ASTNode_getType(child) != AST_MINUS) return 0;
190   if (ASTNode_getNumChildren(child) != 2) return 0;
191   if (!equals(x, ASTNode_getChild(child, 0))) return 0;
192 
193   c2 = ASTNode_getChild(child, 1);
194   if (ASTNode_getType(c2) != AST_TIMES) return 0;
195   if (ASTNode_getNumChildren(c2) != 2) return 0;
196   if (!equals(y, ASTNode_getChild(c2, 0))) return 0;
197   c2 = ASTNode_getChild(c2, 1);
198   if (ASTNode_getType(c2) != AST_FUNCTION_FLOOR) return 0;
199   if (ASTNode_getNumChildren(c2) != 1) return 0;
200   c2 = ASTNode_getChild(c2, 0);
201   if (ASTNode_getType(c2) != AST_DIVIDE) return 0;
202   if (ASTNode_getNumChildren(c2) != 2) return 0;
203   if (!equals(x, ASTNode_getChild(c2, 0))) return 0;
204   if (!equals(y, ASTNode_getChild(c2, 1))) return 0;
205 
206   return 1;
207 }
208 
L3FormulaFormatter_getRightChild(const ASTNode_t * parent)209 const ASTNode_t* L3FormulaFormatter_getRightChild(const ASTNode_t* parent)
210 {
211   if (isTranslatedModulo(parent)==1) {
212     return ASTNode_getChild(ASTNode_getChild(ASTNode_getChild(parent, 0), 1), 0);
213   }
214   if (isUnaryMinus(parent) || isUnaryNot(parent)) {
215     return ASTNode_getChild(parent, 0);
216   }
217   return ASTNode_getRightChild(parent);
218 }
219 
220 
221 /*
222  * @return the precedence of this ASTNode as defined in the L3 parser documentation.
223  */
getL3Precedence(const ASTNode_t * node)224 int getL3Precedence(const ASTNode_t* node)
225 {
226   int precedence;
227   unsigned int numchildren = ASTNode_getNumChildren(node);
228 
229   if ( !ASTNode_hasCorrectNumberArguments((ASTNode_t*)node) )
230   {
231     //If the number of arguments is wrong, it'll be treated like a function call.
232     precedence = 8;
233   }
234   else if ( isTranslatedModulo(node) )
235   {
236     precedence = 5;
237   }
238   else
239   {
240     switch (ASTNode_getType(node))
241     {
242       case AST_POWER:
243       case AST_FUNCTION_POWER:
244         //Anything other than two children is caught above, since that's the only correct number of arguments.
245         precedence = 7;
246         break;
247 
248       case AST_LOGICAL_NOT:
249         //Anything other than unary not is caught above, since that's the wrong number of arguments.
250         precedence = 6;
251         break;
252 
253       case AST_DIVIDE:
254       case AST_TIMES:
255         if (numchildren < 2) {
256           //Written in functional form.
257           precedence = 8;
258         }
259         else {
260           precedence = 5;
261         }
262         break;
263 
264       case AST_MINUS:
265         if (numchildren == 1) {
266           //Unary minus
267           precedence = 6;
268           break;
269         }
270         //Fallthrough to:
271       case AST_PLUS:
272         if (numchildren < 2) {
273           //Written in functional form (unary minus caught above)
274           precedence = 8;
275         }
276         else {
277           precedence = 4;
278         }
279         break;
280 
281       case AST_RELATIONAL_EQ:
282       case AST_RELATIONAL_GEQ:
283       case AST_RELATIONAL_GT:
284       case AST_RELATIONAL_LEQ:
285       case AST_RELATIONAL_LT:
286       case AST_RELATIONAL_NEQ:
287         //The relational symbols (==, >=, etc.) are only used when there are two or more children.
288         if (numchildren >= 2) {
289           precedence = 3;
290         }
291         else {
292           precedence = 8;
293         }
294         break;
295 
296       case AST_LOGICAL_AND:
297       case AST_LOGICAL_OR:
298         //The logical symbols && and || are only used when there are two or more children.
299         if (numchildren < 2) {
300           precedence = 8;
301         }
302         else {
303           precedence = 2;
304         }
305         break;
306 
307       default:
308         bool foundInPlugin = false;
309         if (node)
310         {
311           const ASTBasePlugin* baseplugin = node->getASTPlugin(ASTNode_getType(node));
312           if (baseplugin != NULL)
313           {
314             // this needs to know what the parent node is so do actually
315             // clone it
316             ASTBasePlugin* plugin = baseplugin->clone();
317             plugin->connectToParent((ASTNode*)(node));
318             precedence = plugin->getL3PackageInfixPrecedence();
319             foundInPlugin = true;
320             delete plugin;
321           }
322         }
323         if (!foundInPlugin)
324         {
325           precedence = 8;
326         }
327         break;
328     }
329   }
330 
331   return precedence;
332 }
333 
334 
335 /**
336  * @return true (nonzero) if the given child ASTNode should be grouped
337  * (with parenthesis), false (0) otherwise.
338  *
339  * A node should be group if it is not an argument to a function and
340  * either:
341  *
342  *   - The parent node has higher precedence than the child, or
343  *
344  *   - If parent node has equal precedence with the child and the child is
345  *     to the right.  In this case, operator associativity and right-most
346  *     AST derivation enforce the grouping.
347  */
348 int
L3FormulaFormatter_isGrouped(const ASTNode_t * parent,const ASTNode_t * child,const L3ParserSettings_t * settings)349 L3FormulaFormatter_isGrouped (const ASTNode_t *parent, const ASTNode_t *child, const L3ParserSettings_t *settings)
350 {
351   int pp, cp;
352   int pt, ct;
353   int group = 0;
354   const ASTNode_t* rchild = NULL;
355 
356   if (parent != NULL)
357   {
358     if (isUnaryMinus(parent)) {
359       child = L3FormulaFormatter_getRightChild(parent);
360       if (isUnaryNot(child)) {
361         //Always group: -(!a) because reasons.
362         return 1;
363       }
364     }
365     else if (isUnaryNot(parent)) {
366       child = L3FormulaFormatter_getRightChild(parent);
367       if (isUnaryMinus(child)) {
368         //Always group: !(-a) because reasons.
369         return 1;
370       }
371     }
372     if (ASTNode_isLogical(parent) || ASTNode_isRelational(parent) || isTranslatedModulo(parent)) {
373       if (!L3FormulaFormatter_hasUnambiguousGrammar(NULL, child, settings)) {
374         //Always group potentially-ambiguous children of logical, relational, and modulo operators, since their precedence is unfamiliar to most users.
375         group = 1;
376         rchild = L3FormulaFormatter_getRightChild(parent);
377         if (child == rchild) {
378           if (isUnaryMinus(child) || isUnaryNot(child)) {
379           //Don't need to group right-side unary minus nor unary not: they are not actually ambiguous
380           group = 0;
381           }
382         }
383       }
384     }
385     else if (!L3FormulaFormatter_hasUnambiguousGrammar(parent, child, settings))
386     {
387       if ((ASTNode_isLogical(child) || ASTNode_isRelational(child) || isTranslatedModulo(child)) &&
388           !isUnaryMinus(child) && !isUnaryNot(child)){
389         //Always group child  logical, relational, and modulo, since their precedence is unfamiliar to most users.
390         group = 1;
391       }
392       else {
393         group = 1;
394         pp = getL3Precedence(parent);
395         cp = getL3Precedence(child);
396 
397         if (pp < cp)
398         {
399           group = 0;
400         }
401         else if (pp == cp)
402         {
403           /**
404           * Don't group only if i) child is the first on the list and ii) both parent and
405           * child are the same type, or if they
406           * should be associative operators (i.e. not AST_MINUS or
407           * AST_DIVIDE).  That is, do not group a parent and left child
408           * that are either both AST_PLUS or both AST_TIMES operators, nor the logical operators
409           * that have the same precedence.
410           */
411           if (ASTNode_getLeftChild(parent) == child)
412           {
413             pt = ASTNode_getType(parent);
414             ct = ASTNode_getType(child);
415             if (ASTNode_isLogical(parent) || ASTNode_isRelational(parent)) {
416               group = !(pt == ct);
417             }
418             else {
419               group = !((pt == ct) || (pt == AST_DIVIDE || pt == AST_MINUS));
420             }
421           }
422         }
423         else if (pp==7 && cp==6) {
424           //If the parent is 'power' and the child is 'unary not' or 'unary minus', we only need
425           // to group if the child is the *left* child:  '(-x)^y', but 'x^-y'.
426           if (!(ASTNode_getLeftChild(parent) == child)) {
427             group = 0;
428           }
429         }
430       }
431     }
432   }
433 
434   return group;
435 }
436 
437 
438 /**
439  * Formats the given ASTNode as an SBML L3 token and appends the result to
440  * the given StringBuffer.
441  */
442 void
L3FormulaFormatter_format(StringBuffer_t * sb,const ASTNode_t * node,const L3ParserSettings_t * settings)443 L3FormulaFormatter_format (StringBuffer_t *sb, const ASTNode_t *node, const L3ParserSettings_t *settings)
444 {
445   if (sb == NULL) return;
446   if (L3FormulaFormatter_isFunction(node, settings))
447   {
448     L3FormulaFormatter_formatFunction(sb, node, settings);
449   }
450   else if (ASTNode_isOperator(node) || ASTNode_getType(node) == AST_FUNCTION_POWER)
451   {
452     L3FormulaFormatter_formatOperator(sb, node);
453   }
454   else if (ASTNode_isLogical(node) || ASTNode_isRelational(node))
455   {
456     L3FormulaFormatter_formatLogicalRelational(sb, node);
457   }
458   else if (ASTNode_isRational(node))
459   {
460     L3FormulaFormatter_formatRational(sb, node, settings);
461   }
462   else if (ASTNode_isInteger(node))
463   {
464     L3FormulaFormatter_formatReal(sb, node, settings);
465   }
466   else if (ASTNode_isReal(node))
467   {
468     L3FormulaFormatter_formatReal(sb, node, settings);
469   }
470   else if (ASTNode_isAvogadro(node))
471   {
472     StringBuffer_append(sb, "avogadro");
473   }
474   else if (ASTNode_getType(node) == AST_NAME_TIME)
475   {
476     StringBuffer_append(sb, "time");
477   }
478   else if ( !ASTNode_isUnknown(node) )
479   {
480     StringBuffer_append(sb, ASTNode_getName(node));
481   }
482 }
483 
484 
485 /**
486  * Formats the given ASTNode as an SBML L3 function name and appends the
487  * result to the given StringBuffer.
488  */
489 void
L3FormulaFormatter_formatFunction(StringBuffer_t * sb,const ASTNode_t * node,const L3ParserSettings_t * settings)490 L3FormulaFormatter_formatFunction (StringBuffer_t *sb, const ASTNode_t *node, const L3ParserSettings_t *settings)
491 {
492   ASTNodeType_t type = ASTNode_getType(node);
493   switch (type)
494   {
495   case AST_PLUS:
496     StringBuffer_append(sb, "plus");
497     break;
498   case AST_TIMES:
499     StringBuffer_append(sb, "times");
500     break;
501   case AST_MINUS:
502     StringBuffer_append(sb, "minus");
503     break;
504   case AST_DIVIDE:
505     StringBuffer_append(sb, "divide");
506     break;
507   case AST_POWER:
508     StringBuffer_append(sb, "pow");
509     break;
510   case AST_FUNCTION_LN:
511     StringBuffer_append(sb, "ln");
512     break;
513   case AST_FUNCTION_DELAY:
514     StringBuffer_append(sb, "delay");
515     break;
516   default:
517   {
518     bool foundInPlugin = false;
519     if (node)
520     {
521       const ASTBasePlugin* baseplugin = node->getASTPlugin(type);
522       if (baseplugin != NULL)
523       {
524         if (baseplugin->getConstCharCsymbolURLFor(type) != NULL)
525         {
526           foundInPlugin = true;
527           StringBuffer_append(sb, baseplugin->getConstCharFor(type));
528         }
529       }
530     }
531     if (!foundInPlugin)
532     {
533       FormulaFormatter_formatFunction(sb, node);
534     }
535   }
536     break;
537   }
538 }
539 
540 
541 /**
542  * Formats the given ASTNode as an SBML L1 operator and appends the result
543  * to the given StringBuffer.
544  */
545 void
L3FormulaFormatter_formatOperator(StringBuffer_t * sb,const ASTNode_t * node)546 L3FormulaFormatter_formatOperator (StringBuffer_t *sb, const ASTNode_t *node)
547 {
548   ASTNodeType_t type = ASTNode_getType(node);
549 
550   if (type == AST_FUNCTION_POWER ||
551       type == AST_POWER) {
552     StringBuffer_appendChar(sb, '^');
553   }
554   else
555   {
556     StringBuffer_appendChar(sb, ' ');
557     StringBuffer_appendChar(sb, ASTNode_getCharacter(node));
558     StringBuffer_appendChar(sb, ' ');
559   }
560 }
561 
562 
563 /**
564  * Formats the given ASTNode as a rational number and appends the result to
565  * the given StringBuffer.  For SBML L1 this amounts to:
566  *
567  *   "(numerator/denominator)"
568  */
569 void
L3FormulaFormatter_formatRational(StringBuffer_t * sb,const ASTNode_t * node,const L3ParserSettings_t * settings)570 L3FormulaFormatter_formatRational (StringBuffer_t *sb, const ASTNode_t *node, const L3ParserSettings_t *settings)
571 {
572   char * units;
573   StringBuffer_appendChar( sb, '(');
574   StringBuffer_appendInt ( sb, ASTNode_getNumerator(node)   );
575   StringBuffer_appendChar( sb, '/');
576   StringBuffer_appendInt ( sb, ASTNode_getDenominator(node) );
577   StringBuffer_appendChar( sb, ')');
578 
579   if (L3ParserSettings_getParseUnits(settings)) {
580     if (ASTNode_hasUnits(node)) {
581       StringBuffer_appendChar( sb, ' ');
582       units = ASTNode_getUnits(node);
583       StringBuffer_append( sb, units);
584       safe_free(units);
585     }
586   }
587 }
588 
589 
590 /**
591  * Formats the given ASTNode as a real number and appends the result to
592  * the given StringBuffer.
593  */
594 void
L3FormulaFormatter_formatReal(StringBuffer_t * sb,const ASTNode_t * node,const L3ParserSettings_t * settings)595 L3FormulaFormatter_formatReal (StringBuffer_t *sb, const ASTNode_t *node, const L3ParserSettings_t *settings)
596 {
597   double value = ASTNode_getReal(node);
598   int    sign;
599   char * units;
600 
601   if (ASTNode_isInteger(node)) {
602     value = ASTNode_getInteger(node);
603   }
604 
605   if (util_isNaN(value))
606   {
607     StringBuffer_append(sb, "NaN");
608   }
609   else if ((sign = util_isInf(value)) != 0)
610   {
611     if (sign == -1)
612     {
613       StringBuffer_appendChar(sb, '-');
614     }
615 
616     StringBuffer_append(sb, "INF");
617   }
618   else if (util_isNegZero(value))
619   {
620     StringBuffer_append(sb, "-0");
621   }
622   else
623   {
624     if (ASTNode_getType(node) == AST_REAL_E)
625     {
626       StringBuffer_appendFullExp(sb, ASTNode_getMantissa(node), ASTNode_getExponent(node), value);
627     }
628     else
629     {
630       StringBuffer_appendReal(sb, value);
631     }
632   }
633   if (L3ParserSettings_getParseUnits(settings)) {
634     if (ASTNode_hasUnits(node)) {
635       StringBuffer_appendChar( sb, ' ');
636       units = ASTNode_getUnits(node);
637       StringBuffer_append( sb, units);
638       safe_free(units);
639     }
640   }
641 }
642 
643 /**
644  * Formats the given ASTNode as an SBML L1 operator and appends the result
645  * to the given StringBuffer.
646  */
647 void
L3FormulaFormatter_formatLogicalRelational(StringBuffer_t * sb,const ASTNode_t * node)648 L3FormulaFormatter_formatLogicalRelational (StringBuffer_t *sb, const ASTNode_t *node)
649 {
650   ASTNodeType_t type = ASTNode_getType(node);
651 
652   StringBuffer_appendChar(sb, ' ');
653   switch(type)
654   {
655   case AST_LOGICAL_AND:
656     StringBuffer_append(sb, "&&");
657     break;
658   case AST_LOGICAL_OR:
659     StringBuffer_append(sb, "||");
660     break;
661   case AST_RELATIONAL_EQ:
662     StringBuffer_append(sb, "==");
663     break;
664   case AST_RELATIONAL_GEQ:
665     StringBuffer_append(sb, ">=");
666     break;
667   case AST_RELATIONAL_GT:
668     StringBuffer_append(sb, ">");
669     break;
670   case AST_RELATIONAL_LEQ:
671     StringBuffer_append(sb, "<=");
672     break;
673   case AST_RELATIONAL_LT:
674     StringBuffer_append(sb, "<");
675     break;
676   case AST_RELATIONAL_NEQ:
677     StringBuffer_append(sb, "!=");
678     break;
679   case AST_LOGICAL_NOT:
680   case AST_LOGICAL_XOR:
681   default:
682     //Should never be called for these cases; unary not is
683     // handled by checking unary not earlier; xor always
684     // claims that it's a function, and is caught with 'isFunction'
685     assert(0);
686     StringBuffer_append(sb, "!!");
687     break;
688   }
689   StringBuffer_appendChar(sb, ' ');
690 }
691 
692 
693 /**
694  * Visits the given ASTNode node.  This function is really just a
695  * dispatcher to either SBML_formulaToL3String_visitFunction() or
696  * SBML_formulaToL3String_visitOther().
697  */
698 void
L3FormulaFormatter_visit(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb,const L3ParserSettings_t * settings)699 L3FormulaFormatter_visit ( const ASTNode_t *parent,
700                            const ASTNode_t *node,
701                            StringBuffer_t  *sb,
702                            const L3ParserSettings_t *settings )
703 {
704 
705   if (ASTNode_isLog10(node))
706   {
707     L3FormulaFormatter_visitLog10(parent, node, sb, settings);
708   }
709   else if (ASTNode_isSqrt(node))
710   {
711     L3FormulaFormatter_visitSqrt(parent, node, sb, settings);
712   }
713   else if (isTranslatedModulo(node))
714   {
715     L3FormulaFormatter_visitModulo(parent, node, sb, settings);
716   }
717   else if (L3FormulaFormatter_isFunction(node, settings))
718   {
719     L3FormulaFormatter_visitFunction(parent, node, sb, settings);
720   }
721   else if (ASTNode_isUMinus(node))
722   {
723     L3FormulaFormatter_visitUMinus(parent, node, sb, settings);
724   }
725   else if (ASTNode_hasTypeAndNumChildren(node, AST_LOGICAL_NOT, 1))
726   {
727     L3FormulaFormatter_visitUNot(parent, node, sb, settings);
728   }
729   else
730   {
731     bool foundInPackage = false;
732     if (node != NULL)
733     {
734       const ASTBasePlugin* baseplugin = node->getASTPlugin(ASTNode_getType(node));
735       if (baseplugin != NULL)
736       {
737         // this needs to know what the parent node is so do actually
738         // clone it
739         ASTBasePlugin* plugin = baseplugin->clone();
740         plugin->connectToParent((ASTNode*)(node));
741         if (plugin->hasPackageOnlyInfixSyntax())
742         {
743           L3ParserSettings_visitPackageInfixSyntax(parent, node, sb, settings);
744           foundInPackage = true;
745         }
746         delete plugin;
747       }
748     }
749     if (!foundInPackage)
750     {
751       L3FormulaFormatter_visitOther(parent, node, sb, settings);
752     }
753   }
754 }
755 
756 
757 /**
758  * Visits the given ASTNode as a function.  For this node only the
759  * traversal is preorder.
760  */
761 void
L3FormulaFormatter_visitFunction(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb,const L3ParserSettings_t * settings)762 L3FormulaFormatter_visitFunction ( const ASTNode_t *parent,
763                                    const ASTNode_t *node,
764                                    StringBuffer_t  *sb,
765                                    const L3ParserSettings_t *settings )
766 {
767   unsigned int numChildren = ASTNode_getNumChildren(node);
768   unsigned int n;
769 
770 
771   L3FormulaFormatter_format(sb, node, settings);
772   StringBuffer_appendChar(sb, '(');
773 
774   if (numChildren > 0)
775   {
776     L3FormulaFormatter_visit( node, ASTNode_getChild(node, 0), sb, settings);
777   }
778 
779   for (n = 1; n < numChildren; n++)
780   {
781     StringBuffer_appendChar(sb, ',');
782     StringBuffer_appendChar(sb, ' ');
783     L3FormulaFormatter_visit( node, ASTNode_getChild(node, n), sb, settings);
784   }
785 
786   StringBuffer_appendChar(sb, ')');
787 }
788 
789 
790 /**
791  * Visits the given ASTNode as the function "log(10, x)" and in doing so,
792  * formats it as "log10(x)" (where x is any subexpression).
793  */
794 void
L3FormulaFormatter_visitLog10(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb,const L3ParserSettings_t * settings)795 L3FormulaFormatter_visitLog10 ( const ASTNode_t *parent,
796                                 const ASTNode_t *node,
797                                 StringBuffer_t  *sb,
798                                 const L3ParserSettings_t *settings )
799 {
800   StringBuffer_append(sb, "log10(");
801   L3FormulaFormatter_visit(node, ASTNode_getChild(node, 1), sb, settings);
802   StringBuffer_appendChar(sb, ')');
803 }
804 
805 
806 /**
807  * Visits the given ASTNode as the function "root(2, x)" and in doing so,
808  * formats it as "sqrt(x)" (where x is any subexpression).
809  */
810 void
L3FormulaFormatter_visitSqrt(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb,const L3ParserSettings_t * settings)811 L3FormulaFormatter_visitSqrt ( const ASTNode_t *parent,
812                                const ASTNode_t *node,
813                                StringBuffer_t  *sb,
814                                const L3ParserSettings_t *settings )
815 {
816   StringBuffer_append(sb, "sqrt(");
817   L3FormulaFormatter_visit(node, ASTNode_getChild(node, 1), sb, settings);
818   StringBuffer_appendChar(sb, ')');
819 }
820 
821 
822 /**
823  * Visits the given ASTNode as a unary minus.  For this node only the
824  * traversal is preorder.
825  */
826 void
L3FormulaFormatter_visitUMinus(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb,const L3ParserSettings_t * settings)827 L3FormulaFormatter_visitUMinus ( const ASTNode_t *parent,
828                                  const ASTNode_t *node,
829                                  StringBuffer_t  *sb,
830                                  const L3ParserSettings_t *settings )
831 {
832   //Unary minus is *not* the highest precedence, since it is superceded by 'power'
833   unsigned int group;
834 
835   //If we are supposed to collapse minuses, do so.
836   if (L3ParserSettings_getParseCollapseMinus(settings)) {
837     if (ASTNode_getNumChildren(node) == 1 &&
838         ASTNode_isUMinus(ASTNode_getLeftChild(node))) {
839       L3FormulaFormatter_visit(parent, ASTNode_getLeftChild(ASTNode_getLeftChild(node)), sb, settings);
840       return;
841     }
842   }
843 
844   group = L3FormulaFormatter_isGrouped(parent, node, settings);
845 
846   if (group)
847   {
848     StringBuffer_appendChar(sb, '(');
849   }
850   StringBuffer_appendChar(sb, '-');
851   L3FormulaFormatter_visit ( node, ASTNode_getLeftChild(node), sb, settings);
852   if (group)
853   {
854     StringBuffer_appendChar(sb, ')');
855   }
856 }
857 
858 
859 /**
860  * Visits the given ASTNode as a unary not.
861  */
862 void
L3FormulaFormatter_visitUNot(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb,const L3ParserSettings_t * settings)863 L3FormulaFormatter_visitUNot ( const ASTNode_t *parent,
864                                const ASTNode_t *node,
865                                StringBuffer_t  *sb,
866                                const L3ParserSettings_t *settings )
867 {
868   //Unary not is also not the highest precedence, since it is superceded by 'power'
869   unsigned int group       = L3FormulaFormatter_isGrouped(parent, node, settings);
870 
871   if (group)
872   {
873     StringBuffer_appendChar(sb, '(');
874   }
875   StringBuffer_appendChar(sb, '!');
876   L3FormulaFormatter_visit ( node, ASTNode_getLeftChild(node), sb, settings);
877   if (group)
878   {
879     StringBuffer_appendChar(sb, ')');
880   }
881 }
882 
883 
884 /**
885  * Visits the given ASTNode, translating the piecewise function
886  * to the much simpler 'x % y' format.
887  */
888 void
L3FormulaFormatter_visitModulo(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb,const L3ParserSettings_t * settings)889 L3FormulaFormatter_visitModulo ( const ASTNode_t *parent,
890                                  const ASTNode_t *node,
891                                  StringBuffer_t  *sb,
892                                  const L3ParserSettings_t *settings )
893 {
894   unsigned int group       = L3FormulaFormatter_isGrouped(parent, node, settings);
895   const ASTNode_t* subnode = ASTNode_getLeftChild(node);
896   if (group)
897   {
898     StringBuffer_appendChar(sb, '(');
899   }
900 
901   //Get x and y from the first child of the piecewise function,
902   // then the first child of that (times), and the first child
903   // of that (minus).
904   L3FormulaFormatter_visit ( node, ASTNode_getLeftChild(subnode), sb, settings);
905   StringBuffer_appendChar(sb, ' ');
906   StringBuffer_appendChar(sb, '%');
907   StringBuffer_appendChar(sb, ' ');
908   subnode = ASTNode_getRightChild(subnode);
909   L3FormulaFormatter_visit ( node, ASTNode_getLeftChild(subnode), sb, settings);
910 
911   if (group)
912   {
913     StringBuffer_appendChar(sb, ')');
914   }
915 }
916 
917 
918 /**
919  * Visits the given ASTNode and continues the inorder traversal.
920  */
921 void
L3FormulaFormatter_visitOther(const ASTNode_t * parent,const ASTNode_t * node,StringBuffer_t * sb,const L3ParserSettings_t * settings)922 L3FormulaFormatter_visitOther ( const ASTNode_t *parent,
923                                 const ASTNode_t *node,
924                                 StringBuffer_t  *sb,
925                                 const L3ParserSettings_t *settings )
926 {
927   unsigned int numChildren = ASTNode_getNumChildren(node);
928   unsigned int group       = L3FormulaFormatter_isGrouped(parent, node, settings);
929   unsigned int n;
930 
931 
932   if (group)
933   {
934     StringBuffer_appendChar(sb, '(');
935   }
936 
937   if (numChildren == 0) {
938     L3FormulaFormatter_format(sb, node, settings);
939   }
940 
941   else if (numChildren == 1)
942   {
943     //I believe this would only be called for invalid ASTNode setups,
944     // but this could in theory occur.  This is the safest
945     // behavior I can think of.
946     L3FormulaFormatter_format(sb, node, settings);
947     StringBuffer_appendChar(sb, '(');
948     L3FormulaFormatter_visit( node, ASTNode_getChild(node, 0), sb, settings);
949     StringBuffer_appendChar(sb, ')');
950   }
951 
952   else {
953     L3FormulaFormatter_visit( node, ASTNode_getChild(node, 0), sb, settings);
954 
955     for (n = 1; n < numChildren; n++)
956     {
957       L3FormulaFormatter_format(sb, node, settings);
958       L3FormulaFormatter_visit( node, ASTNode_getChild(node, n), sb, settings);
959     }
960   }
961 
962   if (group)
963   {
964     StringBuffer_appendChar(sb, ')');
965   }
966 }
967 
968 
969 
970 //This function determines if the node in question has unambiguous grammar; that
971 // is, if it needs to worry about any of its components having parentheses.
972 int
L3FormulaFormatter_hasUnambiguousGrammar(const ASTNode_t * node,const ASTNode_t * child,const L3ParserSettings_t * settings)973 L3FormulaFormatter_hasUnambiguousGrammar(const ASTNode_t *node,
974                                const ASTNode_t *child,
975                                const L3ParserSettings_t *settings)
976 {
977   //All of the following situations have grammar that doesn't ever require the child
978   // to have parentheses added when it's a child of 'node'.
979 
980   //Functions delimit their children with commas:
981   if (L3FormulaFormatter_isFunction(node, settings)) return 1;
982 
983   //Packages have their own rules:
984   if (node != NULL)
985   {
986     const ASTBasePlugin* baseplugin = node->getASTPlugin(ASTNode_getType(node));
987     if (baseplugin != NULL)
988     {
989       // this needs to know what the parent node is so do actually
990       // clone it
991       ASTBasePlugin* plugin = baseplugin->clone();
992       plugin->connectToParent((ASTNode*)(node));
993       if (plugin->hasUnambiguousPackageInfixGrammar(child))
994       {
995         delete plugin;
996         return 1;
997       }
998       delete plugin;
999     }
1000   }
1001 
1002   //'8', the highest precedence, is only ever given to functions and other top-level
1003   // unambiguous objects.
1004   if (getL3Precedence(child) == 8) return 1;
1005 
1006   return 0;
1007 }
1008 
1009 
1010 //This function determines if the node in question should be
1011 // expressed in the form "functioname(children)" or if
1012 // it should be expressed as "child1 [symbol] child2 [symbol] child3"
1013 // etc.
1014 int
L3FormulaFormatter_isFunction(const ASTNode_t * node,const L3ParserSettings_t * settings)1015 L3FormulaFormatter_isFunction (const ASTNode_t *node,
1016                                const L3ParserSettings_t *settings)
1017 {
1018   if (node==NULL) return 0;
1019   switch(ASTNode_getType(node))
1020   {
1021   case AST_PLUS:
1022   case AST_TIMES:
1023   case AST_LOGICAL_AND:
1024   case AST_LOGICAL_OR:
1025     if (ASTNode_getNumChildren(node) >= 2) {
1026       return 0;
1027     }
1028     return 1;
1029 
1030   case AST_RELATIONAL_EQ:
1031   case AST_RELATIONAL_GEQ:
1032   case AST_RELATIONAL_GT:
1033   case AST_RELATIONAL_LEQ:
1034   case AST_RELATIONAL_LT:
1035     if (ASTNode_getNumChildren(node) >= 2) {
1036       return 0;
1037     }
1038     return 1;
1039 
1040   case AST_MINUS:
1041     if (ASTNode_getNumChildren(node) == 1) {
1042       return 0;
1043     }
1044   case AST_DIVIDE:
1045   case AST_RELATIONAL_NEQ:
1046   case AST_POWER:
1047   case AST_FUNCTION_POWER:
1048     if (ASTNode_getNumChildren(node) == 2) {
1049       return 0;
1050     }
1051     return 1;
1052 
1053   case AST_LOGICAL_NOT:
1054     if (ASTNode_getNumChildren(node) == 1) {
1055       return 0;
1056     }
1057     return 1;
1058 
1059   case AST_INTEGER:
1060   case AST_REAL:
1061   case AST_REAL_E:
1062   case AST_RATIONAL:
1063   case AST_NAME:
1064   case AST_NAME_AVOGADRO:
1065   case AST_NAME_TIME:
1066   case AST_CONSTANT_E:
1067   case AST_CONSTANT_FALSE:
1068   case AST_CONSTANT_PI:
1069   case AST_CONSTANT_TRUE:
1070     return 0;
1071 
1072   case AST_LOGICAL_XOR:
1073   case AST_LAMBDA:
1074   case AST_FUNCTION:
1075   case AST_FUNCTION_ABS:
1076   case AST_FUNCTION_ARCCOS:
1077   case AST_FUNCTION_ARCCOSH:
1078   case AST_FUNCTION_ARCCOT:
1079   case AST_FUNCTION_ARCCOTH:
1080   case AST_FUNCTION_ARCCSC:
1081   case AST_FUNCTION_ARCCSCH:
1082   case AST_FUNCTION_ARCSEC:
1083   case AST_FUNCTION_ARCSECH:
1084   case AST_FUNCTION_ARCSIN:
1085   case AST_FUNCTION_ARCSINH:
1086   case AST_FUNCTION_ARCTAN:
1087   case AST_FUNCTION_ARCTANH:
1088   case AST_FUNCTION_CEILING:
1089   case AST_FUNCTION_COS:
1090   case AST_FUNCTION_COSH:
1091   case AST_FUNCTION_COT:
1092   case AST_FUNCTION_COTH:
1093   case AST_FUNCTION_CSC:
1094   case AST_FUNCTION_CSCH:
1095   case AST_FUNCTION_DELAY:
1096   case AST_FUNCTION_EXP:
1097   case AST_FUNCTION_FACTORIAL:
1098   case AST_FUNCTION_FLOOR:
1099   case AST_FUNCTION_LN:
1100   case AST_FUNCTION_LOG:
1101   case AST_FUNCTION_PIECEWISE:
1102   case AST_FUNCTION_ROOT:
1103   case AST_FUNCTION_SEC:
1104   case AST_FUNCTION_SECH:
1105   case AST_FUNCTION_SIN:
1106   case AST_FUNCTION_SINH:
1107   case AST_FUNCTION_TAN:
1108   case AST_FUNCTION_TANH:
1109   case AST_UNKNOWN:
1110     return 1;
1111   default:
1112     if (node != NULL)
1113     {
1114       ASTNodeType_t type = ASTNode_getType(node);
1115       const ASTBasePlugin* baseplugin = node->getASTPlugin(type);
1116       if (baseplugin != NULL)
1117       {
1118         // this needs to know what the parent node is so do actually
1119         // clone it
1120         ASTBasePlugin* plugin = baseplugin->clone();
1121         plugin->connectToParent((ASTNode*)(node));
1122         if (plugin->isFunction(type) && !plugin->hasPackageOnlyInfixSyntax())
1123         {
1124           delete plugin;
1125           return 1;
1126         }
1127         delete plugin;
1128         return 0;
1129       }
1130     }
1131   }
1132   //Shouldn't ever get here
1133   assert(0);
1134   return 1;
1135 }
1136 
1137 
1138 /** @endcond */
1139 
1140 LIBSBML_CPP_NAMESPACE_END
1141 
1142