Copyright | (c) Alec Theriault 2017-2018 |
---|---|
License | BSD-style |
Maintainer | alec.theriault@gmail.com |
Stability | experimental |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
NOTE: the following uses hithero unimplemented antiquoting syntax
An AST and its text form should be completely isomorphic, with parse
and pretty
being the
functions allowing you to go back and forth between these forms. Unfortunately, this cannot really
be the case. The AST form can express programs which cannot be literally pretty printed and still
make sense. Sometimes, extra parens or semicolons need to be added.
Simple example
For example, consider the following interaction
>>>
import Language.Rust.Quote
>>>
import Language.Rust.Pretty
>>>
:set -XQuasiQuotes
>>>
x = [expr| 2 + 3 |]
>>>
y = [expr| 1 * $x |]
>>>
pretty y
0 * 1 + 2
The problem is that we haven't introduced the paren AST node (which we would have gotten had we
parsed 1 * (2 + 3)
. This is where resolve
steps in.
>>>
Right y' = resolve y
>>>
pretty y'
0 * (1 + 2)
More involved example
From the above, it is tempting to say: your pretty printer should be smarter! However, things are not always so simple. Consider the less obvious example:
>>>
fnBody = [expr| { let y = x; x += 1; y } + x |]
>>>
fn = [item| fn foo(mut x: i32) -> i32 { $fnBody } |]
>>>
pretty fn
fn foo(mut x: i32) -> i32 { { let y = x; x += 1; y } + x }
This is clearly not the desired output - this won't compile with rustc
because of an invariant in
blocks: if the block ends in an expression, that expression cannot start with a block. To fix this,
we call resolve
on the AST before pretty printing it.
>>>
Right fn' = resolve fn
>>>
pretty fn'
fn foo(mut x: i32) -> i32 { ({ let y = x; x += 1; y }) + x }
And now we have generated valid code.
Synopsis
- class Resolve a where
- resolve :: a -> Either ResolveFail a
- resolve' :: a -> a
- resolveVerbose :: a -> (a, Severity, [Issue])
- data Issue = Issue {}
- data Severity
- = Clean
- | Warning
- | Correction
- | Error
- data ResolveFail = ResolveFail [Dynamic] String
Documentation
class Resolve a where Source #
Since it is possible to have well-typed Haskell expressions which represent invalid Rust ASTs,
it is convenient to fix, warn, or fail ASTs before printing them. The Resolve
typeclass
provides such a facility.
A non-exhaustive list of the more obvious issues it covers:
- missing parens
- invalid identifiers
- invalid paths (for example, generic arguments on a module path)
- inner attributes on things that support only outer attributes (and vice-versa)
resolveM
resolve :: a -> Either ResolveFail a Source #
Convert some value to its resolved form. Informally, resolving a value involves checking that its invariants hold and, if they don't, report an error message or adjust the value so that the invariant holds.
A value of a type satsifying Parse
and Pretty
is resolved if
is an identity
operation on it. We further expect that parse
. pretty
resolve
be an identity operation on any output of
parse
.
Same as resolve
, but throws a ResolveFail
exception if it cannot resolve. Although
this function should not be used, it summarizes nicely the laws around Resolve
:
parse' . pretty' . resolve' == id
resolve' . parse' = parse'
resolveVerbose :: a -> (a, Severity, [Issue]) Source #
Instances
Localized information about an issue in a syntax tree.
Issue | |
|
Diagnostic for how severe an Issue
is.
Clean | Everything is normal (this variant is returned when there was nothing to resolve) |
Warning | There is something fishy looking (AST is valid, but may not be what you expect) |
Correction | The AST was invalid, but in a way that could be corrected |
Error | The AST was invalid in some way that could not be automatically fixed |
Instances
Bounded Severity Source # | |
Enum Severity Source # | |
Defined in Language.Rust.Pretty.Resolve | |
Eq Severity Source # | |
Ord Severity Source # | |
Defined in Language.Rust.Pretty.Resolve | |
Show Severity Source # | |
data ResolveFail Source #
Exceptions that occur during resolving. Unlike parse errors, we don't have positional information. Instead, we try to provide some context via a list of syntax trees which let you "zoom out" from the problematic node.
Instances
Show ResolveFail Source # | Does not show context information |
Defined in Language.Rust.Pretty.Resolve showsPrec :: Int -> ResolveFail -> ShowS # show :: ResolveFail -> String # showList :: [ResolveFail] -> ShowS # | |
Exception ResolveFail Source # | |
Defined in Language.Rust.Pretty.Resolve |