スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

F#で入門 コンパイラ、インタプリタ編 トークン化(2)

F#で入門 コンパイラ、インタプリタ編 トークン化(2) 
 
今回は前回作ったクラスを利用して、ウィンドウソフトとしてトークン化の実際を試してみます。 
 
ここでは自作の次のようなソフトを利用します。(コードは最後にのっけておきます。) 

1002-1.jpg

左のテキストボックスにトークン化のルールを入力します。 
画面ではつぎのように入力してあります。 
 
INT,int 
STRING,string 
ID,[a-z][a-z0-9]* 
NUM,0|[1-9][0-9]* 
COMMA,\. 
EQ,\= 
EX,! 
SEMI,; 
PLUS,\+ 
MINUS,\- 
MUL,\* 
DIV,\/ 
LPAR,\( 
RPAR,\) 
LE,\<\= 
GE,\>\= 
NE,\<\> 
LT,\< 
GT,\> 
EQ,\= 
 
カンマの左がトークン種別を表す文字列で、右側がマッチする正規表現です。 
>や=などは¥を付ける必要があることに留意してください。 
これで、「設定」ボタンをおすと、トークン化するためのオブジェクトが準備されますので、右上のソーステキストボックスにソースを書いて「トークン化」ボタンを押すと、トークン列が表示されます。 
 
画面ではソース 
int i = 3 
int j = (1+2)*5 
に対して、トークン列 
[INT   int  (1,1)] [ID   i  (1,5)] [EQ   =  (1,7)] [NUM   3  (1,9)] [INT   int  (2,1)] [ID   j  (2,5)] [EQ   =  (2,7)] [LPAR   (  (2,9)] [NUM   1  (2,10)] [PLUS   +  (2,11)] [NUM   2  (2,12)] [RPAR   )  (2,13)] [MUL   *  (2,14)] [NUM   5  (2,15)]  
と表示されています。 
 
なお、この例で、 
ID,[a-z][a-z0-9]*をINT,intより先の行で定義してしまうと、intが予約語ではなく、ID(識別子)として認識されてしまうので注意が必要です。 
 
例 
 
ID,[a-z][a-z0-9]* 
INT,int 
STRING,string 
 
でソース 
int string 
の場合 
 
[ID   int  (1,1)] [ID   string  (1,5)]  
 
よろしければ、トークン化ルールとソースをいろいろ変えてみて実験してみてください。 
 
では、この簡易ソフトのコードをのっけておきます。 

 open System    
open System.Windows.Forms    
open System.Drawing    
open System.Text.RegularExpressions 
 
type Token(kind:string,img:string,row:int,col:int) = 
    member this.Kind = kind 
    member this.Img = img 
    member this.Row = row 
    member this.Col = col 
 
    override this.ToString() = 
        sprintf "[%s   %s  (%d,%d)] " kind img row col 
 
//一行をトークン化したときの結果用の型 
type TokenizeOneLineResult = 
    |TOLSuccess of list<Token> 
    |TOLFail of int*int //トークン化失敗したときの行と列 
 
    member this.IsSuccess () = 
        match this with 
        |TOLSuccess(_) -> true 
        | _ -> false 
 
