スポンサーサイト

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

F#で入門 コンパイラ、インタプリタ編 LL(1) 具象構文木生成の自動化

 今回は次のようなソフトを作ってみます。 
トークン化のルールと構文規則を入力して、それに即したコードを書くと、具象構文木が表示されるというソフトです。 
 
使用方法としては次のようになります。 
上の2つのウインドウにトークン化ルールと構文規則を入力します。 
構文規則はZ = Program EOFが付け加えられるようになっているので、内容はProgramの定義からとなります。文法番号は1以上の一意な自然数とします。またトークン化ルールにはEOFの定義を書く必要はありません。 
 
1011-1.jpg
 
次に、「適用」ボタンを押すと次のように非終端記号、終端記号、構文規則が表示されます。 
 
1011-2.jpg
 
これで準備できたので、ソースコード入力欄にトークン化ルールと構文規則にのっとったソースを書き込んで、「→ボタン」を押すと、具象構文木が表示されます。 
 
1011-3.jpg
 
それでは、このソフトの作り方の説明ですが、トークン関連は以前作成した、TokenizerFactoryクラスを利用します。 
下向き構文解析に必要な構文解析表も、以前作成した関数をまとめてクラスにすることによりMapの形で与えられるようにします。 
このクラスのコードは次のようになります。 
 
type LL1Parsing (initStrLst:list<string>) = 
 
    let getNTN_TN__Sets (inStrLst:list<string>) = 
        定義前と同じ 
    let isNullableToken ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<string,bool>)(inTokenName:string) = 
        定義前と同じ 
 
    let isNullableTokenLst ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<stri        定義前と同じ 
           
    let isNullableTokenLstLst ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<string,bool>)(inTokenNameLstLst:list<list<string>>) = 
        定義前と同じ 
 
    let getNTN_NullableMap (inStrLst:list<string>) = 
        定義前と同じ 
 
    let getFirstSetOfToken ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnFirstSetMap:Map<string,Set<string>>)(inTokenName:string) = 
        定義前と同じ 
 
    let getFirstSetOfTokenLst ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<string,bool>) 
        定義前と同じ 
 
    let getFirstSetOfTokenLstLst ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<string,bool>) 
        定義前と同じ 
 
    //first集合を求める 
    let getNTN_FirstMap  (inStrLst:list<string>) = 
        定義前と同じ 
    let getAfterTokens (inStr:string) ((lhdStr,rhStrLst):string*list<string>) = 
        定義前と同じ 
 
    let getNTN_FollowMap  (inStrLst:list<string>) = 
        定義前と同じ 
 
    let getNTN_DirectorMap  (inStrLst:list<string>) = 
        定義前と同じ 
 
    //ここからが修正分 
    let getOneToManyAndOneToOneParsingRelation (inStrLst:list<string>) = 
        let directorMap = getNTN_DirectorMap inStrLst 
        let gramsWithIndex = inStrLst 
                                |> List.map splitOneLineGram 
                                |> List.map (fun (index,lh,rhEles) -> (index,(lh,rhEles)))  
         
        let relationBetweenIndexAndNTN_namdAndDirecSetList 
            = [ for (index,(lh,rhEles)) in gramsWithIndex do 
                    for direcEle in directorMap.[index] do 
                        yield (index,lh,direcEle) ] 
 
        let ParsingRelation 
            = relationBetweenIndexAndNTN_namdAndDirecSetList 
                |> List.fold (fun (stateMap:Map<string*string,Set<int>>)  (index,lh,direcEle)  ->       
                                        let tf = Map.tryFind (lh,direcEle) stateMap 
                                        match tf with 
                                        |Some(oldSet) -> Map.add (lh,direcEle) (oldSet + (Set.ofList [index])) stateMap 
                                        |None         -> Map.add (lh,direcEle) (Set.ofList [index]) stateMap 
                             ) 
                             Map.empty               
         
        ////この部分だけ追加 
        let OneToOneParsingRelation 
            = ParsingRelation 
                |> Map.map (fun (ntnName,direcEle) intSet ->  
                                    if Set.count intSet > 1 then raise (MyGramExcp "衝突しているのでLL(1)解析できません") 
                                    else 
                                       match (List.ofSeq intSet) with 
                                       | [] -> failwith "neverOccurable Error" // 起こらない 
                                       | h::[] -> h 
                                       | _ ->  failwith "neverOccurable Error" // 起こらない 
                           )          
 
        (ParsingRelation,OneToOneParsingRelation) 
 
 
    member this.Get_NTN_TN_Set () = 
         getNTN_TN__Sets initStrLst 
 
    member this.GetOneToManyParsingRelationMap () =  
             let (oneToManyPR,_) = getOneToManyAndOneToOneParsingRelation initStrLst 
             oneToManyPR 
    member this.GetOneToOneParsingRelationMap () =  
             let (_,oneToOnePR) = getOneToManyAndOneToOneParsingRelation initStrLst 
             oneToOnePR 
 
このクラスの型は 
type LL1Parsing = 
  class 
    new : initStrLst:string list -> LL1Parsing 
    member 
      GetOneToManyParsingRelationMap : unit -> Map<(string * string),Set<int>> 
    member GetOneToOneParsingRelationMap : unit -> Map<(string * string),int> 
    member Get_NTN_TN_Set : unit -> Set<string> * Set<string> 
  end 
  
