Slide 1

Slide 1 text

1

Slide 2

Slide 2 text

今日の内容 • プログラムのコンパイラ(の一部)の作り方を学ぶ. • 中身をしっかり学ぶには以下の授業をとることを推奨 • 形式言語理論 (理学部情報科学科2A) • 言語処理系論 (理学部情報科学科3S) 2 字句解析 構文解析 意味解析 中間コード生成,最適化 実行 機械語コード生成 コンパイラの場合 インタプリタの場合 プログラム

Slide 3

Slide 3 text

今日の内容 • 今日は字句解析,構文解析について取り扱う • これらを行うソフトを作る際のソフトがLex, YACC • インタプリタを作る際にも使える 3 字句解析 構文解析 意味解析 中間コード生成,最適化 実行 機械語コード生成 コンパイラの場合 インタプリタの場合 プログラム

Slide 4

Slide 4 text

LEX, YACCとは • Lex … (lexical analyser generator) • 字句解析器をコンパイルするソフト • YACC … (yet another compiler compiler) • 構文解析器をコンパイルするソフト • コンパイラ(の一部)をコンパイルするソフト 4

Slide 5

Slide 5 text

環境構築 • Linux上で開発を行う. • Windowsの場合,WSL (+ VSCode)の環境構築をする必要がある • 今回はVSCode上で行います, • 参考: https://qiita.com/_masa_u/items/d3c1fa7898b0783bc3ed • “sudo apt install flex”, “sudo apt install bison ”を実行してflex, bisonをインストール 5

Slide 6

Slide 6 text

LINUXのコマンド • LinuxはCLIで操作を行うことが多い • コマンド集 • ls … 現在のディレクトリ内のファイル一覧 • cd “行きたいディレクトリ” … そのディレクトリへ移動 • mkdir “ディレクトリ名” … ディレクトリ作成 • tar –xvzf “tgzファイル名” … tar.gz形式のファイルの解凍 • wget “ファイルのURL” … ファイルのダウンロード 6

Slide 7

Slide 7 text

サンプルのダウンロード • 今回はこちらで紹介されたサンプルを動かしながら行います. • https://berthub.eu/lex-yacc/#downloadからプログラムをダウンロードできます. • All examples from the HOWTOをクリック • Linux上にダウンロードするときは,ダウンロードしたいディレクトリ上で ”wget https://berthub.eu/lex-yacc/cvs/lex-yacc-examples.tar.gz” と打つ. • そこで”tar -xvzf lex-yacc-examples.tar.gz”と打って解凍 • VSCodeの環境も構築した場合は”code .”でそのディレクトリをワークスペースとしてVSCodeを開ける 7

Slide 8

Slide 8 text

サンプルの状況設定 • 使うファイルはexample5.*の3つ • 単純な温度調節器のインタプリタを作る • “heat on” で電源ON (“Heater on!”と表示) • “heat off” で電源OFF(”Heater off!”と表示) • “target temperature [int]”で温度を設定(“Temperature set to [int]”と表示) • それ以外はエラー (そのままecho) • “heat on target temperature 29”のように,連続して命令も送れる 8

Slide 9

Slide 9 text

サンプルの動かし方 • 解凍したフォルダに移動 (“cd lex-yacc-examples”) • スクリプトに実行権限を追加 (“sudo chmod 744 example5.compile”) • スクリプトを実行してコンパイル(“./example5.compile”) • .lファイルのコンパイル,.yファイルのコンパイル,それらの出力のコンパイルを順に行っている • 実行(“./example5”) 9

Slide 10

Slide 10 text

字句解析 • プログラムをコンパイルするときに最初に行う • プログラムの断片をトークンに分解し,それに応じた処理を行う. 10 456 Print VAR(Print) BRAC_L INT(123) PLUS INT(456) BRAC_R ( 123 + ) プログラム トークン列

Slide 11

Slide 11 text

