SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

C#プログラマのためのF#入門

F#のfslexとfsyaccを用いたコンパイラ作成

C#プログラマのためのF#入門(9)

  • X ポスト
  • このエントリーをはてなブックマークに追加

コンパイラ作成

 最後にこの計算機言語のメインプログラムについて解説します。

 まず、fslexにて作成した字句解析器とfsyaccで作成した構文解析器の動作確認用にtestProjのProgram.fsに以下の2か所の部分を追記しました(※3※9)。

[リスト10]計算機で字句解析器と構文解析期の動作を確認
open System
open Microsoft.FSharp.Text.Lexing
open Ast
open Lexer
open Parser

// factorの評価
let rec evalFactor factor =
    match factor with
    | Float x   -> x
    | Integer x -> float x
    | ParenEx x -> evalExpr x

// termの評価
and evalTerm term =
    match term with
    | Times (term, fact)  -> (evalTerm term) * (evalFactor fact)
    | Divide (term, fact) -> (evalTerm term) / (evalFactor fact)
    | Factor fact         -> evalFactor fact

// expressionの評価
and evalExpr expr =
    match expr with
    | Plus (expr, term)  -> (evalExpr expr) + (evalTerm term)
    | Minus (expr, term) -> (evalExpr expr) - (evalTerm term)
    | Term term          -> evalTerm term
 
// equationの評価 ※1
and evalEquation eq =
    match eq with
    | Equation expr -> evalExpr expr

printfn "Calculator"

let rec readAndProcess() =  //※2
    printf ":"
    match Console.ReadLine() with
    | "quit" -> ()
    | expr ->
        try
            printfn "Lexing [%s]" expr

            //ここから追記  ※3 字句解析器の動作確認用の追記です。
            let lexbuf = LexBuffer<char>.FromString(expr) //字句解析準備
            let parse txt =
                let mutable keepParse = true
                let mutable tokenList = []    
                while keepParse = true do
                    let parsedToken = Lexer.tokenize txt  //※4 字句解析関数呼び出し
                    tokenList <- tokenList @ [parsedToken]  //※5 トークンをリストに
                    if parsedToken = Parser.EOF then
                        keepParse <- false  //※6 終了フラグ
                tokenList       
            let tokens = parse lexbuf        
            printfn "Tokens : %A" tokens  //トークン表示
            //ここまで追記

            let lexbuff = LexBuffer<char>.FromString(expr)  //※7
            
            printfn "Parsing..."
            let equation = Parser.start Lexer.tokenize lexbuff  //※8

            //以下の1行を追記 ※9 構文解析器の動作確認用追記です。
            printfn "equation is %A" equation  

            printfn "Evaluating Equation..."
            let result = evalEquation equation  //※10
            
            printfn "Result: %s" (result.ToString())
            
        with ex ->
            printfn "Unhandled Exception: %s" ex.Message

        readAndProcess()

readAndProcess()

 実際に操作しながら、解説してみます。ソリューションをビルドして、開始すると、コマンドラインアプリケーションが起動します。

 例として、2 + 3 * 5と入力してみてください。readAndProcess()(※2)は再帰関数です。標準入力からの文字列がquitでない限り、繰り返されます。まず、最初にfslexで作成した字句解析器の動作確認をします。文字列(2 + 3 * 5)を受け取り、文字を読むために字句解析用に用意したバッファlexbufにバインドします。

 その後、parseという関数を作成し、字句解析関数Lexer.tokenizeを呼び(※4)、lexbufを引数として渡すと、トークン列を返すようにします。その戻り値をリストに入れていきます(※5)。トークンが最後EOFになったらフラグをfalseにしてリストの中身を表示しています(※6)。

[リスト11]字句解析結果
INT32 2; PLUS; INT32 3; ASTER; INT32 5; EOF

 その後、※7で再度バッファを作成し、構文解析関数Parser.startを呼んでいます(※8)。Parser.startは字句解析結果(Lexer.tokeninze lexbuff)を必要とし、結果をequationにバインドします。構文解析器の動作確認として、Parser.startが戻した結果を確認するために※9の1行を追加すると、次のように、equationが表示され、構文解析器が動作していることが分かります。

[リスト12]構文解析結果
Equation (Plus (Term (Factor (Integer 2)),Times (Factor (Integer 3)

 最後にequationを引数として、関数evalEquationを呼び出し、方程式(equation)、式(expr)、項(term)、因子(factor)の評価を行います。以下は式の評価と結果を導き出すまでのその書き換え作業です。

evalEquation (Equation (Plus (Term (Factor (Integer 2)),Times (Factor (Integer 3),
Integer 5))))
↓
evalExpr (Plus (Term (Factor (Integer 2)),Times (Factor (Integer 3), Integer 5)))
↓
(evalExpr (Term (Factor (Integer 2)) ) +  (evalTerm (Times (Factor (Integer 3), Integer 5)))
↓
(evalTerm(Factor(Integer 2))) + (((evalTerm(Factor(Integer 3))) * (evalFactor(Integer5)))
↓
(evalFactor(Integer2)) + (evalFactor(Integer 3) * (float 5)
↓
(float 2) + ((float 3) * (float 5))
↓
(float 2) + (float 15)
↓
17

 上記例の出力結果は以下のようになります。

[リスト13]testProj実行例
Calculator
:2 + 3 * 5
Lexing [2 + 3 * 5]
Tokens : [INT32 2; PLUS; INT32 3; ASTER; INT32 5; EOF]
Parsing...
equation is Equation (Plus (Term (Factor (Integer 2)),Times (Factor (Integer 3),
Integer 5)))
Evaluating Equation...
Result: 17

 トークンとして定義されていない文字の入力に対しては以下のようなエラーが表示されます。

[リスト14]エラーの例
:ai + 1
Lexing [ai + 1]
Unhandled Exception: unrecognized input

まとめ

 コマンドラインアプリケーションのfslexとfsyaccですが、lex/yaccと仕様は大変似ています。しかし、ツールの特徴でもあるMSBuildタスクで使える、というメリットを生かして使いこなせるようになるとツールの使い勝手がさらに良くなると思います。次回はF#LOPのコンピュテーション式表現について解説します。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
C#プログラマのためのF#入門連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 星山 仁美(ホシヤマ ヒトミ)

WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS Twitter: @yyamada(公式)、@yyamada/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5355 2010/08/17 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング