yhirose / cpp-peglib

A single file C++ header-only PEG (Parsing Expression Grammars) library
MIT License
879 stars 112 forks source link

Assertion failed: (cut_stack.empty()), function ~Context, file peglib.h, line 803 #269

Closed curldivergence closed 1 year ago

curldivergence commented 1 year ago

Hi! First of all, thanks for the awesome library, I really enjoy using it. However, I've run into a problem: at some point the parsing started to fail with the assertion mentioned in the title. So I'm curious: does this assertion indicate a problem with the user-provided grammar, or something goes wrong inside the library? And if the issue lies in the grammar, are there any bits of advice you could share with me that could help debugging it? Also, I've tried parsing the same code on the online playground, and it didn't fail (but I'm not sure if the version there has assertions enabled).

For the reference,

here is my grammar ``` Program <- Item* Item <- Function / StructDeclaration StructDeclaration <- 'struct' Identifier '{' MemberDeclaration* '}' MemberDeclaration <- Type Identifier ';' Type <- ReferenceType / ValueType ValueType <- Identifier ( '<' TypeList '>' )? ReferenceType <- ValueType '&' TypeList <- Type (',' Type )* Statement <- Initialization / Assignment / Return / ExpressionStatement / Block Initialization <- Type Identifier '=' Expression ';' Assignment <- Expression '=' Expression ';' Return <- 'return' Expression ';' ExpressionStatement <- Expression ';' Expression <- InfixExpression(PrefixExpression, InfixOperators) InfixExpression(AtomicExpression, Operator) <- AtomicExpression (Operator AtomicExpression)* { precedence L == != L < > L + - L * / } If <- 'if' '(' Expression ')' Block ( 'else' Block )? Function <- Type Identifier '(' Parameters ')' Block Parameters <- List(Type Identifier, ',') Block <- '{' ( Statement )* '}' Call <- Primary (Arguments / Index / MemberAccess)* Arguments <- '(' List(Expression, ',') ')' Index <- '[' Expression ']' MemberAccess <- '.' Expression PrefixExpression <- PrefixOperator* Call Primary <- Identifier / If / Function / Array / Hash / Decimal / Integer / Boolean / String / '(' Expression ')' Array <- '[' List(Expression, ',') ']' Hash <- '{' List(HashPair, ',') '}' HashPair <- Expression ':' Expression Identifier <- < !Keyword [a-zA-Z][a-zA-Z0-9_]* > Integer <- < [0-9]+ > Decimal <- < [0-9]+ '.' [0-9]* > String <- < ["] < (!["] .)* > ["] > Boolean <- 'true' / 'false' PrefixOperator <- < [-!] > InfixOperators <- < [-+/*<>] / '==' / '!=' > Keyword <- 'true' | 'false' | 'return' | 'if' | 'else' List(Item, Delimiter) <- (Item (~Delimiter Item)*)? LineComment <- '//' (!LineEnd .)* &LineEnd LineEnd <- '\r\n' / '\r' / '\n' / !. %whitespace <- ([ \t\r\n]+ / LineComment)* %word <- [a-zA-Z]+ ```

(it's a bit vandalized to fit my purposes grammar from your implementation of the monkey language).

The code that leads to the assertion is fairly trivial:

u32 main(i32& testParam)
{
      2+3;
}

I think that the issue is somehow related to parsing the Expression rule, since if line 2+3; is deleted, the assertion goes away, too, but so far I didn't manage to pinpoint the exact issue.

Thank you!

UPD: Immediately after posting this I tried removing all my semantic actions (sorry, did not think of that earlier), and the assertion went away, too, so it's definitely me doing something wrong :) However I still have no idea what I'm doing wrong, so if you have any pieces of advice, I'll very appreciate if you shared them.

curldivergence commented 1 year ago

Looks like it is some weirdness in exception handling, most likely by doctest library (everything is happening inside a doctest's TEST_CASE): apparently I had some uncaught std::bad_any_casts in my semantic actions, and instead of reporting those, both console output and debugger for some reason reported this assertion inside cpp-peglib. After I started catching the bad any_casts locally and using a regular assert() to break the program right at the place where the bad cast happened (as opposed to doctest catching it and reporting in console), the assertion inside cpp-peglib stopped reproducing. So I think that cpp-peglib is not to blame - it was just being taken by surprise unwinding in the wrong moment :) For now I'll live with this assert workaround, and if I have time to get to the bottom of this, I'll report it to doctest people. Sorry for bothering you, I'm closing the issue :)

yhirose commented 1 year ago

No problem. Thanks for letting me know the story. Hope your project goes well!