Я пытаюсь написать расширенную форму Бэкуса-Наура синтаксический анализатор.Тем не менее, я сталкиваюсь с исключением переполнения стека всякий раз, когда я пытаюсь проанализировать альтернативы.Ниже приведен пример, который вызывает проблему:
#r @"..\packages\FParsec\lib\net40-client\FParsecCS.dll"
#r @"..\packages\FParsec\lib\net40-client\FParsec.dll"
open FParsec
type Parser<'t> = Parser<'t, unit>
type Element =
| Alternates of Element list
| ParsedString of string
let (pRuleElement, pRuleElementRef) : (Parser<Element> * Parser<Element> ref) = createParserForwardedToRef()
let pString =
pchar '"' >>. manyCharsTill (noneOf ['"']) (pchar '"')
|>> ParsedString
let pAlternates : Parser<_> =
sepBy1 pRuleElement (many (pchar ' ') >>. (pchar '/') >>. many (pchar ' ') )
|>> Alternates
do pRuleElementRef :=
choice
[
pString
pAlternates
]
"\"0\" / \"1\" / \"2\" / \"3\" / \"4\" / \"5\" / \"6\" / \"7\""
|> run (pRuleElement .>> (skipNewline <|> eof))
Проблема легко решается простым переупорядочением choice
следующим образом:
do pRuleElementRef :=
choice
[
pAlternates
pString
]
Однако это вызывает переполнение стека.потому что он постоянно пытается проанализировать новую последовательность альтернатив без использования входных данных.Кроме того, этот метод затем нарушит приоритет ABNF:
- Строки, формирование имен
- Комментарий
- Диапазон значений
- Повтор
- Группировка, необязательно
- Объединение
- Альтернатива
Мой вопрос сводится к следующему: Как я могу объединить разбор одного элемента, который может быть последовательностьюэлементов или один экземпляр элемента?Пожалуйста, дайте мне знать, если вам нужны какие-либо пояснения / дополнительные примеры.
Ваша помощь очень ценится, спасибо!
РЕДАКТИРОВАТЬ:
Я, вероятно, должен упомянуть, что есть различныеи другие виды группировок.Группа последовательностей (element[s])
и необязательная группа [optional element[s]
.Где element
может быть вложенными группами / необязательными группами / строками / другими типами элементов.Ниже приведен пример с разбором групп последовательностей (необязательный разбор групп не включен для простоты):
#r @"..\packages\FParsec\lib\net40-client\FParsecCS.dll"
#r @"..\packages\FParsec\lib\net40-client\FParsec.dll"
open FParsec
type Parser<'t> = Parser<'t, unit>
type Element =
| Alternates of Element list
| SequenceGroup of Element list
| ParsedString of string
let (pRuleElement, pRuleElementRef) : (Parser<Element> * Parser<Element> ref) = createParserForwardedToRef()
let pString =
pchar '"' >>. manyCharsTill (noneOf ['"']) (pchar '"')
|>> ParsedString
let pAlternates : Parser<_> =
pipe2
(pRuleElement .>> (many (pchar ' ') >>. (pchar '/') >>. many (pchar ' ')))
(sepBy1 pRuleElement (many (pchar ' ') >>. (pchar '/') >>. many (pchar ' ') ))
(fun first rest -> first :: rest)
|>> Alternates
let pSequenceGroup : Parser<_> =
between (pchar '(') (pchar ')') (sepBy1 pRuleElement (pchar ' '))
|>> SequenceGroup
do pRuleElementRef :=
choice
[
pAlternates
pSequenceGroup
pString
]
"\"0\" / ((\"1\" \"2\") / \"2\") / \"3\" / (\"4\" / \"5\") / \"6\" / \"7\""
|> run (pRuleElement .>> (skipNewline <|> eof))
Если я сначала пытаюсь проанализировать группы альтернатив / последовательностей, он завершается с исключением stack overflow
, потому что затем пытаетсяповторно анализировать альтернативы.