Haskell/Parsec: как использовать Text.Parsec.Token с Text.Parsec.Indent(из пакета отступов) - программирование
Подтвердить что ты не робот

Haskell/Parsec: как использовать Text.Parsec.Token с Text.Parsec.Indent(из пакета отступов)

Пакет indents для Haskell Parsec обеспечивает способ анализа языков в стиле отступа (например, Haskell и Python). Он переопределяет тип Parser, поэтому как вы используете функции парсера токенов, экспортируемые модулем Parsec Text.Parsec.Token, которые имеют обычный тип Parser?

Фон

Parsec поставляется с загрузкой модулей. большинство из них экспортируют кучу полезных парсеров (например, newline из Text.Parsec.Char, который анализирует новую строку) или комбинаторы парсеров (например, count n p из Text.Parsec.Combinator, который запускает парсер p, n раз)

Однако модуль Text.Parsec.Token хотел бы экспортировать функции, которые параметризованы пользователем с функциями анализируемого языка, так что, например, Функция braces p запустит парсер p после разбора '{' и до синтаксического анализа '}', игнорируя такие вещи, как комментарии, синтаксис которых зависит от вашего языка.

Способ, которым Text.Parsec.Token достигает этого, заключается в том, что он экспортирует одну функцию makeTokenParser, который вы вызываете, предоставляя ему параметры вашего конкретного языка (например, как выглядит комментарий) и возвращает запись, содержащую все функции в Text.Parsec.Token, адаптированные на ваш язык, как указано.

Конечно, в языке с отступом они должны быть адаптированы (возможно, здесь, где я не уверен - я объясню в одно мгновение), поэтому я отмечаю, что (предположительно устаревший) пакет IndentParser предоставляет модуль Text.ParserCombinators.Parsec.IndentParser.Token, который выглядит как замена для Text.Parsec.Token.

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

Моя проблема

По нескольким небольшим причинам мне кажется, что пакет indents больше или меньше текущей версии IndentParser, однако он не предоставляет модуль который выглядит как Text.ParserCombinators.Parsec.IndentParser.Token, он предоставляет только Text.Parsec.Indent, поэтому Мне интересно, как сделать все парсера-маркера из Text.Parsec.Token (например, reserved "something", который анализирует зарезервированное ключевое слово "что-то" или как braces, о котором я упоминал ранее).

Мне кажется, что (новый) Text.Parsec.Indent работает с помощью какой-то монадической магии штата, чтобы разобраться в каких битах столбца исходного кода, так что ему не нужно модифицировать парсеров-маркеров, таких как whiteSpace от Text.Parsec.Token, что, вероятно, поэтому не дает заменяющего модуля. Но у меня проблема с типами.

Вы видите, что без Text.Parsec.Indent все мои парсеры имеют тип Parser Something, где Something - тип возвращаемого значения, а Parser - это псевдоним типа, определенный в Text.Parsec.String как

type Parser = Parsec String ()

но с Text.Parsec.Indent вместо импорта Text.Parsec.String я использую собственное определение

type Parser a = IndentParser String () a

который делает все мои парсеры типа IndentParser String () Something, где IndentParser определен в Text.Parsec.Indent. но анализаторы токенов, которые я получаю от makeTokenParser в Text.Parsec.Token, имеют неправильный тип.

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


Ошибка, которую я получаю, заключается в том, что я попытался заменить одно определение Parser выше на другое, но тогда, когда я пытаюсь использовать один из парсеров токенов из Text.Parsec.Token, я получаю ошибку компиляции.

Couldn't match expected type `Control.Monad.Trans.State.Lazy.State
                                Text.Parsec.Pos.SourcePos'
            with actual type `Data.Functor.Identity.Identity'
Expected type: P.GenTokenParser
                 String
                 ()
                 (Control.Monad.Trans.State.Lazy.State Text.Parsec.Pos.SourcePos)
  Actual type: P.TokenParser ()

Ссылки

К сожалению, ни один из приведенных выше примеров не использует парсера парсера, как те, что находятся в Text.Parsec.Token.

4b9b3361

Ответ 1

Что вы пытаетесь сделать?

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

Parser Something

(где Something - тип возвращаемого значения) и сделать эту работу, скрывая и переопределяя тип Parser, который обычно импортируется из Text.Parsec.String или аналогичного. Вам все равно нужно импортировать часть Text.Parsec.String, чтобы сделать Stream экземпляром монады; сделайте это с помощью строки:

import Text.Parsec.String ()

Ваше определение Parser верное. Альтернативно и эквивалентно (для тех, кто следит за чатом в комментариях), вы можете использовать

import Control.Monad.State
import Text.Parsec.Pos (SourcePos)

type Parser = ParsecT String () (State SourcePos)

и, возможно, удалите файл import Text.Parsec.Indent (IndentParser) в файле, в котором отображается это определение.

Ошибка, ошибка на стене

Ваша проблема в том, что вы смотрите на неправильную часть сообщения об ошибке компилятора. Вы фокусируетесь на

Couldn't match expected type `State SourcePos' with actual type `Identity'

когда вы должны сосредоточиться на

Expected type: P.GenTokenParser ...
  Actual type: P.TokenParser ...

Он компилируется!

Если вы "импортируете" парсеров из Text.Parsec.Token, то, что вы на самом деле делаете, конечно (как вы кратко упоминали), сначала нужно определить запись ваших параметров языка, а затем передать это функции makeTokenParser, которая возвращает запись, содержащая маркеры парсера.

Поэтому вы должны иметь некоторые строки, которые выглядят примерно так:

import qualified Text.Parsec.Token as P

beetleDef :: P.LanguageDef st
beetleDef =
    haskellStyle {
        parameters, parameters etc.
        }

lexer :: P.TokenParser ()
lexer = P.makeTokenParser beetleDef

... но a P.LanguageDef st является просто a GenLanguageDef String st Identity, a P.TokenParser () действительно является GenTokenParser String () Identity.

Вы должны изменить объявления своего типа на следующее:

import Control.Monad.State
import Text.Parsec.Pos (SourcePos)
import qualified Text.Parsec.Token as P

beetleDef :: P.GenLanguageDef String st (State SourcePos)
beetleDef =
    haskellStyle {
        parameters, parameters etc.
        }

lexer :: P.GenTokenParser String () (State SourcePos)
lexer = P.makeTokenParser beetleDef

... и это! Это позволит вашим "импортированным" синтаксическим анализаторам токенов иметь тип ParsecT String () (State SourcePos) Something вместо Parsec String () Something (который является псевдонимом для ParsecT String () Identity Something), и ваш код теперь должен компилироваться.

(Для максимальной общности, я предполагаю, что вы можете определять тип Parser в отдельном файле и импортировать файл, в котором вы определяете свои фактические функции парсера. Следовательно, два повторенных import заявления.)

Спасибо

Большое спасибо Daniel Fischer за помощь в этом.