packages feed

exon-0.3.0.0: lib/Exon/Parse.hs

{-# options_haddock prune #-}

-- |Description: The parser for the quasiquote body, using "FlatParse".
module Exon.Parse where

import Data.Char (isSpace)
import qualified FlatParse.Stateful as FlatParse
import FlatParse.Stateful (
  Result (Err, Fail, OK),
  anyChar,
  branch,
  char,
  empty,
  eof,
  get,
  inSpan,
  lookahead,
  modify,
  put,
  runParserS,
  satisfy,
  some_,
  spanned,
  string,
  takeRest,
  (<|>),
  )
import Prelude hiding (empty, span, (<|>))

import Exon.Data.RawSegment (RawSegment (ExpSegment, StringSegment, WsSegment))

type Parser =
  FlatParse.Parser Text

span :: Parser () -> Parser String
span seek =
  spanned seek \ _ sp -> inSpan sp takeRest

ws :: Parser Char
ws =
  satisfy isSpace

whitespace :: Parser RawSegment
whitespace =
  WsSegment <$> span (some_ ws)

before ::
  Parser a ->
  Parser () ->
  Parser () ->
  Parser ()
before =
  branch . lookahead

finishBefore ::
  Parser a ->
  Parser () ->
  Parser ()
finishBefore cond =
  before (lookahead cond) unit

expr :: Parser ()
expr =
  branch $(char '{') (modify (1 +) *> expr) $
  before $(char '}') closing (anyChar *> expr)
  where
    closing =
      get >>= \case
        0 -> unit
        cur -> put (cur - 1) *> $(char '}') *> expr

interpolation :: Parser RawSegment
interpolation =
  $(string "#{") *> (ExpSegment <$> span expr) <* $(char '}')

untilTokenEnd :: Parser ()
untilTokenEnd =
  branch $(char '\\') (anyChar *> untilTokenEnd) $
  finishBefore ($(string "#{") <|> void ws) $
  eof <|> (anyChar *> untilTokenEnd)

text :: Parser RawSegment
text =
  StringSegment <$> span untilTokenEnd

segment :: Parser RawSegment
segment =
  branch eof empty (whitespace <|> interpolation <|> text)

parser :: Parser [RawSegment]
parser =
  FlatParse.many segment

parse :: String -> Either Text [RawSegment]
parse =
  runParserS parser 0 0 >>> \case
    OK a _ "" -> Right a
    OK _ _ u -> Left ("unconsumed: " <> decodeUtf8 u)
    Fail -> Left "fail"
    Err e -> Left e