Copyright © 2008 Dave Bayer. Subject to a BSD-style license.
This module is part of the Annote project.
module Filter (Filt,DocSubs,docSubs,filterLine,process,doGeneric) where
Filter
processes the Code and Doc lines of split input text.
Split
divides an input file into code, documentation, and external documentation.
import Split (Split(..))
Regex
provides regular expression matching.
It is a wrapper around Text.Regex.
import Regex (mkRegex,matchRegex,Sub,mkSub,doSub)
Run provides the function runCommand, which
forks threads to call runInteractiveCommand, waiting
for the result and handling errors.
import Run (runCommand)
FiltFilt is a type used to describe arguments to process; these
arguments are filters applied to either source code or documentation text.
type Filt = String → String
DocSubsDocSubs is the type of a description of an Annote documentation
command, such as the command
@here `Filter`
type DocSubs = [(String,String,String)]
docSubsdocSubs is a mapping of regular expression substitutions to apply to
generic documentation. It is a function which appends custom substitutions
subs to the substitutions we want to apply to all documentation.
docSubs :: DocSubs → [Sub]
docSubs subs = map (mkSub.pad) $
[ ("here", "`([^` \t]+[.][^` \t]+)`", "[`\\2`](\\2 \"\\2\")")
, ("here", "`([^` \t]+)`", "[`\\2`](\\2.html \"\\2\")")
, ("def", "`([^` \t]+)`, `([^` \t]+)`, `([^` \t]+)`, `([^` \t]+)`",
"---\n### `\\2`, `\\3`, `\\4`, `\\5`\n`\\2`, `\\3`, `\\4`, `\\5`")
, ("def", "`([^` \t]+)`, `([^` \t]+)`, `([^` \t]+)`",
"---\n### `\\2`, `\\3`, `\\4`\n`\\2`, `\\3`, `\\4`")
, ("def", "`([^` \t]+)`, `([^` \t]+)`",
"---\n### `\\2`, `\\3`\n`\\2`, `\\3`")
, ("def", "`([^` \t]+)`", "---\n### <a name=\"\\2\">`\\2`</a>\n`\\2`")
] ++ subs
where pad (r,s,t) = ("^([0-9.*]*[ ]?)@" ++ r ++ "[ \t]+" ++ s, "\\1" ++ t)
codeSubsPre, codeSubsPostcodeSubsPre, codeSubsPost are mappings of regular expression
substitutions to apply to source code.
We escape & characters before applying
language-specific substitutions, and we escape
<, >, " characters afterwards.
codeSubsPre, codeSubsPost :: [Sub]
codeSubsPre = map mkSub
[ ("&","&")]
codeSubsPost = map mkSub
[ ("<","<")
, (">",">")
, ("\"",""")
]
filterLinefilterLine applies the substitutions given in the mapping subs.
filterLine :: [Sub] → String → String filterLine subs line = foldl doSub line subs
preSubs, postSubspreSubs, postSubs apply the substitutions given in
codeSubsPre and codeSubsPost, respectively.
preSubs, postSubs :: Filt preSubs = filterLine codeSubsPre postSubs = filterLine codeSubsPost
shellshell recognizes shell commands, in which case it returns a
Just Shell containing the result of the command,
wrapped in a <pre class="code1"> environment.
shell :: String → Maybe (Split)
shell s =
let pre = "\n<pre class=\"code1\">\n"
end = "</pre>\n"
in case matchRegex (mkRegex "^@shell (.*)$") s of
Just [cmd] → Just $ Shell $ do
(out,_) ← runCommand cmd [] True
return $ pre ++ out ++ end
_ → Nothing
processprocess applies codeFilter and docFilter to the split lines of
input text. doGeneric and custom language filters call process.
process :: Filt → Filt → [Split] → [Split]
process codeFilter docFilter xs = f xs where
f [] = []
f (x:xt) = case x of
Code s → (Code $ (postSubs . codeFilter . preSubs) s) : f xt
Doc s → case shell s of
Just y → y : f xt
Nothing → (Doc $ docFilter s) : f xt
_ → x : f xt
doGenericdoGeneric provides generic processing for
languages that do not have a custom module.
doGeneric :: [Split] → [Split]
doGeneric = process codeFilter docFilter where
codeFilter = id
docFilter = filterLine $ docSubs []