2010-11-20 3 views
4

Я новичок и для Haskell, и для Parsec. В попытке узнать больше о языке и этой библиотеке, в частности, я пытаюсь создать парсер, который может анализировать Lua сохраненные файлы переменных. В этих файлах переменные могут принимать следующие формы:Проблемы с Parsec Haskell <|> Оператор

VarName = значение

VarName = {значение, значение, ...}

VarName = {{значение, значение}, {значение, значение, ...}}

Я создал парсеры для каждого из этих типов, но когда я их строю вместе с выбором < |> оператор, я получаю ошибку типа.

Couldn't match expected type `[Char]' against inferred type `Char' 
    Expected type: GenParser Char st [[[Char]]] 
    Inferred type: GenParser Char st [[Char]] 
In the first argument of `try', namely `lList' 
In the first argument of `(<|>)', namely `try lList' 

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

data Variable = LuaString ([Char], [Char]) 
      | LuaList ([Char], [[Char]]) 
      | NestedLuaList ([Char], [[[Char]]]) 
      deriving (Show) 

main:: IO() 
main = do 
     case (parse varName "" "variable = {{1234,\"Josh\"},{123,222}}") of 
      Left err -> print err 
      Right xs -> print xs 

varName :: GenParser Char st Variable 
varName = do{ 
     vName <- (many letter); 
     eq <- string " = "; 
     vCon <- try nestList 
      <|> try lList 
      <|> varContent; 
     return (vName, vCon)} 

varContent :: GenParser Char st [Char] 
varContent = quotedString 
    <|> many1 letter 
    <|> many1 digit 

quotedString :: GenParser Char st [Char] 
quotedString = do{ 
     s1 <- string "\""; 
     s2 <- varContent; 
     s3 <- string "\""; 
     return (s1++s2++s3)} 

lList :: GenParser Char st [[Char]] 
lList = between (string "{") (string "}") (sepBy varContent (string ",")) 

nestList :: GenParser Char st [[[Char]]] 
nestList = between (string "{") (string "}") (sepBy lList (string ",")) 

ответ

7

Это правильно.

(<|>) :: (Alternative f) => f a -> f a -> f a 

Обратите внимание, как оба аргумента имеют одинаковый тип.

Я точно не понимаю ваш тип данных Variable. Это способ, которым я хотел бы сделать это:

data LuaValue = LuaString String | LuaList [LuaValue] 
data Binding = Binding String LuaValue 

Это позволяет значения быть произвольно вложены, а не только вложенные два уровня глубоко, как ваша есть. Тогда пишите:

luaValue :: GenParser Char st LuaValue 
luaValue = (LuaString <$> identifier) 
     <|> (LuaList <$> between (string "{") (string "}") (sepBy (string ",") luaValue)) 

Это анализатор для luaValue. Тогда вам просто нужно написать:

binding :: GenParser Char st Binding 
content :: GenParser Char st [Binding] 

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

+0

Тип данных Variable предназначен для инкапсуляции имени переменной и содержимого переменной в кортеж. Я вижу из ваших и ответов Martijn, что это не лучший способ действия, потому что мне нужен дополнительный слой абстракции. У меня есть вопрос о вашем ответе: Какая цель - операторов, работающих в вашем коде? (Я думал, что они просто для сообщений об ошибках). – GraemeFore

+0

Я не использовал ''. Может быть, вы имеете в виду '<$>'. Это просто сокращенная информация для 'fmap', поэтому' LuaString <$> идентификатор' означает «разобрать идентификатор», а затем обернуть результат с помощью функции «LuaString». – luqui

+0

Yikes! Прекрасный пример того, почему вы не должны публиковать вопросы в течение всего времени. Еще раз спасибо. – GraemeFore

3

Действительно, парсеры, переданные оператору выбора должны иметь одинаковые типы. Вы можете сказать, по типу оператора выбора:

(<|>) :: GenParser tok st a -> GenParser tok st a -> GenParser tok st a 

Это говорит о том, что он будет счастливо сочетать два парсера, пока их символические типы, типы государственных и типы результатов одинаковы.

Итак, как мы убеждаемся, что те парсеры, которые вы пытаетесь объединить, имеют одинаковый тип результата? Ну, у вас уже есть тип данных Variable, который отображает различные формы переменных, которые могут появляться в Lua, поэтому нам нужно сделать не возврат String, [String] или [[String]], а только Variable s.

Но когда мы пытаемся, мы сталкиваемся с проблемой. Мы не можем допустить nestList и т. Д. Return Variable s еще потому, что конструкторы Variable требуют имен переменных, и мы пока не знаем их. Для этого есть обходные пути (например, вернуть функцию String -> Variable, которая все еще ожидает это имя переменной), но есть лучшее решение: отделить имя переменной от разных значений, которые может иметь переменная.

data Variable = Variable String Value 
    deriving Show 

data Value = LuaString String 
      | LuaList [Value] 
      deriving (Show) 

Обратите внимание, что я удалил конструктор NestedLuaList. Я изменил LuaList, чтобы принять список Value s, а не String s, поэтому вложенный список теперь можно выразить как LuaList из LuaList s. Это позволяет спискам быть вложенными произвольно глубокими, а не только двумя уровнями, как в вашем примере. Я не знаю, разрешено ли это в Lua, но это упростило запись парсеров. :-)

Теперь мы можем позволить lList и nestList возврат товара Value S:

lList :: GenParser Char st Value 
lList = do 
    ss <- between (string "{") (string "}") (sepBy varContent (string ",")) 
    return (LuaList (map LuaString ss)) 

nestList :: GenParser Char st Value 
nestList = do 
    vs <- between (string "{") (string "}") (sepBy lList (string ",")) 
    return (LuaList vs) 

И varName, который я переименовал variable здесь, теперь возвращает Variable:

variable :: GenParser Char st Variable 
variable = do 
    vName <- (many letter) 
    eq <- string " = " 
    vCon <- try nestList 
     <|> try lList 
     <|> (do v <- varContent; return (LuaString v)) 
    return (Variable vName vCon) 

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

Надеюсь, это поможет!

+0

Это очень помогает. Я думаю, вы правы, что мой тип данных был недостаточным для того, что я хотел сделать. Хотя я нахожу систему типов Haskell захватывающей, я должен признать, что я еще не совсем понял ее. Такие вещи, как правильное определение рекурсивных типов данных, все еще меня задевают. – GraemeFore

Смежные вопросы