packages feed

bricks 0.0.0.2 → 0.0.0.4

raw patch · 24 files changed

+1348/−3002 lines, 24 filesdep +bricks-internaldep +bricks-internal-testdep +bricks-parsecdep ~basedep ~template-haskellPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: bricks-internal, bricks-internal-test, bricks-parsec, bricks-rendering, bricks-syntax, mtl, transformers

Dependency ranges changed: base, template-haskell

API changes (from Hackage documentation)

- Bricks: DictBinding'Inherit :: Inherit -> DictBinding
- Bricks: Expr'With :: With -> Expression
- Bricks: Inherit :: Maybe Expression -> Seq Str'Static -> Inherit
- Bricks: Str'Unquoted'Unsafe :: Text -> Str'Unquoted
- Bricks: With :: Expression -> Expression -> With
- Bricks: [inherit'names] :: Inherit -> Seq Str'Static
- Bricks: [inherit'source] :: Inherit -> Maybe Expression
- Bricks: [str'unquotedToStatic] :: Str'Unquoted -> Text
- Bricks: [with'context] :: With -> Expression
- Bricks: [with'value] :: With -> Expression
- Bricks: char'canRenderUnquoted :: Char -> Bool
- Bricks: data Inherit
- Bricks: data With
- Bricks: inStr'1'empty :: InStr'1 -> Bool
- Bricks: inStr'1'modifyLevel :: (Natural -> Natural) -> (InStr'1 -> InStr'1)
- Bricks: inStr'1'nonEmpty :: InStr'1 -> Bool
- Bricks: inStr'join :: InStr -> Str'Dynamic
- Bricks: keyword'with :: Keyword
- Bricks: newtype InStr
- Bricks: newtype List
- Bricks: newtype Str'Dynamic
- Bricks: newtype Str'Unquoted
- Bricks: parse'antiquote :: Parser Str'Dynamic
- Bricks: parse'inherit :: Parser Inherit
- Bricks: parse'strDynamic'indentedQ :: Parser Str'Dynamic
- Bricks: parse'strDynamic'normalQ :: Parser Str'Dynamic
- Bricks: parse'strDynamic'quoted :: Parser Str'Dynamic
- Bricks: parse'with :: Parser With
- Bricks: render'inStr'1 :: Render InStr'1
- Bricks: render'inherit :: Render Inherit
- Bricks: render'strUnquoted :: Render Str'Unquoted
- Bricks: render'with :: Render With
- Bricks: str'canRenderUnquoted :: Text -> Bool
- Bricks: str'dynamicToStatic :: Str'Dynamic -> Maybe Str'Static
- Bricks: str'staticToDynamic :: Str'Static -> Str'Dynamic
- Bricks: str'tryUnquoted :: Text -> Maybe Str'Unquoted
- Bricks: str'unquoted'orThrow :: Text -> Str'Unquoted
- Bricks: str'unquotedToDynamic :: Str'Unquoted -> Str'Dynamic
- Bricks: strDynamic'fromList :: [Str'1] -> Str'Dynamic
- Bricks: strDynamic'singleton :: Str'1 -> Str'Dynamic
- Bricks: strDynamic'toList :: Str'Dynamic -> [Str'1]
- Bricks: type Str'Static = Text
- Bricks.Expression: Apply :: Expression -> Expression -> Apply
- Bricks.Expression: Dict :: Bool -> Seq DictBinding -> Dict
- Bricks.Expression: DictBinding'Eq :: Expression -> Expression -> DictBinding
- Bricks.Expression: DictBinding'Inherit :: Inherit -> DictBinding
- Bricks.Expression: DictPattern :: Seq DictPattern'1 -> Bool -> DictPattern
- Bricks.Expression: DictPattern'1 :: Str'Unquoted -> Maybe Expression -> DictPattern'1
- Bricks.Expression: Dot :: Expression -> Expression -> Dot
- Bricks.Expression: Expr'Apply :: Apply -> Expression
- Bricks.Expression: Expr'Dict :: Dict -> Expression
- Bricks.Expression: Expr'Dot :: Dot -> Expression
- Bricks.Expression: Expr'Lambda :: Lambda -> Expression
- Bricks.Expression: Expr'Let :: Let -> Expression
- Bricks.Expression: Expr'List :: List -> Expression
- Bricks.Expression: Expr'Str :: Str'Dynamic -> Expression
- Bricks.Expression: Expr'Var :: Str'Unquoted -> Expression
- Bricks.Expression: Expr'With :: With -> Expression
- Bricks.Expression: Inherit :: Maybe Expression -> Seq Str'Static -> Inherit
- Bricks.Expression: Lambda :: Param -> Expression -> Lambda
- Bricks.Expression: Let :: Seq LetBinding -> Expression -> Let
- Bricks.Expression: LetBinding'Eq :: Str'Static -> Expression -> LetBinding
- Bricks.Expression: LetBinding'Inherit :: Inherit -> LetBinding
- Bricks.Expression: List :: (Seq Expression) -> List
- Bricks.Expression: Param'Both :: Str'Unquoted -> DictPattern -> Param
- Bricks.Expression: Param'DictPattern :: DictPattern -> Param
- Bricks.Expression: Param'Name :: Str'Unquoted -> Param
- Bricks.Expression: Str'1'Antiquote :: Expression -> Str'1
- Bricks.Expression: Str'1'Literal :: Str'Static -> Str'1
- Bricks.Expression: Str'Dynamic :: Seq Str'1 -> Str'Dynamic
- Bricks.Expression: With :: Expression -> Expression -> With
- Bricks.Expression: [apply'arg] :: Apply -> Expression
- Bricks.Expression: [apply'func] :: Apply -> Expression
- Bricks.Expression: [dict'bindings] :: Dict -> Seq DictBinding
- Bricks.Expression: [dict'rec] :: Dict -> Bool
- Bricks.Expression: [dictPattern'1'default] :: DictPattern'1 -> Maybe Expression
- Bricks.Expression: [dictPattern'1'name] :: DictPattern'1 -> Str'Unquoted
- Bricks.Expression: [dictPattern'ellipsis] :: DictPattern -> Bool
- Bricks.Expression: [dictPattern'items] :: DictPattern -> Seq DictPattern'1
- Bricks.Expression: [dot'dict] :: Dot -> Expression
- Bricks.Expression: [dot'key] :: Dot -> Expression
- Bricks.Expression: [inherit'names] :: Inherit -> Seq Str'Static
- Bricks.Expression: [inherit'source] :: Inherit -> Maybe Expression
- Bricks.Expression: [lambda'body] :: Lambda -> Expression
- Bricks.Expression: [lambda'head] :: Lambda -> Param
- Bricks.Expression: [let'bindings] :: Let -> Seq LetBinding
- Bricks.Expression: [let'value] :: Let -> Expression
- Bricks.Expression: [strDynamic'toSeq] :: Str'Dynamic -> Seq Str'1
- Bricks.Expression: [with'context] :: With -> Expression
- Bricks.Expression: [with'value] :: With -> Expression
- Bricks.Expression: data Apply
- Bricks.Expression: data Dict
- Bricks.Expression: data DictBinding
- Bricks.Expression: data DictPattern
- Bricks.Expression: data DictPattern'1
- Bricks.Expression: data Dot
- Bricks.Expression: data Expression
- Bricks.Expression: data Inherit
- Bricks.Expression: data Lambda
- Bricks.Expression: data Let
- Bricks.Expression: data LetBinding
- Bricks.Expression: data Param
- Bricks.Expression: data Str'1
- Bricks.Expression: data With
- Bricks.Expression: expression'applyArgs :: Expression -> [Expression] -> Expression
- Bricks.Expression: expression'applyDots :: Expression -> [Expression] -> Expression
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Apply
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Dict
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.DictBinding
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.DictPattern
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.DictPattern'1
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Dot
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Expression
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Inherit
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Lambda
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Let
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.LetBinding
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.List
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Param
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Str'1
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.Str'Dynamic
- Bricks.Expression: instance Bricks.Expression.Show' Bricks.Expression.With
- Bricks.Expression: instance Data.Semigroup.Semigroup Bricks.Expression.List
- Bricks.Expression: instance Data.Semigroup.Semigroup Bricks.Expression.Str'Dynamic
- Bricks.Expression: instance GHC.Base.Monoid Bricks.Expression.List
- Bricks.Expression: instance GHC.Base.Monoid Bricks.Expression.Str'Dynamic
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Apply
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Dict
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.DictBinding
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.DictPattern
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.DictPattern'1
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Dot
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Expression
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Inherit
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Lambda
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Let
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.LetBinding
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.List
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Param
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Str'1
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.Str'Dynamic
- Bricks.Expression: instance GHC.Show.Show Bricks.Expression.With
- Bricks.Expression: newtype List
- Bricks.Expression: newtype Str'Dynamic
- Bricks.Expression: str'dynamicToStatic :: Str'Dynamic -> Maybe Str'Static
- Bricks.Expression: str'staticToDynamic :: Str'Static -> Str'Dynamic
- Bricks.Expression: str'unquotedToDynamic :: Str'Unquoted -> Str'Dynamic
- Bricks.Expression: strDynamic'fromList :: [Str'1] -> Str'Dynamic
- Bricks.Expression: strDynamic'singleton :: Str'1 -> Str'Dynamic
- Bricks.Expression: strDynamic'toList :: Str'Dynamic -> [Str'1]
- Bricks.Expression: type Str'Static = Text
- Bricks.Expression.Construction: Param'Builder :: (NonEmpty Param) -> Param'Builder
- Bricks.Expression.Construction: Str'1' :: Str'1 -> Str'1'
- Bricks.Expression.Construction: [unStr'1'] :: Str'1' -> Str'1
- Bricks.Expression.Construction: antiquote :: Expression -> Str'1'
- Bricks.Expression.Construction: apply :: Expression -> Expression -> Expression
- Bricks.Expression.Construction: binding :: Binding a b => a -> Expression -> b
- Bricks.Expression.Construction: buildParam :: Param'Builder -> Param
- Bricks.Expression.Construction: class Binding a b | b -> a
- Bricks.Expression.Construction: class IsInherit a
- Bricks.Expression.Construction: class IsParam a
- Bricks.Expression.Construction: def :: Expression -> DictPattern'1 -> DictPattern'1
- Bricks.Expression.Construction: dict :: [DictBinding] -> Expression
- Bricks.Expression.Construction: dot :: Expression -> Expression -> Expression
- Bricks.Expression.Construction: ellipsis :: Param'Builder
- Bricks.Expression.Construction: fromInherit :: IsInherit a => Inherit -> a
- Bricks.Expression.Construction: inherit :: IsInherit a => [Text] -> a
- Bricks.Expression.Construction: inherit'from :: IsInherit a => Expression -> [Text] -> a
- Bricks.Expression.Construction: instance Bricks.Expression.Construction.Binding Bricks.Expression.Expression Bricks.Expression.DictBinding
- Bricks.Expression.Construction: instance Bricks.Expression.Construction.Binding Data.Text.Internal.Text Bricks.Expression.LetBinding
- Bricks.Expression.Construction: instance Bricks.Expression.Construction.IsInherit Bricks.Expression.DictBinding
- Bricks.Expression.Construction: instance Bricks.Expression.Construction.IsInherit Bricks.Expression.LetBinding
- Bricks.Expression.Construction: instance Bricks.Expression.Construction.IsParam Bricks.Expression.Construction.Param'Builder
- Bricks.Expression.Construction: instance Bricks.Expression.Construction.IsParam Bricks.Expression.DictPattern'1
- Bricks.Expression.Construction: instance Data.Semigroup.Semigroup Bricks.Expression.Construction.Param'Builder
- Bricks.Expression.Construction: instance Data.String.IsString Bricks.Expression.Construction.Str'1'
- Bricks.Expression.Construction: lambda :: Param'Builder -> Expression -> Expression
- Bricks.Expression.Construction: let'in :: [LetBinding] -> Expression -> Expression
- Bricks.Expression.Construction: mergeDictPatterns :: DictPattern -> DictPattern -> DictPattern
- Bricks.Expression.Construction: mergeParams :: Param -> Param -> Param
- Bricks.Expression.Construction: newtype Param'Builder
- Bricks.Expression.Construction: newtype Str'1'
- Bricks.Expression.Construction: param :: IsParam a => Text -> a
- Bricks.Expression.Construction: paramBuilder :: Param -> Param'Builder
- Bricks.Expression.Construction: pattern :: [DictPattern'1] -> Param'Builder
- Bricks.Expression.Construction: rec'dict :: [DictBinding] -> Expression
- Bricks.Expression.Construction: str :: [Str'1'] -> Expression
- Bricks.Expression.Construction: var :: Text -> Expression
- Bricks.IndentedString: InStr :: Seq InStr'1 -> InStr
- Bricks.IndentedString: InStr'1 :: Natural -> Str'Dynamic -> InStr'1
- Bricks.IndentedString: [inStr'1'level] :: InStr'1 -> Natural
- Bricks.IndentedString: [inStr'1'str] :: InStr'1 -> Str'Dynamic
- Bricks.IndentedString: [inStr'toSeq] :: InStr -> Seq InStr'1
- Bricks.IndentedString: data InStr'1
- Bricks.IndentedString: inStr'1'empty :: InStr'1 -> Bool
- Bricks.IndentedString: inStr'1'modifyLevel :: (Natural -> Natural) -> (InStr'1 -> InStr'1)
- Bricks.IndentedString: inStr'1'nonEmpty :: InStr'1 -> Bool
- Bricks.IndentedString: inStr'dedent :: InStr -> InStr
- Bricks.IndentedString: inStr'join :: InStr -> Str'Dynamic
- Bricks.IndentedString: inStr'level :: InStr -> Natural
- Bricks.IndentedString: inStr'toList :: InStr -> [InStr'1]
- Bricks.IndentedString: inStr'trim :: InStr -> InStr
- Bricks.IndentedString: instance Data.Semigroup.Semigroup Bricks.IndentedString.InStr
- Bricks.IndentedString: instance GHC.Base.Monoid Bricks.IndentedString.InStr
- Bricks.IndentedString: instance GHC.Show.Show Bricks.IndentedString.InStr
- Bricks.IndentedString: instance GHC.Show.Show Bricks.IndentedString.InStr'1
- Bricks.IndentedString: newtype InStr
- Bricks.Internal.Prelude: (<&>) :: Functor f => f a -> (a -> b) -> f b
- Bricks.Internal.Prelude: infixl 1 <&>
- Bricks.Internal.Seq: (<|) :: a -> Seq a -> Seq a
- Bricks.Internal.Seq: (|>) :: Seq a -> a -> Seq a
- Bricks.Internal.Seq: concat :: Foldable f => f (Seq a) -> Seq a
- Bricks.Internal.Seq: data Seq a :: * -> *
- Bricks.Internal.Seq: dropWhileL :: (a -> Bool) -> Seq a -> Seq a
- Bricks.Internal.Seq: dropWhileR :: (a -> Bool) -> Seq a -> Seq a
- Bricks.Internal.Seq: empty :: Seq a
- Bricks.Internal.Seq: filter :: (a -> Bool) -> Seq a -> Seq a
- Bricks.Internal.Seq: fromList :: [a] -> Seq a
- Bricks.Internal.Seq: infixl 5 |>
- Bricks.Internal.Seq: infixr 5 <|
- Bricks.Internal.Seq: intersperse :: a -> Seq a -> Seq a
- Bricks.Internal.Seq: map :: (a -> b) -> Seq a -> Seq b
- Bricks.Internal.Seq: minimum :: Ord a => Seq a -> Maybe a
- Bricks.Internal.Seq: null :: Seq a -> Bool
- Bricks.Internal.Seq: singleton :: a -> Seq a
- Bricks.Internal.Seq: toList :: Foldable t => forall a. t a -> [a]
- Bricks.Internal.Text: all :: (Char -> Bool) -> Text -> Bool
- Bricks.Internal.Text: concat :: Foldable f => f Text -> Text
- Bricks.Internal.Text: data Text :: *
- Bricks.Internal.Text: intercalate :: Foldable f => Text -> f Text -> Text
- Bricks.Internal.Text: null :: Text -> Bool
- Bricks.Internal.Text: pack :: String -> Text
- Bricks.Internal.Text: replace :: Text -> Text -> Text -> Text
- Bricks.Internal.Text: replicate :: Int -> Text -> Text
- Bricks.Internal.Text: singleton :: Char -> Text
- Bricks.Internal.Text: unpack :: Text -> String
- Bricks.Internal.Text: unwords :: [Text] -> Text
- Bricks.Keyword: data Keyword
- Bricks.Keyword: keyword'in :: Keyword
- Bricks.Keyword: keyword'inherit :: Keyword
- Bricks.Keyword: keyword'inlineComment :: Keyword
- Bricks.Keyword: keyword'let :: Keyword
- Bricks.Keyword: keyword'rec :: Keyword
- Bricks.Keyword: keyword'with :: Keyword
- Bricks.Keyword: keywordString :: Keyword -> String
- Bricks.Keyword: keywordText :: Keyword -> Text
- Bricks.Keyword: keywords :: [Keyword]
- Bricks.Parsing: parse'antiquote :: Parser Str'Dynamic
- Bricks.Parsing: parse'comment :: Parser ()
- Bricks.Parsing: parse'comment'block :: Parser ()
- Bricks.Parsing: parse'comment'inline :: Parser ()
- Bricks.Parsing: parse'dict :: Parser Dict
- Bricks.Parsing: parse'dict'noRec :: Parser (Seq DictBinding)
- Bricks.Parsing: parse'dict'rec :: Parser (Seq DictBinding)
- Bricks.Parsing: parse'dictBinding :: Parser DictBinding
- Bricks.Parsing: parse'dictBinding'eq :: Parser DictBinding
- Bricks.Parsing: parse'dictBinding'inherit :: Parser DictBinding
- Bricks.Parsing: parse'dictPattern :: Parser DictPattern
- Bricks.Parsing: parse'dictPattern'start :: Parser ()
- Bricks.Parsing: parse'dot'rhs'chain :: Parser [Expression]
- Bricks.Parsing: parse'expression :: Parser Expression
- Bricks.Parsing: parse'expression'dictKey :: Parser Expression
- Bricks.Parsing: parse'expression'paren :: Parser Expression
- Bricks.Parsing: parse'expressionList :: Parser [Expression]
- Bricks.Parsing: parse'expressionList'1 :: Parser Expression
- Bricks.Parsing: parse'expressionList'1'noDot :: Parser Expression
- Bricks.Parsing: parse'inStr :: Parser InStr
- Bricks.Parsing: parse'inStr'1 :: Parser InStr'1
- Bricks.Parsing: parse'inherit :: Parser Inherit
- Bricks.Parsing: parse'keyword :: Keyword -> Parser ()
- Bricks.Parsing: parse'lambda :: Parser Lambda
- Bricks.Parsing: parse'let :: Parser Let
- Bricks.Parsing: parse'letBinding :: Parser LetBinding
- Bricks.Parsing: parse'letBinding'eq :: Parser LetBinding
- Bricks.Parsing: parse'letBinding'inherit :: Parser LetBinding
- Bricks.Parsing: parse'list :: Parser List
- Bricks.Parsing: parse'param :: Parser Param
- Bricks.Parsing: parse'param'noVar :: Parser Param
- Bricks.Parsing: parse'param'var :: Parser Param
- Bricks.Parsing: parse'spaces :: Parser ()
- Bricks.Parsing: parse'str'escape'normalQ :: Parser Text
- Bricks.Parsing: parse'str'within'normalQ :: Parser Text
- Bricks.Parsing: parse'strDynamic'indentedQ :: Parser Str'Dynamic
- Bricks.Parsing: parse'strDynamic'normalQ :: Parser Str'Dynamic
- Bricks.Parsing: parse'strDynamic'quoted :: Parser Str'Dynamic
- Bricks.Parsing: parse'strStatic :: Parser Str'Static
- Bricks.Parsing: parse'strStatic'quoted :: Parser Str'Static
- Bricks.Parsing: parse'strStatic'unquoted :: Parser Str'Static
- Bricks.Parsing: parse'strUnquoted :: Parser Str'Unquoted
- Bricks.Parsing: parse'with :: Parser With
- Bricks.Rendering: render'apply :: Render Apply
- Bricks.Rendering: render'dict :: Render Dict
- Bricks.Rendering: render'dictBinding :: Render DictBinding
- Bricks.Rendering: render'dictPattern :: Render DictPattern
- Bricks.Rendering: render'dictPattern'1 :: Render DictPattern'1
- Bricks.Rendering: render'dot :: Render Dot
- Bricks.Rendering: render'expression :: Render Expression
- Bricks.Rendering: render'expression'applyLeftContext :: Render Expression
- Bricks.Rendering: render'expression'applyRightContext :: Render Expression
- Bricks.Rendering: render'expression'dictKey :: Render Expression
- Bricks.Rendering: render'expression'dotLeftContext :: Render Expression
- Bricks.Rendering: render'expression'inParens :: Render Expression
- Bricks.Rendering: render'expression'listContext :: Render Expression
- Bricks.Rendering: render'inStr'1 :: Render InStr'1
- Bricks.Rendering: render'inherit :: Render Inherit
- Bricks.Rendering: render'lambda :: Render Lambda
- Bricks.Rendering: render'let :: Render Let
- Bricks.Rendering: render'letBinding :: Render LetBinding
- Bricks.Rendering: render'list :: Render List
- Bricks.Rendering: render'param :: Render Param
- Bricks.Rendering: render'strDynamic'quoted :: Render Str'Dynamic
- Bricks.Rendering: render'strDynamic'unquotedIfPossible :: Render Str'Dynamic
- Bricks.Rendering: render'strStatic'quoted :: Render Str'Static
- Bricks.Rendering: render'strStatic'unquotedIfPossible :: Render Str'Static
- Bricks.Rendering: render'strUnquoted :: Render Str'Unquoted
- Bricks.Rendering: render'with :: Render With
- Bricks.Rendering: str'escape :: Text -> Text
- Bricks.Rendering: type Render a = a -> Text
- Bricks.UnquotedString: Str'Unquoted'Unsafe :: Text -> Str'Unquoted
- Bricks.UnquotedString: [str'unquotedToStatic] :: Str'Unquoted -> Text
- Bricks.UnquotedString: char'canRenderUnquoted :: Char -> Bool
- Bricks.UnquotedString: instance GHC.Show.Show Bricks.UnquotedString.Str'Unquoted
- Bricks.UnquotedString: newtype Str'Unquoted
- Bricks.UnquotedString: str'canRenderUnquoted :: Text -> Bool
- Bricks.UnquotedString: str'tryUnquoted :: Text -> Maybe Str'Unquoted
- Bricks.UnquotedString: str'unquoted'orThrow :: Text -> Str'Unquoted
+ Bricks: DictBinding'Inherit'Dict :: Expression -> Seq Str'Static -> DictBinding
+ Bricks: DictBinding'Inherit'Var :: Seq Var -> DictBinding
+ Bricks: Expr'Str'Indented :: InStr -> Expression
+ Bricks: RenderContext :: Natural -> Natural -> Bool -> RenderContext
+ Bricks: Str'Static :: Text -> Maybe SourceRange -> Str'Static
+ Bricks: Var :: UnquotedString -> Maybe SourceRange -> Var
+ Bricks: [apply'source] :: Apply -> Maybe SourceRange
+ Bricks: [dict'source] :: Dict -> Maybe SourceRange
+ Bricks: [dot'source] :: Dot -> Maybe SourceRange
+ Bricks: [inStr'1'indentSource] :: InStr'1 -> Maybe SourceRange
+ Bricks: [inStr'1'lineBreak] :: InStr'1 -> Maybe Str'Static
+ Bricks: [inStr'source] :: InStr -> Maybe SourceRange
+ Bricks: [lambda'source] :: Lambda -> Maybe SourceRange
+ Bricks: [let'source] :: Let -> Maybe SourceRange
+ Bricks: [list'expressions] :: List -> Seq Expression
+ Bricks: [list'source] :: List -> Maybe SourceRange
+ Bricks: [renderContext'indentStart] :: RenderContext -> Natural
+ Bricks: [renderContext'indentStep] :: RenderContext -> Natural
+ Bricks: [renderContext'lineBreaks] :: RenderContext -> Bool
+ Bricks: [str'static'source] :: Str'Static -> Maybe SourceRange
+ Bricks: [str'static'text] :: Str'Static -> Text
+ Bricks: [strDynamic'source] :: Str'Dynamic -> Maybe SourceRange
+ Bricks: [var'source] :: Var -> Maybe SourceRange
+ Bricks: [var'str] :: Var -> UnquotedString
+ Bricks: apply'discardSource :: Apply -> Apply
+ Bricks: char'canBeUnquoted :: Char -> Bool
+ Bricks: data InStr :: *
+ Bricks: data List :: *
+ Bricks: data RenderContext :: *
+ Bricks: data Str'Dynamic :: *
+ Bricks: data Str'Static :: *
+ Bricks: data UnquotedString :: *
+ Bricks: data Var :: *
+ Bricks: dict'discardSource :: Dict -> Dict
+ Bricks: dictBinding'discardSource :: DictBinding -> DictBinding
+ Bricks: dictPattern'1'discardSource :: DictPattern'1 -> DictPattern'1
+ Bricks: dictPattern'discardSource :: DictPattern -> DictPattern
+ Bricks: dot'discardSource :: Dot -> Dot
+ Bricks: expression'discardSource :: Expression -> Expression
+ Bricks: expression'source :: Expression -> Maybe SourceRange
+ Bricks: inStr'1'discardSource :: InStr'1 -> InStr'1
+ Bricks: inStr'1'toStrParts :: InStr'1 -> Seq Str'1
+ Bricks: inStr'discardSource :: InStr -> InStr
+ Bricks: inStr'to'strDynamic :: InStr -> Str'Dynamic
+ Bricks: lambda'discardSource :: Lambda -> Lambda
+ Bricks: let'discardSource :: Let -> Let
+ Bricks: letBinding'discardSource :: LetBinding -> LetBinding
+ Bricks: list'discardSource :: List -> List
+ Bricks: param'discardSource :: Param -> Param
+ Bricks: parse'expression'antiquote :: Parser Expression
+ Bricks: parse'str'dynamic :: Parser Str'Dynamic
+ Bricks: parse'var :: Parser Var
+ Bricks: render'str'1 :: Render Str'1
+ Bricks: render'str'indented :: Render InStr
+ Bricks: render'str'indented'1 :: Render InStr'1
+ Bricks: render'var :: Render Var
+ Bricks: renderContext'default :: RenderContext
+ Bricks: renderContext'terse :: RenderContext
+ Bricks: str'1'discardSource :: Str'1 -> Str'1
+ Bricks: str'dynamic'append :: Str'Dynamic -> Str'Dynamic -> Str'Dynamic
+ Bricks: str'dynamic'discardSource :: Str'Dynamic -> Str'Dynamic
+ Bricks: str'dynamic'normalize :: Str'Dynamic -> Str'Dynamic
+ Bricks: str'dynamic'to'static :: Str'Dynamic -> Maybe Str'Static
+ Bricks: str'static'append :: Str'Static -> Str'Static -> Str'Static
+ Bricks: str'static'discardSource :: Str'Static -> Str'Static
+ Bricks: str'static'to'dynamic :: Str'Static -> Str'Dynamic
+ Bricks: text'canBeUnquoted :: Text -> Bool
+ Bricks: unquotedString'orThrow :: Text -> UnquotedString
+ Bricks: unquotedString'text :: UnquotedString -> Text
+ Bricks: unquotedString'try :: Text -> Maybe UnquotedString
+ Bricks: var'discardSource :: Var -> Var
+ Bricks: var'text :: Var -> Text
+ Bricks: var'to'str'dynamic :: Var -> Str'Dynamic
+ Bricks: var'to'str'static :: Var -> Str'Static
+ Bricks.BuiltinFunctions: assert'type :: Typeable a => Type a -> Term
+ Bricks.BuiltinFunctions: cast'data :: (MonadEval m, Typeable a) => Type a -> Term -> m a
+ Bricks.BuiltinFunctions: fn'and :: Term
+ Bricks.BuiltinFunctions: fn'comp :: Term
+ Bricks.BuiltinFunctions: fn'const :: Term
+ Bricks.BuiltinFunctions: fn'dict'disallowExtraKeys :: Set Text -> Term
+ Bricks.BuiltinFunctions: fn'dict'lookup :: Term
+ Bricks.BuiltinFunctions: fn'dict'merge'preferLeft :: Term
+ Bricks.BuiltinFunctions: fn'dict'merge'preferRight :: Term
+ Bricks.BuiltinFunctions: fn'flip :: Term
+ Bricks.BuiltinFunctions: fn'id :: Term
+ Bricks.BuiltinFunctions: fn'int'add :: Term
+ Bricks.BuiltinFunctions: fn'int'constructor :: Term
+ Bricks.BuiltinFunctions: fn'or :: Term
+ Bricks.BuiltinFunctions: fn'pure'parametric'arity1 :: (Term -> Term) -> Term
+ Bricks.BuiltinFunctions: fn'pure'parametric'arity2 :: (Term -> Term -> Term) -> Term
+ Bricks.BuiltinFunctions: fn'pure'parametric'arity3 :: (Term -> Term -> Term -> Term) -> Term
+ Bricks.BuiltinFunctions: fn'string'append :: Term
+ Bricks.BuiltinFunctions: req :: forall a b m. (MonadEval m, Typeable a) => ((a, Term) -> b) -> Type a -> Term -> m b
+ Bricks.BuiltinFunctions: standard'library :: Term
+ Bricks.BuiltinFunctions: term'data :: forall a. Typeable a => Type a -> a -> Term
+ Bricks.Evaluation: Eval :: ExceptT Bottom IO a -> Eval a
+ Bricks.Evaluation: [unEval] :: Eval a -> ExceptT Bottom IO a
+ Bricks.Evaluation: does'termPattern'bind :: Text -> TermPattern -> Bool
+ Bricks.Evaluation: instance Bricks.Term.MonadEval Bricks.Evaluation.Eval
+ Bricks.Evaluation: instance Control.Monad.Error.Class.MonadError Bricks.Term.Bottom Bricks.Evaluation.Eval
+ Bricks.Evaluation: instance Control.Monad.IO.Class.MonadIO Bricks.Evaluation.Eval
+ Bricks.Evaluation: instance GHC.Base.Applicative Bricks.Evaluation.Eval
+ Bricks.Evaluation: instance GHC.Base.Functor Bricks.Evaluation.Eval
+ Bricks.Evaluation: instance GHC.Base.Monad Bricks.Evaluation.Eval
+ Bricks.Evaluation: instantiate'many :: forall m. MonadEval m => Map Text Term -> Term -> m Term
+ Bricks.Evaluation: instantiate'one :: forall m. MonadEval m => Text -> Term -> Term -> m Term
+ Bricks.Evaluation: newtype Eval a
+ Bricks.Evaluation: reduce'to'type :: Typeable a => Type a -> Term -> IO (Either Bottom a)
+ Bricks.Evaluation: reduce'to'type'or'throw :: (HasCallStack, Typeable a) => Type a -> Term -> IO a
+ Bricks.ExpressionToTerm: apply'to'term :: Apply -> IO Term
+ Bricks.ExpressionToTerm: dict'to'term :: Dict -> IO Term
+ Bricks.ExpressionToTerm: dictPattern'defaults :: DictPattern -> IO (Map Text Term)
+ Bricks.ExpressionToTerm: dictPattern'names :: DictPattern -> Set Text
+ Bricks.ExpressionToTerm: dot'to'term :: Dot -> IO Term
+ Bricks.ExpressionToTerm: expression'to'term :: Expression -> IO Term
+ Bricks.ExpressionToTerm: lambda'to'term :: Lambda -> IO Term
+ Bricks.ExpressionToTerm: lambda'to'term'both :: Var -> DictPattern -> Term -> IO Term
+ Bricks.ExpressionToTerm: lambda'to'term'dictPattern :: DictPattern -> Term -> IO Term
+ Bricks.ExpressionToTerm: lambda'to'term'simple :: Var -> Term -> IO Term
+ Bricks.ExpressionToTerm: let'to'term :: Let -> IO Term
+ Bricks.ExpressionToTerm: letBinding'to'term :: LetBinding -> IO [(Text, Term)]
+ Bricks.ExpressionToTerm: list'to'term :: List -> IO Term
+ Bricks.ExpressionToTerm: str'1'to'term :: Str'1 -> IO Term
+ Bricks.ExpressionToTerm: str'to'term :: Str'Dynamic -> IO Term
+ Bricks.ExpressionToTerm: var'to'term :: Var -> IO Term
+ Bricks.Internal.Monad: ExceptT :: m Either e a -> ExceptT e a
+ Bricks.Internal.Monad: ReaderT :: (r -> m a) -> ReaderT k r
+ Bricks.Internal.Monad: [runReaderT] :: ReaderT k r -> r -> m a
+ Bricks.Internal.Monad: catchError :: MonadError e m => m a -> (e -> m a) -> m a
+ Bricks.Internal.Monad: class Monad m => MonadError e (m :: * -> *) | m -> e
+ Bricks.Internal.Monad: class Monad m => MonadIO (m :: * -> *)
+ Bricks.Internal.Monad: liftIO :: MonadIO m => IO a -> m a
+ Bricks.Internal.Monad: newtype ReaderT k r (m :: k -> *) (a :: k) :: forall k. () => * -> (k -> *) -> k -> *
+ Bricks.Internal.Monad: newtype ExceptT e (m :: * -> *) a :: * -> (* -> *) -> * -> *
+ Bricks.Internal.Monad: runExceptT :: () => ExceptT e m a -> m Either e a
+ Bricks.Internal.Monad: throwError :: MonadError e m => e -> m a
+ Bricks.Prelude: bricks'eval :: (HasCallStack, Typeable a) => Type a -> Text -> IO a
+ Bricks.Prelude: bricks'eval'stdlib :: (HasCallStack, Typeable a) => Type a -> Text -> IO a
+ Bricks.Term: (/@@\) :: Term -> (Term, Term) -> Term
+ Bricks.Term: (/@\) :: Term -> Term -> Term
+ Bricks.Term: (|->) :: TermPattern -> Term -> Term
+ Bricks.Term: Bottom :: Text -> Bottom
+ Bricks.Term: Term'Apply :: Term -> Term -> Term
+ Bricks.Term: Term'Data :: Text -> Dynamic -> Term
+ Bricks.Term: Term'Dict :: (Seq (Term, Term)) -> Term
+ Bricks.Term: Term'Dict'ReducedKeys :: (Map Text Term) -> Term
+ Bricks.Term: Term'Function :: Function -> Term
+ Bricks.Term: Term'Lambda :: TermPattern -> Term -> Term
+ Bricks.Term: Term'LetRec :: (Map Text Term) -> Term -> Term
+ Bricks.Term: Term'List :: (Seq Term) -> Term
+ Bricks.Term: Term'Pointer :: TermPtr -> Term
+ Bricks.Term: Term'Var :: Text -> Term
+ Bricks.Term: TermPattern'Dict :: (Set Text) -> TermPattern
+ Bricks.Term: TermPattern'Simple :: Text -> TermPattern
+ Bricks.Term: bottom :: MonadError Bottom m => Bottom -> m a
+ Bricks.Term: class (Monad m, MonadIO m, MonadError Bottom m) => MonadEval m
+ Bricks.Term: create'pointer :: MonadIO m => Term -> m Term
+ Bricks.Term: data Bottom
+ Bricks.Term: data Term
+ Bricks.Term: data TermPattern
+ Bricks.Term: dereference :: MonadIO m => Term -> m Term
+ Bricks.Term: displayBottom :: Bottom -> Text
+ Bricks.Term: infixl 9 |->
+ Bricks.Term: newTermPtr :: MonadIO m => Term -> m Term
+ Bricks.Term: readTermPtr :: MonadIO m => TermPtr -> m Term
+ Bricks.Term: reduce'dict'keys :: MonadEval m => Term -> m (Map Text Term)
+ Bricks.Term: reduce'term :: MonadEval m => Term -> m Term
+ Bricks.Term: type Function = forall m. MonadEval m => Term -> m Term
+ Bricks.Term: type TermPtr = IORef Term
+ Bricks.Term: writeTermPtr :: MonadIO m => TermPtr -> Term -> m ()
+ Bricks.Type: Type :: Text -> Type a
+ Bricks.Type: [type'name] :: Type a -> Text
+ Bricks.Type: data Type a
+ Bricks.Type: termTypeName :: MonadIO m => Term -> m Text
+ Bricks.Type: type'boolean :: Type Bool
+ Bricks.Type: type'integer :: Type Integer
+ Bricks.Type: type'string :: Type Text
- Bricks: Apply :: Expression -> Expression -> Apply
+ Bricks: Apply :: Expression -> Expression -> Maybe SourceRange -> Apply
- Bricks: Dict :: Bool -> Seq DictBinding -> Dict
+ Bricks: Dict :: Bool -> Seq DictBinding -> Maybe SourceRange -> Dict
- Bricks: DictPattern'1 :: Str'Unquoted -> Maybe Expression -> DictPattern'1
+ Bricks: DictPattern'1 :: Var -> Maybe Expression -> DictPattern'1
- Bricks: Dot :: Expression -> Expression -> Dot
+ Bricks: Dot :: Expression -> Expression -> Maybe SourceRange -> Dot
- Bricks: Expr'Var :: Str'Unquoted -> Expression
+ Bricks: Expr'Var :: Var -> Expression
- Bricks: InStr :: Seq InStr'1 -> InStr
+ Bricks: InStr :: Seq InStr'1 -> Maybe SourceRange -> InStr
- Bricks: InStr'1 :: Natural -> Str'Dynamic -> InStr'1
+ Bricks: InStr'1 :: Natural -> Maybe SourceRange -> Seq Str'1 -> Maybe Str'Static -> InStr'1
- Bricks: Lambda :: Param -> Expression -> Lambda
+ Bricks: Lambda :: Param -> Expression -> Maybe SourceRange -> Lambda
- Bricks: Let :: Seq LetBinding -> Expression -> Let
+ Bricks: Let :: Seq LetBinding -> Expression -> Maybe SourceRange -> Let
- Bricks: LetBinding'Eq :: Str'Static -> Expression -> LetBinding
+ Bricks: LetBinding'Eq :: Var -> Expression -> LetBinding
- Bricks: LetBinding'Inherit :: Inherit -> LetBinding
+ Bricks: LetBinding'Inherit :: Expression -> Seq Var -> LetBinding
- Bricks: List :: (Seq Expression) -> List
+ Bricks: List :: Seq Expression -> Maybe SourceRange -> List
- Bricks: Param'Both :: Str'Unquoted -> DictPattern -> Param
+ Bricks: Param'Both :: Var -> DictPattern -> Param
- Bricks: Param'Name :: Str'Unquoted -> Param
+ Bricks: Param'Name :: Var -> Param
- Bricks: Str'Dynamic :: Seq Str'1 -> Str'Dynamic
+ Bricks: Str'Dynamic :: Seq Str'1 -> Maybe SourceRange -> Str'Dynamic
- Bricks: [dictPattern'1'name] :: DictPattern'1 -> Str'Unquoted
+ Bricks: [dictPattern'1'name] :: DictPattern'1 -> Var
- Bricks: [inStr'1'str] :: InStr'1 -> Str'Dynamic
+ Bricks: [inStr'1'str] :: InStr'1 -> Seq Str'1
- Bricks: data Apply
+ Bricks: data Apply :: *
- Bricks: data Dict
+ Bricks: data Dict :: *
- Bricks: data DictBinding
+ Bricks: data DictBinding :: *
- Bricks: data DictPattern
+ Bricks: data DictPattern :: *
- Bricks: data DictPattern'1
+ Bricks: data DictPattern'1 :: *
- Bricks: data Dot
+ Bricks: data Dot :: *
- Bricks: data Expression
+ Bricks: data Expression :: *
- Bricks: data InStr'1
+ Bricks: data InStr'1 :: *
- Bricks: data Keyword
+ Bricks: data Keyword :: *
- Bricks: data Lambda
+ Bricks: data Lambda :: *
- Bricks: data Let
+ Bricks: data Let :: *
- Bricks: data LetBinding
+ Bricks: data LetBinding :: *
- Bricks: data Param
+ Bricks: data Param :: *
- Bricks: data Str'1
+ Bricks: data Str'1 :: *
- Bricks: parse'dict'noRec :: Parser (Seq DictBinding)
+ Bricks: parse'dict'noRec :: Parser Dict
- Bricks: parse'dict'rec :: Parser (Seq DictBinding)
+ Bricks: parse'dict'rec :: Parser Dict
- Bricks: parse'str'within'normalQ :: Parser Text
+ Bricks: parse'str'within'normalQ :: Parser Str'Static
- Bricks: parse'strUnquoted :: Parser Str'Unquoted
+ Bricks: parse'strUnquoted :: Parser (UnquotedString, SourceRange)
- Bricks: type Render a = a -> Text
+ Bricks: type Render a = RenderContext -> a -> Text

Files

bricks.cabal view
@@ -1,14 +1,16 @@--- This file has been generated from package.yaml by hpack version 0.18.1.+-- This file has been generated from package.yaml by hpack version 0.20.0. -- -- see: https://github.com/sol/hpack+--+-- hash: edb5a78c7015968892083343d6c79fc374ae3408d793f8921575ed36fe3a3803  name:           bricks-version:        0.0.0.2+version:        0.0.0.4 synopsis:       Bricks is a lazy functional language based on Nix.  description:    Bricks is a lazy functional language based on Nix.                 This package provides parsing, rendering, and-                evaluation (forthcoming) for the Bricks language.+                evaluation for the Bricks language. category:       Language homepage:       https://github.com/chris-martin/bricks#readme bug-reports:    https://github.com/chris-martin/bricks/issues@@ -28,22 +30,25 @@       src   ghc-options: -Wall   build-depends:-      base       >= 4.9   && < 4.10-    , containers >= 0.5.7 && < 0.6-    , parsec     >= 3.1.6 && < 3.2-    , text       >= 1.2.2 && < 1.3+      base >=4.9 && <4.11+    , bricks-internal+    , bricks-parsec+    , bricks-rendering+    , bricks-syntax+    , containers >=0.5.7 && <0.6+    , mtl+    , parsec >=3.1.6 && <3.2+    , text >=1.2.2 && <1.3+    , transformers   exposed-modules:       Bricks-      Bricks.Expression-      Bricks.Expression.Construction-      Bricks.IndentedString-      Bricks.Keyword-      Bricks.Parsing-      Bricks.Rendering-      Bricks.UnquotedString-      Bricks.Internal.Prelude-      Bricks.Internal.Seq-      Bricks.Internal.Text+      Bricks.BuiltinFunctions+      Bricks.Evaluation+      Bricks.ExpressionToTerm+      Bricks.Prelude+      Bricks.Term+      Bricks.Type+      Bricks.Internal.Monad   other-modules:       Paths_bricks   default-language: Haskell2010@@ -55,54 +60,42 @@       test   ghc-options: -Wall -threaded   build-depends:-      base       >= 4.9   && < 4.10-    , containers >= 0.5.7 && < 0.6-    , parsec     >= 3.1.6 && < 3.2-    , text       >= 1.2.2 && < 1.3-    , bricks-    , base    >= 4.9  && < 4.10-    , doctest >= 0.11 && < 0.14-  other-modules:-      Bricks.Test.Hedgehog-      Bricks.Test.QQ-  default-language: Haskell2010--test-suite parsing-  type: exitcode-stdio-1.0-  main-is: parsing.hs-  hs-source-dirs:-      test-  ghc-options: -Wall -threaded-  build-depends:-      base       >= 4.9   && < 4.10-    , containers >= 0.5.7 && < 0.6-    , parsec     >= 3.1.6 && < 3.2-    , text       >= 1.2.2 && < 1.3-    , bricks-    , base             >= 4.9 && < 4.10-    , hedgehog         >= 0.5 && < 0.6-    , template-haskell >= 2.2 && < 2.12+      base >=4.9 && <4.11+    , bricks-internal+    , bricks-parsec+    , bricks-rendering+    , bricks-syntax+    , containers >=0.5.7 && <0.6+    , doctest >=0.11 && <0.14+    , mtl+    , parsec >=3.1.6 && <3.2+    , text >=1.2.2 && <1.3+    , transformers   other-modules:-      Bricks.Test.Hedgehog-      Bricks.Test.QQ+      Paths_bricks   default-language: Haskell2010 -test-suite rendering+test-suite evaluation   type: exitcode-stdio-1.0-  main-is: rendering.hs+  main-is: evaluation.hs   hs-source-dirs:       test   ghc-options: -Wall -threaded   build-depends:-      base       >= 4.9   && < 4.10-    , containers >= 0.5.7 && < 0.6-    , parsec     >= 3.1.6 && < 3.2-    , text       >= 1.2.2 && < 1.3+      base >=4.9 && <4.11     , bricks-    , base             >= 4.9 && < 4.10-    , hedgehog         >= 0.5 && < 0.6-    , template-haskell >= 2.2 && < 2.12+    , bricks-internal+    , bricks-internal-test+    , bricks-parsec+    , bricks-rendering+    , bricks-syntax+    , containers >=0.5.7 && <0.6+    , hedgehog >=0.5 && <0.6+    , mtl+    , parsec >=3.1.6 && <3.2+    , template-haskell >=2.2 && <2.13+    , text >=1.2.2 && <1.3+    , transformers   other-modules:-      Bricks.Test.Hedgehog-      Bricks.Test.QQ+      Paths_bricks   default-language: Haskell2010
src/Bricks.hs view
@@ -1,41 +1,42 @@-{- | __Bricks__ is a lazy functional language that strongly resembles Nix.--Notable differences from Nix:+{- | -- No built-in null, integer, or boolean types-- No @builtins@ and no infix operators (@+@, @-@, @//@)-- No URI literals-- No escape sequences in indented strings (@''@...@''@)-- The inline comment keyword is @--@ rather than @#@-- There are block comments in the form @{\-@...@-\}@-- The concept of "set" is referred to as "dict" (this is not actually a language-  difference, we just use a different word to talk about the same concept)+__Bricks__ is a lazy functional language that resembles+<https://nixos.org/nix/manual/#ch-expression-language Nix>. -The following modules are re-exported from this module in their entireties:+This module serves as fairly exhaustive overview of the entire package, and+should usually serve as your go-to place to start when reading the Bricks+documentation if you want an /in-depth/ understanding of how everything works.+It is a fairly /large/ module, and probably ought to be used via a qualified+import. -- "Bricks.Expression" - Defines most of the types, notably 'Expression'-- "Bricks.IndentedString" - Deals with the whitespace cleanup performed when-  parsing indented strings (@''@...@''@)-- "Bricks.Keyword" - Enumerates the language's keywords-- "Bricks.Parsing" - Defines all of the Parsec parsers for parsing Bricks code-  into 'Expression's-- "Bricks.Rendering" - Defines all of the renderers for turning 'Expression's-  into Bricks code-- "Bricks.UnquotedString" - Defines the rules for what strings are allowed to-  appear unquoted in Bricks code+> import qualified Bricks -Other modules:+If you just want to use Bricks for common cases, look at the simple API in+"Bricks.Prelude" instead. That module is much smaller and is designed to be+imported unqualified. -- "Bricks.Expression.Construction" - Functions for constructing 'Expression's-  in a way that matches their 'Show' implementations.+> import Bricks.Prelude  -} module Bricks    (   -------------------------------------------------++  -- * Module overview+  -- $module-overview++  -------------------------------------------------++  -- * Differences from Nix+  -- $differences-from-nix++  -------------------------------------------------+   -- * Expressions     Expression (..)+  , expression'source+  , expression'discardSource   -- ** Rendering expressions   , render'expression   , render'expression'listContext@@ -47,18 +48,35 @@   -- ** Parsing expressions   , parse'expression   , parse'expression'paren+  , parse'expression'antiquote   , parse'expression'dictKey   -- ** Parsing lists of expressions   , parse'expressionList   , parse'expressionList'1   , parse'expressionList'1'noDot+   -------------------------------------------------++  -- * Variables+  , Var (..)+  , var'text+  , render'var+  , parse'var+  , var'to'str'static+  , var'to'str'dynamic+  , var'discardSource++  -------------------------------------------------+   -- * Strings   , str'escape   , parse'str'within'normalQ   , parse'str'escape'normalQ   -- ** Static strings-  , Str'Static+  , Str'Static (..)+  , str'static'append+  , str'static'discardSource+  , str'static'to'dynamic   , render'strStatic'unquotedIfPossible   , render'strStatic'quoted   , parse'strStatic@@ -67,137 +85,452 @@   -- ** Dynamic strings   , Str'Dynamic (..)   , Str'1 (..)-  , strDynamic'toList-  , strDynamic'fromList-  , strDynamic'singleton+  , str'1'discardSource+  , str'dynamic'append+  , str'dynamic'normalize+  , str'dynamic'discardSource+  , str'dynamic'to'static   , render'strDynamic'unquotedIfPossible   , render'strDynamic'quoted-  , parse'strDynamic'quoted-  , parse'strDynamic'normalQ-  , parse'strDynamic'indentedQ+  , render'str'1+  , parse'str'dynamic   -- ** Unquoted strings-  , Str'Unquoted (..)-  , str'tryUnquoted-  , str'unquoted'orThrow-  , str'canRenderUnquoted-  , char'canRenderUnquoted-  , render'strUnquoted+  , UnquotedString+  , unquotedString'try+  , unquotedString'orThrow+  , unquotedString'text+  , text'canBeUnquoted+  , char'canBeUnquoted   , parse'strUnquoted-  -- ** String conversions-  , str'dynamicToStatic-  , str'staticToDynamic-  , str'unquotedToDynamic   -- ** Indented strings   , InStr (..)+  , InStr'1 (..)+  , inStr'1'toStrParts   , inStr'toList-  , inStr'join+  , inStr'to'strDynamic   , inStr'level   , inStr'dedent   , inStr'trim-  , render'inStr'1+  , inStr'discardSource+  , inStr'1'discardSource+  , render'str'indented+  , render'str'indented'1   , parse'inStr   , parse'inStr'1-  -- ** Single line of an indented string-  , InStr'1 (..)-  , inStr'1'nonEmpty-  , inStr'1'empty-  , inStr'1'modifyLevel+   -------------------------------------------------+   -- * Lists   , List (..)+  , list'discardSource   , render'list   , parse'list+   -------------------------------------------------+   -- * Dicts   , Dict (..)   , keyword'rec+  , dict'discardSource   , render'dict   , parse'dict   , parse'dict'rec   , parse'dict'noRec   -- ** Dict bindings   , DictBinding (..)+  , dictBinding'discardSource   , render'dictBinding   , parse'dictBinding   , parse'dictBinding'inherit   , parse'dictBinding'eq   -- ** Dict lookup (dot)   , Dot (..)+  , dot'discardSource   , expression'applyDots   , render'dot   , parse'dot'rhs'chain+   -------------------------------------------------+   -- * Functions   -- ** Lambdas   , Lambda (..)+  , lambda'discardSource   , render'lambda   , parse'lambda   -- ** Function parameters   , Param (..)+  , param'discardSource   , render'param   , parse'param   , parse'param'var   , parse'param'noVar   -- ** Dict patterns   , DictPattern (..)+  , dictPattern'discardSource   , DictPattern'1 (..)+  , dictPattern'1'discardSource   , render'dictPattern   , render'dictPattern'1   , parse'dictPattern   , parse'dictPattern'start   -- ** Function application   , Apply (..)+  , apply'discardSource   , expression'applyArgs   , render'apply+   -------------------------------------------------+   -- * @let@   , Let (..)+  , let'discardSource   , keyword'let   , keyword'in   , render'let   , parse'let   -- ** @let@ bindings   , LetBinding (..)+  , letBinding'discardSource   , render'letBinding   , parse'letBinding   , parse'letBinding'eq   , parse'letBinding'inherit-  --------------------------------------------------  -- * @with@-  , With (..)-  , keyword'with-  , render'with-  , parse'with+   -------------------------------------------------+   -- * @inherit@-  , Inherit (..)   , keyword'inherit-  , render'inherit-  , parse'inherit+   -------------------------------------------------+   -- * Keywords   , Keyword   , keywords   , keywordString   , keywordText   , parse'keyword+   -------------------------------------------------+   -- * Comments and whitespace   , keyword'inlineComment   , parse'spaces   , parse'comment   , parse'comment'inline   , parse'comment'block+   --------------------------------------------------  -- * Miscellanea++  -- * Rendering   , Render-  , parse'antiquote+  , RenderContext (..)+  , renderContext'default+  , renderContext'terse+   -------------------------------------------------+   ) where  import Bricks.Expression-import Bricks.IndentedString import Bricks.Keyword-import Bricks.Parsing+import Bricks.Parsec import Bricks.Rendering import Bricks.UnquotedString++{- $module-overview++== Modules that are re-exported from "Bricks"++The following modules are re-exported from this top-level "Bricks" module in+their entireties.++Modules related to syntax:++  - "Bricks.Keyword" - Enumerates the language's keywords+  - "Bricks.UnquotedString" - Defines the rules for what strings are allowed to+    appear unquoted in Bricks code+  - "Bricks.Expression" - Defines most of the types related to the AST, notably+    'Expression'+  - "Bricks.Parsec" - Defines all of the Parsec parsers for parsing Bricks code+    into 'Expression's+  - "Bricks.Rendering" - Defines all of the renderers for turning 'Expression's+    into Bricks code++Modules related to evaluation:++  - "Bricks.Term" - ...+  - "Bricks.ExpressionToTerm" - ...+  - "Bricks.Evaluation" - ...++== Modules that are /not/ re-exported from "Bricks"++  - "Bricks.Expression.Construction" - Functions for constructing 'Expression's+    in a way that matches their 'Show' implementations.++-}++{- $differences-from-nix++Bricks is heavily based on+<https://nixos.org/nix/manual/#ch-expression-language the Nix language>,+but there are a number of significant differences. Most of the differences+involve the /removal/ of some feature for the sake of simplicity in both+implementation and use.++This list is not comprehensive.++== Top-level Bricks expressions may contain no free variables++There are a number of cases where variables are allowed to appear free in a+top-level Nix expression: @true@, @false@, @null@, @builtins@, and anything+within the body of a @with@ expression (sort of - we'll elaborate on that+complicated issue further below). None of those cases are present in Bricks:+__/All variables must be explicitly bound/__.++Throughout this section we will more thoroughly address specific Nix built-in+variables and how to translate Nix expressions that use them into equivalent+Bricks code.++== Bricks has no built-in Boolean values++Nix has built-in @true@ and @false@ variables, a handful of operators on them+(@&&@, @||@, @!@), and an @if@-@then@-@else@ construct.++> nix-repl> true && false+> false++> nix-repl> true || false+> true++> nix-repl> !true+> false++> nix-repl> if true then "a" else "b"+> "a"++None of these features are present in Bricks.++__todo:__ Show how we can use the standard library instead.++== Bricks has no built-in @null@++__todo:__ Show how we can use the standard library instead.++== Bricks has no integer literals++__todo:__ Show how we can use the standard library instead.++== Bricks has no built-in @builtins@++__todo:__ Show how we can use the standard library instead.++== Bricks has no infix operators (@+@, @-@, @//@, et cetera)++__todo:__ Show how we can use the standard library instead.++== Bricks has no @with@ expression++The Nix language has a @with@ construct which introduces the contents of a dict+into the lexical scope.++The simple reason for omitting this feature is that it can easily lead to code+that is difficult to trace. When @with@ expressions are nested, it is often+unclear where a variable has come into scope from.++The more subtle reason not to include the @with@ construct is that it introduces+a significant departure from the lambda calulus. Consider the expression @(with+d; x)@. In this expression, is @x@ free or bound? It is neither; it exists in+some state of uncertainty where it /may or may not be/ bound, depending on the+value of @d@.++This has a practical consequence: When you use a @with@ expression, you+sacrifice referential transparency. Consider the following Nix expression:++> nix-repl> let v = (with { x = "a"; }; x); in (x: v) "b"+> "a"++If we attempt to reduce this expression by replacing @v@ with its definition,++> nix-repl> (x: (with { x = "a"; }; x)) "b"+> "b"++then it no longer evaluates to the same value. We find this unacceptable.+Bricks avoids the problem by simply not implementing this feature.++== Bricks allows a list on the right-hand side of the @.@ operator++The following example is syntactically valid Nix code, but it fails to evaluate:++> nix-repl> { x = "a"; y = "b"; }.${[ "x" "y" ]}+> error: value is a list while a string was expected++We expand the meaning of the @.@ operator such that if the expression on the+right-hand side evaluates to a list, then the entire expression evaluates to a+list:++> bricks-repl> { x = "a"; y = "b"; }.${[ "x" "y" ]}+> [ "a" "b" ]++Furthermore, when the expression on the right-hand side is a list literal (an+expression of the form @[@ ... @]@), the antiquotation (wrapping the expression+in @${@ ... @}@) may be omitted:++> bricks-repl> { x = "a"; y = "b"; }.[ "x" "y" ]+> [ "a" "b" ]++This provides a convenient alternative to many situations in which one might use+the @with@ keyword in Nix. For example, where in Nix we might write++> ghcWithPackages (p: with p; [ base containers text ]);++we may write this equivalently in Bricks as++> ghcWithPackages (p: p.[ "base" "containers" "text" ]);++== Bricks does not have URI literals++If a string literal is a URI, it can be written in Nix without quotes.++We have chosen not to include this feature because it provides very little+convenience and steals some syntax from lambda expressions. Consider the+following Nix example:++> nix-repl> (let x = "a"; in y: x) "b"+> "a"++If we remove the space between after the colon (@:@), we get something entirely+different:++> nix-repl> (let x = "a"; in y:x) "b"+> error: attempt to call something which is not+> a function but a string, at (string):1:1++Because @y:x@ contains a colon, Nix interprets it as a URI and parses it as the+string @"y:x"@ (this is the "string" to which the error message refers), rather+than as a lambda.++In Bricks, by contrast, the colon in a lambda is /not/ required to be followed+by whitespace, and the previous example works as we would like.++> bricks-repl> (let x = "a"; in y:x) "b"+> "a"++== Bricks does not have path literals++In Nix, an unquoted string that contains a slash is interpreted as a filesystem+path.++Path literals have some subtle syntax rules. A common mistake is forgetting+to always include a slash in the path. For an example, @./foo.nix@ is a URI:++> nix-repl> ./foo.nix+> /home/chris/foo.nix++But @foo.nix@, without the leading @./@, is parsed differently:++> nix-repl> foo.nix+> error: undefined variable ‘foo’ at (string):1:1++As with URI literals, we find that the unquoted form for paths does not provide+enough convenience to compensate for its potential for confusion, so we have+opted to omit it.++== Bricks does not have a built-in @import@ function++In Nix, a path literal that does not start with a slash (such as @./foo.nix@) is+interpreted as a /relative/ path, and the Nix parser immediately resolves it+with respect to the directory in which the Nix file resides (as we saw in the+example above, where it resolved to @\/home\/chris\/foo.nix@).++We love being able to use relative imports, but we don't like needing a built-in+language feature to do it. Fortunately, Bricks can achieve the same effect by+using an ordinary function instead.++__todo:__ Explain how imports work in the standard library, once it is+implemented.++In doing this, we buy back some purity that Nix's @import@ lacks. By passing the+path argument through a function parameter, rather than deriving it implicitly+from the context of "which file did the expression /come from/?" we eliminate a+case where an expression's meaning depends on something other than the values of+the formal parameters that bind its free variables.++There is another benefit to the Bricks approach: While Nix import syntax is+restricted to static paths only (the argument to @import@ cannot contain free+variables), Bricks has no such limitation.++== Bricks uses Haskell-style comments++The Bricks inline comment keyword is @--@; in Nix it is @#@.++The Bricks block comment form is @{\-@ ... @-\}@; in Nix it is @/*@ ... @*/@.++This decision was made merely due to the Bricks authors' aesthetic preference+and affinity for Haskell.++== Bricks block comments may be nested++Although Nix does have block comments,++> nix-repl> /* */ "a"+> "a"++Nix does not support /nested/ block comments:++> nix-repl> /* /* */ */ "a"+> error: syntax error, unexpected '*', at (string):1:10++Bricks does:++@+bricks-repl> {\- {\- -\} -\} "a"+"a"+@++== Bricks does not support escape sequences in indented strings++Within the indented string form (@''@ ... @''@), Nix supports the following+unorthodox escape sequences:++  - @''${@ → @${@+  - @'''@ → @''@+  - @''\\n@ → newline+  - @''\\r@ → carriage return+  - @''\\t@ → tab++> nix-repl> ''ab''\ncd''+> "ab\ncd"++Bricks does not support any of these. If you want to include any of these+strings within an indented string, you can use antiquotation:++> bricks-repl> ''ab${"\n"}cd''+> "ab\ncd"++Or you can interpret escape sequences at runtime by passing your string through+some function in the standard library that does this sort of thing (__todo:__+discuss said function, once it exists).++== Bricks does not allow quotes in /let/ bindings++In Nix, the left-hand side of a /let/ binding is allowed to be a quoted string.+This lets you create variables that aren't valid as variable expressions (when+you /refer to/ a variable, it may /not/ be quoted), which puts you in a weird+sitation where there is a variable in scope which can only be referred to by+inheriting it into a dict.++> nix-repl> let "a b" = "c"; in { inherit "a b"; }+> { "a b" = "c"; }++This oddity does not seem to serve any real purpose, so we have omitted it.++== The Nix "set" concept is renamed to "dict" in Bricks++The Nix concept of "set" is referred to as "dict" in Bricks. This is not+actually a language difference; we just use a different word to talk about the+same thing. We believe that "dict" is a more familiar term for this data+structure, and that Nix's use of "set" conflicts unnecessarily with the more+common usage of the word.++-}
+ src/Bricks/BuiltinFunctions.hs view
@@ -0,0 +1,150 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase                 #-}+{-# LANGUAGE NoImplicitPrelude          #-}+{-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE RankNTypes                 #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeApplications           #-}++module Bricks.BuiltinFunctions where++import Bricks.Term+import Bricks.Type++-- Bricks internal+import           Bricks.Internal.Prelude+import           Bricks.Internal.Text    (Text)+import qualified Bricks.Internal.Text    as Text++-- Containers+import qualified Data.Map as Map+import           Data.Set (Set)++-- Base+import Data.Dynamic  (fromDynamic, toDyn)+import Data.Typeable (Typeable)+import Prelude       (Integer, Num (..))+import Text.Read     (readMaybe)++term'data :: forall a. Typeable a => Type a -> a -> Term+term'data (Type n) = Term'Data n . toDyn @a++fn'pure'parametric'arity1 :: (Term -> Term) -> Term+fn'pure'parametric'arity1 f =+  Term'Function $ \x -> pure $+  f x++fn'pure'parametric'arity2 :: (Term -> Term -> Term) -> Term+fn'pure'parametric'arity2 f =+  Term'Function $ \x -> pure $+  fn'pure'parametric'arity1 $ f x++fn'pure'parametric'arity3 :: (Term -> Term -> Term -> Term) -> Term+fn'pure'parametric'arity3 f =+  Term'Function $ \x -> pure $+  fn'pure'parametric'arity2 $ f x++fn'id :: Term+fn'id = fn'pure'parametric'arity1 id++fn'const :: Term+fn'const = fn'pure'parametric'arity2 const++{- | Function composition, in the traditional "backwards" order.++Read @f `fn'comp` g@ as "/f/ after /g/." -}++fn'comp :: Term+fn'comp = fn'pure'parametric'arity3 $ \f g x -> f /@\ (g /@\ x)++fn'flip :: Term+fn'flip = fn'pure'parametric'arity3 $ \f x y -> f /@@\ (y, x)++fn'dict'lookup :: Term+fn'dict'lookup =+  Term'Function $ \x -> do+    map <- reduce'dict'keys x+    pure $ Term'Function $ \y -> do+      key <- cast'data type'string y+      case Map.lookup key map of+        Nothing -> bottom . Bottom $ "Key " <> key <> " not found in dict"+        Just a -> pure a++fn'or :: Term+fn'or =+  Term'Function $ \x -> cast'data type'boolean x <&>+  \case+    True -> fn'const /@\ term'data type'boolean True+    False -> assert'type type'boolean++fn'and :: Term+fn'and =+  Term'Function $ \x -> cast'data type'boolean x <&>+  \case+    False -> fn'const /@\ term'data type'boolean False+    True -> assert'type type'boolean++fn'string'append :: Term+fn'string'append =+  Term'Function $ \x -> cast'data type'string x <&> \x' ->+  Term'Function $ \y -> cast'data type'string y <&> \y' ->+  term'data type'string (Text.append x' y')++fn'dict'disallowExtraKeys :: Set Text -> Term+fn'dict'disallowExtraKeys _allowedKeys =+  Term'Function $ undefined++fn'dict'merge'preferLeft :: Term+fn'dict'merge'preferLeft =+  Term'Function $ \x -> reduce'dict'keys x <&> \x' ->+  Term'Function $ \y -> reduce'dict'keys y <&> \y' ->+  Term'Dict'ReducedKeys $ Map.union x' y'++fn'dict'merge'preferRight :: Term+fn'dict'merge'preferRight = fn'flip /@\ fn'dict'merge'preferLeft++cast'data :: (MonadEval m, Typeable a) => Type a -> Term -> m a+cast'data = req fst++-- | Like 'fn'id', but fails if the argument is not of the given type.+assert'type :: Typeable a => Type a -> Term+assert'type t = Term'Function $ req snd t++req :: forall a b m. (MonadEval m, Typeable a)+  => ((a, Term) -> b)+  -> Type a+  -> Term+  -> m b+req s (Type n) = reduce'term >=> \case+  t@(Term'Data n' x) ->+    case fromDynamic @a x of+      Nothing -> bottom . Bottom $ "Expected " <> n <> ", got " <> n'+      Just a -> pure (s (a, t))+  x ->+    termTypeName x >>= \n' ->+      bottom . Bottom $ "Expected " <> n <> ", got " <> n'++fn'int'add :: Term+fn'int'add =+  Term'Function $ \x -> cast'data type'integer x <&> \x' ->+  Term'Function $ \y -> cast'data type'integer y <&> \y' ->+  term'data type'integer (x' + y')++fn'int'constructor :: Term+fn'int'constructor =+  Term'Function $ cast'data type'string >=> (+    Text.unpack >>> readMaybe @Integer >>>+    maybe (bottom . Bottom $ "invalid integer")+          (pure . term'data type'integer)+  )++standard'library :: Term+standard'library =+  Term'Dict'ReducedKeys . Map.fromList $+    [ ("add", fn'int'add)+    , ("integer", fn'int'constructor)+    , ("and", fn'and)+    , ("or", fn'or)+    , ("id", fn'id)+    , ("const", fn'const)+    ]
+ src/Bricks/Evaluation.hs view
@@ -0,0 +1,286 @@+{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE FlexibleContexts           #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE InstanceSigs               #-}+{-# LANGUAGE LambdaCase                 #-}+{-# LANGUAGE NoImplicitPrelude          #-}+{-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TupleSections              #-}+{-# LANGUAGE TypeApplications           #-}++{- |++This module lets you evaluate Bricks expressions. First 'expression'to'term's+converts the abstract syntax tree ('Expression') into an enriched version of the+lambda calculus ('Term'). Then we perform /graph reduction/, repeatedly applying+simplifications until we arrive at an irreducible term.++When we substitute an argument into a lambda body to perform beta-conversion, we+do so by substituting a 'Pointer' of the argument rather than the term itself.+This gives rise to /sharing/, thus turning the tree into a general graph, and+helps avoid reducing the same expression more than once.++= /The Implementation of Functional Programming Languages/++The design of Bricks evaluation is in large part based on Simon Peyton Jones's+1987 book+<https://www.microsoft.com/en-us/research/publication/the-implementation-of-functional-programming-languages/ The Implementation of Functional Programming Languages>.+In attempt to keep the Bricks API documentation mostly self-contained, we avoid+making frequent references to this work throughout. Instead, here we give a list+of some important connections to the book:++  - The rationale for immediately converting the AST into another data structure+    rather than performing any transformations directly on the AST comes from+    page 38.++  - A Bricks function defined using a dict pattern turns into a+    "pattern-matching lambda abstraction"; this term is introduced on page 61.++  - Page 185 introduces the style of drawing ASTs to which the '/@\' operator+    alludes.++  - /Pointer substitution/ is described on page 208.++  - The implementation of 'term'substitute' closely follows the description of+    /Instantiate/, page 210.++  - Page 20 introduces the name capture problem. Pages 199 and 210 discuss how+    we avoid it by only reducing the top-level redex, which has no free+    variables.++  - On page 233 starts the discussion of how letrec expressions are instantiated+    as cyclic graphcs.++-}+module Bricks.Evaluation where++-- Bricks+import Bricks.BuiltinFunctions+import Bricks.Term+import Bricks.Type++-- Bricks internal+import           Bricks.Internal.Map     (Map)+import qualified Bricks.Internal.Map     as Map+import           Bricks.Internal.Monad+import           Bricks.Internal.Prelude+import           Bricks.Internal.Text    (Text)+import qualified Bricks.Internal.Text    as Text++-- Containers+import qualified Data.Set as Set++-- Base+import Data.Typeable (Typeable)+import Prelude       (error)+import System.IO     (IO)++newtype Eval a = Eval { unEval :: ExceptT Bottom IO a }+  deriving (Functor, Applicative, Monad, MonadError Bottom, MonadIO)++instance MonadEval Eval+  where++    reduce'term :: Term -> Eval Term+    reduce'term =+      \case+        Term'Pointer p ->+          readTermPtr p >>= reduce'term++        t@(Term'Data _ _) -> pure t+        t@(Term'Function _) -> pure t+        t@(Term'Lambda _ _) -> pure t+        t@(Term'List _) -> pure t+        t@(Term'Dict _) -> pure t+        t@(Term'Dict'ReducedKeys _) -> pure t+        Term'Var x ->+          bottom . Bottom $ "Free variable: " <> x++        Term'LetRec map body -> do+          -- Create a pointer for each of the let bindings+          map' <- traverse create'pointer map :: Eval (Map Text Term)+          -- Substitute each of the bindings into each of the others+          traverse_ (instantiate'many map') map'+          -- Substitute all of the bindings into the body+          instantiate'many map' body >>= reduce'term++        Term'Apply f value ->+          reduce'term f >>= \case++            Term'Function f' ->+              f' value >>= reduce'term++            -- The function is a lambda, so it can be applied to an argument.+            Term'Lambda pattern body ->+              case pattern of++                -- A simple single-variable pattern+                TermPattern'Simple var ->+                  create'pointer value >>= \value'ptr ->+                    instantiate'one var value'ptr body++                -- A dict pattern+                TermPattern'Dict vars ->+                  reduce'dict'keys value >>= \values ->+                    case Map.exactKeys values vars of+                      Left missingKeys -> bottom . Bottom $+                        "Dict lookup failed: " <>+                        Text.show (Set.toList missingKeys)+                      Right values' -> instantiate'many values' body++            t ->+              termTypeName t >>= \n ->+                bottom . Bottom $ "Expected function, got " <> n++    reduce'dict'keys :: Term -> Eval (Map Text Term)+    reduce'dict'keys = reduce'term >=> \case+      Term'Dict _ ->+        undefined+      Term'Dict'ReducedKeys x ->+        pure x+      x ->+        termTypeName x >>= \n' ->+          bottom . Bottom $ "Expected dict, got " <> n'++does'termPattern'bind :: Text -> TermPattern -> Bool+does'termPattern'bind n = \case+  TermPattern'Simple x  -> n == x+  TermPattern'Dict   xs -> n `Set.member` xs++{- | @instantiate var value body@ produces a copy of the term @body@,+substituting @value@ for free occurrences of @var@. -}++instantiate'one+  :: forall m. MonadEval m+  => Text  -- ^ @var@   - Variable name+  -> Term  -- ^ @value@ - The argument being substituted. We assume that this+           --             term has no free variables; or else we will suffer+           --             the /name capture problem/.+  -> Term  -- ^ @body@  - The term being copied ("instantiated")+  -> m Term++-- The numbered comments within this definition are nearly verbatim from+-- page 210 of /The Implementation of Functional Programming Languages/.+-- There SPJ denotes this construction as body[value/var].++instantiate'one var value =+  go+  where+    go :: Term -> m Term+    go body = case body of+      Term'Var x ->++        -- 1. If /body/ is a variable x and /var/ = x, then return /value/+        --    (here we substitute /value/ for an occurrence of /var/).+        if x == var then pure value++        -- 2. If /body/ is a variable x and /var/ ≠ x, then return /body/.+        else pure body++      -- 3. If /body/ is a constant or built-in function, then return /body/.+      Term'Data _ _   -> pure body -- constant+      Term'Function _ -> pure body -- built-in function++      -- 4. If /body/ is an application (e1 e2), then return the application+      --    (e1[value/var] e2[value/var]).+      Term'Apply a b -> Term'Apply <$> go a <*> go b++      Term'Lambda a b ->+        -- 5. If /body/ is a lambda abstraction λx.E and /var/ = x, then return+        --    /body/ - the new lambda abstraction binds /var/ anew, so no+        --    substitutions should occur inside it, and hence we can avoid+        --    instantiating it altogether.+        if does'termPattern'bind var a then pure body++        -- 6. If /body/ is a lambda abstraction λx.E and /var/ ≠ x, then return+        --    λx.E[value/var] - we must instantiate the body in case there are+        --    any free occurrences of /var/ inside it.+        else Term'Lambda a <$> go b++      Term'LetRec a b ->+        -- The same reasoning as (5) and (6) - If the let expression binds+        -- /var/, then we do nothing. Otherwise we substitute everywhere.+        if Map.member var a then pure body+        else Term'LetRec <$> traverse go a <*> go b++      Term'List xs -> Term'List <$> traverse go xs++      Term'Dict _ -> Term'Dict <$> undefined++      Term'Dict'ReducedKeys x -> Term'Dict'ReducedKeys <$> traverse go x++      Term'Pointer p -> go =<< readTermPtr p+        -- todo - let this function return whether it made any substitutions.+        -- If it didn't, then just return the pointer.++instantiate'many+  :: forall m. MonadEval m+  => Map Text Term -- ^ @values@+  -> Term          -- ^ @body@+  -> m Term+instantiate'many values body =+  foldr f (pure body) (Map.toList values)+  where+    f :: (Text, Term) -> m Term -> m Term+    f (var, value) bod = instantiate'one var value =<< bod++reduce'to'type :: Typeable a => Type a -> Term -> IO (Either Bottom a)+reduce'to'type typ =+  (reduce'term >=> cast'data typ) >>> unEval >>> runExceptT++reduce'to'type'or'throw :: (HasCallStack, Typeable a) => Type a -> Term -> IO a+reduce'to'type'or'throw typ =+  reduce'to'type typ >=>+  either (error . Text.unpack . displayBottom) pure++{-+        -- The function is a lambda with a dict pattern.+        Term'Lambda'DictPattern dp body ->++          -- Reduce the argument, and require it to be a dict.+          reduce'term value >>= \case+            Term'Dict dict -> undefined+            x -> termTypeName x >>= \n -> evalError ("Expected dict, got " <> n)+-}++{-+    t@(Term'Pointer p) ->+      (readTermPtr p >>= reduce >>= writeTermPtr p) $> t+    x -> pure x+-}++{-+  case t of++    Term'Apply f x ->+      create'pointer x >>= \value ->+      reduce'term f >>= \case+        Term'Lambda var body -> term'substitute var value body+        Term'Lambda'DictPattern pattern body ->+          -- Reduce the argument, and require it to be a dict.+          reduce'term value >>= \case+            Term'Dict dict -> undefined+              -- Reduce all of the dict's keys.++            _ -> pure $ Term'Error "Expected dict, got something else"++          -- todo:+          --  -+          --  - fail if there are extra keys and no ellipsis+          --  - fail if there are missing keys+          --  - perform substitutions+        _ -> pure $ Term'Error "Expected function, got something else"++    Term'Var x -> pure $ Term'Error $+      "Unbound variable: " <> render'strStatic'unquotedIfPossible x++    Term'Pointer (Pointer ref) -> do+      t' <- readIORef ref+      t'' <- reduce'term t'+      writeIORef ref t''+      pure t''++    _ -> pure t++-}
− src/Bricks/Expression.hs
@@ -1,583 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase                 #-}-{-# LANGUAGE NoImplicitPrelude          #-}-{-# LANGUAGE OverloadedStrings          #-}-{-# LANGUAGE TypeApplications           #-}--module Bricks.Expression-  (-  -- * Expressions-    Expression (..)--  -- * Strings-  , Str'Static-  , Str'Dynamic (..)-  , Str'1 (..)-  , strDynamic'toList-  , strDynamic'fromList-  , strDynamic'singleton--  -- * String conversions-  , str'dynamicToStatic-  , str'staticToDynamic-  , str'unquotedToDynamic--  -- * Lists-  , List (..)--  -- * Dicts-  , Dict (..)-  , DictBinding (..)--  -- * Dict lookup-  , Dot (..)-  , expression'applyDots--  -- * Lambdas-  , Lambda (..)--  -- * Function parameters-  , Param (..)-  , DictPattern (..)-  , DictPattern'1 (..)--  -- * Function application-  , Apply (..)-  , expression'applyArgs--  -- * @let@-  , Let (..)-  , LetBinding (..)--  -- * @with@-  , With (..)--  -- * @inherit@-  , Inherit (..)--  ) where---- Bricks-import Bricks.UnquotedString---- Bricks internal-import           Bricks.Internal.Prelude-import           Bricks.Internal.Seq     (Seq)-import qualified Bricks.Internal.Seq     as Seq-import           Bricks.Internal.Text    (Text)-import qualified Bricks.Internal.Text    as Text---- Base-import           Data.Foldable (Foldable)-import qualified Data.Foldable as Foldable-import           Prelude       (Int)--data Expression-  = Expr'Var Str'Unquoted-      -- ^ A /variable/, such as @x@.-  | Expr'Str Str'Dynamic-      -- ^ A /string/ may be quoted either in the traditional form using a-      -- single double-quote (@"@...@"@):-      ---      -- > "one\ntwo"-      ---      -- or in the "indented string" form using two single-quotes (@''@...@''@):-      ---      -- > ''-      -- >   one-      -- >   two-      -- > ''-      ---      -- Both of these examples reduce to the same value, because leading-      -- whitespace is stripped from indented strings.-      ---      -- Either may contain "antiquotation" (also known as "string-      -- interpolation") to conveniently concatenate string-valued variables-      -- into the string.-      ---      -- > "Hello, my name is ${name}!"-      ---      -- Normal strings may contain the following escape sequences:-      ---      --  - @\\\\@ → @\\@-      --  - @\\"@  → @"@-      --  - @\\${@ → @${@-      --  - @\\n@  → newline-      --  - @\\r@  → carriage return-      --  - @\\t@  → tab-      ---      -- The indented string form does not interpret any escape sequences.-  | Expr'List List-      -- ^ A /list/ is an ordered collection of expressions.-      ---      -- The empty list:-      ---      -- > [ ]-      ---      -- A list containing three variables:-      ---      -- > [ a b c ]-      ---      -- Lambdas, function applications, @let@-@in@ expressions, and @with@-      -- expressions must be parenthesized when in a list.-      ---      -- > [-      -- >   (x: f x y)-      -- >   (g y)-      -- >   (let a = y; in f a a)-      -- >   (with d; f x a)-      -- > ]-  | Expr'Dict Dict-      -- ^ A /dict/ is an unordered extensional mapping from strings.-      ---      -- The empty dict (with no bindings):-      ---      -- > { }-      ---      -- A dict with two bindings:-      ---      -- > {-      -- >   a = "one";-      -- >   b = "one two";-      -- > }-      ---      -- By default, dict bindings cannot refer to each other. For that, you-      -- need the @rec@ keyword to create a /recursive/ dict.-      ---      -- > rec {-      -- >   a = "one";-      -- >   b = "${a} two";-      -- > }-      ---      -- In either case, the order of the bindings does not matter.-      ---      -- The left-hand side of a dict binding may be a quoted string (in the-      -- traditional @"@...@"@ style, not the indented-string @''@ style),-      -- which make it possible for them to be strings that otherwise couldn't-      -- be expressed unquoted, such as strings containing spaces:-      ---      -- > { "a b" = "c"; }-      ---      -- The left-hand side of a dict may even be an arbitrary expression,-      -- using the @${@ ... @}@ form:-      ---      -- > let x = "a b"; in { ${x} = "c"; }-      ---      -- Dicts also support the @inherit@ keyword:-      ---      -- > { inherit a; inherit (x) c d; }-      ---      -- The previous expression is equivalent to:-      ---      -- > { a = a; c = x.c; d = x.d; }-  | Expr'Dot Dot-      -- ^ A /dot/ expression (named after the @.@ character it contains)-      -- looks up the value in a dict.-      ---      -- The examples in this section all reduce to "Z".-      ---      -- > { a = "Z"; }.a-      ---      -- > let x = { a = "Z"; }; in x.a-      ---      -- > { x = { a = "Z"; }; }.x.a-      ---      -- The right-hand side of a dot may be a quoted string (in the-      -- traditional @"@...@"@ style, not the indented-string @''@ style):-      ---      -- > { a = "Z"; }."a"-      ---      -- The right-hand side of a dot may even be an arbitrary expression,-      -- using the @${@ ... @}@ form:-      ---      -- > { a = "Z"; }.${ let b = "a"; in b }-  | Expr'Lambda Lambda-      -- ^ A /lambda/ expression @x: y@ where @x@ is the parameter.-      ---      -- This is a function that turns a name into a greeting:-      ---      -- > name: "Hello, ${name}!"-      ---      -- The function parameter can also be a /dict pattern/, which looks like-      -- this:-      ---      -- > { a, b, c ? "another" }: "Hello, ${a}, ${b}, and ${c}!"-      ---      -- That function accepts a dict and looks up the keys @a@, @b@, and @c@-      -- from it, applying the default value @"another"@ to @c@ if it is not-      -- present in the dict. Dict patterns therefore give us something that-      -- resembles functions with named parameters and default arguments.-      ---      -- By default, a lambda defined with a dict pattern fails to evaluate if-      -- the dict argument contains keys that are not listed in the pattern.-      -- To prevent it from failing, you can end the pattern with @...@:-      ---      -- > ({ a, ... }: x) { a = "1"; b = "2"; }-      ---      -- Every function has a single parameter. If you need multiple-      -- parameters, you have to curry:-      ---      -- > a: b: [ a b ]-  | Expr'Apply Apply-      -- ^ Function /application/:-      ---      -- > f x-      ---      -- If a function has multiple (curried) parameters, you can chain them-      -- together like so:-      ---      -- > f x y z-  | Expr'Let Let-      -- ^ A /let/-/in/ expression:-      ---      -- > let-      -- >   greet = x: "Hello, ${x}!";-      -- >   name = "Chris";-      -- > in-      -- >   greet name-      ---      -- /Let/ bindings, like dict bindings, may also use the @inherit@ keyword.-      ---      -- > let-      -- >   d = { greet = x: "Hello, ${x}!"; name = "Chris"; }-      -- >   inherit (d) greet name;-      -- > in-      -- >   greet name-      ---      -- The previous example also demonstrates how the bindings in a /let/-      -- expression may refer to each other (much like a dict with the @rec@-      -- keyword). As with dicts, the order of the bindings does not matter.-  | Expr'With With-      -- ^ A /with/ expression is similar to a /let/-/in/ expression, but the-      -- bindings come from a dict.-      ---      -- > with {-      -- >   greet = x: "Hello, ${x}!";-      -- >   name = "Chris";-      -- > };-      -- >   greet name--{- | A fixed string value. We use the description "static" to mean the string-may not contain antiquotation, in contrast with 'Str'Dynamic' which can. -}-type Str'Static = Text--{- | A quoted string expression, which may be a simple string like @"hello"@ or-a more complex string containing antiquotation like @"Hello, my name is-${name}!"@. See 'Expr'Str'.--We use the description "dynamic" to mean the string may contain antiquotation,-in contrast with 'Str'Static' which cannot. -}-newtype Str'Dynamic = Str'Dynamic { strDynamic'toSeq :: Seq Str'1 }-  deriving (Monoid, Semigroup)--strDynamic'toList :: Str'Dynamic -> [Str'1]-strDynamic'toList =-  Seq.toList . strDynamic'toSeq--strDynamic'fromList :: [Str'1] -> Str'Dynamic-strDynamic'fromList =-  Str'Dynamic . Seq.fromList--strDynamic'singleton :: Str'1 -> Str'Dynamic-strDynamic'singleton =-  Str'Dynamic . Seq.singleton--str'dynamicToStatic :: Str'Dynamic -> Maybe Str'Static-str'dynamicToStatic = strDynamic'toList >>> \case-  [Str'1'Literal x] -> Just x-  _                 -> Nothing--str'staticToDynamic :: Str'Static -> Str'Dynamic-str'staticToDynamic =-  strDynamic'singleton . Str'1'Literal--str'unquotedToDynamic :: Str'Unquoted -> Str'Dynamic-str'unquotedToDynamic =-  str'staticToDynamic . str'unquotedToStatic---- | One part of a 'Str'Dynamic'.-data Str'1-  = Str'1'Literal Str'Static-  | Str'1'Antiquote Expression---- | A function expression. See 'Expr'Lambda'.-data Lambda =-  Lambda-    { lambda'head :: Param-        -- ^ Declaration of the function's parameter-    , lambda'body :: Expression-        -- ^ Body of the function; what it evaluates to-    }---- | A function application expression. See 'Expr'Apply'.-data Apply =-  Apply-    { apply'func :: Expression-        -- ^ The function being called-    , apply'arg :: Expression-        -- ^ The argument to the function-    }--{- | A parameter to a 'Lambda'. All functions have a single parameter, but it's-more complicated than that because it may also include dict destructuring. -}-data Param-  = Param'Name Str'Unquoted-      -- ^ A simple single-parameter function-  | Param'DictPattern DictPattern-      -- ^ Dict destructuring, which gives you something resembling multiple-      -- named parameters with default values-  | Param'Both Str'Unquoted DictPattern-      -- ^ Both a param name /and/ a dict pattern, separated by the @\@@-      -- keyword---- | A type of function parameter ('Param') that does dict destructuring.-data DictPattern =-  DictPattern-    { dictPattern'items :: Seq DictPattern'1-        -- ^ The list of keys to pull out of the dict, along with any default-        -- value each may have-    , dictPattern'ellipsis :: Bool-        -- ^ Whether to allow additional keys beyond what is listed in the-        -- items, corresponding to the @...@ keyword-    }---- | One item within a 'DictPattern'.-data DictPattern'1 =-  DictPattern'1-    { dictPattern'1'name :: Str'Unquoted-        -- ^ The name of the key to pull out of the dict-    , dictPattern'1'default :: Maybe Expression-        -- ^ The default value to be used if the key is not present in the dict-    }--{- | A list literal expression, starting with @[@ and ending with @]@.-See 'Expr'List'. -}-newtype List = List (Seq Expression)-  deriving (Monoid, Semigroup)--{- | A dict literal expression, starting with @{@ or @rec {@ and ending with-@}@. See 'Expr'Dict'. -}-data Dict =-  Dict-    { dict'rec :: Bool-        -- ^ Whether the dict is recursive (denoted by the @rec@ keyword)-    , dict'bindings :: Seq DictBinding-        -- ^ The bindings (everything between @{@ and @}@)-    }---- | A binding of the form @x = y;@ within a 'DictLiteral' or 'LetExpr'.-data DictBinding-  = DictBinding'Eq Expression Expression-  | DictBinding'Inherit Inherit--{- | An expression of the form @person.name@ that looks up a key from a dict.-See 'Expr'Dot'. -}-data Dot =-  Dot-    { dot'dict :: Expression-    , dot'key  :: Expression-    }---- | A @let@-@in@ expression. See 'Expr'Let'.-data Let =-  Let-    { let'bindings :: Seq LetBinding-        -- ^ The bindings (everything between the @let@ and @in@ keywords)-    , let'value :: Expression-        -- ^ The value (everything after the @in@ keyword)-    }--{- | A semicolon-terminated binding within the binding list of a 'Let'-expression. -}-data LetBinding-  = LetBinding'Eq Str'Static Expression-      -- ^ A binding with an equals sign, of the form @x = y;@-  | LetBinding'Inherit Inherit-      -- ^ A binding using the @inherit@ keyword, of the form @inherit a b;@-      -- or @inherit (x) a b;@---- | A @with@ expression. See 'Expr'With'.-data With =-  With-    { with'context :: Expression-    , with'value :: Expression-    }--data Inherit =-  Inherit-    { inherit'source :: Maybe Expression-    , inherit'names :: Seq Str'Static-    }--expression'applyArgs-  :: Expression   -- ^ Function-  -> [Expression] -- ^ Args-  -> Expression   -- ^ Function application-expression'applyArgs =-  foldl (\acc b -> Expr'Apply (Apply acc b))--expression'applyDots-  :: Expression   -- ^ Dict-  -> [Expression] -- ^ Lookups-  -> Expression   -- ^ Dot expression-expression'applyDots =-  foldl (\acc b -> Expr'Dot (Dot acc b))-------------------------------------------------------------------------------------  Show-----------------------------------------------------------------------------------{- | This instance is designed for doctests and REPL experimentation. The-format is designed to strike a balance in verbosity between the derived 'Show'-implementations (which are unwieldily long) and the Bricks language itself-(which is quite terse but unsuitable for demonstrating the parser, as-outputting a Bricks rendering of parse result wouldn't illumunate anyone's-understanding of the AST that the 'Show' instances are here to depict). -}-instance Show Expression        where showsPrec = showsPrec'--instance Show Str'Dynamic       where showsPrec = showsPrec'-instance Show Str'1             where showsPrec = showsPrec'-instance Show List              where showsPrec = showsPrec'-instance Show Dict              where showsPrec = showsPrec'-instance Show DictBinding       where showsPrec = showsPrec'-instance Show Dot               where showsPrec = showsPrec'-instance Show Lambda            where showsPrec = showsPrec'-instance Show Param             where showsPrec = showsPrec'-instance Show DictPattern       where showsPrec = showsPrec'-instance Show DictPattern'1     where showsPrec = showsPrec'-instance Show Apply             where showsPrec = showsPrec'-instance Show Let               where showsPrec = showsPrec'-instance Show LetBinding        where showsPrec = showsPrec'-instance Show With              where showsPrec = showsPrec'-instance Show Inherit           where showsPrec = showsPrec'--showsPrec' :: Show' a => Int -> a -> String -> String-showsPrec' _ x = (Text.unpack (show' x) <>)--class Show' a-  where-    show' :: a -> Text--instance Show' Expression-  where-    show' = \case-      Expr'Var x    -> show'var x-      Expr'Str x    -> show' x-      Expr'List x   -> show' x-      Expr'Dict x   -> show' x-      Expr'Dot x    -> show' x-      Expr'Lambda x -> show' x-      Expr'Apply x  -> show' x-      Expr'Let x    -> show' x-      Expr'With x   -> show' x--instance Show' Str'Dynamic-  where-    show' x =-      Text.unwords ["str", show'list (strDynamic'toList x)]--instance Show' Str'1-  where-    show' = \case-      Str'1'Literal x -> show'quoted' x-      Str'1'Antiquote x -> Text.unwords ["antiquote", show'paren x]--instance Show' List-  where-    show' (List xs) = Text.unwords ["list", show'list xs]--instance Show' Dict-  where-    show' (Dict r bs) =-      Text.unwords-        [if r then "rec'dict" else "dict", show'list bs]--instance Show' DictBinding-  where-    show' = \case-      DictBinding'Eq a b -> Text.unwords ["binding", show'paren a, show'paren b]-      DictBinding'Inherit x -> show' x--instance Show' Dot-  where-    show' (Dot a b) = Text.unwords ["dot", show'paren a, show'paren b]--instance Show' Lambda-  where-    show' (Lambda a b) = Text.unwords ["lambda", show'paren a, show'paren b]--instance Show' Param-  where-    show' = \case-      Param'Name a -> show'param a-      Param'DictPattern b -> show' b-      Param'Both a b -> Text.intercalate " <> " [ show'param a, show' b ]--instance Show' DictPattern-  where-    show' = \case-      DictPattern xs e ->-        Text.intercalate " <> " $-        [Text.unwords ["pattern", show'list xs]] <>-        (if e then ["ellipsis"] else [])--instance Show' DictPattern'1-  where-    show' (DictPattern'1 a mb) =-      Text.unwords $-        show'param a :-        maybe [] (\b -> [Text.unwords ["& def", show'paren b]]) mb--instance Show' Apply-  where-    show' (Apply a b) = Text.unwords ["apply", show'paren a, show'paren b]--instance Show' Let-  where-    show' (Let xs y) = Text.unwords ["let'in", show'list xs, show'paren y]--instance Show' LetBinding-  where-    show' = \case-      LetBinding'Eq a b -> Text.unwords ["binding", show'static a, show'paren b]-      LetBinding'Inherit x -> show' x--instance Show' With-  where-    show' = \case-      With a b -> Text.unwords ["with", show'paren a, show'paren b]--instance Show' Inherit-  where-    show' (Inherit mf xs) =-      Text.unwords $-        (maybe ("inherit":) (\a -> ("inherit'from" :) . (show'paren a :)) mf) $-        [show'list' $ fmap show'quoted' xs]--show'list :: (Foldable f, Show' a) => f a -> Text-show'list =-  show'list' . fmap show' . Foldable.toList--show'list' :: Foldable f => f Text -> Text-show'list' x =-  "[" <> Text.intercalate ", " x <> "]"--show'quoted' :: Text -> Text-show'quoted' =-  Text.pack . show @Text--show'paren :: Show' a => a -> Text-show'paren x =-  "(" <> show' x <> ")"--show'static :: Str'Static -> Text-show'static x =-  Text.unwords ["str", show'quoted' x]--show'param :: Str'Unquoted -> Text-show'param x =-  Text.unwords ["param", show'quoted' (str'unquotedToStatic x)]--show'var :: Str'Unquoted -> Text-show'var x =-  Text.unwords ["var", show'quoted' (str'unquotedToStatic x)]
− src/Bricks/Expression/Construction.hs
@@ -1,233 +0,0 @@-{-# LANGUAGE FunctionalDependencies     #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE NoImplicitPrelude          #-}--{- | Functions for constructing 'Expression's that match the 'Show'-implementations.--This module is only designed for testing and REPL use. It isn't re-exported-into the main Bricks API because it's a bit messy:--- It introduces some superfluous typeclasses for the sake of brevity.-- There are a lot of terse function names here that would clash with other-  things easily.-- Some functions are partial, such as those that require strings that can be-  rendered unquoted.-- It uses string overloading in a way that the regular API probably shouldn't.-- The functions are oriented toward constructing 'Expression's, skipping over-  the intermediary types they're composed of, which is convenient but may make-  them insufficient for some use cases.---}-module Bricks.Expression.Construction where---- Bricks-import Bricks.Expression-import Bricks.UnquotedString---- Bricks internal-import           Bricks.Internal.Prelude-import qualified Bricks.Internal.Seq     as Seq-import           Bricks.Internal.Text    (Text)-import qualified Bricks.Internal.Text    as Text---- Base-import Data.List.NonEmpty (NonEmpty ((:|)))-import Data.String        (IsString (fromString))--------------------------------------------------------------------------------------  Lambdas-----------------------------------------------------------------------------------lambda :: Param'Builder -> Expression -> Expression-lambda a b =-  Expr'Lambda $ Lambda (buildParam a) b--------------------------------------------------------------------------------------  Function application-----------------------------------------------------------------------------------apply :: Expression -> Expression -> Expression-apply a b =-  Expr'Apply $ Apply a b--------------------------------------------------------------------------------------  Variables-----------------------------------------------------------------------------------var :: Text -> Expression-var =-  Expr'Var . str'unquoted'orThrow--------------------------------------------------------------------------------------  Dots-----------------------------------------------------------------------------------dot :: Expression -> Expression -> Expression-dot a b =-  Expr'Dot $ Dot a b--------------------------------------------------------------------------------------  Let-----------------------------------------------------------------------------------let'in :: [LetBinding] -> Expression -> Expression-let'in a b =-  Expr'Let $ Let (Seq.fromList a) b--------------------------------------------------------------------------------------  Dicts-----------------------------------------------------------------------------------dict :: [DictBinding] -> Expression-dict =-  Expr'Dict . Dict False . Seq.fromList--rec'dict :: [DictBinding] -> Expression-rec'dict =-  Expr'Dict . Dict False . Seq.fromList--------------------------------------------------------------------------------------  Overloaded 'binding' function-----------------------------------------------------------------------------------class Binding a b | b -> a-  where-    binding :: a -> Expression -> b--instance Binding Expression DictBinding-  where-    binding = DictBinding'Eq--instance Binding Text LetBinding-  where-    binding = LetBinding'Eq--------------------------------------------------------------------------------------  Overloaded 'inherit' functions-----------------------------------------------------------------------------------class IsInherit a-  where-    fromInherit :: Inherit -> a--instance IsInherit DictBinding-  where-    fromInherit = DictBinding'Inherit--instance IsInherit LetBinding-  where-    fromInherit = LetBinding'Inherit--inherit :: IsInherit a => [Text] -> a-inherit =-  fromInherit . Inherit Nothing . Seq.fromList--inherit'from :: IsInherit a => Expression -> [Text] -> a-inherit'from x y =-  fromInherit $ Inherit (Just x) (Seq.fromList y)--------------------------------------------------------------------------------------  Dynamic strings-----------------------------------------------------------------------------------str :: [Str'1'] -> Expression-str =-  Expr'Str . Str'Dynamic . Seq.fromList . fmap unStr'1'--antiquote :: Expression -> Str'1'-antiquote =-  Str'1' . Str'1'Antiquote---- | A newtype for 'Str'1' just so we can give it the 'IsString' instance--- which would be dubiously appropriate for the actual 'Str'1' type.-newtype Str'1' = Str'1' { unStr'1' :: Str'1 }--instance IsString Str'1'-  where-    fromString = Str'1' . Str'1'Literal . Text.pack--------------------------------------------------------------------------------------  Overloaded 'param' function-----------------------------------------------------------------------------------class IsParam a-  where-    param :: Text -> a--instance IsParam Param'Builder-  where-    param x = paramBuilder $ Param'Name $ str'unquoted'orThrow x--instance IsParam DictPattern'1-  where-    param x = DictPattern'1 (str'unquoted'orThrow x) Nothing--------------------------------------------------------------------------------------  Param builder-----------------------------------------------------------------------------------newtype Param'Builder = Param'Builder (NonEmpty Param)-  deriving Semigroup--buildParam :: Param'Builder -> Param-buildParam (Param'Builder xs) =-  foldr1 mergeParams xs--paramBuilder :: Param -> Param'Builder-paramBuilder x =-  Param'Builder (x :| [])--pattern :: [DictPattern'1] -> Param'Builder-pattern xs =-  paramBuilder $ Param'DictPattern $ DictPattern (Seq.fromList xs) False--def :: Expression -> DictPattern'1 -> DictPattern'1-def b (DictPattern'1 a _) =-  DictPattern'1 a (Just b)--ellipsis :: Param'Builder-ellipsis =-  paramBuilder $ Param'DictPattern $ DictPattern Seq.empty True--{- | Combine two params, merging dict patterns with 'mergeDictPatterns' and-preferring the right-hand-side when names conflict. -}-mergeParams :: Param -> Param -> Param-mergeParams = (+)-  where-    (+) :: Param -> Param -> Param-    -- A name on the right overrides a name on the left-    Param'Both _n1 p1 + Param'Name n2 = Param'Both n2 p1-    -- The simplest combinations: turning one or the other into both-    Param'Name n + Param'DictPattern p = Param'Both n p-    Param'DictPattern p + Param'Name n = Param'Both n p-    -- Otherwise a name on the left gets overridden by anything on the right-    Param'Name _n + x = x-    -- Combinations that require merging the dict patterns-    Param'DictPattern p1 + Param'DictPattern p2 =-      Param'DictPattern (mergeDictPatterns p1 p2)-    Param'DictPattern p1 + Param'Both n p2 =-      Param'Both n (mergeDictPatterns p1 p2)-    Param'Both _n1 p1 + Param'Both n2 p2 =-      Param'Both n2 (mergeDictPatterns p1 p2)-    Param'Both n p1 + Param'DictPattern p2 =-      Param'Both n (mergeDictPatterns p1 p2)--{- | Combine two dict patterns, taking the concatenation of the item list, and-the Boolean /or/ of the ellipsis flag. -}-mergeDictPatterns :: DictPattern -> DictPattern -> DictPattern-mergeDictPatterns = (+)-  where-    DictPattern xs1 e1 + DictPattern xs2 e2 =-      DictPattern (xs1 <> xs2) (e1 || e2)
+ src/Bricks/ExpressionToTerm.hs view
@@ -0,0 +1,181 @@+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}++{- |++Conversion from 'Expression' (the AST produced by the parser) to 'Term'+(an augmented form of the lambda calculus used for evaluation).++-}+module Bricks.ExpressionToTerm where++-- Bricks+import Bricks.BuiltinFunctions+import Bricks.Expression+import Bricks.Term+import Bricks.Type++-- Bricks internal+import qualified Bricks.Internal.List    as List+import           Bricks.Internal.Prelude+import qualified Bricks.Internal.Seq     as Seq+import           Bricks.Internal.Text    (Text)++-- Containers+import           Data.Map (Map)+import qualified Data.Map as Map+import           Data.Set (Set)+import qualified Data.Set as Set++-- Base+import Control.Applicative (liftA2)+import System.IO           (IO)++expression'to'term :: Expression -> IO Term+expression'to'term =+  \case+    Expr'Var x -> var'to'term x+    Expr'Str x -> str'to'term x+    Expr'Str'Indented x -> str'to'term (inStr'to'strDynamic x)+    Expr'List x -> list'to'term x+    Expr'Dict x -> dict'to'term x+    Expr'Dot x -> dot'to'term x+    Expr'Lambda x -> lambda'to'term x+    Expr'Apply x -> apply'to'term x+    Expr'Let x -> let'to'term x++var'to'term :: Var -> IO Term+var'to'term = pure . Term'Var . var'text++apply'to'term :: Apply -> IO Term+apply'to'term x =+  do+    a <- expression'to'term (apply'func x)+    b <- expression'to'term (apply'arg x)+    pure $ a /@\ b++str'to'term :: Str'Dynamic -> IO Term+str'to'term x =+  case Seq.toList (strDynamic'toSeq x) of+    [] -> pure $ term'data type'string ""+    ys -> foldr1 (liftA2 f) $ fmap str'1'to'term ys+  where+    f a b = fn'string'append /@@\ (a, b)++str'1'to'term :: Str'1 -> IO Term+str'1'to'term = \case+  Str'1'Literal x -> pure $ term'data type'string (str'static'text x)+  Str'1'Antiquote x -> expression'to'term x++list'to'term :: List -> IO Term+list'to'term x =+  Term'List <$> traverse expression'to'term (list'expressions x)++dict'to'term :: Dict -> IO Term+dict'to'term = undefined++dot'to'term :: Dot -> IO Term+dot'to'term x =+  do+    a <- expression'to'term (dot'dict x)+    b <- expression'to'term (dot'key x)+    pure $ fn'dict'lookup /@@\ (a, b)++let'to'term :: Let -> IO Term+let'to'term x =+  Term'LetRec <$> bindings <*> body++  where+    bindings :: IO (Map Text Term)+    bindings =+      Map.fromList . List.concat <$>+      traverse letBinding'to'term (let'bindings x)++    body :: IO Term+    body =+      expression'to'term $ let'value x++letBinding'to'term :: LetBinding -> IO [(Text, Term)]+letBinding'to'term =+  \case+    LetBinding'Eq a b ->+      do+        b' <- expression'to'term b+        pure [(var'text a, b')]+    LetBinding'Inherit d xs ->+      do+        -- Use a pointer for the dict we're inheriting from, to avoid+        -- having to reduce it more than once.+        p <- newTermPtr =<< expression'to'term d++        pure+          $ fmap (\x ->+            ( var'text x+            , fn'dict'lookup /@@\ (p, term'data type'string (var'text x))+            ))+          $ Seq.toList xs+++--------------------------------------------------------------------------------+--  Converting a lambda expression to a lambda term+--------------------------------------------------------------------------------++lambda'to'term :: Lambda -> IO Term+lambda'to'term x =+  do+    body <- expression'to'term (lambda'body x)+    case lambda'head x of+      Param'Name var       -> lambda'to'term'simple var body+      Param'DictPattern dp -> lambda'to'term'dictPattern dp body+      Param'Both var dp    -> lambda'to'term'both var dp body++lambda'to'term'simple :: Var -> Term -> IO Term+lambda'to'term'simple var body =+  -- For a simple named parameter, the AST translates directly into the+  -- lambda calculus.+  pure $ TermPattern'Simple (var'text var) |-> body++lambda'to'term'dictPattern :: DictPattern -> Term -> IO Term+lambda'to'term'dictPattern dp body = do+  -- For dict patterns, we have to do a few more things:+  let names = dictPattern'names dp++    -- 1. If there is no ellipsis, add a check to fail if there are+    --    extra keys in the argument.+  let h = if dictPattern'ellipsis dp then fn'id+          else fn'dict'disallowExtraKeys names++  defs <- dictPattern'defaults dp++    -- 2. Insert a dict-merging function to apply default arguments.+  let g = fn'dict'merge'preferLeft /@\ Term'Dict'ReducedKeys defs++  let f = TermPattern'Dict names |-> body++  pure $ fn'comp /@@\ (fn'comp /@@\ (f, g), h)++lambda'to'term'both :: Var -> DictPattern -> Term -> IO Term+lambda'to'term'both var dp body =+  -- For a named parameter /and/ a dict pattern, we nest the dict pattern+  -- lambda inside a regular lambda.+  lambda'to'term'simple var =<< lambda'to'term'dictPattern dp body++dictPattern'names :: DictPattern -> Set Text+dictPattern'names (DictPattern xs _) =+  Set.fromList . fmap f . Seq.toList $ xs+  where+    f = var'text . dictPattern'1'name++dictPattern'defaults :: DictPattern -> IO (Map Text Term)+dictPattern'defaults (DictPattern xs _) =+  Map.fromList . catMaybes <$> traverse f (Seq.toList xs)+  where+    f :: DictPattern'1 -> IO (Maybe (Text, Term))+    f x =+      case dictPattern'1'default x of+        Nothing -> pure Nothing+        Just d ->+          do+            a <- expression'to'term d+            pure $ Just (var'text (dictPattern'1'name x), a)
− src/Bricks/IndentedString.hs
@@ -1,120 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase                 #-}-{-# LANGUAGE OverloadedStrings          #-}--module Bricks.IndentedString-  (-  -- * Indented string-    InStr (..)-  , inStr'join-  , inStr'level-  , inStr'dedent-  , inStr'trim-  , inStr'toList--  -- * Single line of an indented string-  , InStr'1 (..)-  , inStr'1'nonEmpty-  , inStr'1'empty-  , inStr'1'modifyLevel--  ) where---- Bricks-import Bricks.Expression---- Bricks internal-import           Bricks.Internal.Prelude-import           Bricks.Internal.Seq     (Seq, (<|))-import qualified Bricks.Internal.Seq     as Seq-import qualified Bricks.Internal.Text    as Text--{- | An "indented string literal," delimited by two single-quotes @''@.--This type of literal is called "indented" because the parser automatically-removes leading whitespace from the string ('inStr'dedent'), which makes it-convenient to use these literals for multi-line strings within an indented-expression without the whitespace from indentation ending up as part of the-string. -}-newtype InStr = InStr { inStr'toSeq :: Seq InStr'1 }-  deriving (Monoid, Semigroup)--instance Show InStr-  where-    show = show . inStr'toList--inStr'toList :: InStr -> [InStr'1]-inStr'toList =-  Seq.toList . inStr'toSeq---- | One line of an 'InStr'.-data InStr'1 =-  InStr'1-    { inStr'1'level :: Natural-        -- ^ The number of leading space characters. We store this separately-        -- for easier implementation of 'inStr'dedent'.-    , inStr'1'str :: Str'Dynamic-        -- ^ The rest of the line after any leading spaces.-    }--instance Show InStr'1-  where-    show (InStr'1 n s) = "indent-" <> show n <> " " <> show s--{- | Join 'InStr's with newlines interspersed. -}-inStr'join :: InStr -> Str'Dynamic-inStr'join xs =-  Str'Dynamic . Seq.concat $-    Seq.intersperse-      (Seq.singleton (Str'1'Literal "\n"))-      (f <$> inStr'toSeq xs)-  where-    f :: InStr'1 -> Seq Str'1-    f (InStr'1 n parts) = Str'1'Literal (Text.replicate (fromIntegral n) " ")-                          <| strDynamic'toSeq parts--{- | Determines whether an 'InStr'1' contains any non-space-characters. The opposite of 'inStr'1'nonEmpty'.--This is used to determine whether this line should be considered when-calculating the number of space characters to strip in 'inStr'dedent'. -}-inStr'1'nonEmpty :: InStr'1 -> Bool-inStr'1'nonEmpty =-  not . inStr'1'empty---- | The opposite of 'inStr'1'nonEmpty'.-inStr'1'empty :: InStr'1 -> Bool-inStr'1'empty (InStr'1{ inStr'1'str = Str'Dynamic x }) =-  Seq.null x--{- | Determine how many characters of whitespace to strip from an indented-string. -}-inStr'level :: InStr -> Natural-inStr'level =-  maybe 0 id-  . Seq.minimum-  . Seq.map inStr'1'level-  . Seq.filter inStr'1'nonEmpty-  . inStr'toSeq---- | Modify an 'InStr' by applying a function to its number of leading spaces.-inStr'1'modifyLevel :: (Natural -> Natural) -> (InStr'1 -> InStr'1)-inStr'1'modifyLevel f x@InStr'1{inStr'1'level = a} =-  x{ inStr'1'level = f a }--{- | Determine the minimum indentation of any nonempty line, and remove that-many space characters from the front of every line. -}-inStr'dedent :: InStr -> InStr-inStr'dedent xs =-  let-    b = inStr'level xs-    f a = if a >= b then a - b else 0-  in-    InStr $ inStr'1'modifyLevel f <$> inStr'toSeq xs---- | Remove any empty lines from the beginning or end of an indented string.-inStr'trim :: InStr -> InStr-inStr'trim =-  InStr . trimWhile inStr'1'empty . inStr'toSeq-  where-    trimWhile f = Seq.dropWhileL f . Seq.dropWhileR f
+ src/Bricks/Internal/Monad.hs view
@@ -0,0 +1,16 @@+module Bricks.Internal.Monad+  (+  -- * IO+    MonadIO (..)++  -- * Error+  , MonadError (..), ExceptT (..), runExceptT++  -- * Reader+  , ReaderT (..)++  ) where++-- mtl+import Control.Monad.Except+import Control.Monad.Reader
− src/Bricks/Internal/Prelude.hs
@@ -1,43 +0,0 @@-module Bricks.Internal.Prelude--  ( (<&>)--  , module Control.Applicative-  , module Control.Arrow-  , module Control.Monad-  , module Data.Bool-  , module Data.Char-  , module Data.Either-  , module Data.Eq-  , module Data.Foldable-  , module Data.Function-  , module Data.Functor-  , module Data.Maybe-  , module Data.Monoid-  , module Data.Semigroup-  , module Data.String-  , module Numeric.Natural-  , module Text.Show--  ) where--import Control.Applicative (pure, (*>), (<*), (<*>), (<|>))-import Control.Arrow       ((>>>))-import Control.Monad       ((>>=))-import Data.Bool           (Bool (False, True), not, (&&), (||))-import Data.Char           (Char)-import Data.Either         (Either (..))-import Data.Eq             (Eq ((/=), (==)))-import Data.Foldable       (asum, fold, foldMap, foldl, foldl1, foldr, foldr1)-import Data.Function       (id, ($), (&), (.))-import Data.Functor        (Functor, fmap, void, ($>), (<$), (<$>))-import Data.Maybe          (Maybe (Just, Nothing), catMaybes, maybe)-import Data.Monoid         (Monoid (mappend, mempty))-import Data.Semigroup      (Semigroup ((<>)))-import Data.String         (String)-import Numeric.Natural     (Natural)-import Text.Show           (Show (show, showList, showsPrec), shows)--(<&>) :: Functor f => f a -> (a -> b) -> f b-(<&>) = flip fmap-infixl 1 <&>
− src/Bricks/Internal/Seq.hs
@@ -1,47 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module Bricks.Internal.Seq--  ( Seq--  , (<|), (|>)--  , concat-  , dropWhileL-  , dropWhileR-  , empty-  , filter-  , fromList-  , intersperse-  , map-  , minimum-  , null-  , toList-  , singleton--  ) where---- Containers-import Data.Sequence (Seq, dropWhileL, dropWhileR, empty, filter, fromList,-                      null, singleton, (<|), (|>))---- Base-import           Data.Foldable (Foldable, fold, toList)-import           Data.Functor  (fmap)-import qualified Data.List     as List-import           Data.Maybe    (Maybe (..))-import           Data.Ord      (Ord)--intersperse :: a -> Seq a -> Seq a-intersperse x xs =-  fromList (List.intersperse x (toList xs))--concat :: Foldable f => f (Seq a) -> Seq a-concat = fold--minimum :: Ord a => Seq a -> Maybe a-minimum xs =-  if null xs then Nothing else Just (List.minimum (toList xs))--map :: (a -> b) -> Seq a -> Seq b-map = fmap
− src/Bricks/Internal/Text.hs
@@ -1,35 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}--module Bricks.Internal.Text--  ( Text--  , all-  , concat-  , intercalate-  , null-  , pack-  , replace-  , replicate-  , singleton-  , unpack-  , unwords--  ) where---- Text-import           Data.Text (Text, all, null, pack, replace, replicate,-                            singleton, unpack, unwords)-import qualified Data.Text as Text---- Base-import Data.Foldable (Foldable, toList)-import Data.Function ((.))--concat :: Foldable f => f Text -> Text-concat =-  Text.concat . toList--intercalate :: Foldable f => Text -> f Text -> Text-intercalate x =-  Text.intercalate x . toList
− src/Bricks/Keyword.hs
@@ -1,67 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedStrings #-}--module Bricks.Keyword-  (-  -- * Type-    Keyword--  -- * List of keywords-  , keywords--  -- * The keywords-  , keyword'rec-  , keyword'let-  , keyword'in-  , keyword'with-  , keyword'inherit-  , keyword'inlineComment--  -- * Type conversion-  , keywordString-  , keywordText--  ) where---- Bricks internal-import           Bricks.Internal.Prelude-import           Bricks.Internal.Text    (Text)-import qualified Bricks.Internal.Text    as Text--newtype Keyword =-  Keyword-    { keywordText :: Text-    }--{- | All of the keywords. This list is used when parsing and rendering because-an unquoted string cannot have a name that is exactly the same as a keyword. -}-keywords :: [Keyword]-keywords =-  [ keyword'rec-  , keyword'let-  , keyword'in-  , keyword'with-  , keyword'inherit-  , keyword'inlineComment-  ]--keywordString :: Keyword -> String-keywordString = Text.unpack . keywordText--keyword'rec :: Keyword-keyword'rec = Keyword "rec"--keyword'let :: Keyword-keyword'let = Keyword "let"--keyword'in :: Keyword-keyword'in = Keyword "in"--keyword'with :: Keyword-keyword'with = Keyword "with"--keyword'inherit :: Keyword-keyword'inherit = Keyword "inherit"--keyword'inlineComment :: Keyword-keyword'inlineComment = Keyword "--"
− src/Bricks/Parsing.hs
@@ -1,679 +0,0 @@-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedStrings #-}--{- | Parsec 'Parser's for the Bricks language.--Most parsers consume trailing whitespace, except ones that operate within-quoted string environments where whitespace is significant.---}-module Bricks.Parsing-  (-  -- * Expressions-    parse'expression-  , parse'expression'paren-  , parse'expression'dictKey--  -- * Expression lists-  , parse'expressionList-  , parse'expressionList'1-  , parse'expressionList'1'noDot--  -- * Strings-  , parse'strUnquoted-  , parse'strStatic-  , parse'strStatic'quoted-  , parse'strStatic'unquoted-  , parse'strDynamic'quoted-  , parse'strDynamic'normalQ-  , parse'strDynamic'indentedQ-  , parse'str'within'normalQ-  , parse'str'escape'normalQ-  , parse'inStr-  , parse'inStr'1--  -- * Lists-  , parse'list--  -- * Dicts-  , parse'dict-  , parse'dict'rec-  , parse'dict'noRec-  , parse'dictBinding-  , parse'dictBinding'inherit-  , parse'dictBinding'eq--  -- * Dict lookup-  , parse'dot'rhs'chain--  -- * Lambdas-  , parse'lambda--  -- * Function parameters-  , parse'param-  , parse'param'var-  , parse'param'noVar-  , parse'dictPattern-  , parse'dictPattern'start--  -- * @let@-  , parse'let-  , parse'letBinding-  , parse'letBinding'eq-  , parse'letBinding'inherit--  -- * @with@-  , parse'with--  -- * @inherit@-  , parse'inherit--  -- * Comments and whitespace-  , parse'spaces-  , parse'comment-  , parse'comment'inline-  , parse'comment'block--  -- * Keywords-  , parse'keyword--  -- * Antiquotation-  , parse'antiquote--  ) where---- Bricks-import Bricks.Expression-import Bricks.IndentedString-import Bricks.Keyword-import Bricks.UnquotedString---- Bricks internal-import           Bricks.Internal.Prelude-import           Bricks.Internal.Seq     (Seq, (|>))-import qualified Bricks.Internal.Seq     as Seq-import           Bricks.Internal.Text    (Text)-import qualified Bricks.Internal.Text    as Text---- Parsec-import           Text.Parsec      ((<?>))-import qualified Text.Parsec      as P-import           Text.Parsec.Text (Parser)---- Base-import Control.Monad (fail)-import Prelude       (succ)--{- $setup-->>> import Data.Foldable (length)->>> import Text.Parsec (parseTest)---}--parse'spaces :: Parser ()-parse'spaces =-  (void $ P.many (void (P.space <?> "") <|> parse'comment))--parse'comment :: Parser ()-parse'comment =-  parse'comment'inline <|> parse'comment'block--parse'comment'inline :: Parser ()-parse'comment'inline =-  void $ P.try (P.string "--" <?> "") *> P.manyTill P.anyChar (P.char '\n')--parse'comment'block :: Parser ()-parse'comment'block =-  start <* P.manyTill middle end-  where-    start  = void $ P.try (P.string "{-" <?> "")-    middle = parse'comment'block <|> void P.anyChar-    end    = P.try (P.string "-}")---- | Backtracking parser for a particular keyword.-parse'keyword :: Keyword -> Parser ()-parse'keyword k =-  P.try $ do-    -- Consume the keyword-    _ <- P.string (keywordString k)--    -- Do /not/ consume any subsequent character that are allowed to be part-    -- of a valid identifier. For example, this prevents this parser from-    -- interpreting the beginning of an identifier named "letter" as the-    -- keyword "let".-    _ <- P.notFollowedBy (P.satisfy char'canRenderUnquoted)--    -- As usual, consume trailing spaces.-    _ <- parse'spaces--    pure ()--{- | Parser for an unquoted string. Unquoted strings are restricted to a-conservative set of characters, and they may not be any of the keywords.-->>> parseTest parse'strUnquoted "abc"-unquoted "abc"-->>> parseTest parse'strUnquoted "x{y"-unquoted "x"-->>> parseTest parse'strUnquoted "let"-parse error at (line 1, column 4):-unexpected end of input---}-parse'strUnquoted :: Parser Str'Unquoted-parse'strUnquoted =-  do-    -- Consume at least one character-    a <- Text.pack <$> P.many1 (P.satisfy char'canRenderUnquoted)--    -- Fail if what we just parsed isn't a valid unquoted string-    case str'tryUnquoted a of-      Nothing -> P.parserZero-      Just b  -> parse'spaces $> b--{- | Parser for a static string which may be either quoted or unquoted.-->>> parseTest parse'strStatic "\"hello\""-"hello"-->>> parseTest parse'strStatic "hello"-"hello"-->>> parseTest parse'strStatic "\"a b\""-"a b"-->>> parseTest parse'strStatic "a b"-"a"--By "static," we mean that the string may /not/ contain antiquotation.-->>> parseTest parse'strStatic "\"a${x}b\" xyz"-parse error at (line 1, column 5):-antiquotation is not allowed in this context---}-parse'strStatic :: Parser Str'Static-parse'strStatic =-  (parse'strStatic'quoted <|> parse'strStatic'unquoted) <?> "static string"---- | Parser for a static string that is quoted.-parse'strStatic'quoted :: Parser Str'Static-parse'strStatic'quoted =-  P.char '"' *> parse'str'within'normalQ <* asum-    [ P.char '"' *> parse'spaces-    , P.string "${" *> fail "antiquotation is not allowed in this context"-    ]---- | Parser for an unquoted static string.-parse'strStatic'unquoted :: Parser Str'Static-parse'strStatic'unquoted =-  parse'strUnquoted <&> str'unquotedToStatic--{- | Parser for a dynamic string that is quoted. It may be a "normal" quoted-string delimited by one double-quote @"@...@"@ ('parse'strDynamic'normalQ') or-an "indented" string delimited by two single-quotes @''@...@''@-('parse'strDynamic'indentedQ'). -}-parse'strDynamic'quoted :: Parser Str'Dynamic-parse'strDynamic'quoted =-  parse'strDynamic'normalQ <|> parse'strDynamic'indentedQ---- | Parser for a dynamic string enclosed in "normal" quotes (@"@...@"@).-parse'strDynamic'normalQ :: Parser Str'Dynamic-parse'strDynamic'normalQ =-  P.char '"' *> go Seq.empty-  where-    go :: Seq Str'1 -> Parser Str'Dynamic-    go previousParts =-      asum-        [ end $> Str'Dynamic previousParts-        , asum-            [ parse'str'within'normalQ <&> Str'1'Literal-            , anti-            ]-          >>= \x -> go $ previousParts |> x-        ]--    -- Read the closing " character-    end = P.char '"' *> parse'spaces--    -- Read an antiquote-    anti = fmap Str'1'Antiquote $-      P.try (P.string "${") *> parse'spaces *> parse'expression <* P.char '}'--{- | Parser for at least one normal character, within a normally-quoted string-context, up to but not including the end of the string or the start of an-antiquotation. -}-parse'str'within'normalQ :: Parser Text-parse'str'within'normalQ = do-  fmap Text.concat $ P.many1 $ asum-    [ P.satisfy (\c -> c /= '$' && c /= '"' && c /= '\\') <&> Text.singleton-    , P.try $ P.char '$' <* P.notFollowedBy (P.char '{')  <&> Text.singleton-    , parse'str'escape'normalQ-    ]--parse'str'escape'normalQ :: Parser Text-parse'str'escape'normalQ =-  P.char '\\' *> asum-    [ P.char '\\'   $> "\\"-    , P.char '"'    $> "\""-    , P.char 'n'    $> "\n"-    , P.char 'r'    $> "\r"-    , P.char 't'    $> "\t"-    , P.string "${" $> "${"-    ]--{- | Parser for a dynamic string enclosed in "indented string" format,-delimited by two single-quotes @''@...@''@. This form of string does not have-any escape sequences. -}-parse'strDynamic'indentedQ :: Parser Str'Dynamic-parse'strDynamic'indentedQ =-  inStr'join . inStr'dedent . inStr'trim <$> parse'inStr--{- | Parser for an indented string. This parser produces a representation of-the lines from the source as-is, before the whitespace is cleaned up. -}-parse'inStr :: Parser InStr-parse'inStr =-  P.string "''" *> go Seq.empty-  where-    go :: Seq InStr'1 -> Parser InStr-    go previousLines =-      do-        line <- parse'inStr'1-        let newLines = previousLines |> line-        asum-          [ P.string "''" *> parse'spaces $> InStr newLines-          , P.char '\n'   *> go newLines-          ]---- | Parser for a single line of an 'InStr'.-parse'inStr'1 :: Parser InStr'1-parse'inStr'1 =-  do-    a <- parse'count (P.char ' ')-    b <- go Seq.empty-    pure $ InStr'1 a b-  where-    go :: Seq Str'1 -> Parser Str'Dynamic-    go previousParts =-      asum-        [ end              $> Str'Dynamic previousParts-        , chars           >>= \x                -> go (previousParts |> x)-        , parse'antiquote >>= \(Str'Dynamic xs) -> go (previousParts <> xs)-        ]--    end = P.lookAhead $ asum-      [ void $ P.char '\n'-      , void $ P.try (P.string "''")-      ]--    chars = fmap (Str'1'Literal . Text.pack) $ P.many1 $ asum-      [ P.satisfy (\c -> c /= '$' && c /= '\'' && c /= '\n')-      , P.try $ P.char '$'  <* P.notFollowedBy (P.char '{')-      , P.try $ P.char '\'' <* P.notFollowedBy (P.char '\'')-      ]--parse'antiquote :: Parser Str'Dynamic-parse'antiquote =-  (P.try (P.string "${") *> parse'spaces *> parse'expression <* P.char '}')-  <&> \case-    Expr'Str x -> x-    x -> strDynamic'singleton (Str'1'Antiquote x)--{- | Parser for a function parameter (the beginning of a 'Lambda'), including-the colon. This forms part of 'parse'expression', so it backtracks in places-where it has overlap with other types of expressions. -}-parse'param :: Parser Param-parse'param =-  parse'param'var <|> parse'param'noVar--{- | Parser for a parameter that starts with a variable. This could be a simple-param that consists only of /only/ the variable, or the variable may be-followed by a dict pattern. -}-parse'param'var :: Parser Param-parse'param'var = do-  -- This part backtracks because until we get to the : or @, we don't-  -- know whether the variable name we're reading is a lambda parameter-  -- or just the name by itself (and not part of a lambda).-  (a, b) <- P.try $ do-    a <- parse'strUnquoted <* parse'spaces-    b <- ((P.char ':' $> False) <|> (P.char '@' $> True)) <* parse'spaces-    pure (a, b)-  if b-    -- If we read an @, then the next thing is a pattern.-    then parse'dictPattern <* P.char ':' <* parse'spaces <&> Param'Both a-    -- Otherwise it's just the variable and we're done.-    else pure $ Param'Name a--{- | Parser for a param that has no variable, only a a dict pattern. This-parser backtracks because the beginning of a dict pattern looks like the-beginning of a dict expression. -}-parse'param'noVar :: Parser Param-parse'param'noVar = Param'DictPattern <$> do-  -- First we look ahead to determine whether it looks like a lambda.-  _ <- P.try . P.lookAhead $ parse'dictPattern'start--  -- And if so, then we go on and parse the dict pattern with no-  -- further backtracking.-  parse'dictPattern <* P.char ':' <* parse'spaces--{- | Parser for a dict pattern (the type of lambda parameter that does dict-destructuring. This parser does not backtrack. -}-parse'dictPattern :: Parser DictPattern-parse'dictPattern =-  P.char '{' *> parse'spaces *> go Seq.empty-  where-    go :: Seq DictPattern'1 -> Parser DictPattern-    go previousItems =-      asum-        [ end $> DictPattern previousItems False-        , ellipsis $> DictPattern previousItems True-        , do-            newItems <- item <&> \x -> previousItems |> x-            asum-              [ P.char ',' *> parse'spaces *> go newItems-              , end $> DictPattern newItems False-              ]-        ]--    item = DictPattern'1 <$> parse'strUnquoted <*> P.optionMaybe def--    ellipsis = P.string "..." *> parse'spaces *> end--    def = P.char '?' *> parse'spaces *> parse'expression--    end = P.char '}' *> parse'spaces--{- | This is used in a lookahead by 'parse'param' to determine whether we're-about to start parsing a 'DictPattern'. -}-parse'dictPattern'start :: Parser ()-parse'dictPattern'start =-  P.char '{' *> parse'spaces *> asum-    [ void $ P.string "..."-    , void $ P.char '}' *> parse'spaces *> P.char ':'-    , void $ parse'strUnquoted *> (P.char ',' <|> P.char '?' <|> P.char '}')-    ]--{- | Parser for a lambda expression (@x: y@).-->>> parseTest parse'lambda "x: [x x \"a\"]"-lambda (param "x") (list [var "x", var "x", str ["a"]])-->>> parseTest parse'lambda "{a,b}:a"-lambda (pattern [param "a", param "b"]) (var "a")-->>> parseTest parse'lambda "{ ... }: \"x\""-lambda (pattern [] <> ellipsis) (str ["x"])-->>> parseTest parse'lambda "a@{ f, b ? g x, ... }: f b"-lambda (param "a" <> pattern [param "f", param "b" & def (apply (var "g") (var "x"))] <> ellipsis) (apply (var "f") (var "b"))-->>> parseTest parse'lambda "a: b: \"x\""-lambda (param "a") (lambda (param "b") (str ["x"]))---}-parse'lambda :: Parser Lambda-parse'lambda =-  Lambda <$> parse'param <*> parse'expression--{- | Parser for a list expression (@[ ... ]@).-->>> parseTest parse'list "[]"-list []-->>> parseTest parse'list "[x \"one\" (a: b) (c d)]"-list [var "x", str ["one"], lambda (param "a") (var "b"), apply (var "c") (var "d")]---}-parse'list :: Parser List-parse'list =-  (start *> parse'expressionList <* end) <&> List . Seq.fromList-  where-    start = P.char '[' *> parse'spaces-    end   = P.char ']' <* parse'spaces--{- | Parser for a dict expression, either recursive (@rec@ keyword) or not.-->>> parseTest parse'dict "{}"-dict []-->>> parseTest parse'dict "rec {  }"-rec'dict []-->>> parseTest parse'dict "{ a = b; inherit (x) y z \"s t\"; }"-dict [binding (str ["a"]) (var "b"), inherit'from (var "x") ["y", "z", "s t"]]---}-parse'dict :: Parser Dict-parse'dict =-  asum-    [ parse'dict'noRec <&> Dict False-    , parse'dict'rec   <&> Dict True-    ]--{- | Parser for a recursive (@rec@ keyword) dict.-->>> parseTest parse'dict "rec {  }"-rec'dict []-->>> parseTest parse'dict "rec { a = \"1\"; b = \"${a}2\"; }"-rec'dict [binding (str ["a"]) (str ["1"]), binding (str ["b"]) (str [antiquote (var "a"), "2"])]---}-parse'dict'rec :: Parser (Seq DictBinding)-parse'dict'rec =-  parse'keyword keyword'rec *> parse'dict'noRec--{- | Parser for a non-recursive (no @rec@ keyword) dict.-->>> parseTest parse'dict "{  }"-dict []-->>> parseTest parse'dict "{ a = \"1\"; b = \"${a}2\"; }"-dict [binding (str ["a"]) (str ["1"]), binding (str ["b"]) (str [antiquote (var "a"), "2"])]---}-parse'dict'noRec :: Parser (Seq DictBinding)-parse'dict'noRec =-  P.char '{' *> parse'spaces *> go Seq.empty-  where-    go :: Seq DictBinding -> Parser (Seq DictBinding)-    go previousBindings = asum-      [ P.char '}' *> parse'spaces $> previousBindings-      , parse'dictBinding >>= \a -> go (previousBindings |> a)-      ]--{- | Parser for a chain of dict lookups (like @.a.b.c@) on the right-hand side-of a 'Dot' expression.-->>> parseTest parse'dot'rhs'chain ""-[]-->>> parseTest parse'dot'rhs'chain ".abc"-[str ["abc"]]-->>> parseTest parse'dot'rhs'chain ".a.${b}.\"c\".\"d${e}\""-[str ["a"],var "b",str ["c"],str ["d", antiquote (var "e")]]---}-parse'dot'rhs'chain :: Parser [Expression]-parse'dot'rhs'chain =-  P.many $-  P.char '.' *> parse'spaces *> parse'expression'dictKey <* parse'spaces--parse'let :: Parser Let-parse'let =-  parse'keyword keyword'let *> go Seq.empty-  where-    go :: Seq LetBinding -> Parser Let-    go previousBindings =-      asum-        [ end              <&> Let previousBindings-        , parse'letBinding >>= \a -> go (previousBindings |> a)-        ]--    end = parse'keyword keyword'in *> parse'expression--parse'with :: Parser With-parse'with =-  With-    <$> (parse'keyword keyword'with *> parse'expression)-    <*> (P.char ';' *> parse'spaces *> parse'expression)--parse'dictBinding :: Parser DictBinding-parse'dictBinding =-  parse'dictBinding'inherit <|> parse'dictBinding'eq--parse'dictBinding'inherit :: Parser DictBinding-parse'dictBinding'inherit =-  DictBinding'Inherit <$> parse'inherit--parse'dictBinding'eq :: Parser DictBinding-parse'dictBinding'eq =-  DictBinding'Eq-    <$> (parse'expression'dictKey <* parse'spaces <* P.char '=' <* parse'spaces)-    <*> (parse'expression         <* parse'spaces <* P.char ';' <* parse'spaces)--parse'letBinding :: Parser LetBinding-parse'letBinding =-  parse'letBinding'inherit <|> parse'letBinding'eq--parse'letBinding'eq :: Parser LetBinding-parse'letBinding'eq =-  LetBinding'Eq-    <$> (parse'strStatic  <* parse'spaces <* P.char '=' <* parse'spaces)-    <*> (parse'expression <* parse'spaces <* P.char ';' <* parse'spaces)--parse'letBinding'inherit :: Parser LetBinding-parse'letBinding'inherit =-  LetBinding'Inherit <$> parse'inherit--parse'inherit :: Parser Inherit-parse'inherit =-  Inherit-    <$> (parse'keyword keyword'inherit *> P.optionMaybe parse'expression'paren)-    <*> go Seq.empty-  where-    go :: Seq Str'Static -> Parser (Seq Str'Static)-    go previousList =-      asum-        [ P.char ';' *> parse'spaces $> previousList-        , parse'strStatic >>= \x -> go (previousList |> x)-        ]--{- | The primary, top-level expression parser. This is what you use to parse a-@.nix@ file.-->>> parseTest parse'expression ""-parse error at (line 1, column 1):-unexpected end of input-expecting expression---}-parse'expression :: Parser Expression-parse'expression =-  p <?> "expression"-  where-    p = asum-      [ parse'let    <&> Expr'Let-      , parse'with   <&> Expr'With-      , parse'lambda <&> Expr'Lambda-      , parse'expressionList >>= \case-          [] -> P.parserZero-          f : args -> pure $ expression'applyArgs f args-      ]--{- | Parser for a list of expressions in a list literal (@[ x y z ]@) or in a-chain of function arguments (@f x y z@).-->>> parseTest parse'expressionList ""-[]-->>> parseTest (length <$> parse'expressionList) "x \"one two\" (a: b) (c d)"-4-->>> parseTest (length <$> parse'expressionList) "(x \"one two\" (a: b) (c d))"-1---}-parse'expressionList :: Parser [Expression]-parse'expressionList =-  P.many parse'expressionList'1 <?> "expression list"--{- | Parser for a single item within an expression list ('expressionListP').-This expression is not a lambda, a function application, a @let@-@in@-expression, or a @with@ expression.-->>> parseTest parse'expressionList'1 "ab.xy"-dot (var "ab") (str ["xy"])-->>> parseTest parse'expressionList'1 "(x: f x x) y z"-lambda (param "x") (apply (apply (var "f") (var "x")) (var "x"))-->>> parseTest parse'expressionList'1 "{ a = b; }.a y"-dot (dict [binding (str ["a"]) (var "b")]) (str ["a"])---}-parse'expressionList'1 :: Parser Expression-parse'expressionList'1 =-  expression'applyDots-    <$> parse'expressionList'1'noDot-    <*> parse'dot'rhs'chain-    <?> "expression list item"--{- | Like 'parse'expressionList'1', but with the further restriction that the-expression may not be a 'Dot'.-->>> parseTest parse'expressionList'1'noDot "ab.xy"-var "ab"-->>> parseTest parse'expressionList'1'noDot "(x: f x x) y z"-lambda (param "x") (apply (apply (var "f") (var "x")) (var "x"))-->>> parseTest parse'expressionList'1'noDot "{ a = b; }.a y"-dict [binding (str ["a"]) (var "b")]---}-parse'expressionList'1'noDot :: Parser Expression-parse'expressionList'1'noDot =-  asum-    [ parse'strDynamic'quoted <&> Expr'Str-    , parse'list              <&> Expr'List-    , parse'dict              <&> Expr'Dict-    , parse'strUnquoted       <&> Expr'Var-    , parse'expression'paren-    ]-    <?> "expression list item without a dot"--{- | Parser for a parenthesized expression, from opening parenthesis to closing-parenthesis. -}-parse'expression'paren :: Parser Expression-parse'expression'paren =-  P.char '(' *> parse'spaces *> parse'expression <* P.char ')' <* parse'spaces--{- | Parser for an expression in a context that is expecting a dict key.--One of:--- an unquoted string-- a quoted dynamic string-- an arbitrary expression wrapped in antiquotes (@${@...@}@)---}-parse'expression'dictKey :: Parser Expression-parse'expression'dictKey =-  asum-    [ parse'strDynamic'quoted <&> Expr'Str-    , P.string "${" *> parse'spaces *> parse'expression-        <* P.char '}' <* parse'spaces-    , parse'strUnquoted <&> Expr'Str . str'unquotedToDynamic-    ]--parse'count :: Parser a -> Parser Natural-parse'count p = go 0-  where-    go :: Natural -> Parser Natural-    go n = (p *> go (succ n)) <|> pure n
+ src/Bricks/Prelude.hs view
@@ -0,0 +1,42 @@+{-# LANGUAGE FlexibleContexts #-}++module Bricks.Prelude+  ( bricks'eval+  , bricks'eval'stdlib+  ) where++-- Bricks+import Bricks+import Bricks.BuiltinFunctions+import Bricks.Evaluation+import Bricks.ExpressionToTerm+import Bricks.Term+import Bricks.Type++-- Bricks internal+import Bricks.Internal.Prelude+import Bricks.Internal.Text    (Text)++-- Parsec+import qualified Text.Parsec as P++-- Base+import Data.Typeable (Typeable)++bricks'eval :: (HasCallStack, Typeable a) => Type a -> Text -> IO a+bricks'eval typ src =+  do+    term <- source'to'term src+    reduce'to'type'or'throw typ term++bricks'eval'stdlib :: (HasCallStack, Typeable a) => Type a -> Text -> IO a+bricks'eval'stdlib typ src =+  do+    term <- source'to'term src+    reduce'to'type'or'throw typ (term /@\ standard'library)++source'to'term :: HasCallStack => Text -> IO Term+source'to'term src =+  do+    expr <- either (error . show) pure (P.parse parse'expression "" src)+    expression'to'term expr
− src/Bricks/Rendering.hs
@@ -1,281 +0,0 @@-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedStrings #-}--module Bricks.Rendering-  (-  -- * @Render@-    Render--  -- * Expressions-  , render'expression-  , render'expression'listContext-  , render'expression'dotLeftContext-  , render'expression'applyLeftContext-  , render'expression'applyRightContext-  , render'expression'inParens-  , render'expression'dictKey--  -- * Strings-  , str'escape-  , render'strUnquoted-  , render'strStatic'unquotedIfPossible-  , render'strStatic'quoted-  , render'strDynamic'unquotedIfPossible-  , render'strDynamic'quoted-  , render'inStr'1--  -- * Lists-  , render'list--  -- * Dicts-  , render'dict-  , render'dictBinding--  -- * Dict lookup-  , render'dot--  -- * Lambdas-  , render'lambda--  -- * Function parameters-  , render'param-  , render'dictPattern-  , render'dictPattern'1--  -- * Function application-  , render'apply--  -- * @let@-  , render'let-  , render'letBinding--  -- * @with@-  , render'with--  -- * @inherit@-  , render'inherit--  ) where---- Bricks-import Bricks.Expression-import Bricks.IndentedString-import Bricks.Keyword-import Bricks.UnquotedString---- Bricks internal-import           Bricks.Internal.Prelude-import qualified Bricks.Internal.Seq     as Seq-import           Bricks.Internal.Text    (Text)-import qualified Bricks.Internal.Text    as Text---- Base-import Prelude (fromIntegral)--type Render a = a -> Text---- | Insert escape sequences for rendering normal double-quoted (@"@) strings.-str'escape :: Text -> Text-str'escape =-  Text.replace "\"" "\\\"" .-  Text.replace "${" "\\${" .-  Text.replace "\n" "\\n" .-  Text.replace "\r" "\\r" .-  Text.replace "\t" "\\t" .-  Text.replace "\\" "\\\\"---- | Render an unquoted string in unquoted form.-render'strUnquoted :: Render Str'Unquoted-render'strUnquoted = str'unquotedToStatic---- | Render a static string, in unquoted form if possible.-render'strStatic'unquotedIfPossible :: Render Str'Static-render'strStatic'unquotedIfPossible x =-  if str'canRenderUnquoted x then x else render'strStatic'quoted x---- | Render a static string, in quoted form.-render'strStatic'quoted :: Render Str'Static-render'strStatic'quoted x =-  "\"" <> str'escape x <> "\""---- | Render a dynamic string, in unquoted form if possible.-render'strDynamic'unquotedIfPossible :: Render Str'Dynamic-render'strDynamic'unquotedIfPossible d =-  case str'dynamicToStatic d of-    Just s  -> render'strStatic'unquotedIfPossible s-    Nothing -> render'strDynamic'quoted d---- | Render a dynamic string, in quoted form.-render'strDynamic'quoted :: Render Str'Dynamic-render'strDynamic'quoted xs =-  "\"" <> foldMap r (strDynamic'toSeq xs) <> "\""-  where-    r = \case-      Str'1'Literal x   -> str'escape x-      Str'1'Antiquote x -> "${" <> render'expression x <> "}"---- | Render one line of an indented string ('InStr').-render'inStr'1 :: Render InStr'1-render'inStr'1 (InStr'1 n xs) =-  Text.replicate (fromIntegral n) " " <> foldMap r (strDynamic'toSeq xs)-  where-    r = \case-      Str'1'Literal x -> x-      Str'1'Antiquote x -> "${" <> render'expression x <> "}"---- | Render a lambda parameter: everything from the beginning of a lambda, up--- to but not including the @:@ that separates the head from the body of the--- lambda.-render'param :: Render Param-render'param =-  \case-    Param'Name a         -> render'strUnquoted a-    Param'DictPattern b -> render'dictPattern b-    Param'Both a b      -> render'strUnquoted a <> "@" <>-                           render'dictPattern b---- | Render a dict pattern (@{ a, b ? c, ... }@).-render'dictPattern :: Render DictPattern-render'dictPattern (DictPattern bs e) =-  if Seq.null xs-    then "{ }"-    else "{ " <> Text.intercalate ", " xs <> " }"-  where-    xs =-      Seq.map render'dictPattern'1 bs <>-      if e then Seq.singleton "..." else Seq.empty---- | Render a single item in a 'DictPattern'.-render'dictPattern'1 :: Render DictPattern'1-render'dictPattern'1 =-  \case-    DictPattern'1 a Nothing  -> render'strUnquoted a-    DictPattern'1 a (Just b) -> render'strUnquoted a <> " ? " <> render'expression b---- | Render a lambda expression (@x: y@).-render'lambda :: Render Lambda-render'lambda (Lambda a b) =-  render'param a <> ": " <> render'expression b---- | Render a function application expression (@f x@).-render'apply :: Render Apply-render'apply (Apply a b) =-  render'expression'applyLeftContext a <> " " <>-  render'expression'applyRightContext b---- | Render a list literal (@[ ... ]@).-render'list :: Render List-render'list (List xs) =-  "[ " <> r xs <> "]"-  where-    r = Text.concat . fmap (\x -> render'expression'listContext x <> " ")---- | Render a dict literal (@{ ... }@).-render'dict :: Render Dict-render'dict =-  \case-    Dict False bs ->     "{ " <> r bs <> "}"-    Dict True  bs -> "rec { " <> r bs <> "}"-  where-    r = Text.concat . fmap (\b -> render'dictBinding b <> "; ")---- | Render a binding within a 'Dict', without the trailing semicolon.-render'dictBinding :: Render DictBinding-render'dictBinding =-  \case-    DictBinding'Eq a b ->-      render'expression'dictKey a <> " = " <> render'expression b-    DictBinding'Inherit x ->-      render'inherit x---- | Render a dot expression (@a.b@).-render'dot :: Render Dot-render'dot (Dot a b) =-  render'expression'dotLeftContext a <> "." <> render'expression'dictKey b---- | Render a @let@-@in@ expression.-render'let :: Render Let-render'let (Let bs x) =-  "let " <> r bs <> "in " <> render'expression x-  where-    r = Text.concat . fmap (\b -> render'letBinding b <> "; ")---- | Render a binding within a 'Let', without the trailing semicolon.-render'letBinding :: Render LetBinding-render'letBinding =-  \case-    LetBinding'Eq a b ->-      render'strStatic'unquotedIfPossible a <> " = " <> render'expression b-    LetBinding'Inherit x ->-      render'inherit x--render'inherit :: Render Inherit-render'inherit =-  \case-    Inherit Nothing xs  -> "inherit" <> r xs-    Inherit (Just a) xs -> "inherit (" <> render'expression a <> ")" <> r xs-  where-    r = foldMap (\x -> " " <> render'strStatic'unquotedIfPossible x)---- | Render a @with@ expression.-render'with :: Render With-render'with (With a b) =-  keywordText keyword'with <> " " <>-  render'expression a <> "; " <>-  render'expression b---- | Render an expression.-render'expression :: Render Expression-render'expression =-  \case-    Expr'Str    x -> render'strDynamic'quoted x-    Expr'Dict   x -> render'dict x-    Expr'List   x -> render'list x-    Expr'Var    x -> render'strUnquoted x-    Expr'Dot    x -> render'dot x-    Expr'Lambda x -> render'lambda x-    Expr'Apply  x -> render'apply x-    Expr'Let    x -> render'let x-    Expr'With   x -> render'with x---- | Render an expression in a list context.-render'expression'listContext :: Render Expression-render'expression'listContext x =-  case x of-    Expr'Lambda _ -> render'expression'inParens x-    Expr'Apply  _ -> render'expression'inParens x-    Expr'Let    _ -> render'expression'inParens x-    Expr'With   _ -> render'expression'inParens x-    _             -> render'expression x---- | Render an expression in the context of the left-hand side of a 'Dot'.-render'expression'dotLeftContext :: Render Expression-render'expression'dotLeftContext = render'expression'listContext---- | Render an expression in the context of the left-hand side of an 'Apply'.-render'expression'applyLeftContext :: Render Expression-render'expression'applyLeftContext x =-  case x of-    Expr'Lambda _ -> render'expression'inParens x-    Expr'Let    _ -> render'expression'inParens x-    Expr'With   _ -> render'expression'inParens x-    _             -> render'expression x---- | Render an expression in the context of the right-hand side of an 'Apply'.-render'expression'applyRightContext :: Render Expression-render'expression'applyRightContext x =-  case x of-    Expr'Apply  _ -> render'expression'inParens x-    Expr'Let    _ -> render'expression'inParens x-    Expr'With   _ -> render'expression'inParens x-    _             -> render'expression x--render'expression'inParens :: Render Expression-render'expression'inParens x =-  "(" <> render'expression x <> ")"--render'expression'dictKey :: Render Expression-render'expression'dictKey = \case-  Expr'Str x -> render'strDynamic'unquotedIfPossible x-  x -> "${" <> render'expression x <> "}"
+ src/Bricks/Term.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE LambdaCase            #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NoImplicitPrelude     #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeApplications      #-}++module Bricks.Term where++-- Bricks internal+import Bricks.Internal.Monad+import Bricks.Internal.Prelude+import Bricks.Internal.Seq     (Seq)+import Bricks.Internal.Text    (Text)++-- Containers+import Data.Map (Map)+import Data.Set (Set)++-- Base+import Data.Dynamic (Dynamic)+import Data.IORef++data Bottom = Bottom Text++displayBottom :: Bottom -> Text+displayBottom (Bottom message) = "Error: " <> message++class (Monad m, MonadIO m, MonadError Bottom m) => MonadEval m+  where+    reduce'term :: Term -> m Term+    reduce'dict'keys :: Term -> m (Map Text Term)++type Function = forall m. MonadEval m => Term -> m Term++data Term+  = Term'Data Text Dynamic+      -- ^ The name of the data type, and a value of that type.+  | Term'Function Function+  | Term'Lambda TermPattern Term+      -- ^ The head and body of a lambda expression.+  | Term'LetRec (Map Text Term) Term+  | Term'List (Seq Term)+  | Term'Dict (Seq (Term, Term))+  | Term'Dict'ReducedKeys (Map Text Term)+  | Term'Var Text+  | Term'Apply Term Term+  | Term'Pointer TermPtr++{- | Alias for 'Term'Apply'. The name is an allusion to the AST depictions in+/The Implementation of Functional Programming Languages/, where "/f/ applied to+/x/" is drawn as:++>   @+>  ╱ ╲+> f   x++For a function of two parameters, see the corresponding '/@@\' operator. -}++(/@\) :: Term -> Term -> Term+(/@\) = Term'Apply+infixl /@\++{- | Like '/@\', but for a function applied to two arguments. Depicted as an+abstract syntax tree, @f /\@\@\\ (x, y)@ looks like this:++>     @+>    ╱ ╲+>   @   y+>  ╱ ╲+> f   x++-}++(/@@\) :: Term -> (Term, Term) -> Term+f /@@\ (x, y) = (f /@\ x) /@\ y+infixl /@@\++{- | Alias for 'Term'Lambda'. -}++(|->) :: TermPattern -> Term -> Term+(|->) = Term'Lambda+infixl |->++data TermPattern+  = TermPattern'Simple Text+  | TermPattern'Dict (Set Text)++type TermPtr = IORef Term++create'pointer :: MonadIO m => Term -> m Term+create'pointer x = case x of+  Term'Pointer _ -> pure x  -- The term is already a pointer, don't make another+  _              -> Term'Pointer <$> liftIO (newIORef x)++dereference :: MonadIO m => Term -> m Term+dereference = \case+  Term'Pointer p -> readTermPtr p >>= dereference+  x -> pure x++newTermPtr :: MonadIO m => Term -> m Term+newTermPtr x = case x of+   Term'Pointer _ -> pure x  -- The term is already a pointer, nothing to do+   _              -> Term'Pointer <$> liftIO (newIORef x)++readTermPtr :: MonadIO m => TermPtr -> m Term+readTermPtr = liftIO . readIORef++writeTermPtr :: MonadIO m => TermPtr -> Term -> m ()+writeTermPtr ptr val = do+  x <- dereference val+  liftIO $ writeIORef ptr x++bottom :: MonadError Bottom m => Bottom -> m a+bottom = throwError
+ src/Bricks/Type.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase                 #-}+{-# LANGUAGE NoImplicitPrelude          #-}+{-# LANGUAGE OverloadedStrings          #-}+{-# LANGUAGE ScopedTypeVariables        #-}+{-# LANGUAGE TypeApplications           #-}++module Bricks.Type where++-- Bricks+import Bricks.Term++-- Bricks internal+import Bricks.Internal.Monad+import Bricks.Internal.Prelude+import Bricks.Internal.Text    (Text)++-- Base+import Prelude (Integer)++data Type a = Type { type'name :: Text }++type'boolean :: Type Bool+type'boolean = Type "boolean"++type'string :: Type Text+type'string = Type "string"++type'integer :: Type Integer+type'integer = Type "integer"++termTypeName :: MonadIO m => Term -> m Text+termTypeName = \case+  Term'Data x _ -> pure x+  Term'Function{} -> pure "built-in function"+  Term'Lambda{} -> pure "lambda"+  Term'LetRec{} -> pure "recursive let"+  Term'List{} -> pure "list"+  Term'Dict{} -> pure "dict"+  Term'Dict'ReducedKeys{} -> pure "dict with reduced keys"+  Term'Var{} -> pure "variable"+  Term'Apply{} -> pure "function application"+  Term'Pointer p -> readTermPtr p >>= termTypeName
− src/Bricks/UnquotedString.hs
@@ -1,83 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedStrings #-}--module Bricks.UnquotedString-  (-  -- * Type-    Str'Unquoted (..)--  -- * Constructor-  , str'tryUnquoted-  , str'unquoted'orThrow--  -- * Predicates-  , str'canRenderUnquoted-  , char'canRenderUnquoted--  ) 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 'str'canRenderUnquoted' for the full-rules.--The constructor is tagged "unsafe" because it lets you construct and invalid-value. Prefer 'str'tryUnquoted' which does validate the text. -}-newtype Str'Unquoted = Str'Unquoted'Unsafe { str'unquotedToStatic :: Text }--instance Show Str'Unquoted-  where-    showsPrec _ x = ("unquoted " <>) . shows (str'unquotedToStatic x)--str'tryUnquoted :: Text -> Maybe Str'Unquoted-str'tryUnquoted x =-  if str'canRenderUnquoted x then Just (Str'Unquoted'Unsafe x) else Nothing---- | Throws an exception if the string cannot render unquoted.-str'unquoted'orThrow :: Text -> Str'Unquoted-str'unquoted'orThrow x =-  if str'canRenderUnquoted x then Str'Unquoted'Unsafe x else-  error $ "String " <> show x <> " cannot render unquoted"--{- | Whether a string having this name can be rendered without quoting it.-We allow a string to render unquoted if all these conditions are met:--- The string is nonempty-- All characters satify 'char'canRenderUnquoted'-- The string is not a keyword-->>> str'canRenderUnquoted "-ab_c"-True-->>> str'canRenderUnquoted ""-False-->>> str'canRenderUnquoted "a\"b"-False-->>> str'canRenderUnquoted "let"-False---}-str'canRenderUnquoted :: Text -> Bool-str'canRenderUnquoted x =-  Text.all char'canRenderUnquoted x-  && not (Text.null x)-  && List.all ((/= x) . keywordText) keywords---- | Letters, @-@, and @_@.-char'canRenderUnquoted :: Char -> Bool-char'canRenderUnquoted c =-  Char.isLetter c || c == '-' || c == '_'
− test/Bricks/Test/Hedgehog.hs
@@ -1,23 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Bricks.Test.Hedgehog-  ( runTests-  ) where---- Base-import           Control.Monad (unless)-import           Data.Foldable (for_)-import qualified System.Exit   as Exit-import qualified System.IO     as IO---- Hedgehog-import qualified Hedgehog--runTests :: Hedgehog.Group -> IO ()-runTests tests =-  do-    for_ [IO.stdout, IO.stderr] $ \h -> do-      IO.hSetEncoding h IO.utf8-      IO.hSetBuffering h IO.LineBuffering-    success <- Hedgehog.checkParallel tests-    unless success Exit.exitFailure
− test/Bricks/Test/QQ.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Bricks.Test.QQ-  ( text-  ) where---- Base-import Control.Arrow ((>>>))-import Data.Function (const)---- Template Haskell-import Language.Haskell.TH-import Language.Haskell.TH.Quote---- Text-import qualified Data.Text as Text--text :: QuasiQuoter-text =-  QuasiQuoter-    { quoteExp  = pure . LitE . StringL . stripMargin-    , quotePat  = err-    , quoteType = err-    , quoteDec  = err-    }-  where-    err = const . fail $ "illegal text QuasiQuote (allowed as expression only)"--stripMargin :: String -> String-stripMargin =-  Text.pack-  >>> Text.splitOn "\n"-  >>> fmap (\x ->-        let (a, b) = Text.breakOn "|" x-        in  if Text.all (== ' ') a && not (Text.null b)-            then Text.drop 1 b-            else x)-  >>> Text.intercalate "\n"-  >>> Text.unpack
+ test/evaluation.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes       #-}+{-# LANGUAGE TemplateHaskell   #-}+{-# LANGUAGE TypeApplications  #-}++-- Bricks+import Bricks.Prelude+import Bricks.Type++-- Bricks internal+import Bricks.Internal.Monad+import Bricks.Internal.Prelude++-- Bricks test+import Bricks.Test.Hedgehog+import Bricks.Test.QQ++-- Hedgehog+import           Hedgehog (Property, property, withTests, (===))+import qualified Hedgehog++-- Base+import System.IO (IO)++main :: IO ()+main = runTests $$(Hedgehog.discover)++prop_str :: Property+prop_str = withTests 1 $ property $ do++  do+    x <- liftIO $ bricks'eval type'string [text|"abc"|]+    x === "abc"++  do+    x <- liftIO $ bricks'eval type'string [text|''a${"b"}c''|]+    x === "abc"++{-+prop_dict :: Property+prop_dict = withTests 1 $ property $ do++  x <- liftIO $ bricks'eval type'string [text|{ a = "1"; }.a|]+  x === "1"++  x <- liftIO $ bricks'eval type'string [text|{ "a b" = "1"; }."a b"|]+  x === "1"+-}++prop_add :: Property+prop_add = withTests 1 $ property $ do++  do+    x <- liftIO $ bricks'eval'stdlib type'integer+      [text|┃{ add, integer, ... }:+            ┃add (integer "1") (integer "2")|]+    x === 3++  do+    x <- liftIO $ bricks'eval'stdlib type'integer+      [text|┃lib:+            ┃let+            ┃  inherit (lib) add integer;+            ┃in+            ┃  add (integer "1") (integer "2")|]+    x === 3
− test/parsing.hs
@@ -1,527 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes       #-}-{-# LANGUAGE TemplateHaskell   #-}---- Bricks-import Bricks---- Bricks internal-import           Bricks.Internal.Prelude-import           Bricks.Internal.Text    (Text)-import qualified Bricks.Internal.Text    as Text---- Bricks test-import Bricks.Test.Hedgehog-import Bricks.Test.QQ---- Parsec-import qualified Text.Parsec      as P-import           Text.Parsec.Text (Parser)---- Hedgehog-import           Hedgehog (Property, property, (===))-import qualified Hedgehog---- Base-import System.IO (IO)-import Text.Show (show)--main :: IO ()-main = runTests $$(Hedgehog.discover)--{- | We'll use the @parseTest@ function a lot to test parsers. It's a bit like-'P.parseTest' from the Parsec library, but it works on parsers of type 'Text'-rather than @'Show' a => a@. It also prints the unparsed input so we can verify-that our parser consumes the right amount of input, and it prints a message if-the parser fails and consumes input. -}-parseTest :: Parser Text -> Text -> Text-parseTest p input =-  Text.intercalate "\n" $-  catMaybes--    [ Just $ case P.parse p "" input of-        Left err ->-          "Parse error at " <>-          Text.replace "\n" "\n - " (Text.pack (show err))-        Right x -> x--    , let-        p' = do-          _ <- p-          r <- P.many1 P.anyChar-          pure $ "Remaining input: " <> Text.pack (show r)-      in-        case P.parse p' "" input of-          Left _  -> Nothing-          Right x -> Just x--    , case P.parse (void p <|> pure ()) "" input of-        Left _  -> Just "Parser failed and consumed input"-        Right _ -> Nothing--    ]--prop_parse_str_unquoted :: Property-prop_parse_str_unquoted = property $ do--  let test = parseTest $ fmap str'unquotedToStatic $ parse'strUnquoted--  test "-ab_c" === [text|-ab_c|]--  test ""      === [text|Parse error at (line 1, column 1):-                        | - unexpected end of input|]--  test "a\"b"  === [text|a-                        |Remaining input: "\"b"|]--  test "a b"   === [text|a-                        |Remaining input: "b"|]--  -- The unquoted string parser doesn't backtrack.-  -- Note how in this example it fails and consumes input.-  test "rec { }" === [text|Parse error at (line 1, column 4):-                          | - unexpected " "-                          |Parser failed and consumed input|]--prop_parse_expression_dictKey :: Property-prop_parse_expression_dictKey = property $ do--  let test = parseTest $ fmap render'expression $ parse'expression'dictKey--  test "a"      === [text|"a"|]--  test "\"a\""  === [text|"a"|]--  test "a b"    === [text|"a"-                         |Remaining input: "b"|]--  test "${a.b}" === [text|a.b|]--prop_parse_strDynamic_normalQ :: Property-prop_parse_strDynamic_normalQ = property $ do--  let test = parseTest-           $ fmap render'strDynamic'quoted-           $ parse'strDynamic'normalQ--  test "\"a\""        === [text|"a"|]--  test "\"a\" x"      === [text|"a"-                               |Remaining input: "x"|]--  test "\"a ${b} c\"" === [text|"a ${b} c"|]--  test "\"a${ b }c\"" === [text|"a${b}c"|]--  test "\"$\""        === [text|"$"|]--  test "\"a$\""       === [text|"a$"|]--  test "\"\\${\""     === [text|"\${"|]--  test "\"a\\${\""    === [text|"a\${"|]--prop_parse_strDynamic_indentedQ :: Property-prop_parse_strDynamic_indentedQ = property $ do--  let test = parseTest-           $ fmap render'strDynamic'quoted-           $ P.spaces *> parse'strDynamic'indentedQ--  test "''hello''x"    === [text|"hello"-                                |Remaining input: "x"|]--  test "''hello'' x"   === [text|"hello"-                                |Remaining input: "x"|]--  test [text|  ''-            |    one-            |    two-            |  ''x|] === [text|"one\ntwo"-                              |Remaining input: "x"|]--  test [text|  ''-            |    one-            |-            |    two-            |  ''x|] === [text|"one\n\ntwo"-                              |Remaining input: "x"|]--prop_parse_inStr :: Property-prop_parse_inStr = property $ do--  let test = parseTest-           $ fmap (Text.pack . show . fmap render'inStr'1 . inStr'toList)-           $ P.spaces *> parse'inStr--  test [text|  ''-            |    one-            |    two-            |  ''x|] === [text|["","    one","    two","  "]-                              |Remaining input: "x"|]--  test [text|  ''-            |    one-            |-            |    two-            |  ''x|] === [text|["","    one","","    two","  "]-                              |Remaining input: "x"|]--  test "'''' x"   === [text|[""]-                           |Remaining input: "x"|]--  test "''abc''"  === [text|["abc"]|]--  test "''\n''"   === [text|["",""]|]--  test "''  \n''" === [text|["  ",""]|]--  test "''   abc\ndef''" === [text|["   abc","def"]|]--prop_parse_inStr_line :: Property-prop_parse_inStr_line = property $ do--  let test = parseTest-           $ fmap (Text.pack . show . render'inStr'1)-           $ parse'inStr'1--  test "abc" === [text|Parse error at (line 1, column 4):-                      | - unexpected end of input-                      | - expecting "$", "'", "\n", "''" or "${"-                      |Parser failed and consumed input|]--  test "\n"   === [text|""-                       |Remaining input: "\n"|]--  test "  \n" === [text|"  "-                       |Remaining input: "\n"|]--  test "   abc\ndef" === [text|"   abc"-                              |Remaining input: "\ndef"|]--  test "   abc''x"   === [text|"   abc"-                              |Remaining input: "''x"|]--prop_parse_dict_pattern_start :: Property-prop_parse_dict_pattern_start = property $ do--  let test = parseTest $ (P.try parse'dictPattern'start $> "yes") <|> pure "no"--  test "{a, b}:"     === [text|yes-                              |Remaining input: " b}:"|]--  test "{a ? 4, b}:" === [text|yes-                              |Remaining input: " 4, b}:"|]--  test "{ }: x"      === [text|yes-                              |Remaining input: " x"|]--  -- { } is not enough to determine whether we're parsing a dict param, because-  -- if it isn't followed by a colon, then it's actually an empty dict literal.-  test "{ } x"       === [text|no-                              |Remaining input: "{ } x"|]--  test "{ ... }:"    === [text|yes-                              |Remaining input: " }:"|]--prop_parse_dict_pattern :: Property-prop_parse_dict_pattern = property $ do--  let test = parseTest $ fmap render'dictPattern $ parse'dictPattern--  test "{}"               === [text|{ }|]-  test "{...}"            === [text|{ ... }|]-  test "{a}"              === [text|{ a }|]-  test "{a , b}"          === [text|{ a, b }|]-  test "{a , b ? c, ...}" === [text|{ a, b ? c, ... }|]--prop_parse_dot_rhs_chain :: Property-prop_parse_dot_rhs_chain = property $ do--  let test = parseTest-           $ fmap (Text.intercalate "\n" . fmap render'expression)-           $ parse'dot'rhs'chain--  -- The dots parser /does/ match the empty string.-  test ""         === [text||]--  -- The simplest nonempty dot list-  test ".a"       === [text|"a"|]--  -- The dots parser consumes any trailing whitespace beyond the dot list.-  test ".a "      === [text|"a"|]--  -- Dot attributes are usually unquoted strings, but they may also be quoted.-  test ".\"a\""   === [text|"a"|]-  test ". \"a\""  === [text|"a"|]--  -- Here we throw some extra whitespace into the middle, which makes no-  -- difference, and some extra stuff onto the end, which does not get consumed.-  test ".a . b c" === [text|"a"-                           |"b"-                           |Remaining input: "c"|]--  -- Another example of a quoted dot, this time following an unquoted dot-  test ".a.\"b\"" === [text|"a"-                           |"b"|]--  -- If quotes or braces are involved, the stuff that follows a dot expression-  -- can directly abut it with no whitespace in between.-  test [text|.a."b"x|] === [text|"a"-                                |"b"-                                |Remaining input: "x"|]--  test [text|.a.b"x"|] === [text|"a"-                                |"b"-                                |Remaining input: "\"x\""|]--  test ".a.b(x)"   === [text|"a"-                            |"b"-                            |Remaining input: "(x)"|]--  test ". a . b"   === [text|"a"-                            |"b"|]--  test ". \"a\".b" === [text|"a"-                            |"b"|]--  test ". \"a\".${b}" === [text|"a"-                               |b|]--prop_parse_expression :: Property-prop_parse_expression = property $ do--  let test = parseTest $ fmap render'expression $ parse'expression--  -- The empty string is /not/ a valid expression.-  test "" === [text|Parse error at (line 1, column 1):-                   | - unexpected end of input-                   | - expecting expression|]--  -- A very simple expression: a one-letter variable-  test "a"         === [text|a|]--  -- Parsing an expression consumes any subsequent whitespace.-  test "a "        === [text|a|]--  -- When there are multiple expressions, that is parsed as a function call.-  test "f x"       === [text|f x|]--  -- Expressions can directly abut each other, so it's important that the-  -- expression parser is also able to read an expression even when another-  -- expression directly follows it.-  test "f[x y]"    === [text|f [ x y ]|]--  -- A simple example of parsing a dot expression-  test "a.b"       === [text|a.b|]--  -- Dot parsing also consumes trailing whitespace.-  test "a.b "      === [text|a.b|]--  -- It looks odd when a subsequent expression appears after a dot expression-  -- with no whitespace, but it is permitted.-  test "a.b\"c\""  === [text|a.b "c"|]--  -- A simple list example-  test "[ a b ]"   === [text|[ a b ]|]--  -- A list with trailing whitespace that get consumed-  test "[ a b ] "  === [text|[ a b ]|]--  -- A list that is in the left-hand side of a function call. This will fail at-  -- runtime if the call is evaluated, because a list is not a function, but it-  -- should /parse/ successfully.-  test "[ a b ] x" === [text|[ a b ] x|]--  -- The same thing with other weird stuff on the left-hand side of a function-  -- call.-  test "{ a = b; } x" === [text|{ a = b; } x|]--  test "{ } x"        === [text|{ } x|]--  -- Note that the case where an empty dict is on the left-hand side of a-  -- function call looks very similar to the case where a function expression-  -- using dict deconstruction with no bindings. The only difference is the-  -- colon.-  test "{ }: x"      === [text|{ }: x|]--  -- A list with a function call inside-  test "[ (f x) ]"   === [text|[ (f x) ]|]--  test "[ a (f x) ]" === [text|[ a (f x) ]|]--  -- A minimal dict literal-  test "{ x = y; }"  === [text|{ x = y; }|]--  -- The left-hand side of a dict binding is allowed to be any expression.-  test "{ \"a b\" = y; }"  === [text|{ "a b" = y; }|]-  test "{ ${x} = y; }"     === [text|{ ${x} = y; }|]--  -- It may even be the empty string.-  test "{ \"\" = y; }"     === [text|{ "" = y; }|]--  -- None of the conventional whitespace within a dict literal is mandatory.-  test "{x=y;}"            === [text|{ x = y; }|]--  -- A simple dict literal with two bindings-  test "{ x = y; a = b; }" === [text|{ x = y; a = b; }|]--  -- The same thing without any whitespace-  test "{x=y;a=b;}" === [text|{ x = y; a = b; }|]--  -- Dicts with 'inherit' bindings-  test "{ inherit a; }"       === [text|{ inherit a; }|]-  test "{ inherit a b; }"     === [text|{ inherit a b; }|]-  test "{ inherit (x) a b; }" === [text|{ inherit (x) a b; }|]--  -- An inherit binding can be empty, although it is weird.-  test "{ inherit; }"   === [text|{ inherit; }|]--  -- A simple function-  test "x : y" === [text|x: y|]--  -- Whitespace before the colon is unconventional, but allowed.-  test "x : y" === [text|x: y|]--  -- The space after the colon is not mandatory. (In Nix, this example would be-  -- parsed as the string "x:y", but here we do not support URI literals.)-  test "x:y"   === [text|x: y|]--  -- A slightly bigger example where we're starting to nest more things-  test "[ \"abc\" f { x = y; } ]"   === [text|[ "abc" f { x = y; } ]|]--  test "[ \"abc\" (f { x = y; }) ]" === [text|[ "abc" (f { x = y; }) ]|]--  -- This is not valid a expression (though the first bit of it is).-  test "a b: c"   === [text|a b-                           |Remaining input: ": c"|]--  -- This is not a valid expression.-  test "(a b: c)" === [text|Parse error at (line 1, column 5):-                           | - unexpected ":"-                           | - expecting expression list item or ")"-                           |Parser failed and consumed input|]--  -- Some functions that use dict deconstruction-  test "{ a, b, c ? x, ... }: g b (f a c)"-    === [text|{ a, b, c ? x, ... }: g b (f a c)|]--  test "{ x, ... }: f x"    === [text|{ x, ... }: f x|]-  test "{ x?\"abc\" }: x"   === [text|{ x ? "abc" }: x|]-  test "{ ... }: x"         === [text|{ ... }: x|]-  test "a@{ x, ... }: f x"  === [text|a@{ x, ... }: f x|]-  test "a@{ x?\"abc\" }: x" === [text|a@{ x ? "abc" }: x|]-  test "a@{ ... }: x"       === [text|a@{ ... }: x|]--  -- A let expression-  test "let f = x: plus one x; in f seven"-    === [text|let f = x: plus one x; in f seven|]--  -- A let binding list may be empty, although it is silly.-  test "let in f x" === [text|let in f x|]--  test "with x; y"  === [text|with x; y|]--  test "with{x=y;}; f x z" === [text|with { x = y; }; f x z|]--  -- Indented strings do not support any escape sequences.-  test [text|''-            |  There \ is \n no \$ escape.-            |''|] === [text|"There \\ is \\n no \\$ escape."|]--  -- Therefore if you want to include something like '' in an indented string,-  -- you have to put it inside an antiquote.-  test [text|''-            |  Isn't it-            |  ${"''"}interesting-            |''-            |] === [text|"Isn't it\n''interesting"|]--  -- Comments-  test [text|let                -- hi-            |  x {- ! -} = "a"; -- yep-            | in                -- lol-            |   f x-            |] === [text|let x = "a"; in f x|]--  -- Nested block comments-  test [text|f{- a-            |  -- b-            |    {- c {- d-            |    -}-} e-            |  -}x|] === "f x"--prop_parse_expression_list :: Property-prop_parse_expression_list = property $ do--  let test = parseTest-           $ fmap (Text.intercalate "\n" . fmap render'expression)-           $ parse'expressionList--  test ""                       === [text||]--  test "x y z"                  === [text|x-                                         |y-                                         |z|]---  test "(a)b c(d)"              === [text|a-                                         |b-                                         |c-                                         |d|]--  test "a.\"b\"c"               === [text|a.b-                                         |c|]--  test "\"abc\" (f { x = y; })" === [text|"abc"-                                         |f { x = y; }|]--  -- Parsing lists of variables that are similar to keywords-  test "r re reck"              === [text|r-                                         |re-                                         |reck|]-  test "r re rec { } reck"      === [text|r-                                         |re-                                         |rec { }-                                         |reck|]-  test "l le lets"              === [text|l-                                         |le-                                         |lets|]-  test "i ins"                  === [text|i-                                         |ins|]-  test "wi wit withs"           === [text|wi-                                         |wit-                                         |withs|]-  test "inheri inherits"        === [text|inheri-                                         |inherits|]--prop_parse_expression_list_item :: Property-prop_parse_expression_list_item = property $ do--  let test = parseTest $ fmap render'expression $ parse'expressionList'1--  test "abc def"  === [text|abc-                           |Remaining input: "def"|]-  test "a.b c"    === [text|a.b-                           |Remaining input: "c"|]-  test "a.\"b\"c" === [text|a.b-                           |Remaining input: "c"|]-  test "(a.b)c"   === [text|a.b-                           |Remaining input: "c"|]-  test "a.b(c)"   === [text|a.b-                           |Remaining input: "(c)"|]-  test "[ a b ]c" === [text|[ a b ]-                           |Remaining input: "c"|]-  test "a[ b c ]" === [text|a-                           |Remaining input: "[ b c ]"|]-  test "\"a\"b"   === [text|"a"-                           |Remaining input: "b"|]--prop_parse_expression_list_item_no_dot :: Property-prop_parse_expression_list_item_no_dot = property $ do--  let test = parseTest $ fmap render'expression $ parse'expressionList'1'noDot--  test "a.b c" === [text|a-                        |Remaining input: ".b c"|]
− test/rendering.hs
@@ -1,123 +0,0 @@-{-# LANGUAGE NoImplicitPrelude #-}-{-# LANGUAGE OverloadedLists   #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes       #-}-{-# LANGUAGE TemplateHaskell   #-}---- Bricks-import Bricks-import Bricks.Expression.Construction---- Bricks internal-import Bricks.Internal.Prelude---- Bricks test-import Bricks.Test.Hedgehog-import Bricks.Test.QQ---- Hedgehog-import           Hedgehog (Property, property, (===))-import qualified Hedgehog---- Base-import System.IO (IO)--main :: IO ()-main = runTests $$(Hedgehog.discover)--prop_render_expression :: Property-prop_render_expression = property $ do--  render'expression (dot (var "a") (str ["b"])) === [text|a.b|]--  render'expression (dot (var "a") (var "b"))   === [text|a.${b}|]--  render'expression (dot (str ["a"])-    (str ["b", antiquote (var "c")]))           === [text|"a"."b${c}"|]--  render'expression-    (lambda-      (param "a" <> pattern-        [ param "f"-        , param "b" & def (apply (var "g") (var "x"))-        ] <> ellipsis)-      (apply (var "f") (var "b")))-    === [text|a@{ f, b ? g x, ... }: f b|]--  render'expression-    (let'in-      [ binding "d" (dict-        [ binding (str ["a"]) (str ["b", antiquote (var "c")])-        , inherit'from (var "x") ["y", "z"]-        ])]-      (dot (var "d") (str ["y"])))-    === [text|let d = { a = "b${c}"; inherit (x) y z; }; in d.y|]--prop_render_identifier :: Property-prop_render_identifier = property $ do--  let test = render'strStatic'unquotedIfPossible--  test "abc"  === [text|abc|]-  test "a\"b" === [text|"a\"b"|]-  test "-ab"  === [text|-ab|]-  test ""     === [text|""|]--prop_render_string_dynamic_quoted :: Property-prop_render_string_dynamic_quoted = property $ do--  let test = render'strDynamic'quoted . strDynamic'fromList--  test []                        === [text|""|]-  test [ Str'1'Literal "hello" ] === [text|"hello"|]--  test [ Str'1'Literal "escape ${ this and \" this" ]-    === [text|"escape \${ this and \" this"|]--  test [ Str'1'Literal "Hello, my name is "-       , Str'1'Antiquote (Expr'Var (Str'Unquoted'Unsafe "name"))-       , Str'1'Literal "!"-       ]-    === [text|"Hello, my name is ${name}!"|]--prop_render_indented_string_line :: Property-prop_render_indented_string_line = property $ do--  let test n xs = render'inStr'1 $ InStr'1 n (strDynamic'fromList xs)--  test 2 [ Str'1'Literal "abc"-         , Str'1'Antiquote (Expr'Var $ Str'Unquoted'Unsafe "x")-         ]-    === [text|  abc${x}|]--prop_render_dict_pattern :: Property-prop_render_dict_pattern = property $ do--  let test a b = render'dictPattern $ DictPattern a b--  test [] False === [text|{ }|]-  test [] True  === [text|{ ... }|]--  let-    item1 = DictPattern'1 (Str'Unquoted'Unsafe "x") Nothing-    item2 = DictPattern'1 (Str'Unquoted'Unsafe "y") $-      Just $ Expr'Str (strDynamic'singleton (Str'1'Literal "abc"))--  test [ item1, item2 ] False === [text|{ x, y ? "abc" }|]-  test [ item1, item2 ] True  === [text|{ x, y ? "abc", ... }|]--prop_render_list :: Property-prop_render_list = property $ do--  let test = render'list . List--  test []                                     === [text|[ ]|]-  test [ Expr'Var (Str'Unquoted'Unsafe "a") ] === [text|[ a ]|]-  test [ Expr'Var (Str'Unquoted'Unsafe "a")-       , Expr'Var (Str'Unquoted'Unsafe "b") ] === [text|[ a b ]|]--  let call = Expr'Apply $ Apply (Expr'Var (Str'Unquoted'Unsafe "f"))-                                (Expr'Var (Str'Unquoted'Unsafe "x"))--  test [ call ]                                     === [text|[ (f x) ]|]-  test [ call, Expr'Var (Str'Unquoted'Unsafe "a") ] === [text|[ (f x) a ]|]