Я пытаюсь проанализировать стандартные простые типы (в смысле лямбда-исчисления), используя FParsec, но у меня возникают трудности при переходе от стиля Lex / Yacc к стилю, используемому в FParsec, особенно в отношении рекурсивных определений.
Примеры типов, которые я пытаюсь проанализировать:
- о
- o -> o
- (o -> o -> o) -> o
А вот моя попытка:
type SType =
| Atom
| Arrow of SType * SType
let ws = spaces
let stype, styperef = createParserForwardedToRef()
let atom = pchar 'o' .>> ws |>> (fun _ -> Atom)
let arrow = pipe2 (stype .>> (pstring "->" .>> ws))
stype
(fun t1 t2 -> Arrow (t1,t2))
let arr = parse {
let! t1 = stype
do! ws
let! _ = pstring "->"
let! t2 = stype
do! ws
return Arrow (t1,t2)
}
styperef := choice [ pchar '(' >>. stype .>> pchar ')';
arr;
atom ]
let _ = run stype "o -> o"`
Когда я загружаю это в интерактив, последняя строка вызывает переполнение стека (по иронии судьбы довольно трудно искать в эти дни). Я могу себе представить, почему, учитывая, что есть рекурсивные ссылки, но я бы подумал, что однозначный прогноз не позволил бы после первого (заключенного в скобки) выбора в stype
. Поэтому я предполагаю, что он должен выбирать arr
, который выбирает stype
и так далее. Но как предотвратить этот цикл?
Меня интересуют комментарии по поводу идиоматического использования библиотеки, а также исправления к моей попытке решения.