字句解析 • 各トークンは(基本は)正規(正則)表現で記述される. • 基本的には最長一致をとる • 例:int, int_to_stringのトークンがあった場合,int_to_stringは後者としてとらえられる. • トークンを認識する際,その属性も出力する必要がある • 例:さっきのVAR(Print), INT(123)など 11

Slide 12

Slide 12 text

LEXの入力 • example5.lがlexの入力となる. • 主に2つの部分に分かれる (名称は普及したものではない) • コード部 • %{, %}で囲まれた部分. • 他のライブラリ,トークンの定義ファイルを読み込む • トークン部で使いたい関数をここに定義できる. • トークン部 • %%, %%で囲まれた部分 • 基本的にはCのコード • 各行が各トークンに対応 • 先に認識したい文字列を正規表現で書き,その後,”return [トークン名]“でどのトークンに対応するかを記述 • yytval, yytextについては後ほど 12

Slide 13

Slide 13 text

LEXの入力 13 %{ #include #include "y.tab.h" %} … %% [0-9]+ yylval=atoi(yytext); return NUMBER; heat return TOKHEAT; on|off yylval=!strcmp(yytext,"on"); return STATE; target return TOKTARGET; temperature return TOKTEMPERATURE; ¥n /* ignore end of line */; [ ¥t]+ /* ignore whitespace */; %% コード部 使うライブラリを列挙.y.tab.hは YACCの出力(後述) トークン部 認識したい文字列を正規表現で 書き,y.tab.hで定義したトークンを 返すように指定する サンプルのexample5.l (一部省略)

Slide 14

Slide 14 text

よく使う正規表現 文字 説明 . 任意の一文字を表す. + 直前の文字を1回以上繰り返したものの最長一致に対応. * 直前の文字を0回以上繰り返したものの最長一致に対応. | 前後のどちらかの条件が合えばOK ¥ 直後のメタ文字をエスケープする […] 括弧内のいずれか1文字 [a-x] aからxまでのいずれかの文字 ¥d 半角数字のいずれか (Lexでは使用不可) ¥D 半角数字以外のいずれか (Lexでは使用不可) 14

Slide 15

Slide 15 text

エラー処理 • トークンとして認識できない文字列を読んだ場合,デフォルトはそのままechoする. • エラー処理を行いたい場合, • 変換規則の最後に. error();と書き, • コード部にerror()関数を定義すればよい 15

Slide 16

Slide 16 text

正規言語 • 文字列の集合を言語という. • 規則に則っているとみなせる文字列の集合 • 例: 日本語 = {“こんにちは”, “私はHNです.”, …} • 正規表現で表せる言語の集合を正規言語と呼ぶ. • 構成規則の表現の多様さに応じていくつか集合がある • 広い集合に属するほどいろんな文字列を生成でき, 計算が複雑になる • 一般的なプログラミング言語は.文脈自由文法(後述)で十分 16 正規言語 文脈自由言語 文脈依存言語 帰納的言語 コンピュータで計 算できる限界 日本語,英語 プログラミング言語

Slide 17

Slide 17 text

正規言語の限界 • かなり制約の厳しい表現なので,当然パースできない言語もある. • 例:ネストも含むコメント(以下すべてを正しくパースできるか) • /* コメント*/ • /* コメント */ int a = 1; /* コメント */ • /* /* コメント */ コメント */ • /* /* … /* コメント */ … */ */ • これらもパースしたい場合 • より表現力の強いものを使う • 部分部分に分けてあとはYACCに任せる 17

Slide 18

Slide 18 text

構文解析 • トークン列がどのような構文で導出されるかを解析する • この導出の様子を木で表したものを構文木という • どのような構文を許すかは文脈自由文法(のサブセット)で表せる • 文脈自由文法で表せる言語全体が文脈自由言語 18 VAR(Print) BRAC_L INT(123) PLUS INT(456) BRAC_R formula formula formula command

Slide 19

Slide 19 text

文脈自由文法 • 定義:文脈自由文法𝐺 = (𝑉, 𝑇, 𝑆, 𝑃)を以下のように与える. • 𝑉: 有限要素数の文字集合.この要素を変数と呼ぶ. • 𝑇: 有限要素数の文字集合.この要素数を終端記号とよび,𝑉 ∩ 𝑇 = ∅である. • 𝑆: 開始記号と呼ばれる特別な変数.つまり,𝑆 ∈ 𝑉 • 𝑃: 生成規則の集合. • 生成規則: 変数𝐴 ∈ 𝑉と,変数と終端記号からなる文字列𝛼 ∈ 𝑉 ∪ 𝑇 ∗に対して,𝐴 → 𝛼の形の規則. • 𝑆から生成規則に基づいて変数を繰り返し置換していき,終端記号のみで生成される文字列が 生成されれば,その文字列を𝐺から導出できたとする. 19

Slide 20

Slide 20 text

文脈自由文法の例 • サンプルの温度調節器のコマンドは以下の文脈自由文法で導出できる.(説明の都合上文字 列を文字として扱えるようにしています) • 𝑉 = (commands, command, heat_switch, target_set) • 𝑇 = TOKHEAT, STATE, TOKTARGET, TOKTEMPERATURE, NUMBER • 𝑆 = commands • 𝑃 = commands → commands command 𝜖, command → heat_switch | target_set, heat_switch → TOKHEAT STATE, target_set → TOKTARGET TOKTEMPERATURE NUMBER} 20 ※𝜖は空文字列 ※開始記号が同じとき,|を使って 複数の規則をまとめることができる

