In today’s Programming Praxis exercise, our task is to write a program that converts K&R-style literate code to the resulting code listing. Let’s get started, shall we?
Some needed imports:
import Control.Applicative hiding (many, (<|>)) import Text.Parsec
The provided Scheme solution uses regular expressions. We will be using a parser-based approach instead. The idea is to store all the definitions in a list so we can then go in and make all the required substitutions. A definition can contain two types of lines: regular code or references to other definitions, in which case we also store the preceding indentation.
data Lit = Line String | Ref String String
The parser for this file format isn’t too complicated.
def = (,) <$> (many literate *> chunk <* char '=' <* literate) <*> sepEndBy1 line newline where rol = many (noneOf "\n") upto end = manyTill anyChar (try $ end) chunk = string "<<" *> many space *> upto (many space *> string ">>") literate = notFollowedBy chunk *> rol <* newline line = try (Ref <$> many space <*> chunk <* rol) <|> (Line <$> many1 (noneOf "\n"))
As mentioned earlier, the program parses all the definitions, stores them in a list and recursively substitutes all the references with their content.
tangle :: String -> String tangle src = maybe "" (unlines . (f =<<)) $ lookup "*" defs where defs = either (error . show) id $ parse (many def) "" src f (Line s) = [s] f (Ref n s) = map (n ++) . maybe  (f =<<) $ lookup s defs
Let’s see if everything works:
main :: IO () main = putStrLn . tangle =<< readFile "tangle.txt"
Yep. Alternatively, just use a language with built-in literate programming, like Haskell 🙂