Lua在fslex中长串

Rai*_*ine 6 lua f# fslex ocamllex

我在业余时间一直在研究Lua fslex lexer,使用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);
               (* ... *)           
           }

但是我被卡住了,原因有两个:第一,我不认为我可以"推",可以说,一旦我读完长串,就会成为下一个规则的标记; 第二,我不喜欢通过char读取char的想法,直到找到正确的结束标记,使得当前的设计无用.

如何在fslex中标记Lua长字符串?谢谢阅读.

Rai*_*ine 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
Run Code Online (Sandbox Code Playgroud)

也许它不是很实用,但它似乎正在完成工作.

  • 使用tokenize规则,直到找到长括号的开头
  • 切换到longstring规则并循环,直到找到相同级别的结束长括号
  • 将每个与同一级别的结束长括号不匹配的词汇存储到StringBuilder中,而StringBuilder又存储在LexBuffer BufferLocalStore中.
  • 一旦longstring结束,清除BufferLocalStore.

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