packages feed

ribosome-host-0.9.9.9: lib/Ribosome/Host/Data/ApiType.hs

module Ribosome.Host.Data.ApiType where

import Data.Char (isSpace)
import Exon (exon)
import qualified FlatParse.Basic as FlatParse
import FlatParse.Basic (
  Result (Err, Fail, OK),
  branch,
  char,
  inSpan,
  isLatinLetter,
  many_,
  optional,
  readInt,
  runParser,
  satisfy,
  satisfyASCII,
  string,
  switch,
  takeRest,
  withSpan,
  (<|>),
  )
import Prelude hiding (optional, some, span, try, (<|>))
import Text.Show (showsPrec)

import Ribosome.Host.Class.Msgpack.Decode (MsgpackDecode (fromMsgpack))

-- TODO see if using GADT can move some TH stuff to type level
data ApiPrim =
  Boolean
  |
  Integer
  |
  Float
  |
  String
  |
  Dictionary
  |
  Object
  |
  Void
  |
  LuaRef
  deriving stock (Eq, Show)

data ApiType =
  Prim ApiPrim
  |
  Array ApiType (Maybe Int)
  |
  Ext String
  deriving stock (Show, Eq)

polyType :: ApiType -> Bool
polyType = \case
  Prim Object -> True
  Prim Dictionary -> True
  _ -> False

pattern PolyType :: ApiType
pattern PolyType <- (polyType -> True)

type Parser =
  FlatParse.Parser Text

ws :: Parser ()
ws =
  many_ (satisfy isSpace)

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

prim :: Parser ApiPrim
prim =
  $(switch [|
  case _ of
    "Boolean" -> pure Boolean
    "Integer" -> pure Integer
    "Float" -> pure Float
    "String" -> pure String
    "Dictionary" -> pure Dictionary
    "Object" -> pure Object
    "void" -> pure Void
    "LuaRef" -> pure LuaRef
  |])

typedArray :: Parser ApiType
typedArray = do
  t <- apiType
  arity <- optional do
    $(char ',')
    ws
    readInt
  pure (Array t arity)

array :: Parser ApiType
array = do
  $(string "Array")
  branch $(string "Of(") (typedArray <* $(char ')')) (pure (Array (Prim Object) Nothing))

ext :: Parser ApiType
ext =
  Ext <$> span (many_ (satisfyASCII isLatinLetter))

apiType :: Parser ApiType
apiType =
  array <|> (Prim <$> prim) <|> ext

parseApiType :: ByteString -> Either Text ApiType
parseApiType =
  runParser apiType >>> \case
    OK a "" -> Right a
    OK a u -> Left [exon|Parsed #{toText (showsPrec 11 a "")} but got leftovers: #{decodeUtf8 u}|]
    Fail -> Left "fail"
    Err e -> Left e

instance MsgpackDecode ApiType where
  fromMsgpack =
    parseApiType <=< fromMsgpack