Разбор типа стрелки с помощью FParsec

Я пытаюсь разобрать тип стрелки с FParsec. То есть это:

Int -> Int -> Int -> Float -> Char

Например.

Я пытался с этим кодом, но он работает только для одного типа стрелки (Int -> Int) и не более. Я также хочу избежать скобок, потому что у меня уже есть тип кортежа, который использует их, и я не хочу, чтобы он был слишком тяжелым с точки зрения синтаксиса.

let ws = pspaces >>. many pspaces |>> (fun _ -> ())

let str_ws s = pstring s .>> ws

type Type = ArrowType of Type * Type

let arrowtype' =
    pipe2
        (ws >>. ty')
        (ws >>. str_ws "->" >>. ws >>. ty')
        (fun t1 t2 -> ArrowType(t1, t2))

let arrowtype =
    pipe2
        (ws >>. ty' <|> arrowtype')
        (ws >>. str_ws "->" >>. ws >>. ty' <|> arrowtype')
        (fun t1 t2 -> ArrowType(t1, t2)) <?> "arrow type"

ty' это просто другие типы, такие как кортеж или идентификатор.

У тебя есть решение?

1 ответ

Решение

Прежде чем перейти к синтаксису стрелок, я хочу прокомментировать ваш ws синтаксический анализатор. С помощью |>> (fun _ -> ()) немного неэффективно, так как FParsec должен создать объект результата, а затем сразу же выбросить его. Встроенный spaces а также spaces1 парсеры, вероятно, лучше для ваших нужд, так как им не нужно создавать объект результата.

Теперь, что касается проблемы, с которой вы боретесь, мне кажется, что вы хотите рассмотреть анализатор стрелок немного по-другому. Как насчет того, чтобы рассматривать его как серию типов, разделенных -> и используя sepBy семейство синтаксических анализаторов? Что-то вроде этого:

let arrow = spaces1 >>. pstring "->" .>> spaces1
let arrowlist = sepBy1 ty' arrow
let arrowtype = arrowlist |>> (fun types ->
    types |> List.reduce (fun ty1 ty2 -> ArrowType(ty1, ty2))

Обратите внимание, что arrowlist Парсер также будет соответствовать просто Int потому что определение sepBy1 это не "должен быть хотя бы один разделитель списка", а скорее "должен быть хотя бы один элемент в списке". Таким образом, чтобы различать тип Int и тип стрелки, вы хотели бы сделать что-то вроде:

let typeAlone = ty' .>> notFollowedBy arrow
let typeOrArrow = attempt typeAlone <|> arrowtype

Использование attempt здесь необходимо, чтобы символы, ty' будет возвращен, если стрелка присутствовала.

Есть усложняющий фактор, который я вообще не учел, так как вы упомянули, что не хотите скобок. Но если вы решите, что хотите иметь типы стрелок с типами стрелок (то есть с функциями, которые принимают функции в качестве входных данных), вам нужно проанализировать такие типы, как (Int -> Int) -> (Int -> Float) -> Char, Это усложнит использование sepBy и я не обращался к этому вообще. Если вам понадобится более сложный синтаксический анализ, включая скобки, возможно, вы захотите использовать OperatorPrecedenceParser, Но для ваших простых потребностей, где скобки не участвуют, sepBy1 выглядит как ваша лучшая ставка.

Наконец, я должен дать ПРЕДУПРЕЖДЕНИЕ: я не проверял это вообще, просто напечатал это в поле переполнения стека. Пример кода, который я дал вам, предназначен не для того, чтобы работать как есть, а для того, чтобы дать вам представление о том, как действовать дальше. Если вам нужен пример "как есть", я с радостью постараюсь вам его дать, но сейчас у меня нет времени на это.

Другие вопросы по тегам