となり Get_NTN_TN_Setで非終端記号と終端記号を得ることができ、GetOneToOneParsingRelationMapによって、「非終端記号と終端記号の組」と「使用すべき文法番号」との対応を保持するMapを得ることができます。 
実際の具象構文木の作成方法は大雑把に次のようになります。 
使用する文法番号から、導出すべき終端記号と非終端記号をリスト得て、このリストの要素と先読みしたトークン(終端記号)を考えます。 
文法リストの要素が終端記号の場合は、先読みしたトークンと一致するはずなのでLeafを作ります。 
またリストの要素が非終端記号の場合はトークンと組にして上のMapで、次に使用すべき文法番号を調べます。その場合は再帰的に同じことをを繰り返し,,,,という感じになります。。 
 
具象構文木の定義は 
type embodyST = 
    |EPS_Leaf of Token //tokenは存在しないので、εの直後のtokenを与える 
    |Leaf of Token  
    |Node of (int* string * list<embodyST>) //intは構文規則番号,stringは "(1, "Program", ["DeclStmts"; "PrintStmts"])"等 
とします。 
 
補助関数を一つ定義しておいて 
let getIndexGRmap (inStrLst:list<string>) = 
    inStrLst 
        |> List.map splitOneLineGram  
        |> List.map (fun (index,_,gramLst) -> (index,gramLst)) 
        |> Map.ofList 
 
具象抽象木を作る関数は次のようになります。 
let rec makeEmbodyST ((tntSet:Set<string>),(ntSet:Set<string>))  
                     (prMap:Map<(string * string),int>) (iGmap:Map<int,string list>) 
                     ((remainToken:list<Token>),(remainGRM:list<string>),(acc:list<embodyST>)) = 
    match remainToken,remainGRM with 
    |[],_ -> ([],[],acc) 
    //一つの構文要素終了時 
    |remt,[]    -> (remt,[],acc)  
    //εの場合 
    |hdt::tlt,hdg::tlg  when hdg = STR_EPS  
                -> makeEmbodyST (tntSet,ntSet) prMap iGmap (remainToken,tlg,acc @ [EPS_Leaf(hdt)])  
    //終端記号 
    |hdt::tlt,hdg::tlg  when hdt.Kind = hdg 
                -> makeEmbodyST (tntSet,ntSet) prMap iGmap  (tlt,tlg,acc @ [Leaf(hdt)]) 
    //非終端記号 
    |hdt::tlt,hdg::tlg   
               -> let foundPRIndex = Map.tryFind (hdg,hdt.Kind) prMap 
                  match foundPRIndex with 
                  |Some(i) -> let shouldUseGram = iGmap.[i] 
                              let (remT,remG,underNodes) = makeEmbodyST (tntSet,ntSet) prMap iGmap (remainToken,shouldUseGram,[]) 
                              //上の行でremGは[]として返ってくる 
                              makeEmbodyST (tntSet,ntSet) prMap iGmap (remT,tlg, acc @ [Node(i,hdg,underNodes)]) 
                               
                  |None    -> raise (MyGramExcp (sprintf "%sで文法エラー" (hdt.ToString()))) 
 
 
前回のLogo風言語での使用例は次のようになります。 
 
1011-4.jpg 
 
全コードは次の通りです。 
(なお、以前使ったコードをそのまま流用していますので、nullable,first,follow集合等を求める部分で同じ計算を何度も何度も繰り返していますが、この部分の修正はしていません。) 
 
 open System    
open System.Windows.Forms    
open System.Drawing    
open System.Text.RegularExpressions 
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
 
//文法定義のエラー 
exception MyGramExcp of string 
 
let STR_EPS ="EPSILON" 
 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
 
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 
 
///////////////////////////////////////////type Tokenの定義ここまで///////////////////////////////////////////// 
 
//splitOneLineGram "5:Program = DeclStmts PrintStmts" 
//結果 (5,"Program", ["DeclStmts"; "PrintStmts"]) 
let splitOneLineGram (inStr:string)= 
    let (lhdIndex,rhd) =  
        match inStr.Split([|':'|]) with 
        [|mlhd;mrhd|]  ->  (mlhd.Trim(),mrhd) 
        | _             ->  raise <| MyGramExcp(inStr)  
    let (lhd,rhd2) = 
        match rhd.Split([|'='|]) with 
        |[|mlhd;mrhd|]  ->  (mlhd.Trim(),mrhd) 
        | _             ->  raise <| MyGramExcp(inStr)  
    let rhdElems =  
        rhd2.Split([|' '|]) 
            |> List.ofArray 
            |> List.map (fun s -> s.Trim()) 
            |> List.filter (fun s -> s <> "") 
    (System.Int32.Parse(lhdIndex),lhd,rhdElems) 
 
 
