Я столкнулся с проблемой парсеров с двумя ветвями рекурсии. Чтобы упростить демонстрацию проблемы, я использую простую грамматику лямбда-исчисления из статья, написанная Лукой Болоньезе в качестве примера:
<expression> ::= <name> | <function> | <application> <name> ::= nonblank character sequence <function> ::= \ <name> . <body> <body> ::= <expression> <application> ::= ( <function expression> <argument expression> ) <function expression> ::= <expression> <argument expression> ::= <expression>
Парсер в статье достаточно лаконичен:
let ws = " \t\n"
let specialChars = ".)(\\\n"
let pWs = spaces
let pName = manyChars (noneOf (ws + specialChars)) |>> EName
let pExpr, pExprRef = createParserForwardedToRef<Expression, Unit>()
let curry2 f a b = f(a,b)
let pFunction = pchar '\\' >>. pipe2 pName (pchar '.' >>. pExpr) (curry2 Function)
let pApplication = pchar '(' >>. pipe2 pExpr (pWs >>. pExpr) (curry2 Application)
.>> pWs .>> pchar ')'
do pExprRef := pFunction <|> pApplication <|> pName
let pExpressions = sepBy pExpr spaces1
let fparseString text =
match run pExpressions text with
| Success(result, _, _) -> result
| Failure(errorMsg, _, _) -> failwith (sprintf "Failure: %s" errorMsg)
Меня интересуют pApplication
, так как они состоят из двух pExpr
, которые, в свою очередь, тоже могут быть pApplication
. Парсеру не хватает места в стеке в следующем тесте:
let generateString level =
let rec loop i =
seq {
if i < level then
yield "("
yield! loop level
yield " "
yield! loop (i+1)
yield ")"
else
yield "(x x)"
}
loop 0 |> String.concat ""
let N = 5000
let s = generateString N;;
let _ = fparseString s;;
Как переписать синтаксический анализатор, чтобы он был хвостовой рекурсией?
Я обнаружил проблему, когда пытался написать парсер для Lisp-подобного языка и протестировать его с помощью реальных тестов. У меня есть Term
и VarBinding
, которые являются взаимно рекурсивными типами, и анализатор let
, который демонстрирует ту же проблему, что и pApplication
выше. Мой синтаксический анализатор на github на случай, если анализ будет неверным в отношении нехвоста -рекурсивная задача.