type TokenizerFactory (defLst:list<string*string>) = 
    //makeTokenizeRules [("INTNUM","\d+");("STR","\"[^\"]*\"");("TLE" , @"<=")]    
    //結果val it : (string * Regex) list = [("INTNUM", ^(?<sPart>\s*)(?<parts>\d+)以下略 (sPartは先頭の空白用)  
    let makeTokenizeRules (inDefLst:list<string*string>) = 
        inDefLst 
            |> List.map(fun (name,rgText) ->(name,(new Regex ( @"^(?<sPart>\s*)(?<parts>" + rgText + @")")))) 
     
    //tokenizeTopPart " 23 56 <=" (makeTokenizeRules [("INTNUM","\d+");("STR","\"[^\"]*\"");("TLE" , @"<=")]) 10 20 
    //結果(どれかにマッチする場合)   Token * int = (FSI_0013+Token {Col = 21;Img = "23";Kind = "INTNUM";Row = 10;},3) 
    //結果(どれにもマッチしない場合)Token * int = (FSI_0013+Token {Col = 0; Img = ""  ;Kind = "";      Row = 0;}, 0) 
    let tokenizeTopPart (textPart:string) (trl:list<string*Regex>) (row:int) (col:int) = 
        trl 
          |>List.fold (fun (curToken:Token,curLongestLength:int) (name,rg) -> 
                            let wholeMatch = rg.Match(textPart) 
                            let partMatch = wholeMatch.Groups.["parts"]  //必要な部分 
                            let sPartMatch = wholeMatch.Groups.["sPart"] //先頭の空白部分 
                            if wholeMatch.Value.Length > curLongestLength then //最長マッチ 
                                (new Token(name,partMatch.Value,row, col + sPartMatch.Value.Length),wholeMatch.Value.Length ) 
                            else 
                                (curToken,curLongestLength) 
                      ) 
                      (new Token("","",0,0),0) 
 
 
 
    //tokenizeOneLine  [("INTNUM","\d+");("STR","\"[^\"]*\"");("TLE" , @"<=")] 5 " 23 \"abc\" <= " 
    //結果TOLSuccess [FSI_0002+Token {Col = 2;Img = "23";Kind = "INTNUM";Row = 5;};  
    //////////////////FSI_0002+Token {Col = 5; Img = ""abc"";Kind = "STR";Row = 5;}; 
    //////////////////FSI_0002+Token {Col = 11;Img = "<=";Kind = "TLE"; Row = 5;}] 
    //tokenizeOneLine  [("INTNUM","\d+");("STR","\"[^\"]*\"");("TLE" , @"<=")] 100 " 23 fail  <= ";; 
    //結果TOLFail (100,5) 
    let tokenizeOneLine (inDefLst:list<string*string>) (inRow:int) (inOneLineStr:string) = 
        let trs = makeTokenizeRules inDefLst 
        let rec tokenizeOneLineSub (curCol:int) (remainStr:string) res = 
            if remainStr.Trim().Length = 0 then 
                TOLSuccess(List.rev res) 
            else 
               let (slicedToken,length) = tokenizeTopPart remainStr trs inRow curCol 
               if length = 0 then 
                    let topBlankNum = remainStr.Length - remainStr.TrimStart().Length 
                    TOLFail(inRow,curCol+topBlankNum) 
               else 
                  tokenizeOneLineSub (curCol + length) (remainStr.Substring(length)) (slicedToken::res)        
        tokenizeOneLineSub 1 inOneLineStr [] 
 
    //tokenizer [("INTNUM","\d+");("STR","\"[^\"]*\"");("TLE" , @"<=")] [" 23 <= ";"\"abc\"  89"];; 
    //結果 Token list = [FSI_0002+Token {Col = 2;Img = "23";Kind = "INTNUM"; Row = 1;};FSI_0002+Token {Col = 5;Img = "<=";Kind = "TLE"; Row = 1;}; 
    //                   FSI_0002+Token {Col = 1;Img = ""abc"";Kind = "STR";Row = 2;}; FSI_0002+Token {Col = 8;Img = "89"; Kind = "INTNUM";Row = 2;}] 
    //tokenizer [("INTNUM","\d+");("STR","\"[^\"]*\"");("TLE" , @"<=")] [" <<< <= ";" 23 fail  <= "];; 
    //結果 System.Exception: [TOLFail (1,2); TOLFail (2,5)] 
    let tokenizer (inDefLst:list<string*string>) (source:list<string>) = 
        let oneLineTokenizer = tokenizeOneLine inDefLst 
        let isTOLSuccess (x:TokenizeOneLineResult) = 
            match x with 
            |TOLSuccess(_) -> true 
            |_             -> false 
        let sucLst,failLst = 
            source 
            |>List.map (fun str -> str.TrimEnd()) 
            |>List.mapi (fun i str -> oneLineTokenizer (i+1) str ) 
            |>List.partition (fun tr -> tr.IsSuccess () ) 
        if failLst.Length > 0 then  
            failwith (sprintf "%A" failLst) 
        else  
            sucLst 
              |>List.map (fun tolr -> match tolr with 
                                         |TOLSuccess(tol) -> tol 
                                         | _ -> failwith "error" //これは起こらない 
                         ) 
              |>List.fold (fun s lst -> s @ lst) [] 
 
 
    member this.GetTokenizer() = 
        tokenizer defLst 
 