type LL1Parsing (initStrLst:list<string>) = 
 
    //非終端記号と終端記号のSetを返す 
    //getNTN_TN_Sets ["1:Program = DeclStmts PrintStmts";"2:DeclStmts = VAR SEMI";"3:PrintStmts = EPSILON"] 
    //(set ["DeclStmts"; "PrintStmts"; "Program"], set ["SEMI"; "VAR"]) 
    let getNTN_TN__Sets (inStrLst:list<string>) = 
        let (sumUpLhdSet,sumUpRhdSet) = 
            inStrLst 
                |> List.map splitOneLineGram 
                |> List.fold (fun (acclh,accrh)  (_,lhd,rhdLst) -> (lhd :: acclh,rhdLst @ accrh)) ([],[]) 
                |> (fun (hdLst,rhLst) -> (Set.ofList hdLst, Set.ofList rhLst)) 
        (sumUpLhdSet,sumUpRhdSet - sumUpLhdSet - (Set.ofList [STR_EPS])) 
 
    let isNullableToken ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<string,bool>)(inTokenName:string) = 
        if inTokenName = STR_EPS then  
             true 
        elif Set.contains inTokenName in_tn then 
             false 
        else 
            in_ntnNullableMap.[inTokenName] 
 
    let isNullableTokenLst ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<string,bool>)(inTokenNameLst:list<string>) = 
          List.forall (isNullableToken (in_ntn,in_tn) in_ntnNullableMap )inTokenNameLst //リスト中のすべてのtokenがnullableか 
           
    let isNullableTokenLstLst ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<string,bool>)(inTokenNameLstLst:list<list<string>>) = 
          List.exists (isNullableTokenLst (in_ntn,in_tn) in_ntnNullableMap ) inTokenNameLstLst//リスト中のどれかのtokenリストがnullableか 
 
    //getNTNNullableMap ["1:Program = DeclStmts SEMI PrintStmts";"2:DeclStmts = EPSILON";"3:PrintStmts = EPSILON";"4:PrintStmts = PRINT"] 
    // map [("DeclStmts", true); ("PrintStmts", true); ("Program", false)] 
    let getNTN_NullableMap (inStrLst:list<string>) = 
     
        let (ntnSet,tnSet) = getNTN_TN__Sets inStrLst 
     
        let grams = inStrLst 
                      |> List.map splitOneLineGram 
                      |> List.map (fun (_,lh,rhEles) -> (lh,rhEles)) //[("Program",["DeclStmts";"PrintStmts"]);("DeclStmts",["VAR";"SEMI"])] 
     
        let rec getNTN_NullableMapSub (inOldNullableMap:Map<string,bool>) (count:int) =  
            let nextNullableMap = 
                ntnSet 
                    |> Set.fold (fun stateMap ele -> 
                                    let targetGramsLstLst = 
                                        grams 
                                            |>List.filter (fun (ntnName,_) -> ntnName = ele) 
                                            |>List.map (fun (_,lst) -> lst) 
                                    let thisEleNullable = 
                                        isNullableTokenLstLst(ntnSet,tnSet) inOldNullableMap targetGramsLstLst 
                                    Map.add ele thisEleNullable stateMap 
                                 ) 
                                 Map.empty 
            if count > 10000 then 
                failwith "count error" 
            elif nextNullableMap = inOldNullableMap then 
                nextNullableMap 
            else 
                getNTN_NullableMapSub nextNullableMap (count + 1) 
     
        let initNullableMap = 
                    ntnSet 
                        |> Set.map (fun ele -> (ele,false)) 
                        |> Map.ofSeq 
     
        getNTN_NullableMapSub initNullableMap 0 
 
    //first集合を求める(トークン一つ用) 
    let getFirstSetOfToken ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnFirstSetMap:Map<string,Set<string>>)(inTokenName:string) = 
        if inTokenName = STR_EPS then  
             Set.empty 
        elif Set.contains inTokenName in_tn then 
             Set.ofList ([inTokenName]) 
        else 
            in_ntnFirstSetMap.[inTokenName] 
 
    //first集合を求める(トークンリスト用) 
    let getFirstSetOfTokenLst ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<string,bool>) 
                               (in_firstSetMap:Map<string,Set<string>>) (inTokenNameLst:list<string>) = 
          let isNullableTokenPartApply = isNullableToken (in_ntn,in_tn) in_ntnNullableMap    
       
          let rec getFirstSetOfTokenLstSub (tokenLst:list<string>)  = 
            match tokenLst with 
            |[] -> Set.empty 
            |hd::tl when isNullableTokenPartApply hd -> (getFirstSetOfToken (in_ntn,in_tn) in_firstSetMap hd) + (getFirstSetOfTokenLstSub tl) 
            |hd::tl                                  -> (getFirstSetOfToken (in_ntn,in_tn) in_firstSetMap hd) 
 
          getFirstSetOfTokenLstSub inTokenNameLst 
 
    //first集合を求める(トークンリストのリスト) 
    let getFirstSetOfTokenLstLst ((in_ntn,in_tn):Set<string>*Set<string>) (in_ntnNullableMap:Map<string,bool>) 
                              (in_firstSetMap:Map<string,Set<string>>) (inTokenNameLstLst:list<list<string>>) =   
 
        let getFirstSetOfTokenLstPartApply = getFirstSetOfTokenLst (in_ntn,in_tn) in_ntnNullableMap in_firstSetMap 
     
        List.fold (fun acc ele -> acc + (getFirstSetOfTokenLstPartApply ele)) Set.empty inTokenNameLstLst 
 
    //first集合を求める 
    let getNTN_FirstMap  (inStrLst:list<string>) = 
     
        let (ntnSet,tnSet) = getNTN_TN__Sets inStrLst 
        let ntnNullableMap = getNTN_NullableMap inStrLst 
 
        let grams = inStrLst 
                      |> List.map splitOneLineGram 
                      |> List.map (fun (_,lh,rhEles) -> (lh,rhEles)) //[("Program",["DeclStmts";"PrintStmts"]);("DeclStmts",["VAR";"SEMI"])] 
     
        let FisrtSetOfTokenLstLstPA = getFirstSetOfTokenLstLst (ntnSet,tnSet) ntnNullableMap 
     
        let rec getNTN_FirstMapSub (inOldFirstMap:Map<string,Set<string>>) (count:int) =  
            let nextFirstMap = 
                ntnSet 
                    |> Set.fold (fun stateMap ele -> 
                                    let targetGramsLstLst = 
                                        grams 
                                            |>List.filter (fun (ntnName,_) -> ntnName = ele) 
                                            |>List.map (fun (_,lst) -> lst) 
                                    let thisEleFisrtSet = 
                                         FisrtSetOfTokenLstLstPA inOldFirstMap targetGramsLstLst 
                                    Map.add ele thisEleFisrtSet stateMap 
                                 ) 
                                 Map.empty 
            if count > 10000 then 
                failwith "count error" 
            elif nextFirstMap = inOldFirstMap then 
                nextFirstMap 
            else 
                getNTN_FirstMapSub nextFirstMap (count + 1) 
     
        let initFirstMap = 
                    ntnSet 
                        |> Set.map (fun ele -> (ele,Set.empty)) 
                        |> Map.ofSeq 
     
        getNTN_FirstMapSub initFirstMap 0 
 
    //follow補助 
 
    // getAfterTokens "DeclStmts1" ("PrintStmts1",["VarDef1";"DeclStmts1";"VarDef2";"VarDef3";"DeclStmts1";"VarDef4"]);; 
    //val it : (string list * string) list = 
    //  [(["VarDef4"], "PrintStmts1"); 
    //   (["VarDef2"; "VarDef3"; "DeclStmts1"; "VarDef4"], "PrintStmts1")] 
    let getAfterTokens (inStr:string) ((lhdStr,rhStrLst):string*list<string>) = 
        let rec getAfterTokensSub strLst res = 
            match strLst with 
            |hd::tl when hd = inStr -> getAfterTokensSub tl ((tl,lhdStr)::res) 
            |hd::tl                 -> getAfterTokensSub tl res 
            | [] -> res 
        getAfterTokensSub rhStrLst [] 
 
 
    let getNTN_FollowMap  (inStrLst:list<string>) = 
        let (ntnSet,tnSet) = getNTN_TN__Sets inStrLst 
        let ntnNullableMap = getNTN_NullableMap inStrLst 
        let ntnFirstMap = getNTN_FirstMap inStrLst 
        let isNullableTokensLstPA (tokenLst:list<string>) = isNullableTokenLst (ntnSet,tnSet) ntnNullableMap tokenLst 
        let getFirstSetOfTokenLstPA (tokenLst:list<string>) = getFirstSetOfTokenLst (ntnSet,tnSet) ntnNullableMap  ntnFirstMap tokenLst 
        let grams = inStrLst 
                      |> List.map splitOneLineGram 
                      |> List.map (fun (_,lh,rhEles) -> (lh,rhEles)) //[("Program",["DeclStmts";"PrintStmts"]);("DeclStmts",["VAR";"SEMI"])] 
  
        let rec getNTN_FollowMapSub (inOldFollowMap:Map<string,Set<string>>) (count:int) =  
           let getFollowSet (afterTokens:list<string>,ntnName:string) = 
                    if isNullableTokensLstPA afterTokens then 
                        (getFirstSetOfTokenLstPA afterTokens) + (inOldFollowMap.[ntnName]) 
                    else 
                        (getFirstSetOfTokenLstPA afterTokens)                 
           let nextFollowMap = 
                ntnSet 
                    |> Set.fold (fun stateMap ele -> 
                                    let includeEleGrams = 
                                        grams 
                                          |> List.fold (fun state2 (ntnName2,tokenLst) 
                                                            -> state2 @ (getAfterTokens ele (ntnName2,tokenLst))) 
                                                        [] 
                                                     
                                    let followSet = 
                                        includeEleGrams 
                                          |> List.fold (fun (state3:Set<string>) (afterTokens,ntnName) -> 
                                                        state3 + (getFollowSet (afterTokens,ntnName))) 
                                                        Set.empty 
                                    Map.add ele followSet stateMap 
                                 ) 
                                 Map.empty 
           if count > 10000 then 
                failwith "count error" 
           elif nextFollowMap = inOldFollowMap then 
                nextFollowMap 
           else 
                getNTN_FollowMapSub nextFollowMap (count + 1) 
 
 
        let initFollowMap = 
                    ntnSet 
                        |> Set.map (fun ele -> (ele,Set.empty)) 
                        |> Map.ofSeq 
     
        getNTN_FollowMapSub initFollowMap 0 
 
    //directorMap キーは構文番号(整数) 値はSet<string> 
    let getNTN_DirectorMap  (inStrLst:list<string>) = 
        let (ntnSet,tnSet) = getNTN_TN__Sets inStrLst 
        let ntnNullableMap = getNTN_NullableMap inStrLst 
        let ntnFirstMap = getNTN_FirstMap inStrLst 
        let isNullableTokensLstPA (tokenLst:list<string>) = isNullableTokenLst (ntnSet,tnSet) ntnNullableMap tokenLst 
        let getFirstSetOfTokenLstPA (tokenLst:list<string>) = getFirstSetOfTokenLst (ntnSet,tnSet) ntnNullableMap  ntnFirstMap tokenLst 
        let ntnFollowMap = getNTN_FollowMap inStrLst 
        let gramsWithIndex = inStrLst 
                                |> List.map splitOneLineGram 
                                |> List.map (fun (index,lh,rhEles) -> (index,(lh,rhEles)))  
     
        let getNTN_DirectorSet (inHh:string,inRhEles:list<string>) = 
            if isNullableTokensLstPA inRhEles then 
                (getFirstSetOfTokenLstPA inRhEles) + ntnFollowMap.[inHh] 
            else 
                (getFirstSetOfTokenLstPA inRhEles) 
 
 
        gramsWithIndex 
            |> List.map (fun (index,(lh,rhEles)) -> (index, getNTN_DirectorSet (lh,rhEles))) 
            |> Map.ofList              
 
 
 
    let getOneToManyAndOneToOneParsingRelation (inStrLst:list<string>) = 
        let directorMap = getNTN_DirectorMap inStrLst 
        let gramsWithIndex = inStrLst 
                                |> List.map splitOneLineGram 
                                |> List.map (fun (index,lh,rhEles) -> (index,(lh,rhEles)))  
         
        let relationBetweenIndexAndNTN_namdAndDirecSetList 
            = [ for (index,(lh,rhEles)) in gramsWithIndex do 
                    for direcEle in directorMap.[index] do 
                        yield (index,lh,direcEle) ] 
 
        let ParsingRelation 
            = relationBetweenIndexAndNTN_namdAndDirecSetList 
                |> List.fold (fun (stateMap:Map<string*string,Set<int>>)  (index,lh,direcEle)  ->       
                                        let tf = Map.tryFind (lh,direcEle) stateMap 
                                        match tf with 
                                        |Some(oldSet) -> Map.add (lh,direcEle) (oldSet + (Set.ofList [index])) stateMap 
                                        |None         -> Map.add (lh,direcEle) (Set.ofList [index]) stateMap 
                             ) 
                             Map.empty               
         
        ////この部分だけ付けたし 
        let OneToOneParsingRelation 
            = ParsingRelation 
                |> Map.map (fun (ntnName,direcEle) intSet ->  
                                    if Set.count intSet > 1 then raise (MyGramExcp "衝突しているのでLL(1)解析できません") 
                                    else 
                                       match (List.ofSeq intSet) with 
                                       | [] -> failwith "neverOccurable Error" // 起こらない 
                                       | h::[] -> h 
                                       | _ ->  failwith "neverOccurable Error" // 起こらない 
                           )          
 
        (ParsingRelation,OneToOneParsingRelation) 
 
 
    member this.Get_NTN_TN_Set () = 
         getNTN_TN__Sets initStrLst 
 
    member this.GetOneToManyParsingRelationMap () =  
             let (oneToManyPR,_) = getOneToManyAndOneToOneParsingRelation initStrLst 
             oneToManyPR 
    member this.GetOneToOneParsingRelationMap () =  
             let (_,oneToOnePR) = getOneToManyAndOneToOneParsingRelation initStrLst 
             oneToOnePR 
        
 
