technique-0.2.5: lib/Technique/Formatter.hs
{-# 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)