{-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -fno-warn-orphans #-} {-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-unused-matches -fno-warn-unused-imports #-} module Technique.Formatter where import Core.System.Pretty import Core.Text.Rope import Core.Text.Utilities import Data.Foldable (foldl') import Data.Int (Int8) import Technique.Language import Technique.Quantity data TechniqueToken = MagicToken | ProcedureToken | TypeToken | SymbolToken | OperatorToken | VariableToken | ApplicationToken | LabelToken | StringToken | QuantityToken | RoleToken | ErrorToken | FilenameToken | StepToken instance Pretty Procedure where pretty = unAnnotate . highlight colourizeTechnique :: TechniqueToken -> AnsiColour colourizeTechnique token = case token of MagicToken -> brightGrey ProcedureToken -> bold dullBlue TypeToken -> dullYellow SymbolToken -> bold dullCyan OperatorToken -> bold dullYellow VariableToken -> brightCyan ApplicationToken -> bold brightBlue LabelToken -> brightGreen StringToken -> bold brightGreen QuantityToken -> bold brightMagenta RoleToken -> dullYellow ErrorToken -> bold pureRed FilenameToken -> bold brightWhite StepToken -> bold brightGrey -- for diagnostics in evalutator instance Render Procedure where type Token Procedure = TechniqueToken colourize = colourizeTechnique highlight proc = let name = highlight . procedureName $ proc params = case procedureParams proc of [] -> emptyDoc xs -> commaCat xs <> " " from = commaCat . procedureInput $ proc into = highlight . procedureOutput $ proc block = highlight . procedureBlock $ proc description = case procedureDescription proc of Nothing -> emptyDoc Just text -> highlight text in description <> ( indent 4 ( annotate ProcedureToken name <+> params <> annotate SymbolToken ":" <+> annotate TypeToken from <+> annotate SymbolToken "->" <+> annotate TypeToken into <> line <> block ) ) -- | -- Punctuate a list with commas annotated with Symbol highlighting. commaCat :: (Render a, Token a ~ TechniqueToken) => [a] -> Doc (Token a) commaCat = hcat . punctuate (annotate SymbolToken comma) . fmap (annotate VariableToken . highlight) instance Render Type where type Token Type = TechniqueToken colourize = colourizeTechnique highlight (Type name) = annotate TypeToken (pretty name) instance Render Markdown where type Token Markdown = TechniqueToken colourize = colourizeTechnique highlight (Markdown text) = pretty text instance Render Block where type Token Block = TechniqueToken colourize = colourizeTechnique highlight (Block statements) = nest 4 ( annotate SymbolToken lbrace <> go statements ) <> line <> annotate SymbolToken rbrace where go :: [Statement] -> Doc TechniqueToken go [] = emptyDoc go (x@(Series _) : x1 : xs) = highlight x <> highlight x1 <> go xs go (x : xs) = line <> highlight x <> go xs instance Render Statement where type Token Statement = TechniqueToken colourize = colourizeTechnique highlight statement = case statement of Assignment _ vars expr -> commaCat vars <+> annotate SymbolToken "=" <+> highlight expr Execute _ expr -> highlight expr Comment _ text -> "-- " <> pretty text -- TODO what about multiple lines? Declaration _ proc -> highlight proc Blank _ -> emptyDoc Series _ -> annotate SymbolToken " ; " instance Render Attribute where type Token Attribute = TechniqueToken colourize = colourizeTechnique highlight role = case role of Role name -> annotate RoleToken ("@" <> pretty name) Place name -> annotate RoleToken ("#" <> pretty name) Inherit -> annotate ErrorToken "Inherit" instance Render Expression where type Token Expression = TechniqueToken colourize = colourizeTechnique highlight expr = case expr of Application _ name subexpr -> annotate ApplicationToken (highlight name) <+> highlight subexpr None _ -> annotate SymbolToken ("()") Undefined _ -> annotate ErrorToken "?" Amount _ qty -> highlight qty Text _ text -> annotate SymbolToken dquote <> annotate StringToken (pretty text) <> annotate SymbolToken dquote Object _ tablet -> highlight tablet Variable _ vars -> commaCat vars Operation _ operator subexpr1 subexpr2 -> highlight subexpr1 <+> highlight operator <+> highlight subexpr2 Grouping _ subexpr -> annotate SymbolToken lparen <> highlight subexpr <> annotate SymbolToken rparen Restriction _ attribute block -> highlight attribute <> line <> highlight block -- TODO some nesting? instance Render Identifier where type Token Identifier = TechniqueToken colourize = colourizeTechnique highlight (Identifier name) = pretty name instance Pretty Identifier where pretty = unAnnotate . highlight instance Render Decimal where type Token Decimal = TechniqueToken colourize = colourizeTechnique highlight = pretty . decimalToRope instance Render Quantity where type Token Quantity = TechniqueToken colourize = colourizeTechnique highlight qty = case qty of Number i -> annotate QuantityToken (pretty i) Quantity i u m unit -> let measurement = highlight i <> " " uncertainty = if isZeroDecimal u then emptyDoc else "± " <> highlight u <> " " magnitude = if m == 0 then emptyDoc else "× 10" <> numberToSuperscript m <> " " in annotate QuantityToken (measurement <> uncertainty <> magnitude <> pretty unit) numberToSuperscript :: Int8 -> Doc ann numberToSuperscript number = let digits = show number digits' = fmap toSuperscript digits in pretty digits' toSuperscript :: Char -> Char toSuperscript c = case c of '0' -> '⁰' -- U+2070 '1' -> '¹' -- U+00B9 '2' -> '²' -- U+00B2 '3' -> '³' -- U+00B3 '4' -> '⁴' -- U+2074 '5' -> '⁵' -- U+2075 '6' -> '⁶' -- U+2076 '7' -> '⁷' -- U+2077 '8' -> '⁸' -- U+2078 '9' -> '⁹' -- U+2079 '-' -> '⁻' -- U+207B _ -> error "Invalid, digit expected" instance Pretty Quantity where pretty = unAnnotate . highlight instance Render Tablet where type Token Tablet = TechniqueToken colourize = colourizeTechnique highlight (Tablet bindings) = nest 4 ( annotate SymbolToken lbracket <> foldl' g emptyDoc bindings ) <> line <> annotate SymbolToken rbracket where g :: Doc TechniqueToken -> Binding -> Doc TechniqueToken g built binding = built <> line <> highlight binding instance Render Label where type Token Label = TechniqueToken colourize = colourizeTechnique highlight (Label text) = annotate SymbolToken dquote <> annotate LabelToken (pretty text) <> annotate SymbolToken dquote instance Pretty Label where pretty = unAnnotate . highlight -- the annotation for the label duplicates the code Quantity's Text -- constructor, but for the LabelToken token. This distinction may not be -- necessary (at present we have the same colouring for both). instance Render Binding where type Token Binding = TechniqueToken colourize = colourizeTechnique highlight (Binding label subexpr) = highlight label <+> annotate SymbolToken "~" <+> highlight subexpr instance Render Operator where type Token Operator = TechniqueToken colourize = colourizeTechnique highlight operator = annotate OperatorToken $ case operator of WaitBoth -> pretty '&' WaitEither -> pretty '|' Combine -> pretty '+' instance Render Technique where type Token Technique = TechniqueToken colourize = colourizeTechnique highlight technique = let version = pretty . techniqueVersion $ technique license = pretty . techniqueLicense $ technique copyright = case techniqueCopyright technique of Just owner -> "; ©" <+> pretty owner Nothing -> emptyDoc body = fmap highlight . techniqueBody $ technique in annotate MagicToken ("%" <+> "technique" <+> "v" <> version) <> line <> annotate MagicToken ("!" <+> license <> copyright) <> line <> line <> vsep (punctuate line body)