////////////type LL1Parsingの定義ここまで///////////////////////////////////////////////////////////////////////// 
 
//引数分の空白文字を作る補助関数 
let spaceStr(i) =  
    let sb = new System.Text.StringBuilder() 
    let rec addSpace count = 
        if count = i then sb.ToString() 
        else 
            sb.Append(" ") |> ignore 
            addSpace (count + 1) 
    addSpace 0  
 
type embodyST = 
    |EPS_Leaf of Token //tokenは存在しないので、εの直後のtokenを与える 
    |Leaf of Token  
    |Node of (int* string * list<embodyST>) //intは構文規則番号,stringは "(1, "Program", ["DeclStmts"; "PrintStmts"])"等 
 
     
    //表示用 
    member this.dispStr (inc :int)  = //inc = インシデント 
            match this with 
            |EPS_Leaf (token) 
                -> spaceStr(inc) +  "ε" + (sprintf "(%d,%d)の前" token.Row token.Col ) + "\r\n" 
            |Leaf(token) 
                -> spaceStr(inc) +  token.ToString() + "\r\n"  
            |Node(index,str,lst)  
                -> spaceStr(inc) + (sprintf "(%d)" index) + str + "\r\n"  
                   + List.fold (fun state (ele:embodyST) -> state + (ele.dispStr (inc + 4)) ) "" lst  
 
 
