diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-03-22 06:46:16 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-03-22 09:52:29 +0900 |
| commit | b73729e8d965a8962b3ef5103d37a6456b53a265 (patch) | |
| tree | a1fe2751faa0e81a0267b7f6783de6032e47092e /slides.typ | |
| parent | b00d51802e6a5bd5dc46d9ab64ce6376d7f0121b (diff) | |
| download | phperkaigi-2026-php-modification-slides-b73729e8d965a8962b3ef5103d37a6456b53a265.tar.gz phperkaigi-2026-php-modification-slides-b73729e8d965a8962b3ef5103d37a6456b53a265.tar.zst phperkaigi-2026-php-modification-slides-b73729e8d965a8962b3ef5103d37a6456b53a265.zip | |
Diffstat (limited to 'slides.typ')
| -rw-r--r-- | slides.typ | 556 |
1 files changed, 556 insertions, 0 deletions
diff --git a/slides.typ b/slides.typ new file mode 100644 index 0000000..e730c31 --- /dev/null +++ b/slides.typ @@ -0,0 +1,556 @@ +#import "@preview/touying:0.6.3": * +#import "@preview/codly:1.3.0": * +#import "@preview/cjk-unbreak:0.2.0": remove-cjk-break-space, transform-childs +#import "@preview/fletcher:0.5.8" as fletcher: diagram, node, edge +#import "setoka.typ": * + +#show: codly-init.with() + +#show: remove-cjk-break-space + +#show: setoka-theme.with( + aspect-ratio: "16-9", + config-info( + title: [ + PHP を魔改造して学ぶ \ + 言語処理系 + ], + subtitle: [PHPerKaigi 2026], + author: [nsfisis (いまむら)], + date: datetime(year: 2026, month: 3, day: 22), + ), + config-common(preamble: { + codly( + fill: rgb("#eee"), + lang-format: none, + number-format: none, + zebra-fill: none, + ) + }) +) + +#set text(font: "BIZ UDPGothic", lang: "ja") +#show raw: set text(font: "UDEV Gothic 35") + +#let process-color = rgb("#ffa500").lighten(60%) +#let data-color = rgb("#e0e0e0") + +#let overview-diagram() = { + set text(size: 18pt) + diagram( + node-stroke: 0.5pt, + spacing: (6mm, 12mm), + node((0, 0), [ソースコード], fill: data-color, corner-radius: 2pt), + edge("-"), + node((1, 0), [字句解析], fill: process-color, corner-radius: 2pt), + edge("-|>"), + node((2, 0), [トークン列], fill: data-color, corner-radius: 2pt), + edge("-"), + node((3, 0), [構文解析], fill: process-color, corner-radius: 2pt), + edge((3, 0), (3, 0.5), (-0.75, 0.5), (-0.75, 1), (0, 1), "-|>"), + node((0, 1), [AST], fill: data-color, corner-radius: 2pt), + edge("-"), + node((1, 1), [コンパイル], fill: process-color, corner-radius: 2pt), + edge("-|>"), + node((2, 1), [opcode], fill: data-color, corner-radius: 2pt), + edge("-"), + node((3, 1), [実行], fill: process-color, corner-radius: 2pt), + edge("-|>"), + node((4, 1), [結果], fill: data-color, corner-radius: 2pt), + ) +} + +#title-slide() + +#about-slide() + +#[ + #set align(center + horizon) + #set text(size: 40pt) + PHP のソースコードを魔改造して\ + 独自の演算子を追加する\ +\ + #pause + PHP 処理系の内部実装を理解する +] + +--- + +#[ + #set align(center + horizon) + + 追加する演算子 +] + +--- + +#[ + #set text(size: 30pt) + + 関数合成演算子「`∘`」 + + #codly-range(2) + ```php + <?php + $h = $f ∘ $g; + $h($x); // → $f($g($x)) + ``` + + $(f ∘ g)(x) = f(g(x))$ +] + +--- + +#[ + #set text(size: 22pt) + + #codly-range(2) + ```php + <?php + $double = fn($x) => $x * 2; + $add_one = fn($x) => $x + 1; + + $f = $add_one ∘ $double; + echo $f(3); // → $add_one($double(3)) → 7 + ``` +] + +--- + +#[ + #set text(size: 22pt) + + #codly-range(2) + ```php + <?php + $double = fn($x) => $x * 2; + $add_one = fn($x) => $x + 1; + $negate = fn($x) => -$x; + + $g = $negate ∘ $add_one ∘ $double; + echo $g(3); + // → $negate($add_one($double(3))) + // → -7 + ``` +] + +--- + +#[ + #set align(center + horizon) + 実行の流れ + + #overview-diagram() +] + +--- + +#[ + #set align(center + horizon) + + 字句解析 + #set text(size: 24pt) + + ソースコードの文字列を\ + トークンの列に分割する処理 + + #set text(size: 18pt) + + #diagram( + node-stroke: 0.5pt, + spacing: (5mm, 10mm), + node-shape: rect, + node((3.5, 0), [`$g = $negate ∘ $add_one ∘ $double;`], fill: data-color, corner-radius: 2pt), + edge((3.5, 0), (3.5, 1), "-|>"), + ) + + #diagram( + node-stroke: 0.5pt, + spacing: (5mm, 10mm), + node-shape: rect, + node((0, 0), [`$g`], fill: process-color, corner-radius: 2pt), + node((1, 0), [`=`], fill: process-color, corner-radius: 2pt), + node((2, 0), [`$negate`], fill: process-color, corner-radius: 2pt), + node((3, 0), [`∘`], fill: process-color, corner-radius: 2pt), + node((4, 0), [`$add_one`], fill: process-color, corner-radius: 2pt), + node((5, 0), [`∘`], fill: process-color, corner-radius: 2pt), + node((6, 0), [`$double`], fill: process-color, corner-radius: 2pt), + node((7, 0), [`;`], fill: process-color, corner-radius: 2pt), + ) +] + +--- + +#[ + #set align(center + horizon) + + re2c + + #set text(size: 36pt) + 正規表現からコードを生成 +] + +--- + +#[ + #set text(size: 24pt) + + `zend_language_scanner.l` + + ```re2c + <ST_IN_SCRIPTING>"\xe2\x88\x98" { + RETURN_TOKEN(T_COMPOSE); + } + ``` + + - `\xe2\x88\x98` は `∘` (U+2218) の UTF-8 バイト列 + - `T_COMPOSE` を返す + - `re2c` が `zend_language_scanner.c` を生成 +] + +--- + +#[ + #set align(center + horizon) + 実行の流れ + + #overview-diagram() +] + +--- + +#[ + #set align(center + horizon) + + 構文解析 + #set text(size: 24pt) + + トークン列を\ + 抽象構文木 (AST) に変換する処理 +] + +--- + +#[ + #set align(center + horizon) + #set text(size: 18pt) + + #diagram( + node-stroke: 0.5pt, + spacing: (5mm, 10mm), + node-shape: rect, + node((0, 0), [`$g`], fill: data-color, corner-radius: 2pt), + node((1, 0), [`=`], fill: data-color, corner-radius: 2pt), + node((2, 0), [`$negate`], fill: data-color, corner-radius: 2pt), + node((3, 0), [`∘`], fill: data-color, corner-radius: 2pt), + node((4, 0), [`$add_one`], fill: data-color, corner-radius: 2pt), + node((5, 0), [`∘`], fill: data-color, corner-radius: 2pt), + node((6, 0), [`$double`], fill: data-color, corner-radius: 2pt), + node((7, 0), [`;`], fill: data-color, corner-radius: 2pt), + edge((3.5, 0), (3.5, 1), "-|>"), + ) + + #diagram( + node-stroke: 0.5pt, + spacing: (8mm, 8mm), + node-shape: rect, + + node((2, 0), [`ZEND_AST_ASSIGN`], fill: process-color, corner-radius: 2pt), + edge((2, 0), (0.5, 1), "-|>"), + edge((2, 0), (3.5, 1), "-|>"), + node((0.5, 1), [`$g`], fill: process-color, corner-radius: 2pt), + + node((3.5, 1), [`ZEND_AST_COMPOSE`], fill: process-color, corner-radius: 2pt), + edge((3.5, 1), (2, 2), "-|>"), + edge((3.5, 1), (5, 2), "-|>"), + node((2, 2), [`$negate`], fill: process-color, corner-radius: 2pt), + + node((5, 2), [`ZEND_AST_COMPOSE`], fill: process-color, corner-radius: 2pt), + edge((5, 2), (4, 3), "-|>"), + edge((5, 2), (6, 3), "-|>"), + node((4, 3), [`$add_one`], fill: process-color, corner-radius: 2pt), + node((6, 3), [`$double`], fill: process-color, corner-radius: 2pt), + ) +] + +--- + +#[ + #set align(center + horizon) + + bison + + #set text(size: 36pt) + 文法規則からコードを生成 +] + +--- + +#[ + #set text(size: 20pt) + + `zend_language_parser.y` + + ```c + /* 優先順位の設定 */ + %left T_PIPE + %right T_COMPOSE /* 追加 */ + %left '.' + ``` + + - `|>` より強い + - `$f ∘ $g |> $h` → `($f ∘ $g) |> $h` + - 右結合 + - `$f ∘ $g ∘ $h` → `$f ∘ ($g ∘ $h)` +] + +--- + +#[ + #set text(size: 20pt) + + `zend_language_parser.y` + + ```c + /* 文法定義 */ + expr : expr T_COMPOSE expr + { + /* f ∘ g の並びを見つけたら AST を構築 */ + $$ = zend_ast_create(ZEND_AST_COMPOSE, $1, $3); + } + ``` +] + +--- + +#[ + #set align(center + horizon) + 実行の流れ + + #overview-diagram() +] + +--- + +#[ + #set align(center + horizon) + + コンパイル + #set text(size: 24pt) + + AST を PHP VM の\ + opcode列 (命令列) に変換する処理 +] + +--- + +#[ + #set align(center + horizon) + #set text(size: 16pt) + + #diagram( + node-stroke: 0.5pt, + spacing: (8mm, 8mm), + node-shape: rect, + + node((2, 0), [`ZEND_AST_ASSIGN`], fill: data-color, corner-radius: 2pt), + edge((2, 0), (0.5, 1), "-|>"), + edge((2, 0), (3.5, 1), "-|>"), + node((0.5, 1), [`$g`], fill: data-color, corner-radius: 2pt), + + node((3.5, 1), [`ZEND_AST_COMPOSE`], fill: data-color, corner-radius: 2pt), + edge((3.5, 1), (2, 2), "-|>"), + edge((3.5, 1), (5, 2), "-|>"), + node((2, 2), [`$negate`], fill: data-color, corner-radius: 2pt), + + node((5, 2), [`ZEND_AST_COMPOSE`], fill: data-color, corner-radius: 2pt), + edge((5, 2), (4, 3), "-|>"), + edge((5, 2), (6, 3), "-|>"), + node((4, 3), [`$add_one`], fill: data-color, corner-radius: 2pt), + node((6, 3), [`$double`], fill: data-color, corner-radius: 2pt), + + edge((4, 3), (4, 4), "-|>"), + ) + + #diagram( + node-stroke: 0.5pt, + spacing: (5mm, 5mm), + node-shape: rect, + node((0, 0), [`ZEND_COMPOSE` \ `$add_one, $double → $0`], fill: process-color, corner-radius: 2pt), + node((1, 0), [`ZEND_COMPOSE` \ `$negate, $0 → $1`], fill: process-color, corner-radius: 2pt), + node((2, 0), [`ZEND_ASSIGN` \ `$g, $1`], fill: process-color, corner-radius: 2pt), + ) +] + +--- + +#[ + #set text(size: 18pt) + + `zend_compile.c` + + ```c + static void zend_compile_compose(znode *result, zend_ast *ast) { + zend_ast *lhs_ast = ast->child[0]; + zend_ast *rhs_ast = ast->child[1]; + znode lhs_result, rhs_result; + zend_compile_expr(&lhs_result, lhs_ast); // 左辺をコンパイル + zend_compile_expr(&rhs_result, rhs_ast); // 右辺をコンパイル + // ZEND_COMPOSE opcode を出力 + // 実際の関数合成は実行時に + zend_emit_op_tmp(result, ZEND_COMPOSE, &lhs_result, &rhs_result); + } + ``` +] + +--- + +#[ + #set align(center + horizon) + 実行の流れ + + #overview-diagram() +] + +--- + +#[ + #set align(center + horizon) + + `zend_vm_gen.php` + + #set text(size: 36pt) + テンプレートから VM コードを生成 + + #set text(size: 18pt) + + #diagram( + node-stroke: 0.5pt, + spacing: (10mm, 5mm), + node-shape: rect, + node((0, 1.5), [`zend_vm_def.h`], fill: data-color, corner-radius: 2pt), + edge((0, 1.5), (1, 1.5), "-|>"), + node((1, 1.5), [`zend_vm_gen.php`], fill: process-color, corner-radius: 2pt, name: <zend_vm_gen_php>), + node((2, 0), [`zend_vm_execute.h`], fill: data-color, corner-radius: 2pt, name: <zend_vm_execute_h>), + node((2, 1), [`zend_vm_opcodes.h`], fill: data-color, corner-radius: 2pt, name: <zend_vm_opcodes_h>), + node((2, 2), [`zend_vm_opcodes.c`], fill: data-color, corner-radius: 2pt, name: <zend_vm_opcodes_c>), + node((2, 3), [`zend_vm_handlers.h`], fill: data-color, corner-radius: 2pt, name: <zend_vm_handlers_h>), + edge(<zend_vm_gen_php.east>, <zend_vm_execute_h.west>, "-|>"), + edge(<zend_vm_gen_php.east>, <zend_vm_opcodes_h.west>, "-|>"), + edge(<zend_vm_gen_php.east>, <zend_vm_opcodes_c.west>, "-|>"), + edge(<zend_vm_gen_php.east>, <zend_vm_handlers_h.west>, "-|>"), + ) +] + +--- + +#[ + #set text(size: 18pt) + + `zend_vm_def.h` + + ```c + ZEND_VM_HANDLER( + 211, // opcode の番号 + ZEND_COMPOSE, // opcode の名前 + CONST|TMPVAR|CV, // operand 1 の種類 + CONST|TMPVAR|CV) // operand 2 の種類 + { + /* (略) */ + zend_compose_callables(EX_VAR(opline->result.var), op1, op2); + /* (略) */ + } + ``` +] + +--- + +#[ + #set text(size: 18pt) + + ```c + ZEND_API void zend_compose_callables(zval *result, zval *lhs, zval *rhs) + { + // (略; いい感じに合成後のクロージャのメタデータを初期化) + // (略; 左辺と右辺の情報をクロージャの $this に埋め込む) + // (略; いい感じに結果データを生成) + } + ``` +] + +--- + +#[ + #set align(center + horizon) + 実行の流れ + + #overview-diagram() +] + +--- + +#[ + #set align(center + horizon) + #set text(size: 32pt) + + PHPerKaigi 2025 + + PHPで作るPHP\ + ~セルフホストできる\ + 言語処理系を作ろう~ +] + +--- + +#[ + #set align(center + horizon) + #set text(size: 32pt) + + PHPerKaigi 2026 + + PHP を魔改造して学ぶ \ + 言語処理系 +] + +--- + +#[ + #set text(size: 32pt) + + - PHP サブセット実装 + - 複雑なものを単純化して\ + 全体像を理解 + - PHP 魔改造 + - 複雑なものを複雑なまま\ + 部分的に理解 +] + +--- + +#[ + #set align(center + horizon) + + 言語処理系を読もう\ + 言語処理系を書こう +] + +--- + +#[ + #set align(center + horizon) + + ご静聴 \ + ありがとうございました +] + +--- + +#set text(size: 20pt) + +参考文献: + +- https://docs.raku.org/routine/o,%20infix%20%E2%88%98 +- https://docs.julialang.org/en/v1/manual/functions/#Function-composition-and-piping +- https://www.php.net/manual/ja/language.operators.precedence.php +- https://www.npopov.com/2017/04/14/PHP-7-Virtual-machine.html +- https://www.phpinternalsbook.com/ |
