From 09b8483ed67d3b85e983ef86c34260081975e1cb Mon Sep 17 00:00:00 2001 From: nsfisis Date: Fri, 2 May 2025 06:47:07 +0900 Subject: fix(blog/nuldoc): remove unnecessary "language" attribute --- vhosts/blog/nuldoc-src/djot/to_html.ts | 1 + .../index.html | 4 +-- .../python-unbound-local-error/index.html | 6 ++-- .../ruby-detect-running-implementation/index.html | 4 +-- .../ruby-then-keyword-and-case-in/index.html | 22 ++++++------ .../rust-where-are-primitive-types-from/index.html | 8 ++--- .../index.html | 8 ++--- .../vim-swap-order-of-selected-lines/index.html | 8 ++--- .../2022-04-09/phperkaigi-2022-tokens/index.html | 28 +++++++-------- .../php-conference-okinawa-code-golf/index.html | 4 +-- .../index.html | 28 +++++++-------- .../phperkaigi-2023-unused-token-quiz-1/index.html | 18 +++++----- .../setup-server-for-this-site/index.html | 40 +++++++++++----------- .../phperkaigi-2023-unused-token-quiz-2/index.html | 14 ++++---- .../phperkaigi-2023-unused-token-quiz-3/index.html | 14 ++++---- .../index.html | 20 +++++------ .../compile-php-runtime-to-wasm/index.html | 20 +++++------ .../index.html | 6 ++-- .../index.html | 6 ++-- .../pipefail-option-in-gitlab-ci-cd/index.html | 12 +++---- .../index.html | 6 ++-- .../reparojson-fix-only-json-formatter/index.html | 8 ++--- .../index.html | 2 +- .../posts/2024-12-04/cohackpp-report/index.html | 2 +- .../phperkaigi-2023-tokens-q1/index.html | 6 ++-- .../index.html | 2 +- .../trick-2025-most-ruby-on-ruby-award/index.html | 14 ++++---- 27 files changed, 156 insertions(+), 155 deletions(-) (limited to 'vhosts/blog') diff --git a/vhosts/blog/nuldoc-src/djot/to_html.ts b/vhosts/blog/nuldoc-src/djot/to_html.ts index 72c169a0..f31408da 100644 --- a/vhosts/blog/nuldoc-src/djot/to_html.ts +++ b/vhosts/blog/nuldoc-src/djot/to_html.ts @@ -317,5 +317,6 @@ async function transformAndHighlightCodeBlockElement(doc: Document) { sourceCodeNode.raw = true; n.name = "div"; n.attributes.set("class", "codeblock"); + n.attributes.delete("language"); }); } diff --git a/vhosts/blog/public/posts/2021-10-02/cpp-you-can-use-keywords-in-attributes/index.html b/vhosts/blog/public/posts/2021-10-02/cpp-you-can-use-keywords-in-attributes/index.html index 94b9d8bc..16c466ba 100644 --- a/vhosts/blog/public/posts/2021-10-02/cpp-you-can-use-keywords-in-attributes/index.html +++ b/vhosts/blog/public/posts/2021-10-02/cpp-you-can-use-keywords-in-attributes/index.html @@ -73,7 +73,7 @@

タイトル落ち。まずはこのコードを見て欲しい。

-
+
#include <iostream>
 
 [[alignas]] [[alignof]] [[and]] [[and_eq]] [[asm]] [[auto]] [[bitand]]
@@ -138,7 +138,7 @@
           

上のコードでは [[using]] をコメントアウトしているが、これは using キーワードのみ属性構文の中で意味を持つからであり、このコメントアウトを外すとコンパイルに失敗する。

-
+
// using の例
 [[using foo: attr1, attr2]] int x; // [[foo::attr1, foo::attr2]] の糖衣構文
diff --git a/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html b/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html index db2da17c..c3d48b89 100644 --- a/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html +++ b/vhosts/blog/public/posts/2021-10-02/python-unbound-local-error/index.html @@ -76,7 +76,7 @@

Python でクロージャを作ろうと、次のようなコードを書いた。

-
+
def f():
     x = 0
     def g():
@@ -96,7 +96,7 @@
           

local変数 x が代入前に参照された、とある。これは、fx を参照するのではなく、新しく別の変数を g 内に作ってしまっているため。 前述のコードを宣言と代入を便宜上分けて書き直すと次のようになる。var を変数宣言のための構文として擬似的に利用している。

-
+
# 注: var は正しい Python の文法ではない。上記参照のこと
 def f():
   var x           #  f の local変数 'x' を宣言
@@ -111,7 +111,7 @@
           

当初の意図を表現するには、次のように書けばよい。

-
+
def f():
     x = 0
     def g():
diff --git a/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html b/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html
index b4d41329..b11626c9 100644
--- a/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html
+++ b/vhosts/blog/public/posts/2021-10-02/ruby-detect-running-implementation/index.html
@@ -79,7 +79,7 @@
           

上記ページの例から引用する:

-
+
$ ruby-1.9.1 -ve 'p RUBY_ENGINE'
 ruby 1.9.1p0 (2009-03-04 revision 22762) [x86_64-linux]
 "ruby"
@@ -116,7 +116,7 @@
           

mruby 該当部分のソース より引用:

-
+
/*
  * Ruby engine.
  */
diff --git a/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html b/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html
index aa5b2781..614957e4 100644
--- a/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html
+++ b/vhosts/blog/public/posts/2021-10-02/ruby-then-keyword-and-case-in/index.html
@@ -81,7 +81,7 @@
             

使われることは稀だが、Ruby では then がキーワードになっている。次のように使う:

-
+
if cond then
   puts "Y"
 else
@@ -91,7 +91,7 @@
             

このキーワードが現れうる場所はいくつかあり、ifunlessrescuecase 構文がそれに当たる。 上記のように、何か条件を書いた後 then を置き、式がそこで終了していることを示すマーカーとして機能する。

-
+
# Example:
 
 if x then
