2014-01-16 4 views
4

Я следую за Gentle introduction to Haskell учебником, и представленный там код кажется сломанным. Мне нужно понять, так ли это, или мое понимание концепции неверно.Parse string with lex in Haskell

Я реализую анализатор для пользовательского типа: функция

data Tree a = Leaf a | Branch (Tree a) (Tree a) 

печати для удобства

showsTree    :: Show a => Tree a -> String -> String 
showsTree (Leaf x)  = shows x 
showsTree (Branch l r) = ('<':) . showsTree l . ('|':) . showsTree r . ('>':) 

instance Show a => Show (Tree a) where 
    showsPrec _ x = showsTree x 

этот анализатор хорошо, но перерывы, когда есть пространства

readsTree   :: (Read a) => String -> [(Tree a, String)] 
readsTree ('<':s) = [(Branch l r, u) | (l, '|':t) <- readsTree s, 
             (r, '>':u) <- readsTree t ] 
readsTree s  = [(Leaf x, t)  | (x,t)  <- reads s] 

это один считается лучшим soluti , но это не работает без пространств

readsTree_lex :: (Read a) => String -> [(Tree a, String)] 
readsTree_lex s = [(Branch l r, x) | ("<", t) <- lex s, 
            (l, u) <- readsTree_lex t, 
            ("|", v) <- lex u, 
            (r, w) <- readsTree_lex v, 
            (">", x) <- lex w ] 
       ++ 
       [(Leaf x, t)  | (x, t) <- reads s ] 

рядом я выбираю один из парсеров для использования с read

instance Read a => Read (Tree a) where 
    readsPrec _ s = readsTree s 

тогда я загрузить его в GHCI, используя режим отладки Leksah (это unrelevant, я думаю), и попытаться разобрать две строки:

read "<1|<2|3>>" :: Tree Int -- succeeds with readsTree 
    read "<1| <2|3> >" :: Tree Int -- succeeds with readsTree_lex 

когда lex сталкивается |<2... пар t прежней струны, он разбивается на ("|<", _). Это не соответствует части анализатора и не удается завершить разбор.

Есть два вопроса, возникающие в связи:

  1. Как определить синтаксический анализатор, который действительно игнорирует пространства, не требует их?
  2. как я могу определить правила для расщепления встречаются литералов с Лексом

Говоря о втором вопросе - спрашивается больше любопытства, как определение моего собственного лексера кажется более правильным, чем определение правил существующего.

ответ

4

lex делится на Haskell lexemes, пропуская пробелы.

Это означает, что, поскольку Haskell разрешает |< в качестве лексемы, lex не разбивает его на два лексема, так как это не так, как он анализирует в Haskell.

Вы можете использовать только lex в своем парсере, если вы используете те же (или подобные) синтаксические правила для Haskell.

Если вы хотите игнорировать все пробелы (в отличие от внесения каких-либо пробелов, эквивалентное одному пространству), это намного проще и эффективнее первого запуска filter (not.isSpace).

+0

, что было что-то я не был уверен. Я до сих пор не понимаю, почему это пример в книге. Возможно, реализация lex изменилась за последнее десятилетие. – sukhmel

+0

Он говорит, что это следует за лексическими правилами Haskell, возможно, они предполагали, что вы не дадите ему '' <3|<4,5>> "на том основании, что для Haskell потребуется пространство между' '' и '<', чтобы отличить от потенциального '| <'. В тексте следует толковать очень подробно, но я согласен. –

+1

Я нашел ответ на эту (по-видимому) внутреннюю кодовую базу для этой книги. Эта кодовая база не была включена в мою (переведенную) версию, что заставило меня не замечать это раньше. Спасибо за ваши усилия. – sukhmel

2

Ответ на этот вопрос представляет собой небольшой пробел между текстом Gentle introduction to Haskell и его code samples, а также ошибку в примере кода.

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

lexAll :: ReadS String 
lexAll s = case lex s of 
      [("",_)] -> []         -- nothing to parse. 
      [(c, r)] -> if length c == 1 then [(c, r)]  -- we will try to match 
          else [(c, r), ([head s], tail s)]-- not only as it was 
      any_else -> any_else       -- parsed but also splitted 

автор сай:

Наконец, полный читатель. Это не чувствительно к пробелу, так как были предыдущими версиями. Когда вы выставляете класс Show для данных , созданный автоматически схожий стиль подобен стилю.

но lexAll следует использовать вместо Лекса (который, кажется, говорит об ошибке):

readsTree' :: (Read a) => ReadS (Tree a) 
readsTree' s = [(Branch l r, x) | ("<", t) <- lexAll s, 
        (l, u) <- readsTree' t, 
            ("|", v) <- lexAll u, 
            (r, w) <- readsTree' v, 
        (">", x) <- lexAll w ] 
       ++ 
       [(Leaf x, t) | (x, t) <- reads s]