let f2c x = x :> System.Windows.Forms.Control  
let tokenRule_tb= new TextBox(Location = new Point(21, 74),Multiline = true,Name = "tokenRule_tb",ScrollBars = ScrollBars.Both,Size = new Size(211, 435),TabIndex = 0) 
let source_tb= new TextBox(Location = new Point(248, 41),Multiline = true,Name = "source_tb",ScrollBars = ScrollBars.Both,Size = new Size(495, 173),TabIndex = 1) 
let token_tb= new TextBox(Location = new Point(248, 255),Multiline = true,Name = "token_tb",ScrollBars = ScrollBars.Both,Size = new Size(495, 273),TabIndex = 2) 
let error_tb= new TextBox(Location = new Point(21, 574),Multiline = true,Name = "error_tb",ScrollBars = ScrollBars.Both,Size = new Size(722, 66),TabIndex = 3) 
let tokenRule_btn= new Button(Location = new Point(21, 519),Name = "tokenRule_btn",Size = new Size(211, 23),TabIndex = 4,Text = "設定",UseVisualStyleBackColor = true) 
let tokenize_btn= new Button(Location = new Point(396, 223),Name = "tokenize_btn",Size = new Size(228, 23),TabIndex = 5,Text = "↓トークン化↓",UseVisualStyleBackColor = true) 
let sourceLoad_btn= new Button(Location = new Point(535, 12),Name = "sourceLoad_btn",Size = new Size(101, 23),TabIndex = 6,Text = "ソースのLoad",UseVisualStyleBackColor = true) 
let sourceSave_btn= new Button(Location = new Point(642, 12),Name = "sourceSave_btn",Size = new Size(101, 23),TabIndex = 7,Text = "ソースのSave",UseVisualStyleBackColor = true) 
let tokenRuleSave_btn= new Button(Location = new Point(110, 45),Name = "tokenRuleSave_btn",Size = new Size(132, 23),TabIndex = 9,Text = "トークン化ルールのSave",UseVisualStyleBackColor = true) 
let tokenRuleLoad_btn= new Button(Location = new Point(109, 14),Name = "tokenRuleLoad_btn",Size = new Size(133, 23),TabIndex = 8,Text = "トークン化ルールのLoad",UseVisualStyleBackColor = true) 
let label1= new Label(AutoSize = true,Location = new Point(24, 14),Name = "label1",Size = new Size(82, 12),TabIndex = 10,Text = "トークン化ルール") 
let label2= new Label(AutoSize = true,Location = new Point(252, 21),Name = "label2",Size = new Size(33, 12),TabIndex = 11,Text = "ソース") 
let label3= new Label(AutoSize = true,Location = new Point(252, 234),Name = "label3",Size = new Size(52, 12),TabIndex = 12,Text = "トークン列") 
let label4= new Label(AutoSize = true,Location = new Point(25, 559),Name = "label4",Size = new Size(32, 12),TabIndex = 13,Text = "エラー") 
let label5= new Label(AutoSize = true,Location = new Point(24, 32),Name = "label5",Size = new Size(68, 12),TabIndex = 14,Text = "INTNUM,\\d+") 
let label6= new Label(AutoSize = true,Location = new Point(25, 44),Name = "label6",Size = new Size(34, 12),TabIndex = 15,Text = "EQ,\\=") 
let label7= new Label(AutoSize = true,Location = new Point(25, 56),Name = "label7",Size = new Size(79, 12),TabIndex = 16,Text = "というように記述") 
let mainForm= new Form(AutoScaleDimensions = new SizeF(6.0f, 12.0f),AutoScaleMode = AutoScaleMode.Font,ClientSize = new Size(750, 680),Name = "mainForm",Text = "TokenizeTest") 
[ f2c label7; f2c label6; f2c label5; f2c label4; f2c label3; f2c label2; f2c label1; f2c tokenRuleSave_btn; f2c tokenRuleLoad_btn; f2c sourceSave_btn; f2c sourceLoad_btn; f2c tokenize_btn; f2c tokenRule_btn; f2c error_tb; f2c token_tb; f2c source_tb; f2c tokenRule_tb] |> List.iter(fun cnt -> mainForm.Controls.Add cnt) 
 
 
let mutable (GL_tokenizer:(string list -> Token list)) = (fun _ -> []) 
 
