LINUX.ORG.RU

Распарсить «конфигурационный» файл

 


0

1

Язык привел только для указания готовых инструментов, если имеются. Проблема глобальнее. Пишу (точнее, хочу написать) для саморазвития аудио плеер по типу mpd, с коллекцией, б. и ш. Встал вопрос - как парсить cue-файлы.

Допустим, вход такой: INDEX 00 13 : 56

Сейчас в пробном варианте написано абы как (примерный код):

(register-parser "INDEX" #'parse-index)

(defun parse-index (string)
  (let ((first-part (split-1 string))
        (second-part (split-2 string))) ....)

Короче, мы читаем строку, отрываем «слово», анализируем комманду, отдаем парсеру, который тоже рвет строку на части. А я хочу глобальней и надежней, типа такого:

(define-parser "INDEX" '(integer integer #\: integer) #'callback)

(defun callback (idx min sec)
   ;; idx, min, sec - числа, распарсенные из шаблона
   ())

Смысл - чтобы колбэк не сам возился со строками, а получал уже готовые значения по шаблону. При этом не желательно всяких регекспов, так как шаблоны простые, разные значения разделены пробелами.

Смотрю в гугль, а там какие-то яки, бизоны, флексы - нифига не понятно, что из этого брать и как им пользоваться. Посоветуйте.


При этом не желательно всяких регекспов, так как шаблоны простые, разные значения разделены пробелами.

Тогда это просто лексер. Имеем с одной стороны список шаблонных токенов и, с другой стороны, сами токены данной строки, обходим параллельно оба списка (сравниваем 0-ой элемент с 0-ым, 1-ый с 1-ым и т.д.) и в итоге либо получаем полезный результат, либо строка не подходит под шаблон.

import Control.Applicative
import Control.Monad
import Text.Printf

data Token = IntegerToken | ConstToken String deriving ( Eq, Show )

data Result = IntegerResult Integer | ConstMatch deriving ( Eq, Show )

unsafeInteger :: Result -> Integer
unsafeInteger (IntegerResult x) = x

parser :: Monad m => [Token] -> String -> m [Result]
parser tokens string
  | length tokens /= length ws = fail $ printf "length %s /= length %s" (show tokens) $ show ws
  | otherwise = zipWithM zipper tokens ws where
    ws = words string
    zipper IntegerToken ys
      | [(n, "")] <- reads ys = return $ IntegerResult n
      | otherwise = fail $ printf "bad parse: %s is not a number" ys
    zipper (ConstToken xs) ys
      | xs == ys = return ConstMatch
      | otherwise = fail $ printf "bad parse: %s /= %s" xs ys

parseIndex :: Monad m => String -> m [Result]
parseIndex = parser [ConstToken "INDEX", IntegerToken, IntegerToken, ConstToken ":", IntegerToken]
*Main> parseIndex "INDEX"
*** Exception: user error (length [ConstToken "INDEX",IntegerToken,IntegerToken,ConstToken ":",IntegerToken] /= length ["INDEX"])
*Main> parseIndex "INDEX x 13 : 56"
*** Exception: user error (bad parse: x is not a number)
*Main> parseIndex "INDEX 00 13 + 56"
*** Exception: user error (bad parse: : /= +)
*Main> parseIndex "INDEX 00 13 + 56" :: Maybe [Result]
Nothing
*Main> parseIndex "INDEX 00 13 : 56"
[ConstMatch,IntegerResult 0,IntegerResult 13,ConstMatch,IntegerResult 56]
*Main> parseIndex "INDEX 00 13 : 56" :: Maybe [Result]
Just [ConstMatch,IntegerResult 0,IntegerResult 13,ConstMatch,IntegerResult 56]
*Main> map unsafeInteger . filter (/= ConstMatch) <$> parseIndex "INDEX 00 13 : 56"
-- ^ unsafeInteger is safe here
[0,13,56]

В CL вместо zipWith* будет (iter (for template-token in template-tokens) (for token in tokens) и т.д.

quasimoto ★★★★ ()
Ответ на: комментарий от archimag

Кстати, я правильно понимаю, что в sbcl нет способа указать байт(ы), которыми осушествляется перевод строки? Из-за того, что используется lf в строке содержится после всех символов ещё и невидимый cr (#'babel:string-to-octet показал) мой парсер не парсит (не уверен, но по-идее :junk-allowed t должен помочь).

К тому же эти мои cue-файлы оказались в latin-1, а не юникоде, нет ли возможности как-нибудь автодетектить external-format?

Zorn ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.