Skip to content
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 quasi quotation #17

Open
3 of 4 tasks
harpocrates opened this issue Mar 23, 2017 · 3 comments
Open
3 of 4 tasks

Add quasi quotation #17

harpocrates opened this issue Mar 23, 2017 · 3 comments

Comments

@harpocrates
Copy link
Owner

harpocrates commented Mar 23, 2017

Besides the obvious quasi quotation, I want to capture variables with SubstNt tokens in Exp and bind variables with MatchNt in Pat. The following interaction should work:

>>> import Language.Rust.Quote
>>> :set -XQuasiQuotes
>>> let one = [lit| 1i32 |]
>>> [expr| |x: i32| -> $ret:ty $body:expr |] = [expr| |x: i32| -> i32 { x + $one } |]
ret :: Ty Span
body :: Expr Span
>>> import Language.Rust.Pretty
>>> pretty ret
i32
>>> pretty body
{ x + 1 }

Here is my current approach. It is terrible. I can't believe I have been reduced to this, but I've been thinking non-stop about this for 2 weeks now and have come up with nothing better.

  • Find an (unsafe) way to get a Q (String -> Maybe Type) function (instead of String -> Q Type) and use that to make the swapping function. Alternately, lex twice and extract all of the interesting tokens, look them up, and pass them in as a function in the second lexing pass.
  • Swap in NonTerminals containing error <variable-name> - but of the right type!
  • Make view patterns for the parser (instead of pattern matching directly) and make those patterns check for errors using something atrocious like this. Hopefully this function is cheap - if it isn't, we may have a problem (although I can think of more typeclass based hacks with specialization)
  • In the quoters below in dataToPatQ and dataToExpQ, whenever something is of the right type, check if it is defined. If so, do nothing, otherwise, use the error message (ewwwwww!) to lookup variable or make an identifier pattern.

Is there nothing better?

Maybe there is. Here are some unorganized thoughts on this topic:

  • Parametrize the P monad over an arbitrary monad (in particular Q and Identity, allowing me to swap tokens in this monad) - this is already done in another branch, although I have misgivings about performance. I had to disable the monomorphism restriction to get the generated files to compile, and I have a feeling that polymorphic lexer/parser will take a hit in performance.
  • Add "variable" cases to the AST. That has the crappy effect of annoying a user who is not using quasi quotes - the extra cases don't make sense in the context of pure Rust. I can always not export those constructors, but then pattern matches will never be exhaustive. :(

The "good"TM solution is probably an extensible AST with type families, but given that the AST is already ~1kloc, adding a type family for every constructor may get... a bit too big. Plus this also looks ugly to the end user.

Also, the solution described initially isn't so bad. It does not change the reliability of regular parsing - it only risks failing in the case of quasi quotes (which will be marked experimental and non-portable, and will only cause compile-time headaches).

harpocrates added a commit that referenced this issue Mar 23, 2017
Started implementing unquoting. So far, pretty much all my changes have been limited to the 'Quote'
module. I've followed the approach detailed in #17 - "all" that remains is updating the grammar to
use ViewPatterns instead of directly pattern matching (against possibly undefined things)
harpocrates added a commit that referenced this issue Mar 23, 2017
Started implementing unquoting. So far, pretty much all my changes have been limited to the 'Quote'
module. I've followed the approach detailed in #17 - "all" that remains is updating the grammar to
use ViewPatterns instead of directly pattern matching (against possibly undefined things)
harpocrates added a commit that referenced this issue Mar 24, 2017
Explained in detail the tradeoffs already mentioned in #17. Also cleaned up some code around
quasiquotes.
@harpocrates
Copy link
Owner Author

harpocrates commented Apr 4, 2017

Here is a simple example of why we need the ViewPatterns approach:

> let x = [expr| 1 |]
> let y = [stmt| $x; |]
<interactive>:42:9: error:
    • Exception when trying to run compile-time code:
        Subst x
      Code: Language.Haskell.TH.Quote.quoteExp stmt " $x; "
    • In the quasi-quotation: [stmt| $x; |]

Even more insidiously (check -ddump-splices and you'll see the error is "leaking")

> let x = [expr| 1 + 2 |]
> let y = [expr| $x * 3 |]
<interactive>:11:15: error:
    • Couldn't match expected type ‘Language.Rust.Data.Position.Span’
                  with actual type ‘Language.Rust.Syntax.AST.Expr
                                      Language.Rust.Data.Position.Span’
    • In the fifth argument of ‘Language.Rust.Syntax.AST.Binary’, namely
        ‘x’
      In the expression:
        (Language.Rust.Syntax.AST.Binary
           []
           Language.Rust.Syntax.AST.MulOp
           x
           (Language.Rust.Syntax.AST.Lit
              []
              (Language.Rust.Syntax.AST.Int
                 Language.Rust.Syntax.AST.Dec
                 3
                 Language.Rust.Syntax.AST.Unsuffixed
                 (Language.Rust.Data.Position.Span
                    (Language.Rust.Data.Position.Position 6 11 21)
                    (Language.Rust.Data.Position.Position 7 11 22)))
              (Language.Rust.Data.Position.Span
                 (Language.Rust.Data.Position.Position 6 11 21)
                 (Language.Rust.Data.Position.Position 8 11 23)))
           x)
      In an equation for ‘y’:
          y = (Language.Rust.Syntax.AST.Binary
                 []
                 Language.Rust.Syntax.AST.MulOp
                 x
                 (Language.Rust.Syntax.AST.Lit
                    []
                    (Language.Rust.Syntax.AST.Int
                       Language.Rust.Syntax.AST.Dec
                       3
                       Language.Rust.Syntax.AST.Unsuffixed
                       (Language.Rust.Data.Position.Span
                          (Language.Rust.Data.Position.Position 6 11 21)
                          (Language.Rust.Data.Position.Position 7 11 22)))
                    (Language.Rust.Data.Position.Span
                       (Language.Rust.Data.Position.Position 6 11 21)
                       (Language.Rust.Data.Position.Position 8 11 23)))
                 x)

@harpocrates
Copy link
Owner Author

harpocrates commented Apr 26, 2017

Alright. I've mulled over this for a week and I've come up with a new approach, which isn't a hack:

  • wrap all recursive ASTs in m (so data ExprT m a = PlusT (m (ExprT m a)) (m (ExprT m a)) a...)
  • update production rules to be parametrized over Context m => ExprT m Span where Context defines functions that need to look at the contents of parsed values (like the path case of patterns which checks for one identifier patterns)
  • make type synonyms (type Expr = ExprT Identity) and patterns (pattern Plus x y = PlusT (Identity x) (Identity y)). This also means fixing how Haddock handles pattern synonyms
  • re-make Quote!

This may take some time...

harpocrates added a commit that referenced this issue May 8, 2017
  * Tweaked Haddock related documentation in a couple of places.
  * Expose only Language.Rust.Syntax (no AST, Ident, and Token modules). This will make is easier to
    address #17 while maintaining backwards compatibility (the pattern synonyms will be defined in
    the 'Syntax' module which otherwise currently only just re-exports 'AST')
  * Add key points of 0.1.0.0 release to CHANGELOG. fixes #7
@harpocrates
Copy link
Owner Author

This won't make the 0.1.0.0 release. Instead, Language.Rust.Quote quasiquoters will not support $x or $x:ty splices/extractors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant