Filter

Filter.hs

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)

Filt

Filt 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

DocSubs

DocSubs is the type of a description of an Annote documentation command, such as the command

@here `Filter`
type DocSubs = [(String,String,String)]

docSubs

docSubs 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, codeSubsPost

codeSubsPre, 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
    [ ("&","&amp;")]

codeSubsPost = map mkSub
    [ ("<","&lt;")
    , (">","&gt;")
    , ("\"","&quot;")
    ]

filterLine

filterLine applies the substitutions given in the mapping subs.

filterLine :: [Sub] → String → String
filterLine subs line = foldl doSub line subs

preSubs, postSubs

preSubs, postSubs apply the substitutions given in codeSubsPre and codeSubsPost, respectively.

preSubs, postSubs :: Filt
preSubs  = filterLine codeSubsPre
postSubs = filterLine codeSubsPost

shell

shell 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    

process

process 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

doGeneric

doGeneric 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 []