Automatically generate documentation from comments in R7RS Scheme code
git clone https://git.8pit.net/scmdoc.git
1{-# LANGUAGE OverloadedStrings #-} 2 3-- | This module implements comment-preserving pseudo macro expansion 4-- for record type definitions. All relevant record type comments are 5-- expanded to dummy procedure definitions, allowing for them to be 6-- formatter with the 'SchemeDoc.Formatter.Procedure' formatter later. 7module SchemeDoc.Format.Record (expand) where 8 9import Data.Maybe (fromMaybe)10import qualified Data.Text as T11import SchemeDoc.Format.Util12import SchemeDoc.Types1314-- Return a new list where the element at the given index has15-- been modified according to the given function. If the index16-- is out of range, or if the functions returns 'Nothing', then17-- 'Nothing' is returned. Indices start at zero.18modifyAt :: (a -> Maybe a) -> [a] -> Int -> Maybe [a]19modifyAt f ls i20 | i < 0 = Nothing21 | otherwise = go i ls22 where23 go 0 (x : xs) = (: xs) <$> f x24 go n (x : xs) = (x :) <$> go (n - 1) xs25 go 0 [] = Just []26 go _ _ = Nothing2728objIdx :: Int -> [Sexp] -> Maybe Int29objIdx n' = go n' 030 where31 go :: Int -> Int -> [Sexp] -> Maybe Int32 go n i ((DocComment _) : xs) = go n (i + 1) xs33 go n i (_ : xs) = if n == 0 then Just i else go (n - 1) (i + 1) xs34 go _ _ _ = Nothing3536mkProc :: [Sexp] -> T.Text -> Sexp37mkProc params name = List [Id "define", List $ Id name : params, Id "_"]3839------------------------------------------------------------------------4041-- Expand a record type constructor into a dummy procedure definition.42--43-- > <constructor> → (<identifier> <field name>*)44expandCons :: [Sexp] -> Maybe [Sexp]45expandCons xs = do46 idx <- objIdx 0 xs47 modifyAt expandCons' xs idx48 where49 expandCons' (List (Id consName : fields)) = Just $ mkProc fields consName50 expandCons' _ = Nothing5152-- Expand a record type predicate into a dummy procedure definition.53expandPred :: [Sexp] -> Maybe [Sexp]54expandPred xs = objIdx 1 xs >>= modifyAt (onId $ mkProc [Id "obj"]) xs5556-- Expand a record field spec into a dummy procedure definition.57--58-- > <field spec> → (<field name> <accessor>)59-- > | (<field name> <accessor> <mutator>)60expandField :: T.Text -> Sexp -> Maybe Sexp61expandField typeName (List xs) = do62 -- Expand accessor procedure (must be present).63 idx <- objIdx 1 xs64 expanded <- modifyAt (onId $ mkProc [Id typeName]) xs idx6566 pure (List $ Id "begin" : fromMaybe expanded (mutator expanded))67 where68 -- Optionally expand mutator procedure.69 mutator lst = do70 idx <- objIdx 2 lst71 modifyAt (onId $ mkProc [Id typeName, Id "new-value"]) lst idx72expandField _ _ = Nothing7374-- Expand multiple record type field specs into dummy procedures.75expandFields :: T.Text -> [Sexp] -> Maybe [Sexp]76expandFields typeName exprs = do77 -- Extract all fields.78 idx <- objIdx 2 exprs79 let (xs, fields) = splitAt idx exprs8081 -- Expand all fields and append them to the existing list.82 (xs ++) <$> mapM (expandField typeName) fields8384-- | Expand a record type definition into multiple dummy procedures85-- while preserving documentation comments. Thereby, allowing record86-- type constructors, predicates, and field accessors/mutators to be87-- documented as procedures.88--89-- > (define-record-type <identifier>90-- > <constructor> <identifier> <field spec>*)91--92-- TODO: Use SyntaxError errors.93expand :: Sexp -> Maybe Sexp94expand (List (Id "define-record-type" : Id typeName : xs)) = do95 e <- expandCons xs >>= expandPred >>= expandFields (T.toLower typeName)96 Just $ List (Id "begin" : e)97expand _ = Nothing