Lua长字符串在fslex中的应用

6

我在业余时间里一直在开发一个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长字符串?谢谢阅读。


顺便提一下,你总是可以选择解析它而不是词法分析。 - Brian
@Brian,您能否详细解释一下? :) 我有点不知所措,试图理解如何解析一系列无关的标记以创建原始长字符串 - 假设词法分析器可以为字符串的所有内容生成标记。感谢您的评论。 - raine
是的,这可能不是一个好策略,我只是随口说了一下。 - Brian
@Brian 非常感谢,我还在努力掌握 F# 和 fslex,每一点帮助都很重要。 - raine
@Raine 无论如何,请保持我们的联系;我也对F#和Lua都很感兴趣。 - TechNeilogy
1个回答

5

如果我回答了自己的问题,我很抱歉,但我想为以后的参考贡献我的解决方案。

我使用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。欢迎评论和建设性批评。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接