New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add sbml infix parsing and printing #1785
Conversation
- add parser function `parse_sbml()` - add print function `sbml()` - add sbml_tokenizer, sbml_parser, etc to symengine/parser/sbml - use `api.prefix {sbml}` in sbml_parser.yy to avoid nameclashes with main parser
I am excited about this PR! I don't think it is related, but we need to do something about the fail of the |
+ "' has no arguments"); | ||
} | ||
|
||
RCP<const Basic> SbmlParser::functionify(const std::string &name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can reduce the code duplication here using the same trick as init_sbml_printer_names
. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried this here: a159dcd
Is that what you meant? If so, it does reduce duplication, but it might also require more maintenance - e.g. if a new function is added to the main parser that is not supported by sbml, it would also have to be explicitly removed from the sbml parser. Either way is fine by me though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having to remember to remove something in one end when something was added in another sounds like an extra maintenance burden. Don't we want to avoid that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, looks good to me.
if a new function is added to the main parser that is not supported by sbml, it would also have to be explicitly removed from the sbml parser
Is it necessary to remove them though? Would it be bad if they were kept?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, looks good to me.
if a new function is added to the main parser that is not supported by sbml, it would also have to be explicitly removed from the sbml parser
Is it necessary to remove them though? Would it be bad if they were kept?
- it could mean invalid sbml infix gets parsed as valid
but it would give an error when printing: so that avoids the invalid sbml from being exported somewhere else- it would also print without errors, since
sbml
inherits the default function names, but the resulting sbml infix wouldn't be valid
- if a function is added to the main parser with the same name as an existing sbml one it would take precedence (since
insert
is a no-op if the key already exists), which could potentially change the meaning of this function in the sbml parser- but this would then cause a sbml_parser test to fail
tldr; no, it's probably fine to keep them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess the question is what should the sbml parser/printer do with math that the main parser+symengine understands, but that is not part of sbml?
- Raise an error
- need to define all valid functions -> code duplication
- or inherit default functions & remove invalid ones -> maintenance burden when defaults change
- Fallback to the default printer/parser behaviour
- no code duplication or maintenance issues
Currently we do 1.
when parsing sbml, and 2.
when printing sbml
- As a user I would prefer option
1.
for both printing & parsing: raising an error is much more helpful than silently parsing/producing invalid infix - But the current setup is also fine, since the user can just re-parse the printed output to check it is valid
- As a maintainer I would probably prefer to implement this by defining all valid functions (i.e. duplication rather than maintenance) but either way is fine by me
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also think the parser should throw on illegal sbml. Also the printer should throw for things that cannot be supported or isn't currenty supported in sbml. Some cases I guess could be supported with workarounds if needed. For example we have a printer for Fortran code in Pharmpy that converts sign(x)
to ABS(X) / X
(if x is known to be positive) or similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@isuruf what are your thoughts on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm okay with either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I'll keep the current behaviour for now then (sbml parser throws on invalid sbml, sbml printer falls back to default printer), and leave improving the printer to throw on invalid sbml for a future PR.
I've reverted to defining all sbml parser functions to avoid having to update the sbml parser when new functions are added to the main parser (4054db5)
symengine/parser/sbml/sbml_parser.h
Outdated
namespace SymEngine | ||
{ | ||
|
||
class SbmlParser |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make this inherit from the string parse so that parse_numeric
and other functions need not be duplicated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes - after struggling with strange compiler errors it turns out this also required a change to the main parser: b9d40f8
- bison uses `YYSTYPE` internally, and defines a macro to refer to the actual type, e.g. `#define YYSTYPE ParserSType` - when including multiple parsers this macro expansion resulted in missing/re- definition errors
…d of defining all functions
functions.insert({"arcsin", asin}); | ||
functions.insert({"arccos", acos}); | ||
functions.insert({"arctan", atan}); | ||
functions.insert({"arcsec", asec}); | ||
functions.insert({"arccsc", acsc}); | ||
functions.insert({"arccot", acot}); | ||
functions.insert({"arcsinh", asinh}); | ||
functions.insert({"arccosh", acosh}); | ||
functions.insert({"arctanh", atanh}); | ||
functions.insert({"arcsech", asech}); | ||
functions.insert({"arccoth", acoth}); | ||
functions.insert({"arccsch", acsch}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with adding these into the main parser.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done here: 8b80e66
I think the code is looking good. Would it be possible to keep the code coverage above 90% ? |
@rikardn I added a couple more tests (and found #1804 in the process), but I don't think the coverage will get much higher: nearly all the written code is fully covered - the remaining uncovered code is in the source files auto-generated by re2c/bison & appears to be mostly error handling or implementing things that we don't use: https://app.codecov.io/gh/symengine/symengine/compare/master...sbml_parser/diff |
parse_sbml()
sbml()
sbml_tokenizer.*
,sbml_parser.*
to symengine/parser/sbml (see design question: multiple parsers #1780)api.prefix {sbml}
in sbml_parser.yy to avoid nameclashes with main parser