packages feed

anatomy-0.4: src/Anatomy/Scanner.hs

module Anatomy.Scanner where

import Control.Monad.State
import Data.IORef
import System.Directory
import System.FilePath

import Anatomy.Parser
import Anatomy.Types

import Atomo.Environment
import Atomo.Helpers
import Atomo.Types
import Atomo.Parser.Expand (macroExpand)


-- scan through everything and build up the initial state for generation
scan :: Int -> Int -> FilePath -> [Segment] -> VM Section
scan depth num path ss' = do
    a <- here "A" >>= dispatch . single "clone"
    env <- here "Lobby" >>= dispatch . single "clone"
    defineOn a (Slot (single "environment" PThis) env)

    sec <- liftIO . newSection $ \s -> s
        { sectionDepth = depth
        , sectionNumber = num
        , sectionPath = path
        , sectionA = a
        }

    st <- scan' sec ss'
    liftIO (writeIORef (sectionID st) st)
    return st
  where
    scan' acc [] = return acc
    scan' acc (KeywordDispatch ["title"] [s]:ss) =
        scan' (acc { sectionTitle = Title s Nothing Nothing }) ss
    scan' acc (KeywordDispatch ["title", "tag"] [s, t]:ss) =
        scan' (acc { sectionTitle = Title s (Just t) Nothing }) ss
    scan' acc (KeywordDispatch ["title", "tag", "version"] [s, t, v]:ss) =
        scan' (acc { sectionTitle = Title s (Just t) (Just v) }) ss
    scan' acc (KeywordDispatch ["include-section"] [sfn]:ss) = do
        cfn <- buildForString sfn
        fn <- findFile [sectionPath acc, ""] cfn
        liftIO (putStrLn ("including section: " ++ fn))

        ast <- parseFile fn
        sec <- fmap (\s -> s { sectionParent = Just (sectionID acc) }) $
            scan (depth + 1) (length (subSections acc) + 1) (takeDirectory fn) ast

        liftIO (writeIORef (sectionID sec) sec)

        scan' (acc
            { sectionBody = sectionBody acc ++
                [SectionReference (length (subSections acc))]
            , subSections = subSections acc ++ [sec]
            }) ss
      where
        findFile [] fn = throwError (FileNotFound fn)
        findFile (p:ps) fn = do
            check <- liftIO . doesFileExist $ p </> fn

            if check
                then liftIO (canonicalizePath (p </> fn))
                else findFile ps fn

    scan' acc (KeywordDispatch ["section", "tag"] [s, t]:ss) = do
        subsection acc s (Just t) ss
    scan' acc (KeywordDispatch ["section"] [s]:ss) =
        subsection acc s Nothing ss
    scan' acc (KeywordDispatch ["define"] [sb]:ss) = do
        body <- buildForString sb

        def <- parseDefinition body
        scan' (acc
            { sectionBody = sectionBody acc ++ [InlineDefinition def Nothing]
            , sectionBindings = defKey def : sectionBindings acc
            }) ss
    scan' acc (KeywordDispatch ["define", "body"] [sd, sb]:ss) = do
        body <- buildForString sd
        def <- parseDefinition body
        scan' (acc
            { sectionBody = sectionBody acc ++ [InlineDefinition def (Just sb)]
            , sectionBindings = defKey def : sectionBindings acc
            }) ss
    scan' acc (SingleDispatch "table-of-contents":ss) = do
        liftIO (putStrLn "table of contents")

        scan' (acc
            { sectionStyle = addStyle TOC (sectionStyle acc)
            , sectionBody = sectionBody acc ++ [TableOfContents]
            }) ss
    scan' acc (SingleDispatch "annotated-source":ss) = do
        liftIO (putStrLn "annotated source")

        scan' (acc
            { sectionStyle = addStyle Annotated (sectionStyle acc)
            }) ss
    scan' acc (s:ss) =
        scan' (acc { sectionBody = sectionBody acc ++ [s] }) ss

    subsection acc s t ss = do
        liftIO (putStrLn ("subsection: " ++ show s))

        sec <- fmap (\sec -> sec
            { sectionTitle = Title s t Nothing
            , sectionParent = Just (sectionID acc)
            }) $ scan (depth + 1) (length (subSections acc) + 1) "" sb

        liftIO (writeIORef (sectionID sec) sec)

        scan' (acc
            { sectionBody = sectionBody acc ++
                [SectionReference (length (subSections acc))]
            , subSections = subSections acc ++ [sec]
            }) rest
      where
        (sb, rest) = span (\sec ->
            case sec of
                KeywordDispatch ["section"] _ -> False
                KeywordDispatch ["section", "tag"] _ -> False
                _ -> True) ss


buildForString :: Segment -> VM String
buildForString (Atomo e) = liftM (fromText . fromString) (macroExpand e >>= eval)
buildForString (Chunk s) = return s -- TODO: escaping
buildForString (Nested ns) = fmap concat (mapM buildForString ns)
buildForString x = error $ "cannot be built into a string: " ++ show x