let getIndexGRmap (inStrLst:list<string>) = 
    inStrLst 
        |> List.map splitOneLineGram  
        |> List.map (fun (index,_,gramLst) -> (index,gramLst)) 
        |> Map.ofList 
 
 
//let testGrammersStrLst = 
//   ["0:Z = Program EOF";  
//   "1:Program = DeclStmts PrintStmts"; 
//    "2:DeclStmts = DeclStmt SEMI DeclStmts2"; 
//    "3:DeclStmts2  = EPSILON"; 
//    "4:DeclStmts2  = DeclStmts"; 
//    "5:DeclStmt = INT VarDefs"; 
//    "6:VarDefs = ID EQ NUM VarDefs2"; 
//    "7:VarDefs2 = COMMA ID EQ NUM VarDefs2"; 
//    "8:VarDefs2 = EPSILON"; 
//    "9:PrintStmts = EPSILON"; 
//    "10:PrintStmts = PrintStmt SEMI PrintStmts"; 
//    "11:PrintStmt = EX VarRefs"; 
//    "12:VarRefs = ID VarRefs2"; 
//    "13:VarRefs2 = COMMA ID VarRefs2"; 
//    "14:VarRefs2 = EPSILON"] 
 
//INT ID EQ NUM SEMI EOF" 
//let (tntSet,ntSet) = ((new LL1Parsing (testGrammersStrLst)).Get_NTN_TN_Set()) 
//let testMap =  ((new LL1Parsing (testGrammersStrLst)).GetOneToOneParsingRelationMap()) 
//let indexGRmap = getIndexGRmap testGrammersStrLst 
//let testTokens = [new Token("INT","",0,0);new Token("ID","",1,1);new Token("EQ","",2,2); 
//                  new Token("NUM","" ,3,3);new Token("SEMI","",4,4);new Token("EOF","",5,5)] 
// 
//int * string * string list =  (1, "Program", ["DeclStmts"; "PrintStmts"]) 
//let testFirstLine = splitOneLineGram (List.head testGrammersStrLst) 
 
