Skip to content

Commit c265c31

Browse files
committed
Add partial parsers
This makes it easier to incorporate the parsers in other libraries afterwards.
1 parent 98c1afb commit c265c31

File tree

5 files changed

+162
-22
lines changed

5 files changed

+162
-22
lines changed

src/Language/Rust/Parser.hs

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ sourceFile :: SourceFile Span
2323

2424
module Language.Rust.Parser (
2525
-- * Parsing
26-
parse, parse', readSourceFile, readTokens, Parse(..), P, execParser, initPos, Span,
26+
parse, parsePartial, parse', readSourceFile, readTokens, Parse(..), P, execParser, initPos, Span,
2727
-- * Lexing
2828
lexToken, lexNonSpace, lexTokens, translateLit,
2929
-- * Input stream
@@ -38,7 +38,7 @@ import Language.Rust.Data.Position (Position, Span, Spanned, initPos, prettyPosi
3838
import Language.Rust.Parser.Internal
3939
import Language.Rust.Parser.Lexer (lexToken, lexNonSpace, lexTokens, lexicalError)
4040
import Language.Rust.Parser.Literals (translateLit)
41-
import Language.Rust.Parser.ParseMonad (P, execParser, parseError)
41+
import Language.Rust.Parser.ParseMonad (P, execParser, parseError, getPosition)
4242

4343
import Data.Typeable (Typeable)
4444
import Control.Exception (Exception, throw)
@@ -55,6 +55,12 @@ parse' is = case execParser parser is initPos of
5555
Left (pos, msg) -> throw (ParseFail pos msg)
5656
Right x -> x
5757

58+
-- | Parse something from an input stream (it is assumed the initial position is 'initPos'), return
59+
-- the position of the longest partial parse, as well as what was parsed
60+
parsePartial :: Parse a => InputStream -> Either (Position,String) (Position,a)
61+
parsePartial is = execParser parser' is initPos
62+
where parser' = flip (,) <$> partialParser <*> getPosition
63+
5864
-- | Given a path pointing to a Rust source file, read that file and parse it into a 'SourceFile'
5965
readSourceFile :: FilePath -> IO (SourceFile Span)
6066
readSourceFile fileName = parse' <$> readInputStream fileName
@@ -78,23 +84,77 @@ instance Exception ParseFail
7884

7985
-- | Describes things that can be parsed
8086
class Parse a where
87+
-- | Complete parser (fails if not all of the input is consumed)
8188
parser :: P a
8289

83-
instance Parse (Lit Span) where parser = parseLit
84-
instance Parse (Attribute Span) where parser = parseAttr
85-
instance Parse (Ty Span) where parser = parseTy
86-
instance Parse (Pat Span) where parser = parsePat
87-
instance Parse (Expr Span) where parser = parseExpr
88-
instance Parse (Stmt Span) where parser = parseStmt
89-
instance Parse (Item Span) where parser = parseItem
90-
instance Parse (SourceFile Span) where parser = parseSourceFile
91-
instance Parse TokenTree where parser = parseTt
92-
instance Parse TokenStream where parser = parseTokenStream
93-
instance Parse (Block Span) where parser = parseBlock
94-
instance Parse (ImplItem Span) where parser = parseImplItem
95-
instance Parse (TraitItem Span) where parser = parseTraitItem
96-
instance Parse (TyParam Span) where parser = parseTyParam
97-
instance Parse (LifetimeDef Span) where parser = parseLifetimeDef
98-
instance Parse (Generics Span) where parser = parseGenerics
99-
instance Parse (WhereClause Span) where parser = parseWhereClause
90+
-- | Partial parser (doesn't fail if not all the input is consumed)
91+
partialParser :: P a
92+
93+
instance Parse (Lit Span) where
94+
parser = parseLit
95+
partialParser = parseLitP
96+
97+
instance Parse (Attribute Span) where
98+
parser = parseAttr
99+
partialParser = parseAttrP
100+
101+
instance Parse (Ty Span) where
102+
parser = parseTy
103+
partialParser = parseTyP
104+
105+
instance Parse (Pat Span) where
106+
parser = parsePat
107+
partialParser = parsePatP
108+
109+
instance Parse (Expr Span) where
110+
parser = parseExpr
111+
partialParser = parseExprP
112+
113+
instance Parse (Stmt Span) where
114+
parser = parseStmt
115+
partialParser = parseStmtP
116+
117+
instance Parse (Item Span) where
118+
parser = parseItem
119+
partialParser = parseItemP
120+
121+
instance Parse (SourceFile Span) where
122+
parser = parseSourceFile
123+
partialParser = parseSourceFileP
124+
125+
instance Parse TokenTree where
126+
parser = parseTt
127+
partialParser = parseTtP
128+
129+
instance Parse TokenStream where
130+
parser = parseTokenStream
131+
partialParser = parseTokenStreamP
132+
133+
instance Parse (Block Span) where
134+
parser = parseBlock
135+
partialParser = parseBlockP
136+
137+
instance Parse (ImplItem Span) where
138+
parser = parseImplItem
139+
partialParser = parseImplItemP
140+
141+
instance Parse (TraitItem Span) where
142+
parser = parseTraitItem
143+
partialParser = parseTraitItemP
144+
145+
instance Parse (TyParam Span) where
146+
parser = parseTyParam
147+
partialParser = parseTyParamP
148+
149+
instance Parse (LifetimeDef Span) where
150+
parser = parseLifetimeDef
151+
partialParser = parseLifetimeDefP
152+
153+
instance Parse (Generics Span) where
154+
parser = parseGenerics
155+
partialParser = parseGenericsP
156+
157+
instance Parse (WhereClause Span) where
158+
parser = parseWhereClause
159+
partialParser = parseWhereClauseP
100160

src/Language/Rust/Parser/Internal.y

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,15 @@ To get information about transition states and such, run
2727
{-# LANGUAGE OverloadedStrings, OverloadedLists, PartialTypeSignatures #-}
2828

2929
module Language.Rust.Parser.Internal (
30+
-- * Complete parsers
3031
parseLit, parseAttr, parseTy, parsePat, parseStmt, parseExpr, parseItem, parseSourceFile,
3132
parseBlock, parseImplItem, parseTraitItem, parseTt, parseTokenStream, parseTyParam,
32-
parseGenerics, parseWhereClause, parseLifetimeDef
33+
parseGenerics, parseWhereClause, parseLifetimeDef,
34+
35+
-- * Partial parsers
36+
parseLitP, parseAttrP, parseTyP, parsePatP, parseStmtP, parseExprP, parseItemP, parseSourceFileP,
37+
parseBlockP, parseImplItemP, parseTraitItemP, parseTtP, parseTokenStreamP, parseTyParamP,
38+
parseGenericsP, parseWhereClauseP, parseLifetimeDefP
3339
) where
3440

3541
import Language.Rust.Syntax
@@ -66,12 +72,32 @@ import Text.Read (readMaybe)
6672
%name parseWhereClause where_clause
6773
%name parseGenerics generics
6874

75+
-- we also document the partial parsers
76+
%partial parseLitP lit
77+
%partial parseAttrP export_attribute
78+
%partial parseTyP export_ty
79+
%partial parsePatP pat
80+
%partial parseStmtP stmt
81+
%partial parseExprP expr
82+
%partial parseItemP mod_item
83+
%partial parseSourceFileContentsP source_file
84+
%partial parseBlockP export_block
85+
%partial parseImplItemP impl_item
86+
%partial parseTraitItemP trait_item
87+
%partial parseTtP token_tree
88+
%partial parseTokenStreamP token_stream
89+
%partial parseTyParamP ty_param
90+
%partial parseLifetimeDefP lifetime_def
91+
%partial parseWhereClauseP where_clause
92+
%partial parseGenericsP generics
93+
94+
6995
%tokentype { Spanned Token }
7096
%lexer { lexNonSpace >>= } { Spanned Eof _ }
7197
%monad { P } { >>= } { return }
7298

7399
%errorhandlertype explist
74-
%error { expParseError } --parseError }
100+
%error { expParseError }
75101

76102
%expect 0
77103

@@ -1794,6 +1820,13 @@ parseSourceFile = do
17941820
(as,items) <- parseSourceFileContents
17951821
pure (SourceFile sh as items)
17961822

1823+
-- | Parse a partial source file
1824+
parseSourceFileP :: P (SourceFile Span)
1825+
parseSourceFileP = do
1826+
sh <- lexShebangLine
1827+
(as,items) <- parseSourceFileContentsP
1828+
pure (SourceFile sh as items)
1829+
17971830
-- | Nudge the span endpoints of a 'Span' value
17981831
nudge :: Int -> Int -> Span -> Span
17991832
nudge leftSide rightSide (Span l r) = Span l' r'

src/Language/Rust/QuasiQuote.hs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{-# LANGUAGE TemplateHaskellQuotes #-}
2+
3+
module Language.Rust.QuasiQuote (
4+
expr
5+
) where
6+
7+
import Language.Rust.Parser.ParseMonad
8+
import Language.Rust.Parser.Internal
9+
import Language.Rust.Data.InputStream (inputStreamFromString)
10+
import Language.Rust.Data.Position (Position(..))
11+
12+
import Language.Haskell.TH
13+
import Language.Haskell.TH.Quote (QuasiQuoter(..), dataToExpQ, dataToPatQ)
14+
15+
import Data.Data (Data)
16+
17+
expr :: QuasiQuoter
18+
expr = QuasiQuoter { quoteExp = expr'
19+
, quotePat = notExprFail
20+
, quoteType = noExprFail
21+
, quoteDec = notExprFail
22+
}
23+
where
24+
notExprFail :: String -> Q a
25+
notExprFail _ = fail "This quasiquoter only works for expressions"
26+
27+
exprParser
28+
29+
expr' :: String -> Q Exp
30+
expr' s =
31+
case parsePartial (inputStreamFromString s) of
32+
Left (pos,msg) -> fail $ "Could not parse the type in the quasiquote
33+
let inp = inputStreamFromString s
34+
ty_either = parsePartial inp
35+
in
36+
37+
38+

src/Language/Rust/QuasiQuoteParser.y

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
{-# OPTIONS_HADDOCK hide, not-home #-}
3+
{-# LANGUAGE OverloadedStrings, OverloadedLists, PartialTypeSignatures #-}
4+
5+
module Language.Rust.QuasiQuoteParser (
6+
7+
) where
8+
9+

src/Language/Rust/Syntax/Token.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ data Token
9292
| Underscore -- ^ @_@ token
9393
| LifetimeTok Ident -- ^ a lifetime (something like @\'a@ or @\'static@)
9494
| Space Space Name -- ^ whitespace
95-
-- ^ doc comment with its contents, whether it is outer/inner, and whether it is inline or not
9695
| Doc String !AttrStyle !Bool
96+
-- ^ doc comment with its contents, whether it is outer/inner, and whether it is inline or not
9797
| Shebang -- ^ @#!@ shebang token
9898
| Eof -- ^ end of file token
9999

0 commit comments

Comments
 (0)