Ubuntu22.04でwineを使ってKindle本を読むときの設定

インストールや設定はこれに従えば良い。

Ubuntu 22.04 で WineHQ を使って Kindle を読む 2023年度版 #Ubuntu - Qiita

とにかく従うことが重要。勝手な判断はしてはいけない。

2系列だと動かねえ・・・昔のバージョンが見つからない場合はこれ。

https://kindleforpc.s3.amazonaws.com/65535/KindleForPC-installer-1.40.65535.exe

ビルド番号が間に挟まり、最後はバージョン番号のようだ。

そして、うまく行かなければ~/.wine以下を消す。中途半端に残ってるとamazonに登録するあたりで真っ白になったりする。

他にwineで使ってるWindowsアプリがある人は難しいだろうが自分はKindleしか使ってないので問題ない。

最後に、Kindle Cloud Readerは拡大できるようにしてほしい。

「低レイヤを知りたい人のためのCコンパイラ作成入門」のポインタ加減算のテスト

各ステップのテストは自分で書くこともあれば、chibiccの該当部分のテストをそのまま使ってそれが通ればヨシ!ということにしている。

ポインタの加減算のところで少し注意が必要で、これはローカル変数をスタックに積む(領域を確保する)順番が問題になることがある。

int main() {
  int x = 3;
  int y = 5;
  return *(&y - 1);
}

このようなテスト用プログラムがあったときに、x、yの順番で積むかy、xの順番で積むかによって結果が変わる。

単純に作っていくと、ここで積み方が逆になっててひっかかることがある。というか、自分はひっかかった。 (*(&y + 1);にしないと通らない。)

GCCでローカル変数のアドレスを表示させてもchibiccのテストのような順番だったので、ここはテストが通るように実装しておいたほうが 良いのだろうと思い、それに従った。

何かルールがあるのかは調べていない。

「低レイヤを知りたい人のためのCコンパイラ作成入門」の「ステップ12: 制御構文を足す」ではまる

はじめに

今、

低レイヤを知りたい人のためのCコンパイラ作成入門

を見ながらCコンパイラを作っている。C言語は一通り文法を学んでちょっとしたプログラムが書けるくらいで、ポインタへのポインタが出てくるとちょっと「うっ」となるレベル。

セルフホストまで行けるかかなり怪しいが、少なくともこのオンラインブックに書かれているステップはほぼ写経になろうともやり遂げたいと思う。

なお、この問題は自分があまりに愚直にオンラインブックの実装を辿っていただけだから発生したのかもしれない。 9ccやchibiccのソースを参考にしたり、自分でちゃんと実装を考えてた人は当たらないかも。

(ちなみに私はfor文は「for(初期化部; 条件部; 更新部) 本体」という構成だと思っているので「初期化部」などの用語が出てきたらあそこだと思ってほしい)

問題

で、今ステップ12まで来てif、for、whileあたりを実装していてできたと思っていた。 簡単なハッピーパスのテストは通ったので満足していた。

しかし、色々試しながら出てくるアセンブリを見ていたところpushとpopの数が合ってない。

例えば、この時点で以下のような無限ループするfor文

for(;;) 1;

を作ったコンパイラに通すと出てくるアセンブリ

.intel_syntax noprefix
.globl main
main:
  push rbp
  mov rbp, rsp
  sub rsp, 208
.Lbegin0:
  push 1
  jmp .Lbegin0
.Lend0:
  pop rax
  mov rsp, rbp
  pop rbp
  ret

こうなる。push 1が無限に呼ばれるので最終的にsegmentation faultになる。 無限ループしたいが無限ループにならずに落ちる。

解決

これは式(文)がスタックに結果を積むことを忘れていた(考慮してなかった)ために起こっているので、 forの本体のコード生成直後にpop raxを追加してやれば良い。

なお、自分は.Lend0:のあとにpush raxを追加するようにしておき、for文の評価結果とした。 これは、無限ループにならないfor文なら条件部の評価結果で0が入っているはず。

これをしておかないとコード全体のpushとpopの数が合わなくなる。

