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