書籍概要

Rustで作るプログラミング言語
——コンパイラ/インタプリタの基礎からプログラミング言語の新潮流まで

著者
発売日
更新日

概要

小さなプログラミング言語から本格的なプログラミング言語へ……ステップバイステップでの開発を通して,プログラミングそのものへの理解を深めよう!

こんな方におすすめ

  • コンピュータサイエンス,とくにプログラミング言語そのものやコンパイラについての素養を得たいと考えている人
  • 他言語でのプログラミング経験があり,Rustの習得に興味のある人。とくに,学習のための題材を探している人

目次

第1章:プログラミング言語概論

  • プログラミング言語の分類
  • 他の切り口での分類方法
  • コンパイルパイプライン

第2章:スタックベース仮想マシン

  • 仮想マシン概論
  • Rustでの実装
  • 標準入力からの読み込み
  • パースとコマンドの実行
  • ブロックとネスト構造
  • if制御構文
  • 変数の定義
  • 複数行のソースコードへの対応
  • 関数呼び出し
  • 関数の再帰呼び出し
  • WebAssemblyへのコンパイルとブラウザでの実行

第3章:プログラミング言語の構文解析

  • 構文解析が解決する課題
  • 構文へのマッチ
  • トークンの切り出し
  • 括弧によるグループ化
  • 木構造の構築
  • 式の構文木
  • パーサコンビネータnom
  • Parsing Expression Grammarによる構文解析
  • ASTインタプリタ
  • 関数呼び出しの構文と評価
  • 設定ファイルへの応用

第4章:スクリプト言語ランタイム

  • 本章で設計する言語
  • 構文と意味論
  • AST実行モデル
  • 文(Statement)の導入
  • 変数宣言
  • 代入文
  • 条件分岐
  • ループ
  • 関数定義
  • その他の制御構文
  • 動的型の導入

第5章:静的型付けと型チェック

  • 静的型システムの概要
  • 型チェックの基本構成法
  • 型宣言
  • 式と文の型チェック
  • 組み込み関数の型定義
  • エラー位置の報告

第6章:バイトコードへのコンパイル

  • バイトコードとネイティブコード
  • スタックマシンとレジスタマシン
  • 命令セットの特徴と設計
  • バイトコードの構成
  • 最小限のバイトコードの定義と実行
  • バイトコードへのコンパイル
  • リテラルテーブルの実装
  • 任意のソースコードをパースしてバイトコードへ翻訳
  • 完全な式のコンパイルと実行
  • 条件分岐式のコンパイルと実行
  • ループ制御のコンパイルと実行
  • ユーザー定義関数
  • 型チェッカーとの統合
  • 状態マシンとコルーチン
  • コルーチンオブジェクトとジェネレータ
  • 応用例

第7章:LLVMを使ったネイティブコードへのコンパイル

  • llvm-sysとinkwell
  • inkwellでのHello, world!
  • ネイティブコードによるパフォーマンスの向上

サポート

正誤表

本書の以下の部分に誤りがありました。ここに訂正するとともに,ご迷惑をおかけしたことを深くお詫び申し上げます。

(2024年12月5日最終更新)

「はじめに」 P.vi:4行目

ほとんど扱いませんません
ほとんど扱いません。

第1章 P.13:図1.4

図01-04_誤.png

図01-04_正.png

第2章 P.48:1行目

深淵な理由」
「深遠な理由」

第2章 P.60:下から3つめのコードブロック

/vec2sqlen { square exch square exch + }
/vec2sqlen { square exch square exch + } def

第3章 P.81: コードブロック

fn number(mut input: &str) -> &str {
   if matches!(
     input.chars().next(),
     Some(_x @ ('-' | '+' | '.' | '0'..='9'))
   ) {
     while matches!(
       input.chars().next(),
       Some(_x @ ('.' | '0'..='9'))
     ) {
       let mut chars = input.chars();
       chars.next();
       input = chars.as_str();
     }
   }
fn number(mut input: &str) -> &str {
   if matches!(
     input.chars().next(),
     Some(_x @ ('-' | '+' | '.' | '0'..='9'))
   ) {
     let mut chars = input.chars();
     chars.next();
     input = chars.as_str();
     while matches!(
       input.chars().next(),
       Some(_x @ ('.' | '0'..='9'))
     ) {
       let mut chars = input.chars();
       chars.next();
       input = chars.as_str();
     }
   }

第3章 P.99:最初のコードブロック1行目

fn add_term(input: &str) -> (&str, Option<Expression>) {
fn add_term(input: &str) -> Option<(&str, Expression)> {

第4章 P.156: 下から2行目

これは0から100までの整数を出力するはずです。
これは0から99までの整数を出力するはずです。

第4章 P.161:2つめのコードブロック1行目

#[derive(Default)]ß
#[derive(Default)]

第4章 P.184:箇条書き

⑦break文が出現した場合は、EvalResult::Break(BreakResult::Continue)を返す
continue文が出現した場合は、EvalResult::Break(BreakResult::Continue)を返す

第5章 P.234:1行目

6行目の16列目で生じている
5行目の16列目で生じている

第6章 P.313: 箇条書き

②ターゲットスタックに引数のリストを追加する。ここでターゲットとしてTarget::Local(arg.to_string())を使9っているのがポイントで、これによって関数内の識別子が引数に拘束されるようになる
②ターゲットスタックに引数のリストを追加する。ここでターゲットとしてTarget::Local(arg.to_string())を使っているのがポイントで、これによって関数内の識別子が引数に拘束されるようになる

商品一覧