File: Preprocess.hs

package info (click to toggle)
hugs98 98.200311-4
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 12,964 kB
  • ctags: 8,084
  • sloc: ansic: 67,521; haskell: 61,497; xml: 4,566; sh: 3,264; cpp: 1,936; yacc: 1,094; makefile: 915; cs: 883; sed: 10
file content (89 lines) | stat: -rw-r--r-- 3,666 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
--------------------------------------------------------------------------------
-- 
-- Program     :  Preprocess
-- Copyright   :  (c) Sven Panne 2003
-- License     :  BSD-style (see the file libraries/OpenGL/LICENSE)
-- 
-- Maintainer  :  sven_panne@yahoo.com
-- Stability   :  provisional
-- Portability :  portable
--
-- The .spec files from the SI are normally processed by Perl/AWK scripts and
-- have  therefore a rather ugly line-oriented syntax. To make things more
-- amenable to "real" parsing, some lexical preprocessing is useful. Note that
-- the following algorithm doesn't remove or insert lines, which is important
-- for good error messages later. After this preprocessing, whitespace is not
-- significant anymore, apart from its common use as a token separator.
-- 
-- For every line do:
-- 
--   1) Remove comments: Remove everything starting at the first '#'.
-- 
--   2) Ignore passthru-hack: Consider lines starting with 'passthru:' as empty.
-- 
--   3) Remove trailing whitespace.
-- 
--   4) Mangle property declarations: Append ';' to a line where the first ':'
--      is only preceded by non-TAB and non-SPC characters. Additionally, move
--      that ':' to the beginning of the line.
-- 
--   5) Separate definitions: Append ',' to a line starting with TAB and
--      followed (ignoring empty lines) by a line starting with TAB.
-- 
--   6) Terminate definitions: Append ';' to a line starting with TAB and not
--      followed (ignoring empty lines) by a line starting with TAB.
-- 
--------------------------------------------------------------------------------

module Main ( main ) where

import Control.Monad      ( liftM )
import Data.Char          ( isSpace )
import Data.List          ( isPrefixOf, tails )
import System.Environment ( getArgs )

--------------------------------------------------------------------------------
-- Preprocessing of spec files, making it more amenable to "real" parsing
--------------------------------------------------------------------------------

preprocess :: String -> String
preprocess = unlines .
             addSeparators . mangleColonLines .
             removeTrailingWhitespace . removePassthru . removeComments .
             lines

   where removeComments = map $ takeWhile (/= '#')
         removePassthru = map $ \l -> if "passthru:" `isPrefixOf` l then "" else l
         removeTrailingWhitespace = map $ reverse . dropWhile isSpace . reverse
         mangleColonLines = map $ \l ->
            case break (== ':') l of
               (xs, ':':ys) | noSpaceIn xs -> ":" ++ xs ++ " " ++ ys ++ ";"
               _ -> l
         noSpaceIn = not . any (`elem` ['\t',' '])

         addSeparators = map addSeparator . tails

         addSeparator []                                  = []
         addSeparator xs@(l:ls) | startsWithTabbedLine xs = l ++ separatorFor ls
                                | otherwise               = l 

         separatorFor ls | startsWithTabbedLine (dropEmpty ls) = ","
                         | otherwise                           = ";"

         dropEmpty = dropWhile ((== 0) . length)

         startsWithTabbedLine (('\t':_):_) = True
         startsWithTabbedLine _            = False

--------------------------------------------------------------------------------
-- The driver
--------------------------------------------------------------------------------

-- behave like 'cat'
mainWithArgs :: [String] -> IO ()
mainWithArgs fileNames = putStr . preprocess =<< input
   where input | null fileNames = getContents
               | otherwise      = liftM concat (mapM readFile fileNames)

main :: IO ()
main = getArgs >>= mainWithArgs