let rec makeEmbodyST ((tntSet:Set<string>),(ntSet:Set<string>))  
                     (prMap:Map<(string * string),int>) (iGmap:Map<int,string list>) 
                     ((remainToken:list<Token>),(remainGRM:list<string>),(acc:list<embodyST>)) = 
    match remainToken,remainGRM with 
    |[],_ -> ([],[],acc) 
    //一つの構文要素終了時 
    |remt,[]    -> (remt,[],acc)  
    //εの場合 
    |hdt::tlt,hdg::tlg  when hdg = STR_EPS  
                -> makeEmbodyST (tntSet,ntSet) prMap iGmap (remainToken,tlg,acc @ [EPS_Leaf(hdt)])  
    //終端記号 
    |hdt::tlt,hdg::tlg  when hdt.Kind = hdg 
                -> makeEmbodyST (tntSet,ntSet) prMap iGmap  (tlt,tlg,acc @ [Leaf(hdt)]) 
    //非終端記号 
    |hdt::tlt,hdg::tlg   
               -> let foundPRIndex = Map.tryFind (hdg,hdt.Kind) prMap 
                  match foundPRIndex with 
                  |Some(i) -> let shouldUseGram = iGmap.[i] 
                              let (remT,remG,underNodes) = makeEmbodyST (tntSet,ntSet) prMap iGmap (remainToken,shouldUseGram,[]) 
                              //上の行でremGは[]として返ってくる 
                              makeEmbodyST (tntSet,ntSet) prMap iGmap (remT,tlg, acc @ [Node(i,hdg,underNodes)]) 
                               
                  |None    -> raise (MyGramExcp (sprintf "%sで文法エラー" (hdt.ToString()))) 
 
 