@@ -119,7 +119,7 @@
             

普通 Ruby のコードで then を書くことはない。なぜか。次のコードを実行してみるとわかる。

-
+
if true puts 'Hello, World!' end

@@ -138,7 +138,7 @@

ポイントは改行が then (や ;) の代わりとなることである。true の後に改行を入れてみる。

-
+
if true
 puts 'Hello, World!' end
@@ -151,19 +151,19 @@

なぜ then; や改行 (以下 「then 等」) が必要なのだろうか。次の例を見てほしい:

-
+
if a b end

then; も改行もないのでエラーになるが、これは条件式がどこまで続いているのかわからないためだ。 この例は二通りに解釈できる。

-
+
# a という変数かメソッドの評価結果が truthy なら b という変数かメソッドを評価
 if a then
 b
 end
-
+
# a というメソッドに b という変数かメソッドの評価結果を渡して呼び出し、
 # その結果が truthy なら何もしない
 if a(b) then
@@ -184,7 +184,7 @@
             

https://github.com/ruby/ruby/blob/221ca0f8281d39f0dfdfe13b2448875384bbf735/parse.y#L3961-L3986

-
+
p_case_body : keyword_in
 {
   SET_LEX_STATE(EXPR_BEG|EXPR_LABEL);
@@ -215,7 +215,7 @@
             

簡略版:

-
+
p_case_body : keyword_in p_top_expr then compstmt p_cases
 ;
@@ -225,7 +225,7 @@

これにより、case - when による従来の構文と同じように、then 等をパターンの後ろに挿入すればよいことがわかった。つまり次の3通りのいずれかになる:

-
+
case x
 in 1 then a
 in 2 then b
@@ -250,7 +250,7 @@
             

ところで、p_top_expr には if による guard clause が書けるので、その場合は if - then と似たような見た目になる。

-
+
case x
 in 0 then a
 in n if n < 0 then b
diff --git a/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html b/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html
index d0d5156c..dffa5173 100644
--- a/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html
+++ b/vhosts/blog/public/posts/2021-10-02/rust-where-are-primitive-types-from/index.html
@@ -72,7 +72,7 @@
             

Rust において、プリミティブ型の名前は予約語でない。したがって、次のコードは合法である。

-
+
#![allow(non_camel_case_types)]
 #![allow(dead_code)]
 
@@ -140,7 +140,7 @@
             

rustc_resolve というのはいかにも名前解決を担いそうなクレート名である。該当箇所を見てみる。

-
+
/// Interns the names of the primitive types.
 ///
 /// All other types are defined somewhere and possibly imported, but the primitive ones need
@@ -185,7 +185,7 @@
             

とある。次はこの struct の使用箇所を追う。追うと言っても使われている箇所は次の一箇所しかない。なお説明に不要な箇所は大きく削っている。

-
+
/// This resolves the identifier `ident` in the namespace `ns` in the current lexical scope.
 /// (略)
 fn resolve_ident_in_lexical_scope(
@@ -220,7 +220,7 @@
             

動作がわかったところで、例として次のコードを考える。

-
+
#![allow(non_camel_case_types)]
 
 struct bool;
diff --git a/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html b/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html
index 3b9470bb..1d1d97fe 100644
--- a/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html
+++ b/vhosts/blog/public/posts/2021-10-02/vim-difference-between-autocmd-bufwrite-and-bufwritepre/index.html
@@ -119,14 +119,14 @@
               

https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L85-L86

-
+
{"BufAdd",      EVENT_BUFADD},
 {"BufCreate",   EVENT_BUFADD},

https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L95-L97

-
+
{"BufRead",     EVENT_BUFREADPOST},
 {"BufReadCmd",  EVENT_BUFREADCMD},
 {"BufReadPost", EVENT_BUFREADPOST},
@@ -134,7 +134,7 @@

https://github.com/vim/vim/blob/8e6be34338f13a6a625f19bcef82019c9adc65f2/src/autocmd.c#L103-L105

-
+
{"BufWrite",    EVENT_BUFWRITEPRE},
 {"BufWritePost",    EVENT_BUFWRITEPOST},
 {"BufWritePre", EVENT_BUFWRITEPRE},
@@ -148,7 +148,7 @@

https://github.com/neovim/neovim/blob/71d4f5851f068eeb432af34850dddda8cc1c71e3/src/nvim/auevents.lua#L119-L124

-
+
aliases = {
   BufCreate = 'BufAdd',
   BufRead = 'BufReadPost',
diff --git a/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html b/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html
index 1ccc0513..3da75e76 100644
--- a/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html
+++ b/vhosts/blog/public/posts/2021-10-02/vim-swap-order-of-selected-lines/index.html
@@ -69,7 +69,7 @@
           

TL; DR

-
+
" License: Public Domain
 
 command! -bar -range=%
@@ -131,7 +131,7 @@
               

なお、:g/^/m0 は全ての行を入れ替えるが、:N,Mg/^/mN-1 とすることで N行目から M行目を処理範囲とするよう拡張できる。手でこれを入力するわけにはいかないので、次のようなコマンドを用意する。

-
+
command! -bar -range=%
     \ Reverse
     \ <line1>,<line2>g/^/m<line1>-1
@@ -167,7 +167,7 @@

前述した :Reverse コマンドの定義を少し変えて、次のようにする:

-
+
function! s:reverse_lines(from, to) abort
     execute printf("%d,%dg/^/m%d", a:from, a:to, a:from - 1)
 endfunction
@@ -220,7 +220,7 @@
                 

-
+
command! -bar -range=%
     \ Reverse
     \ keeppatterns <line1>,<line2>g/^/m<line1>-1
diff --git a/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html b/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html index 9717789c..36183432 100644 --- a/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html +++ b/vhosts/blog/public/posts/2022-04-09/phperkaigi-2022-tokens/index.html @@ -80,7 +80,7 @@

ソースコードはこちら。実行には PHP 8.1 以上が必要なので注意。

-
+
<?php
 
 declare(strict_types=0O1);
@@ -250,7 +250,7 @@
                 

ソースコードのライセンスを示したこの部分だが、

-
+
https://creativecommons.org/publicdomain/zero/1.0/

@@ -262,7 +262,7 @@

ソースコード中に、ほとんど数値リテラルが書かれていないことにお気づきだろうか。 PHP では、型変換を利用することで任意の整数を作り出すことができる。

-
+
assert(0 === +!![]);
 assert(1 === +![]);
 assert(2 === ![]+![]);
@@ -301,7 +301,7 @@
             

ソースコードはこちら。実行には PHP 8.0 以上が必要なので注意。

-
+
<?php
 
 /*********************************************************
@@ -348,7 +348,7 @@
               

まずはソースコードを読んでいく。

-
+
$token = [
   // 略
 ];
@@ -356,25 +356,25 @@

数値からなる $token があり、各要素をループしている。

-
+
$x = $x ^ N;

まずは排他的論理和 (xor) を取り、

-
+
$x = sprintf('%025b', $x);

二進数に変換して、

-
+
$x = str_replace(search: ['0', '1'], replace: [' ', '#'], subject: $x);

0 を空白に、1 を # にし、

-
+
$x = implode("\n", str_split($x, length: 5));

@@ -412,13 +412,13 @@

N は高々

-
+
assert(0 <= N && N <= 0b11111_11111_11111_11111_11111);

なのでブルートフォースしてもよいが、ここではブルートフォースしない方法を紹介する。

-
+
<?php
 
 $x = 0x14B499C;
@@ -439,7 +439,7 @@
               

この一連の変換に対する逆変換を考えると、次のようになる。

-
+
<?php
 
 $x =
@@ -467,7 +467,7 @@
             

ソースコードはこちら。

-
+
<?php
 
 // License: https://creativecommons.org/publicdomain/zero/1.0/
@@ -501,7 +501,7 @@
             

コメントにもあるとおり、次のようにして実行すれば答えがでてくる。

-
+
$ php toquine.php | php | php | php | ...

diff --git a/vhosts/blog/public/posts/2022-08-27/php-conference-okinawa-code-golf/index.html b/vhosts/blog/public/posts/2022-08-27/php-conference-okinawa-code-golf/index.html index 75354b32..e93f8c0c 100644 --- a/vhosts/blog/public/posts/2022-08-27/php-conference-okinawa-code-golf/index.html +++ b/vhosts/blog/public/posts/2022-08-27/php-conference-okinawa-code-golf/index.html @@ -108,7 +108,7 @@

書いたものがこちら:

-
+
[<?php $n=$argv[1];foreach([1e4,5e3,2e3,1e3,500,100,50,10,5,1]as$x)for(;$n>=$x;$n-=$x)$r[]=$x;echo implode(', ',$r??[]);?>]

@@ -117,7 +117,7 @@

こちらは改行とスペースを追加したバージョン:

-
+
[<?php
 
 $n = $argv[1];
diff --git a/vhosts/blog/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html b/vhosts/blog/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
index a1f17f47..8f028472 100644
--- a/vhosts/blog/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
+++ b/vhosts/blog/public/posts/2022-09-29/write-fizzbuzz-in-php-2-letters-per-line/index.html
@@ -117,7 +117,7 @@
             

特に、C言語でこのような試みをおこなったことがあるかたならそう思うだろう。事実、Cでのこの制約はほとんど無意味に等しい。

-
+
#\
 i\
 n\
@@ -232,7 +232,7 @@
             

また、2文字だと文字列がまともに書けないのも辛い。'' だけで2文字使うので、 「1文字の文字列リテラル」というものを書くことができない。PHP では文字列リテラル中に生の改行が書けるので

-
+
$a
 ='
 a'
@@ -252,7 +252,7 @@
               

まずは普通に書くとしよう。

-
+
<?php
 
 for ($i = 1; $i < 100; $i++) {
@@ -268,7 +268,7 @@
               

for は、3文字もある長いキーワードである。 こんなものは使えない。array_ 系の関数を使って、適当に置き換えるとしよう。

-
+
<?php
 
 $s = range(1, 100);
@@ -287,7 +287,7 @@
               

rangearray_walkprintf は長すぎるのでどうにかせねばならない。 ここで、PHP の可変関数を使う。可変関数とは、関数名が文字列として入った変数を経由して、関数を呼び出す機能である。

-
+
<?php
 
 $r = 'range';
@@ -320,7 +320,7 @@
               

というルールがない場合、「未定義の定数が評価された場合、その定数の名前が値になる」という PHP 7.x までの仕様が利用できる。 例えば、 Fizz という文字列が欲しければ、次のようにする。

-
+
$f
 =F
 .i
@@ -331,7 +331,7 @@
               

こうして簡単に文字列を作れる。 なお、この仕様は 7.x 時点でも警告を受けるので、@ 演算子を使って抑制してやるとよい。

-
+
$f
 =@
 F.
@@ -354,7 +354,7 @@
               

ずばり、文字列同士のビット演算を使う。 PHP では、文字列同士でビット演算 (&|^) をした場合、 文字列の各バイトごとに指定したビット演算がなされ、それを結合したものが演算結果となる。

-
+
$a = "12345";
 $b = "world";
 
@@ -370,7 +370,7 @@
               

これを踏まえ、次のコードを見てみよう。

-
+
$x = "x\nOm\n";
 $y = "\nk!\no";
 $r = $x ^ $y;
@@ -379,7 +379,7 @@
               

実行すると、range が表示される。 さて、PHP では文字列リテラル中に生の改行を直接書いてもよいのだった (「主な障害」の節を参照のこと)。 書きかえてみよう。

-
+
$x
 ='x
 Om
@@ -396,7 +396,7 @@
               

さらに # を使って適当に調整すると、次のようになる。

-
+
$x
 =#
 'x
@@ -429,7 +429,7 @@
             

完成したものがこちら。

-
+
<?php
 
 $x
@@ -595,7 +595,7 @@
             

PHP では、バッククォートを使ってシェルを呼び出せる。 これは shell_exec 関数と等価である。 さて、PHP ではバックスラッシュによる行継続が使えないと書いたが、シェルでは使える (当然だが、呼び出されるシェルに依存する。Bash なら大丈夫だろう。知らんけど)。

-
+
<?php
 
 printf(`
@@ -628,7 +628,7 @@
             

もうこれ以上は不可能だと思っていたのだが、この記事の執筆中に解決する方法を思いついたので載せておく。

-
+
<?php
 
 $c = 'chr';
diff --git a/vhosts/blog/public/posts/2022-10-23/phperkaigi-2023-unused-token-quiz-1/index.html b/vhosts/blog/public/posts/2022-10-23/phperkaigi-2023-unused-token-quiz-1/index.html
index 3d84fe35..f5b4922c 100644
--- a/vhosts/blog/public/posts/2022-10-23/phperkaigi-2023-unused-token-quiz-1/index.html
+++ b/vhosts/blog/public/posts/2022-10-23/phperkaigi-2023-unused-token-quiz-1/index.html
@@ -80,7 +80,7 @@
             

注意: これはボツ問なので、得られたトークンを PHPerKaigi で入力してもポイントにはならない。

-
+
<?php
 
 = $argv[1] ?? null;
@@ -109,14 +109,14 @@
             

ソースを見るとわかるとおり、$argv[1] を参照している。 それを なる変数に代入しているので、円周率を渡してみる。

-
+
$ php Q.php 3.14
 Failed.

失敗してしまった。精度を上げてみる。

-
+
$ php Q.php 3.1415
 Failed.
@@ -126,7 +126,7 @@

最初にトークンが得られるのは、小数点以下 16 桁目まで入力したときで、こうなる。

-
+
$ php Q.php 3.1415926535897932
 Token: #YO
@@ -139,7 +139,7 @@

短いので頭から追っていく。

-
+
= $argv[1] ?? null;
 if ($π === null) {
   exit('No input.');
@@ -152,7 +152,7 @@
             

入力のバリデーション部分。数値のみ受け付ける。

-
+
$s = implode(array_map(chr(...), str_split($π, 2)));

@@ -161,13 +161,13 @@

例えば、'656667' だったとすると、 656667 に対応した 'A''B''C' へと変換され、'ABC' になる。

-
+
= '656667';
 $s = implode(array_map(chr(...), str_split($π, 2)));
 echo $s;
 // => ABC
-
+
preg_match('/(\x23.+?) /', $s, $m);
 $t = $m[1] ?? '';
@@ -177,7 +177,7 @@

なお、# を直接書いていないのは、/#.+?) / と書くと、 #.+?) という意図せぬトークンが登録されてしまうからである。

-
+
if (md5($t) === '056e831a4146bf123e8ea16613303d2e') {
   echo "Token: {$t}\n";
 } else {
diff --git a/vhosts/blog/public/posts/2022-10-28/setup-server-for-this-site/index.html b/vhosts/blog/public/posts/2022-10-28/setup-server-for-this-site/index.html
index 76bce3d6..fd9bba80 100644
--- a/vhosts/blog/public/posts/2022-10-28/setup-server-for-this-site/index.html
+++ b/vhosts/blog/public/posts/2022-10-28/setup-server-for-this-site/index.html
@@ -88,7 +88,7 @@
               

ローカルマシンで鍵を生成する。

-
+
$ ssh-keygen -t ed25519 -b 521 -f ~/.ssh/teika.key
 $ ssh-keygen -t ed25519 -b 521 -f ~/.ssh/github2teika.key
@@ -101,7 +101,7 @@

.ssh/config に設定しておく。

-
+
Host teika
     HostName **********
     User **********
@@ -124,7 +124,7 @@
               

管理者ユーザで作業すると危ないので、メインで使うユーザを作成する。 sudo グループに追加して sudo できるようにし、su で切り替え。

-
+
$ sudo adduser **********
 $ sudo adduser ********** sudo
 $ su **********
@@ -133,13 +133,13 @@
             

ホスト名を変える

-
+
$ sudo hostname teika

公開鍵を置く

-
+
$ mkdir ~/.ssh
 $ chmod 700 ~/.ssh
 $ vi ~/.ssh/authorized_keys
@@ -153,7 +153,7 @@

SSH の設定を変更し、少しでも安全にしておく。

-
+
$ sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
 $ sudo vi /etc/ssh/sshd_config
@@ -171,7 +171,7 @@

そして設定を反映。

-
+
$ sudo systemctl restart sshd
 $ sudo systemctl status sshd
@@ -181,7 +181,7 @@

今の SSH セッションは閉じずに、ターミナルを別途開いて疎通確認する。 セッションを閉じてしまうと、SSH の設定に不備があった場合に締め出しをくらう。

-
+
$ ssh teika
@@ -190,7 +190,7 @@

デフォルトの 22 番を閉じ、設定したポートだけ空ける。

-
+
$ sudo ufw deny ssh
 $ sudo ufw allow *******
 $ sudo ufw enable
@@ -206,20 +206,20 @@
               

GitHub に置いてある private リポジトリをサーバから clone したいので、SSH 鍵を生成して置いておく。

-
+
$ ssh-keygen -t ed25519 -b 521 -f ~/.ssh/github.key
 $ cat ~/.ssh/github.key.pub

GitHub の設定画面 から、この公開鍵を追加する。

-
+
$ vi ~/.ssh/config

設定はこう。

-
+
Host github.com
     HostName github.com
     User git
@@ -230,13 +230,13 @@
               

最後に接続できるか確認しておく。

-
+
$ ssh -T github.com

パッケージの更新

-
+
$ sudo apt update
 $ sudo apt upgrade
 $ sudo apt update
@@ -255,13 +255,13 @@
             

使うソフトウェアのインストール

-
+
$ sudo apt install docker docker-compose git make

メインユーザが Docker を使えるように

-
+
$ sudo adduser ********** docker
@@ -270,7 +270,7 @@

80 番と 443 番を空ける。

-
+
$ sudo ufw allow 80/tcp
 $ sudo ufw allow 443/tcp
 $ sudo ufw reload
@@ -279,7 +279,7 @@
             
             

リポジトリのクローン

-
+
$ cd
 $ git clone git@github.com:nsfisis/nsfisis.dev.git
 $ cd nsfisis.dev
@@ -288,14 +288,14 @@
             

certbot で証明書取得

-
+
$ docker-compose up -d acme-challenge
 $ make setup

サーバを稼動させる

-
+
$ make serve
diff --git a/vhosts/blog/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html b/vhosts/blog/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html index f91cef9e..01806f5b 100644 --- a/vhosts/blog/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html +++ b/vhosts/blog/public/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2/index.html @@ -83,7 +83,7 @@

注意: これはボツ問なので、得られたトークンを PHPerKaigi で入力してもポイントにはならない。

-
+
<?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s='​<?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
 <?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s='​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​<?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
 <?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s='​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​<?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
@@ -104,7 +104,7 @@
             

実行してみると、次のような出力が得られる。

-
+
#
 <?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s='​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​<?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
 <?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s='​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​<?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
@@ -119,7 +119,7 @@
             

1 行目を除き、先ほどのコードとほぼ同じものが出てきた。もう一度実行してみる。

-
+
#
 W
 <?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s='​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​<?php printf((isset($s)?fn($s)=>trim($s,"​"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>
@@ -134,7 +134,7 @@
             

今度は 2 行目が書き換えられた。すべての行が変化するまで繰り返すと次のようになる。

-
+
#
 W
 E
@@ -158,7 +158,7 @@
             

Vim で開くと次のようになる (1 行目を抜粋)。

-
+
<?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s='<200b><?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?>

@@ -180,13 +180,13 @@

続いて、トークンへの変換ロジックを解析する。注目すべきはこの部分だ。以下、ゼロ幅スペースは Vim での表示に合わせて <200b> と記載する。

-
+
fn($s)=>chr(strlen($s)/3)

PHP の strlen() は文字列のバイト数を返す。1 行目の $s は以下の内容となっており、

-
+
$s='<200b><?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>'

diff --git a/vhosts/blog/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html b/vhosts/blog/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html index 2ea331c1..62a66b04 100644 --- a/vhosts/blog/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html +++ b/vhosts/blog/public/posts/2023-01-10/phperkaigi-2023-unused-token-quiz-3/index.html @@ -88,7 +88,7 @@

注意: これはボツ問なので、得られたトークンを PHPerKaigi で入力してもポイントにはならない。

-
+
<?php
 try {
   f(g() / __LINE__);
@@ -233,7 +233,7 @@
               

このうち 1つ目のケースは、 finally 節の中でエラーを投げると PHP 処理系が勝手に $previous を設定してくれる。

-
+
<?php
 
 try {
@@ -258,7 +258,7 @@
               

出力部をコメントや改行を追加して再掲する:

-
+
<?php
 try {
   f(g() / __LINE__);
@@ -275,7 +275,7 @@
               

フォーマット指定子 %c は、整数を ASCII コード と見做して印字する。トークン #base64_decode('SGVsbG8sIFdvcmxkIQ==')b であれば、ASCII コード 98 なので、75 行目で発生したエラー、

-
+
1, 20 => 0 / 0,

@@ -290,7 +290,7 @@

f() の定義を再掲する (エラーオブジェクトの行数を利用しているので、一部分だけ抜き出すと値が変わることに注意):

-
+
function f(int $i) {
   if ($i < 0) f();
   try {
@@ -315,12 +315,12 @@
               

前述のように、 finally 節でエラーを投げると PHP 処理系が $previous を設定する。ここでは、エラーを繋げるために f() を再帰呼び出ししている。最初に f() を呼び出している箇所を確認すると、

-
+
<?php
 try {
   f(g() / __LINE__); // 3 行目
-
+
function g() {
   return __LINE__; // 111 行目
 }
diff --git a/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html b/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html index f8f850da..30ee2471 100644 --- a/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html +++ b/vhosts/blog/public/posts/2023-04-01/implementation-of-minimal-png-image-encoder/index.html @@ -88,7 +88,7 @@

以下のソースコードをベースにする。 今回 PNG のデコーダは扱わないので、読み込みには Go の標準ライブラリ image/png を用いる。

-
+
package main
 
 import (
@@ -168,7 +168,7 @@
               

writeSignature の実装はこちら:

-
+
import "encoding/binary"
 
 func writeSignature(w io.Writer) {
@@ -211,7 +211,7 @@
               

CRC (Cyclic Redundancy Check) は誤り検出符号の一種。Go 言語では hash/crc32 パッケージにあるが、今回はこれも自前で実装する。PNG の仕様書に C 言語のサンプルコードが載っている ( D. Sample CRC implementation ) ので、これを Go に移植する。

-
+
var (
 	crcTable         [256]uint32
 	crcTableComputed bool
@@ -251,7 +251,7 @@
               

できた crc 関数を使って、chunk 一般を書き込む関数も用意しておこう。

-
+
func writeChunk(w io.Writer, chunkType string, data []byte) {
 	typeAndData := make([]byte, 0, len(chunkType)+len(data))
 	typeAndData = append(typeAndData, []byte(chunkType)...)
@@ -347,7 +347,7 @@
               

今回ほとんどのデータは決め打ちするので、データに応じて変わるのは width と height だけになる。コードは次のようになる。

-
+
import "bytes"
 
 func writeChunkIhdr(w io.Writer, width, height uint32) {
@@ -394,7 +394,7 @@
                 

Adler-32 も CRC と同じく誤り検出符号である。こちらも zlib の仕様書に C 言語でサンプルコードが記載されている ( 9. Appendix: Sample code ) ので、Go に移植する。

-
+
const adler32Base = 65521
 
 func updateAdler32(adler uint32, buf []byte) uint32 {
@@ -435,7 +435,7 @@
                 

実際にこの手抜き zlib を実装したものがこちら:

-
+
func encodeZlib(data []byte) []byte {
 	var buf bytes.Buffer
 
@@ -473,7 +473,7 @@
                 

先ほどの encodeZlib も使って実際に実装したものがこちら:

-
+
func writeChunkIdat(w io.Writer, width, height uint32, img image.Image) {
 	var pixels bytes.Buffer
 	for y := uint32(0); y < height; y++ {
@@ -499,7 +499,7 @@
               

特に追加のデータはなく、必要なのは chunk type の IEND くらいなので実装は簡単:

-
+
func writeChunkIend(w io.Writer) {
 	writeChunk(w, "IEND", nil)
 }
@@ -511,7 +511,7 @@

最後に全ソースコードを再掲しておく。

-
+
package main
 
 import (
diff --git a/vhosts/blog/public/posts/2023-10-02/compile-php-runtime-to-wasm/index.html b/vhosts/blog/public/posts/2023-10-02/compile-php-runtime-to-wasm/index.html
index c00e82d0..0f4f9b88 100644
--- a/vhosts/blog/public/posts/2023-10-02/compile-php-runtime-to-wasm/index.html
+++ b/vhosts/blog/public/posts/2023-10-02/compile-php-runtime-to-wasm/index.html
@@ -103,7 +103,7 @@
             

先にこの記事のゴールを示しておく。これから示す手順のとおりに進めると、次のようなコードが動くようになる。 このコードはこのあと使うので、index.mjs の名前で保存しておくこと。

-
+
import { readFile } from 'node:fs/promises';
 import PHPWasm from './php-wasm.mjs'
 
@@ -128,7 +128,7 @@
               

先ほどのコードでも使っていたエントリポイントである php_wasm_run を用意する。

-
+
#include <stdio.h>
 #include <emscripten.h>
 #include <Zend/zend_execute.h>
@@ -177,7 +177,7 @@
                   

デフォルトの出力方法は index.mjs の中で PHPWasm() を呼ぶとき、stdoutstderr というオプションを渡せば変更できる。

-
+
const { ccall } = await PHPWasm({
   stdout: (c) => {
     if (c === null) {
@@ -205,13 +205,13 @@
               

まずは Emscripten 公式が提供している Docker イメージ を使って、PHP 処理系と先ほど示した C 言語のソースコードを WebAssembly にコンパイルする。

-
+
FROM emscripten/emsdk:3.1.46 AS wasm-builder

次に、 php/php-src から PHP 処理系のソースコードを取得し、ビルドに必要な apt パッケージを取ってくる。 有効にする拡張を増やしたいなら、ここでインストールするパッケージも増やすことになるだろう。

-
+
RUN git clone --depth=1 --branch=php-8.2.10 https://github.com/php/php-src
 
 RUN apt-get update && \
@@ -226,7 +226,7 @@
               

続けて、Emscripten のツールチェインを用いて PHP 処理系をビルドする。

-
+
RUN cd php-src && \
     ./buildconf --force && \
     emconfigure ./configure \
@@ -271,7 +271,7 @@
               

さて、PHP 処理系をライブラリ化できたので、次に先ほど載せた C のソースコードをビルドしていこう。 Dockerfile と同じ場所に php-wasm.c という名前で保存し、次のようにする。

-
+
COPY php-wasm.c /src/
 
 RUN cd php-src && \
@@ -295,7 +295,7 @@
               

libphp.aphp-wasm.o が手に入ったので、これらをリンクして WebAssembly のバイナリとそのラッパである JavaScript ファイルを生成する。 これにも emcc コマンドを使う。

-
+
RUN emcc \
     -s ENVIRONMENT=node \
     -s ERROR_ON_UNDEFINED_SYMBOLS=0 \
@@ -339,7 +339,7 @@
               

といっても、Node.js はビルトインで WebAssembly をサポートしているので、ほとんどやることはない。 先ほど掲載した JavaScript のコードは、Dockerfile と同じディレクトリに index.mjs で配置すること。

-
+
FROM node:20.7
 
 WORKDIR /app
@@ -356,7 +356,7 @@
             

Dockerfilephp-wasm.cindex.mjs を用意したら、Docker コンテナをビルドして実行する。

-
+
$ docker build -t php-wasm .
 $ echo 'echo "Hello, World!", PHP_EOL;' | docker run --rm -i php-wasm
 Hello, World!
diff --git a/vhosts/blog/public/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html b/vhosts/blog/public/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html
index 27eede1c..4a52d698 100644
--- a/vhosts/blog/public/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html
+++ b/vhosts/blog/public/posts/2024-01-10/neovim-insert-namespace-declaration-to-empty-php-file/index.html
@@ -75,7 +75,7 @@
             

Neovim で空の PHP ファイルを開いたとき、そのファイルが置かれているディレクトリの構造に基づいて、自動的に namespace 宣言を挿入したい。具体的には、トップレベルの名前空間が MyNamespace であり、ファイル src/Foo/Bar/Baz.php を開いたときに、そのファイルが空であるなら、次のようなテンプレートが自動的に挿入されてほしい。

-
+
<?php
 
 namespace MyNamespace\Foo\Bar;
@@ -110,7 +110,7 @@

ファイルタイプは読み込んだあとに変更されることもあるので、ftplugin は複数回実行されうる。 二重読み込みを防ぐために、did_ftplugin_<FILE_TYPE>_after というバッファローカル変数を定義しておくのが慣習となっている。

-
+
if vim.b.did_ftplugin_php_after then
    return
 end
@@ -148,7 +148,7 @@
             

実装を簡単にするため、Composer を用いない場合や PSR 4 以外のオートロード規則を使う場合には対応しない。少々長くなるが、以下にスクリプト全文を載せる。

-
+
if vim.b.did_ftplugin_php_after then
    return
 end
diff --git a/vhosts/blog/public/posts/2024-02-03/install-wireguard-on-personal-server/index.html b/vhosts/blog/public/posts/2024-02-03/install-wireguard-on-personal-server/index.html
index f80ee38a..8b427ccf 100644
--- a/vhosts/blog/public/posts/2024-02-03/install-wireguard-on-personal-server/index.html
+++ b/vhosts/blog/public/posts/2024-02-03/install-wireguard-on-personal-server/index.html
@@ -107,7 +107,7 @@
             

公式サイトから各 OS 向けのクライアントソフトウェアを入手し、インストールする。次に、設定をおこなう。

-
+
# クライアント 1 の場合
 [Interface]
 Address = 10.10.1.2/32
@@ -118,7 +118,7 @@
 AllowedIPs = <サーバの外部 IP アドレス>/32
 Endpoint = <サーバの外部 IP アドレス>:51820
-
+
# クライアント 2 の場合
 [Interface]
 Address = 10.10.1.3/32
@@ -141,7 +141,7 @@
             
$ sudo vim /etc/wireguard/wg0.conf
-
+
[Interface]
 Address = 10.10.1.1/32
 SaveConfig = true
diff --git a/vhosts/blog/public/posts/2024-04-21/pipefail-option-in-gitlab-ci-cd/index.html b/vhosts/blog/public/posts/2024-04-21/pipefail-option-in-gitlab-ci-cd/index.html
index 8a8897f5..db6ea870 100644
--- a/vhosts/blog/public/posts/2024-04-21/pipefail-option-in-gitlab-ci-cd/index.html
+++ b/vhosts/blog/public/posts/2024-04-21/pipefail-option-in-gitlab-ci-cd/index.html
@@ -86,7 +86,7 @@
               

例:

-
+
hello-world:
   stage: test
   image: alpine:latest
@@ -102,7 +102,7 @@
               

では、次のようなケースだとどうなるか。

-
+
hello-world:
   stage: test
   image: alpine:latest
@@ -121,7 +121,7 @@
               

前述したようなケースにおいて、途中で失敗したときに全体を失敗させるには、pipefail オプションを有効にする。

-
+
# On にする
 set -o pipefail
 # Off にする
@@ -137,7 +137,7 @@
             

次のような GitLab CI/CD ジョブが失敗してしまった。

-
+
hoge:
   stage: test
   image: alpine:latest
@@ -181,7 +181,7 @@
             

.gitlab-ci.yml で明示的には書いていないので、GitLab Runner (GitLab CI/CD のスクリプトを実行するプログラム) が勝手に追加しているに違いない。 そう仮説を立てて GitLab Runner のリポジトリ を調査したところ、 ソースコード中の以下の箇所set -o pipefail していることが判明した (コメントは筆者による)。

-
+
// pipefail オプションが存在しない環境にも対応するため、
 // 先に set -o でオプション一覧を表示させたあと、set -o pipefail している
 buf.WriteString("if set -o | grep pipefail > /dev/null; then set -o pipefail; fi; set -o errexit\n")
@@ -192,7 +192,7 @@

通常の Bash スクリプトを書く場合と同様に、pipefail が on になっていては困る場所だけ off にしてやればよい。

-
+
 hoge:
    stage: test
    image: alpine:latest
diff --git a/vhosts/blog/public/posts/2024-04-29/zsh-file-completion-for-composer-custom-commands/index.html b/vhosts/blog/public/posts/2024-04-29/zsh-file-completion-for-composer-custom-commands/index.html
index 153716c9..b1e43381 100644
--- a/vhosts/blog/public/posts/2024-04-29/zsh-file-completion-for-composer-custom-commands/index.html
+++ b/vhosts/blog/public/posts/2024-04-29/zsh-file-completion-for-composer-custom-commands/index.html
@@ -91,7 +91,7 @@
             

このことは、先ほどリンクを載せた _composer 関数を定義しているファイルの冒頭にも書かれている。

-
+
# - @todo We don't complete custom commands (including script aliases). This is
 #   easy to do in the general case, but it probably requires some clever caching
 #   to avoid introducing a noticeable lag to every completion operation, due to
@@ -113,7 +113,7 @@
             

まずは、Zsh で補完関数を提供する場合のボイラープレートコードを書く。 以下は ~/.zshrc にすべて書く前提だが、autoload を設定するなどすれば別ファイルに分離できる (詳細な手順は割愛)。

-
+
compdef _my_composer composer composer.phar

@@ -122,7 +122,7 @@

次に _my_composer を定義する。基本的にはデフォルトの composer コマンドの補完関数 (つまり _composer 関数) を使い、それが何も返さなかった場合に限り、Zsh のファイル・ディレクトリ補完へフォールバックする。

-
+
function _my_composer() {
     _composer "$@" || _files "$@"
 }
diff --git a/vhosts/blog/public/posts/2024-07-19/reparojson-fix-only-json-formatter/index.html b/vhosts/blog/public/posts/2024-07-19/reparojson-fix-only-json-formatter/index.html index 88518883..6882fc3b 100644 --- a/vhosts/blog/public/posts/2024-07-19/reparojson-fix-only-json-formatter/index.html +++ b/vhosts/blog/public/posts/2024-07-19/reparojson-fix-only-json-formatter/index.html @@ -129,7 +129,7 @@

ここでは、 nvim-lspconfigefm-langserver を用いた設定例を紹介する。

-
+
   local lspconfig = require('lspconfig')
 
    lspconfig.efm.setup({
@@ -167,7 +167,7 @@
             

このツールが威力を発揮するのは、行の入れ換え時である。次のような JSON があり、

-
+
   {
       "a": true,
       "b": false
@@ -176,7 +176,7 @@
             

2行目と3行目を入れ換えて以下のように編集した。

-
+
   {
       "b": false
       "a": true,
@@ -185,7 +185,7 @@
             

これは不正な JSON だが、このツールを通せば次のようになる。

-
+
   {
       "b": false,
       "a": true
diff --git a/vhosts/blog/public/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range/index.html b/vhosts/blog/public/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range/index.html
index 82f0e1bb..93c46c85 100644
--- a/vhosts/blog/public/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range/index.html
+++ b/vhosts/blog/public/posts/2024-08-19/go-template-access-outer-scope-pipeline-within-with-or-range/index.html
@@ -92,7 +92,7 @@
             

つまりこのテンプレートは、次のような構造をレンダリングしている (Execute() の第2引数)。

-
+
tmpl.Execute(out, Params{
     Title: "foo",
     User: User{
diff --git a/vhosts/blog/public/posts/2024-12-04/cohackpp-report/index.html b/vhosts/blog/public/posts/2024-12-04/cohackpp-report/index.html
index 48fe9dae..1e62ee11 100644
--- a/vhosts/blog/public/posts/2024-12-04/cohackpp-report/index.html
+++ b/vhosts/blog/public/posts/2024-12-04/cohackpp-report/index.html
@@ -131,7 +131,7 @@
             

https://github.com/nsfisis/cohackpp/blob/main/congrats.php

-
+
<?php
 $s=<<<'Q'
 <?php
diff --git a/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html b/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html
index a4da14ea..7835ad25 100644
--- a/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html
+++ b/vhosts/blog/public/posts/2025-01-08/phperkaigi-2023-tokens-q1/index.html
@@ -257,7 +257,7 @@
               

画像の正体がわかったところで、画像に隠されていた PHP プログラムについて見ていこう。 先ほどは一部しか記載しなかったので、全体を載せる。 なお、ある程度ゴルフしながら書いたので、空白こそ残しているものの可読性は非常に低いことと思う。

-
+
<?php
 error_reporting(-1);
 $b = unpack('C*', file_get_contents(__FILE__));
@@ -366,7 +366,7 @@
               

プログラムの冒頭にあるこの箇所

-
+
$b = unpack('C*', file_get_contents(__FILE__));

@@ -426,7 +426,7 @@

ところで、先ほど掲載した Piet のインタプリタのソースコード末尾には次のような箇所がある。

-
+
// The original Piet image is wrong: it outputs 403 error for invalid passwords.
 // Failure of authentication should be notified by 401, not 403.
 // I noticed that one month before PHPerKaigi, but I could not read or write (paint)
diff --git a/vhosts/blog/public/posts/2025-01-26/yaml-breaking-changes-between-v1-1-and-v1-2/index.html b/vhosts/blog/public/posts/2025-01-26/yaml-breaking-changes-between-v1-1-and-v1-2/index.html
index 667ef8c5..f927c26e 100644
--- a/vhosts/blog/public/posts/2025-01-26/yaml-breaking-changes-between-v1-1-and-v1-2/index.html
+++ b/vhosts/blog/public/posts/2025-01-26/yaml-breaking-changes-between-v1-1-and-v1-2/index.html
@@ -98,7 +98,7 @@
               

YAML 1.1 では、<< という文字列をキーに指定することで、マップをマージすることができた。

-
+
x: &base
   a: 123
 # => { "x": { "a": 123 } }
diff --git a/vhosts/blog/public/posts/2025-04-20/trick-2025-most-ruby-on-ruby-award/index.html b/vhosts/blog/public/posts/2025-04-20/trick-2025-most-ruby-on-ruby-award/index.html
index 1cbf5be2..7c7dda1e 100644
--- a/vhosts/blog/public/posts/2025-04-20/trick-2025-most-ruby-on-ruby-award/index.html
+++ b/vhosts/blog/public/posts/2025-04-20/trick-2025-most-ruby-on-ruby-award/index.html
@@ -132,7 +132,7 @@
             

表示している。つまり、Ruby プログラムにルビを振った作品である。例えば、先頭の2行目の require は次のような HTML で構成されている。

-
+
<ruby class="IDENTIFIER">require<rp class="">(</rp><rt class="">リクワイア</rt><rp class="">)</rp></ruby>

@@ -143,7 +143,7 @@

改めて quine について説明する。Quine とは、自身のソースコードを出力するようなプログラムである。Ruby では様々な方法で quine を書くことができるが、この作品で使っている基本形は以下のようなものである。

-
+
eval $s=<<'EOS'
 print "eval $s=<<'EOS'\n"
 print $s
@@ -162,7 +162,7 @@
               

トークナイズには Ruby 3.4 からデフォルトのパーサになった Prism を利用している。 Prism.lex() を使うとトークナイズができるので、トークンに付いているソースコード位置の情報を使いつつ元のソースコードを復元する。

-
+
y = 1                 # 現在の行
 x = 0                 # 現在の列
 Prism.lex($s).value[..-2].each {|t, *|
@@ -187,7 +187,7 @@
               

トークン種別に応じた色付けは CSS でおこなっている。出力する HTML のクラス名に Prism::Token#type を指定しておいて、index.html でそれぞれのクラスにスタイルを当てた。

-
+
    <style>
       /* ... */
 
@@ -212,7 +212,7 @@
               

それぞれの英単語や記号に対応した振り仮名のデータは、プログラム中に埋め込まれている。

-
+
def rt(t)
   r = {
     :"&&" => "1136",
@@ -241,7 +241,7 @@
               

このテーブルはサイズ制限を突破するために圧縮されており、kana() 関数で展開される。

-
+
def kana(s)
   s
     &.scan(/.{2}/)
@@ -252,7 +252,7 @@
               

例えば value に対応する振り仮名データ "48746992" であれば、次のような変換を経て振り仮名へと展開される。

-
+
  s
     # => "48746992"
     &.scan(/.{2}/)
-- 
cgit v1.2.3-70-g09d2