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