@onmach см редактировать

ей работе я сталкиваюсь с множеством скучных SQL-запросов, и у меня появилась блестящая идея написать программу, которая будет анализировать SQL-файл и аккуратно его распечатывать. Я сделал это довольно быстро, но столкнулся с проблемой, которую не знаю, как решить.

Итак, давайте притворимся, что sql «select foo from bar, где 1». Я думал, что всегда есть ключевое слово, за которым следуют данные, поэтому все, что мне нужно сделать, - это проанализировать ключевое слово, а затем перехватить все тарабарщины перед следующим ключевым словом и сохранить его для последующей очистки, если оно того стоит. Вот код:

import Text.Parsec
import Text.Parsec.Combinator
import Text.Parsec.Char
import Data.Text (strip)

newtype Statement = Statement [Atom]
data Atom = Branch String [Atom] | Leaf String deriving Show

trim str = reverse $ trim' (reverse $ trim' str)
  where
    trim' (' ':xs) = trim' xs
    trim' str = str

printStatement atoms = mapM_ printAtom atoms
printAtom atom = loop 0 atom 
  where
    loop depth (Leaf str) = putStrLn $ (replicate depth ' ') ++ str
    loop depth (Branch str atoms) = do 
      putStrLn $ (replicate depth ' ') ++ str
      mapM_ (loop (depth + 2)) atoms

keywords :: [String]
keywords = [
  "select",
  "update",
  "delete",
  "from",
  "where"]

keywordparser :: Parsec String u String
keywordparser = try ((choice $ map string keywords) <?> "keywordparser")

stuffparser :: Parsec String u String
stuffparser = manyTill anyChar (eof <|> (lookAhead keywordparser >> return ()))

statementparser = do
  key <- keywordparser
  stuff <- stuffparser
  return $ Branch key [Leaf (trim stuff)]
  <?> "statementparser"

tp = parse (many statementparser) ""

Ключевым моментом здесь является материал-парсер. Это то, что находится между ключевыми словами, которые могут быть чем угодно, от списков столбцов до критериев где. Эта функция ловит все символы, ведущие к ключевому слову. Но для этого нужно что-то еще, прежде чем оно закончится. Что делать, если есть подвыбор? msgstr "выбрать идентификатор, (выбрать продукт из продуктов) из бара". Ну, в этом случае, если он попадет в это ключевое слово, он все испортит, неправильно его проанализирует и испортит мой отступ. Также, где пункты могут иметь круглые скобки.

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

stuffparser :: Parsec String u String
stuffparser = fmap concat $ manyTill somechars (eof <|> (lookAhead keywordparser >> return ()))
  where
    somechars = parens <|> fmap (\c -> [c]) anyChar
    parens= between (char '(') (char ')') somechars

Это будет ошибка так:

> tp "select asdf(qwerty) from foo where 1"
Left (line 1, column 14):
unexpected "w"
expecting ")"

Но я не могу придумать, как это переписать, чтобы оно работало. Я попытался использовать manyTill в скобках, но у меня возникли проблемы с проверкой типов, когда в качестве альтернативы у меня есть как строковые выражения, так и одиночные символы. У кого-нибудь есть какие-либо предложения по поводу этого?

Ответы на вопрос(1)

Ваш ответ на вопрос