let f2c x = x :> System.Windows.Forms.Control  
let label4= new Label(AutoSize = true,Location = new Point(16, 49),Name = "label4",Size = new Size(79, 12),TabIndex = 23,Text = "というように記述") 
let label3= new Label(AutoSize = true,Location = new Point(16, 37),Name = "label3",Size = new Size(34, 12),TabIndex = 22,Text = "EQ,\\=") 
let label2= new Label(AutoSize = true,Location = new Point(15, 25),Name = "label2",Size = new Size(68, 12),TabIndex = 21,Text = "INTNUM,\\d+") 
let label1= new Label(AutoSize = true,Location = new Point(15, 7),Name = "label1",Size = new Size(82, 12),TabIndex = 20,Text = "トークン化ルール") 
let tokenRuleSave_btn= new Button(Location = new Point(101, 38),Name = "tokenRuleSave_btn",Size = new Size(132, 23),TabIndex = 19,Text = "トークン化ルールのSave",UseVisualStyleBackColor = true) 
let tokenRuleLoad_btn= new Button(Location = new Point(100, 7),Name = "tokenRuleLoad_btn",Size = new Size(133, 23),TabIndex = 18,Text = "トークン化ルールのLoad",UseVisualStyleBackColor = true) 
let tokenRule_tb= new TextBox(Location = new Point(12, 67),Multiline = true,Name = "tokenRule_tb",ScrollBars = ScrollBars.Both,Size = new Size(221, 141),TabIndex = 17) 
let label5= new Label(AutoSize = true,Location = new Point(16, 500),Name = "label5",Size = new Size(33, 12),TabIndex = 27,Text = "ソース") 
let sourceSave_btn= new Button(Location = new Point(406, 491),Name = "sourceSave_btn",Size = new Size(101, 23),TabIndex = 26,Text = "ソースのSave",UseVisualStyleBackColor = true) 
let sourceLoad_btn= new Button(Location = new Point(299, 491),Name = "sourceLoad_btn",Size = new Size(101, 23),TabIndex = 25,Text = "ソースのLoad",UseVisualStyleBackColor = true) 
let source_tb= new TextBox(Location = new Point(12, 520),Multiline = true,Name = "source_tb",ScrollBars = ScrollBars.Both,Size = new Size(495, 255),TabIndex = 24) 
let label6= new Label(AutoSize = true,Location = new Point(18, 416),Name = "label6",Size = new Size(32, 12),TabIndex = 29,Text = "エラー") 
let error_tb= new TextBox(Location = new Point(17, 441),Multiline = true,Name = "error_tb",ScrollBars = ScrollBars.Both,Size = new Size(490, 44),TabIndex = 28) 
let base_gram_tb= new TextBox(Location = new Point(239, 67),Multiline = true,Name = "base_gram_tb",ScrollBars = ScrollBars.Both,Size = new Size(884, 141),TabIndex = 30) 
let llabel10= new Label(AutoSize = true,Location = new Point(591, 43),Name = "llabel10",Size = new Size(73, 12),TabIndex = 36,Text = "εはEPSILON") 
let label7= new Label(AutoSize = true,Location = new Point(390, 43),Name = "label7",Size = new Size(176, 12),TabIndex = 35,Text = "文法番号:非終端記号 = トークン列") 
let label8= new Label(AutoSize = true,Location = new Point(242, 43),Name = "label8",Size = new Size(101, 12),TabIndex = 34,Text = "構文規則(入力用)") 
let save_btn= new Button(Location = new Point(1048, 32),Name = "save_btn",Size = new Size(75, 23),TabIndex = 38,Text = "Save",UseVisualStyleBackColor = true) 
let load_btn= new Button(Location = new Point(967, 32),Name = "load_btn",Size = new Size(75, 23),TabIndex = 37,Text = "Load",UseVisualStyleBackColor = true) 
let term_lb= new ListBox(FormattingEnabled = true,ItemHeight = 12,Location = new Point(14, 274),Name = "term_lb",Size = new Size(128, 136),TabIndex = 45) 
let ntn_lb= new ListBox(FormattingEnabled = true,ItemHeight = 12,Location = new Point(158, 274),Name = "ntn_lb",Size = new Size(143, 136),TabIndex = 44) 
let gram_lb= new ListBox(FormattingEnabled = true,ItemHeight = 12,Location = new Point(322, 274),Name = "gram_lb",Size = new Size(801, 136),TabIndex = 43) 
let label9= new Label(AutoSize = true,Location = new Point(325, 256),Name = "label9",Size = new Size(53, 12),TabIndex = 42,Text = "構文規則") 
let ulabel10= new Label(AutoSize = true,Location = new Point(161, 256),Name = "ulabel10",Size = new Size(65, 12),TabIndex = 41,Text = "非終端記号") 
let label11= new Label(AutoSize = true,Location = new Point(17, 256),Name = "label11",Size = new Size(53, 12),TabIndex = 40,Text = "終端記号") 
let apply_btn= new Button(Location = new Point(124, 221),Name = "apply_btn",Size = new Size(364, 23),TabIndex = 39,Text = "適用",UseVisualStyleBackColor = true) 
let ast_tb= new TextBox(Location = new Point(560, 441),Multiline = true,Name = "ast_tb",ScrollBars = ScrollBars.Both,Size = new Size(563, 334),TabIndex = 46,Text = "ast_tb") 
let conv_btn= new Button(Location = new Point(513, 564),Name = "conv_btn",Size = new Size(41, 63),TabIndex = 47,Text = "→",UseVisualStyleBackColor = true) 
let mainForm= new Form(AutoScaleDimensions = new SizeF(6.0f, 12.0f),AutoScaleMode = AutoScaleMode.Font,ClientSize = new Size(1135, 787),Name = "mainForm",Text = "FSSimpleCCLL1") 
[ f2c conv_btn; f2c ast_tb; f2c term_lb; f2c ntn_lb; f2c gram_lb; f2c label9; f2c ulabel10; f2c label11; f2c apply_btn; f2c save_btn; f2c load_btn; f2c llabel10; f2c label7; f2c label8; f2c base_gram_tb; f2c label6; f2c error_tb; f2c label5; f2c sourceSave_btn; f2c sourceLoad_btn; f2c source_tb; f2c label4; f2c label3; f2c label2; f2c label1; f2c tokenRuleSave_btn; f2c tokenRuleLoad_btn; f2c tokenRule_tb] |> List.iter(fun cnt -> mainForm.Controls.Add cnt) 
 
