@@ -1,136 +1,77 @@
11{-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
2−{-# LANGUAGE BangPatterns, Rank2Types, UnboxedTuples, OverloadedStrings #-}
2+{-# LANGUAGE OverloadedStrings #-}
33module Parse.Primitives
4− ( Parser
4+ ( I.Parser
5+ , I.oneOf
56 , run, runAt
67 , try, deadend, hint, endOfFile
7− , oneOf
8− , symbol, underscore, keyword, keywords
9− , lowVar, capVar, infixOp
108 , getPosition, getCol
119 , getContext, pushContext, popContext
1210 , getIndent, setIndent
13− , SPos(..), whitespace
14− , docComment, chompUntilDocs
15− , string, character
16− , digit, number
17− , shaderSource, shaderFailure
18− , kernelChunk, ChunkTag(..)
11+ , W.SPos
12+ , SParser
13+ , addLocation, inContext
14+ , spaces, noSpace, checkSpace, checkAligned, checkFreshLine
1915 )
2016 where
2117
18+
2219import Prelude hiding (length)
23−import qualified Control.Applicative as Applicative ( Applicative(..), Alternative(..) )
24−import Control.Monad
25−import qualified Data.ByteString as BS
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)
42−
43−import qualified AST.Helpers as Help (isSymbol)
44−import qualified AST.Literal as L
20+import qualified Data.ByteString.Internal as B
21+
22+import Parse.Primitives.Internals (Parser(..), State(..), expect, noError)
23+import qualified Parse.Primitives.Internals as I
24+import qualified Parse.Primitives.Whitespace as W
4525import qualified Reporting.Annotation as A
4626import qualified Reporting.Error.Syntax as E
4727import qualified Reporting.Region as R
48−import Reporting.Error.Syntax
49− ( ParseError(..), Problem(..), Theory(..), BadOp(..)
50− )
51−
52−
53−
54−-- PARSER
55−
56−
57−newtype Parser a =
58− Parser (
59− forall b.
60− State
61− -> (a -> State -> ParseError -> b) -- consumed ok
62− -> ( ParseError -> b) -- consumed err
63− -> (a -> State -> ParseError -> b) -- empty ok
64− -> ( ParseError -> b) -- empty err
65− -> b
66− )
67−
68−
69−data State =
70− State
71− { _array :: !Text.Array
72− , _offset :: !Int
73− , _length :: !Int
74− , _indent :: !Int
75− , _row :: !Int
76− , _col :: !Int
77− , _context :: E.ContextStack
78− }
79−
80−
81−{-# NOINLINE noError #-}
82−noError :: ParseError
83−noError =
84− ParseError 0 0 (Theories [] [])
8528
8629
87−{-# INLINE expect #-}
88−expect :: Int -> Int -> E.ContextStack -> Theory -> ParseError
89−expect row col ctx theory =
90− ParseError row col (Theories ctx [theory])
9130
31+-- RUN
9232
9333
94−-- RUN PARSER
34+run :: Parser a -> B.ByteString -> Either (A.Located E.Error) a
35+run parser bytes =
36+ runAt 1 1 parser bytes
9537
9638
97−run :: Parser a -> Text -> Either (A.Located E.Error) a
98−run parser text =
99− runAt 1 1 parser text
100−
101−
102−runAt :: Int -> Int -> Parser a -> Text -> Either (A.Located E.Error) a
103−runAt sRow sCol (Parser parser) (Text.Text array offset length) =
104− case parser (State array offset length 0 sRow sCol []) Ok Err Ok Err of
39+runAt :: Int -> Int -> Parser a -> B.ByteString -> Either (A.Located E.Error) a
40+runAt startRow startColumn (Parser parser) (B.PS fp offset length) =
41+ case parser (State fp offset (offset + length) 0 startRow startColumn []) Ok Err Ok Err of
10542 Ok value _ _ ->
10643 Right value
10744
108− Err (ParseError row col problem) ->
45+ Err (E.ParseError row col problem) ->
10946 let
11047 pos = R.Position row col
11148 mkError overallRegion subRegion =
11249 Left (A.A overallRegion (E.Parse subRegion problem))
11350 in
11451 case problem of
115− BadChar endCol ->
52+ E.BadChar endCol ->
11653 mkError (R.Region pos (R.Position row endCol)) Nothing
11754
118− BadEscape width _ ->
55+ E.BadEscape width _ ->
11956 mkError (R.Region pos (R.Position row (col + width))) Nothing
12057
121− BadOp _ ((_, start) : _) ->
58+ E.BadOp _ ((_, start) : _) ->
12259 mkError (R.Region start pos) (Just (R.Region pos pos))
12360
124− Theories ((_, start) : _) _ ->
61+ E.Theories ((_, start) : _) _ ->
12562 mkError (R.Region start pos) (Just (R.Region pos pos))
12663
12764 _ ->
12865 mkError (R.Region pos pos) Nothing
12966
13067
68+
69+-- RESULT
70+
71+
13172data Result a
132− = Ok a State ParseError
133− | Err ParseError
73+ = Ok a State E.ParseError
74+ | Err E.ParseError
13475
13576
13677
@@ -143,16 +84,10 @@ try (Parser parser) =
14384 parser state cok eerr eok eerr
14485
14586
146−shaderFailure :: Int -> Int -> Text -> Parser a
147−shaderFailure row col msg =
148− Parser $ \_ _ cerr _ _ ->
149− cerr (ParseError row col (BadShader msg))
150−
151−
152−deadend :: [Theory] -> Parser a
87+deadend :: [E.Theory] -> Parser a
15388deadend thrys =
15489 Parser $ \(State _ _ _ _ row col ctx) _ _ _ eerr ->
155− eerr (ParseError row col (Theories ctx thrys))
90+ eerr (E.ParseError row col (E.Theories ctx thrys))
15691
15792
15893hint :: E.Next -> Parser a -> Parser a
@@ -160,387 +95,21 @@ hint next (Parser parser) =
16095 Parser $ \state@(State _ _ _ _ row col ctx) cok cerr eok eerr ->
16196 let
16297 eok' x s _ =
163− eok x s (expect row col ctx (Expecting next))
98+ eok x s (expect row col ctx (E.Expecting next))
16499
165100 eerr' _ =
166− eerr (expect row col ctx (Expecting next))
101+ eerr (expect row col ctx (E.Expecting next))
167102 in
168103 parser state cok cerr eok' eerr'
169104
170105
171106endOfFile :: Parser ()
172107endOfFile =
173− Parser $ \state@(State _ _ length _ _ _ _) _ _ eok eerr ->
174− if length == 0 then
175− eok () state noError
176−
177− else
108+ Parser $ \state@(State _ offset terminal _ _ _ _) _ _ eok eerr ->
109+ if offset < terminal then
178110 eerr noError
179−
180−
181−
182−-- FUNCTOR
183−
184−
185−instance Functor Parser where
186− {-# INLINE fmap #-}
187− fmap f (Parser parser) =
188− Parser $ \state cok cerr eok eerr ->
189− let
190− cok' x s e = cok (f x) s e
191− eok' x s e = eok (f x) s e
192− in
193− parser state cok' cerr eok' eerr
194−
195−
196−
197−-- APPLICATIVE
198−
199−
200−instance Applicative.Applicative Parser where
201− {-# INLINE pure #-}
202− pure = return
203−
204− {-# INLINE (<*>) #-}
205− (<*>) = ap
206−
207−
208−instance Applicative.Alternative Parser where
209− {-# INLINE empty #-}
210− empty = allTheOptionsFailed
211−
212− {-# INLINE (<|>) #-}
213− (<|>) = oneOfHelp
214−
215−
216−oneOf :: [Parser a] -> Parser a
217−oneOf parsers =
218− foldr oneOfHelp allTheOptionsFailed parsers
219−
220−
221−allTheOptionsFailed :: Parser a
222−allTheOptionsFailed =
223− Parser $ \_ _ _ _ eerr ->
224− eerr noError
225−
226−
227−oneOfHelp :: Parser a -> Parser a -> Parser a
228−oneOfHelp (Parser parser1) (Parser parser2) =
229− Parser $ \state cok cerr eok eerr ->
230− let
231− eerr1 e1 =
232− let
233− eok2 y s e2 = eok y s (mergeErrors e1 e2)
234− eerr2 e2 = eerr (mergeErrors e1 e2)
235− in
236− parser2 state cok cerr eok2 eerr2
237− in
238− parser1 state cok cerr eok eerr1
239−
240−
241−mergeErrors :: ParseError -> ParseError -> ParseError
242−mergeErrors e1@(ParseError r1 c1 p1) e2@(ParseError r2 c2 p2) =
243− case compare r1 r2 of
244− LT -> e2
245− GT -> e1
246− EQ ->
247− case compare c1 c2 of
248− LT -> e2
249− GT -> e1
250− EQ ->
251− case (p1, p2) of
252− (Theories _ [], Theories _ _) ->
253− e2
254−
255− (Theories _ _, Theories _ []) ->
256− e1
257−
258− (Theories ctx ts1, Theories _ ts2) ->
259− ParseError r1 c1 (Theories ctx (ts1 ++ ts2))
260−
261− (Theories _ _, _) ->
262− e2
263−
264− (_, _) ->
265− e1
266−
267−
268−
269−-- MONAD
270−
271−
272−instance Monad Parser where
273− {-# INLINE return #-}
274− return value =
275− Parser $ \state _ _ eok _ ->
276− eok value state noError
277−
278− {-# INLINE (>>=) #-}
279− (Parser parser) >>= callback =
280− Parser $ \state cok cerr eok eerr ->
281− let
282− cok1 x s1 e1 =
283− let
284− eok2 y s2 e2 = cok y s2 (mergeErrors e1 e2)
285− eerr2 e2 = cerr (mergeErrors e1 e2)
286− in
287− case callback x of
288− Parser parser2 -> parser2 s1 cok cerr eok2 eerr2
289−
290− eok1 x s1 e1 =
291− let
292− eok2 y s2 e2 = eok y s2 (mergeErrors e1 e2)
293− eerr2 e2 = eerr (mergeErrors e1 e2)
294− in
295− case callback x of
296− Parser parser2 -> parser2 s1 cok cerr eok2 eerr2
297− in
298− parser state cok1 cerr eok1 eerr
299−
300−
301−
302−-- TEXT
303−
304−
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
311− let
312− (# newRow, newCol #) =
313− moveCursor tArray tOffset tLen row col
314−
315− !newState =
316− State array (offset + tLen) (length - tLen) indent newRow newCol ctx
317− in
318− cok () newState noError
319−
320− else
321− eerr (expect row col ctx theory)
322−
323−
324−moveCursor :: Text.Array -> Int -> Int -> Int -> Int -> (# Int, Int #)
325−moveCursor array offset length row col =
326− if length == 0 then
327− (# row, col #)
328−
329− else
330− let
331− !word = Text.unsafeIndex array offset
332− in
333− if word == 0x000A {- \n -} then
334− moveCursor array (offset + 1) (length - 1) (row + 1) 1
335−
336− else if word < 0xD800 || 0xDBFF < word then
337− moveCursor array (offset + 1) (length - 1) row (col + 1)
338−
339− else
340− moveCursor array (offset + 2) (length - 2) row (col + 1)
341−
342−
343−underscore :: Parser ()
344−underscore =
345− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
346− if length == 0 || Text.unsafeIndex array offset /= 0x005F {- _ -} then
347− eerr noError
348−
349− else if length >= 2 && isInnerVarChar (peekChar array (offset + 1)) then
350− cerr (ParseError row (col + 1) (BadUnderscore (peekChar array (offset + 1))))
351−
352− else
353− let
354− !newState =
355− State array (offset + 1) (length - 1) indent row (col + 1) ctx
356− in
357− cok () newState noError
358−
359−
360−
361−-- KEYWORDS
362−
363−
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
368−
369− if isInnerVarChar (peekChar array (offset + tLen)) then
370− eerr (expect row col ctx (Keyword kwd))
371−
372− else
373− let
374− (# newRow, newCol #) =
375− moveCursor tArray tOffset tLen row col
376−
377− !newState =
378− State array (offset + tLen) (length - tLen) indent newRow newCol ctx
379− in
380− cok () newState noError
381−
382− else
383− eerr (expect row col ctx (Keyword kwd))
384−
385−
386−keywords :: Set.Set Text
387−keywords =
388− Set.fromList
389− [ "if", "then", "else"
390− , "case", "of"
391− , "let", "in"
392− , "type"
393− , "module", "where"
394− , "import", "exposing"
395− , "as"
396− , "port"
397− ]
398−
399−
400−
401−-- VARIABLES
402−
403−
404−lowVar :: Parser Text.Text
405−lowVar =
406− varPrim LowVar Char.isLower
407−
408−
409−capVar :: Parser Text.Text
410−capVar =
411− varPrim CapVar Char.isUpper
412−
413−
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 ->
418− if length == 0 then
419− eerr (expect row col ctx theory)
420−
421− else
422− let
423− (Text.Iter char size) = peek array offset
424− in
425− if isGoodFirstChar char then
426− let
427− (# newOffset, newLength, newCol #) =
428− varPrimHelp array (offset + size) (length - size) (col + 1)
429−
430− copiedText =
431− copyText array offset (newOffset - offset)
432− in
433− if Set.member copiedText keywords then
434− eerr (expect row newCol ctx theory)
435− else
436− cok copiedText (State array newOffset newLength indent row newCol ctx) noError
437−
438− else
439− eerr (expect row col ctx theory)
440−
441−
442−{-# INLINE copyText #-}
443−copyText :: Text.Array -> Int -> Int -> Text.Text
444−copyText array offset size =
445− let
446− makeArray =
447− do mutableArray <- Text.new size
448− Text.copyI mutableArray 0 array offset size
449− return mutableArray
450− in
451− Text.Text (Text.run makeArray) 0 size
452−
453−
454−{-# INLINE peek #-}
455−peek :: Text.Array -> Int -> Text.Iter
456−peek array offset =
457− let
458− !word = Text.unsafeIndex array offset
459− in
460− if word < 0xD800 || 0xDBFF < word then
461− Text.Iter (unsafeChr word) 1
462− else
463− Text.Iter (chr2 word (Text.unsafeIndex array (offset + 1))) 2
464−
465−
466−{-# INLINE peekChar #-}
467−peekChar :: Text.Array -> Int -> Char
468−peekChar array offset =
469− let
470− !word = Text.unsafeIndex array offset
471− in
472− if word < 0xD800 || 0xDBFF < word then
473− unsafeChr word
474− else
475− chr2 word (Text.unsafeIndex array (offset + 1))
476−
477−
478−varPrimHelp :: Text.Array -> Int -> Int -> Int -> (# Int, Int, Int #)
479−varPrimHelp array offset length col =
480− if length == 0 then
481− (# offset, length, col #)
482−
483− else
484− let
485− (Text.Iter char size) = peek array offset -- TODO look for latin chars directly?
486− in
487− if isInnerVarChar char then
488− varPrimHelp array (offset + size) (length - size) (col + 1)
489−
490− else
491− (# offset, length, col #)
492−
493−
494−{-# INLINE isInnerVarChar #-}
495−isInnerVarChar :: Char -> Bool
496−isInnerVarChar char =
497− Char.isAlphaNum char || char == '_'
498−
499−
500−
501−-- INFIX OPS
502−
503−
504−infixOp :: Parser Text
505−infixOp =
506− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
507− if length == 0 then
508− eerr (expect row col ctx InfixOp)
509−
510111 else
511− let
512− (Text.Iter char size) = peek array offset
513− in
514− if not (Help.isSymbol char) then
515− eerr (expect row col ctx InfixOp)
516− else
517− case infixOpHelp array (offset + size) (length - size) row (col + 1) of
518− Left err ->
519− cerr err
520−
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
529−
530−
531−infixOpHelp :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError (Int, Int, Int)
532−infixOpHelp array offset length row col =
533− if length == 0 then
534− Right ( offset, length, col )
535−
536− else
537− let
538− (Text.Iter char size) = peek array offset
539− in
540− if Help.isSymbol char then
541− infixOpHelp array (offset + size) (length - size) row (col + 1)
542− else
543− Right ( offset, length, col )
112+ eok () state noError
544113
545114
546115
@@ -591,920 +160,65 @@ setIndent indent =
591160
592161
593162
594−-- WHITESPACE
595−
596−
597−newtype SPos =
598− SPos R.Position
599−
600−
601−whitespace :: Parser SPos
602−whitespace =
603− Parser $ \(State array offset length indent row col ctx) cok cerr _ _ ->
604− case eatSpaces array offset length row col of
605− Left err ->
606− cerr err
607−
608− Right (newOffset, newLength, newRow, newCol) ->
609− cok
610− (SPos (R.Position newRow newCol))
611− (State array newOffset newLength indent newRow newCol ctx)
612− noError
613−
614−
615−eatSpaces :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
616−eatSpaces array offset length row col =
617− if length == 0 then
618− Right ( offset, length, row, col )
619−
620− else
621− case Text.unsafeIndex array offset of
622− 0x0020 {- -} ->
623− eatSpaces array (offset + 1) (length - 1) row (col + 1)
624−
625− 0x000A {- \n -} ->
626− eatSpaces array (offset + 1) (length - 1) (row + 1) 1
627−
628− 0x007B {- { -} ->
629− eatMultiComment array offset length row col
630−
631− 0x002D {- - -} ->
632− eatLineComment array offset length row col
633−
634− 0x000D {- \r -} ->
635− eatSpaces array (offset + 1) (length - 1) row col
636−
637− 0x0009 {- \t -} ->
638− Left (ParseError row col Tab)
639−
640− _ ->
641− Right ( offset, length, row, col )
642−
643−
644−
645−-- LINE COMMENTS
646−
647−
648−eatLineComment :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
649−eatLineComment array offset length row col =
650− if length == 1 then
651− Right ( offset, length, row, col )
652−
653− else
654− case Text.unsafeIndex array (offset + 1) of
655− 0x002D {- - -} ->
656− eatLineCommentHelp array (offset + 2) (length - 2) row (col + 2)
657−
658− _ ->
659− Right ( offset, length, row, col )
660−
661−
662−eatLineCommentHelp :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
663−eatLineCommentHelp array offset length row col =
664− if length == 0 then
665− Right ( offset, length, row, col )
666−
667− else
668− let
669− !word = Text.unsafeIndex array offset
670− in
671− if word == 0x000A {- \n -} then
672− eatSpaces array (offset + 1) (length - 1) (row + 1) 1
673−
674− else if word < 0xD800 || 0xDBFF < word then
675− eatLineCommentHelp array (offset + 1) (length - 1) row (col + 1)
676−
677− else
678− eatLineCommentHelp array (offset + 2) (length - 2) row (col + 1)
679−
680−
681−
682−-- MULTI COMMENTS
683−
684−
685−eatMultiComment :: Text.Array -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
686−eatMultiComment array offset length row col =
687− if length <= 2 then
688− Right ( offset, length, row, col )
689−
690− else
691− let
692− !yesDash = Text.unsafeIndex array (offset + 1) == 0x002D {- - -}
693− !noBar = Text.unsafeIndex array (offset + 2) /= 0x007C {- | -}
694− in
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
699−
700− else
701− Right ( offset, length, row, col )
702−
703−
704−eatMultiCommentHelp :: Text.Array -> Int -> Int -> Int -> Int -> Int -> Either ParseError ( Int, Int, Int, Int )
705−eatMultiCommentHelp array offset length row col openComments =
706− if length == 0 then
707− Left (ParseError row col EndOfFile_Comment)
708−
709− else
710− let
711− !word = Text.unsafeIndex array offset
712− in
713− if word == 0x000A {- \n -} then
714−
715− eatMultiCommentHelp array (offset + 1) (length - 1) (row + 1) 1 openComments
716−
717− else if word == 0x002D {- - -} && length > 1 && Text.unsafeIndex array (offset + 1) == 0x007D {- } -} then
718−
719− if openComments == 1 then
720− Right ( offset + 2, length - 2, row, col + 2 )
721− else
722− eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 2) (openComments - 1)
723−
724− else if word == 0x007B {- { -} && length > 1 && Text.unsafeIndex array (offset + 1) == 0x002D {- - -} then
725−
726− eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 2) (openComments + 1)
727−
728− else if word < 0xD800 || 0xDBFF < word then
729−
730− eatMultiCommentHelp array (offset + 1) (length - 1) row (col + 1) openComments
731−
732− else
733−
734− eatMultiCommentHelp array (offset + 2) (length - 2) row (col + 1) openComments
735−
736−
737−
738−-- DOCUMENTATION COMMENT
739−
740−
741−docComment :: Parser Text
742−docComment =
743− do docCommentOpen
744− Parser $ \(State array offset length indent row col ctx) cok cerr _ _ ->
745− case eatMultiCommentHelp array offset length row col 1 of
746− Left err ->
747− cerr err
748−
749− Right (newOffset, newLength, newRow, newCol) ->
750− cok
751− (copyText array offset (newOffset - offset - 2))
752− (State array newOffset newLength indent newRow newCol ctx)
753− noError
754−
755−
756−{-# NOINLINE docCommentOpen #-}
757−docCommentOpen :: Parser ()
758−docCommentOpen =
759− symbol "{-|"
760−
761−
762−chompUntilDocs :: Parser Bool
763−chompUntilDocs =
764− Parser $ \(State array offset length indent row col ctx) cok _ _ _ ->
765− let
766− (# isStart, newOffset, newLength, newRow, newCol #) =
767− eatDocs array offset length row col
768−
769− !newState =
770− State array newOffset newLength indent newRow newCol ctx
771− in
772− cok isStart newState noError
773−
774−
775−eatDocs :: Text.Array -> Int -> Int -> Int -> Int -> (# Bool, Int, Int, Int, Int #)
776−eatDocs array offset length row col =
777− if length == 0 then
778− (# False, offset, length, row, col #)
779−
780− else if isDocsStart array offset length then
781− (# True, offset + 5, length - 5, row, col + 5 #)
782−
783− else
784− let
785− !word = Text.unsafeIndex array offset
786− in
787− if word == 0x000A {- \n -} then
788− eatDocs array (offset + 1) (length - 1) (row + 1) 1
789−
790− else if word < 0xD800 || 0xDBFF < word then
791− eatDocs array (offset + 1) (length - 1) row (col + 1)
792−
793− else
794− eatDocs array (offset + 2) (length - 2) row (col + 1)
795−
796−
797−isDocsStart :: Text.Array -> Int -> Int -> Bool
798−isDocsStart array offset length =
799− length >= 5
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 -}
805−
806−
807−
808−-- STRINGS
809−
810−
811−string :: Parser Text.Text
812−string =
813− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
814− if length == 0 then
815− eerr noError
816−
817− else
818− let
819− !word = Text.unsafeIndex array offset
820− in
821− if word /= 0x0022 {- " -} then
822− eerr noError
823−
824− else
825− let
826− stringResult =
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
829− else
830− singleString array (offset + 1) (length - 1) row (col + 1) (offset + 1) mempty
831− in
832− case stringResult of
833− Left err ->
834− cerr err
835−
836− Right ( newOffset, newLength, newCol, builder ) ->
837− cok
838− (LT.toStrict (B.toLazyText builder))
839− (State array newOffset newLength indent row newCol ctx)
840− noError
841−
842−
843−{-# INLINE isQuote #-}
844−isQuote :: Text.Array -> Int -> Bool
845−isQuote array offset =
846− Text.unsafeIndex array offset == 0x0022 {- " -}
847−
848−
849−
850−-- SINGLE STRINGS
851−
852−
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 =
855− if length == 0 then
856− Left (ParseError row col EndOfFile_String)
857−
858− else
859− let
860− !word = Text.unsafeIndex array offset
861− in
862− if word == 0x0022 {- " -} then
863− Right
864− ( offset + 1
865− , length - 1
866− , col + 1
867− , builder <> B.fromText (copyText array initialOffset (offset - initialOffset))
868− )
869−
870− else if word == 0x000A {- \n -} then
871− Left (ParseError row col NewLineInString)
872−
873− else if word == 0x0027 {- ' -} then
874− singleStringHelp array offset length row col initialOffset builder 1 "\\'"
875−
876− else if word == 0x005C {- \ -} then
877− case eatEscape array (offset + 1) (length - 1) of
878− EscapeNormal ->
879− singleString array (offset + 2) (length - 2) row (col + 2) initialOffset builder
880−
881− EscapeUnicode delta code ->
882− singleStringHelp array offset length row col initialOffset builder delta code
883−
884− EscapeProblem newOffset problem ->
885− Left (ParseError row col (BadEscape (newOffset - offset) problem))
886−
887− EscapeEndOfFile ->
888− Left (ParseError row (col + 1) EndOfFile_String)
889−
890− else if word < 0xD800 || 0xDBFF < word then
891− singleString array (offset + 1) (length - 1) row (col + 1) initialOffset builder
892−
893− else
894− singleString array (offset + 2) (length - 2) row (col + 1) initialOffset builder
895−
896−
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 =
899− let
900− chunk = copyText array initialOffset (offset - initialOffset)
901− newBuilder = builder <> B.fromText chunk <> B.fromText escape
902− !newOffset = offset + delta
903− in
904− singleString array newOffset (length - delta) row (col + delta) newOffset newBuilder
905−
906−
907−
908−-- MULTI STRINGS
909−
910−
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 =
913− if length < 3 then
914− Left (ParseError row col EndOfFile_MultiString)
915−
916− else
917− let
918− !word = Text.unsafeIndex array offset
919− in
920− if word == 0x0022 {- " -} && isQuote array (offset + 1) && isQuote array (offset + 2) then
921− Right
922− ( offset + 3
923− , length - 3
924− , col + 3
925− , builder <> B.fromText (copyText array initialOffset (offset - initialOffset))
926− )
927−
928− else if word == 0x0027 {- ' -} then
929− multiStringHelp array offset length row col initialOffset builder 1 "\\'"
930−
931− else if word == 0x000A {- \n -} then
932− multiStringHelp array offset length row col initialOffset builder 1 "\\n"
933−
934− else if word == 0x005C {- \ -} then
935−
936− case eatEscape array (offset + 1) (length - 1) of
937− EscapeNormal ->
938− multiString array (offset + 2) (length - 2) row (col + 2) initialOffset builder
939−
940− EscapeUnicode delta code ->
941− multiStringHelp array offset length row col initialOffset builder delta code
942−
943− EscapeProblem newOffset problem ->
944− Left (ParseError row col (BadEscape (newOffset - offset) problem))
945−
946− EscapeEndOfFile ->
947− Left (ParseError row (col + 1) EndOfFile_MultiString)
948−
949− else if word < 0xD800 || 0xDBFF < word then
950− multiString array (offset + 1) (length - 1) row (col + 1) initialOffset builder
951−
952− else
953− multiString array (offset + 2) (length - 2) row (col + 1) initialOffset builder
954−
955−
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 =
958− let
959− chunk = copyText array initialOffset (offset - initialOffset)
960− newBuilder = builder <> B.fromText chunk <> B.fromText escape
961− !newOffset = offset + delta
962− in
963− multiString array newOffset (length - delta) row (col + delta) newOffset newBuilder
964−
965−
966−
967−-- CHARACTER
968−
969−
970−character :: Parser Text
971−character =
972− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
973− if length == 0 then
974− eerr noError
975−
976− else
977− if Text.unsafeIndex array offset /= 0x0027 {- ' -} then
978− eerr noError
979−
980− else
981− case chompChar array (offset + 1) (length - 1) row (col + 1) 0 "" of
982− Left err ->
983− cerr err
984−
985− Right ( newOffset, newLength, newCol, numChars, mostRecentChar ) ->
986− if numChars /= 1 then
987− cerr (ParseError row col (BadChar newCol))
988− else
989− cok mostRecentChar
990− (State array newOffset newLength indent row newCol ctx)
991− noError
992−
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 =
995− if length == 0 then
996− Left (ParseError row col EndOfFile_Char)
997−
998− else
999− let
1000− !word = Text.unsafeIndex array offset
1001− in
1002− if word == 0x0027 {- ' -} then
1003− Right
1004− ( offset + 1
1005− , length - 1
1006− , col + 1
1007− , numChars
1008− , mostRecentChar
1009− )
1010−
1011− else if word == 0x000A {- \n -} then
1012− Left (ParseError row col NewLineInChar)
1013−
1014− else if word == 0x0022 {- " -} then
1015− chompChar array (offset + 1) (length - 1) row (col + 1) (numChars + 1) "\\\""
1016−
1017− else if word == 0x005C {- \ -} then
1018− case eatEscape array (offset + 1) (length - 1) of
1019− EscapeNormal ->
1020− chompChar array (offset + 2) (length - 2) row (col + 2) (numChars + 1) (copyText array offset 2)
1021−
1022− EscapeUnicode delta code ->
1023− chompChar array (offset + delta) (length - delta) row (col + delta) (numChars + 1) code
1024−
1025− EscapeProblem newOffset problem ->
1026− Left (ParseError row col (BadEscape (newOffset - offset) problem))
1027−
1028− EscapeEndOfFile ->
1029− Left (ParseError row col EndOfFile_Char)
1030−
1031− else if word < 0xD800 || 0xDBFF < word then
1032− chompChar array (offset + 1) (length - 1) row (col + 1) (numChars + 1) (copyText array offset 1)
1033−
1034− else
1035− chompChar array (offset + 2) (length - 2) row (col + 1) (numChars + 1) (copyText array offset 2)
1036−
1037−
1038−
1039−-- ESCAPE CHARACTERS
1040−
1041−
1042−data Escape
1043− = EscapeNormal
1044− | EscapeUnicode !Int Text.Text
1045− | EscapeProblem !Int E.EscapeProblem
1046− | EscapeEndOfFile
1047−
1048−
1049−eatEscape :: Text.Array -> Int -> Int -> Escape
1050−eatEscape array offset length =
1051− if length == 0 then
1052− EscapeEndOfFile
1053−
1054− else
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
1064−
1065−
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
1070− else
1071− let
1072− !digitOffset =
1073− offset + 1
1074−
1075− (# newOffset, newLength, code #) =
1076− chompHex array digitOffset (length - 1)
1077−
1078− !numDigits =
1079− newOffset - digitOffset
1080− in
1081− if newLength == 0 || Text.unsafeIndex array newOffset /= 0x007D {- } -} then
1082− EscapeProblem newOffset E.UnicodeSyntax
1083−
1084− else if code < 0 || 0x10FFFF < code then
1085− EscapeProblem (newOffset + 1) E.UnicodeRange
1086−
1087− else if numDigits < 4 || 6 < numDigits then
1088− EscapeProblem (newOffset + 1) $
1089− E.UnicodeLength numDigits (copyText array digitOffset numDigits)
1090−
1091− else
1092− EscapeUnicode (numDigits + 4) (codePointToBuilder code)
1093−
1094−
1095−{-# INLINE codePointToBuilder #-}
1096−codePointToBuilder :: Int -> Text.Text
1097−codePointToBuilder code =
1098− if code < 0xFFFF then
1099− "\\u" <> toHex code
1100−
1101− else
1102− "\\u" <> toHex ((code `div` 0x400) + 0xD800)
1103− <>
1104− "\\u" <> toHex ((code `mod` 0x400) + 0xDC00)
1105−
1106−
1107−toHex :: Int -> Text.Text
1108−toHex word16 =
1109− Text.decodeUtf8 $ BS.toStrict $ BS.toLazyByteString $
1110− BS.word16HexFixed (fromIntegral word16)
1111−
1112−
1113−
1114−-- DIGITS
1115−
1116−
1117−digit :: Parser Int
1118−digit =
1119− Parser $ \(State array offset length indent row col ctx) cok _ _ eerr ->
1120− if length == 0 then
1121− eerr (expect row col ctx Digit)
1122−
1123− else
1124− let
1125− !word = Text.unsafeIndex array offset
1126− in
1127− if isDigit word then
1128− cok
1129− (fromIntegral (word - 0x0030 {- 0 -}))
1130− (State array (offset + 1) (length - 1) indent row (col + 1) ctx)
1131− noError
1132− else
1133− eerr (expect row col ctx Digit)
1134−
1135−
1136−
1137−-- NUMBERS
1138−
1139−
1140−number :: Parser L.Literal
1141−number =
1142− Parser $ \(State array offset length indent row col ctx) cok cerr _ eerr ->
1143− if length == 0 then
1144− eerr noError
1145−
1146− else
1147− let
1148− !word = Text.unsafeIndex array offset
1149− in
1150− if not (isDigit word) then
1151− eerr noError
1152−
1153− else
1154− let
1155− chompResults =
1156− if word == 0x0030 {- 0 -} then
1157− chompZero array offset (offset + 1) (length - 1)
1158− else
1159− chompInt array offset (offset + 1) (length - 1)
1160− in
1161− case chompResults of
1162− Left (newOffset, problem) ->
1163− cerr (ParseError row (col + (newOffset - offset)) problem)
1164−
1165− Right (newOffset, newLength, literal) ->
1166− if isDirtyEnd array newOffset newLength then
1167− cerr (ParseError row (col + (newOffset - offset)) BadNumberEnd)
1168−
1169− else
1170− cok
1171− literal
1172− (State array newOffset newLength indent row (col + (newOffset - offset)) ctx)
1173− noError
1174−
1175−
1176−isDirtyEnd :: Text.Array -> Int -> Int -> Bool
1177−isDirtyEnd array offset length =
1178− if length == 0 then
1179− False
1180−
1181− else
1182− let
1183− !char = peekChar array offset
1184− in
1185− Char.isAlpha char || char == '_'
1186−
1187−
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
1191−
1192− Right ( offset, length, readInt array startOffset offset )
1193−
1194− else
1195−
1196− let
1197− !word = Text.unsafeIndex array offset
1198− in
1199− if isDigit word then
1200− chompInt array startOffset (offset + 1) (length - 1)
1201−
1202− else if word == 0x002E {- . -} then
1203− chompFraction array startOffset (offset + 1) (length - 1)
1204−
1205− else if word == 0x0065 {- e -} || word == 0x0045 {- E -} then
1206− chompExponent array startOffset (offset + 1) (length - 1)
1207−
1208− else
1209− Right ( offset, length, readInt array startOffset offset )
1210−
1211−
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)
1216−
1217− else if isDigit (Text.unsafeIndex array offset) then
1218− chompFractionHelp array startOffset (offset + 1) (length - 1)
1219−
1220− else
1221− Left (offset, BadNumberDot)
1222−
1223−
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)
1228−
1229− else
1230− let
1231− !word = Text.unsafeIndex array offset
1232− in
1233− if isDigit word then
1234− chompFractionHelp array startOffset (offset + 1) (length - 1)
1235−
1236− else if word == 0x0065 {- e -} || word == 0x0045 {- E -} then
1237− chompExponent array startOffset (offset + 1) (length - 1)
1238−
1239− else
1240− Right (offset, length, readFloat array startOffset offset)
1241−
1242−
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)
1247−
1248− else
1249− let
1250− !word = Text.unsafeIndex array offset
1251− in
1252− if isDigit word then
1253− chompExponentHelp array startOffset (offset + 1) (length - 1)
1254−
1255− else if word == 0x002B {- + -} || word == 0x002D {- - -} then
1256−
1257− if length > 1 && isDigit (Text.unsafeIndex array (offset + 1)) then
1258− chompExponentHelp array startOffset (offset + 2) (length - 2)
1259− else
1260− Left (offset, BadNumberExp)
1261−
1262− else
1263− Left (offset, BadNumberExp)
1264−
1265−
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)
1270−
1271− else if isDigit (Text.unsafeIndex array offset) then
1272− chompExponentHelp array startOffset (offset + 1) (length - 1)
1273−
1274− else
1275− Right (offset, length, readFloat array startOffset offset)
1276−
1277−
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 )
1282−
1283− else
1284− let
1285− !word = Text.unsafeIndex array offset
1286− in
1287− if word == 0x0078 {- x -} then
1288− chompHexNumber array (offset + 1) (length - 1)
1289−
1290− else if word == 0x002E {- . -} then
1291− chompFraction array startOffset (offset + 1) (length - 1)
1292−
1293− else if isDigit word then
1294− Left (offset, BadNumberZero)
163+-- SPACE PARSER
1295164
1296− else
1297− Right ( offset, length, L.IntNum 0 )
1298165
166+type SParser a =
167+ Parser (a, R.Position, W.SPos)
1299168
1300−chompHexNumber :: Text.Array -> Int -> Int -> Either (Int, Problem) (Int, Int, L.Literal)
1301−chompHexNumber array offset length =
1302− let
1303− (# newOffset, newLength, hexNumber #) =
1304− chompHex array offset length
1305− in
1306− if hexNumber == -1 then
1307− Left ( newOffset, BadNumberHex )
1308− else
1309− Right ( newOffset, newLength, L.IntNum hexNumber )
1310169
1311170
171+-- LOCATION
1312172
1313−-- NUMBER HELPERS
1314173
174+addLocation :: Parser a -> Parser (A.Located a)
175+addLocation parser =
176+ do start <- getPosition
177+ value <- parser
178+ end <- getPosition
179+ return (A.at start end value)
1315180
1316−{-# INLINE isDigit #-}
1317−isDigit :: Word16 -> Bool
1318−isDigit word =
1319− word <= 0x0039 {- 9 -} && word >= 0x0030 {- 0 -}
1320181
182+inContext :: R.Position -> E.Context -> Parser a -> Parser a
183+inContext pos ctx parser =
184+ do pushContext pos ctx
185+ a <- parser
186+ popContext a
1321187
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)
1327−
1328−
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)
1334−
1335−
1336−
1337−-- CHOMP HEX
1338−
1339−
1340−-- Return -1 if it has NO digits
1341−-- Return -2 if it has BAD digits
1342−
1343−{-# INLINE chompHex #-}
1344−chompHex :: Text.Array -> Int -> Int -> (# Int, Int, Int #)
1345−chompHex array offset length =
1346− chompHexHelp array offset length (-1) 0
1347−
1348−
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 #)
1353− else
1354− let
1355− !newNumber =
1356− stepHex (Text.unsafeIndex array offset) hexNumber
1357− in
1358− if newNumber < 0 then
1359− (# offset, length, if newNumber == -1 then finalNumber else -2 #)
1360− else
1361− chompHexHelp array (offset + 1) (length - 1) newNumber newNumber
1362−
1363−
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 -})
1369−
1370− else if word <= 0x0066 {- f -} && word >= 0x0061 {- a -} then
1371− 16 * hexNumber + 10 + fromIntegral (word - 0x0061 {- a -})
1372−
1373− else if word <= 0x0046 {- F -} && word >= 0x0041 {- A -} then
1374− 16 * hexNumber + 10 + fromIntegral (word - 0x0041 {- A -})
1375−
1376− else
1377− -1
1378−
1379−
1380−
1381−-- SHADER
1382−
1383−
1384−shaderSource :: Parser Text
1385−shaderSource =
1386− do shaderSourceOpen
1387− Parser $ \(State array offset length indent row col ctx) cok cerr _ _ ->
1388− case eatShader array offset length row col of
1389− Nothing ->
1390− cerr (ParseError row col EndOfFile_Shader)
1391−
1392− Just ( shaderEndOffset, newRow, newCol ) ->
1393− let
1394− !size = shaderEndOffset - offset
1395− !newState =
1396− State array (shaderEndOffset + 2) (length - size - 2) indent newRow newCol ctx
1397− in
1398− cok (copyText array offset size) newState noError
1399−
1400−
1401−{-# NOINLINE shaderSourceOpen #-}
1402−shaderSourceOpen :: Parser ()
1403−shaderSourceOpen =
1404− symbol "[glsl|"
1405−
1406−
1407−eatShader :: Text.Array -> Int -> Int -> Int -> Int -> Maybe (Int, Int, Int)
1408−eatShader array offset length row col =
1409− if length < 2 then
1410− Nothing
1411−
1412− else
1413− let
1414− !word = Text.unsafeIndex array offset
1415− in
1416− if word == 0x007C {- | -} && Text.unsafeIndex array (offset + 1) == 0x005D {- ] -} then
1417− Just ( offset, row, col + 2 )
1418−
1419− else if word == 0x000A {- \n -} then
1420− eatShader array (offset + 1) (length - 1) (row + 1) 1
1421−
1422− else if word < 0xD800 || 0xDBFF < word then
1423− eatShader array (offset + 1) (length - 1) row (col + 1)
1424−
1425− else
1426− eatShader array (offset + 2) (length - 2) row (col + 1)
1427−
1428−
1429−
1430−-- KERNEL CHOMP
1431−
1432−
1433−data ChunkTag
1434− = KernelEnum Word8 Text
1435− | KernelProd
1436− | KernelDebug
1437− | KernelImport Text
1438− | KernelJsField Text
1439− | KernelElmField Text
1440−
1441−
1442−kernelChunk :: Parser (BS.ByteString, Maybe ChunkTag)
1443−kernelChunk =
1444− Parser $ \(State array offset length indent row col ctx) cok _ _ _ ->
1445− let
1446− (# maybeChunkTag, jsOffset, newOffset, newLength, newRow, newCol #) =
1447− chompChunk array offset length row col
1448−
1449− !javascript =
1450− Text.encodeUtf8 (copyText array offset (jsOffset - offset))
1451−
1452− !newState =
1453− State array newOffset newLength indent newRow newCol ctx
1454− in
1455− cok (javascript, maybeChunkTag) newState noError
1456−
1457−
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 #)
1462−
1463− else
1464− let
1465− !word = Text.unsafeIndex array offset
1466− in
1467− if word == 0x005F {- _ -} && length > 3 && Text.unsafeIndex array (offset + 1) == 0x005F {- _ -} then
1468− chompChunkTag array offset (offset + 2) (length - 2) row (col + 2)
1469−
1470− else if word == 0x000A {- \n -} then
1471− chompChunk array (offset + 1) (length - 1) (row + 1) 1
1472188
1473− else if word < 0xD800 || 0xDBFF < word then
1474− chompChunk array (offset + 1) (length - 1) row (col + 1)
189+-- WHITESPACE VARIATIONS
1475190
1476− else
1477− chompChunk array (offset + 2) (length - 2) row (col + 1)
1478191
192+spaces :: Parser ()
193+spaces =
194+ checkSpace =<< W.whitespace
1479195
1480−chompChunkTag :: Text.Array -> Int -> Int -> Int -> Int -> Int -> (# Maybe ChunkTag, Int, Int, Int, Int, Int #)
1481−chompChunkTag array jsOffset offset length row col =
1482− let
1483− (# newOffset, newLength, newCol #) =
1484− varPrimHelp array (offset + 1) (length - 1) (col + 1)
1485196
1486− !word =
1487− Text.unsafeIndex array offset
197+noSpace :: R.Position -> W.SPos -> Parser ()
198+noSpace pos (W.SPos spos) =
199+ if pos == spos
200+ then return ()
201+ else deadend []
1488202
1489− !chunkTag =
1490− if word == 0x0024 {- $ -} then
1491− KernelElmField (copyText array (offset + 1) (newOffset - offset - 1))
1492203
1493− else
1494− let !chunk = copyText array offset (newOffset - offset) in
1495− if isDigit word then
1496− KernelEnum (fromIntegral (word - 0x0030 {- 0 -})) chunk
204+checkSpace :: W.SPos -> Parser ()
205+checkSpace (W.SPos (R.Position _ col)) =
206+ do indent <- getIndent
207+ if col > indent && col > 1
208+ then return ()
209+ else deadend [E.BadSpace]
1497210
1498− else if 0x0061 {- a -} <= word && word <= 0x007A {- z -} then
1499− KernelJsField chunk
1500211
1501− else if chunk == "DEBUG" then
1502− KernelDebug
212+checkAligned :: W.SPos -> Parser ()
213+checkAligned (W.SPos (R.Position _ col)) =
214+ do indent <- getIndent
215+ if col == indent
216+ then return ()
217+ else deadend [E.BadSpace]
1503218
1504− else if chunk == "PROD" then
1505− KernelProd
1506219
1507− else
1508− KernelImport chunk
1509− in
1510− (# Just chunkTag, jsOffset, newOffset, newLength, row, newCol #)
220+checkFreshLine :: W.SPos -> Parser ()
221+checkFreshLine (W.SPos (R.Position _ col)) =
222+ if col == 1
223+ then return ()
224+ else deadend [E.BadSpace]