//splitNameDef [|"INTNUM,\d+";"EQ,="|];; 
//結果 (string * string) [] = [|("INTNUM", "\d+"); ("EQ", "=")|] 
let splitNameDef (lst:array<string>) = 
    lst 
      |>Array.filter(fun str -> str.Trim() <> "") 
      |>Array.map(fun (str:string) ->  
                     let posOfKamma = str.IndexOf(',') 
                     let name = str.Substring(0,posOfKamma) 
                     let def = str.Substring(posOfKamma + 1 ,str.Length - posOfKamma - 1) 
                     (name.Trim(),def.Trim()) 
              
                 ) 
 
tokenRule_btn.Click.Add 
    (fun _ -> error_tb.Text <- "" 
              try 
                let tokenRules = 
                    tokenRule_tb.Text.Replace("\r\n", "\n").Split([|'\n'|]) 
                      |>splitNameDef 
                      |>Array.toList 
                GL_tokenizer <- (new TokenizerFactory (tokenRules)).GetTokenizer() 
              with  
              | ex -> error_tb.Text <- ex.Message  
     )     
      
 
tokenRuleLoad_btn.Click.Add 
    (fun _ -> error_tb.Text <- "" 
              try  
                let ofd = new OpenFileDialog(Filter = "TRファイル(*.tr)|*.tr|すべてのファイル(*.*)|*.*")  
                if(ofd.ShowDialog() = DialogResult.OK) then  
                    let loadedText = 
                        System.IO.File.ReadAllLines (ofd.FileName) 
                            |>splitNameDef         
                            |>Array.fold (fun acc (name,def) -> acc + name + "," + def + "\r\n") "" 
                    tokenRule_tb.Text <- loadedText 
                     
              with  
                | ex -> error_tb.Text <- ex.Message  
    )  
      
      
tokenRuleSave_btn.Click.Add 
    (fun _ -> error_tb.Text <- "" 
              try 
                 
                let sfd = new SaveFileDialog(Filter = "TRファイル(*.tr)|*.tr|すべてのファイル(*.*)|*.*",  
                                              RestoreDirectory = true)  
                if (sfd.ShowDialog() = DialogResult.OK) then  
                    System.IO.File.WriteAllLines(sfd.FileName,(tokenRule_tb.Text.Replace("\r\n", "\n").Split([|'\n'|]))) 
              with  
                | ex -> error_tb.Text <- ex.Message  
    ) 
 
tokenize_btn.Click.Add 
    (fun _ -> error_tb.Text <- "" 
              try 
                let sourceTextLst = source_tb.Text.Replace("\r\n", "\n").Split([|'\n'|]) 
                let tokenizedLst = GL_tokenizer (List.ofArray sourceTextLst) 
                let sb = new System.Text.StringBuilder() 
                for ele in tokenizedLst do 
                    sb.Append(ele.ToString()) |> ignore 
                    token_tb.Text <- sb.ToString() 
              with  
                | ex -> error_tb.Text <- ex.Message  
    ) 
     
sourceLoad_btn.Click.Add 
    (fun _ -> error_tb.Text <- ""  
              try  
                let ofd = new OpenFileDialog(Filter = "srcファイル(*.src)|*.src|すべてのファイル(*.*)|*.*")  
                if(ofd.ShowDialog() = DialogResult.OK) then  
                    use sr = new System.IO.StreamReader(ofd.FileName)  
                    source_tb.Text <- sr.ReadToEnd()  
              with  
                | ex -> error_tb.Text <- ex.Message  
    )  
 
sourceSave_btn.Click.Add  
    (fun _ -> error_tb.Text <- ""   
              try  
                let sfd = new SaveFileDialog(Filter = "srcファイル(*.src)|*.src|すべてのファイル(*.*)|*.*",  
                                              RestoreDirectory = true)  
                if (sfd.ShowDialog() = DialogResult.OK) then  
                    use sw = new System.IO.StreamWriter(sfd.FileName)  
                    sw.Write(source_tb.Text)  
              with  
                | ex -> error_tb.Text <- ex.Message  
                  
    )  
 
[<STAThread()>]   
do Application.Run(mainForm)
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

プロフィール

T GYOUTEN

Author:T GYOUTEN
F#と英単語とフリーソフトと読書に興味があります。
ホームページでフリーソフトも公開しています。どぞ御贔屓に。

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
フリーエリア
フリーエリア
blogram投票ボタン
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QRコード
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。