如何使FsLex规则尾巴

我正在使用fslex / fsyacc实现脚本语言,并且遇到大量用户输入(即> 1k脚本)的问题,尤其是当词法分析器分析一个非常大的字符串时发生此错误。

我在自定义词法分析器函数中扫描字符串(以允许用户使用带反斜杠的引号来转义字符)。 功能如下:

and myText pos builder = parse 
| '' escapable                 { let char = lexbuf.LexemeChar(1)
                                   builder.Append (escapeSpecialTextChar char) |> ignore
                                   myText pos builder lexbuf 
| newline                        { newline lexbuf; 
                                   builder.Append Environment.NewLine |> ignore 
                                   myText pos builder lexbuf 
                                 }
| (whitespace | letter | digit)+ { builder.Append (lexeme lexbuf) |> ignore
                                   myText pos builder lexbuf     
                                 } // scan as many regular characters as possible
| '"'                            { TEXT (builder.ToString())   } // finished reading myText
| eof                            { raise (LexerError (sprintf "The text started at line %d, column %d has not been closed with "." pos.pos_lnum (pos.pos_cnum - pos.pos_bol))) }
| _                              { builder.Append (lexbuf.LexemeChar 0) |> ignore
                                   myText pos builder lexbuf 
                                 } // read all other characters individually

函数只是解释各种字符,然后递归地调用它自己 - 或者在读取结束引号时返回读取字符串。 当输入过大时,将在_(whitespace | ...规则中引发StackOverflowException

生成的Lexer.fs包含以下内容:

(* Rule myText *)
and myText pos builder (lexbuf : Microsoft.FSharp.Text.Lexing.LexBuffer<_>) = _fslex_myText pos builder 21 lexbuf
// [...] 

and _fslex_myText pos builder _fslex_state lexbuf =
  match _fslex_tables.Interpret(_fslex_state,lexbuf) with
  | 0 -> ( 
# 105 "Lexer.fsl"
                                                   let char = lexbuf.LexemeChar(1) in
                                                   builder.Append (escapeSpecialTextChar char) |> ignore
                                                   myText pos builder lexbuf 
// [...]

所以实际的规则变成_fslex_myTextmyText用一些内部的_fslex_state调用它。

我认为这是问题所在: myText不是尾递归的,因为它被转换为两个互相调用的函数,这些函数在某些时候会导致StackOverflowException

所以我的问题确实是:

1)我的假设是否正确? 或者,F#编译器是否可以像使用尾递归一样优化这两个函数,并生成一个while循环而不是递归调用? (函数式编程对我来说仍然是新的)

2)我能做些什么来实现函数尾递归/防止StackOverflowException ? FsLex文档并不是那么广泛,我不知道官方的F#编译器做了什么不同的事情 - 显然它对大字符串没有问题,但是它的字符串规则也会递归地调用它自己,所以我在这里感到茫然: /


正如所写的,你的myText函数似乎没问题--F#编译器应该能够进行尾递归。

有一件事要检查 - 你是在Debug还是Release配置中编译/运行它? 默认情况下,F#编译器仅在Release配置中执行tail-call优化。 如果你在调试的时候需要tail-calls,你需要在项目的属性中(在Build选项卡上)明确地启用它。

链接地址: http://www.djcxy.com/p/80569.html

上一篇: How to make FsLex rules tail

下一篇: Conversion to tail recursion