6

私は暇なときにLuafslexlexerに取り組んでおり、ocamllexのマニュアルを参考にしています。

長い文字列を正しくトークン化しようとしているときに、いくつかの問題が発生しました。「長い文字列」はとトークンで区切られ'[' ('=')* '['ます。標識']' ('=')* ']'の数は同じでなければなりません。=

最初の実装では、レクサーは[[パターンを認識しないようでLBRACKET、最長の一致ルールにもかかわらず2つのトークンを生成しましたが[=[、バリエーションは正しく認識されました。さらに、正規表現は']' ('=')* ']'、実際の長い文字列の「レベル」に関係なく、正しい終了トークンが使用されていることを確認できず、最初のキャプチャで停止しました。また、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);
               (* ... *)           
           }

しかし、2つの理由で行き詰まっています。1つは、長い文字列を読み終えたら、いわば次のルールへのトークンを「プッシュ」できないと思います。次に、正しい終了トークンが見つかるまで文字ごとに読み取るというアイデアが好きではないため、現在のデザインは役に立たなくなります。

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

おそらくそれはあまり機能的ではありませんが、それは仕事を成し遂げているようです。

  • 長い括弧の先頭が見つかるまで、tokenizeルールを使用します
  • ロングストリングルールに切り替えて、同じレベルの閉じている長いブラケットが見つかるまでループします
  • 同じレベルの閉じている長いブラケットと一致しないすべての語彙素をStringBuilderに格納し、StringBuilderはLexBufferBufferLocalStoreに格納されます。
  • ロングストリングが終了したら、BufferLocalStoreをクリアします。

編集:プロジェクトはhttp://ironlua.codeplex.comで見つけることができます。字句解析と構文解析は問題ないはずです。DLRの使用を計画しています。コメントと建設的な批判を歓迎します。

于 2010-12-05T13:17:24.197 に答える