我在业余时间使用ocamllex手册作为参考,一直在研究Lua fslex lexer。
我试图正确地标记长字符串时遇到了一些障碍。 “长字符串”由'[' ('=')* '['
和']' ('=')* ']'
令牌分隔; =
符号的数量必须相同。
在第一个实现中,词法分析器似乎无法识别[[
模式,尽管匹配规则最长,但产生了两个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);
(* ... *)
}
但是我被困了,原因有两个:首先,我不认为我可以“推”,可以说,一旦我读完长串,就会成为下一个规则的标记;第二,我不喜欢通过char读取char的想法,直到找到正确的结束标记,使得当前的设计无用。
如何在fslex中标记Lua长字符串?谢谢你的阅读。
答案 0 :(得分:5)
如果我回答我自己的问题,请道歉,但我想为自己的问题做出贡献,以备将来参考。
我使用LexBuffer< _> .BufferLocalStore属性在lexer函数调用中保持状态,该属性只是一个可写的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
也许它不是很实用,但它似乎正在完成工作。
编辑:您可以在http://ironlua.codeplex.com找到该项目。 Lexing和解析应该没问题。我打算使用DLR。评论和建设性的批评欢迎。