Lua在fslex中的长字符串

我闲暇时间一直在做一个Lua的fslex词法分析器,并参考了ocamllex手册。

在尝试正确对长字符串进行标记化时,遇到了一些问题。“长字符串”由 '[' ('=')* '['']' ('=')* ']'标记限定; "="号的数量必须相同。

在第一次实现时,词法分析器似乎无法识别 [[ 模式,尽管应该符合最长匹配原则,产生了两个LBRACKET标记,而 [=[ 和其他变体正确识别。此外,正则表达式无法确保使用正确的关闭标记,在第一个 ']' ('=')* ']' 捕获时停止,无论实际的长字符串“级别”如何。此外,fslex似乎不支持正则表达式中的“as”构造。


let lualongstring =    '[' ('=')* '[' ( escapeseq | [^ '\\' '[' ] )* ']' ('=')* ']'

(* ... *)
    | lualongstring    { (* ... *) }
    | '['              { LBRACKET }
    | ']'              { RBRACKET }
(* ... *)

我一直在尝试使用词法分析器中的另一条规则解决这个问题:


rule tokenize = parse
    (* ... *)
    | '[' ('=')* '['   { longstring (getLongStringLevel(lexeme lexbuf)) lexbuf }
    (* ... *)

and longstring level = parse
    | ']' ('=')* ']'   { (* check level, do something *) }
    | _                { (* aggregate other chars *) }

    (* or *)

    | _    {
               let c = lexbuf.LexerChar(0);
               (* ... *)
           }

但我被卡住了,原因有两个:首先,我不认为我可以“推送”一个标记到下一个规则,一旦我读完了长字符串; 其次,我不喜欢一个字符一个字符地读取,直到找到正确的关闭标记为止的想法,使得当前设计无用。

如何在fslex中对Lua长字符串进行标记化?谢谢阅读。

原文链接 https://stackoverflow.com/questions/4351074

点赞
stackoverflow用户67065
stackoverflow用户67065

抱歉如果我回答自己的问题,但是我想贡献我自己的解决方案,以便于未来参考。

我使用 LexBuffer<_>.BufferLocalStore 属性在词法分析器函数调用之间保持状态,它只是一个可写的 IDictionary 实例。

注:长括号既用于长字符串,也用于多行注释。这是 Lua 语法经常被忽视的部分。


let beginlongbracket =    '[' ('=')* '['
let endlongbracket =      ']' ('=')* ']'

rule tokenize = parse
    | beginlongbracket
    { longstring (longBracketLevel(lexeme lexbuf)) lexbuf }

(* ... *)

and longstring level = parse
    | endlongbracket
    { if longBracketLevel(lexeme lexbuf) = level then
          LUASTRING(endLongString(lexbuf))
      else
          longstring level lexbuf
    }

    | _
    { toLongString lexbuf (lexeme lexbuf); longstring level lexbuf }

    | eof
    { failwith "Unexpected end of file in string." }

这里是我使用的函数,简化了将数据存储到 BufferLocalStore 的过程:

let longBracketLevel (str : string) =
    str.Count(fun c -> c = '=')

let createLongStringStorage (lexbuf : LexBuffer<_>) =
    let sb = new StringBuilder(1000)
    lexbuf.BufferLocalStore.["longstring"] <- box sb
    sb

let toLongString (lexbuf : LexBuffer<_>) (c : string) =
    let hasString, sb = lexbuf.BufferLocalStore.TryGetValue("longstring")
    let storage = if hasString then (sb :?> StringBuilder) else (createLongStringStorage lexbuf)
    storage.Append(c.[0]) |> ignore

let endLongString (lexbuf : LexBuffer<_>) : string =
    let hasString, sb = lexbuf.BufferLocalStore.TryGetValue("longstring")
    let ret = if not hasString then "" else (sb :?> StringBuilder).ToString()
    lexbuf.BufferLocalStore.Remove("longstring") |> ignore
    ret

也许这不是很函数式,但它似乎能够胜任工作。

  • 使用 tokenize 规则,直到找到长括号的开头
  • 切换到 longstring 规则并循环,直到找到相同级别的关闭长括号
  • 将不匹配相同级别的关闭长括号的每个词法符号存储到 StringBuilder,然后将其存储到 LexBuffer BufferLocalStore 中。
  • 一旦长字符串结束,清除 BufferLocalStore

编辑:你可以在 http://ironlua.codeplex.com 找到该项目。词法分析和解析应该没问题。我计划使用 DLR。欢迎评论和建设性批评。

2010-12-05 13:17:24