2010-12-04 9 views
6

ocamllexのマニュアルを参考にして、私は暇な時間にLua fslex lexerに取り組んできました。fslexのLua長い文字列

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

最初の実装では、レクサーは一致ルールが最も長くても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); 
       (* ... *)   
      } 
 

しかし、私はこだわっている、二つの理由:まず、私は「私ができるとは思いません言い換えると、長い文字列を読み終えたら、次の規則へのトークンを押します。第二に、私は現在のデザインが役に立たないように、右の閉じトークンが見つかるまでcharをcharで読むという考え方が嫌いです。

fslexでLuaの長い文字列をどのようにトークン化できますか?読んでくれてありがとう。

+0

オフハンドでは、ちょうど言いたがっています:あなたはそれをレックスではなく常に解析することを選択する必要があります。 – Brian

+0

@Brian、詳しく教えていただけますか?:)私は、元の長い文字列を作成するために無関係なトークンのシーケンスを解析する方法を理解しようとしていますが、文字列のすべての内容に対してトークンを生成することができます。ご意見ありがとうございます。 – Raine

+0

ええ、それはおそらく良い戦略ではない、私はそこにそれを投げていた。 – Brian

答えて

5

私自身の質問には謝りがありますが、今後の参考になるように私自身の解決策を提供したいと思います。

私は、LexBuffer < _>を使ってレクサー関数呼び出しの間で状態を保持しています。BufferLocalStoreプロパティは、単なる書き込み可能なIDictionaryインスタンスです。

注:長い角かっこは、長い文字列と複数行のコメントの両方で使用されます。これは、しばしばLua文法の見過ごされる部分です。

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 

は、おそらくそれは非常に機能していないのですが、それは仕事を取得しているように見える:

 


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に格納したデータを簡素化するために使用する関数です。

  • 長いブラケットの開始をのLongStringルールとループへ
  • スイッチが見つかるまで
  • ストア一致しないすべての語彙素を発見しているのと同じレベルの閉じ長括弧までトークン化ルールを使用同じレベルの閉じたロングブラケットをStringBuilderに挿入します。これはLexBuffer BufferLocalStoreに格納されます。
  • ロングストリングが終わったら、BufferLocalStoreをクリアします。

編集:このプロジェクトはhttp://ironlua.codeplex.comにあります。レキシングと解析は大丈夫です。私はDLRの使用を計画しています。コメントと建設的な批判を歓迎します。

+0

それがうまくいくなら、答えを受け入れます;-) –