Действительно, парсеры, передаваемые оператору выбора, должны иметь одинаковые типы. Вы можете сказать по типу оператора выбора:
(<|>) :: GenParser tok st a -> GenParser tok st a -> GenParser tok st a
Это говорит о том, что он удачно объединит два парсера, если их типы токенов, типы состояний и типы результатов одинаковы.
Итак, как мы можем убедиться, что те парсеры, которые вы пытаетесь объединить, имеют одинаковый тип результата? Ну, у вас уже есть тип данных Variable
, который фиксирует различные формы переменных, которые могут появиться в Lua, поэтому нам нужно не вернуть String
, [String]
или [[String]]
, а просто Variable
с.
Но когда мы пытаемся это сделать, мы сталкиваемся с проблемой. Мы не можем позволить nestList
и т. Д. Возвращать Variable
с, поскольку конструкторы Variable
требуют имен переменных, и мы пока не знаем их. Для этого есть обходные пути (например, возвращать функцию String -> Variable
, которая все еще ожидает это имя переменной), но есть лучшее решение: отделить имя переменной от различных типов значений, которые может иметь переменная.
data Variable = Variable String Value
deriving Show
data Value = LuaString String
| LuaList [Value]
deriving (Show)
Обратите внимание, что я удалил конструктор NestedLuaList
. Я изменил LuaList
, чтобы принимать список Value
с, а не String
с, поэтому вложенный список теперь можно выразить как LuaList
из LuaList
с. Это позволяет произвольно углублять списки, а не только два уровня, как в вашем примере. Я не знаю, разрешено ли это в 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)
Я думаю, вы обнаружите, что когда вы запускаете парсер на каком-то вводе, все еще есть некоторые проблемы, но вы уже намного ближе к решению, чем раньше.
Надеюсь, это поможет!