@@ -1,136 +1,77 @@
1{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
1{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
2−{-# LANGUAGE BangPatterns, Rank2Types, UnboxedTuples, OverloadedStrings #-}
2+{-# LANGUAGE OverloadedStrings #-}
3module Parse.Primitives
3module Parse.Primitives
5 , run, runAt
6 , run, runAt
6 , try, deadend, hint, endOfFile
7 , try, deadend, hint, endOfFile
8− , symbol, underscore, keyword, keywords
9− , lowVar, capVar, infixOp
10 , getPosition, getCol
8 , getPosition, getCol
11 , getContext, pushContext, popContext
9 , getContext, pushContext, popContext
12 , getIndent, setIndent
10 , getIndent, setIndent
13− , SPos(..), whitespace
11+ , W.SPos
14− , docComment, chompUntilDocs
12+ , SParser
15− , string, character
13+ , addLocation, inContext
16− , digit, number
14+ , spaces, noSpace, checkSpace, checkAligned, checkFreshLine
17− , shaderSource, shaderFailure
18− , kernelChunk, ChunkTag(..)
22import Prelude hiding (length)
19import Prelude hiding (length)
23−import qualified Control.Applicative as Applicative ( Applicative(..), Alternative(..) )
20+import qualified Data.ByteString.Internal as B
24−import Control.Monad
21+
25−import qualified Data.ByteString as BS
22+import Parse.Primitives.Internals (Parser(..), State(..), expect, noError)
26−import qualified Data.ByteString.Builder as BS (toLazyByteString, word16HexFixed)
27−import qualified Data.ByteString.Lazy as BS (toStrict)
28−import qualified Data.Char as Char
29−import Data.Monoid ((<>))
30−import qualified Data.Set as Set
31−import Data.Text (Text)
32−import qualified Data.Text as Text
33−import qualified Data.Text.Array as Text
34−import qualified Data.Text.Encoding as Text
35−import qualified Data.Text.Internal as Text
36−import qualified Data.Text.Unsafe as Text
37−import qualified Data.Text.Lazy as LT
38−import qualified Data.Text.Lazy.Builder as B
39−import Data.Text.Internal.Encoding.Utf16 (chr2)
40−import Data.Text.Internal.Unsafe.Char (unsafeChr)
41−import GHC.Word (Word8, Word16)
43−import qualified AST.Helpers as Help (isSymbol)
23+import qualified Parse.Primitives.Internals as I
44−import qualified AST.Literal as L
24+import qualified Parse.Primitives.Whitespace as W
45import qualified Reporting.Annotation as A
25import qualified Reporting.Annotation as A
46import qualified Reporting.Error.Syntax as E
26import qualified Reporting.Error.Syntax as E
47import qualified Reporting.Region as R
27import qualified Reporting.Region as R
48−import Reporting.Error.Syntax
49− ( ParseError(..), Problem(..), Theory(..), BadOp(..)
61− -> (a -> State -> ParseError -> b) -- consumed ok
62− -> ( ParseError -> b) -- consumed err
63− -> (a -> State -> ParseError -> b) -- empty ok
64− -> ( ParseError -> b) -- empty err
71− { _array :: !Text.Array
77− , _context :: E.ContextStack
81−{-# NOINLINE noError #-}
84− ParseError 0 0 (Theories [] [])
88−expect :: Int -> Int -> E.ContextStack -> Theory -> ParseError
89−expect row col ctx theory =
90− ParseError row col (Theories ctx [theory])
94−-- RUN PARSER
34+run :: Parser a -> B.ByteString -> Either (A.Located E.Error) a
36+ runAt 1 1 parser bytes
97−run :: Parser a -> Text -> Either (A.Located E.Error) a
99− runAt 1 1 parser text
102−runAt :: Int -> Int -> Parser a -> Text -> Either (A.Located E.Error) a
39+runAt :: Int -> Int -> Parser a -> B.ByteString -> Either (A.Located E.Error) a
103−runAt sRow sCol (Parser parser) (Text.Text array offset length) =
40+runAt startRow startColumn (Parser parser) (B.PS fp offset length) =
104− case parser (State array offset length 0 sRow sCol []) Ok Err Ok Err of
41+ case parser (State fp offset (offset + length) 0 startRow startColumn []) Ok Err Ok Err of
105 Ok value _ _ ->
42 Ok value _ _ ->
106 Right value
43 Right value
108− Err (ParseError row col problem) ->
45+ Err (E.ParseError row col problem) ->
110 pos = R.Position row col
47 pos = R.Position row col
111 mkError overallRegion subRegion =
48 mkError overallRegion subRegion =
112 Left (A.A overallRegion (E.Parse subRegion problem))
49 Left (A.A overallRegion (E.Parse subRegion problem))
114 case problem of
51 case problem of
115− BadChar endCol ->
52+ E.BadChar endCol ->
116 mkError (R.Region pos (R.Position row endCol)) Nothing
53 mkError (R.Region pos (R.Position row endCol)) Nothing
118− BadEscape width _ ->
55+ E.BadEscape width _ ->
119 mkError (R.Region pos (R.Position row (col + width))) Nothing
56 mkError (R.Region pos (R.Position row (col + width))) Nothing
121− BadOp _ ((_, start) : _) ->
58+ E.BadOp _ ((_, start) : _) ->
122 mkError (R.Region start pos) (Just (R.Region pos pos))
59 mkError (R.Region start pos) (Just (R.Region pos pos))
124− Theories ((_, start) : _) _ ->
61+ E.Theories ((_, start) : _) _ ->
125 mkError (R.Region start pos) (Just (R.Region pos pos))
62 mkError (R.Region start pos) (Just (R.Region pos pos))
128 mkError (R.Region pos pos) Nothing
65 mkError (R.Region pos pos) Nothing
131data Result a
72data Result a
132− = Ok a State ParseError
73+ = Ok a State E.ParseError
133− | Err ParseError
74+ | Err E.ParseError
@@ -143,16 +84,10 @@ try (Parser parser) =
143 parser state cok eerr eok eerr
84 parser state cok eerr eok eerr
146−shaderFailure :: Int -> Int -> Text -> Parser a
147−shaderFailure row col msg =
148− Parser $ \_ _ cerr _ _ ->
149− cerr (ParseError row col (BadShader msg))
152−deadend :: [Theory] -> Parser a
87+deadend :: [E.Theory] -> Parser a
153deadend thrys =
88deadend thrys =
154 Parser $ \(State _ _ _ _ row col ctx) _ _ _ eerr ->
89 Parser $ \(State _ _ _ _ row col ctx) _ _ _ eerr ->
155− eerr (ParseError row col (Theories ctx thrys))
90+ eerr (E.ParseError row col (E.Theories ctx thrys))
158hint :: E.Next -> Parser a -> Parser a
93hint :: E.Next -> Parser a -> Parser a
@@ -160,387 +95,21 @@ hint next (Parser parser) =
160 Parser $ \state@(State _ _ _ _ row col ctx) cok cerr eok eerr ->
95 Parser $ \state@(State _ _ _ _ row col ctx) cok cerr eok eerr ->
162 eok' x s _ =
97 eok' x s _ =
163− eok x s (expect row col ctx (Expecting next))
98+ eok x s (expect row col ctx (E.Expecting next))
165 eerr' _ =
100 eerr' _ =
166− eerr (expect row col ctx (Expecting next))
101+ eerr (expect row col ctx (E.Expecting next))
168 parser state cok cerr eok' eerr'
103 parser state cok cerr eok' eerr'
171endOfFile :: Parser ()
106endOfFile :: Parser ()
172endOfFile =
107endOfFile =
173− Parser $ \state@(State _ _ length _ _ _ _) _ _ eok eerr ->
108+ Parser $ \state@(State _ offset terminal _ _ _ _) _ _ eok eerr ->
174− if length == 0 then
109+ if offset < terminal then
175− eok () state noError
178 eerr noError
110 eerr noError
185−instance Functor Parser where
187− fmap f (Parser parser) =
188− Parser $ \state cok cerr eok eerr ->
190− cok' x s e = cok (f x) s e
191− eok' x s e = eok (f x) s e
193− parser state cok' cerr eok' eerr
200−instance Applicative.Applicative Parser where
204− {-# INLINE (<*>) #-}
208−instance Applicative.Alternative Parser where
209− {-# INLINE empty #-}
210− empty = allTheOptionsFailed
212− {-# INLINE (<|>) #-}
216−oneOf :: [Parser a] -> Parser a
218− foldr oneOfHelp allTheOptionsFailed parsers
221−allTheOptionsFailed :: Parser a
222−allTheOptionsFailed =
223− Parser $ \_ _ _ _ eerr ->
227−oneOfHelp :: Parser a -> Parser a -> Parser a
228−oneOfHelp (Parser parser1) (Parser parser2) =
229− Parser $ \state cok cerr eok eerr ->
233− eok2 y s e2 = eok y s (mergeErrors e1 e2)
234− eerr2 e2 = eerr (mergeErrors e1 e2)
236− parser2 state cok cerr eok2 eerr2
238− parser1 state cok cerr eok eerr1
241−mergeErrors :: ParseError -> ParseError -> ParseError
242−mergeErrors e1@(ParseError r1 c1 p1) e2@(ParseError r2 c2 p2) =
243− case compare r1 r2 of
247− case compare c1 c2 of
252− (Theories _ [], Theories _ _) ->
255− (Theories _ _, Theories _ []) ->
258− (Theories ctx ts1, Theories _ ts2) ->
259− ParseError r1 c1 (Theories ctx (ts1 ++ ts2))
261− (Theories _ _, _) ->
272−instance Monad Parser where
273− {-# INLINE return #-}
275− Parser $ \state _ _ eok _ ->
276− eok value state noError
278− {-# INLINE (>>=) #-}
279− (Parser parser) >>= callback =
280− Parser $ \state cok cerr eok eerr ->
284− eok2 y s2 e2 = cok y s2 (mergeErrors e1 e2)
285− eerr2 e2 = cerr (mergeErrors e1 e2)
288− Parser parser2 -> parser2 s1 cok cerr eok2 eerr2
292− eok2 y s2 e2 = eok y s2 (mergeErrors e1 e2)
293− eerr2 e2 = eerr (mergeErrors e1 e2)
296− Parser parser2 -> parser2 s1 cok cerr eok2 eerr2
298− parser state cok1 cerr eok1 eerr
305−{-# INLINE symbol #-}
306−symbol :: Text -> Parser ()
307−symbol sym@(Text.Text tArray tOffset tLen) =
308− let !theory = Symbol sym in
309− Parser $ \(State array offset length indent row col ctx) cok _ _ eerr ->
310− if tLen <= length && Text.equal tArray tOffset array offset tLen then
312− (# newRow, newCol #) =
313− moveCursor tArray tOffset tLen row col
316− State array (offset + tLen) (length - tLen) indent newRow newCol ctx
318− cok () newState noError
321− eerr (expect row col ctx theory)
324−moveCursor :: Text.Array -> Int -> Int -> Int -> Int -> (# Int, Int #)
325−moveCursor array offset length row col =
331− !word = Text.unsafeIndex array offset
333− if word == 0x000A {- \n -} then
334− moveCursor array (offset + 1) (length - 1) (row + 1) 1
336− else if word < 0xD800 || 0xDBFF < word then
337− moveCursor array (offset + 1) (length - 1) row (col + 1)
340− moveCursor array (offset + 2) (length - 2) row (col + 1)
343−underscore :: Parser ()
345− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
346− if length == 0 || Text.unsafeIndex array offset /= 0x005F {- _ -} then
349− else if length >= 2 && isInnerVarChar (peekChar array (offset + 1)) then
350− cerr (ParseError row (col + 1) (BadUnderscore (peekChar array (offset + 1))))
355− State array (offset + 1) (length - 1) indent row (col + 1) ctx
357− cok () newState noError
364−keyword :: Text -> Parser ()
365−keyword kwd@(Text.Text tArray tOffset tLen) =
366− Parser $ \(State array offset length indent row col ctx) cok _ _ eerr ->
367− if tLen <= length && Text.equal tArray tOffset array offset tLen then
369− if isInnerVarChar (peekChar array (offset + tLen)) then
370− eerr (expect row col ctx (Keyword kwd))
374− (# newRow, newCol #) =
375− moveCursor tArray tOffset tLen row col
378− State array (offset + tLen) (length - tLen) indent newRow newCol ctx
380− cok () newState noError
383− eerr (expect row col ctx (Keyword kwd))
386−keywords :: Set.Set Text
389− [ "if", "then", "else"
394− , "import", "exposing"
404−lowVar :: Parser Text.Text
406− varPrim LowVar Char.isLower
409−capVar :: Parser Text.Text
411− varPrim CapVar Char.isUpper
414−{-# INLINE varPrim #-}
415−varPrim :: Theory -> (Char -> Bool) -> Parser Text.Text
416−varPrim theory isGoodFirstChar =
417− Parser $ \(State array offset length indent row col ctx) cok _ _ eerr ->
419− eerr (expect row col ctx theory)
423− (Text.Iter char size) = peek array offset
425− if isGoodFirstChar char then
427− (# newOffset, newLength, newCol #) =
428− varPrimHelp array (offset + size) (length - size) (col + 1)
431− copyText array offset (newOffset - offset)
433− if Set.member copiedText keywords then
434− eerr (expect row newCol ctx theory)
436− cok copiedText (State array newOffset newLength indent row newCol ctx) noError
439− eerr (expect row col ctx theory)
442−{-# INLINE copyText #-}
443−copyText :: Text.Array -> Int -> Int -> Text.Text
444−copyText array offset size =
447− do mutableArray <- Text.new size
448− Text.copyI mutableArray 0 array offset size
451− Text.Text (Text.run makeArray) 0 size
455−peek :: Text.Array -> Int -> Text.Iter
458− !word = Text.unsafeIndex array offset
460− if word < 0xD800 || 0xDBFF < word then
461− Text.Iter (unsafeChr word) 1
463− Text.Iter (chr2 word (Text.unsafeIndex array (offset + 1))) 2
466−{-# INLINE peekChar #-}
467−peekChar :: Text.Array -> Int -> Char
468−peekChar array offset =
470− !word = Text.unsafeIndex array offset
472− if word < 0xD800 || 0xDBFF < word then
475− chr2 word (Text.unsafeIndex array (offset + 1))
478−varPrimHelp :: Text.Array -> Int -> Int -> Int -> (# Int, Int, Int #)
479−varPrimHelp array offset length col =
481− (# offset, length, col #)
485− (Text.Iter char size) = peek array offset -- TODO look for latin chars directly?
487− if isInnerVarChar char then
488− varPrimHelp array (offset + size) (length - size) (col + 1)
491− (# offset, length, col #)
494−{-# INLINE isInnerVarChar #-}
495−isInnerVarChar :: Char -> Bool
496−isInnerVarChar char =
497− Char.isAlphaNum char || char == '_'
504−infixOp :: Parser Text
506− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
508− eerr (expect row col ctx InfixOp)
511− let
112+ eok () state noError
512− (Text.Iter char size) = peek array offset
514− if not (Help.isSymbol char) then
515− eerr (expect row col ctx InfixOp)
517− case infixOpHelp array (offset + size) (length - size) row (col + 1) of
521− Right ( newOffset, newLength, newCol ) ->
522− case copyText array offset (newOffset - offset) of
523− "." -> cerr (ParseError row col (BadOp Dot ctx))
524− "|" -> cerr (ParseError row col (BadOp Pipe ctx))
525− "->" -> cerr (ParseError row col (BadOp Arrow ctx))
526− "=" -> cerr (ParseError row col (BadOp Equals ctx))
527− ":" -> cerr (ParseError row col (BadOp HasType ctx))
528− op -> cok op (State array newOffset newLength indent row newCol ctx) noError
531−infixOpHelp :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError (Int, Int, Int)
532−infixOpHelp array offset length row col =
534− Right ( offset, length, col )
538− (Text.Iter char size) = peek array offset
540− if Help.isSymbol char then
541− infixOpHelp array (offset + size) (length - size) row (col + 1)
543− Right ( offset, length, col )
@@ -591,920 +160,65 @@ setIndent indent =
594−-- WHITESPACE
163+-- SPACE PARSER
601−whitespace :: Parser SPos
603− Parser $ \(State array offset length indent row col ctx) cok cerr _ _ ->
604− case eatSpaces array offset length row col of
608− Right (newOffset, newLength, newRow, newCol) ->
610− (SPos (R.Position newRow newCol))
611− (State array newOffset newLength indent newRow newCol ctx)
615−eatSpaces :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
616−eatSpaces array offset length row col =
618− Right ( offset, length, row, col )
621− case Text.unsafeIndex array offset of
623− eatSpaces array (offset + 1) (length - 1) row (col + 1)
626− eatSpaces array (offset + 1) (length - 1) (row + 1) 1
629− eatMultiComment array offset length row col
632− eatLineComment array offset length row col
635− eatSpaces array (offset + 1) (length - 1) row col
638− Left (ParseError row col Tab)
641− Right ( offset, length, row, col )
648−eatLineComment :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
649−eatLineComment array offset length row col =
651− Right ( offset, length, row, col )
654− case Text.unsafeIndex array (offset + 1) of
656− eatLineCommentHelp array (offset + 2) (length - 2) row (col + 2)
659− Right ( offset, length, row, col )
662−eatLineCommentHelp :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
663−eatLineCommentHelp array offset length row col =
665− Right ( offset, length, row, col )
669− !word = Text.unsafeIndex array offset
671− if word == 0x000A {- \n -} then
672− eatSpaces array (offset + 1) (length - 1) (row + 1) 1
674− else if word < 0xD800 || 0xDBFF < word then
675− eatLineCommentHelp array (offset + 1) (length - 1) row (col + 1)
678− eatLineCommentHelp array (offset + 2) (length - 2) row (col + 1)
685−eatMultiComment :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
686−eatMultiComment array offset length row col =
688− Right ( offset, length, row, col )
692− !yesDash = Text.unsafeIndex array (offset + 1) == 0x002D {- - -}
693− !noBar = Text.unsafeIndex array (offset + 2) /= 0x007C {- | -}
695− if yesDash && noBar then
696− do (newOffset, newLength, newRow, newCol) <-
697− eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 2) 1
698− eatSpaces array newOffset newLength newRow newCol
701− Right ( offset, length, row, col )
704−eatMultiCommentHelp :: Text.Array -> Int -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
705−eatMultiCommentHelp array offset length row col openComments =
707− Left (ParseError row col EndOfFile_Comment)
711− !word = Text.unsafeIndex array offset
713− if word == 0x000A {- \n -} then
715− eatMultiCommentHelp array (offset + 1) (length - 1) (row + 1) 1 openComments
717− else if word == 0x002D {- - -} && length > 1 && Text.unsafeIndex array (offset + 1) == 0x007D {- } -} then
719− if openComments == 1 then
720− Right ( offset + 2, length - 2, row, col + 2 )
722− eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 2) (openComments - 1)
724− else if word == 0x007B {- { -} && length > 1 && Text.unsafeIndex array (offset + 1) == 0x002D {- - -} then
726− eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 2) (openComments + 1)
728− else if word < 0xD800 || 0xDBFF < word then
730− eatMultiCommentHelp array (offset + 1) (length - 1) row (col + 1) openComments
734− eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 1) openComments
738−-- DOCUMENTATION COMMENT
741−docComment :: Parser Text
744− Parser $ \(State array offset length indent row col ctx) cok cerr _ _ ->
745− case eatMultiCommentHelp array offset length row col 1 of
749− Right (newOffset, newLength, newRow, newCol) ->
751− (copyText array offset (newOffset - offset - 2))
752− (State array newOffset newLength indent newRow newCol ctx)
756−{-# NOINLINE docCommentOpen #-}
757−docCommentOpen :: Parser ()
762−chompUntilDocs :: Parser Bool
764− Parser $ \(State array offset length indent row col ctx) cok _ _ _ ->
766− (# isStart, newOffset, newLength, newRow, newCol #) =
767− eatDocs array offset length row col
770− State array newOffset newLength indent newRow newCol ctx
772− cok isStart newState noError
775−eatDocs :: Text.Array -> Int -> Int -> Int -> Int -> (# Bool, Int, Int, Int, Int #)
776−eatDocs array offset length row col =
778− (# False, offset, length, row, col #)
780− else if isDocsStart array offset length then
781− (# True, offset + 5, length - 5, row, col + 5 #)
785− !word = Text.unsafeIndex array offset
787− if word == 0x000A {- \n -} then
788− eatDocs array (offset + 1) (length - 1) (row + 1) 1
790− else if word < 0xD800 || 0xDBFF < word then
791− eatDocs array (offset + 1) (length - 1) row (col + 1)
794− eatDocs array (offset + 2) (length - 2) row (col + 1)
797−isDocsStart :: Text.Array -> Int -> Int -> Bool
798−isDocsStart array offset length =
800− && Text.unsafeIndex array offset == 0x0040 {- @ -}
801− && Text.unsafeIndex array (offset + 1) == 0x0064 {- d -}
802− && Text.unsafeIndex array (offset + 2) == 0x006F {- o -}
803− && Text.unsafeIndex array (offset + 3) == 0x0063 {- c -}
804− && Text.unsafeIndex array (offset + 4) == 0x0073 {- s -}
811−string :: Parser Text.Text
813− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
819− !word = Text.unsafeIndex array offset
821− if word /= 0x0022 {- " -} then
827− if length >= 3 && isQuote array (offset + 1) && isQuote array (offset + 2) then
828− multiString array (offset + 3) (length - 3) row (col + 3) (offset + 3) mempty
830− singleString array (offset + 1) (length - 1) row (col + 1) (offset + 1) mempty
832− case stringResult of
836− Right ( newOffset, newLength, newCol, builder ) ->
838− (LT.toStrict (B.toLazyText builder))
839− (State array newOffset newLength indent row newCol ctx)
843−{-# INLINE isQuote #-}
844−isQuote :: Text.Array -> Int -> Bool
845−isQuote array offset =
846− Text.unsafeIndex array offset == 0x0022 {- " -}
853−singleString :: Text.Array -> Int -> Int -> Int -> Int -> Int -> B.Builder -> Either ParseError ( Int, Int, Int, B.Builder )
854−singleString array offset length row col initialOffset builder =
856− Left (ParseError row col EndOfFile_String)
860− !word = Text.unsafeIndex array offset
862− if word == 0x0022 {- " -} then
867− , builder <> B.fromText (copyText array initialOffset (offset - initialOffset))
870− else if word == 0x000A {- \n -} then
871− Left (ParseError row col NewLineInString)
873− else if word == 0x0027 {- ' -} then
874− singleStringHelp array offset length row col initialOffset builder 1 "\\'"
876− else if word == 0x005C {- \ -} then
877− case eatEscape array (offset + 1) (length - 1) of
879− singleString array (offset + 2) (length - 2) row (col + 2) initialOffset builder
881− EscapeUnicode delta code ->
882− singleStringHelp array offset length row col initialOffset builder delta code
884− EscapeProblem newOffset problem ->
885− Left (ParseError row col (BadEscape (newOffset - offset) problem))
888− Left (ParseError row (col + 1) EndOfFile_String)
890− else if word < 0xD800 || 0xDBFF < word then
891− singleString array (offset + 1) (length - 1) row (col + 1) initialOffset builder
894− singleString array (offset + 2) (length - 2) row (col + 1) initialOffset builder
897−singleStringHelp :: Text.Array -> Int -> Int -> Int -> Int -> Int -> B.Builder -> Int -> Text.Text -> Either ParseError ( Int, Int, Int, B.Builder )
898−singleStringHelp array offset length row col initialOffset builder delta escape =
900− chunk = copyText array initialOffset (offset - initialOffset)
901− newBuilder = builder <> B.fromText chunk <> B.fromText escape
902− !newOffset = offset + delta
904− singleString array newOffset (length - delta) row (col + delta) newOffset newBuilder
911−multiString :: Text.Array -> Int -> Int -> Int -> Int -> Int -> B.Builder -> Either ParseError ( Int, Int, Int, B.Builder )
912−multiString array offset length row col initialOffset builder =
914− Left (ParseError row col EndOfFile_MultiString)
918− !word = Text.unsafeIndex array offset
920− if word == 0x0022 {- " -} && isQuote array (offset + 1) && isQuote array (offset + 2) then
925− , builder <> B.fromText (copyText array initialOffset (offset - initialOffset))
928− else if word == 0x0027 {- ' -} then
929− multiStringHelp array offset length row col initialOffset builder 1 "\\'"
931− else if word == 0x000A {- \n -} then
932− multiStringHelp array offset length row col initialOffset builder 1 "\\n"
934− else if word == 0x005C {- \ -} then
936− case eatEscape array (offset + 1) (length - 1) of
938− multiString array (offset + 2) (length - 2) row (col + 2) initialOffset builder
940− EscapeUnicode delta code ->
941− multiStringHelp array offset length row col initialOffset builder delta code
943− EscapeProblem newOffset problem ->
944− Left (ParseError row col (BadEscape (newOffset - offset) problem))
947− Left (ParseError row (col + 1) EndOfFile_MultiString)
949− else if word < 0xD800 || 0xDBFF < word then
950− multiString array (offset + 1) (length - 1) row (col + 1) initialOffset builder
953− multiString array (offset + 2) (length - 2) row (col + 1) initialOffset builder
956−multiStringHelp :: Text.Array -> Int -> Int -> Int -> Int -> Int -> B.Builder -> Int -> Text.Text -> Either ParseError ( Int, Int, Int, B.Builder )
957−multiStringHelp array offset length row col initialOffset builder delta escape =
959− chunk = copyText array initialOffset (offset - initialOffset)
960− newBuilder = builder <> B.fromText chunk <> B.fromText escape
961− !newOffset = offset + delta
963− multiString array newOffset (length - delta) row (col + delta) newOffset newBuilder
970−character :: Parser Text
972− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
977− if Text.unsafeIndex array offset /= 0x0027 {- ' -} then
981− case chompChar array (offset + 1) (length - 1) row (col + 1) 0 "" of
985− Right ( newOffset, newLength, newCol, numChars, mostRecentChar ) ->
986− if numChars /= 1 then
987− cerr (ParseError row col (BadChar newCol))
990− (State array newOffset newLength indent row newCol ctx)
993−chompChar :: Text.Array -> Int -> Int -> Int -> Int -> Int -> Text.Text -> Either ParseError ( Int, Int, Int, Int, Text.Text )
994−chompChar array offset length row col numChars mostRecentChar =
996− Left (ParseError row col EndOfFile_Char)
1000− !word = Text.unsafeIndex array offset
1002− if word == 0x0027 {- ' -} then
1011− else if word == 0x000A {- \n -} then
1012− Left (ParseError row col NewLineInChar)
1014− else if word == 0x0022 {- " -} then
1015− chompChar array (offset + 1) (length - 1) row (col + 1) (numChars + 1) "\\\""
1017− else if word == 0x005C {- \ -} then
1018− case eatEscape array (offset + 1) (length - 1) of
1020− chompChar array (offset + 2) (length - 2) row (col + 2) (numChars + 1) (copyText array offset 2)
1022− EscapeUnicode delta code ->
1023− chompChar array (offset + delta) (length - delta) row (col + delta) (numChars + 1) code
1025− EscapeProblem newOffset problem ->
1026− Left (ParseError row col (BadEscape (newOffset - offset) problem))
1029− Left (ParseError row col EndOfFile_Char)
1031− else if word < 0xD800 || 0xDBFF < word then
1032− chompChar array (offset + 1) (length - 1) row (col + 1) (numChars + 1) (copyText array offset 1)
1035− chompChar array (offset + 2) (length - 2) row (col + 1) (numChars + 1) (copyText array offset 2)
1039−-- ESCAPE CHARACTERS
1044− | EscapeUnicode !Int Text.Text
1045− | EscapeProblem !Int E.EscapeProblem
1049−eatEscape :: Text.Array -> Int -> Int -> Escape
1050−eatEscape array offset length =
1051− if length == 0 then
1055− case Text.unsafeIndex array offset of
1056− 0x006E {- n -} -> EscapeNormal
1057− 0x0072 {- r -} -> EscapeNormal
1058− 0x0074 {- t -} -> EscapeNormal
1059− 0x0022 {- " -} -> EscapeNormal
1060− 0x0027 {- ' -} -> EscapeNormal
1061− 0x005C {- \ -} -> EscapeNormal
1062− 0x0075 {- u -} -> eatUnicode array (offset + 1) (length - 1)
1063− _ -> EscapeProblem offset E.UnknownEscape
1066−eatUnicode :: Text.Array -> Int -> Int -> Escape
1067−eatUnicode array offset length =
1068− if length == 0 || Text.unsafeIndex array offset /= 0x007B {- { -} then
1069− EscapeProblem offset E.UnicodeSyntax
1075− (# newOffset, newLength, code #) =
1076− chompHex array digitOffset (length - 1)
1079− newOffset - digitOffset
1081− if newLength == 0 || Text.unsafeIndex array newOffset /= 0x007D {- } -} then
1082− EscapeProblem newOffset E.UnicodeSyntax
1084− else if code < 0 || 0x10FFFF < code then
1085− EscapeProblem (newOffset + 1) E.UnicodeRange
1087− else if numDigits < 4 || 6 < numDigits then
1088− EscapeProblem (newOffset + 1) $
1089− E.UnicodeLength numDigits (copyText array digitOffset numDigits)
1092− EscapeUnicode (numDigits + 4) (codePointToBuilder code)
1095−{-# INLINE codePointToBuilder #-}
1096−codePointToBuilder :: Int -> Text.Text
1097−codePointToBuilder code =
1098− if code < 0xFFFF then
1099− "\\u" <> toHex code
1102− "\\u" <> toHex ((code `div` 0x400) + 0xD800)
1104− "\\u" <> toHex ((code `mod` 0x400) + 0xDC00)
1107−toHex :: Int -> Text.Text
1109− Text.decodeUtf8 $ BS.toStrict $ BS.toLazyByteString $
1110− BS.word16HexFixed (fromIntegral word16)
1119− Parser $ \(State array offset length indent row col ctx) cok _ _ eerr ->
1120− if length == 0 then
1121− eerr (expect row col ctx Digit)
1125− !word = Text.unsafeIndex array offset
1127− if isDigit word then
1129− (fromIntegral (word - 0x0030 {- 0 -}))
1130− (State array (offset + 1) (length - 1) indent row (col + 1) ctx)
1133− eerr (expect row col ctx Digit)
1140−number :: Parser L.Literal
1142− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
1143− if length == 0 then
1148− !word = Text.unsafeIndex array offset
1150− if not (isDigit word) then
1156− if word == 0x0030 {- 0 -} then
1157− chompZero array offset (offset + 1) (length - 1)
1159− chompInt array offset (offset + 1) (length - 1)
1161− case chompResults of
1162− Left (newOffset, problem) ->
1163− cerr (ParseError row (col + (newOffset - offset)) problem)
1165− Right (newOffset, newLength, literal) ->
1166− if isDirtyEnd array newOffset newLength then
1167− cerr (ParseError row (col + (newOffset - offset)) BadNumberEnd)
1172− (State array newOffset newLength indent row (col + (newOffset - offset)) ctx)
1176−isDirtyEnd :: Text.Array -> Int -> Int -> Bool
1177−isDirtyEnd array offset length =
1178− if length == 0 then
1183− !char = peekChar array offset
1185− Char.isAlpha char || char == '_'
1188−chompInt :: Text.Array -> Int -> Int -> Int -> Either (Int, Problem) (Int, Int, L.Literal)
1189−chompInt array startOffset offset length =
1190− if length == 0 then
1192− Right ( offset, length, readInt array startOffset offset )
1197− !word = Text.unsafeIndex array offset
1199− if isDigit word then
1200− chompInt array startOffset (offset + 1) (length - 1)
1202− else if word == 0x002E {- . -} then
1203− chompFraction array startOffset (offset + 1) (length - 1)
1205− else if word == 0x0065 {- e -} || word == 0x0045 {- E -} then
1206− chompExponent array startOffset (offset + 1) (length - 1)
1209− Right ( offset, length, readInt array startOffset offset )
1212−chompFraction :: Text.Array -> Int -> Int -> Int -> Either (Int, Problem) (Int, Int, L.Literal)
1213−chompFraction array startOffset offset length =
1214− if length == 0 then
1215− Left (offset, BadNumberDot)
1217− else if isDigit (Text.unsafeIndex array offset) then
1218− chompFractionHelp array startOffset (offset + 1) (length - 1)
1221− Left (offset, BadNumberDot)
1224−chompFractionHelp :: Text.Array -> Int -> Int -> Int -> Either (Int, Problem) (Int, Int, L.Literal)
1225−chompFractionHelp array startOffset offset length =
1226− if length == 0 then
1227− Right (offset, length, readFloat array startOffset offset)
1231− !word = Text.unsafeIndex array offset
1233− if isDigit word then
1234− chompFractionHelp array startOffset (offset + 1) (length - 1)
1236− else if word == 0x0065 {- e -} || word == 0x0045 {- E -} then
1237− chompExponent array startOffset (offset + 1) (length - 1)
1240− Right (offset, length, readFloat array startOffset offset)
1243−chompExponent :: Text.Array -> Int -> Int -> Int -> Either (Int, Problem) (Int, Int, L.Literal)
1244−chompExponent array startOffset offset length =
1245− if length == 0 then
1246− Right (offset, length, readFloat array startOffset offset)
1250− !word = Text.unsafeIndex array offset
1252− if isDigit word then
1253− chompExponentHelp array startOffset (offset + 1) (length - 1)
1255− else if word == 0x002B {- + -} || word == 0x002D {- - -} then
1257− if length > 1 && isDigit (Text.unsafeIndex array (offset + 1)) then
1258− chompExponentHelp array startOffset (offset + 2) (length - 2)
1260− Left (offset, BadNumberExp)
1263− Left (offset, BadNumberExp)
1266−chompExponentHelp :: Text.Array -> Int -> Int -> Int -> Either (Int, Problem) (Int, Int, L.Literal)
1267−chompExponentHelp array startOffset offset length =
1268− if length == 0 then
1269− Right (offset, length, readFloat array startOffset offset)
1271− else if isDigit (Text.unsafeIndex array offset) then
1272− chompExponentHelp array startOffset (offset + 1) (length - 1)
1275− Right (offset, length, readFloat array startOffset offset)
1278−chompZero :: Text.Array -> Int -> Int -> Int -> Either (Int, Problem) (Int, Int, L.Literal)
1279−chompZero array startOffset offset length =
1280− if length == 0 then
1281− Right ( offset, length, L.IntNum 0 )
1285− !word = Text.unsafeIndex array offset
1287− if word == 0x0078 {- x -} then
1288− chompHexNumber array (offset + 1) (length - 1)
1290− else if word == 0x002E {- . -} then
1291− chompFraction array startOffset (offset + 1) (length - 1)
1293− else if isDigit word then
1294− Left (offset, BadNumberZero)
1297− Right ( offset, length, L.IntNum 0 )
167+ Parser (a, R.Position, W.SPos)
1300−chompHexNumber :: Text.Array -> Int -> Int -> Either (Int, Problem) (Int, Int, L.Literal)
1301−chompHexNumber array offset length =
1303− (# newOffset, newLength, hexNumber #) =
1304− chompHex array offset length
1306− if hexNumber == -1 then
1307− Left ( newOffset, BadNumberHex )
1309− Right ( newOffset, newLength, L.IntNum hexNumber )
174+addLocation :: Parser a -> Parser (A.Located a)
176+ do start <- getPosition
179+ return (A.at start end value)
1316−{-# INLINE isDigit #-}
1317−isDigit :: Word16 -> Bool
1319− word <= 0x0039 {- 9 -} && word >= 0x0030 {- 0 -}
182+inContext :: R.Position -> E.Context -> Parser a -> Parser a
183+inContext pos ctx parser =
184+ do pushContext pos ctx
1322−{-# INLINE readInt #-}
1323−readInt :: Text.Array -> Int -> Int -> L.Literal
1324−readInt array startOffset endOffset =
1325− L.IntNum $ read $ Text.unpack $
1326− Text.Text array startOffset (endOffset - startOffset)
1329−{-# INLINE readFloat #-}
1330−readFloat :: Text.Array -> Int -> Int -> L.Literal
1331−readFloat array startOffset endOffset =
1332− L.FloatNum $ read $ Text.unpack $
1333− Text.Text array startOffset (endOffset - startOffset)
1340−-- Return -1 if it has NO digits
1341−-- Return -2 if it has BAD digits
1343−{-# INLINE chompHex #-}
1344−chompHex :: Text.Array -> Int -> Int -> (# Int, Int, Int #)
1345−chompHex array offset length =
1346− chompHexHelp array offset length (-1) 0
1349−chompHexHelp :: Text.Array -> Int -> Int -> Int -> Int -> (# Int, Int, Int #)
1350−chompHexHelp array offset length finalNumber hexNumber =
1351− if length == 0 then
1352− (# offset, length, finalNumber #)
1356− stepHex (Text.unsafeIndex array offset) hexNumber
1358− if newNumber < 0 then
1359− (# offset, length, if newNumber == -1 then finalNumber else -2 #)
1361− chompHexHelp array (offset + 1) (length - 1) newNumber newNumber
1364−{-# INLINE stepHex #-}
1365−stepHex :: Word16 -> Int -> Int
1366−stepHex word hexNumber =
1367− if word <= 0x0039 {- 9 -} && word >= 0x0030 {- 0 -} then
1368− 16 * hexNumber + fromIntegral (word - 0x0030 {- 0 -})
1370− else if word <= 0x0066 {- f -} && word >= 0x0061 {- a -} then
1371− 16 * hexNumber + 10 + fromIntegral (word - 0x0061 {- a -})
1373− else if word <= 0x0046 {- F -} && word >= 0x0041 {- A -} then
1374− 16 * hexNumber + 10 + fromIntegral (word - 0x0041 {- A -})
1384−shaderSource :: Parser Text
1386− do shaderSourceOpen
1387− Parser $ \(State array offset length indent row col ctx) cok cerr _ _ ->
1388− case eatShader array offset length row col of
1390− cerr (ParseError row col EndOfFile_Shader)
1392− Just ( shaderEndOffset, newRow, newCol ) ->
1394− !size = shaderEndOffset - offset
1396− State array (shaderEndOffset + 2) (length - size - 2) indent newRow newCol ctx
1398− cok (copyText array offset size) newState noError
1401−{-# NOINLINE shaderSourceOpen #-}
1402−shaderSourceOpen :: Parser ()
1407−eatShader :: Text.Array -> Int -> Int -> Int -> Int -> Maybe (Int, Int, Int)
1408−eatShader array offset length row col =
1414− !word = Text.unsafeIndex array offset
1416− if word == 0x007C {- | -} && Text.unsafeIndex array (offset + 1) == 0x005D {- ] -} then
1417− Just ( offset, row, col + 2 )
1419− else if word == 0x000A {- \n -} then
1420− eatShader array (offset + 1) (length - 1) (row + 1) 1
1422− else if word < 0xD800 || 0xDBFF < word then
1423− eatShader array (offset + 1) (length - 1) row (col + 1)
1426− eatShader array (offset + 2) (length - 2) row (col + 1)
1434− = KernelEnum Word8 Text
1437− | KernelImport Text
1438− | KernelJsField Text
1439− | KernelElmField Text
1442−kernelChunk :: Parser (BS.ByteString, Maybe ChunkTag)
1444− Parser $ \(State array offset length indent row col ctx) cok _ _ _ ->
1446− (# maybeChunkTag, jsOffset, newOffset, newLength, newRow, newCol #) =
1447− chompChunk array offset length row col
1450− Text.encodeUtf8 (copyText array offset (jsOffset - offset))
1453− State array newOffset newLength indent newRow newCol ctx
1455− cok (javascript, maybeChunkTag) newState noError
1458−chompChunk :: Text.Array -> Int -> Int -> Int -> Int -> (# Maybe ChunkTag, Int, Int, Int, Int, Int #)
1459−chompChunk array offset length row col =
1460− if length == 0 then
1461− (# Nothing, offset, offset, length, row, col #)
1465− !word = Text.unsafeIndex array offset
1467− if word == 0x005F {- _ -} && length > 3 && Text.unsafeIndex array (offset + 1) == 0x005F {- _ -} then
1468− chompChunkTag array offset (offset + 2) (length - 2) row (col + 2)
1470− else if word == 0x000A {- \n -} then
1471− chompChunk array (offset + 1) (length - 1) (row + 1) 1
1473− else if word < 0xD800 || 0xDBFF < word then
189+-- WHITESPACE VARIATIONS
1474− chompChunk array (offset + 1) (length - 1) row (col + 1)
1477− chompChunk array (offset + 2) (length - 2) row (col + 1)
194+ checkSpace =<< W.whitespace
1480−chompChunkTag :: Text.Array -> Int -> Int -> Int -> Int -> Int -> (# Maybe ChunkTag, Int, Int, Int, Int, Int #)
1481−chompChunkTag array jsOffset offset length row col =
1483− (# newOffset, newLength, newCol #) =
1484− varPrimHelp array (offset + 1) (length - 1) (col + 1)
1486− !word =
197+noSpace :: R.Position -> W.SPos -> Parser ()
1487− Text.unsafeIndex array offset
198+noSpace pos (W.SPos spos) =
1490− if word == 0x0024 {- $ -} then
1491− KernelElmField (copyText array (offset + 1) (newOffset - offset - 1))
1493− else
204+checkSpace :: W.SPos -> Parser ()
1494− let !chunk = copyText array offset (newOffset - offset) in
205+checkSpace (W.SPos (R.Position _ col)) =
1495− if isDigit word then
206+ do indent <- getIndent
1496− KernelEnum (fromIntegral (word - 0x0030 {- 0 -})) chunk
207+ if col > indent && col > 1
209+ else deadend [E.BadSpace]
1498− else if 0x0061 {- a -} <= word && word <= 0x007A {- z -} then
1499− KernelJsField chunk
1501− else if chunk == "DEBUG" then
212+checkAligned :: W.SPos -> Parser ()
1502− KernelDebug
213+checkAligned (W.SPos (R.Position _ col)) =
214+ do indent <- getIndent
217+ else deadend [E.BadSpace]
1504− else if chunk == "PROD" then
1507− else
220+checkFreshLine :: W.SPos -> Parser ()
1508− KernelImport chunk
221+checkFreshLine (W.SPos (R.Position _ col)) =
1510− (# Just chunkTag, jsOffset, newOffset, newLength, row, newCol #)
223+ then return ()
224+ else deadend [E.BadSpace]