この状態で上のfor無限ループをコンパイルすると生成されるアセンブリはこうなった。

.intel_syntax noprefix
.globl main
main:
  push rbp
  mov rbp, rsp
  sub rsp, 208
.Lbegin0:
  push 1
  pop rax
  jmp .Lbegin0
.Lend0:
  push rax
  pop rax
  mov rsp, rbp
  pop rbp
  ret

おわりに

この対処が正しいのかもわからないし、本当ならそろそろ生成するアセンブリを工夫しないといけないのかもしれない。

なお、この問題はwhileでも起こる。while(1) 1;のようなコードを書けばわかる。

for文の初期化部や更新部でも起こる。for(;; i = i+1) 1;のようなコードをコンパイルすると同じくsegmentation faultになった。

「最小限のコードで動く最も汚いコードから始める」という記事を読んだ

「動作するきれいなコード」を目指すために、まずは「きたないけど動作するコード」で行こうという話を具体的なコードと思考をまじえて語っている。 この記事が良いなと思ったら、t_wadaさんの

動作するきれいなコード: SeleniumConf Tokyo 2019 基調講演文字起こし+α - t-wadaのブログ

に動画とそれを書き起こした記事があるので見ると大変良いと思う。

で、これ自体はそのとおりでよくやる。

ただ、あくまでたたき台であるとか、全体のごく一部であって全体のことはまた別に考えてあるなら良いけど こんな感じのまま発展していくと自分の場合、大きくなるにつれてきつくなる。

それはひとつにはリファクタリングが足りてないんだと思う。そしてテストも足りてない。設計力も足りてない。 特にテスト力が足りてないと感じるのでテストコードをやたら書いたり記事を読んだりしている。


記事

最小限のコードで動く最も汚いコードから始める

Vite + React + TypeScript + Vitestの環境構築(pnpm)

メモです。

Viteの導入

$ pnpm create vite

画面表示したがってReactやTypeScriptを選ぶ。+ SWCを選んでも良い。SWCはRust製の高速なバンドラらしい。

Vitestとtesting-libraryの導入

$ pnpm add -D vitest jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event

vitest.config.tsを以下のように書く。

import { defineConfig } from "vite";

export default defineConfig({
  test: {
    globals: true,
    environment: "jsdom",
  },
});

environmentはjsdomを使うので指定。

globalsはexpectやtestを毎回importしなくて済むように。 また、globalsをtrueにしておくと@testing-library/jest-domをimportするだけで使えるようになる。

ただし、このままだとTypeScriptがexpectやtestで怒り出すのでtsconfig.jsonの"compilerOptions"に

"types": ["vitest/globals"],

を追加しておく。

最後に、package.jsonの"scripts"に"test": "vitest run src"を追加するのを忘れずに。 watchモードがほしければ、"test:watch": "vitest"とでも書いておこう。

「AHA Testing」という記事を読んだ

前回読んだ記事は抽象化しすぎというかDRYにしすぎることを戒める記事だったけど、この記事はそれとともに全く抽象化しないのも駄目だという記事。

この記事にあるAHAというのはAvoid Hasty Abstractionの略で、「性急な抽象化を避けよ」という意味らしい。

テストもコード(プロダクトコード)も同じかな。DRYにしすぎても駄目。


記事

AHA Testing 💡

(日本語訳)

AHAテスト 💡 | makotot.dev

「Avoid Nesting when you're Testing」という記事を読んだ

describeによるネストを避けようという記事。 なんだけど、ネストも問題だけどbeforeEachやスコープの広い変数の使いすぎを戒める点の方が強い気がした。

このあたりは

【初心者向け】テストコードの方針を考える(何をテストすべきか?どんなテストを書くべきか?) - Qiita

にある「上から下へ、素直に読み下せるテストコードを書く 」と似たような感じかな。

こういったテストを実際書いたことがあって、しかも命名もあまりよくなかったので「これなんだっけ?」と上下の移動が激しくてめんどくさかったので 実体験からも避けるようにしている。 あと、beforeEachで書きそうなことを関数にすれば良いってのも自然にやっていた。


記事

Avoid Nesting when you're Testing