-
Notifications
You must be signed in to change notification settings - Fork 0
Home
wikiは主に読書記録とメモへのリンクに使います
「電卓レベルの言語の作成」まで読み終えた
やったこと
- da(逆アセンブルを手軽に実行するshellscript)を用意した。
- 提示されたソース,test1,test2,test3,test4の逆アセンブルを検証した。
次回は「ステップ1:整数1個をコンパイルする言語の作成」から
「gitによるバージョン管理」まで読み終えた
やったこと
- テストコードを準備した
- Makefileを準備した
gitは初日からリポジトリを準備していたので特にgitを設定する作業はなし。
次回は「ステップ2:加減算のできるコンパイラの作成」から
「ステップ2:加減算のできるコンパイラの作成」までよみおえた
やったこと
- strtolについて調べた
- シンプルな加減算処理とそのテストを追加した。
- wikiのページurlを全て英語になるように変更した
ライブラリのマニュアルを読むのに大半の時間を使ってしまった。しかもこれは今後置き換えられて出てこないのであまり重要じゃなさそう。反省。
次回は「ステップ3:トークナイザを導入」から
「ステップ3:トークナイザを導入」まで読み終えた
やったこと
- callocについて知った。callocではメモリを0クリアできる
- トークナイザのプログラムを熟読した
100行のpgのリーディングに30分全部使ってしまった。構造体とかポインタとかばりばり使うから初心者向けではないと思う。
次回は「ステップ4:エラーメッセージを改良」から
「再帰下降構文解析」まで読んだ。 コンパイルエラーに苦戦。
やったこと
- エラーメッセージを改善した
- 構文解析をする関数の追加
- テスト未追加
ソースコードはこの辺から全文掲載ではなくなってきた。
次回は「スタックマシン」から
「ステップ5:四則演算のできる言語の作成」まで読んだ。
やったこと
- 構文解析のコードを追加した。()*/
- コードフォーマッタを導入した。
本書はソースコードを全文掲載せず、いい感じに読者に感がさせながら穴埋めをさせるような作りになっているとおもう。
次回は「ステップ6:単項プラスと単項マイナス」から
一週間もあいてしまった!
「ステップ6:単項プラスと単項マイナス」を読んだ
やったこと
- 手書きで構文木をかいてみた。
- 単項演算子(unary operator)を実装した。
手書きで構文木を書いてコンパイラがパースするときの気持ちを理解するとコードを実装しやすくなる?
次回は「ステップ7: 比較演算子」から
また一日あいてしまった。少しずつでいいからなるべく毎日やりたい
「ステップ7: 比較演算子」
文法が大きく変わってしまっているので、expr,mul,unary,termを書き換え、equality,relational,addを追加する必要がある
old
expr = mul ("+" mul | "-" mul)*
mul = unary ("*" unary | "/" unary)*
unary = ("+" | "-")? term
term = num | "(" expr ")"
new
expr = equality
equality = relational ("==" relational | "!=" relational)*
relational = add ("<" add | "<=" add | ">" add | ">=" add)*
add = mul ("+" mul | "-" mul)*
mul = unary ("*" unary | "/" unary)*
unary = ("+" | "-")? term
term = num | "(" expr ")"
とりあえず、ラッパー関数(new_token_with_len)をつくって、今までの長さが1のトークンは今までどおり、new_tokenでトークナイズし、今回出現した二文字のトークンはラッパーの方で処理する。※いずれリファクタリングしてnew_tokenという名前で一本化する方針。
// 新しいトークンを作成してcurに繋げる
Token *new_token(TokenKind kind, Token *cur, char *str) {
Token *tok = calloc(1, sizeof(Token));
tok->kind = kind;
tok->str = str;
tok->len = 1;
cur->next = tok;
return tok;
}
//長さ付きトークンの作成メソッド
Token *new_token_with_len(TokenKind kind, Token *cur, char *str,int len){
Token *tok = new_token(kind,cur,str);
tok->len=len;
return tok;
}
次回も「ステップ7: 比較演算子」
「ステップ7: 比較演算子」の実装を進めた ==と!=を実装した
- 関係演算子の命名は以下のサイトを参考にした。 https://ja.wikipedia.org/wiki/%E9%96%A2%E4%BF%82%E6%BC%94%E7%AE%97%E5%AD%90
// 抽象構文木のノードの種類
typedef enum {
ND_ADD, // +
ND_SUB, // -
ND_MUL, // *
ND_DIV, // /
ND_NUM, // 整数
ND_EQU, // ==
ND_NEQ, // != (node not equal)
ND_LSS, // < (less than)
ND_LEQ, // <= (less equal)
ND_GTR, // > (greater than)
ND_GEQ // >= (greater equal)
} NodeKind;
次回も「ステップ7: 比較演算子」(<=と>=などの実装がメイン)
「ステップ7: 比較演算子」の実装が完成した <,<=,>,>=を実装した。
今回からCLionでやってる。なかなか使い心地が良い。
「分割コンパイルとリンク」を読んだ。 C言語の基本的な構成管理というか、ファイルの分割方法を復習した。
分割コンパイルの対応
次回は「グローバル変数の宣言と定義」から
ローカル変数
文法の変更
old
expr = equality
equality = relational ("==" relational | "!=" relational)*
relational = add ("<" add | "<=" add | ">" add | ">=" add)*
add = mul ("+" mul | "-" mul)*
mul = unary ("*" unary | "/" unary)*
unary = ("+" | "-")? term
term = num | "(" expr ")"
new
program = stmt*
stmt = expr ";"
expr = assign
assign = equality ("=" assign)?
equality = relational ("==" relational | "!=" relational)*
relational = add ("<" add | "<=" add | ">" add | ">=" add)*
add = mul ("+" mul | "-" mul)*
mul = unary ("*" unary | "/" unary)*
unary = ("+" | "-")? term
term = num | ident | "(" expr ")"
「ステップ10:複数文字のローカル変数」を読んだ
任意の長さの変数名に対応
次回「ステップ11:return文」
「ステップ11:return文」を読んだ
- return文を実装した
- gdbを復習してためしに使ってみると驚くほど早くバグを発見できた。
- gdbを宣伝したら軽くバズった
Cでプログラミングしてる人はprint文デバッグ捨ててgdb覚えた方が絶対いいです。もちろんgdb使えない場所ではprint文デバッグしますが、難しいからgdb使わないというのは損な気がします。バグ見つけるスピードがダンチです pic.twitter.com/OmG97NIkoc
— yuyabu@ネスペ10/20 (@yuyabu2) August 12, 2019
次回は「ステップ12: 制御構文を足す」から
久々にコンパイラ。最近cmakeを学習しているのでMakefileを参考にcmakeに書き換えてみた。