在F#中同时进行Leading和parsing
当使用fslex和fsyacc时,有没有一种简单的方法可以让lexing和parsing同时运行?
首先在实际情况下,lexing和parsing是时间关键的。 特别是如果你需要在parsing之前处理令牌。 例如 – 过滤和收集评论或解决依赖于上下文的冲突。 在这种情况下,parsing器经常等待一个词法分析器。
一个问题的答案。 您可以与MailboxProcessor同时运行Leading和parsing。
理念的核心。 你可以在mailBoxProcessor中运行词法分析器。 Lexer应该产生新的令牌,处理并发布它们。 Lexer通常比parsing器快,有时它应该等待parsing器。 parsing器可以在需要时接收下一个令牌。 代码如下。 您可以修改超时,traceStep以find最适合您的解决scheme。
[<Literal>] let traceStep = 200000L let tokenizerFun = let lexbuf = Lexing.LexBuffer<_>.FromTextReader sr let timeOfIteration = ref System.DateTime.Now fun (chan:MailboxProcessor<lexer_reply>) -> let post = chan.Post async { while not lexbuf.IsPastEndOfStream do lastTokenNum := 1L + !lastTokenNum if (!lastTokenNum % traceStep) = 0L then let oldTime = !timeOfIteration timeOfIteration := System.DateTime.Now let mSeconds = int64 ((!timeOfIteration - oldTime).Duration().TotalMilliseconds) if int64 chan.CurrentQueueLength > 2L * traceStep then int (int64 chan.CurrentQueueLength * mSeconds / traceStep) |> System.Threading.Thread.Sleep let tok = Calc.Lexer.token lexbuf // Process tokens. Filter comments. Add some context-depenede information. post tok } use tokenizer = new MailboxProcessor<_>(tokenizerFun) let getNextToken (lexbuf:Lexing.LexBuffer<_>) = let res = tokenizer.Receive 150000 |> Async.RunSynchronously i := 1L + !i if (!i % traceStep) = 0L then let oldTime = !timeOfIteration timeOfIteration := System.DateTime.Now let seconds = (!timeOfIteration - oldTime).TotalSeconds res let res = tokenizer.Start() Calc.Parser.file getNextToken <| Lexing.LexBuffer<_>.FromString "*this is stub*"
完整的解决scheme可以在这里find: https : //github.com/YaccConstructor/ConcurrentLexPars在这个解决scheme中,我们只展示了所描述的想法的完整实现。 性能比较不是实际的,因为语义计算非常简单并且不需要令牌处理。
要查看性能比较结果,请查看完整报告https://docs.google.com/document/d/1K43g5jokNKFOEHQJVlHM1gVhZZ7vFK2g9CJHyAVtUtg/edit?usp=sharing这里我们比较T-SQL子集的parsing器的顺序和并发解决scheme的性能。; 顺序:27秒,并发:20秒。
我们也在生产T-SQL翻译器中使用这种技术。