module Language.Syntactic.Analysis.Render
    ( Render (..)
    , printExpr
    , ToTree (..)
    , showAST
    , drawAST
    ) where



import Data.Tree

import Language.Syntactic.Syntax



-- | Render an expression as concrete syntax. A complete instance must define
-- either of the methods 'render' and 'renderPart'.
class Render expr
  where
    -- | Render an expression as a 'String'
    render :: expr a -> String
    render = renderPart []

    -- | Render a partially applied constructor given a list of rendered missing
    -- arguments
    renderPart :: [String] -> expr a -> String
    renderPart []   a = render a
    renderPart args a = "(" ++ unwords (render a : args) ++ ")"

instance Render expr => Render (AST expr)
  where
    renderPart args (Symbol a) = renderPart args a
    renderPart args (f :$: a)  = renderPart (render a : args) f

instance Render expr => Show (AST expr a)
  where
    show = render

instance (Render expr1, Render expr2) => Render (expr1 :+: expr2)
  where
    renderPart args (InjectL a) = renderPart args a
    renderPart args (InjectR a) = renderPart args a

instance (Render expr1, Render expr2) => Show ((expr1 :+: expr2) a)
  where
    show = render

-- | Print an expression
printExpr :: Render expr => expr a -> IO ()
printExpr = putStrLn . render



class Render expr => ToTree expr
  where
    -- | Convert a partially applied constructor to a syntax tree given a list
    -- of rendered missing arguments
    toTreePart :: [Tree String] -> expr a -> Tree String
    toTreePart args a = Node (render a) args

instance ToTree dom => ToTree (AST dom)
  where
    toTreePart args (Symbol a) = toTreePart args a
    toTreePart args (f :$: a)  = toTreePart (toTree a : args) f

instance (ToTree expr1, ToTree expr2) => ToTree (expr1 :+: expr2)
  where
    toTreePart args (InjectL a) = toTreePart args a
    toTreePart args (InjectR a) = toTreePart args a

-- | Convert an expression to a syntax tree
toTree :: ToTree expr => expr a -> Tree String
toTree = toTreePart []

-- | Show syntax tree using ASCII art
showAST :: ToTree dom => AST dom a -> String
showAST = drawTree . toTree

-- | Print syntax tree using ASCII art
drawAST :: ToTree dom => AST dom a -> IO ()
drawAST = putStrLn . showAST