summaryrefslogtreecommitdiffhomepage
path: root/slides.typ
diff options
context:
space:
mode:
Diffstat (limited to 'slides.typ')
-rw-r--r--slides.typ556
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/