{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} module Bricks.Expression ( -- * Expressions Expression (..) , expression'source , expression'discardSource -- * Variables -- $variables , Var (..) , var'text , var'to'str'static , var'to'str'dynamic , var'discardSource -- * Static strings , Str'Static (..) , str'static'append , str'static'discardSource , str'static'to'dynamic -- * Dynamic strings , Str'Dynamic (..) , Str'1 (..) , str'1'discardSource , str'dynamic'append , str'dynamic'normalize , str'dynamic'discardSource , str'dynamic'to'static -- * Indented string , InStr (..) , inStr'to'strDynamic , inStr'level , inStr'dedent , inStr'trim , inStr'toList , inStr'discardSource -- * Single line of an indented string , InStr'1 (..) , inStr'1'toStrParts , inStr'1'discardSource -- * Lists , List (..) , list'discardSource -- * Dicts , Dict (..) , dict'discardSource , DictBinding (..) , dictBinding'discardSource -- * Dict lookup , Dot (..) , expression'applyDots , dot'discardSource -- * Lambdas , Lambda (..) , lambda'discardSource -- * Function parameters , Param (..) , param'discardSource , DictPattern (..) , dictPattern'discardSource , DictPattern'1 (..) , dictPattern'1'discardSource -- * Function application , Apply (..) , expression'applyArgs , apply'discardSource -- * @let@ , Let (..) , let'discardSource , LetBinding (..) , letBinding'discardSource ) where -- Bricks import Bricks.Source import Bricks.UnquotedString -- Bricks internal import Bricks.Internal.List as List 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 Prelude (Num (..), fromIntegral) data Expression = Expr'Var Var -- ^ A variable, such as @x@ | Expr'Str Str'Dynamic -- ^ A string, quoted in the traditional form using a single -- double-quote (@"@ ... @"@) | Expr'Str'Indented InStr -- ^ A string in \"indented string\" form, using two single-quotes -- (@''@ ... @''@) | Expr'List List -- ^ A list is an ordered collection of expressions. | Expr'Dict Dict -- ^ A dict is an unordered enumerated mapping from strings. | Expr'Dot Dot -- ^ A dot expression (named after the @.@ character it contains) looks up -- the value at a particular key in a dict. | Expr'Lambda Lambda -- ^ A lambda expression @x: y@ where @x@ is the parameter. | Expr'Apply Apply -- ^ The application of a function to a single argument. | Expr'Let Let -- ^ A /let/-/in/ expression consists of a list of variable bindings -- followed by an expression. expression'source :: Expression -> Maybe SourceRange expression'source = \case Expr'Var x -> var'source x Expr'Str x -> strDynamic'source x Expr'Str'Indented x -> inStr'source x Expr'List x -> list'source x Expr'Dict x -> dict'source x Expr'Dot x -> dot'source x Expr'Lambda x -> lambda'source x Expr'Apply x -> apply'source x Expr'Let x -> let'source x expression'discardSource :: Expression -> Expression expression'discardSource = \case Expr'Var x -> Expr'Var $ var'discardSource x Expr'Str x -> Expr'Str $ str'dynamic'discardSource x Expr'Str'Indented x -> Expr'Str'Indented $ inStr'discardSource x Expr'List x -> Expr'List $ list'discardSource x Expr'Dict x -> Expr'Dict $ dict'discardSource x Expr'Dot x -> Expr'Dot $ dot'discardSource x Expr'Lambda x -> Expr'Lambda $ lambda'discardSource x Expr'Apply x -> Expr'Apply $ apply'discardSource x Expr'Let x -> Expr'Let $ let'discardSource x -------------------------------------------------------------------------------- -- Variables -------------------------------------------------------------------------------- {- | A /variable/ @x@, as in the lambda calculus sense, is in one of two positions: 1. A binding, which may take a number of forms: - @x:@ ... ('Param'Name') - @let x =@ ... @; in@ ... ('LetBinding'Eq') - @let inherit (@ ... @) x; in@ ... ('LetBinding'Inhherit') 2. A contextual reference to a lambda head or /let/ binding in which @x@ is bound: - The expression @x@ by itself - An @inherit@ binding in a dict expression ('DictBinding'Inherit'Var') ==== Syntax Variables are always written without quotes. Unquoted strings are used for variables ('Expr'Var') and places that bind variables ('Lambda' and 'Let'). -} data Var = Var { var'str :: UnquotedString , var'source :: Maybe SourceRange } var'text :: Var -> Text var'text (Var x _) = unquotedString'text x var'to'str'static :: Var -> Str'Static var'to'str'static x = Str'Static (var'text x) (var'source x) var'to'str'dynamic :: Var -> Str'Dynamic var'to'str'dynamic = str'static'to'dynamic . var'to'str'static var'discardSource :: Var -> Var var'discardSource x = Var { var'str = var'str x , var'source = Nothing } -------------------------------------------------------------------------------- -- Static strings -------------------------------------------------------------------------------- {- | A fixed string value. We use the description "static" to mean the string may not contain antiquotation, in contrast with 'Str'Dynamic' which can. -} data Str'Static = Str'Static { str'static'text :: Text , str'static'source :: Maybe SourceRange } str'static'append :: Str'Static -> Str'Static -> Str'Static str'static'append (Str'Static t1 s1) (Str'Static t2 s2) = Str'Static (Text.append t1 t2) (sourceRangeMaybe'join s1 s2) instance Semigroup Str'Static where (<>) = str'static'append str'static'discardSource :: Str'Static -> Str'Static str'static'discardSource x = Str'Static { str'static'text = str'static'text x , str'static'source = Nothing } -------------------------------------------------------------------------------- -- Dynamic strings -------------------------------------------------------------------------------- {- | A /dynamic string/ is 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. This is the type of string expressions ('Expr'Str'). ==== String syntax 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. -} data Str'Dynamic = Str'Dynamic { strDynamic'toSeq :: Seq Str'1 , strDynamic'source :: Maybe SourceRange } str'dynamic'discardSource :: Str'Dynamic -> Str'Dynamic str'dynamic'discardSource x = Str'Dynamic { strDynamic'source = Nothing , strDynamic'toSeq = fmap str'1'discardSource (strDynamic'toSeq x) } str'dynamic'append :: Str'Dynamic -> Str'Dynamic -> Str'Dynamic str'dynamic'append (Str'Dynamic x1 y1) (Str'Dynamic x2 y2) = Str'Dynamic (Seq.append x1 x2) (sourceRangeMaybe'join y1 y2) instance Semigroup Str'Dynamic where (<>) = str'dynamic'append {- | One part of a 'Str'Dynamic'. -} data Str'1 = Str'1'Literal Str'Static | Str'1'Antiquote Expression str'1'discardSource :: Str'1 -> Str'1 str'1'discardSource = \case Str'1'Literal x -> Str'1'Literal (str'static'discardSource x) Str'1'Antiquote x -> Str'1'Antiquote (expression'discardSource x) {- | Simplify a dynamic string by combining consecutive pieces of static text. -} -- | ==== Examples -- -- >>> :{ -- >>> str :: Text -> Str'1 -- >>> str x = Str'1'Literal $ Str'Static x Nothing -- >>> -- >>> var :: Text -> Str'1 -- >>> var x = Str'1'Antiquote . Expr'Var $ -- >>> Var (unquotedString'orThrow x) Nothing -- >>> :} -- -- >>> :{ -- >>> str'dynamic'normalize $ Str'Dynamic (Seq.fromList -- >>> [str "a", str "b", var "x", var "y", str "c", str "d"]) Nothing -- >>> :} -- str ["ab", antiquote (var "x"), antiquote (var "y"), "cd"] str'dynamic'normalize :: Str'Dynamic -> Str'Dynamic str'dynamic'normalize s = s{ strDynamic'toSeq = f (strDynamic'toSeq s) } where f = Seq.fromList . List.concat . List.map (\case Right xs -> [Str'1'Literal (List.foldr1 str'static'append xs)] Left xs -> xs ) . List.groupEither . List.map (\case Str'1'Literal x -> Right x x -> Left x ) . Seq.toList -------------------------------------------------------------------------------- -- Indented strings -------------------------------------------------------------------------------- {- | 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. -} data InStr = InStr { inStr'toSeq :: Seq InStr'1 , inStr'source :: Maybe SourceRange } inStr'toList :: InStr -> [InStr'1] inStr'toList = Seq.toList . inStr'toSeq inStr'discardSource :: InStr -> InStr inStr'discardSource x = InStr { inStr'toSeq = fmap inStr'1'discardSource (inStr'toSeq x) , inStr'source = Nothing } {- | 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'indentSource :: Maybe SourceRange -- ^ The source position of the leading space characters , inStr'1'str :: Seq Str'1 -- ^ The meat of the line, after any leading spaces and before the line -- break. , inStr'1'lineBreak :: Maybe Str'Static -- ^ The line break at the end, if any; all lines but the last one -- should have a line break } inStr'1'discardSource :: InStr'1 -> InStr'1 inStr'1'discardSource x = InStr'1 { inStr'1'level = inStr'1'level x , inStr'1'indentSource = Nothing , inStr'1'str = fmap str'1'discardSource (inStr'1'str x) , inStr'1'lineBreak = fmap str'static'discardSource (inStr'1'lineBreak x) } inStr'1'toStrParts :: InStr'1 -> Seq Str'1 inStr'1'toStrParts x = indent <> inStr'1'str x <> end where indent :: Seq Str'1 indent = case inStr'1'level x of 0 -> Seq.empty level -> Seq.singleton . Str'1'Literal $ Str'Static (Text.replicate (fromIntegral level) " ") (inStr'1'indentSource x) end :: Seq Str'1 end = maybe Seq.empty (Seq.singleton . Str'1'Literal) $ inStr'1'lineBreak x {- | Determine how many characters of whitespace to strip from an indented string. -} inStr'level :: InStr -> Natural inStr'level = maybe 0 id . List.minimum . catMaybes . List.map (\x -> if Seq.null (inStr'1'str x) then Nothing else Just (inStr'1'level x) ) . inStr'toList {- | 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 x = let b = inStr'level x in x { inStr'toSeq = inStr'toSeq x <&> (\l -> l { inStr'1'level = let a = inStr'1'level l in if a >= b then a - b else 0 }) } inStr'to'strDynamic :: InStr -> Str'Dynamic inStr'to'strDynamic = inStr'trim >>> inStr'dedent >>> (\inStr -> Str'Dynamic (Seq.concatMap inStr'1'toStrParts (inStr'toSeq inStr)) (inStr'source inStr) ) >>> str'dynamic'normalize {- | Remove any empty lines from the beginning or end of an indented string, and remove the newline from the final nonempty line. -} inStr'trim :: InStr -> InStr inStr'trim x = x { inStr'toSeq = inStr'toSeq x & Seq.trimWhile (Seq.null . inStr'1'str) & Seq.adjustLast (\y -> y { inStr'1'lineBreak = Nothing }) } -------------------------------------------------------------------------------- -- Conversions between types of strings -------------------------------------------------------------------------------- -- | ==== Examples -- -- >>> str'dynamic'to'static $ Str'Dynamic (Seq.fromList []) Nothing -- Just "" -- -- >>> a = Str'1'Literal (Str'Static "hi" Nothing) -- -- >>> b = Str'1'Antiquote $ Expr'Var $ Var (unquotedString'orThrow "x") Nothing -- -- >>> str'dynamic'to'static $ Str'Dynamic (Seq.fromList [ a ]) Nothing -- Just "hi" -- -- >>> str'dynamic'to'static $ Str'Dynamic (Seq.fromList [ a, b ]) Nothing -- Nothing str'dynamic'to'static :: Str'Dynamic -> Maybe Str'Static str'dynamic'to'static x = case Seq.toList (strDynamic'toSeq x) of [] -> Just (Str'Static "" (strDynamic'source x)) [Str'1'Literal a] -> Just (a{ str'static'source = strDynamic'source x }) _ -> Nothing str'static'to'dynamic :: Str'Static -> Str'Dynamic str'static'to'dynamic x = Str'Dynamic (Seq.singleton (Str'1'Literal x)) (str'static'source x) -------------------------------------------------------------------------------- -- Lambda -------------------------------------------------------------------------------- {- | A function expressed as a lambda abstraction. ==== Syntax A lambda expression ('Expr'Lambda') has the form @x: y@ where @x@ is the function parameter to bind in the function body @y@. 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 ] -} data Lambda = Lambda { lambda'head :: Param -- ^ Declaration of the function's parameter , lambda'body :: Expression -- ^ Body of the function; what it evaluates to , lambda'source :: Maybe SourceRange } lambda'discardSource :: Lambda -> Lambda lambda'discardSource x = Lambda { lambda'head = param'discardSource (lambda'head x) , lambda'body = expression'discardSource (lambda'body x) , lambda'source = Nothing } -------------------------------------------------------------------------------- -- Apply -------------------------------------------------------------------------------- {- | The application of a function to a single argument. ==== Syntax An function application expression ('Expr'Apply') looks like this: > f x If a function has multiple (curried) parameters, you can chain them together like so: > f x y z -} data Apply = Apply { apply'func :: Expression -- ^ The function being called , apply'arg :: Expression -- ^ The argument to the function , apply'source :: Maybe SourceRange } expression'applyArgs :: Expression -- ^ Function -> [Expression] -- ^ Args -> Expression -- ^ Function application expression'applyArgs = foldl f where f acc b = Expr'Apply (Apply acc b src) where src = sourceRangeMaybe'join (expression'source acc) (expression'source b) apply'discardSource :: Apply -> Apply apply'discardSource x = Apply { apply'func = expression'discardSource (apply'func x) , apply'arg = expression'discardSource (apply'arg x) , apply'source = Nothing } -------------------------------------------------------------------------------- -- Param -------------------------------------------------------------------------------- {- | 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 Var -- ^ A simple single-parameter function | Param'DictPattern DictPattern -- ^ Dict destructuring, which gives you something resembling multiple -- named parameters with default values | Param'Both Var DictPattern -- ^ Both a param name /and/ a dict pattern, separated by the @\@@ -- keyword param'discardSource :: Param -> Param param'discardSource = \case Param'Name x -> Param'Name (var'discardSource x) Param'DictPattern x -> Param'DictPattern (dictPattern'discardSource x) Param'Both x y -> Param'Both (var'discardSource x) (dictPattern'discardSource y) -------------------------------------------------------------------------------- -- Dict pattern -------------------------------------------------------------------------------- {- | 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 } dictPattern'discardSource :: DictPattern -> DictPattern dictPattern'discardSource x = DictPattern { dictPattern'items = fmap dictPattern'1'discardSource (dictPattern'items x) , dictPattern'ellipsis = dictPattern'ellipsis x } {- | One item within a 'DictPattern'. -} data DictPattern'1 = DictPattern'1 { dictPattern'1'name :: Var -- ^ 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 } dictPattern'1'discardSource :: DictPattern'1 -> DictPattern'1 dictPattern'1'discardSource x = DictPattern'1 { dictPattern'1'name = var'discardSource (dictPattern'1'name x) , dictPattern'1'default = fmap expression'discardSource (dictPattern'1'default x) } -------------------------------------------------------------------------------- -- List -------------------------------------------------------------------------------- {- | A list is an ordered collection. ==== Syntax A list expression ('Expr'List') starts with @[@, ends with @]@, and contains any number of expressions in between. 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) > ] -} data List = List { list'expressions :: Seq Expression , list'source :: Maybe SourceRange } list'discardSource :: List -> List list'discardSource x = List { list'expressions = fmap expression'discardSource (list'expressions x) , list'source = Nothing } -------------------------------------------------------------------------------- -- Dict -------------------------------------------------------------------------------- {- | A dict is an unordered enumerated mapping from strings. ==== Syntax A dict expression ('Expr'Dict') starts with @{@ or @rec {@, ends with @}@, and contains any number of 'DictBinding's in between. 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; } -} 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 @}@) , dict'source :: Maybe SourceRange } dict'discardSource :: Dict -> Dict dict'discardSource x = Dict { dict'rec = dict'rec x , dict'bindings = fmap dictBinding'discardSource (dict'bindings x) , dict'source = Nothing } {- | A binding within a 'Dict'. -} data DictBinding = DictBinding'Eq Expression Expression -- ^ A binding of the form @x = y;@ | DictBinding'Inherit'Dict Expression (Seq Str'Static) | DictBinding'Inherit'Var (Seq Var) dictBinding'discardSource :: DictBinding -> DictBinding dictBinding'discardSource = \case DictBinding'Eq a b -> DictBinding'Eq (expression'discardSource a) (expression'discardSource b) DictBinding'Inherit'Dict a xs -> DictBinding'Inherit'Dict (expression'discardSource a) (fmap str'static'discardSource xs) DictBinding'Inherit'Var xs -> DictBinding'Inherit'Var (fmap var'discardSource xs) -------------------------------------------------------------------------------- -- Dot -------------------------------------------------------------------------------- {- | The /dot/ function looks up a value (or a list of values) from a dict. ==== Syntax A dot expression is named after the @.@ character it contains. @a.b@ looks up value at key @b@ in the dict @a@. 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 } -} data Dot = Dot { dot'dict :: Expression , dot'key :: Expression , dot'source :: Maybe SourceRange } expression'applyDots :: Expression -- ^ Dict -> [Expression] -- ^ Lookups -> Expression -- ^ Dot expression expression'applyDots = foldl f where f acc b = Expr'Dot (Dot acc b src) where src = sourceRangeMaybe'join (expression'source acc) (expression'source b) dot'discardSource :: Dot -> Dot dot'discardSource x = Dot { dot'dict = expression'discardSource (dot'dict x) , dot'key = expression'discardSource (dot'key x) , dot'source = Nothing } -------------------------------------------------------------------------------- -- Let -------------------------------------------------------------------------------- {- | ==== Syntax A /let/-/in/ expression ('Expr'Let') looks like this: > 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. -} 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) , let'source :: Maybe SourceRange } let'discardSource :: Let -> Let let'discardSource x = Let { let'bindings = fmap letBinding'discardSource (let'bindings x) , let'value = expression'discardSource (let'value x) , let'source = Nothing } {- | A semicolon-terminated binding within the binding list of a 'Let' expression. -} data LetBinding = LetBinding'Eq Var Expression -- ^ A binding with an equals sign, of the form @x = y;@ | LetBinding'Inherit Expression (Seq Var) -- ^ A binding using the @inherit@ keyword, of the form @inherit (x) a b;@ letBinding'discardSource :: LetBinding -> LetBinding letBinding'discardSource = \case LetBinding'Eq a b -> LetBinding'Eq (var'discardSource a) (expression'discardSource b) LetBinding'Inherit a b -> LetBinding'Inherit (expression'discardSource a) (fmap var'discardSource 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 show = Text.unpack . show'expression instance Show Var where show = Text.unpack . show'var instance Show Str'Static where show = Text.unpack . show'str'static instance Show Str'Dynamic where show = Text.unpack . show'str'dynamic instance Show Str'1 where show = Text.unpack . show'str'1 instance Show InStr where show = Text.unpack . show'str'indented instance Show InStr'1 where show = Text.unpack . show'str'indented'1 instance Show List where show = Text.unpack . show'list instance Show Dict where show = Text.unpack . show'dict instance Show DictBinding where show = Text.unpack . show'dictBinding instance Show Dot where show = Text.unpack . show'dot instance Show Lambda where show = Text.unpack . show'lambda instance Show Param where show = Text.unpack . show'param instance Show DictPattern where show = Text.unpack . show'dictPattern instance Show DictPattern'1 where show = Text.unpack . show'dictPattern'1 instance Show Apply where show = Text.unpack . show'apply instance Show Let where show = Text.unpack . show'let instance Show LetBinding where show = Text.unpack . show'letBinding show'expression :: Expression -> Text show'expression = \case Expr'Var x -> show'var x Expr'Str x -> show'str'dynamic x Expr'Str'Indented x -> show'str'indented x Expr'List x -> show'list x Expr'Dict x -> show'dict x Expr'Dot x -> show'dot x Expr'Lambda x -> show'lambda x Expr'Apply x -> show'apply x Expr'Let x -> show'let x source'comment :: Maybe SourceRange -> Maybe Text source'comment = fmap $ \x -> "{- " <> show'sourceRange x <> " -}" show'var :: Var -> Text show'var (Var x s) = maybe "" (<> " ") (source'comment s) <> "var " <> (Text.show @Text . unquotedString'text) x show'str'static :: Str'Static -> Text show'str'static (Str'Static x s) = maybe "" (<> " ") (source'comment s) <> Text.show @Text x show'str'dynamic :: Str'Dynamic -> Text show'str'dynamic (Str'Dynamic xs s) = maybe "" (<> " ") (source'comment s) <> "str [" <> Text.intercalateMap ", " show'str'1 xs <> "]" show'str'1 :: Str'1 -> Text show'str'1 = \case Str'1'Literal (Str'Static x s) -> maybe "" (<> " ") (source'comment s) <> Text.show @Text x Str'1'Antiquote x -> "antiquote (" <> show'expression x <> ")" show'str'indented :: InStr -> Text show'str'indented x = maybe "" (<> " ") (source'comment (inStr'source x)) <> "str'indented [" <> Text.intercalateMap ", " show'str'indented'1 (inStr'toSeq x) <> "]" show'str'indented'1 :: InStr'1 -> Text show'str'indented'1 x = "indent " <> maybe "" (<> " ") (source'comment (inStr'1'indentSource x)) <> Text.show @Natural (inStr'1'level x) <> " [" <> Text.intercalateMap ", " (Text.show @Str'1) (Seq.toList (inStr'1'str x)) <> "] " <> case inStr'1'lineBreak x of Nothing -> "Nothing" Just a -> "(Just " <> Text.show @Str'Static a <> ")" show'list :: List -> Text show'list (List xs s) = maybe "" (<> " ") (source'comment s) <> "list [" <> Text.intercalateMap ", " show'expression xs <> "]" show'dict :: Dict -> Text show'dict (Dict r bs s) = maybe "" (<> " ") (source'comment s) <> (if r then "rec'dict [" else "dict [") <> Text.intercalateMap ", " show'dictBinding bs <> "]" show'dictBinding :: DictBinding -> Text show'dictBinding = \case DictBinding'Eq a b -> "dict'eq (" <> show'expression a <> ") (" <> show'expression b <> ")" DictBinding'Inherit'Var xs -> "dict'inherit [" <> Text.intercalateMap ", " (Text.show @Text . var'text) xs <> "]" DictBinding'Inherit'Dict from xs -> "dict'inherit'from (" <> show'expression from <> ") [" <> Text.intercalateMap ", " show'str'static xs <> "]" show'dot :: Dot -> Text show'dot (Dot a b s) = maybe "" (<> " ") (source'comment s) <> "dot (" <> show'expression a <> ") (" <> show'expression b <> ")" show'lambda :: Lambda -> Text show'lambda (Lambda a b s) = maybe "" (<> " ") (source'comment s) <> "lambda (" <> show'param a <> ") (" <> show'expression b <> ")" show'param :: Param -> Text show'param = \case Param'Name a -> "param " <> Text.show @Text (var'text a) Param'DictPattern b -> show'dictPattern b Param'Both a b -> "param " <> Text.show @Text (var'text a) <> " <> " <> show'dictPattern b show'dictPattern :: DictPattern -> Text show'dictPattern (DictPattern xs e) = "pattern [" <> Text.intercalateMap ", " show'dictPattern'1 xs <> "]" <> (if e then " <> ellipsis" else "") show'dictPattern'1 :: DictPattern'1 -> Text show'dictPattern'1 (DictPattern'1 a mb) = "dict'param " <> Text.show @Text (var'text a) <> maybe "" (\b -> " & def (" <> show'expression b <> ")") mb show'apply :: Apply -> Text show'apply (Apply a b s) = maybe "" (<> " ") (source'comment s) <> "apply (" <> show'expression a <> ") (" <> show'expression b <> ")" show'let :: Let -> Text show'let (Let xs y s) = maybe "" (<> " ") (source'comment s) <> "let'in [" <> Text.intercalateMap ", " show'letBinding xs <> "] (" <> show'expression y <> ")" show'letBinding :: LetBinding -> Text show'letBinding = \case LetBinding'Eq a b -> "let'eq " <> Text.show @Text (var'text a) <> " (" <> show'expression b <> ")" LetBinding'Inherit from xs -> "let'inherit'from (" <> show'expression from <> ") [" <> Text.intercalateMap ", " (Text.show @Text . var'text) xs <> "]"