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