文法どおりなのに動かない
売上を集計するスクリプトに、show total + step という行があります。文法には合っていて、トークンもどれも正しいものです。
でも step は、もう終わったループの中にしかありませんでした。パーサーが調べるのは文の形で、意味が通るかではありません。意味を調べるのは、コンパイラの次の部分です。
このレッスンのゴール
メモ
パーサーには見つけられない誤りを見つけられる。
重要な言葉
| ことば | いみ | れい |
|---|---|---|
| 意味解析 | プログラムの意味が通るかを調べること | |
| 宣言 | 名前を使えるようにする行 | int step |
| スコープ | ある名前が使える、プログラムの範囲 | |
| 記号表 | スコープにある名前を、コンパイラが記録する表 | |
| 型 | 値がどんな種類か | int |
| 警告 | ビルドを止めないメッセージ |
学生が入力するセルは、このプレビューでは空白です。
コンパイラは名前の表を持つ
コンパイラは構文木をたどりながら、記号表を持ちます。{ で新しいスコープが表の上に積まれ、宣言は一番上のスコープに名前を足します。} で一番上のスコープが捨てられます。
名前を調べるときは、一番上のスコープから下へ順に探します。どこにもなければ、宣言されていない名前です。Run で、scope.c の step がどこにあるかを見ます。
コンパイラのフォルダに入る
cd ~/compilerここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
step が出てくる行
grep -n "step" scope.cここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
例: scope.c の記号表
| 行 | 起きること | スコープにある名前 |
|---|---|---|
| 4と5 | main が unused と total を宣言する | unused, total |
| 6 | ループがスコープを開き、i を宣言する | unused, total | i |
| 7 | ループの中身が step を宣言する | unused, total | i | step |
| 9 | ループが終わり、そのスコープが捨てられる | unused, total |
| 10 | printf が step を使う。見つからない | unused, total |
学生が入力するセルは、このプレビューでは空白です。
追加の検査なしでコンパイル
gcc -c scope.cここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある警告も頼む
gcc -Wall -c scope.cここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
オブジェクトファイルはできたか
ls scope.oここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
関数の宣言と呼び出しを比べる
コンパイラは、式ごとの型も求め、使う場所に合うかを調べます。square は int を1つ受け取ると宣言されています。
最初の Run は、square(7, 8) と呼ぶコピー args.c を作ります。もとの hello.c は変わりません。呼び出しの形は文法に合っていますが、宣言とは合いません。次の Run でコンパイルします。
引数が多すぎるコピー
sed "s/square(7)/square(7, 8)/" hello.c > args.cここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
コピーをコンパイルする
gcc -Wall -c args.cここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
エラーは止め、警告は止めない
次のコピー kata.c は、printf に数を出す %d を頼み、文字列を渡します。gcc は printf の書式の読み方を知っているので、型を比べられます。
今度は警告です。ビルドは進み、ls で kata.o ができたと分かります。エラーのあとでは、オブジェクトファイルはできません。
書式の型が違うコピー
sed "s/%s: 7/%d: 7/" hello.c > kata.cここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
コンパイルする
gcc -Wall -c kata.cここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
今度はできたか
ls kata.oここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
よくある間違い
注意
ビルドが通るのだから、警告は無視してよいと思いがちです。警告は、文法の上では正しいが、おそらく誤りだ、というコンパイラの知らせです。kata.c は通りますが、動かすと文字列のアドレスを数として出します。警告は、安全な理由が分かるまで不具合として扱います。
Python は動かしながら調べる
C は、名前と型を実行の前に調べます。これを静的な検査と言います。Python は、その行を実行するときに初めて調べ、そのときエラーになります。
下の3つの Run は、どれもプログラムを起動します。どの誤りも、Python がその行に来るまで見つかりません。
Python が知らない名前
python3 -c "print(price)"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
足せない2つの型
python3 -c "print('500' + 1)"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
長さのない値
python3 -c "print(len(500))"ここでは表示のみで、実行されません。無料アカウントを作成すると、実行ボタンで自分のラボのターミナルに入力できます。
さらに挑戦: 使われない名前
課題のチェッカーに、3つ目の種類 "unused" を足しましょう。宣言されたのに一度も使われない名前を、宣言した行の番号で報告します。スコープが閉じるとき、またはプログラムの終わりに調べます。
PROGRAM の最初に var spare = 1 を足して確かめましょう。そして、gcc が unused は -Wall のときだけ報告し、step はいつも報告する理由を説明しましょう。