//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()) 
              
                 ) 
 
 
let mutable (GL_tokenizer:(string list -> Token list)) = (fun _ -> []) 
let mutable GL_gramLst:list<string> = [] 
let mutable GL_llParser:LL1Parsing = new LL1Parsing(["0:Z = Program EOF"]) //仮 
 
//トークナイザーの登録 
let setTokenizer () = 
    let tokenRules = 
        tokenRule_tb.Text.Replace("\r\n", "\n").Split([|'\n'|]) 
            |>splitNameDef 
            |>Array.toList 
    GL_tokenizer <- (new TokenizerFactory (("EOF","EOF")::tokenRules)).GetTokenizer() 
 
 
 
let clearGramPart () = 
    error_tb.Clear() 
    term_lb.Items.Clear() 
    ntn_lb.Items.Clear() 
    gram_lb.Items.Clear() 
 
     
 
apply_btn.Click.Add 
    (fun _ ->   clearGramPart () 
                try 
                    clearGramPart() 
                    setTokenizer () 
                     
                    let tempLst =  base_gram_tb.Text.Split([|'\n'|]) 
                                     |> Array.map (fun str -> str.Trim()) 
                                     |> Array.filter(fun str -> str <> "") 
                                     |> List.ofArray  
               
                    GL_gramLst <- "0:Z = Program EOF" :: tempLst  
                    GL_llParser<-  new LL1Parsing (GL_gramLst) 
                     
                     
                     
                    //非終端記号、終端記号の表示 
                    let (ntnSet,tnSet) = GL_llParser.Get_NTN_TN_Set () 
                    Set.iter (fun ele -> ntn_lb.Items.Add(ele) |> ignore ) ntnSet 
                    Set.iter (fun ele -> term_lb.Items.Add(ele) |> ignore ) tnSet 
                    //構文規則の表示 
                    List.iter(fun str -> gram_lb.Items.Add(str) |> ignore ) GL_gramLst 
                    //LL(1)解析の可能性表示 
                    for ntn in ntnSet do 
                        for tn in tnSet do 
                           let findRelation = Map.tryFind (ntn,tn) (GL_llParser.GetOneToManyParsingRelationMap()) 
                           match findRelation with 
                           |Some(inSet) when Set.count inSet > 1  
                                -> error_tb.Text <- error_tb.Text + (sprintf "%s %s で衝突があります\n" ntn tn)  
                           |_ -> ()  
 
                with 
                |MyGramExcp(str) -> error_tb.Text <- sprintf "構文規則の表記が不正です:%s" str   
                | ex -> error_tb.Text <- ex.Message  
    ) 
 
conv_btn.Click.Add 
    (fun _ -> try 
                error_tb.Text <- "" 
                ast_tb.Text <- "" 
                let (ntnSet,tnSet) = GL_llParser.Get_NTN_TN_Set() 
                let relMap =  GL_llParser.GetOneToOneParsingRelationMap() 
                let indexGRmap = getIndexGRmap GL_gramLst 
                let sourceTextLst = source_tb.Text.Replace("\r\n", "\n").Split([|'\n'|]) 
                let forTokenizedLst =  (List.ofArray sourceTextLst) @ ["EOF"] 
                let tokens = GL_tokenizer forTokenizedLst 
                let (_,_,madeTree) = makeEmbodyST (ntnSet,tnSet) relMap indexGRmap (tokens,["Z"],[]) 
                ast_tb.Text <-  ((List.head madeTree).dispStr(6)) 
 
              with 
              |MyGramExcp(str) -> error_tb.Text <- sprintf "ソースが構文にのっとっていません:%s" str   
              | 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  
    ) 
 
 
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  
                  
    )  
 
  
load_btn.Click.Add  
    (fun _ -> try  
                let ofd = new OpenFileDialog(Filter = "GRA3ファイル(*.gra3)|*.gra3|すべてのファイル(*.*)|*.*")  
                if(ofd.ShowDialog() = DialogResult.OK) then  
                    use sr = new System.IO.StreamReader(ofd.FileName)  
                    base_gram_tb.Text <- sr.ReadToEnd()  
              with  
                | ex -> error_tb.Text <- ex.Message  
    )  
 
  
save_btn.Click.Add  
    (fun _ -> try  
                let sfd = new SaveFileDialog(Filter = "GRA3ファイル(*.gra3)|*.gra3|すべてのファイル(*.*)|*.*",  
                                              RestoreDirectory = true)  
                if (sfd.ShowDialog() = DialogResult.OK) then  
                    use sw = new System.IO.StreamWriter(sfd.FileName)  
                    sw.Write(base_gram_tb.Text)  
              with  
                | ex -> error_tb.Text <- ex.Message  
                  
    )  
 
 
 
[<STAThread()>]   
do Application.Run(mainForm) 
 
スポンサーサイト

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

コメントの投稿

非公開コメント

おおっ!

これ面白いですね。v-218

No title

上向き構文解釈版(LR1)もできてますので、そのうちブログにのっける予定です。
プロフィール

T GYOUTEN

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

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

この人とブロともになる

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