Парсер ini-файлов на Haskell


Я учусь Хаскелл на данный момент, и просто написал мой первый полезный модуль - парсер Ини файлы ФО. Я использовал парсек. Я хотел бы знать, что можно здесь улучшить - или, может быть, я сделал некоторые вещи, совершенно неправильно и есть лучший способ. Спасибо.

module IniFile (iniFileToMap) where

import Text.ParserCombinators.Parsec
import qualified Data.Map as Map
import Char


parseIniFile :: Parser ([(String, [(String, String)])])
parseSection :: Parser (String, [(String, String)])
parseSectionHeader :: Parser String
parseNameValue :: Parser (String, String)
parseComment :: Parser ()
parseNormalValue :: Parser String
parseQuotedValue :: Parser String
parseValue :: Parser String
parseName :: Parser String
parseInsideQuotes :: Parser String
parseCmtOrSpace :: Parser ()
iniFileToMap :: FilePath -> IO (Map.Map String (Map.Map String String))

parseCmtOrSpace = do{
   spaces;
   skipMany parseComment;
}

parseInsideQuotes = many (noneOf "\"")

parseQuotedValue = between (char '"') (char '"') parseInsideQuotes 

parseNormalValue = many1 (satisfy (\c -> isPrint c && not (isSpace c)))

parseValue = parseQuotedValue <|> parseNormalValue

parseName =  many1 (satisfy (\c -> isAlpha c || isDigit c || c == '_' || c == '.'));

parseComment = do{
   skipMany1 (char ';');
   skipMany (noneOf "\n");
   spaces;
}

parseNameValue = do{
   name <-  parseName;
   between (skipMany (oneOf " \t")) (skipMany (oneOf " \t")) (char '=');
   value <- parseValue;
   return (name, value);
}

parseSectionHeader = do{
   name <- between (char '[') (char ']') parseName;
   return name;
}

parseSection = do{
   name <- between parseCmtOrSpace parseCmtOrSpace parseSectionHeader;
   values <- endBy1 (parseNameValue) (parseCmtOrSpace);
   return (name, values);
}

parseIniFile  = do{
   result <- many1 (parseSection);
   return result;
}

list2Map list = Map.fromList (map (\e -> (fst e, Map.fromList (snd e))) list)

iniFileToMap path = do{
   result <- parseFromFile parseIniFile path;
      case (result) of
         Left err -> error (show err)
         Right xs -> return (list2Map(xs));
}


930
6
задан 5 мая 2011 в 12:05 Источник Поделиться
Комментарии
2 ответа

Первая вещь, которую я заметил, заключается в том, что вы сгруппировали все тип подписи вместе, вдали от тела функции. Это кажется мне очень странным. Это способ более распространен (и насколько я боюсь также способ более читабельным), чтобы ввести значение непосредственно перед его определением.

Второе, что я заметил, были все фигурные скобки и точки с запятой. Если у вас нет особых причин использовать их, я бы настоятельно рекомендуем использовать значительные Хаскелла отступы вместо. Гораздо меньше визуального шума. Особенно меня смущает точка с запятой в конце parseNameс определением как parseName не использовать вообще-нотации, а вы не использовали точку с запятой на любое другое определение, которое не использовать вообще.


Вы иногда окружают переменные в скобки (например, в значения <- endBy1 (parseNameValue) (parseCmtOrSpace), но не в строке имя <- между parseCmtOrSpace ... прямо над ней). Я не могу видеть любую систему, когда ты сделаешь это, и когда вы не делаете, так это только кажется случайным и непоследовательным. Поэтому я рекомендую избавляться от ненужных скобок.

Говоря в скобках, можно также избавиться от некоторых больше скобок, разбрасывая пару $ знаки в коде, чтобы придать ему более "haskellish" чувствовать.


parseSectionHeader = do{
name <- between (char '[') (char ']') parseName;
return name;
}

parseIniFile = do{
result <- many1 (parseSection);
return result;
}

Когда у вас есть что-то вида:

foo <- bar
return foo

Вы можете просто написать бар. Т. е.:

parseSectionHeader = between (char '[') (char ']') parseName

parseIniFile = many1 parseSection


iniFileToMap path = do{
result <- parseFromFile parseIniFile path;
case (result) of
Left err -> error (show err)
Right xs -> return (list2Map(xs));
}

Здесь отступ выключен - случае не должно быть отступа дальше, чем результат <-. Она должна быть:

iniFileToMap path = do
result <- parseFromFile parseIniFile path
case result of
Left err -> error $ show err
Right xs -> return $ list2Map xs

6
ответ дан 6 мая 2011 в 04:05 Источник Поделиться

Форматирование/Конвенций

Во-первых, обычное соглашение состоит в данных псевдонима.Карта как М. Как правило, квалифицированные импорта являются одним письмом, если вы не нуждаетесь в двух из-за столкновения.

Не используйте скобки, за исключением записи синтаксис.

Не используйте точку с запятой.

Монады

Много ли ваши нотации не нужны не нотации. Чтобы добавить в примеры sepp2k, мы также имеем нечто вроде:

parseCmtOrSpace = do
spaces
skipMany parseComment

что должно быть написано гораздо более красиво, как

parseCmtOrSpace = spaces >> skipMany parseComment

начиная делать записи, как правило, не надо за две линию.

Как еще один пример очистки нотации делать: (это значительно более личное дело, чем мой пример. Однако она также вносит некоторые другие изменения, которые вы должны посмотреть, даже если вы хотите сохранить свои монадические делать нотации, такие как рефакторинг вкладок)

parseNameValue = do
name <- parseName
between (skipMany (oneOf " \t")) (skipMany (oneOf " \t")) (char '=')
value <- parseValue
return (name, value)

Вместо этого мы можем написать: (Прошу прощения, если после неправильный, у меня нет компилятора)

parseNameValue = liftM3 g parseName eq parseValue
where eq = between ts ts $ char '='
ts = skipMany $ oneOf " \t"
g x _ y = (x, y)

Мы можем сделать то же самое для parseSection:

parseSection = do
name <- between parseCmtOrSpace parseCmtOrSpace parseSectionHeader
values <- endBy1 (parseNameValue) (parseCmtOrSpace)
return (name, values)

становится

parseSection = liftM2 g names values
where names = between parseCmtOrSpace parseCmtOrSpace parseSectionHeader
values = endBy1 parseNameValue parseCmtOrSpace
g x y = (x, y)

Парсек

Необходимо определить способ получить либо ParseError (м. Карта строка) вместо erroring в библиотеке. Erroring следует оставить на клиент. Хотя вы можете предоставить один, что обеспечивает либо, и одна функция, что ошибки автоматически.

Кажется, соглашение о том, что монады, которые составляют парсер не должен начинаться с разбора. Мы видим это в парсек библиотека (ее чар, не parseChar). Функции не разобрать; они возвращают данные, которые можно использовать для парсинга. По крайней мере делать nameValueParser, но theres ничего плохого с просто namevalue с. Просто не экспортировать функции и не будет никаких проблем пространства имен большую часть времени.

2
ответ дан 7 мая 2011 в 12:05 Источник Поделиться