@@ -1,84 +1,681 @@
1{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
1{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
2+{-# LANGUAGE BangPatterns, Rank2Types, UnboxedTuples #-}
2module Parse.Primitives
3module Parse.Primitives
3− ( IParser, parse
4+ ( Parser
4− , expecting, failure
5+ , run
5− , withPos, block, checkIndent, indented
6+ , try, lookAhead, failure, deadend, expecting, endOfFile
8+ , text, lowVar, capVar
9+ , getPosition, setIndent
10+ , Space(..), whitespace
9−import Control.Monad.Identity (Identity, runIdentity)
15+import Prelude hiding (length)
16+import qualified Data.Char as Char
17+import qualified Data.Text as Text
18+import qualified Data.Text.Array as Text
19+import qualified Data.Text.Internal as Text
20+import qualified Data.Text.Unsafe as Text
21+import qualified Data.Text.Lazy as LT
22+import qualified Data.Text.Lazy.Builder as LB
23+import Data.Text.Internal.Encoding.Utf16 (chr2)
24+import Data.Text.Internal.Unsafe.Char (unsafeChr)
25+import qualified Control.Applicative as Applicative ( Applicative(..), Alternative(..) )
10−import qualified Text.Parsec.Pos as Pos
28+import qualified Reporting.Region as Region
12− ( ParsecT, runParserT, getState, putState
13− , SourcePos, sourceColumn, sourceLine, setSourceLine, getPosition
15− , ParseError, (<?>), parserFail, anyToken, many1
23−type IParser a =
35+newtype Parser a =
24− ParsecT String SourcePos Identity a
36+ Parser
40+ -> (a -> State -> b) -- consumed ok
41+ -> (Error -> b) -- consumed err
42+ -> (a -> State -> b) -- empty ok
43+ -> (Error -> b) -- empty err
50+ { _array :: !Text.Array
73+run :: Parser a -> Text.Text -> Either Error a
74+run parser (Text.Text array offset length) =
75+ case _run parser (State array offset length 0 1 1) Ok Err Ok Err of
87+try :: Parser a -> Parser a
89+ Parser $ \state cok _ eok eerr ->
90+ _run parser state cok eerr eok eerr
93+lookAhead :: Parser a -> Parser a
95+ Parser $ \state _ cerr eok eerr ->
96+ _run parser state eok cerr eok eerr
97+ -- TODO used to mess with error info in ok. Why?
100+failure :: error -> Parser a
102+ Parser $ \(State _ _ _ _ row col) _ cerr _ _ ->
103+ cerr (Error row col)
106+deadend :: error -> Parser a
108+ Parser $ \(State _ _ _ _ row col) _ _ _ eerr ->
109+ eerr (Error row col)
112+expecting :: String -> Parser a -> Parser a
113+expecting _msg parser =
114+ Parser $ \state@(State _ _ _ _ row col) cok cerr eok eerr ->
117+ eerr' _err = eerr (Error row col)
119+ _run parser state cok cerr eok' eerr'
122+endOfFile :: Parser ()
124+ Parser $ \state@(State _ _ length _ row col) _ _ eok eerr ->
129+ eerr (Error row col)
136+instance Functor Parser where
138+ Parser $ \state cok cerr eok eerr ->
140+ cok' x s = cok (f x) s
141+ eok' x s = eok (f x) s
143+ _run parser state cok' cerr eok' eerr
150+instance Applicative.Applicative Parser where
152+ (<*>) = ap -- TODO: Can this be optimized?
155+instance Applicative.Alternative Parser where
156+ empty = allTheOptionsFailed
160+oneOf :: [Parser a] -> Parser a
162+ foldr oneOfHelp allTheOptionsFailed parsers
165+allTheOptionsFailed :: Parser a
166+allTheOptionsFailed =
167+ Parser $ \_ _ _ _ eerr ->
168+ eerr $ error "TODO allTheOptionsFailed"
171+oneOfHelp :: Parser a -> Parser a -> Parser a
172+oneOfHelp parser1 parser2 =
173+ Parser $ \state cok cerr eok eerr ->
177+ neok y state' = eok y state'
178+ neerr err' = eerr (error "TODO oneOfHelp" err err')
180+ _run parser2 state cok cerr neok neerr
182+ _run parser1 state cok cerr eok meerr
189+instance Monad Parser where
191+ Parser $ \state _ _ eok _ ->
194+ parser >>= callback =
195+ Parser $ \state cok cerr eok eerr ->
199+ peok y state2 = cok y state2
200+ peerr err = cerr (error "TODO >>= cok" err)
202+ _run (callback x) state1 cok cerr peok peerr
206+ peok y state2 = eok y state2
207+ peerr err = eerr (error "TODO >>= eok" err)
209+ _run (callback x) state1 cok cerr peok peerr
211+ _run parser state cok' cerr eok' eerr
218+text :: Text.Text -> Parser ()
219+text (Text.Text tArray tOffset tLen) =
220+ Parser $ \(State array offset length indent row col) cok _ _ eerr ->
221+ if tLen <= length && Text.equal tArray tOffset array offset tLen then
223+ (# newRow, newCol #) =
224+ moveCursor tArray tOffset tLen row col
226+ cok () (State array (offset + tLen) (length - tLen) indent newRow newCol)
229+ eerr (error "TODO text")
232+moveCursor :: Text.Array -> Int -> Int -> Int -> Int -> (# Int, Int #)
233+moveCursor array offset length row col =
239+ !word1 = Text.unsafeIndex array offset
241+ if word1 == 0x000A then
242+ moveCursor array (offset + 1) (length - 1) (row + 1) 1
244+ else if word1 < 0xD800 || word1 > 0xDBFF then
245+ moveCursor array (offset + 1) (length - 1) row (col + 1)
248+ moveCursor array (offset + 2) (length - 2) row (col + 1)
255+lowVar :: Parser Text.Text
257+ varPrim Char.isLower
260+capVar :: Parser Text.Text
262+ varPrim Char.isUpper
265+{-# INLINE varPrim #-}
266+varPrim :: (Char -> Bool) -> Parser Text.Text
267+varPrim isGoodFirstChar =
268+ Parser $ \(State array offset length indent row col) cok _ _ eerr ->
270+ eerr (error "TODO varPrim")
274+ (Text.Iter char size) = peek array offset
276+ if isGoodFirstChar char then
278+ (# newOffset, newLength, newCol #) =
279+ varPrimHelp array (offset + size) (length - size) (col + 1)
285+ do mutableArray <- Text.new newSize
286+ Text.copyI mutableArray 0 array offset newSize
290+ Text.Text (Text.run makeArray) 0 newSize
292+ cok copiedText (State array newOffset newLength indent row newCol)
295+ eerr (error "TODO varPrim 2")
299+peek :: Text.Array -> Int -> Text.Iter
302+ !word1 = Text.unsafeIndex array offset
304+ if word1 < 0xD800 || word1 > 0xDBFF then
305+ Text.Iter (unsafeChr word1) 1
307+ Text.Iter (chr2 word1 (Text.unsafeIndex array (offset + 1))) 2
310+varPrimHelp :: Text.Array -> Int -> Int -> Int -> (# Int, Int, Int #)
311+varPrimHelp array offset length col =
313+ (# offset, length, col #)
317+ (Text.Iter char size) = peek array offset -- TODO look for latin chars directly?
319+ if Char.isAlphaNum char || char == '_' then
320+ varPrimHelp array (offset + size) (length - size) (col + 1)
323+ (# offset, length, col #)
330+getPosition :: Parser Region.Position
332+ Parser $ \state@(State _ _ _ _ row col) _ _ eok _ ->
333+ eok (Region.Position row col) state
336+setIndent :: Int -> Parser ()
338+ Parser $ \state _ _ eok _ ->
339+ eok () (state { _indent = indent })
355+whitespace :: Parser Space
357+ Parser $ \(State array offset length indent row col) cok cerr _ _ ->
358+ case eatSpaces array offset length row col of
362+ Right (newOffset, newLength, newRow, newCol) ->
365+ if newOffset == offset then
366+ None -- TODO empty success?
367+ else if newCol == 1 then
369+ else if newCol > indent then
371+ else if newCol == indent then
376+ cok space (State array newOffset newLength indent newRow newCol)
379+eatSpaces :: Text.Array -> Int -> Int -> Int -> Int -> Either Error ( Int, Int, Int, Int )
380+eatSpaces array offset length row col =
382+ Right ( offset, length, row, col )
385+ case Text.unsafeIndex array offset of
387+ eatSpaces array (offset + 1) (length - 1) row (col + 1)
390+ eatSpaces array (offset + 1) (length - 1) (row + 1) 1
393+ eatMultiComment array offset length row col
396+ eatLineComment array offset length row col
399+ eatSpaces array (offset + 1) (length - 1) row col
402+ Left (Error row col)
405+ Right ( offset, length, row, col )
412+eatLineComment :: Text.Array -> Int -> Int -> Int -> Int -> Either Error ( Int, Int, Int, Int )
413+eatLineComment array offset length row col =
415+ Right ( offset, length, row, col )
418+ case Text.unsafeIndex array (offset + 1) of
420+ eatLineCommentHelp array (offset + 2) (length - 2) row (col + 2)
423+ Right ( offset, length, row, col )
426+eatLineCommentHelp :: Text.Array -> Int -> Int -> Int -> Int -> Either Error ( Int, Int, Int, Int )
427+eatLineCommentHelp array offset length row col =
429+ Right ( offset, length, row, col )
433+ !word = Text.unsafeIndex array offset
435+ if word == 0x000A {- \n -} then
436+ eatSpaces array (offset + 1) (length - 1) (row + 1) 1
438+ else if word < 0xD800 || word > 0xDBFF then
439+ eatLineCommentHelp array (offset + 1) (length - 1) row (col + 1)
442+ eatLineCommentHelp array (offset + 2) (length - 2) row (col + 1)
449+eatMultiComment :: Text.Array -> Int -> Int -> Int -> Int -> Either Error ( Int, Int, Int, Int )
450+eatMultiComment array offset length row col =
452+ Right ( offset, length, row, col )
455+ case Text.unsafeIndex array (offset + 1) of
457+ eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 2) 1
460+ Right ( offset, length, row, col )
463+eatMultiCommentHelp :: Text.Array -> Int -> Int -> Int -> Int -> Int -> Either Error ( Int, Int, Int, Int )
464+eatMultiCommentHelp array offset length row col openComments =
466+ Left (Error row col) -- never closed the comment!
470+ !word = Text.unsafeIndex array offset
472+ if word == 0x000A {- \n -} then
474+ eatMultiCommentHelp array (offset + 1) (length - 1) (row + 1) 1 openComments
476+ else if word == 0x002D {- - -} && length > 1 && Text.unsafeIndex array (offset + 1) == 0x007D {- } -} then
478+ if openComments == 1 then
479+ eatSpaces array (offset + 2) (length - 2) row (col + 2)
481+ eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 2) (openComments - 1)
483+ else if word == 0x007B {- { -} && length > 1 && Text.unsafeIndex array (offset + 1) == 0x002D {- - -} then
485+ eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 2) (openComments + 1)
487+ else if word < 0xD800 || word > 0xDBFF then
489+ eatMultiCommentHelp array (offset + 1) (length - 1) row (col + 1) openComments
493+ eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 1) openComments
500+string :: Parser Text.Text
502+ Parser $ \(State array offset length indent row col) cok cerr _ eerr ->
504+ eerr (Error row col)
508+ !word = Text.unsafeIndex array offset
510+ if word /= 0x0022 {- " -} then
511+ eerr (Error row col)
513+ else if Text.unsafeIndex array (offset + 1) == 0x0022 {- " -} && Text.unsafeIndex array (offset + 2) == 0x0022 {- " -} then
514+ error "TODO multiString"
517+ case singleString array (offset + 1) (length - 1) row (col + 1) (offset + 1) mempty of
521+ Right ( newOffset, newLength, newCol, str ) ->
522+ cok str (State array newOffset newLength indent row newCol)
529+singleString :: Text.Array -> Int -> Int -> Int -> Int -> Int -> LB.Builder -> Either Error ( Int, Int, Int, Text.Text )
530+singleString array offset length row col initialOffset builder =
532+ Left (Error row col)
536+ !word = Text.unsafeIndex array offset
538+ if word == 0x0022 {- " -} then
541+ mappend builder $ LB.fromText $
542+ Text.Text array initialOffset (offset - initialOffset)
545+ LT.toStrict (LB.toLazyTextWith (offset - initialOffset) finalBuilder)
547+ Right ( offset + 1, length - 1, col + 1, str )
549+ else if word == 0x000A {- \n -} then
550+ Left (Error row col)
552+ else if word == 0x005C {- \ -} then
553+ case eatEscape array (offset + 1) (length - 1) row (col + 1) of
557+ Right ( size, char ) ->
559+ !newOffset = offset + size
560+ chunk = Text.Text array initialOffset (offset - initialOffset)
561+ newBuilder = mappend builder (mappend (LB.fromText chunk) (LB.singleton char))
563+ singleString array newOffset (length - size) row (col + size) newOffset newBuilder
565+ else if word < 0xD800 || word > 0xDBFF then
566+ singleString array (offset + 1) (length - 1) row (col + 1) initialOffset builder
569+ singleString array (offset + 2) (length - 2) row (col + 1) initialOffset builder
572+eatEscape :: Text.Array -> Int -> Int -> Int -> Int -> Either Error ( Int, Char )
573+eatEscape array offset length row col =
575+ Left (Error row col)
578+ case Text.unsafeIndex array offset of
579+ 0x0061 {- a -} -> Right ( 2, '\a' )
580+ 0x0062 {- b -} -> Right ( 2, '\b' )
581+ 0x0066 {- f -} -> Right ( 2, '\f' )
582+ 0x006E {- n -} -> Right ( 2, '\n' )
583+ 0x0072 {- r -} -> Right ( 2, '\r' )
584+ 0x0074 {- t -} -> Right ( 2, '\t' )
585+ 0x0076 {- v -} -> Right ( 2, '\v' )
586+ 0x0022 {- " -} -> Right ( 2, '\"' )
587+ 0x005C {- \ -} -> Right ( 2, '\\' )
588+ 0x0027 {- ' -} -> Right ( 2, '\'' )
590+ case eatHex array (offset + 1) (length - 1) 0 of
592+ Left (Error row col)
594+ Just (newOffset, code) ->
595+ if code <= 0x10FFFF then
596+ Right ( newOffset - offset + 1, toEnum (fromInteger code) )
598+ Left (Error row col)
601+ Left (Error row col)
604+eatHex :: Text.Array -> Int -> Int -> Integer -> Maybe ( Int, Integer )
605+eatHex array offset length n =
611+ !word = Text.unsafeIndex array offset
613+ if 0x0030 <= word && word <= 0x0039 then
614+ eatHex array (offset + 1) (length - 1) (16 * n + fromIntegral (word - 0x0030))
616+ else if 0x0061 <= word && word <= 0x0066 then
617+ eatHex array (offset + 1) (length - 1) (16 * n + 10 + fromIntegral (word - 0x0061))
27−parse :: IParser a -> String -> Either ParseError a
619+ else if 0x0041 <= word && word <= 0x0046 then
28−parse parser source =
620+ eatHex array (offset + 1) (length - 1) (16 * n + 10 + fromIntegral (word - 0x0041))
29− runIdentity (runParserT parser (Pos.initialPos "") "" source)
36−expecting :: String -> IParser a -> IParser a
630+character :: Parser Char
632+ Parser $ \(State array offset length indent row col) cok cerr _ eerr ->
634+ eerr (Error row col)
41−failure :: String -> IParser a
636+ else
42−failure msg =
637+ if Text.unsafeIndex array offset /= 0x0027 {- ' -} then
43− do input <- getInput
638+ eerr (Error row col)
44− setInput ('x' : input)
641+ case characterHelp array (offset + 1) (length - 1) row (col + 1) of
645+ Right ( size, char ) ->
647+ !newOffset = offset + size
648+ !newLength = length + size
650+ if newLength == 0 then
651+ cerr (Error row col)
50−-- INDENTATION
653+ else if Text.unsafeIndex array newOffset == 0x0027 {- ' -} then
654+ cok char (State array newOffset newLength indent row (col + size + 2))
657+ cerr (Error row col)
53−withPos :: IParser a -> IParser a
660+characterHelp :: Text.Array -> Int -> Int -> Int -> Int -> Either Error ( Int, Char )
661+characterHelp array offset length row col =
663+ !word = Text.unsafeIndex array offset
665+ if word == 0x0027 {- ' -} then
666+ Left (Error row col)
63−block :: IParser a -> IParser [a]
668+ else if word == 0x005C {- \ -} then
64−block parser =
669+ eatEscape array (offset + 1) (length - 1) row (col + 1)
66− many1 (checkIndent >> parser)
671+ else if word == 0x000A {- \n -} then
672+ Left (Error row col)
69−checkIndent :: IParser ()
674+ else if word < 0xD800 || word > 0xDBFF then
70−checkIndent =
675+ Right (1, unsafeChr word)
73− if sourceColumn old == sourceColumn new
75− else parserFail "indentation doesn't match"
677+ else if length > 0 then
678+ Right (2, chr2 word (Text.unsafeIndex array offset))
78−indented :: IParser ()
680+ else
79−indented =
681+ Left (Error row col)
82− if sourceColumn new <= sourceColumn old
83− then parserFail "not indented"
84− else putState $ setSourceLine old (sourceLine new)