Skip to content

Instantly share code, notes, and snippets.

@izgzhen
Last active October 22, 2015 15:32
Show Gist options
  • Save izgzhen/bb0b3d12998e90d5bbf8 to your computer and use it in GitHub Desktop.
Save izgzhen/bb0b3d12998e90d5bbf8 to your computer and use it in GitHub Desktop.
Parse autoconf.mk
-- autoconf parser
module AutoParser where
import Data.List.Extra (splitOn)
import qualified Data.Map as M
isComment ('#':_) = True
isComment _ = False
isBlank = all (`elem` " \n\t\r")
data Segment = Plain String
| Var String
| For String String String
deriving (Show)
parseRHS :: String -> [Segment]
parseRHS "" = []
parseRHS s = case span (/= '$') s of
("", '$':_:rest) | take 7 rest == "foreach" -> foreach (drop 7 rest)
("", '$':_:rest) -> f Var rest
(plain, rest) -> Plain plain : parseRHS rest
where
f cons rest = case span (not . flip elem "})") rest of
(_, "") -> error "undefined string"
(var, _:rest) -> cons var : parseRHS rest
foreach rest = case splitOn "," rest of
(var:src:str:[]) -> [For var src (drop (length str - 1) str)]
_ -> error $ "illegal foreach: " ++ rest
data Expr = Assign String String
| Append String String
parseExpr :: M.Map String String -> Expr -> M.Map String String
parseExpr m expr = let f v = concat $ (map fSeg) (parseRHS v)
in case expr of
Assign k v -> M.insert k (f v) m
Append k v ->
case M.lookup k m of
Nothing -> error $ "not defined: " ++ k
Just v' -> M.insert k (v' ++ " " ++ f v) m
where
fSeg seg = case seg of
Plain x -> x
Var var -> case M.lookup var m of
Nothing -> error $ "not defined: " ++ var
Just value -> value
For iter src str ->
let srcs = case src of
'$':_:rest ->
let rest' = take (length rest - 1) rest
in case M.lookup rest' m of
Nothing -> error $ "not defined: " ++ rest'
Just v -> words v
x -> words x
xs = flip map srcs $ \str ->
let Just k = M.lookup "____" (parseExpr m (Assign "____" str))
in k
in unwords xs
breakdown :: String -> Expr
breakdown s = case words s of
var:":=":key -> Assign var (unwords key)
var:"+=":key -> Append var (unwords key)
_ -> error $ "illegal line: " ++ show s
parseText :: [String] -> M.Map String String
parseText = foldl parseExpr M.empty . map breakdown
readAutoConf :: String -> IO (M.Map String String)
readAutoConf filename = do
s <- readFile filename
let lns = filter (not . isBlank) . filter (not . isComment) $ lines s
return $ parseText lns
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment