haskell / happy

The Happy parser generator for Haskell
Other
291 stars 84 forks source link

Question: monadic actions and attributes? #282

Closed snizzo closed 2 months ago

snizzo commented 5 months ago

Hi!

Is it possible to use a monadic action and specify an attribute in a grammar rule?

Example: MyRule { %myMonadicAction }

I'd like to do something like this:

MyRule { %myMonadicAction
                ; $$.myattr = 1 }

Is this possible? I didn't find anything useful explaining this. I've seen in the source there are 2 different parsers for attributed and non attributed grammar but couldn't find an answer. Thank you very much!

sgraf812 commented 5 months ago

I think you want to define a monadic action with {% }. Here's an example:

https://github.com/haskell/happy/blob/f5a5fb1ce9e9ad7e6411eeb05cffd368d7ff5ef5/packages/frontend/boot-src/Parser.ly#L84

In your case, MyRule {% do myMonadicAction; $$.myattr = 1 } should work (with a space before myMonadicAction, but none before %). See also https://haskell-happy.readthedocs.io/en/latest/using.html#sec-monads where this is somewhat documented.

snizzo commented 5 months ago

I tried with your instruction and: MyRule : {% myMonadicAction; $$.myattr = 0 }

doesn't work. Terminal says: MyParser.y: 134: error in attribute grammar rules: Parse error. I guess happy isn't able to parse that.

Edit: I tried with almost any combination possible, I don't know what I'm missing. An other option could be to write my own data Attributes a = Attr{ value :: a, testattr :: Int } and use that as return inside the monadic action along with (value $1) and (testattr $1) in my rules. I tested that with a simple rule and works, but it's a bit cumbersome and if it can be done in a cleaner way using the happy attributed semantic I'd prefer that.

Since I'm working with a medium sized grammar and converting it is a bit of work may I ask you if this is actually the only way? Thank you very much for your time

sgraf812 commented 4 months ago

I tried with your instruction and: MyRule : {% myMonadicAction; $$.myattr = 0 }

But that is not what I wrote. I wrote

In your case, MyRule {% do myMonadicAction; $$.myattr = 1 } should work

Note the use of do that your snippet is missing.

snizzo commented 4 months ago

Yes, I tried a lot of combinations but couldn't get past an happy parse error.

%attributetype { MyAttributes a }
%attribute value { a }
%attribute num   { Int }

%%

Integer  : L_integ  { $$ =  (read $1) :: Integer; $$.num = 0 }

Program : BEGINMARKER Integer ENDMARKER { $$ = Prog $2; $$.num = 0 }

BEGINMARKER : {-empty-} {% do beginBlock; $$.num = 0}
ENDMARKER : {-empty-} {% do endBlock; $$.num = 0}

The code above doesn't work and result in this output from happy:

happy -gcai ParLang.y
ParLang.y: 101: error in attribute grammar rules: Parse error

ParLang.y: 165: error in attribute grammar rules: Parse error

make: *** [Makefile:26: ParLang.hs] Error 1

Line 101 and 165 refers to the subsequent line to BEGINMARKER and ENDMARKER rules.


Instead, the not attributed grammar works perfectly with monadic actions, and the attributed one works perfectly without the monadic actions. It's the mix that doens't get through and I can't get where I'm failing.

sgraf812 commented 4 months ago

Apologies, I was somehow repeatedly missing that you were talking about the parser for attribute grammars.

Looking at the grammar file for the attribute grammar parser it appears there is no case for "just code": https://github.com/haskell/happy/blob/f5a5fb1ce9e9ad7e6411eeb05cffd368d7ff5ef5/packages/frontend/boot-src/AttrGrammarParser.ly#L41-L45

But I think you are supposed to use where in that case, which embeds a monadic action of type m (). If you just want a side-effect, simply do not fail. In your example, some variation of the following should work:

%attributetype { MyAttributes a }
%attribute value { a }
%attribute num   { Int }

%%

Integer  : L_integ  { $$ =  (read $1) :: Integer; $$.num = 0 }

Program : BEGINMARKER Integer ENDMARKER { $$ = Prog $2; $$.num = 0 }

BEGINMARKER : {-empty-} { where beginBlock; $$.num = 0 }
ENDMARKER : {-empty-} { where endBlock; $$.num = 0 }

Sadly, the only two test cases are non-monadic and hence do not really have side-effects, but they do use where rules. I think it would be great to have more test cases for monadic attribute grammars. Would you like to submit one when you think you have figured it out?

sgraf812 commented 4 months ago

@snizzo did my previous comment resolve your issues? Would you be so kind to submit a test case enshrining your solution?