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 长字符串?谢谢阅读。

4

1 回答 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

也许它不是很实用,但它似乎完成了工作。

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

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

于 2010-12-05T13:17:24.197 回答