bricks-syntax-0.0.0.4: src/Bricks/UnquotedString.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Bricks.UnquotedString
(
-- * Type
UnquotedString
-- * Construction
, unquotedString'try
, unquotedString'orThrow
-- * Deconstruction
, unquotedString'text
-- * Predicates
, text'canBeUnquoted
, char'canBeUnquoted
) where
-- Bricks
import Bricks.Keyword
-- Bricks internal
import Bricks.Internal.Prelude
import Bricks.Internal.Text (Text)
import qualified Bricks.Internal.Text as Text
-- Base
import qualified Data.Char as Char
import qualified Data.List as List
import Prelude (error)
{- | A string that can be rendered unquoted. Unquoted strings are restricted to
a conservative set of characters; see 'text'canBeUnquoted' for the full rules.
This type does not represent a particular part of Bricks syntax, but it is a
wrapper for 'Text' that enforces the limitations of strings at various places in
the Bricks syntax.
==== Construction
- 'unquotedString'try'
- 'unquotedString'orThrow'
==== Deconstruction
- 'unquotedString'text'
==== See also
- 'text'canBeUnquoted'
- 'char'canBeUnquoted'
-}
newtype UnquotedString = UnquotedString { unquotedString'text :: Text }
instance Show UnquotedString where show = show @Text . unquotedString'text
{- | ==== Properties
- A text value may be used to construct an 'UnquotedString' iff it satisfies
'text'canBeUnquoted'.
@'text'canBeUnquoted' x = 'isJust' ('unquotedString'try' x)@ -}
unquotedString'try :: Text -> Maybe UnquotedString
unquotedString'try x =
if text'canBeUnquoted x then Just (UnquotedString x) else Nothing
-- | Throws an exception if the string cannot render unquoted.
unquotedString'orThrow :: Text -> UnquotedString
unquotedString'orThrow x =
if text'canBeUnquoted x then UnquotedString x else
error $ "String " <> show x <> " cannot render unquoted"
{- | Whether a string having this name can be rendered without quoting it.
==== Requirements for unquoted strings
We allow a string to render unquoted if all these conditions are met:
- The string is nonempty
- All characters satify 'char'canBeUnquoted'
- The string is not a keyword
==== Properties
- A text value may be used to construct an 'UnquotedString' iff it satisfies
'text'canBeUnquoted'.
@'text'canBeUnquoted' x = 'isJust' ('unquotedString'try' x)@ -}
-- | ==== Examples
--
-- >>> text'canBeUnquoted "-ab_c"
-- True
--
-- >>> text'canBeUnquoted ""
-- False
--
-- >>> text'canBeUnquoted "a\"b"
-- False
--
-- >>> text'canBeUnquoted "let"
-- False
text'canBeUnquoted :: Text -> Bool
text'canBeUnquoted x =
Text.all char'canBeUnquoted x
&& not (Text.null x)
&& List.all ((/= x) . keywordText) keywords
{- | Whether the character is allowed to be included in an 'UnquotedString'.
Such characters are letters, @+@, @-@, @*@, @/@, and @_@.
This is used in the implementation of 'text'canBeUnquoted'. -}
char'canBeUnquoted :: Char -> Bool
char'canBeUnquoted c =
Char.isLetter c || List.elem c ("+-*/_" :: [Char])