Slide 21

Slide 21 text

YACCの入力 • コード部 • %{, %}で囲まれた部分. • 他のライブラリ,トークンの定義ファイ ルを読み込む • トークン部で使いたい関数をここに定 義できる. • ここにmain関数を記述することもできる. • yyerrorはパースエラー時に呼び出され る関数 21 %{ #include #include … main() { yyparse(); } %} ※名称は普及したものでない example.5.yより(一部省略)

Slide 22

Slide 22 text

YACCの入力 • トークン部 • コード部とルール部の間にある. • ルール部で使うトークンを”%token …” の形でここで指定 • ここで指定されたトークンをlexで使える • 優先度もここで指定できる(後述) 22 %token NUMBER TOKHEAT STATE TOKTARGET TOKTEMPERATURE ※名称は普及したものでない example.5.yより(一部省略)

Slide 23

Slide 23 text

YACCの入力 • ルール部 • %%の後ろの部分 • “変数 : 変換後1 | 変換後2 … ”という, BNF記法で書ける • 一番最初の規則の変数が自動的に開 始記号となる • このルールが適用された際に{}内の処 理を実行する • n番目のトークンの属性値を取得した い場合,$nとすればそれを取得できる. 23 %% commands: | commands command ; heat_switch: TOKHEAT STATE { if($2) printf("¥tHeat turned on¥n"); else printf("¥tHeat turned off¥n"); } ; ※名称は普及したものでない example.5.yより(一部省略)

Slide 24

Slide 24 text

属性値の取得 • YACCで属性値を得るにはlexで下処理が必要 • yytext … 規則に合致した実際の文字列が代入され る • yylval … これに代入した値がYACCのトークンの属 性値に割り当てられる. • 属性値を返したいとき • “$$ = …”と書く 24 on|off yylval=!strcmp(yytext,"on"); return STATE; heat_switch: TOKHEAT STATE { if($2) printf("¥tHeat turned on¥n"); else printf("¥tHeat turned off¥n"); } ; サンプルコードより一部抜粋

Slide 25

Slide 25 text

YACCの限界 • YACCは文脈自由言語すべてを取り扱えるわけではない • 文脈自由言語全部扱えるようにすると,構文木のパースに𝑂(𝑛3)かかるため • 曖昧でないLL(1)言語しか扱えない • 結構強い制約だが,パースにかかる時間は𝑂(𝑛) • これを破る制約を入力するとconflictsというエラーを吐く 25

Slide 26

Slide 26 text

文脈自由文法の曖昧性 • 𝐸 → 𝐸 + 𝐸 𝐸 ∗ 𝐸 𝑎という規則ででき る文脈自由文法を考える. • a+a*aは導出可能 • しかし,導出木が二通りある • 解釈の仕方によっては計算結果が変 わるためプログラミング言語としては避 けたい仕様 26 a + a * a a + a * a E E E E E E E E E E

Slide 27

Slide 27 text

LL(K)文法 • LL(k)文法 … トークンを左から読んでいったときに,多くとも k文字先読みすれば生成規則の適用を決定できる文法 • 例: 𝑆 → 𝐴 | 𝐵 𝑐𝑑, 𝐴 → 𝑎𝑏𝑐𝑒, 𝐵 → 𝑎𝑏は𝐿𝐿(1)文法ではない • 𝑎𝑏と入力されたとき,これが𝐵から生成されたか,𝐴から生成 されたものの一部かは次の一文字(𝑐)をみてもわからない 27 a b c ? a b c ? A B S S

Slide 28

Slide 28 text

優先度による曖昧性の除去 • 演算子の間には優先度がある • 例: + < * < - (単項演算子) • 二項演算子の場合,両辺どちらを先に計算するかの優先度がある • 例 1 + 2 + 3 = (1 + 2) + 3, 𝐴 → 𝐵 → 𝐶 = 𝐴 → 𝐵 → 𝐶 • 前者を左結合,後者を右結合と呼ぶ. 28

Slide 29

Slide 29 text

優先度による曖昧性の除去 • YACCでは,トークンの優先度を指定できる. • トークン部で複数行にわたってトークンを宣言すると,上から 昇順の優先度が割り当てられる • 同じ行なら同じ優先度 • %tokenの代わりに%left, %rightを使って左結合,右結合を指定 できる 29 %left PLUS, MINUS %left TIMES, DIVIDE %token BRAC_L, BRAC_R 優先度 高 低

Slide 30

Slide 30 text

演習 • 以下の条件を満たすインタプリタを作成せよ • 加算,減算,乗算を一般的な中置記法で記述して整数の計算ができるもの. • とりあえず負の整数が式内に現れることは考えないとする. • “;”で計算を区切り,その時点までの結果を出力して次の入力に移る • 括弧で演算の順序も変えられるように • 入出力例 • 1 + 2 * 3; … 7 • (51 – 41) * 0 + 20; … 20 • 10 + 1 – 2; 3 * 1 – (11 + 1); … 9 -9 30

Slide 31

Slide 31 text

演習のヒント • 非負整数を表す正規表現は”([1-9][0-9]+)|[0-9]” • “[0-9]+”だと00012のようなものも認識してしまうことに注意(場合によってはこれでもよい). • “;”で各計算をという部分はサンプルの開始記号の生成規則部分を流用できそう • 文脈自由文法の各変数は,表したい文字列の部分集合と考えるとイメージしやすい • 例 𝑒𝑥𝑝 という変数を作って 𝑒𝑥𝑝 → 𝑒𝑥𝑝 | 𝑒𝑥𝑝 + 𝑒𝑥𝑝 … • 答え 31

Slide 32

Slide 32 text

参考文献 • https://linuxjf.osdn.jp/JFdocs/Lex-YACC-HOWTO.html • https://docs.oracle.com/cd/E19620-01/805-5827/6j5gframt/index.html • http://www.momo.cs.okayama-u.ac.jp/~sasakura/jikken/2017/yacc-lex/yacc.pdf • 形式言語理論,言語処理系論の授業スライド • サンプルのライセンス • Copyright (c) 2001 by bert hubert. この著作物の配布に関しては Open Publication License, vX.Y またはそれ以降 で定められている規約と条件に準 拠します(最新版は http://www.opencontent.org/openpub/ で入手可能です)。 32