BNFC / bnfc

BNF Converter
http://bnfc.digitalgrammars.com/
586 stars 165 forks source link

[ CPP ] Throw runtime exception on parsing error #288

Closed miketsukerman closed 4 years ago

miketsukerman commented 4 years ago

When BNFC generated code embedded into another project it's more convenient to get parsing errors as C++ exceptions rather than just via standard output.

I've added parse_error class to the Absyn.H

class parse_error : public std::runtime_error {
  public:
  parse_error(int line, std::string str) : std::runtime_error(str) , m_line(line) {}
  int getLine() { return m_line; }
  private:
  int m_line;
  };
miketsukerman commented 4 years ago

Hi @andreasabel, I moved the definition of parser_exception to the separate header. Would it be okay?

miketsukerman commented 4 years ago

Regarding the tests. To be honest, not that much proficient in Haskell. I would be very pleased if you could help me with tests or at least explain to me how to extend them ;)

andreasabel commented 4 years ago

Thanks for the update, @miketsukerman ! At the moment I am a bit snowed under, but I will look at the PR when things are back to normal.

andreasabel commented 4 years ago

Hi Mike, with your patch the standard testsuite fails for C++ (with namespace), e.g.:

[TEST] Parameterized tests:C++ (with namespace):Examples:LBNF
bnfc -mMyMakefile --cpp '-p foobar' LBNF.cf
make -f MyMakefile
error running: make -f MyMakefile
exit status: 2
stderr: LBNF.y:26:18: error: no member named 'parse_error' in namespace 'foobar'
  throw  foobar::parse_error( foobaryy_mylinenumber,str);
         ~~~~~~~~^
1 error generated.

One way to run the test is simply to type make in the bnfc root folder.

The generated ParseError.H typically looks like this:

 namespace bnfc
 {
 class parse_error : public std::runtime_error
 {
 public:
     parse_error(int line, std::string str)
         : std::runtime_error(str)
         , m_line(line) {}
...

using its own namespace bnfc.

But the call to parse_error in Parser.C looks like this:

throw foo::parse_error(fooyy_mylinenumber,str);

expecting parse_error in the namespace as declared by the user.

If parse_error would be placed into Parser.H I think this would work automatically, because Parser.H uses the namespace as given by the -p flag to BNFC.

However, you might have strong reasons to prefer a separate .H file. (Then, of course, you would also have to use the namespace as given by the user.) Could you explain your rationale?

miketsukerman commented 4 years ago

Hi Mike, with your patch the standard testsuite fails for C++ (with namespace), e.g.:

[TEST] Parameterized tests:C++ (with namespace):Examples:LBNF
bnfc -mMyMakefile --cpp '-p foobar' LBNF.cf
make -f MyMakefile
error running: make -f MyMakefile
exit status: 2
stderr: LBNF.y:26:18: error: no member named 'parse_error' in namespace 'foobar'
  throw  foobar::parse_error( foobaryy_mylinenumber,str);
         ~~~~~~~~^
1 error generated.

One way to run the test is simply to type make in the bnfc root folder.

The generated ParseError.H typically looks like this:

 namespace bnfc
 {
 class parse_error : public std::runtime_error
 {
 public:
     parse_error(int line, std::string str)
         : std::runtime_error(str)
         , m_line(line) {}
...

using its own namespace bnfc.

But the call to parse_error in Parser.C looks like this:

throw foo::parse_error(fooyy_mylinenumber,str);

expecting parse_error in the namespace as declared by the user.

If parse_error would be placed into Parser.H I think this would work automatically, because Parser.H uses the namespace as given by the -p flag to BNFC.

However, you might have strong reasons to prefer a separate .H file. (Then, of course, you would also have to use the namespace as given by the user.) Could you explain your rationale?

Very simple - just a mistake. Fixed

andreasabel commented 4 years ago

Thanks. Er, I meant the rationale of having a separate .H file.

miketsukerman commented 4 years ago

Thanks. Er, I meant the rationale of having a separate .H file.

It was the simplest way to include it in the <bison>.y

#include "ParserError.H"
#include "Absyn.H"

#define YYMAXDEPTH 10000000

typedef struct yy_buffer_state *YY_BUFFER_STATE;
int yyparse(void);
int yylex(void);
YY_BUFFER_STATE bnfcyy_scan_string(const char *str);
void bnfcyy_delete_buffer(YY_BUFFER_STATE buf);
int bnfcyy_mylinenumber;
void bnfcinitialize_lexer(FILE * inp);
int bnfcyywrap(void)
{
  return 1;
}
void bnfcyyerror(const char *str)
{
  extern char *bnfcyytext;
  throw bnfc::parse_error(bnfcyy_mylinenumber,str);
}

I can't include Parser.H here. And I can't use forward declaration here as well unfortunately.

andreasabel commented 4 years ago

Ok, I see, thanks for the explanation!

I ran the testsuite on your PR and now there is only a small problem left:

* Failures:
  * Parameterized tests:C++:distclean removes all generated files
  * Parameterized tests:C++ (with namespace):distclean removes all generated files

This means that the cleaning goals of the generated Makefile for the CPP-STL backend (the one you changed) need to be extended by the new file ParserError.H.

I can fix this myself. Thanks for your contribution!