@@ -1,228 +1,328 @@
1−{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
1+{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing #-}
2−{-# LANGUAGE OverloadedStrings #-}
2+{-# LANGUAGE BangPatterns, Rank2Types, UnboxedTuples, OverloadedStrings #-}
3module Parse.Primitives
3module Parse.Primitives
4− ( I.Parser
7+ , Parser(..)
7− , try, deadend, hint, endOfFile
8− , noFloatsAllowedInPatterns
10− , pushContext, popContext
11+ , pushContext, popContext, inContext
12+ , getPosition, getCol, addLocation
11 , getIndent, setIndent
13 , getIndent, setIndent
12− , W.SPos
14+ , word1, word2
13− , SParser
15+ , unsafeIndex, isWord, getCharWidth
14− , addLocation, inContext
15− , spaces, noSpace, checkSpace, checkAligned, checkFreshLine
20import Prelude hiding (length)
20import Prelude hiding (length)
21+import qualified Control.Applicative as Applicative (Applicative(..))
21import qualified Data.ByteString.Internal as B
23import qualified Data.ByteString.Internal as B
24+import Data.Word (Word8, Word16)
25+import Foreign.Ptr (Ptr, plusPtr)
26+import Foreign.Storable (peek)
27+import GHC.ForeignPtr (touchForeignPtr, unsafeForeignPtrToPtr)
23−import Parse.Primitives.Internals (Parser(..), State(..), expect, noError)
29+import qualified File.IO as IO
24−import qualified Parse.Primitives.Internals as I
25−import qualified Parse.Primitives.Whitespace as W
26import qualified Reporting.Annotation as A
30import qualified Reporting.Annotation as A
27−import qualified Reporting.Error.Syntax as E
28−import qualified Reporting.Region as R
35−run :: Parser a -> B.ByteString -> Either E.Error a
37+newtype Parser c x a =
36−run parser bytes =
38+ Parser (
37− runAt 1 1 parser bytes
39+ forall b.
41+ -> (a -> State c -> b) -- consumed ok
42+ -> (a -> State c -> b) -- empty ok
43+ -> (Word16 -> Word16 -> Context c -> x -> b) -- consumed err
44+ -> (Word16 -> Word16 -> Context c -> x -> b) -- empty err
51+ { _pos :: !(Ptr Word8)
52+ , _end :: !(Ptr Word8)
56+ , _context :: Context c
62+ | Frame {-# UNPACK #-} !Word16 {-# UNPACK #-} !Word16 c (Context c)
40−runAt :: Int -> Int -> Parser a -> B.ByteString -> Either E.Error a
66+-- FUNCTOR
41−runAt startRow startColumn (Parser parser) (B.PS fp offset length) =
42− case parser (State fp offset (offset + length) 0 startRow startColumn []) Ok Err Ok Err of
46− Err (E.ParseError row col problem) ->
68+
69+instance Functor (Parser c x) where
71+ fmap f (Parser parser) =
72+ Parser $ \state cok eok cerr eerr ->
48− pos = R.Position row col
74+ cok' a s = cok (f a) s
49− mkError overallRegion subRegion =
75+ eok' a s = eok (f a) s
50− Left (E.Parse overallRegion subRegion problem)
52− case problem of
77+ parser state cok' eok' cerr eerr
53− E.BadChar endCol ->
78+
54− mkError (R.Region pos (R.Position row endCol)) Nothing
79+
56− E.BadEscape width _ ->
84+instance Applicative.Applicative (Parser c x) where
57− mkError (R.Region pos (R.Position row (col + width))) Nothing
85+ {-# INLINE pure #-}
59− E.BadUnderscore badCol ->
88+ {-# INLINE (<*>) #-}
60− mkError (R.Region pos (R.Position row badCol)) Nothing
89+ (<*>) = ap
62− E.BadOp _ ((_, start) : _) ->
63− mkError (R.Region start pos) (Just (R.Region pos pos))
65− E.Theories ((_, start) : _) _ ->
66− mkError (R.Region start pos) (Just (R.Region pos pos))
69− mkError (R.Region pos pos) Nothing
97+oneOf :: x -> [Parser c x a] -> Parser c x a
99+ Parser $ \state cok eok cerr eerr ->
100+ oneOfHelp state cok eok cerr eerr x parsers
105+ -> (a -> State c -> b)
106+ -> (a -> State c -> b)
107+ -> (Word16 -> Word16 -> Context c -> x -> b)
108+ -> (Word16 -> Word16 -> Context c -> x -> b)
112+oneOfHelp state cok eok cerr eerr x parsers =
114+ Parser parser : parsers ->
117+ oneOfHelp state cok eok cerr eerr x parsers
119+ parser state cok eok cerr eerr'
76−data Result a
121+ [] ->
77− = Ok a State E.ParseError
122+ let
78− | Err E.ParseError
123+ (State _ _ _ row col ctx) = state
82−-- COMBINATORS
129+-- MONAD
85−try :: Parser a -> Parser a
132+instance Monad (Parser c x) where
86−try (Parser parser) =
133+ {-# INLINE return #-}
87− Parser $ \state cok _ eok eerr ->
135+ Parser $ \state _ eok _ _ ->
88− parser state cok eerr eok eerr
136+ eok value state
138+ {-# INLINE (>>=) #-}
139+ (Parser parserA) >>= callback =
140+ Parser $ \state cok eok cerr eerr ->
144+ Parser parserB -> parserB s cok cok cerr cerr
91−deadend :: [E.Theory] -> Parser a
146+ eok' a s =
92−deadend thrys =
147+ case callback a of
93− Parser $ \(State _ _ _ _ row col ctx) _ _ _ eerr ->
148+ Parser parserB -> parserB s cok eok cerr eerr
94− eerr (E.ParseError row col (E.Theories ctx thrys))
149+ in
150+ parserA state cok' eok' cerr eerr
97−hint :: E.Next -> Parser a -> Parser a
98−hint next (Parser parser) =
99− Parser $ \state@(State _ _ _ _ row col ctx) cok cerr eok eerr ->
102− eok x s (expect row col ctx (E.Expecting next))
104− eerr' _ =
154+-- FROM BYTESTRING
105− eerr (expect row col ctx (E.Expecting next))
155+
159+ | Err Word16 Word16 (Context c) x
162+fromByteString :: Parser c x a -> B.ByteString -> Result c x a
163+fromByteString (Parser parser) (B.PS fp offset length) =
164+ B.accursedUnutterablePerformIO $
166+ !pos = plusPtr (unsafeForeignPtrToPtr fp) offset
167+ !end = plusPtr pos length
168+ !result = parser (State pos end 0 1 1 NoContext) Ok Ok Err Err
107− parser state cok cerr eok' eerr'
170+ do touchForeignPtr fp
110−endOfFile :: Parser ()
174+fromFile :: Parser c x a -> FilePath -> IO (Result c x a)
111−endOfFile =
175+fromFile parser path =
112− Parser $ \state@(State _ offset terminal _ _ _ _) _ _ eok eerr ->
176+ fromByteString parser <$> IO.readUtf8 path -- TODO try mmap
113− if offset < terminal then
116− eok () state noError
119−noFloatsAllowedInPatterns :: Parser a
120−noFloatsAllowedInPatterns =
121− Parser $ \(State _ _ _ _ row col _) _ cerr _ _ ->
122− cerr (E.ParseError row col E.FloatInPattern)
126−-- STATE
183+getCol :: Parser c x Word16
185+ Parser $ \state@(State _ _ _ _ col _) _ eok _ _ ->
129{-# INLINE getPosition #-}
189{-# INLINE getPosition #-}
130−getPosition :: Parser R.Position
190+getPosition :: Parser c x A.Position
131getPosition =
191getPosition =
132− Parser $ \state@(State _ _ _ _ row col _) _ _ eok _ ->
192+ Parser $ \state@(State _ _ _ row col _) _ eok _ _ ->
133− eok (R.Position row col) state noError
193+ eok (A.Position row col) state
136−getIndent :: Parser Int
196+addLocation :: Parser c x a -> Parser c x (A.Located a)
137−getIndent =
197+addLocation (Parser parser) =
138− Parser $ \state@(State _ _ _ indent _ _ _) _ _ eok _ ->
198+ Parser $ \state@(State _ _ _ srow scol _) cok eok cerr eerr ->
139− eok indent state noError
199+ let
140−
200+ cok' a s@(State _ _ _ erow ecol _) = cok (A.At (A.Region (A.Position srow scol) (A.Position erow ecol)) a) s
201+ eok' a s@(State _ _ _ erow ecol _) = eok (A.At (A.Region (A.Position srow scol) (A.Position erow ecol)) a) s
203+ parser state cok' eok' cerr eerr
144− Parser $ \state@(State _ _ _ _ _ col _) _ _ eok _ ->
145− eok col state noError
148−pushContext :: R.Position -> E.Context -> Parser ()
207+-- INDENT
149−pushContext pos ctx =
150− Parser $ \state@(State _ _ _ _ _ _ context) _ _ eok _ ->
151− eok () (state { _context = (ctx, pos) : context }) noError
154−popContext :: a -> Parser a
210+getIndent :: Parser c x Word16
155−popContext value =
211+getIndent =
156− Parser $ \state@(State _ _ _ _ _ _ context) _ _ eok _ ->
212+ Parser $ \state@(State _ _ indent _ _ _) _ eok _ _ ->
157− eok value (state { _context = tail context }) noError
213+ eok indent state
160−setIndent :: Int -> Parser ()
216+setIndent :: Word16 -> Parser c x ()
161setIndent indent =
217setIndent indent =
162− Parser $ \state _ _ eok _ ->
218+ Parser $ \(State pos end _ row col context) _ eok _ _ ->
163− eok () (state { _indent = indent }) noError
219+ let
221+ State pos end indent row col context
171− Parser (a, R.Position, W.SPos)
230+pushContext :: A.Position -> c -> Parser c x ()
231+pushContext (A.Position r c) ctx =
232+ Parser $ \(State pos end indent row col context) _ eok _ _ ->
234+ !newContext = Frame r c ctx context
235+ !newState = State pos end indent row col newContext
175−-- LOCATION
240+popContext :: a -> Parser c x a
242+ Parser $ \(State pos end indent row col context) _ eok _ _ ->
244+ Frame _ _ _ context ->
247+ State pos end indent row col context
252+ error "compiler error, trying to popContext on NoContext"
255+inContext :: c -> Parser c x start -> Parser c x a -> Parser c x a
256+inContext tag (Parser parserStart) (Parser parserA) =
257+ Parser $ \state@(State _ _ _ frow fcol _) cok eok cerr eerr ->
259+ cokS _ (State pos end indent row col ctx) =
261+ !newContext = Frame frow fcol tag ctx
262+ !newState = State pos end indent row col newContext
263+ cokA a s = cok a (s { _context = ctx })
265+ parserA newState cokA cokA cerr cerr
267+ eokS _ (State pos end indent row col ctx) =
269+ !newContext = Frame frow fcol tag ctx
270+ !newState = State pos end indent row col newContext
271+ cokA a s = cok a (s { _context = ctx })
272+ eokA a s = eok a (s { _context = ctx })
274+ parserA newState cokA eokA cerr eerr
276+ parserStart state cokS eokS cerr eerr
178−addLocation :: Parser a -> Parser (A.Located a)
180− do start <- getPosition
183− return (A.at start end value)
186−inContext :: R.Position -> E.Context -> Parser a -> Parser a
187−inContext pos ctx parser =
188− do pushContext pos ctx
283+word1 :: Word8 -> x -> Parser c x ()
285+ Parser $ \(State pos end indent row col ctx) cok _ _ eerr ->
286+ if pos < end && unsafeIndex pos == word then
287+ let !newState = State (plusPtr pos 1) end indent row (col + 1) ctx in
193−-- WHITESPACE VARIATIONS
293+word2 :: Word8 -> Word8 -> x -> Parser c x ()
295+ Parser $ \(State pos end indent row col ctx) cok _ _ eerr ->
297+ !pos1 = plusPtr pos 1
299+ if pos1 < end && unsafeIndex pos == w1 && unsafeIndex pos1 == w2 then
300+ let !newState = State (plusPtr pos 2) end indent row (col + 2) ctx in
198− checkSpace =<< W.whitespace
201−noSpace :: R.Position -> W.SPos -> Parser ()
307+-- LOW-LEVEL CHECKS
202−noSpace pos (W.SPos spos) =
208−checkSpace :: W.SPos -> Parser ()
310+unsafeIndex :: Ptr Word8 -> Word8
209−checkSpace (W.SPos (R.Position _ col)) =
311+unsafeIndex ptr =
210− do indent <- getIndent
312+ B.accursedUnutterablePerformIO (peek ptr)
211− if col > indent && col > 1
213− else deadend [E.BadSpace]
216−checkAligned :: W.SPos -> Parser ()
315+{-# INLINE isWord #-}
217−checkAligned (W.SPos (R.Position _ col)) =
316+isWord :: Ptr Word8 -> Ptr Word8 -> Word8 -> Bool
218− do indent <- getIndent
317+isWord pos end word =
219− if col == indent
318+ pos < end && unsafeIndex pos == word
221− else deadend [E.BadSpace]
224−checkFreshLine :: W.SPos -> Parser ()
321+getCharWidth :: Ptr Word8 -> Ptr Word8 -> Word8 -> Int
225−checkFreshLine (W.SPos (R.Position _ col)) =
322+getCharWidth _pos _end word
226− if col == 1
323+ | word < 0x80 = 1
227− then return ()
324+ | word < 0xc0 = error "Need UTF-8 encoded input. Ran into unrecognized bits."
228− else deadend [E.BadSpace]
325+ | word < 0xe0 = 2
328+ | True = error "Need UTF-8 encoded input. Ran into unrecognized bits."