├── .github └── FUNDING.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── ch01 ├── README.md └── mul │ ├── Cargo.toml │ └── src │ └── main.rs ├── ch02 ├── README.md └── sort │ ├── Cargo.toml │ └── src │ └── main.rs ├── ch03 ├── README.md └── rwlock │ ├── Cargo.toml │ └── src │ └── main.rs ├── ch04 ├── README.md ├── display │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── iter │ ├── Cargo.toml │ └── src │ │ └── main.rs └── serialize │ ├── Cargo.toml │ ├── src │ └── main.rs │ └── test.yml ├── ch05 ├── README.md ├── markdown │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── mod_ex2 │ ├── Cargo.toml │ └── src │ │ ├── a.rs │ │ ├── a │ │ ├── a_1.rs │ │ └── a_2.rs │ │ ├── b.rs │ │ ├── b │ │ ├── b_1.rs │ │ └── b_2.rs │ │ └── main.rs ├── mod_ex3 │ ├── Cargo.toml │ └── src │ │ ├── a │ │ ├── a_1.rs │ │ ├── a_2.rs │ │ └── mod.rs │ │ ├── b │ │ ├── b_1.rs │ │ ├── b_2.rs │ │ └── mod.rs │ │ └── main.rs └── visibility │ ├── Cargo.toml │ └── src │ └── main.rs ├── ch06 ├── README.md └── regex │ ├── Cargo.toml │ ├── benches │ └── benchmark.rs │ └── src │ ├── engine.rs │ ├── engine │ ├── codegen.rs │ ├── evaluator.rs │ └── parser.rs │ ├── helper.rs │ ├── lib.rs │ └── main.rs ├── ch07 ├── README.md └── zrsh │ ├── Cargo.toml │ └── src │ ├── helper.rs │ ├── main.rs │ └── shell.rs ├── ch08 ├── README.md ├── ackerman │ ├── Cargo.toml │ └── src │ │ ├── ackerman_tailcall.rs │ │ └── main.rs ├── dbg_target │ ├── .cargo │ │ └── config.toml │ ├── Cargo.toml │ └── src │ │ └── main.rs └── zdbg │ ├── Cargo.toml │ └── src │ ├── dbg.rs │ ├── helper.rs │ └── main.rs ├── ch09 ├── README.md ├── linz │ ├── Cargo.toml │ ├── codes │ │ ├── err1.lin │ │ ├── err2.lin │ │ ├── err3.lin │ │ ├── err4.lin │ │ ├── err5.lin │ │ ├── err6.lin │ │ ├── err7.lin │ │ ├── ex1.lin │ │ ├── ex2.lin │ │ ├── ex3.lin │ │ ├── ex4.lin │ │ ├── ex5.lin │ │ ├── ex6.lin │ │ ├── ex7.lin │ │ ├── ex8.lin │ │ └── parse_err.lin │ └── src │ │ ├── helper.rs │ │ ├── main.rs │ │ ├── parser.rs │ │ └── typing.rs └── parser │ ├── Cargo.toml │ └── src │ └── main.rs ├── errata.md └── fig └── cover.jpg /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ytakano] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | **/target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "ch01/mul", 6 | "ch02/sort", 7 | "ch03/rwlock", 8 | "ch04/display", 9 | "ch04/iter", 10 | "ch04/serialize", 11 | "ch05/markdown", 12 | "ch05/mod_ex2", 13 | "ch05/mod_ex3", 14 | "ch05/visibility", 15 | "ch06/regex", 16 | "ch08/ackerman", 17 | "ch08/dbg_target", 18 | "ch08/zdbg", 19 | "ch09/parser", 20 | "ch09/linz", 21 | ] 22 | 23 | exclude = ["ch07/zrsh"] 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Yuuki Takano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ゼロから学ぶRust システムプログラミングの基礎から線形型システム 2 | 3 | [![ゼロから学ぶRust システムプログラミングの基礎から線形型システム](./fig/cover.jpg)](https://amzn.to/3igAgkU) 4 | 5 | 著: 高野祐輝、出版社: 講談社、出版日: 2022年12月15日 6 | 7 | ## 各章のソースコード 8 | 9 | - [第1章 環境構築とHello, world!](./ch01/) 10 | - [第2章 Rustの基本](./ch02/) 11 | - [第3章 所有権・ライフタイム・借用](./ch03/) 12 | - [第4章 トレイト](./ch04/) 13 | - [第5章 モジュール・ドキュメント・テスト](./ch05/) 14 | - [第6章 正規表現](./ch06/) 15 | - [第7章 シェル](./ch07/) 16 | - [第8章 デバッガ](./ch08/) 17 | - [第9章 線形型システム](./ch09/) 18 | 19 | ## 動作可能なOS 20 | 21 | | | Linux | macOS | Windows | 22 | |------|-------|-------|---------| 23 | |第1章 | ✅ | ✅ | ✅ | 24 | |第2章 | ✅ | ✅ | ✅ | 25 | |第3章 | ✅ | ✅ | ✅ | 26 | |第4章 | ✅ | ✅ | ✅ | 27 | |第5章 | ✅ | ✅ | ✅ | 28 | |第6章 | ✅ | ✅ | ✅ | 29 | |第7章 | ✅ | ✅ | | 30 | |第8章 | ✅ | | | 31 | |第9章 | ✅ | ✅ | ✅ | 32 | 33 | 34 | ## 正誤表 35 | 36 | [正誤表](./errata.md)のページを参照ください。 37 | Pull RequestおよびIssueで誤植、誤りを報告いただけます。 38 | 39 | ## ソースコードのライセンス 40 | 41 | ソースコードは、[MITライセンス](./LICENSE)で利用が可能です。 42 | 商用の如何に関わらず再頒布・改変可能ですが、再頒布時に著作権とライセンスの表記が必要です。ソースコード利用に関して、著作権者はいかなる責任も負いません。 43 | -------------------------------------------------------------------------------- /ch01/README.md: -------------------------------------------------------------------------------- 1 | # 第1章 環境構築とHello, world! 2 | 3 | - [1.4 Rustプログラムの概観](./mul/) 4 | -------------------------------------------------------------------------------- /ch01/mul/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mul" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /ch01/mul/src/main.rs: -------------------------------------------------------------------------------- 1 | // mainという関数を定義 2 | fn main() { 3 | let x: i32 = 10; // i32型の変数をxを定義して、10を代入(型指定あり) 4 | let y = 20; // 変数yを定義して20を代入(型指定なし) 5 | let z = mul(x, y); // 関数呼び出し 6 | println!("z = {z}"); // {z}で変数zを表示 7 | } 8 | 9 | // i32型の引数xとyを受け取り、i32型の値を返す関数mulを定義 10 | fn mul(x: i32, y: i32) -> i32 { 11 | // セミコロンが最後にないことに注意 12 | // 関数の最後の値が返り値となる 13 | x * y 14 | } 15 | 16 | // この位置には、以下のように式は書けない 17 | // let x = 10; 18 | // let y = 20; 19 | // let z = mul(x, y); 20 | -------------------------------------------------------------------------------- /ch02/README.md: -------------------------------------------------------------------------------- 1 | # 第2章 Rustの基本 2 | 3 | - [2.2.15 マルチスレッド](./sort/) 4 | -------------------------------------------------------------------------------- /ch02/sort/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sort" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /ch02/sort/src/main.rs: -------------------------------------------------------------------------------- 1 | //! 実行すると5GB以上のメモリを消費するため注意すること。 2 | //! メモリが足りない場合は、NUMの値を減らしてください。 3 | 4 | const NUM: usize = 200000000; // 生成する乱数の合計数 5 | 6 | /// xorshift 7 | struct XOR64 { 8 | x: u64, 9 | } 10 | 11 | impl XOR64 { 12 | fn new(seed: u64) -> XOR64 { 13 | XOR64 { 14 | x: seed ^ 88172645463325252, 15 | } 16 | } 17 | 18 | /// 乱数生成関数 19 | fn next(&mut self) -> u64 { 20 | let x = self.x; 21 | let x = x ^ (x << 13); 22 | let x = x ^ (x >> 7); 23 | let x = x ^ (x << 17); 24 | self.x = x; 25 | return x; 26 | } 27 | } 28 | 29 | fn main() { 30 | single_threaded(); 31 | multi_threaded(); 32 | } 33 | 34 | /// 乱数値を要素に持つVecを生成 35 | fn randomized_vec() -> (Vec, Vec) { 36 | let mut v1 = Vec::new(); 37 | let mut v2 = Vec::new(); 38 | 39 | let mut generator = XOR64::new(1234); 40 | 41 | // 疑似乱数生成 42 | for _ in 0..NUM { 43 | let x1 = generator.next(); 44 | let x2 = generator.next(); 45 | v1.push(x1); 46 | v2.push(x2); 47 | } 48 | 49 | (v1, v2) 50 | } 51 | 52 | fn single_threaded() { 53 | let (mut v1, mut v2) = randomized_vec(); 54 | 55 | let start = std::time::Instant::now(); // 開始時間 56 | 57 | v1.sort(); // 順番にソート 58 | v2.sort(); 59 | 60 | let end = start.elapsed(); // 経過時間 61 | println!( 62 | "single_threaded: {}.{:03}秒", 63 | end.as_secs(), 64 | end.subsec_nanos() / 1_000_000 65 | ); 66 | } 67 | 68 | fn multi_threaded() { 69 | let (mut v1, mut v2) = randomized_vec(); 70 | 71 | let start = std::time::Instant::now(); // 開始時間 72 | 73 | // スレッドを生成してソート 74 | let handler1 = std::thread::spawn(move || { 75 | v1.sort(); 76 | v1 77 | }); 78 | let handler2 = std::thread::spawn(move || { 79 | v2.sort(); 80 | v2 81 | }); 82 | let _v1 = handler1.join().unwrap(); 83 | let _v2 = handler2.join().unwrap(); 84 | 85 | let end = start.elapsed(); // 経過時間 86 | println!( 87 | "multi_threaded: {}.{:03}秒", 88 | end.as_secs(), 89 | end.subsec_nanos() / 1_000_000 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /ch03/README.md: -------------------------------------------------------------------------------- 1 | # 第3章 所有権・ライフタイム・借用 2 | 3 | - [3.5 借用と排他制御の類似性](./rwlock/) 4 | -------------------------------------------------------------------------------- /ch03/rwlock/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rwlock" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /ch03/rwlock/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::BTreeMap, 3 | sync::{Arc, RwLock}, 4 | thread::sleep, 5 | time::Duration, 6 | }; 7 | 8 | fn main() { 9 | // 美術館を初期化 10 | let mut gallery = BTreeMap::new(); 11 | gallery.insert("葛飾北斎", "富嶽三十六景 神奈川沖浪裏"); 12 | gallery.insert("ミュシャ", "黄道十二宮"); 13 | 14 | // RwLockとArcを利用して共有可能に 15 | let gallery = Arc::new(RwLock::new(gallery)); 16 | 17 | let mut hdls = Vec::new(); // joinハンドラ 18 | for n in 0..3 { 19 | // 客を表すスレッドを生成 20 | let gallery = gallery.clone(); // 参照カウンタをインクリメント 21 | let hdl = std::thread::spawn(move || { 22 | for _ in 0..8 { 23 | { 24 | let guard = gallery.read().unwrap(); // リードロック 25 | if n == 0 { 26 | // 美術館の内容を表示 27 | for (key, value) in guard.iter() { 28 | print!("{key}:{value}, "); 29 | } 30 | println!(); 31 | } 32 | } 33 | sleep(Duration::from_secs(1)); 34 | } 35 | }); 36 | hdls.push(hdl); 37 | } 38 | 39 | // 美術館スタッフ 40 | let staff = std::thread::spawn(move || { 41 | for n in 0..4 { 42 | // 展示内容入れ替え 43 | if n % 2 == 0 { 44 | let mut guard = gallery.write().unwrap(); // ライトロック 45 | guard.clear(); 46 | guard.insert("ゴッホ", "星月夜"); 47 | guard.insert("エッシャー", "滝"); 48 | } else { 49 | let mut guard = gallery.write().unwrap(); // ライトロック 50 | guard.clear(); 51 | guard.insert("葛飾北斎", "富嶽三十六景 神奈川沖浪裏"); 52 | guard.insert("ミュシャ", "黄道十二宮"); 53 | } 54 | sleep(Duration::from_secs(2)); 55 | } 56 | }); 57 | 58 | for hdl in hdls { 59 | hdl.join().unwrap(); 60 | } 61 | staff.join().unwrap(); 62 | } 63 | -------------------------------------------------------------------------------- /ch04/README.md: -------------------------------------------------------------------------------- 1 | # 第4章 トレイト 2 | 3 | - [4.1 トレイトの定義と実装](./display/) 4 | - [4.2 イテレータ](./iter/) 5 | - [4.3 シリアライズとファイル入出力](./serialize/) 6 | -------------------------------------------------------------------------------- /ch04/display/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "display" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /ch04/display/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | use std::fmt::{Display, Formatter}; 3 | 4 | /// 虚数を表す型 5 | struct ImaginaryNumber { 6 | real: f64, 7 | img: f64, 8 | } 9 | 10 | /// 虚数を表示するため、Displayトレイトを実装 11 | impl Display for ImaginaryNumber { 12 | fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { 13 | write!(f, "{} + {}i", self.real, self.img) 14 | } 15 | } 16 | 17 | let n = ImaginaryNumber { 18 | real: 3.0, 19 | img: 4.0, 20 | }; 21 | 22 | println!("{n}"); 23 | } 24 | -------------------------------------------------------------------------------- /ch04/iter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iter" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /ch04/iter/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::iter::Iterator; 2 | 3 | /// リストを表す型 4 | #[derive(Debug, Clone)] 5 | enum List { 6 | Node { data: T, next: Box> }, 7 | Nil, 8 | } 9 | 10 | impl List { 11 | fn new() -> List { 12 | List::Nil 13 | } 14 | 15 | /// リストを消費して、そのリストの先頭にdataを追加したリストを返す 16 | fn cons(self, data: T) -> List { 17 | List::Node { 18 | data, 19 | next: Box::new(self), 20 | } 21 | } 22 | 23 | /// 不変イテレータを返す 24 | fn iter<'a>(&'a self) -> ListIter<'a, T> { 25 | ListIter { elm: self } 26 | } 27 | } 28 | 29 | /// 不変イテレータを表す型 30 | struct ListIter<'a, T> { 31 | elm: &'a List, 32 | } 33 | 34 | impl<'a, T> Iterator for ListIter<'a, T> { 35 | type Item = &'a T; 36 | 37 | /// 次の要素を指す 38 | fn next(&mut self) -> Option { 39 | match self.elm { 40 | List::Node { data, next } => { 41 | self.elm = next; 42 | Some(data) 43 | } 44 | List::Nil => None, 45 | } 46 | } 47 | } 48 | 49 | fn main() { 50 | // [2, 1, 0]というリストを生成 51 | let list = List::new().cons(0).cons(1).cons(2); 52 | 53 | // forで表示 54 | for x in list.iter() { 55 | println!("{x}"); 56 | } 57 | 58 | println!(); 59 | 60 | // イテレータで表示 61 | let mut it = list.iter(); 62 | println!("{:?}", it.next().unwrap()); 63 | println!("{:?}", it.next().unwrap()); 64 | println!("{:?}", it.next().unwrap()); 65 | } 66 | -------------------------------------------------------------------------------- /ch04/serialize/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serialize" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | serde = {version = "1.0.136", features = ["derive"] } 12 | serde_json = "1.0.79" 13 | serde_yaml = "0.9.14" 14 | rmp-serde = "1.0.0" 15 | -------------------------------------------------------------------------------- /ch04/serialize/src/main.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | #[derive(Debug, Clone, Serialize, Deserialize)] 4 | enum List { 5 | Node { data: T, next: Box> }, 6 | Nil, 7 | } 8 | 9 | impl List { 10 | fn new() -> List { 11 | List::Nil 12 | } 13 | 14 | /// リストを消費して、そのリストの先頭にdataを追加したリストを返す 15 | fn cons(self, data: T) -> List { 16 | List::Node { 17 | data, 18 | next: Box::new(self), 19 | } 20 | } 21 | } 22 | 23 | fn main() { 24 | // リストを生成 25 | let list = List::new().cons(1).cons(2).cons(3); 26 | 27 | // JSONにシリアライズ 28 | let js = serde_json::to_string(&list).unwrap(); 29 | println!("JSON: {} bytes", js.len()); 30 | println!("{js}"); 31 | 32 | // YAMLにシリアライズ 33 | let yml = serde_yaml::to_string(&list).unwrap(); 34 | println!("YAML: {} bytes", yml.len()); 35 | println!("{yml}"); 36 | 37 | // MessagePackにシリアライズ 38 | let msgpack = rmp_serde::to_vec(&list).unwrap(); 39 | println!("MessagePack: {} bytes", msgpack.len()); 40 | 41 | // JSONからデシリアライズ 42 | let list = serde_json::from_str::>(&js).unwrap(); 43 | println!("{:?}", list); 44 | 45 | // YAMLからデシリアライズ 46 | let list = serde_yaml::from_str::>(&yml).unwrap(); 47 | println!("{:?}", list); 48 | 49 | // MessagePackからデシリアライズ 50 | let list = rmp_serde::from_slice::>(&msgpack).unwrap(); 51 | println!("{:?}", list); 52 | 53 | write_to_file(); 54 | read_from_file(); 55 | } 56 | 57 | fn write_to_file() { 58 | use std::{fs::File, io::prelude::*, path::Path}; 59 | 60 | // リストを生成し、YAMLにシリアライズ 61 | let list = List::new().cons(1).cons(2).cons(3); 62 | let yml = serde_yaml::to_string(&list).unwrap(); 63 | 64 | // ファイルに書き込み 65 | let path = Path::new("test.yml"); 66 | let mut f = File::create(path).unwrap(); // 新規ファイルを生成 67 | f.write_all(yml.as_bytes()).unwrap(); 68 | } 69 | 70 | fn read_from_file() { 71 | use std::{fs::File, io::prelude::*, path::Path}; 72 | 73 | // ファイルからYAML読み込み 74 | let path = Path::new("test.yml"); 75 | let mut f = File::open(path).unwrap(); // 既存のファイルをオープン 76 | let mut yml = String::new(); 77 | f.read_to_string(&mut yml).unwrap(); 78 | 79 | // YAMLからデシリアライズ 80 | let list = serde_yaml::from_str::>(&yml).unwrap(); 81 | println!("{:?}", list); 82 | } 83 | -------------------------------------------------------------------------------- /ch04/serialize/test.yml: -------------------------------------------------------------------------------- 1 | !Node 2 | data: 3 3 | next: !Node 4 | data: 2 5 | next: !Node 6 | data: 1 7 | next: Nil 8 | -------------------------------------------------------------------------------- /ch05/README.md: -------------------------------------------------------------------------------- 1 | # 第5章 モジュール・ドキュメント・テスト 2 | 3 | - 5.2.1 モジュールの定義 4 | - [モジュールの定義例 その2](./mod_ex2/) 5 | - [モジュールの定義例 その3](./mod_ex2/) 6 | - [5.2.2 可視性](./visibility/) 7 | - [5.2.1 マークダウン](./markdown/) 8 | -------------------------------------------------------------------------------- /ch05/markdown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "markdown" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /ch05/markdown/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # 第一見出し 2 | //! 3 | //! テキストを書く。 4 | //! 5 | //! ## 第二見出し 6 | //! 7 | //! ### 第三見出し 8 | //! 9 | //! - 箇条書き1 10 | //! - 箇条書き2 11 | //! 12 | //! 1. 番号付きリスト1 13 | //! 2. 番号付きリスト2 14 | //! 15 | //! > 引用 16 | //! > 文字列 17 | //! 18 | //! [KSPUB](https://www.kspub.co.jp/) 19 | //! 20 | //! `println!("Hello, world!");` 21 | //! 22 | //! ``` 23 | //! println!("Hello, world!"); 24 | //! ``` 25 | //! 26 | 27 | mod my_module { 28 | //! これはモジュールのドキュメントです。 29 | //! 30 | //! # 利用例 31 | } 32 | 33 | /// my_funcは私独自の関数です。 34 | /// 35 | /// # 利用例 36 | /// 37 | /// ``` 38 | /// use markdown::my_func; 39 | /// let n = my_func().unwrap(); 40 | /// ``` 41 | pub fn my_func() -> Option { 42 | Some(100) 43 | } 44 | 45 | /// nの一つ前の数字を返す 46 | /// nが0の場合はNoneを返す 47 | pub fn pred(n: u32) -> Option { 48 | if n == 0 { 49 | None 50 | } else { 51 | Some(n - 1) 52 | } 53 | } 54 | 55 | #[cfg(test)] 56 | mod test { 57 | use super::*; 58 | 59 | #[test] 60 | fn test_my_func() { 61 | assert_eq!(my_func(), Some(100)); 62 | } 63 | 64 | #[test] 65 | #[should_panic] 66 | fn test_pred() { 67 | pred(0).unwrap(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ch05/mod_ex2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mod_ex2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /ch05/mod_ex2/src/a.rs: -------------------------------------------------------------------------------- 1 | mod a_1; 2 | mod a_2; 3 | 4 | struct TypeA; 5 | -------------------------------------------------------------------------------- /ch05/mod_ex2/src/a/a_1.rs: -------------------------------------------------------------------------------- 1 | struct TypeA1; 2 | -------------------------------------------------------------------------------- /ch05/mod_ex2/src/a/a_2.rs: -------------------------------------------------------------------------------- 1 | struct TypeA2; 2 | -------------------------------------------------------------------------------- /ch05/mod_ex2/src/b.rs: -------------------------------------------------------------------------------- 1 | mod b_1; 2 | mod b_2; 3 | 4 | struct TypeB; 5 | -------------------------------------------------------------------------------- /ch05/mod_ex2/src/b/b_1.rs: -------------------------------------------------------------------------------- 1 | struct TypeB1; 2 | -------------------------------------------------------------------------------- /ch05/mod_ex2/src/b/b_2.rs: -------------------------------------------------------------------------------- 1 | struct TypeB2; 2 | -------------------------------------------------------------------------------- /ch05/mod_ex2/src/main.rs: -------------------------------------------------------------------------------- 1 | mod a; 2 | mod b; 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /ch05/mod_ex3/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mod_ex3" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /ch05/mod_ex3/src/a/a_1.rs: -------------------------------------------------------------------------------- 1 | struct TypeA1; 2 | -------------------------------------------------------------------------------- /ch05/mod_ex3/src/a/a_2.rs: -------------------------------------------------------------------------------- 1 | struct TypeA2; 2 | -------------------------------------------------------------------------------- /ch05/mod_ex3/src/a/mod.rs: -------------------------------------------------------------------------------- 1 | mod a_1; 2 | mod a_2; 3 | 4 | struct TypeA; 5 | -------------------------------------------------------------------------------- /ch05/mod_ex3/src/b/b_1.rs: -------------------------------------------------------------------------------- 1 | struct TypeB1; 2 | -------------------------------------------------------------------------------- /ch05/mod_ex3/src/b/b_2.rs: -------------------------------------------------------------------------------- 1 | struct TypeB2; 2 | -------------------------------------------------------------------------------- /ch05/mod_ex3/src/b/mod.rs: -------------------------------------------------------------------------------- 1 | mod b_1; 2 | mod b_2; 3 | 4 | struct TypeB; 5 | -------------------------------------------------------------------------------- /ch05/mod_ex3/src/main.rs: -------------------------------------------------------------------------------- 1 | mod a; 2 | mod b; 3 | 4 | fn main() {} 5 | -------------------------------------------------------------------------------- /ch05/visibility/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "visibility" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | -------------------------------------------------------------------------------- /ch05/visibility/src/main.rs: -------------------------------------------------------------------------------- 1 | mod a { 2 | struct TypeA { 3 | // a1: a_1::TypeA1, // エラー。子のプライベートな要素は見えない 4 | a2: Box, // 子のパブリックな要素は見える 5 | } 6 | 7 | mod a_1 { 8 | struct TypeA1 { 9 | // 親が見えるものは見える 10 | a: Box, 11 | a2: Box, 12 | } 13 | } 14 | 15 | mod a_2 { 16 | pub struct TypeA2 { 17 | // 親が見えるものは見える 18 | a: Box, 19 | // a1: super::a_1::TypeA1, // エラー。親の見えないものは見えない 20 | } 21 | } 22 | } 23 | 24 | mod b { 25 | pub struct TypeB; 26 | 27 | mod b_1 { 28 | pub struct TypeB1 { 29 | pub n: usize, 30 | m: usize, 31 | } 32 | 33 | impl TypeB1 { 34 | fn g(&self) {} 35 | pub fn h(&self) {} 36 | } 37 | 38 | fn f1(p: &super::b_1::TypeB1) { 39 | println!("{}", p.n); 40 | println!("{}", p.m); 41 | p.g(); 42 | p.h(); 43 | } 44 | } 45 | 46 | pub mod b_2 { 47 | pub struct TypeB2; 48 | 49 | fn f2(p: &super::b_1::TypeB1) { 50 | println!("{}", p.n); 51 | // println!("{}", p.m); // エラー。mはプライベート 52 | // p.g(); // エラー。gはプライベート 53 | p.h(); 54 | } 55 | } 56 | } 57 | 58 | mod c { 59 | mod c_1_outer { 60 | pub mod c_1_inner { 61 | pub(crate) struct TypeC1; // 同じクレート内からのみ見える 62 | pub(super) struct TypeC2; // 親モジュールからのみ見える 63 | pub(in crate::c::c_1_outer) struct TypeC3; // 親モジュールからのみ見える 64 | pub(self) struct TypeC4; // プライベートと同義 65 | } 66 | 67 | fn f() { 68 | let p1 = c_1_inner::TypeC1; 69 | let p2 = c_1_inner::TypeC2; 70 | let p3 = c_1_inner::TypeC3; 71 | // let p4 = c_1_inner::TypeC4; // エラー。プライベートなので見えない 72 | } 73 | } 74 | 75 | fn g() { 76 | let p1 = c_1_outer::c_1_inner::TypeC1; 77 | // let p2 = c_1_outer::c_1_inner::TypeC2; // エラー 78 | // let p3 = c_1_outer::c_1_inner::TypeC3; // エラー 79 | // let p4 = c_1_outer::c_1_inner::TypeC4; // エラー 80 | } 81 | } 82 | 83 | mod d { 84 | pub struct TypeD; 85 | } 86 | 87 | mod e { 88 | pub use crate::d::TypeD; 89 | } 90 | 91 | fn main() { 92 | // let a = a::TypeA; // エラー。子のプライベートな要素は見えない 93 | let b = b::TypeB; // 子のパブリックな要素は見える 94 | 95 | //let b1 = b::b_1::TypeB1; // 子のプライベートな要素なモジュールb_1は見えない 96 | let b2 = b::b_2::TypeB2; // パブリックな孫b_2のパブリックな要素TypeB2は見える 97 | 98 | let e = e::TypeD; // 再エクスポートされた型を利用 99 | } 100 | -------------------------------------------------------------------------------- /ch06/README.md: -------------------------------------------------------------------------------- 1 | # 第6章 正規表現 2 | 3 | - [第6章 正規表現](./regex/) 4 | -------------------------------------------------------------------------------- /ch06/regex/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "regex" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | 12 | [dev-dependencies] 13 | criterion = "0.3.5" 14 | 15 | [[bench]] 16 | name = "benchmark" 17 | harness = false 18 | -------------------------------------------------------------------------------- /ch06/regex/benches/benchmark.rs: -------------------------------------------------------------------------------- 1 | //! # パフォーマンス計測 2 | //! 3 | //! ## 計測方法 4 | //! a?^n a^nという正規表現を、a^nという文字列にマッチさせる。 5 | //! ただし、a?^nとa^nは、a?とaのn回の繰り返し。 6 | //! 計測は幅優先と深さ優先で行う。 7 | //! 8 | //! ## n = 3の場合の例 9 | //! 10 | //! - 正規表現: a?a?a?aaa 11 | //! - str: aaa 12 | //! 13 | //! ## 実行方法 14 | //! 15 | //! cargo-criterionをインストール後、cargo criterionと実行。 16 | //! 17 | //! ```text 18 | //! $ cargo install cargo-criterion 19 | //! $ cargo criterion 20 | //! ``` 21 | //! 22 | //! 実行後は、target/criterion/reports/index.htmlというファイルが生成されるため、 23 | //! それをWebブラウザで閲覧する。 24 | use criterion::{criterion_group, criterion_main, Criterion}; 25 | use regex::do_matching; 26 | use std::time::Duration; 27 | 28 | /// (計測のid、a?^n a^nという正規表現、文字列)というタプル 29 | const INPUTS: &[(&str, &str, &str)] = &[ 30 | ("n = 2", "a?a?aa", "aa"), 31 | ("n = 4", "a?a?a?a?aaaa", "aaaa"), 32 | ("n = 6", "a?a?a?a?a?a?aaaaaa", "aaaaaa"), 33 | ("n = 8", "a?a?a?a?a?a?a?a?aaaaaaaa", "aaaaaaaa"), 34 | ("n = 10", "a?a?a?a?a?a?a?a?a?a?aaaaaaaaaa", "aaaaaaaaaa"), 35 | ( 36 | "n = 12", 37 | "a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaa", 38 | "aaaaaaaaaaaa", 39 | ), 40 | ( 41 | "n = 14", 42 | "a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaa", 43 | "aaaaaaaaaaaaaa", 44 | ), 45 | ( 46 | "n = 16", 47 | "a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaa", 48 | "aaaaaaaaaaaaaaaa", 49 | ), 50 | ( 51 | "n = 18", 52 | "a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaa", 53 | "aaaaaaaaaaaaaaaaaa", 54 | ), 55 | ( 56 | "n = 20", 57 | "a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?a?aaaaaaaaaaaaaaaaaaaa", 58 | "aaaaaaaaaaaaaaaaaaaa", 59 | ), 60 | ]; 61 | 62 | fn depth_first(c: &mut Criterion) { 63 | let mut g = c.benchmark_group("Depth First"); 64 | g.measurement_time(Duration::from_secs(12)); 65 | 66 | for i in INPUTS { 67 | g.bench_with_input(i.0, &(i.1, i.2), |b, args| { 68 | b.iter(|| do_matching(args.0, args.1, true)) 69 | }); 70 | } 71 | } 72 | 73 | fn width_first(c: &mut Criterion) { 74 | let mut g = c.benchmark_group("Width First"); 75 | g.measurement_time(Duration::from_secs(12)); 76 | 77 | for i in INPUTS { 78 | g.bench_with_input(i.0, &(i.1, i.2), |b, args| { 79 | b.iter(|| do_matching(args.0, args.1, false)) 80 | }); 81 | } 82 | } 83 | 84 | criterion_group!(benches, width_first, depth_first); 85 | criterion_main!(benches); 86 | -------------------------------------------------------------------------------- /ch06/regex/src/engine.rs: -------------------------------------------------------------------------------- 1 | //! 正規表現エンジン 2 | mod codegen; 3 | mod evaluator; 4 | mod parser; 5 | 6 | use crate::helper::DynError; 7 | use std::fmt::{self, Display}; 8 | 9 | /// 命令列 10 | #[derive(Debug)] 11 | pub enum Instruction { 12 | Char(char), 13 | Match, 14 | Jump(usize), 15 | Split(usize, usize), 16 | } 17 | 18 | impl Display for Instruction { 19 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 20 | match self { 21 | Instruction::Char(c) => write!(f, "char {}", c), 22 | Instruction::Match => write!(f, "match"), 23 | Instruction::Jump(addr) => write!(f, "jump {:>04}", addr), 24 | Instruction::Split(addr1, addr2) => write!(f, "split {:>04}, {:>04}", addr1, addr2), 25 | } 26 | } 27 | } 28 | 29 | /// 正規表現をパースしてコード生成し、 30 | /// ASTと命令列を標準出力に表示。 31 | /// 32 | /// # 利用例 33 | /// 34 | /// ``` 35 | /// use regex; 36 | /// regex::print("abc|(de|cd)+"); 37 | /// ``` 38 | /// 39 | /// # 返り値 40 | /// 41 | /// 入力された正規表現にエラーがあったり、内部的な実装エラーがある場合はErrを返す。 42 | pub fn print(expr: &str) -> Result<(), DynError> { 43 | println!("expr: {expr}"); 44 | let ast = parser::parse(expr)?; 45 | println!("AST: {:?}", ast); 46 | 47 | println!(); 48 | println!("code:"); 49 | let code = codegen::get_code(&ast)?; 50 | for (n, c) in code.iter().enumerate() { 51 | println!("{:>04}: {c}", n); 52 | } 53 | 54 | Ok(()) 55 | } 56 | 57 | /// 正規表現と文字列をマッチング。 58 | /// 59 | /// # 利用例 60 | /// 61 | /// ``` 62 | /// use regex; 63 | /// regex::do_matching("abc|(de|cd)+", "decddede", true); 64 | /// ``` 65 | /// 66 | /// # 引数 67 | /// 68 | /// exprに正規表現、lineにマッチ対象とする文字列を与える。 69 | /// is_depthがtrueの場合は深さ優先探索を、falseの場合は幅優先探索を利用。 70 | /// 71 | /// # 返り値 72 | /// 73 | /// エラーなく実行でき、かつマッチングに**成功**した場合はOk(true)を返し、 74 | /// エラーなく実行でき、かつマッチングに**失敗**した場合はOk(false)を返す。 75 | /// 76 | /// 入力された正規表現にエラーがあったり、内部的な実装エラーがある場合はErrを返す。 77 | pub fn do_matching(expr: &str, line: &str, is_depth: bool) -> Result { 78 | let ast = parser::parse(expr)?; 79 | let code = codegen::get_code(&ast)?; 80 | let line = line.chars().collect::>(); 81 | Ok(evaluator::eval(&code, &line, is_depth)?) 82 | } 83 | -------------------------------------------------------------------------------- /ch06/regex/src/engine/codegen.rs: -------------------------------------------------------------------------------- 1 | //! ASTからコード生成を行う 2 | use super::{parser::AST, Instruction}; 3 | use crate::helper::safe_add; 4 | use std::{ 5 | error::Error, 6 | fmt::{self, Display}, 7 | }; 8 | 9 | /// コード生成エラーを表す型 10 | #[derive(Debug)] 11 | pub enum CodeGenError { 12 | PCOverFlow, 13 | FailStar, 14 | FailOr, 15 | FailQuestion, 16 | } 17 | 18 | impl Display for CodeGenError { 19 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 20 | write!(f, "CodeGenError: {:?}", self) 21 | } 22 | } 23 | 24 | impl Error for CodeGenError {} 25 | 26 | /// コード生成器 27 | #[derive(Default, Debug)] 28 | struct Generator { 29 | pc: usize, 30 | insts: Vec, 31 | } 32 | 33 | /// コード生成を行う関数 34 | pub fn get_code(ast: &AST) -> Result, CodeGenError> { 35 | let mut generator = Generator::default(); 36 | generator.gen_code(ast)?; 37 | Ok(generator.insts) 38 | } 39 | 40 | /// コード生成器のメソッド定義 41 | impl Generator { 42 | /// コード生成を行う関数の入り口 43 | fn gen_code(&mut self, ast: &AST) -> Result<(), CodeGenError> { 44 | self.gen_expr(ast)?; 45 | self.inc_pc()?; 46 | self.insts.push(Instruction::Match); 47 | Ok(()) 48 | } 49 | 50 | /// ASTをパターン分けしコード生成を行う関数 51 | fn gen_expr(&mut self, ast: &AST) -> Result<(), CodeGenError> { 52 | match ast { 53 | AST::Char(c) => self.gen_char(*c)?, 54 | AST::Or(e1, e2) => self.gen_or(e1, e2)?, 55 | AST::Plus(e) => self.gen_plus(e)?, 56 | AST::Star(e1) => { 57 | match &**e1 { 58 | // `(a*)*`のように`Star`が二重となっている場合にスタックオーバーフローする問題を回避するため、 59 | // このような`(((r*)*)*...*)*`を再帰的に処理して1つの`r*`へと変換する。 60 | AST::Star(_) => self.gen_expr(&e1)?, 61 | AST::Seq(e2) if e2.len() == 1 => { 62 | if let Some(e3 @ AST::Star(_)) = e2.get(0) { 63 | self.gen_expr(e3)? 64 | } else { 65 | self.gen_star(e1)? 66 | } 67 | } 68 | e => self.gen_star(&e)?, 69 | } 70 | } 71 | AST::Question(e) => self.gen_question(e)?, 72 | AST::Seq(v) => self.gen_seq(v)?, 73 | } 74 | 75 | Ok(()) 76 | } 77 | 78 | /// char命令生成関数 79 | fn gen_char(&mut self, c: char) -> Result<(), CodeGenError> { 80 | let inst = Instruction::Char(c); 81 | self.insts.push(inst); 82 | self.inc_pc()?; 83 | Ok(()) 84 | } 85 | 86 | /// OR演算子のコード生成器。 87 | /// 88 | /// 以下のようなコードを生成。 89 | /// 90 | /// ```text 91 | /// split L1, L2 92 | /// L1: e1のコード 93 | /// jmp L3 94 | /// L2: e2のコード 95 | /// L3: 96 | /// ``` 97 | fn gen_or(&mut self, e1: &AST, e2: &AST) -> Result<(), CodeGenError> { 98 | // split L1, L2 99 | let split_addr = self.pc; 100 | self.inc_pc()?; 101 | let split = Instruction::Split(self.pc, 0); // L1 = self.pc。L2は仮に0と設定 102 | self.insts.push(split); 103 | 104 | // L1: e1のコード 105 | self.gen_expr(e1)?; 106 | 107 | // jmp L3 108 | let jmp_addr = self.pc; 109 | self.insts.push(Instruction::Jump(0)); // L3を仮に0と設定 110 | 111 | // L2の値を設定 112 | self.inc_pc()?; 113 | if let Some(Instruction::Split(_, l2)) = self.insts.get_mut(split_addr) { 114 | *l2 = self.pc; 115 | } else { 116 | return Err(CodeGenError::FailOr); 117 | } 118 | 119 | // L2: e2のコード 120 | self.gen_expr(e2)?; 121 | 122 | // L3の値を設定 123 | if let Some(Instruction::Jump(l3)) = self.insts.get_mut(jmp_addr) { 124 | *l3 = self.pc; 125 | } else { 126 | return Err(CodeGenError::FailOr); 127 | } 128 | 129 | Ok(()) 130 | } 131 | 132 | /// ?限量子のコード生成器。 133 | /// 134 | /// 以下のようなコードを生成 135 | /// 136 | /// ```text 137 | /// split L1, L2 138 | /// L1: eのコード 139 | /// L2: 140 | /// ``` 141 | fn gen_question(&mut self, e: &AST) -> Result<(), CodeGenError> { 142 | // split L1, L2 143 | let split_addr = self.pc; 144 | self.inc_pc()?; 145 | let split = Instruction::Split(self.pc, 0); // self.pcがL1。L2を仮に0と設定 146 | self.insts.push(split); 147 | 148 | // L1: eのコード 149 | self.gen_expr(e)?; 150 | 151 | // L2の値を設定 152 | if let Some(Instruction::Split(_, l2)) = self.insts.get_mut(split_addr) { 153 | *l2 = self.pc; 154 | Ok(()) 155 | } else { 156 | Err(CodeGenError::FailQuestion) 157 | } 158 | } 159 | 160 | /// 以下のようなコードを生成 161 | /// 162 | /// ```text 163 | /// L1: eのコード 164 | /// split L1, L2 165 | /// L2: 166 | /// ``` 167 | fn gen_plus(&mut self, e: &AST) -> Result<(), CodeGenError> { 168 | // L1: eのコード 169 | let l1 = self.pc; 170 | self.gen_expr(e)?; 171 | 172 | // split L1, L2 173 | self.inc_pc()?; 174 | let split = Instruction::Split(l1, self.pc); // self.pcがL2 175 | self.insts.push(split); 176 | 177 | Ok(()) 178 | } 179 | 180 | /// *限量子のコード生成器。 181 | /// 182 | /// 以下のようなコードを生成 183 | /// 184 | /// ```text 185 | /// L1: split L2, L3 186 | /// L2: eのコード 187 | /// jump L1 188 | /// L3: 189 | /// ``` 190 | fn gen_star(&mut self, e: &AST) -> Result<(), CodeGenError> { 191 | // L1: split L2, L3 192 | let l1 = self.pc; 193 | self.inc_pc()?; 194 | let split = Instruction::Split(self.pc, 0); // self.pcがL2。L3を仮に0と設定 195 | self.insts.push(split); 196 | 197 | // L2: eのコード 198 | self.gen_expr(e)?; 199 | 200 | // jump L1 201 | self.inc_pc()?; 202 | self.insts.push(Instruction::Jump(l1)); 203 | 204 | // L3の値を設定 205 | if let Some(Instruction::Split(_, l3)) = self.insts.get_mut(l1) { 206 | *l3 = self.pc; 207 | Ok(()) 208 | } else { 209 | Err(CodeGenError::FailStar) 210 | } 211 | } 212 | 213 | /// 連続する正規表現のコード生成 214 | fn gen_seq(&mut self, exprs: &[AST]) -> Result<(), CodeGenError> { 215 | for e in exprs { 216 | self.gen_expr(e)?; 217 | } 218 | 219 | Ok(()) 220 | } 221 | 222 | /// プログラムカウンタをインクリメント 223 | fn inc_pc(&mut self) -> Result<(), CodeGenError> { 224 | safe_add(&mut self.pc, &1, || CodeGenError::PCOverFlow) 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /ch06/regex/src/engine/evaluator.rs: -------------------------------------------------------------------------------- 1 | //! 命令列と入力文字列を受け取り、マッチングを行う 2 | use super::Instruction; 3 | use crate::helper::safe_add; 4 | use std::{ 5 | collections::VecDeque, 6 | error::Error, 7 | fmt::{self, Display}, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub enum EvalError { 12 | PCOverFlow, 13 | SPOverFlow, 14 | InvalidPC, 15 | InvalidContext, 16 | } 17 | 18 | impl Display for EvalError { 19 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 20 | write!(f, "CodeGenError: {:?}", self) 21 | } 22 | } 23 | 24 | impl Error for EvalError {} 25 | 26 | /// 命令列の評価を行う関数。 27 | /// 28 | /// instが命令列となり、その命令列を用いて入力文字列lineにマッチさせる。 29 | /// is_depthがtrueの場合に深さ優先探索を、falseの場合に幅優先探索を行う。 30 | /// 31 | /// 実行時エラーが起きた場合はErrを返す。 32 | /// マッチ成功時はOk(true)を、失敗時はOk(false)を返す。 33 | pub fn eval(inst: &[Instruction], line: &[char], is_depth: bool) -> Result { 34 | if is_depth { 35 | eval_depth(inst, line, 0, 0) 36 | } else { 37 | eval_width(inst, line) 38 | } 39 | } 40 | 41 | /// 深さ優先探索で再帰的にマッチングを行う評価器 42 | fn eval_depth( 43 | inst: &[Instruction], 44 | line: &[char], 45 | mut pc: usize, 46 | mut sp: usize, 47 | ) -> Result { 48 | loop { 49 | let next = if let Some(i) = inst.get(pc) { 50 | i 51 | } else { 52 | return Err(EvalError::InvalidPC); 53 | }; 54 | 55 | match next { 56 | Instruction::Char(c) => { 57 | if let Some(sp_c) = line.get(sp) { 58 | if c == sp_c { 59 | safe_add(&mut pc, &1, || EvalError::PCOverFlow)?; 60 | safe_add(&mut sp, &1, || EvalError::SPOverFlow)?; 61 | } else { 62 | return Ok(false); 63 | } 64 | } else { 65 | return Ok(false); 66 | } 67 | } 68 | Instruction::Match => { 69 | return Ok(true); 70 | } 71 | Instruction::Jump(addr) => { 72 | pc = *addr; 73 | } 74 | Instruction::Split(addr1, addr2) => { 75 | if eval_depth(inst, line, *addr1, sp)? || eval_depth(inst, line, *addr2, sp)? { 76 | return Ok(true); 77 | } else { 78 | return Ok(false); 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | fn pop_ctx( 86 | pc: &mut usize, 87 | sp: &mut usize, 88 | ctx: &mut VecDeque<(usize, usize)>, 89 | ) -> Result<(), EvalError> { 90 | if let Some((p, s)) = ctx.pop_back() { 91 | *pc = p; 92 | *sp = s; 93 | Ok(()) 94 | } else { 95 | Err(EvalError::InvalidContext) 96 | } 97 | } 98 | 99 | /// 幅優先探索で再帰的にマッチングを行う評価器 100 | fn eval_width(inst: &[Instruction], line: &[char]) -> Result { 101 | let mut ctx = VecDeque::new(); 102 | let mut pc = 0; 103 | let mut sp = 0; 104 | 105 | loop { 106 | let next = if let Some(i) = inst.get(pc) { 107 | i 108 | } else { 109 | return Err(EvalError::InvalidPC); 110 | }; 111 | 112 | match next { 113 | Instruction::Char(c) => { 114 | if let Some(sp_c) = line.get(sp) { 115 | if c == sp_c { 116 | safe_add(&mut pc, &1, || EvalError::PCOverFlow)?; 117 | safe_add(&mut sp, &1, || EvalError::SPOverFlow)?; 118 | } else { 119 | if ctx.is_empty() { 120 | return Ok(false); 121 | } else { 122 | pop_ctx(&mut pc, &mut sp, &mut ctx)?; 123 | } 124 | } 125 | } else { 126 | if ctx.is_empty() { 127 | return Ok(false); 128 | } else { 129 | pop_ctx(&mut pc, &mut sp, &mut ctx)?; 130 | } 131 | } 132 | } 133 | Instruction::Match => { 134 | return Ok(true); 135 | } 136 | Instruction::Jump(addr) => { 137 | pc = *addr; 138 | } 139 | Instruction::Split(addr1, addr2) => { 140 | pc = *addr1; 141 | ctx.push_back((*addr2, sp)); 142 | continue; 143 | } 144 | } 145 | 146 | if !ctx.is_empty() { 147 | ctx.push_back((pc, sp)); 148 | pop_ctx(&mut pc, &mut sp, &mut ctx)?; 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /ch06/regex/src/engine/parser.rs: -------------------------------------------------------------------------------- 1 | //! 正規表現の式をパースし、抽象構文木に変換 2 | use std::{ 3 | error::Error, 4 | fmt::{self, Display}, 5 | mem::take, 6 | }; 7 | 8 | /// パースエラーを表すための型 9 | #[derive(Debug)] 10 | pub enum ParseError { 11 | InvalidEscape(usize, char), // 誤ったエスケープシーケンス 12 | InvalidRightParen(usize), // 左開き括弧無し 13 | NoPrev(usize), // +、|、*、?の前に式がない 14 | NoRightParen, // 右閉じ括弧無し 15 | Empty, // 空のパターン 16 | } 17 | 18 | /// パースエラーを表示するために、Displayトレイトを実装 19 | impl Display for ParseError { 20 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 21 | match self { 22 | ParseError::InvalidEscape(pos, c) => { 23 | write!(f, "ParseError: invalid escape: pos = {pos}, char = '{c}'") 24 | } 25 | ParseError::InvalidRightParen(pos) => { 26 | write!(f, "ParseError: invalid right parenthesis: pos = {pos}") 27 | } 28 | ParseError::NoPrev(pos) => { 29 | write!(f, "ParseError: no previous expression: pos = {pos}") 30 | } 31 | ParseError::NoRightParen => { 32 | write!(f, "ParseError: no right parenthesis") 33 | } 34 | ParseError::Empty => write!(f, "ParseError: empty expression"), 35 | } 36 | } 37 | } 38 | 39 | impl Error for ParseError {} // エラー用に、Errorトレイトを実装 40 | 41 | /// 抽象構文木を表現するための型 42 | #[derive(Debug)] 43 | pub enum AST { 44 | Char(char), 45 | Plus(Box), 46 | Star(Box), 47 | Question(Box), 48 | Or(Box, Box), 49 | Seq(Vec), 50 | } 51 | 52 | /// parse_plus_star_question関数で利用するための列挙型 53 | enum PSQ { 54 | Plus, 55 | Star, 56 | Question, 57 | } 58 | 59 | /// 正規表現を抽象構文木に変換 60 | pub fn parse(expr: &str) -> Result { 61 | // 内部状態を表現するための型 62 | // Char状態 : 文字列処理中 63 | // Escape状態 : エスケープシーケンス処理中 64 | enum ParseState { 65 | Char, 66 | Escape, 67 | } 68 | 69 | let mut seq = Vec::new(); // 現在のSeqのコンテキスト 70 | let mut seq_or = Vec::new(); // 現在のOrのコンテキスト 71 | let mut stack = Vec::new(); // コンテキストのスタック 72 | let mut state = ParseState::Char; // 現在の状態 73 | 74 | for (i, c) in expr.chars().enumerate() { 75 | match &state { 76 | ParseState::Char => { 77 | match c { 78 | '+' => parse_plus_star_question(&mut seq, PSQ::Plus, i)?, 79 | '*' => parse_plus_star_question(&mut seq, PSQ::Star, i)?, 80 | '?' => parse_plus_star_question(&mut seq, PSQ::Question, i)?, 81 | '(' => { 82 | // 現在のコンテキストをスタックに追加し、 83 | // 現在のコンテキストを空の状態にする 84 | let prev = take(&mut seq); 85 | let prev_or = take(&mut seq_or); 86 | stack.push((prev, prev_or)); 87 | } 88 | ')' => { 89 | // 現在のコンテキストをスタックからポップ 90 | if let Some((mut prev, prev_or)) = stack.pop() { 91 | // "()"のように式が空の場合はpushしない 92 | if !seq.is_empty() { 93 | seq_or.push(AST::Seq(seq)); 94 | } 95 | 96 | // Orを生成 97 | if let Some(ast) = fold_or(seq_or) { 98 | prev.push(ast); 99 | } 100 | 101 | // 以前のコンテキストを、現在のコンテキストにする 102 | seq = prev; 103 | seq_or = prev_or; 104 | } else { 105 | // "abc)"のように、開き括弧がないのに閉じ括弧がある場合はエラー 106 | return Err(ParseError::InvalidRightParen(i)); 107 | } 108 | } 109 | '|' => { 110 | if seq.is_empty() { 111 | // "||", "(|abc)"などと、式が空の場合はエラー 112 | return Err(ParseError::NoPrev(i)); 113 | } else { 114 | let prev = take(&mut seq); 115 | seq_or.push(AST::Seq(prev)); 116 | } 117 | } 118 | '\\' => state = ParseState::Escape, 119 | _ => seq.push(AST::Char(c)), 120 | }; 121 | } 122 | ParseState::Escape => { 123 | // エスケープシーケンス処理 124 | let ast = parse_escape(i, c)?; 125 | seq.push(ast); 126 | state = ParseState::Char; 127 | } 128 | } 129 | } 130 | 131 | // 閉じ括弧が足りない場合はエラー 132 | if !stack.is_empty() { 133 | return Err(ParseError::NoRightParen); 134 | } 135 | 136 | // "()"のように式が空の場合はpushしない 137 | if !seq.is_empty() { 138 | seq_or.push(AST::Seq(seq)); 139 | } 140 | 141 | // Orを生成し、成功した場合はそれを返す 142 | if let Some(ast) = fold_or(seq_or) { 143 | Ok(ast) 144 | } else { 145 | Err(ParseError::Empty) 146 | } 147 | } 148 | 149 | /// +、*、?をASTに変換 150 | /// 151 | /// 後置記法で、+、*、?の前にパターンがない場合はエラー 152 | /// 153 | /// 例 : *ab、abc|+などはエラー 154 | fn parse_plus_star_question( 155 | seq: &mut Vec, 156 | ast_type: PSQ, 157 | pos: usize, 158 | ) -> Result<(), ParseError> { 159 | if let Some(prev) = seq.pop() { 160 | let ast = match ast_type { 161 | PSQ::Plus => AST::Plus(Box::new(prev)), 162 | PSQ::Star => AST::Star(Box::new(prev)), 163 | PSQ::Question => AST::Question(Box::new(prev)), 164 | }; 165 | seq.push(ast); 166 | Ok(()) 167 | } else { 168 | Err(ParseError::NoPrev(pos)) 169 | } 170 | } 171 | 172 | /// 特殊文字のエスケープ 173 | fn parse_escape(pos: usize, c: char) -> Result { 174 | match c { 175 | '\\' | '(' | ')' | '|' | '+' | '*' | '?' => Ok(AST::Char(c)), 176 | _ => { 177 | let err = ParseError::InvalidEscape(pos, c); 178 | Err(err) 179 | } 180 | } 181 | } 182 | 183 | /// orで結合された複数の式をASTに変換 184 | /// 185 | /// たとえば、abc|def|ghi は、AST::Or("abc", AST::Or("def", "ghi"))というASTとなる 186 | fn fold_or(mut seq_or: Vec) -> Option { 187 | if seq_or.len() > 1 { 188 | // seq_orの要素が複数ある場合は、Orで式を結合 189 | let mut ast = seq_or.pop().unwrap(); 190 | seq_or.reverse(); 191 | for s in seq_or { 192 | ast = AST::Or(Box::new(s), Box::new(ast)); 193 | } 194 | Some(ast) 195 | } else { 196 | // seq_orの要素が一つのみの場合は、Orではなく、最初の値を返す 197 | seq_or.pop() 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /ch06/regex/src/helper.rs: -------------------------------------------------------------------------------- 1 | pub trait SafeAdd: Sized { 2 | fn safe_add(&self, n: &Self) -> Option; 3 | } 4 | 5 | impl SafeAdd for usize { 6 | fn safe_add(&self, n: &Self) -> Option { 7 | self.checked_add(*n) 8 | } 9 | } 10 | 11 | pub fn safe_add(dst: &mut T, src: &T, f: F) -> Result<(), E> 12 | where 13 | T: SafeAdd, 14 | F: Fn() -> E, 15 | { 16 | if let Some(n) = dst.safe_add(src) { 17 | *dst = n; 18 | Ok(()) 19 | } else { 20 | Err(f()) 21 | } 22 | } 23 | 24 | pub type DynError = Box; 25 | -------------------------------------------------------------------------------- /ch06/regex/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # 正規表現エンジン用クレート。 2 | //! 3 | //! ## 利用例 4 | //! 5 | //! ``` 6 | //! use regex; 7 | //! let expr = "a(bc)+|c(def)*"; // 正規表現 8 | //! let line = "cdefdefdef"; // マッチ対象文字列 9 | //! regex::do_matching(expr, line, true); // 幅優先探索でマッチング 10 | //! regex::print(expr); // 正規表現のASTと命令列を表示 11 | //! ``` 12 | mod engine; 13 | mod helper; 14 | 15 | pub use engine::{do_matching, print}; 16 | -------------------------------------------------------------------------------- /ch06/regex/src/main.rs: -------------------------------------------------------------------------------- 1 | mod engine; 2 | mod helper; 3 | 4 | use helper::DynError; 5 | use std::{ 6 | env, 7 | fs::File, 8 | io::{BufRead, BufReader}, 9 | }; 10 | 11 | fn main() -> Result<(), DynError> { 12 | let args: Vec = env::args().collect(); 13 | if args.len() <= 2 { 14 | eprintln!("usage: {} regex file", args[0]); 15 | return Err("invalid arguments".into()); 16 | } else { 17 | match_file(&args[1], &args[2])?; 18 | } 19 | 20 | Ok(()) 21 | } 22 | 23 | /// ファイルをオープンし、行ごとにマッチングを行う。 24 | /// 25 | /// マッチングはそれぞれの行頭から1文字ずつずらして行い、 26 | /// いずれかにマッチした場合に、その行がマッチしたものとみなす。 27 | /// 28 | /// たとえば、abcdという文字列があった場合、以下の順にマッチが行われ、 29 | /// このいずれかにマッチした場合、与えられた正規表現にマッチする行と判定する。 30 | /// 31 | /// - abcd 32 | /// - bcd 33 | /// - cd 34 | /// - d 35 | fn match_file(expr: &str, file: &str) -> Result<(), DynError> { 36 | let f = File::open(file)?; 37 | let reader = BufReader::new(f); 38 | 39 | engine::print(expr)?; 40 | println!(); 41 | 42 | for line in reader.lines() { 43 | let line = line?; 44 | for (i, _) in line.char_indices() { 45 | if engine::do_matching(expr, &line[i..], true)? { 46 | println!("{line}"); 47 | break; 48 | } 49 | } 50 | } 51 | 52 | Ok(()) 53 | } 54 | 55 | // 単体テスト。プライベート関数もテスト可能 56 | #[cfg(test)] 57 | mod tests { 58 | use crate::{ 59 | engine::do_matching, 60 | helper::{safe_add, SafeAdd}, 61 | }; 62 | 63 | #[test] 64 | fn test_safe_add() { 65 | let n: usize = 10; 66 | assert_eq!(Some(30), n.safe_add(&20)); 67 | 68 | let n: usize = !0; // 2^64 - 1 (64 bits CPU) 69 | assert_eq!(None, n.safe_add(&1)); 70 | 71 | let mut n: usize = 10; 72 | assert!(safe_add(&mut n, &20, || ()).is_ok()); 73 | 74 | let mut n: usize = !0; 75 | assert!(safe_add(&mut n, &1, || ()).is_err()); 76 | } 77 | 78 | #[test] 79 | fn test_matching() { 80 | // パースエラー 81 | assert!(do_matching("+b", "bbb", true).is_err()); 82 | assert!(do_matching("*b", "bbb", true).is_err()); 83 | assert!(do_matching("|b", "bbb", true).is_err()); 84 | assert!(do_matching("?b", "bbb", true).is_err()); 85 | 86 | // パース成功、マッチ成功 87 | assert!(do_matching("abc|def", "def", true).unwrap()); 88 | assert!(do_matching("(abc)*", "abcabc", true).unwrap()); 89 | assert!(do_matching("(ab|cd)+", "abcdcd", true).unwrap()); 90 | assert!(do_matching("abc?", "ab", true).unwrap()); 91 | assert!(do_matching("((((a*)*)*)*)", "aaaaaaaaa", true).unwrap()); 92 | assert!(do_matching("(a*)*b", "aaaaaaaaab", true).unwrap()); 93 | assert!(do_matching("(a*)*b", "b", true).unwrap()); 94 | assert!(do_matching("a**b", "aaaaaaaaab", true).unwrap()); 95 | assert!(do_matching("a**b", "b", true).unwrap()); 96 | 97 | // パース成功、マッチ失敗 98 | assert!(!do_matching("abc|def", "efa", true).unwrap()); 99 | assert!(!do_matching("(ab|cd)+", "", true).unwrap()); 100 | assert!(!do_matching("abc?", "acb", true).unwrap()); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /ch07/README.md: -------------------------------------------------------------------------------- 1 | # 第7章 シェル 2 | 3 | - [第7章 シェル](./zrsh/) 4 | -------------------------------------------------------------------------------- /ch07/zrsh/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zerosh" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | dirs = "4.0.0" 12 | rustyline = "10.0.0" 13 | nix = "0.25" 14 | signal-hook = "0.3.14" 15 | 16 | [profile.release] 17 | panic = "abort" 18 | 19 | [profile.dev] 20 | panic = "abort" 21 | -------------------------------------------------------------------------------- /ch07/zrsh/src/helper.rs: -------------------------------------------------------------------------------- 1 | pub type DynError = Box; 2 | -------------------------------------------------------------------------------- /ch07/zrsh/src/main.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | mod shell; 3 | 4 | use helper::DynError; 5 | 6 | const HISTORY_FILE: &str = ".zerosh_history"; 7 | 8 | fn main() -> Result<(), DynError> { 9 | let mut logfile = HISTORY_FILE; 10 | let mut home = dirs::home_dir(); 11 | if let Some(h) = &mut home { 12 | h.push(HISTORY_FILE); 13 | logfile = h.to_str().unwrap_or(HISTORY_FILE); 14 | } 15 | 16 | let sh = shell::Shell::new(logfile); 17 | sh.run()?; 18 | 19 | Ok(()) 20 | } 21 | -------------------------------------------------------------------------------- /ch07/zrsh/src/shell.rs: -------------------------------------------------------------------------------- 1 | use crate::helper::DynError; 2 | use nix::{ 3 | libc, 4 | sys::{ 5 | signal::{killpg, signal, SigHandler, Signal}, 6 | wait::{waitpid, WaitPidFlag, WaitStatus}, 7 | }, 8 | unistd::{self, dup2, execvp, fork, pipe, setpgid, tcgetpgrp, tcsetpgrp, ForkResult, Pid}, 9 | }; 10 | use rustyline::{error::ReadlineError, Editor}; 11 | use signal_hook::{consts::*, iterator::Signals}; 12 | use std::{ 13 | collections::{BTreeMap, HashMap, HashSet}, 14 | ffi::CString, 15 | mem::replace, 16 | path::PathBuf, 17 | process::exit, 18 | sync::mpsc::{channel, sync_channel, Receiver, Sender, SyncSender}, 19 | thread, 20 | }; 21 | 22 | /// ドロップ時にクロージャfを呼び出す型 23 | struct CleanUp 24 | where 25 | F: Fn(), 26 | { 27 | f: F, 28 | } 29 | 30 | impl Drop for CleanUp 31 | where 32 | F: Fn(), 33 | { 34 | fn drop(&mut self) { 35 | (self.f)() 36 | } 37 | } 38 | 39 | /// workerスレッドが受信するメッセージ 40 | enum WorkerMsg { 41 | Signal(i32), // シグナルを受信 42 | Cmd(String), // コマンド入力 43 | } 44 | 45 | /// mainスレッドが受信するメッセージ 46 | enum ShellMsg { 47 | Continue(i32), // シェルの読み込みを再開。i32は最後の終了コード 48 | Quit(i32), // シェルを終了。i32はシェルの終了コード 49 | } 50 | 51 | #[derive(Debug)] 52 | pub struct Shell { 53 | logfile: String, // ログファイル 54 | } 55 | 56 | impl Shell { 57 | pub fn new(logfile: &str) -> Self { 58 | Shell { 59 | logfile: logfile.to_string(), 60 | } 61 | } 62 | 63 | /// mainスレッド 64 | pub fn run(&self) -> Result<(), DynError> { 65 | // SIGTTOUを無視に設定しないと、SIGTSTPが配送される 66 | unsafe { signal(Signal::SIGTTOU, SigHandler::SigIgn).unwrap() }; 67 | 68 | let mut rl = Editor::<()>::new()?; 69 | if let Err(e) = rl.load_history(&self.logfile) { 70 | eprintln!("ZeroSh: ヒストリファイルの読み込みに失敗: {e}"); 71 | } 72 | 73 | // チャネルを生成し、signal_handlerとworkerスレッドを生成 74 | let (worker_tx, worker_rx) = channel(); 75 | let (shell_tx, shell_rx) = sync_channel(0); 76 | spawn_sig_handler(worker_tx.clone())?; 77 | Worker::new().spawn(worker_rx, shell_tx); 78 | 79 | let exit_val; // 終了コード 80 | let mut prev = 0; // 直前の終了コード 81 | loop { 82 | // 1行読み込んで、その行をworkerに送信 83 | let face = if prev == 0 { '\u{1F642}' } else { '\u{1F480}' }; 84 | match rl.readline(&format!("ZeroSh {face} %> ")) { 85 | Ok(line) => { 86 | let line_trimed = line.trim(); // 前後の空白文字を削除 87 | if line_trimed.is_empty() { 88 | continue; // 空のコマンドの場合は再読み込み 89 | } else { 90 | rl.add_history_entry(line_trimed); // ヒストリファイルに追加 91 | } 92 | 93 | worker_tx.send(WorkerMsg::Cmd(line)).unwrap(); // workerに送信 94 | match shell_rx.recv().unwrap() { 95 | ShellMsg::Continue(n) => prev = n, // 読み込み再開 96 | ShellMsg::Quit(n) => { 97 | // シェルを終了 98 | exit_val = n; 99 | break; 100 | } 101 | } 102 | } 103 | Err(ReadlineError::Interrupted) => eprintln!("ZeroSh: 終了はCtrl+D"), 104 | Err(ReadlineError::Eof) => { 105 | worker_tx.send(WorkerMsg::Cmd("exit".to_string())).unwrap(); 106 | match shell_rx.recv().unwrap() { 107 | ShellMsg::Quit(n) => { 108 | // シェルを終了 109 | exit_val = n; 110 | break; 111 | } 112 | _ => panic!("exitに失敗"), 113 | } 114 | } 115 | Err(e) => { 116 | eprintln!("ZeroSh: 読み込みエラー\n{e}"); 117 | exit_val = 1; 118 | break; 119 | } 120 | } 121 | } 122 | 123 | if let Err(e) = rl.save_history(&self.logfile) { 124 | eprintln!("ZeroSh: ヒストリファイルの書き込みに失敗: {e}"); 125 | } 126 | exit(exit_val); 127 | } 128 | } 129 | 130 | /// signal_handlerスレッド 131 | fn spawn_sig_handler(tx: Sender) -> Result<(), DynError> { 132 | let mut signals = Signals::new(&[SIGINT, SIGTSTP, SIGCHLD])?; 133 | thread::spawn(move || { 134 | for sig in signals.forever() { 135 | // シグナルを受信しworkerスレッドに送信 136 | tx.send(WorkerMsg::Signal(sig)).unwrap(); 137 | } 138 | }); 139 | 140 | Ok(()) 141 | } 142 | 143 | #[derive(Debug, PartialEq, Eq, Clone)] 144 | enum ProcState { 145 | Run, // 実行中 146 | Stop, // 停止中 147 | } 148 | 149 | #[derive(Debug, Clone)] 150 | struct ProcInfo { 151 | state: ProcState, // 実行状態 152 | pgid: Pid, // プロセスグループID 153 | } 154 | 155 | #[derive(Debug)] 156 | struct Worker { 157 | exit_val: i32, // 終了コード 158 | fg: Option, // フォアグラウンドのプロセスグループID 159 | 160 | // ジョブIDから(プロセスグループID, 実行コマンド)へのマップ 161 | jobs: BTreeMap, 162 | 163 | // プロセスグループIDから(ジョブID, プロセスID)へのマップ 164 | pgid_to_pids: HashMap)>, 165 | 166 | pid_to_info: HashMap, // プロセスIDからプロセスグループIDへのマップ 167 | shell_pgid: Pid, // シェルのプロセスグループID 168 | } 169 | 170 | impl Worker { 171 | fn new() -> Self { 172 | Worker { 173 | exit_val: 0, 174 | fg: None, // フォアグラウンドはシェル 175 | jobs: BTreeMap::new(), 176 | pgid_to_pids: HashMap::new(), 177 | pid_to_info: HashMap::new(), 178 | 179 | // シェルのプロセスグループIDを取得 180 | shell_pgid: tcgetpgrp(libc::STDIN_FILENO).unwrap(), 181 | } 182 | } 183 | 184 | /// workerスレッドを起動 185 | fn spawn(mut self, worker_rx: Receiver, shell_tx: SyncSender) { 186 | thread::spawn(move || { 187 | for msg in worker_rx.iter() { 188 | match msg { 189 | WorkerMsg::Cmd(line) => { 190 | match parse_cmd(&line) { 191 | Ok(cmd) => { 192 | if self.built_in_cmd(&cmd, &shell_tx) { 193 | // 組み込みコマンドならworker_rxから受信 194 | continue; 195 | } 196 | 197 | if !self.spawn_child(&line, &cmd) { 198 | // 子プロセス生成に失敗した場合シェルからの入力を再開 199 | shell_tx.send(ShellMsg::Continue(self.exit_val)).unwrap(); 200 | } 201 | } 202 | Err(e) => { 203 | eprintln!("ZeroSh: {e}"); 204 | shell_tx.send(ShellMsg::Continue(self.exit_val)).unwrap(); 205 | } 206 | } 207 | } 208 | WorkerMsg::Signal(SIGCHLD) => { 209 | self.wait_child(&shell_tx); // 子プロセスの状態変化管理 210 | } 211 | _ => (), // 無視 212 | } 213 | } 214 | }); 215 | } 216 | 217 | /// 子プロセスの状態変化を管理 218 | fn wait_child(&mut self, shell_tx: &SyncSender) { 219 | // WUNTRACED: 子プロセスの停止 220 | // WNOHANG: ブロックしない 221 | // WCONTINUED: 実行再開時 222 | let flag = Some(WaitPidFlag::WUNTRACED | WaitPidFlag::WNOHANG | WaitPidFlag::WCONTINUED); 223 | 224 | loop { 225 | match syscall(|| waitpid(Pid::from_raw(-1), flag)) { 226 | Ok(WaitStatus::Exited(pid, status)) => { 227 | // プロセスが終了 228 | self.exit_val = status; // 終了コードを保存 229 | self.process_term(pid, shell_tx); 230 | } 231 | Ok(WaitStatus::Signaled(pid, sig, core)) => { 232 | // プロセスがシグナルにより終了 233 | eprintln!( 234 | "\nZeroSh: 子プロセスがシグナルにより終了{}: pid = {pid}, signal = {sig}", 235 | if core { "(コアダンプ)" } else { "" } 236 | ); 237 | self.exit_val = sig as i32 + 128; // 終了コードを保存 238 | self.process_term(pid, shell_tx); 239 | } 240 | // プロセスが停止 241 | Ok(WaitStatus::Stopped(pid, _sig)) => self.process_stop(pid, shell_tx), 242 | // プロセスが実行再開 243 | Ok(WaitStatus::Continued(pid)) => self.process_continue(pid), 244 | Ok(WaitStatus::StillAlive) => return, // waitすべき子プロセスはいない 245 | Err(nix::Error::ECHILD) => return, // 子プロセスはいない 246 | Err(e) => { 247 | eprintln!("\nZeroSh: waitが失敗: {e}"); 248 | exit(1); 249 | } 250 | #[cfg(any(target_os = "linux", target_os = "android"))] 251 | Ok(WaitStatus::PtraceEvent(pid, _, _) | WaitStatus::PtraceSyscall(pid)) => { 252 | self.process_stop(pid, shell_tx) 253 | } 254 | } 255 | } 256 | } 257 | 258 | /// プロセスの再開処理 259 | fn process_continue(&mut self, pid: Pid) { 260 | self.set_pid_state(pid, ProcState::Run); 261 | } 262 | 263 | /// プロセスの停止処理 264 | fn process_stop(&mut self, pid: Pid, shell_tx: &SyncSender) { 265 | self.set_pid_state(pid, ProcState::Stop); // プロセスを停止中に設定 266 | let pgid = self.pid_to_info.get(&pid).unwrap().pgid; // プロセスグループIDを取得 267 | let job_id = self.pgid_to_pids.get(&pgid).unwrap().0; // ジョブIDを取得 268 | self.manage_job(job_id, pgid, shell_tx); // 必要ならフォアグラウンドプロセスをシェルに設定 269 | } 270 | 271 | /// プロセスの終了処理 272 | fn process_term(&mut self, pid: Pid, shell_tx: &SyncSender) { 273 | // プロセスの情報を削除し、必要ならフォアグラウンドプロセスをシェルに設定 274 | if let Some((job_id, pgid)) = self.remove_pid(pid) { 275 | self.manage_job(job_id, pgid, shell_tx); 276 | } 277 | } 278 | 279 | /// プロセスの実行状態を設定し、以前の状態を返す。 280 | /// pidが存在しないプロセスの場合はNoneを返す。 281 | fn set_pid_state(&mut self, pid: Pid, state: ProcState) -> Option { 282 | let info = self.pid_to_info.get_mut(&pid)?; 283 | Some(replace(&mut info.state, state)) 284 | } 285 | 286 | /// プロセスの情報を削除し、削除できた場合プロセスの所属する。 287 | /// (ジョブID, プロセスグループID)を返す。 288 | /// 存在しないプロセスの場合はNoneを返す。 289 | fn remove_pid(&mut self, pid: Pid) -> Option<(usize, Pid)> { 290 | let pgid = self.pid_to_info.get(&pid)?.pgid; // プロセスグループIDを取得 291 | let it = self.pgid_to_pids.get_mut(&pgid)?; 292 | it.1.remove(&pid); // プロセスグループからpidを削除 293 | let job_id = it.0; // ジョブIDを取得 294 | Some((job_id, pgid)) 295 | } 296 | 297 | /// ジョブ情報を削除し、関連するプロセスグループの情報も削除 298 | fn remove_job(&mut self, job_id: usize) { 299 | if let Some((pgid, _)) = self.jobs.remove(&job_id) { 300 | if let Some((_, pids)) = self.pgid_to_pids.remove(&pgid) { 301 | assert!(pids.is_empty()); // ジョブを削除するときはプロセスグループは空のはず 302 | } 303 | } 304 | } 305 | 306 | /// 空のプロセスグループなら真 307 | fn is_group_empty(&self, pgid: Pid) -> bool { 308 | self.pgid_to_pids.get(&pgid).unwrap().1.is_empty() 309 | } 310 | 311 | /// プロセスグループのプロセスすべてが停止中なら真 312 | fn is_group_stop(&self, pgid: Pid) -> Option { 313 | for pid in self.pgid_to_pids.get(&pgid)?.1.iter() { 314 | if self.pid_to_info.get(pid).unwrap().state == ProcState::Run { 315 | return Some(false); 316 | } 317 | } 318 | Some(true) 319 | } 320 | 321 | /// 新たなジョブIDを取得 322 | fn get_new_job_id(&self) -> Option { 323 | for i in 0..=usize::MAX { 324 | if !self.jobs.contains_key(&i) { 325 | return Some(i); 326 | } 327 | } 328 | None 329 | } 330 | 331 | /// 新たなジョブ情報を追加 332 | /// 333 | /// - job_id: ジョブID 334 | /// - pgid: プロセスグループID 335 | /// - pids: プロセス 336 | fn insert_job(&mut self, job_id: usize, pgid: Pid, pids: HashMap, line: &str) { 337 | assert!(!self.jobs.contains_key(&job_id)); 338 | self.jobs.insert(job_id, (pgid, line.to_string())); // ジョブ情報を追加 339 | 340 | let mut procs = HashSet::new(); // pgid_to_pidsへ追加するプロセス 341 | for (pid, info) in pids { 342 | procs.insert(pid); 343 | 344 | assert!(!self.pid_to_info.contains_key(&pid)); 345 | self.pid_to_info.insert(pid, info); // プロセスの情報を追加 346 | } 347 | 348 | assert!(!self.pgid_to_pids.contains_key(&pgid)); 349 | self.pgid_to_pids.insert(pgid, (job_id, procs)); // プロセスグループの情報を追加 350 | } 351 | 352 | /// シェルをフォアグラウンドに設定 353 | fn set_shell_fg(&mut self, shell_tx: &SyncSender) { 354 | self.fg = None; 355 | tcsetpgrp(libc::STDIN_FILENO, self.shell_pgid).unwrap(); 356 | shell_tx.send(ShellMsg::Continue(self.exit_val)).unwrap(); 357 | } 358 | 359 | /// ジョブの管理。引数には変化のあったジョブとプロセスグループを指定 360 | /// 361 | /// - フォアグラウンドプロセスが空の場合、シェルをフォアグラウンドに設定 362 | /// - フォアグラウンドプロセスがすべて停止中の場合、シェルをフォアグラウンドに設定 363 | fn manage_job(&mut self, job_id: usize, pgid: Pid, shell_tx: &SyncSender) { 364 | let is_fg = self.fg.map_or(false, |x| pgid == x); // フォアグラウンドのプロセスか? 365 | let line = &self.jobs.get(&job_id).unwrap().1; 366 | if is_fg { 367 | // 状態が変化したプロセスはフォアグラウンド 368 | if self.is_group_empty(pgid) { 369 | // フォアグラウンドプロセスが空の場合、 370 | // ジョブ情報を削除しシェルをフォアグラウンドに設定 371 | eprintln!("[{job_id}] 終了\t{line}"); 372 | self.remove_job(job_id); 373 | self.set_shell_fg(shell_tx); 374 | } else if self.is_group_stop(pgid).unwrap() { 375 | // フォアグラウンドプロセスがすべて停止中の場合、シェルをフォアグラウンドに設定 376 | eprintln!("\n[{job_id}] 停止\t{line}"); 377 | self.set_shell_fg(shell_tx); 378 | } 379 | } else { 380 | // プロセスグループが空の場合、ジョブ情報を削除 381 | if self.is_group_empty(pgid) { 382 | eprintln!("\n[{job_id}] 終了\t{line}"); 383 | self.remove_job(job_id); 384 | } 385 | } 386 | } 387 | 388 | /// 子プロセスを生成。失敗した場合はシェルからの入力を再開させる必要あり 389 | fn spawn_child(&mut self, line: &str, cmd: &[(&str, Vec<&str>)]) -> bool { 390 | assert_ne!(cmd.len(), 0); // コマンドが空でないか検査 391 | 392 | // ジョブIDを取得 393 | let job_id = if let Some(id) = self.get_new_job_id() { 394 | id 395 | } else { 396 | eprintln!("ZeroSh: 管理可能なジョブの最大値に到達"); 397 | return false; 398 | }; 399 | 400 | if cmd.len() > 2 { 401 | eprintln!("ZeroSh: 三つ以上のコマンドによるパイプはサポートしていません"); 402 | return false; 403 | } 404 | 405 | let mut input = None; // 二つめのプロセスの標準入力 406 | let mut output = None; // 一つめのプロセスの標準出力 407 | if cmd.len() == 2 { 408 | // パイプを作成 409 | let p = pipe().unwrap(); 410 | input = Some(p.0); 411 | output = Some(p.1); 412 | } 413 | 414 | // パイプを閉じる関数を定義 415 | let cleanup_pipe = CleanUp { 416 | f: || { 417 | if let Some(fd) = input { 418 | syscall(|| unistd::close(fd)).unwrap(); 419 | } 420 | if let Some(fd) = output { 421 | syscall(|| unistd::close(fd)).unwrap(); 422 | } 423 | }, 424 | }; 425 | 426 | let pgid; 427 | // 一つめのプロセスを生成 428 | match fork_exec(Pid::from_raw(0), cmd[0].0, &cmd[0].1, None, output, input) { 429 | Ok(child) => { 430 | pgid = child; 431 | } 432 | Err(e) => { 433 | eprintln!("ZeroSh: プロセス生成エラー: {e}"); 434 | return false; 435 | } 436 | } 437 | 438 | // プロセス、ジョブの情報を追加 439 | let info = ProcInfo { 440 | state: ProcState::Run, 441 | pgid, 442 | }; 443 | let mut pids = HashMap::new(); 444 | pids.insert(pgid, info.clone()); // 一つめのプロセスの情報 445 | 446 | // 二つめのプロセスを生成 447 | if cmd.len() == 2 { 448 | match fork_exec(pgid, cmd[1].0, &cmd[1].1, input, None, output) { 449 | Ok(child) => { 450 | pids.insert(child, info); 451 | } 452 | Err(e) => { 453 | eprintln!("ZeroSh: プロセス生成エラー: {e}"); 454 | return false; 455 | } 456 | } 457 | } 458 | 459 | std::mem::drop(cleanup_pipe); // パイプをクローズ 460 | 461 | // ジョブ情報を追加し、子プロセスをフォアグラウンドに 462 | self.fg = Some(pgid); 463 | self.insert_job(job_id, pgid, pids, line); 464 | tcsetpgrp(libc::STDIN_FILENO, pgid).unwrap(); 465 | 466 | true 467 | } 468 | 469 | /// 組み込みコマンドの場合はtrueを返す 470 | fn built_in_cmd(&mut self, cmd: &[(&str, Vec<&str>)], shell_tx: &SyncSender) -> bool { 471 | if cmd.len() > 1 { 472 | return false; // 組み込みコマンドのパイプは非対応なのでエラー 473 | } 474 | 475 | match cmd[0].0 { 476 | "exit" => self.run_exit(&cmd[0].1, shell_tx), 477 | "jobs" => self.run_jobs(shell_tx), 478 | "fg" => self.run_fg(&cmd[0].1, shell_tx), 479 | "cd" => self.run_cd(&cmd[0].1, shell_tx), 480 | _ => false, 481 | } 482 | } 483 | 484 | /// カレントディレクトリを変更。引数がない場合は、ホームディレクトリに移動。第2引数以降は無視 485 | fn run_cd(&mut self, args: &[&str], shell_tx: &SyncSender) -> bool { 486 | let path = if args.len() == 1 { 487 | // 引数が指定されていない場合、ホームディレクトリか/へ移動 488 | dirs::home_dir() 489 | .or_else(|| Some(PathBuf::from("/"))) 490 | .unwrap() 491 | } else { 492 | PathBuf::from(args[1]) 493 | }; 494 | 495 | // カレントディレクトリを変更 496 | if let Err(e) = std::env::set_current_dir(&path) { 497 | self.exit_val = 1; // 失敗 498 | eprintln!("cdに失敗: {e}"); 499 | } else { 500 | self.exit_val = 0; // 成功 501 | } 502 | 503 | shell_tx.send(ShellMsg::Continue(self.exit_val)).unwrap(); 504 | true 505 | } 506 | 507 | /// exitコマンドを実行 508 | /// 509 | /// 第1引数が指定された場合、それを終了コードとしてシェルを終了。 510 | /// 引数がない場合は、最後に終了したプロセスの終了コードとしてシェルを終了。 511 | fn run_exit(&mut self, args: &[&str], shell_tx: &SyncSender) -> bool { 512 | // 実行中のジョブがある場合は終了しない 513 | if !self.jobs.is_empty() { 514 | eprintln!("ジョブが実行中なので終了できません"); 515 | self.exit_val = 1; // 失敗 516 | shell_tx.send(ShellMsg::Continue(self.exit_val)).unwrap(); // シェルを再開 517 | return true; 518 | } 519 | 520 | // 終了コードを取得 521 | let exit_val = if let Some(s) = args.get(1) { 522 | if let Ok(n) = (*s).parse::() { 523 | n 524 | } else { 525 | // 終了コードか整数ではない 526 | eprintln!("{s}は不正な引数です"); 527 | self.exit_val = 1; // 失敗 528 | shell_tx.send(ShellMsg::Continue(self.exit_val)).unwrap(); // シェルを再開 529 | return true; 530 | } 531 | } else { 532 | self.exit_val 533 | }; 534 | 535 | shell_tx.send(ShellMsg::Quit(exit_val)).unwrap(); // シェルを終了 536 | true 537 | } 538 | 539 | /// jobsコマンドを実行 540 | fn run_jobs(&mut self, shell_tx: &SyncSender) -> bool { 541 | for (job_id, (pgid, cmd)) in &self.jobs { 542 | let state = if self.is_group_stop(*pgid).unwrap() { 543 | "停止中" 544 | } else { 545 | "実行中" 546 | }; 547 | println!("[{job_id}] {state}\t{cmd}") 548 | } 549 | self.exit_val = 0; // 成功 550 | shell_tx.send(ShellMsg::Continue(self.exit_val)).unwrap(); // シェルを再開 551 | true 552 | } 553 | 554 | /// fgコマンドを実行 555 | fn run_fg(&mut self, args: &[&str], shell_tx: &SyncSender) -> bool { 556 | self.exit_val = 1; // とりあえず失敗に設定 557 | 558 | // 引数をチェック 559 | if args.len() < 2 { 560 | eprintln!("usage: fg 数字"); 561 | shell_tx.send(ShellMsg::Continue(self.exit_val)).unwrap(); // シェルを再開 562 | return true; 563 | } 564 | 565 | // ジョブIDを取得 566 | if let Ok(n) = args[1].parse::() { 567 | if let Some((pgid, cmd)) = self.jobs.get(&n) { 568 | eprintln!("[{n}] 再開\t{cmd}"); 569 | 570 | // フォアグラウンドプロセスに設定 571 | self.fg = Some(*pgid); 572 | tcsetpgrp(libc::STDIN_FILENO, *pgid).unwrap(); 573 | 574 | // ジョブの実行を再開 575 | killpg(*pgid, Signal::SIGCONT).unwrap(); 576 | return true; 577 | } 578 | } 579 | 580 | // 失敗 581 | eprintln!("{}というジョブは見つかりませんでした", args[1]); 582 | shell_tx.send(ShellMsg::Continue(self.exit_val)).unwrap(); // シェルを再開 583 | true 584 | } 585 | } 586 | 587 | /// システムコール呼び出しのラッパ。EINTRならリトライ 588 | fn syscall(f: F) -> Result 589 | where 590 | F: Fn() -> Result, 591 | { 592 | loop { 593 | match f() { 594 | Err(nix::Error::EINTR) => (), // リトライ 595 | result => return result, 596 | } 597 | } 598 | } 599 | 600 | /// プロセスグループIDを指定してfork & exec 601 | /// pgidが0の場合は子プロセスのPIDが、プロセスグループIDとなる 602 | /// 603 | /// - inputがSome(fd)の場合は、標準入力をfdと設定 604 | /// - outputSome(fd)の場合は、標準出力をfdと設定 605 | /// - fd_closeがSome(fd)の場合は、fork後にfdをクローズ 606 | fn fork_exec( 607 | pgid: Pid, 608 | filename: &str, 609 | args: &[&str], 610 | input: Option, 611 | output: Option, 612 | fd_close: Option, 613 | ) -> Result { 614 | let filename = CString::new(filename).unwrap(); 615 | let args: Vec = args.iter().map(|s| CString::new(*s).unwrap()).collect(); 616 | 617 | match syscall(|| unsafe { fork() })? { 618 | ForkResult::Parent { child, .. } => { 619 | // 子プロセスのプロセスグループIDをpgidに設定 620 | setpgid(child, pgid).unwrap(); 621 | Ok(child) 622 | } 623 | ForkResult::Child => { 624 | // 子プロセスのプロセスグループIDをpgidに設定 625 | setpgid(Pid::from_raw(0), pgid).unwrap(); 626 | 627 | if let Some(fd) = fd_close { 628 | syscall(|| unistd::close(fd)).unwrap(); 629 | } 630 | 631 | // 標準入出力を設定 632 | if let Some(infd) = input { 633 | syscall(|| dup2(infd, libc::STDIN_FILENO)).unwrap(); 634 | } 635 | if let Some(outfd) = output { 636 | syscall(|| dup2(outfd, libc::STDOUT_FILENO)).unwrap(); 637 | } 638 | 639 | // signal_hookで利用されるUNIXドメインソケットとpipeをクローズ 640 | for i in 3..=6 { 641 | let _ = syscall(|| unistd::close(i)); 642 | } 643 | 644 | // 実行ファイルをメモリに読み込み 645 | match execvp(&filename, &args) { 646 | Err(_) => { 647 | unistd::write(libc::STDERR_FILENO, "不明なコマンドを実行\n".as_bytes()).ok(); 648 | exit(1); 649 | } 650 | Ok(_) => unreachable!(), 651 | } 652 | } 653 | } 654 | } 655 | 656 | /// スペースでsplit 657 | fn parse_cmd_one(line: &str) -> Result<(&str, Vec<&str>), DynError> { 658 | let cmd: Vec<&str> = line.split(' ').collect(); 659 | let mut filename = ""; 660 | let mut args = Vec::new(); // 引数を生成。ただし、空の文字列filterで取り除く 661 | for (n, s) in cmd.iter().filter(|s| !s.is_empty()).enumerate() { 662 | if n == 0 { 663 | filename = *s; 664 | } 665 | args.push(*s); 666 | } 667 | 668 | if filename.is_empty() { 669 | Err("空のコマンド".into()) 670 | } else { 671 | Ok((filename, args)) 672 | } 673 | } 674 | 675 | /// パイプでsplit 676 | fn parse_pipe(line: &str) -> Vec<&str> { 677 | let cmds: Vec<&str> = line.split('|').collect(); 678 | cmds 679 | } 680 | 681 | type CmdResult<'a> = Result)>, DynError>; 682 | 683 | /// コマンドをパースし、実行ファイルと引数にわける。 684 | /// また、パイプの場合は複数のコマンドにわけてVecに保存。 685 | /// 686 | /// # 例1 687 | /// 688 | /// 入力"echo abc def"に対して、`Ok(vec![("echo", vec!["echo", "abc", "def"])])` 689 | /// を返す。 690 | /// 691 | /// # 例2 692 | /// 693 | /// 入力"echo abc | less"に対して、`Ok(vec![("echo", vec!["echo", "abc"]), ("less", vec!["less"])])` 694 | /// を返す。 695 | fn parse_cmd(line: &str) -> CmdResult { 696 | let cmds = parse_pipe(line); 697 | if cmds.is_empty() { 698 | return Err("空のコマンド".into()); 699 | } 700 | 701 | let mut result = Vec::new(); 702 | for cmd in cmds { 703 | let (filename, args) = parse_cmd_one(cmd)?; 704 | result.push((filename, args)); 705 | } 706 | 707 | Ok(result) 708 | } 709 | -------------------------------------------------------------------------------- /ch08/README.md: -------------------------------------------------------------------------------- 1 | # 第8章 デバッガ 2 | 3 | - [8.1 GDB アッカーマン関数](./ackerman/) 4 | - [8.2 割り込みとシグナル 実験用コード](./dbg_target/) 5 | - [8.3 ZDbgの実装](./zdbg/) 6 | -------------------------------------------------------------------------------- /ch08/ackerman/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ackerman" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | num = "0.4.0" -------------------------------------------------------------------------------- /ch08/ackerman/src/ackerman_tailcall.rs: -------------------------------------------------------------------------------- 1 | //! 末尾呼び出し版のアッカーマン関数実装 2 | 3 | use num::{BigUint, One, Zero}; 4 | 5 | #[derive(Debug, Clone)] 6 | pub enum N { 7 | VAL(BigUint), 8 | A(usize, BigUint), 9 | } 10 | 11 | impl N { 12 | fn get(self) -> BigUint { 13 | match self { 14 | N::VAL(n) => n, 15 | N::A(m, n) => ackerman_tail(m, N::VAL(n)), 16 | } 17 | } 18 | } 19 | 20 | pub fn ackerman(m: usize, n: BigUint) -> BigUint { 21 | ackerman_tail(m, N::VAL(n)) 22 | } 23 | 24 | fn ackerman_tail(m: usize, n: N) -> BigUint { 25 | let one: BigUint = One::one(); 26 | let zero: BigUint = Zero::zero(); 27 | if m == 0 { 28 | n.get() + one // n + 1 29 | } else if n.clone().get() == zero { 30 | ackerman_tail(m - 1, N::VAL(one)) 31 | } else { 32 | let n_dec = n.get() - one; // n - 1 33 | ackerman_tail(m - 1, N::A(m, n_dec)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ch08/ackerman/src/main.rs: -------------------------------------------------------------------------------- 1 | //! アッカーマン関数 2 | 3 | use num::{BigUint, FromPrimitive, One, Zero}; 4 | 5 | const M: usize = 4; 6 | const N: usize = 4; 7 | 8 | fn main() { 9 | let m = M; 10 | let n = BigUint::from_usize(N).unwrap(); 11 | let a = ackerman(m, n.clone()); 12 | println!("ackerman({M}, {N}) = {a}"); 13 | } 14 | 15 | fn ackerman(m: usize, n: BigUint) -> BigUint { 16 | let one: BigUint = One::one(); 17 | let zero: BigUint = Zero::zero(); 18 | if m == 0 { 19 | n + one 20 | } else if n == zero { 21 | ackerman(m - 1, one) 22 | } else { 23 | ackerman(m - 1, ackerman(m, n - one)) 24 | } 25 | } 26 | 27 | #[cfg(test)] 28 | mod tests { 29 | use num::{BigUint, FromPrimitive, ToPrimitive}; 30 | 31 | #[test] 32 | fn test_ackerman() { 33 | let a = crate::ackerman(3, BigUint::from_usize(3).unwrap()); 34 | assert_eq!(a.to_usize().unwrap(), 61); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ch08/dbg_target/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = ["-Crelocation-model=dynamic-no-pic"] -------------------------------------------------------------------------------- /ch08/dbg_target/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dbg_target" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | nix = "0.25" 12 | -------------------------------------------------------------------------------- /ch08/dbg_target/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::arch::asm; 2 | 3 | use nix::{ 4 | sys::signal::{kill, Signal}, 5 | unistd::getpid, 6 | }; 7 | 8 | fn main() { 9 | println!("int 3"); 10 | unsafe { asm!("int 3") }; 11 | 12 | println!("kill -SIGTRAP"); 13 | let pid = getpid(); 14 | kill(pid, Signal::SIGTRAP).unwrap(); 15 | 16 | for i in 0..3 { 17 | unsafe { asm!("nop") }; 18 | println!("i = {i}"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ch08/zdbg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "zdbg" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | rustyline = "10.0.0" 12 | nix = "0.25" 13 | -------------------------------------------------------------------------------- /ch08/zdbg/src/dbg.rs: -------------------------------------------------------------------------------- 1 | use crate::helper::DynError; 2 | use nix::{ 3 | libc::user_regs_struct, 4 | sys::{ 5 | personality::{self, Persona}, 6 | ptrace, 7 | wait::{waitpid, WaitStatus}, 8 | }, 9 | unistd::{execvp, fork, ForkResult, Pid}, 10 | }; 11 | use std::ffi::{c_void, CString}; 12 | 13 | /// デバッガ内の情報 14 | pub struct DbgInfo { 15 | pid: Pid, 16 | brk_addr: Option<*mut c_void>, // ブレークポイントのアドレス 17 | brk_val: i64, // ブレークポイントを設定したメモリの元の値 18 | filename: String, // 実行ファイル 19 | } 20 | 21 | /// デバッガ 22 | /// ZDbgは子プロセスを実行中 23 | /// ZDbgは子プロセスは実行していない 24 | pub struct ZDbg { 25 | info: Box, 26 | _state: T, 27 | } 28 | 29 | /// デバッガの実装 30 | pub struct Running; // 実行中 31 | pub struct NotRunning; // 実行していない 32 | 33 | /// デバッガの実装の列挙型表現。Exitの場合終了 34 | pub enum State { 35 | Running(ZDbg), 36 | NotRunning(ZDbg), 37 | Exit, 38 | } 39 | 40 | /// RunningとNotRunningで共通の実装 41 | impl ZDbg { 42 | /// ブレークポイントのアドレスを設定する関数。子プロセスのメモリ上には反映しない。 43 | /// アドレス設定に成功した場合はtrueを返す 44 | fn set_break_addr(&mut self, cmd: &[&str]) -> bool { 45 | if self.info.brk_addr.is_some() { 46 | eprintln!( 47 | "<<ブレークポイントは設定済みです : Addr = {:p}>>", 48 | self.info.brk_addr.unwrap() 49 | ); 50 | false 51 | } else if let Some(addr) = get_break_addr(cmd) { 52 | self.info.brk_addr = Some(addr); // ブレークポイントのアドレスを保存 53 | true 54 | } else { 55 | false 56 | } 57 | } 58 | 59 | /// 共通のコマンドを実行 60 | fn do_cmd_common(&self, cmd: &[&str]) { 61 | match cmd[0] { 62 | "help" | "h" => do_help(), 63 | _ => (), 64 | } 65 | } 66 | } 67 | 68 | /// NotRunning時に呼び出し可能なメソッド 69 | impl ZDbg { 70 | pub fn new(filename: String) -> Self { 71 | ZDbg { 72 | info: Box::new(DbgInfo { 73 | pid: Pid::from_raw(0), 74 | brk_addr: None, 75 | brk_val: 0, 76 | filename, 77 | }), 78 | _state: NotRunning, 79 | } 80 | } 81 | 82 | /// ブレークポイントを設定 83 | fn do_break(&mut self, cmd: &[&str]) -> bool { 84 | self.set_break_addr(cmd) 85 | } 86 | 87 | /// 子プロセスを生成し、成功した場合はRunning状態に遷移 88 | fn do_run(mut self, cmd: &[&str]) -> Result { 89 | // 子プロセスに渡すコマンドライン引数 90 | let args: Vec = cmd.iter().map(|s| CString::new(*s).unwrap()).collect(); 91 | 92 | match unsafe { fork()? } { 93 | ForkResult::Child => { 94 | // ASLRを無効に 95 | let p = personality::get().unwrap(); 96 | personality::set(p | Persona::ADDR_NO_RANDOMIZE).unwrap(); 97 | ptrace::traceme().unwrap(); 98 | 99 | // exec 100 | execvp(&CString::new(self.info.filename.as_str()).unwrap(), &args).unwrap(); 101 | unreachable!(); 102 | } 103 | ForkResult::Parent { child, .. } => match waitpid(child, None)? { 104 | WaitStatus::Stopped(..) => { 105 | println!("<<子プロセスの実行に成功しました : PID = {child}>>"); 106 | self.info.pid = child; 107 | let mut dbg = ZDbg:: { 108 | info: self.info, 109 | _state: Running, 110 | }; 111 | dbg.set_break()?; // ブレークポイントを設定 112 | dbg.do_continue() 113 | } 114 | WaitStatus::Exited(..) | WaitStatus::Signaled(..) => { 115 | Err("子プロセスの実行に失敗しました".into()) 116 | } 117 | _ => Err("子プロセスが不正な状態です".into()), 118 | }, 119 | } 120 | } 121 | 122 | pub fn do_cmd(mut self, cmd: &[&str]) -> Result { 123 | if cmd.is_empty() { 124 | return Ok(State::NotRunning(self)); 125 | } 126 | 127 | match cmd[0] { 128 | "run" | "r" => return self.do_run(cmd), 129 | "break" | "b" => { 130 | self.do_break(cmd); 131 | } 132 | "exit" => return Ok(State::Exit), 133 | "continue" | "c" | "stepi" | "s" | "registers" | "regs" => { 134 | eprintln!("<<ターゲットを実行していません。runで実行してください>>") 135 | } 136 | _ => self.do_cmd_common(cmd), 137 | } 138 | 139 | Ok(State::NotRunning(self)) 140 | } 141 | } 142 | 143 | /// Running時に呼び出し可能なメソッド 144 | impl ZDbg { 145 | pub fn do_cmd(mut self, cmd: &[&str]) -> Result { 146 | if cmd.is_empty() { 147 | return Ok(State::Running(self)); 148 | } 149 | 150 | match cmd[0] { 151 | "break" | "b" => self.do_break(cmd)?, 152 | "continue" | "c" => return self.do_continue(), 153 | "registers" | "regs" => { 154 | let regs = ptrace::getregs(self.info.pid)?; 155 | print_regs(®s); 156 | } 157 | "stepi" | "s" => return self.do_stepi(), 158 | "run" | "r" => eprintln!("<<既に実行中です>>"), 159 | "exit" => { 160 | self.do_exit()?; 161 | return Ok(State::Exit); 162 | } 163 | _ => self.do_cmd_common(cmd), 164 | } 165 | 166 | Ok(State::Running(self)) 167 | } 168 | 169 | /// exitを実行。実行中のプロセスはkill 170 | fn do_exit(self) -> Result<(), DynError> { 171 | loop { 172 | ptrace::kill(self.info.pid)?; 173 | match waitpid(self.info.pid, None)? { 174 | WaitStatus::Exited(..) | WaitStatus::Signaled(..) => return Ok(()), 175 | _ => (), 176 | } 177 | } 178 | } 179 | 180 | /// ブレークポイントを実際に設定 181 | /// つまり、該当アドレスのメモリを"int 3" = 0xccに設定 182 | fn set_break(&mut self) -> Result<(), DynError> { 183 | let addr = if let Some(addr) = self.info.brk_addr { 184 | addr 185 | } else { 186 | return Ok(()); 187 | }; 188 | 189 | // ブレークするアドレスにあるメモリ上の値を取得 190 | let val = match ptrace::read(self.info.pid, addr) { 191 | Ok(val) => val, 192 | Err(e) => { 193 | eprintln!("<>", addr); 194 | return Ok(()); 195 | } 196 | }; 197 | 198 | // メモリの値を表示する補助関数 199 | fn print_val(addr: usize, val: i64) { 200 | print!("{:x}:", addr); 201 | for n in (0..8).map(|n| ((val >> (n * 8)) & 0xff) as u8) { 202 | print!(" {:x}", n); 203 | } 204 | } 205 | 206 | println!("<<以下のようにメモリを書き換えます>>"); 207 | print!("<>"); 210 | 211 | let val_int3 = (val & !0xff) | 0xcc; // "int 3"に設定 212 | print!("<>"); 215 | 216 | // "int 3"をメモリに書き込み 217 | match unsafe { ptrace::write(self.info.pid, addr, val_int3 as *mut c_void) } { 218 | Ok(_) => { 219 | self.info.brk_addr = Some(addr); 220 | self.info.brk_val = val; // 元の値を保持 221 | } 222 | Err(e) => { 223 | eprintln!("<>", addr); 224 | } 225 | } 226 | 227 | Ok(()) 228 | } 229 | 230 | /// breakを実行 231 | fn do_break(&mut self, cmd: &[&str]) -> Result<(), DynError> { 232 | if self.set_break_addr(cmd) { 233 | self.set_break()?; 234 | } 235 | Ok(()) 236 | } 237 | 238 | /// stepiを実行。機械語レベルで1行実行 239 | fn do_stepi(self) -> Result { 240 | let regs = ptrace::getregs(self.info.pid)?; 241 | if Some((regs.rip) as *mut c_void) == self.info.brk_addr { 242 | // 次の実行先がブレークポイントの場合、 243 | // 先に、0xccに書き換えたメモリを元の値に戻してから実行する必要あり 244 | unsafe { 245 | ptrace::write( 246 | self.info.pid, 247 | self.info.brk_addr.unwrap(), 248 | self.info.brk_val as *mut c_void, 249 | )? 250 | }; 251 | self.step_and_break() 252 | } else { 253 | ptrace::step(self.info.pid, None)?; 254 | self.wait_child() 255 | } 256 | } 257 | 258 | /// ブレークポイントで停止していた場合は 259 | /// 1ステップ実行しブレークポイントを再設定 260 | fn step_and_break(mut self) -> Result { 261 | let regs = ptrace::getregs(self.info.pid)?; 262 | if Some((regs.rip) as *mut c_void) == self.info.brk_addr { 263 | ptrace::step(self.info.pid, None)?; // 1ステップ実行 264 | match waitpid(self.info.pid, None)? { 265 | WaitStatus::Exited(..) | WaitStatus::Signaled(..) => { 266 | println!("<<子プロセスが終了しました>>"); 267 | return Ok(State::NotRunning(ZDbg:: { 268 | info: self.info, 269 | _state: NotRunning, 270 | })); 271 | } 272 | _ => (), 273 | } 274 | self.set_break()?; // 再度ブレークポイントを設定 275 | } 276 | 277 | Ok(State::Running(self)) 278 | } 279 | 280 | /// continueを実行 281 | fn do_continue(self) -> Result { 282 | // ブレークポイントで停止していた場合は1ステップ実行後再設定 283 | match self.step_and_break()? { 284 | State::Running(r) => { 285 | // 実行再開 286 | ptrace::cont(r.info.pid, None)?; 287 | r.wait_child() 288 | } 289 | n => Ok(n), 290 | } 291 | } 292 | 293 | /// 子プロセスをwait。子プロセスが終了した場合はNotRunning状態に遷移 294 | fn wait_child(self) -> Result { 295 | match waitpid(self.info.pid, None)? { 296 | WaitStatus::Exited(..) | WaitStatus::Signaled(..) => { 297 | println!("<<子プロセスが終了しました>>"); 298 | let not_run = ZDbg:: { 299 | info: self.info, 300 | _state: NotRunning, 301 | }; 302 | Ok(State::NotRunning(not_run)) 303 | } 304 | WaitStatus::Stopped(..) => { 305 | let mut regs = ptrace::getregs(self.info.pid)?; 306 | if Some((regs.rip - 1) as *mut c_void) == self.info.brk_addr { 307 | // 書き換えたメモリを元の値に戻す 308 | unsafe { 309 | ptrace::write( 310 | self.info.pid, 311 | self.info.brk_addr.unwrap(), 312 | self.info.brk_val as *mut c_void, 313 | )? 314 | }; 315 | 316 | // ブレークポイントで停止したアドレスから一つもどす 317 | regs.rip -= 1; 318 | ptrace::setregs(self.info.pid, regs)?; 319 | } 320 | println!("<<子プロセスが停止しました : PC = {:#x}>>", regs.rip); 321 | 322 | Ok(State::Running(self)) 323 | } 324 | _ => Err("waitpidの返り値が不正です".into()), 325 | } 326 | } 327 | } 328 | 329 | /// ヘルプを表示 330 | fn do_help() { 331 | println!( 332 | r#"コマンド一覧 (括弧内は省略記法) 333 | break 0x8000 : ブレークポイントを0x8000番地に設定 (b 0x8000) 334 | run : プログラムを実行 (r) 335 | continue : プログラムを再開 (c) 336 | stepi : 機械語レベルで1ステップ実行 (s) 337 | registers : レジスタを表示 (regs) 338 | exit : 終了 339 | help : このヘルプを表示 (h)"# 340 | ); 341 | } 342 | 343 | /// レジスタを表示 344 | fn print_regs(regs: &user_regs_struct) { 345 | println!( 346 | r#"RIP: {:#016x}, RSP: {:#016x}, RBP: {:#016x} 347 | RAX: {:#016x}, RBX: {:#016x}, RCX: {:#016x} 348 | RDX: {:#016x}, RSI: {:#016x}, RDI: {:#016x} 349 | R8: {:#016x}, R9: {:#016x}, R10: {:#016x} 350 | R11: {:#016x}, R12: {:#016x}, R13: {:#016x} 351 | R14: {:#016x}, R15: {:#016x}"#, 352 | regs.rip, 353 | regs.rsp, 354 | regs.rbp, 355 | regs.rax, 356 | regs.rbx, 357 | regs.rcx, 358 | regs.rdx, 359 | regs.rsi, 360 | regs.rdi, 361 | regs.r8, 362 | regs.r9, 363 | regs.r10, 364 | regs.r11, 365 | regs.r12, 366 | regs.r13, 367 | regs.r14, 368 | regs.r15, 369 | ); 370 | } 371 | 372 | /// コマンドからブレークポイントを計算 373 | fn get_break_addr(cmd: &[&str]) -> Option<*mut c_void> { 374 | if cmd.len() < 2 { 375 | eprintln!("<<アドレスを指定してください\n例 : break 0x8000>>"); 376 | return None; 377 | } 378 | 379 | let addr_str = cmd[1]; 380 | if &addr_str[0..2] != "0x" { 381 | eprintln!("<<アドレスは16進数でのみ指定可能です\n例 : break 0x8000>>"); 382 | return None; 383 | } 384 | 385 | let addr = match usize::from_str_radix(&addr_str[2..], 16) { 386 | Ok(addr) => addr, 387 | Err(e) => { 388 | eprintln!("<<アドレス変換エラー : {}>>", e); 389 | return None; 390 | } 391 | } as *mut c_void; 392 | 393 | Some(addr) 394 | } 395 | -------------------------------------------------------------------------------- /ch08/zdbg/src/helper.rs: -------------------------------------------------------------------------------- 1 | pub type DynError = Box; 2 | -------------------------------------------------------------------------------- /ch08/zdbg/src/main.rs: -------------------------------------------------------------------------------- 1 | mod dbg; 2 | mod helper; 3 | 4 | use dbg::{State, ZDbg}; 5 | use helper::DynError; 6 | use rustyline::{error::ReadlineError, Editor}; 7 | use std::env; 8 | 9 | fn main() -> Result<(), DynError> { 10 | let args: Vec = env::args().collect(); 11 | if args.len() < 2 { 12 | let msg = format!("引数が必要です\n例 : {} 実行ファイル [引数*]", args[0]); 13 | return Err(msg.into()); 14 | } 15 | 16 | run_dbg(&args[1])?; 17 | Ok(()) 18 | } 19 | 20 | fn run_dbg(filename: &str) -> Result<(), DynError> { 21 | let debugger = ZDbg::new(filename.to_string()); 22 | let mut state = State::NotRunning(debugger); 23 | let mut rl = Editor::<()>::new()?; 24 | 25 | loop { 26 | match rl.readline("zdbg > ") { 27 | Ok(line) => { 28 | let trimed = line.trim(); // 行頭と行末の空白文字を削除 29 | let cmd: Vec<&str> = trimed.split(' ').filter(|c| !c.is_empty()).collect(); // 空文字を削除 30 | state = match state { 31 | State::Running(r) => r.do_cmd(&cmd)?, 32 | State::NotRunning(n) => n.do_cmd(&cmd)?, 33 | _ => break, 34 | }; 35 | if let State::Exit = state { 36 | break; 37 | } 38 | rl.add_history_entry(line); 39 | } 40 | Err(ReadlineError::Interrupted) => eprintln!("<<終了はCtrl+D>>"), 41 | _ => { 42 | if let State::Running(r) = state { 43 | // 子プロセスが実行中の場合はkill 44 | r.do_cmd(&["exit"])?; 45 | }; 46 | break; 47 | } 48 | } 49 | } 50 | 51 | Ok(()) 52 | } 53 | -------------------------------------------------------------------------------- /ch09/README.md: -------------------------------------------------------------------------------- 1 | # 第9章 線形型システム 2 | 3 | - [9.2 パーサコンビネータ](./parser/) 4 | - [9.3 LinZ言語の実装](./linz) 5 | -------------------------------------------------------------------------------- /ch09/linz/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "linz" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | nom = "7.1.1" 12 | -------------------------------------------------------------------------------- /ch09/linz/codes/err1.lin: -------------------------------------------------------------------------------- 1 | un 2 | -------------------------------------------------------------------------------- /ch09/linz/codes/err2.lin: -------------------------------------------------------------------------------- 1 | lin fn x : lin bool { 2 | free x; 3 | x 4 | } 5 | -------------------------------------------------------------------------------- /ch09/linz/codes/err3.lin: -------------------------------------------------------------------------------- 1 | lin fn x : lin bool { 2 | free x; 3 | free x; 4 | lin true 5 | } 6 | -------------------------------------------------------------------------------- /ch09/linz/codes/err4.lin: -------------------------------------------------------------------------------- 1 | split lin as x, y { 2 | x 3 | } 4 | -------------------------------------------------------------------------------- /ch09/linz/codes/err5.lin: -------------------------------------------------------------------------------- 1 | lin fn x : lin (lin bool * lin bool) { 2 | split x as a, b { 3 | if a { 4 | b 5 | } else { 6 | lin true 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ch09/linz/codes/err6.lin: -------------------------------------------------------------------------------- 1 | un fn x : lin bool { 2 | un fn y : un bool { 3 | lin fn z : un bool { 4 | lin 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch09/linz/codes/err7.lin: -------------------------------------------------------------------------------- 1 | let x : lin bool = lin true; 2 | un false 3 | -------------------------------------------------------------------------------- /ch09/linz/codes/ex1.lin: -------------------------------------------------------------------------------- 1 | lin fn x : lin bool { 2 | if x { 3 | lin false 4 | } else { 5 | lin true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch09/linz/codes/ex2.lin: -------------------------------------------------------------------------------- 1 | let x : un bool = un true; 2 | if x { 3 | un false 4 | } else { 5 | un true 6 | } 7 | -------------------------------------------------------------------------------- /ch09/linz/codes/ex3.lin: -------------------------------------------------------------------------------- 1 | lin fn x : lin bool { 2 | free x; 3 | lin false 4 | } 5 | -------------------------------------------------------------------------------- /ch09/linz/codes/ex4.lin: -------------------------------------------------------------------------------- 1 | split lin as x, y { 2 | free x; 3 | free y; 4 | un true 5 | } 6 | -------------------------------------------------------------------------------- /ch09/linz/codes/ex5.lin: -------------------------------------------------------------------------------- 1 | (lin fn x : lin bool { 2 | if x { 3 | un 4 | } else { 5 | un 6 | } 7 | } lin true) 8 | -------------------------------------------------------------------------------- /ch09/linz/codes/ex6.lin: -------------------------------------------------------------------------------- 1 | un 2 | -------------------------------------------------------------------------------- /ch09/linz/codes/ex7.lin: -------------------------------------------------------------------------------- 1 | lin fn x : lin (lin bool * lin bool) { 2 | split x as a, b { 3 | if a { 4 | b 5 | } else { 6 | b 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ch09/linz/codes/ex8.lin: -------------------------------------------------------------------------------- 1 | let x : lin bool = lin true; 2 | let y : lin bool = lin false; 3 | lin 4 | -------------------------------------------------------------------------------- /ch09/linz/codes/parse_err.lin: -------------------------------------------------------------------------------- 1 | lin fn x : lin bool { 2 | iff x { 3 | lin false 4 | } else { 5 | lin true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ch09/linz/src/helper.rs: -------------------------------------------------------------------------------- 1 | pub trait SafeAdd: Sized { 2 | fn safe_add(&self, n: &Self) -> Option; 3 | } 4 | 5 | impl SafeAdd for usize { 6 | fn safe_add(&self, n: &Self) -> Option { 7 | self.checked_add(*n) 8 | } 9 | } 10 | 11 | pub fn safe_add(dst: &mut T, src: &T, f: F) -> Result<(), E> 12 | where 13 | T: SafeAdd, 14 | F: Fn() -> E, 15 | { 16 | if let Some(n) = dst.safe_add(src) { 17 | *dst = n; 18 | Ok(()) 19 | } else { 20 | Err(f()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ch09/linz/src/main.rs: -------------------------------------------------------------------------------- 1 | mod helper; 2 | mod parser; 3 | mod typing; 4 | 5 | use nom::error::convert_error; 6 | use std::{env, error::Error, fs}; 7 | 8 | fn main() -> Result<(), Box> { 9 | // コマンドライン引数の検査 10 | let args: Vec = env::args().collect(); 11 | if args.len() < 2 { 12 | eprintln!("以下のようにファイル名を指定して実行してください\ncargo run codes/ex1.lin"); 13 | return Err("引数が不足".into()); 14 | } 15 | 16 | // ファイル読み込み 17 | let content = fs::read_to_string(&args[1])?; 18 | 19 | let ast = parser::parse_expr(&content); // パース 20 | println!("AST:\n{:#?}\n", ast); 21 | match ast { 22 | Ok((_, expr)) => { 23 | let mut ctx = typing::TypeEnv::new(); 24 | println!("式:\n{content}"); 25 | 26 | // 型付け 27 | let a = typing::typing(&expr, &mut ctx, 0)?; 28 | println!("の型は\n{a}\nです。"); 29 | } 30 | Err(nom::Err::Error(e)) => { 31 | let msg = convert_error(content.as_str(), e); 32 | eprintln!("パースエラー:\n{msg}"); 33 | return Err(msg.into()); 34 | } 35 | _ => (), 36 | } 37 | 38 | Ok(()) 39 | } 40 | -------------------------------------------------------------------------------- /ch09/linz/src/parser.rs: -------------------------------------------------------------------------------- 1 | //! # 線形型言語のパーサ 2 | //! 3 | //! λ計算に線形型システムを適用した独自の線形型言語のパーサ。 4 | //! 独自言語の構文は以下を参照。 5 | //! 6 | //! ## 構文 7 | //! 8 | //! ```text 9 | //! := 1文字以上のアルファベットから成り立つ変数 10 | //! 11 | //! := | | | | | | 12 | //! 13 | //! := let : = ; 14 | //! := if { } else { } 15 | //! := split as , { } 16 | //! := free ; 17 | //! := ( ) 18 | //! 19 | //! := lin | un 20 | //! 21 | //! 値 22 | //! := 23 | //! := | | 24 | //! := true | false 25 | //! := < , > 26 | //! := fn : { } 27 | //! 28 | //! 型 29 | //! :=

30 | //!

:= bool | 31 | //! ( * ) 32 | //! ( -> ) 33 | //! ``` 34 | 35 | use nom::{ 36 | branch::alt, 37 | bytes::complete::tag, 38 | character::complete::{alpha1, char, multispace0, multispace1}, 39 | error::VerboseError, 40 | sequence::delimited, 41 | IResult, 42 | }; 43 | use std::fmt; 44 | 45 | /// 抽象構文木 46 | /// 47 | /// ```text 48 | /// := | | | | | | 49 | /// ``` 50 | #[derive(Debug)] 51 | pub enum Expr { 52 | Let(LetExpr), // let式 53 | If(IfExpr), // if式 54 | Split(SplitExpr), // split式 55 | Free(FreeExpr), // free文 56 | App(AppExpr), // 関数適用 57 | Var(String), // 変数 58 | QVal(QValExpr), // 値 59 | } 60 | 61 | /// 関数適用 62 | /// 63 | /// ```text 64 | /// := ( ) 65 | /// 66 | /// (expr1 expr2) 67 | /// ``` 68 | #[derive(Debug)] 69 | pub struct AppExpr { 70 | pub expr1: Box, 71 | pub expr2: Box, 72 | } 73 | 74 | /// if式 75 | /// 76 | /// ```text 77 | /// := if { } else { } 78 | /// 79 | /// if cond_expr { 80 | /// then_expr 81 | /// } else { 82 | /// else_expr 83 | /// } 84 | /// ``` 85 | #[derive(Debug)] 86 | pub struct IfExpr { 87 | pub cond_expr: Box, 88 | pub then_expr: Box, 89 | pub else_expr: Box, 90 | } 91 | 92 | /// split式 93 | /// 94 | /// ```text 95 | /// := split as , { } 96 | /// 97 | /// split expr as left, right { 98 | /// body 99 | /// } 100 | /// ``` 101 | #[derive(Debug)] 102 | pub struct SplitExpr { 103 | pub expr: Box, 104 | pub left: String, 105 | pub right: String, 106 | pub body: Box, 107 | } 108 | 109 | /// let式 110 | /// 111 | /// ```text 112 | /// := let : = { } 113 | /// 114 | /// let var : ty = expr1 { expr2 } 115 | /// ``` 116 | #[derive(Debug)] 117 | pub struct LetExpr { 118 | pub var: String, 119 | pub ty: TypeExpr, 120 | pub expr1: Box, 121 | pub expr2: Box, 122 | } 123 | 124 | /// 値。真偽値、関数、ペア値などになる 125 | /// 126 | /// ```text 127 | /// := | | 128 | /// := true | false 129 | /// := < , > 130 | /// := fn : { } 131 | /// ``` 132 | #[derive(Debug)] 133 | pub enum ValExpr { 134 | Bool(bool), // 真偽値リテラル 135 | Pair(Box, Box), // ペア 136 | Fun(FnExpr), // 関数(λ抽象) 137 | } 138 | 139 | /// 修飾子 140 | /// 141 | /// ```text 142 | /// := lin | un 143 | /// ``` 144 | #[derive(Debug, Eq, PartialEq, Clone, Copy)] 145 | pub enum Qual { 146 | Lin, // 線形型 147 | Un, // 制約のない一般的な型 148 | } 149 | 150 | /// 修飾子付き値 151 | /// 152 | /// ``` 153 | /// := 154 | /// ``` 155 | #[derive(Debug)] 156 | pub struct QValExpr { 157 | pub qual: Qual, 158 | pub val: ValExpr, 159 | } 160 | 161 | /// 関数 162 | /// 163 | /// ```text 164 | /// := fn : { } 165 | /// 166 | /// fn var : ty { expr } 167 | /// ``` 168 | #[derive(Debug)] 169 | pub struct FnExpr { 170 | pub var: String, 171 | pub ty: TypeExpr, 172 | pub expr: Box, 173 | } 174 | 175 | /// free文 176 | /// 177 | /// ```text 178 | /// := free ; 179 | /// 180 | /// free var; expr 181 | /// ``` 182 | #[derive(Debug)] 183 | pub struct FreeExpr { 184 | pub var: String, 185 | pub expr: Box, 186 | } 187 | 188 | /// 修飾子付き型 189 | /// 190 | /// ```text 191 | /// := 192 | /// ``` 193 | #[derive(Debug, Eq, PartialEq, Clone)] 194 | pub struct TypeExpr { 195 | pub qual: Qual, 196 | pub prim: PrimType, 197 | } 198 | 199 | impl fmt::Display for TypeExpr { 200 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 201 | if self.qual == Qual::Lin { 202 | write!(f, "lin {}", self.prim) 203 | } else { 204 | write!(f, "un {}", self.prim) 205 | } 206 | } 207 | } 208 | 209 | /// プリミティブ型 210 | /// 211 | /// ```text 212 | ///

:= bool | 213 | /// ( * ) 214 | /// ( -> ) 215 | /// ``` 216 | #[derive(Debug, Eq, PartialEq, Clone)] 217 | pub enum PrimType { 218 | Bool, // 真偽値型 219 | Pair(Box, Box), // ペア型 220 | Arrow(Box, Box), // 関数型 221 | } 222 | 223 | impl fmt::Display for PrimType { 224 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 225 | match self { 226 | PrimType::Bool => write!(f, "bool"), 227 | PrimType::Pair(t1, t2) => write!(f, "({t1} * {t2})"), 228 | PrimType::Arrow(t1, t2) => write!(f, "({t1} -> {t2})"), 229 | } 230 | } 231 | } 232 | 233 | pub fn parse_expr(i: &str) -> IResult<&str, Expr, VerboseError<&str>> { 234 | let (i, _) = multispace0(i)?; 235 | let (i, val) = alt((alpha1, tag("(")))(i)?; 236 | 237 | match val { 238 | "let" => parse_let(i), 239 | "if" => parse_if(i), 240 | "split" => parse_split(i), 241 | "free" => parse_free(i), 242 | "lin" => parse_qval(Qual::Lin, i), 243 | "un" => parse_qval(Qual::Un, i), 244 | "(" => parse_app(i), 245 | _ => Ok((i, Expr::Var(val.to_string()))), 246 | } 247 | } 248 | 249 | /// 関数適用をパース。 250 | fn parse_app(i: &str) -> IResult<&str, Expr, VerboseError<&str>> { 251 | let (i, _) = multispace0(i)?; 252 | let (i, e1) = parse_expr(i)?; // 適用する関数 253 | 254 | let (i, _) = multispace1(i)?; 255 | 256 | let (i, e2) = parse_expr(i)?; // 引数 257 | 258 | let (i, _) = multispace0(i)?; 259 | let (i, _) = char(')')(i)?; 260 | 261 | Ok(( 262 | i, 263 | Expr::App(AppExpr { 264 | expr1: Box::new(e1), 265 | expr2: Box::new(e2), 266 | }), 267 | )) 268 | } 269 | 270 | /// free文をパース。 271 | fn parse_free(i: &str) -> IResult<&str, Expr, VerboseError<&str>> { 272 | let (i, _) = multispace1(i)?; 273 | let (i, var) = alpha1(i)?; // 解放する変数 274 | let (i, _) = multispace0(i)?; 275 | let (i, _) = char(';')(i)?; 276 | 277 | let (i, e) = parse_expr(i)?; // 続けて実行する式 278 | Ok(( 279 | i, 280 | Expr::Free(FreeExpr { 281 | var: var.to_string(), 282 | expr: Box::new(e), 283 | }), 284 | )) 285 | } 286 | 287 | /// split式をパース。 288 | fn parse_split(i: &str) -> IResult<&str, Expr, VerboseError<&str>> { 289 | let (i, _) = multispace1(i)?; 290 | let (i, e1) = parse_expr(i)?; // 分解するペア 291 | 292 | let (i, _) = multispace1(i)?; 293 | let (i, _) = tag("as")(i)?; 294 | let (i, _) = multispace1(i)?; 295 | 296 | let (i, v1) = parse_var(i)?; // 一つめの変数 297 | 298 | let (i, _) = multispace0(i)?; 299 | let (i, _) = char(',')(i)?; 300 | let (i, _) = multispace0(i)?; 301 | 302 | let (i, v2) = parse_var(i)?; // 二つめの変数 303 | let (i, _) = multispace0(i)?; 304 | 305 | // { }というように、波括弧で囲まれた式をパース 306 | let (i, e2) = delimited( 307 | char('{'), 308 | delimited(multispace0, parse_expr, multispace0), 309 | char('}'), 310 | )(i)?; 311 | 312 | Ok(( 313 | i, 314 | Expr::Split(SplitExpr { 315 | expr: Box::new(e1), 316 | left: v1, 317 | right: v2, 318 | body: Box::new(e2), 319 | }), 320 | )) 321 | } 322 | 323 | /// if式をパース。 324 | fn parse_if(i: &str) -> IResult<&str, Expr, VerboseError<&str>> { 325 | let (i, _) = multispace1(i)?; 326 | let (i, e1) = parse_expr(i)?; // 条件 327 | let (i, _) = multispace0(i)?; 328 | 329 | // 条件が真の時に実行する式 330 | let (i, e2) = delimited( 331 | char('{'), 332 | delimited(multispace0, parse_expr, multispace0), 333 | char('}'), 334 | )(i)?; 335 | 336 | let (i, _) = multispace0(i)?; 337 | let (i, _) = tag("else")(i)?; 338 | let (i, _) = multispace0(i)?; 339 | 340 | // 条件が偽の時に実行する式 341 | let (i, e3) = delimited( 342 | char('{'), 343 | delimited(multispace0, parse_expr, multispace0), 344 | char('}'), 345 | )(i)?; 346 | 347 | Ok(( 348 | i, 349 | Expr::If(IfExpr { 350 | cond_expr: Box::new(e1), 351 | then_expr: Box::new(e2), 352 | else_expr: Box::new(e3), 353 | }), 354 | )) 355 | } 356 | 357 | /// let式をパース。 358 | fn parse_let(i: &str) -> IResult<&str, Expr, VerboseError<&str>> { 359 | let (i, _) = multispace1(i)?; 360 | 361 | let (i, var) = parse_var(i)?; // 束縛する変数 362 | 363 | let (i, _) = multispace0(i)?; 364 | let (i, _) = char(':')(i)?; 365 | let (i, _) = multispace0(i)?; 366 | 367 | let (i, ty) = parse_type(i)?; // 変数の型 368 | 369 | let (i, _) = multispace0(i)?; 370 | let (i, _) = char('=')(i)?; 371 | let (i, _) = multispace0(i)?; 372 | 373 | let (i, e1) = parse_expr(i)?; // 変数の値 374 | let (i, _) = multispace0(i)?; 375 | 376 | let (i, _) = char(';')(i)?; 377 | let (i, e2) = parse_expr(i)?; // 実行する式 378 | 379 | Ok(( 380 | i, 381 | Expr::Let(LetExpr { 382 | var, 383 | ty, 384 | expr1: Box::new(e1), 385 | expr2: Box::new(e2), 386 | }), 387 | )) 388 | } 389 | 390 | /// ペアをパース。 391 | fn parse_pair(i: &str) -> IResult<&str, ValExpr, VerboseError<&str>> { 392 | let (i, _) = multispace0(i)?; 393 | 394 | let (i, v1) = parse_expr(i)?; // 一つめの値 395 | 396 | let (i, _) = multispace0(i)?; 397 | let (i, _) = char(',')(i)?; 398 | let (i, _) = multispace0(i)?; 399 | 400 | let (i, v2) = parse_expr(i)?; // 二つめの値 401 | 402 | let (i, _) = multispace0(i)?; 403 | let (i, _) = char('>')(i)?; // 閉じ括弧 404 | 405 | Ok((i, ValExpr::Pair(Box::new(v1), Box::new(v2)))) 406 | } 407 | 408 | /// linとun修飾子をパース。 409 | fn parse_qual(i: &str) -> IResult<&str, Qual, VerboseError<&str>> { 410 | let (i, val) = alt((tag("lin"), tag("un")))(i)?; 411 | if val == "lin" { 412 | Ok((i, Qual::Lin)) 413 | } else { 414 | Ok((i, Qual::Un)) 415 | } 416 | } 417 | 418 | /// 関数をパース。 419 | fn parse_fn(i: &str) -> IResult<&str, ValExpr, VerboseError<&str>> { 420 | let (i, _) = multispace1(i)?; 421 | let (i, var) = parse_var(i)?; // 引数 422 | 423 | let (i, _) = multispace0(i)?; 424 | let (i, _) = char(':')(i)?; 425 | let (i, _) = multispace0(i)?; 426 | 427 | let (i, ty) = parse_type(i)?; // 引数の型 428 | let (i, _) = multispace0(i)?; 429 | 430 | // { }というように、波括弧で囲まれた式をパース 431 | let (i, expr) = delimited( 432 | char('{'), 433 | delimited(multispace0, parse_expr, multispace0), 434 | char('}'), 435 | )(i)?; 436 | 437 | Ok(( 438 | i, 439 | ValExpr::Fun(FnExpr { 440 | var, 441 | ty, 442 | expr: Box::new(expr), 443 | }), 444 | )) 445 | } 446 | 447 | /// 真偽値、関数、ペアの値をパース。 448 | fn parse_val(i: &str) -> IResult<&str, ValExpr, VerboseError<&str>> { 449 | let (i, val) = alt((tag("fn"), tag("true"), tag("false"), tag("<")))(i)?; 450 | match val { 451 | "fn" => parse_fn(i), 452 | "true" => Ok((i, ValExpr::Bool(true))), 453 | "false" => Ok((i, ValExpr::Bool(false))), 454 | "<" => parse_pair(i), 455 | _ => unreachable!(), 456 | } 457 | } 458 | 459 | /// 修飾子付き値をパース。 460 | fn parse_qval(q: Qual, i: &str) -> IResult<&str, Expr, VerboseError<&str>> { 461 | let (i, _) = multispace1(i)?; 462 | let (i, v) = parse_val(i)?; 463 | 464 | Ok((i, Expr::QVal(QValExpr { qual: q, val: v }))) 465 | } 466 | 467 | /// 変数をパース。変数は1文字以上のアルファベットから成り立つ。 468 | fn parse_var(i: &str) -> IResult<&str, String, VerboseError<&str>> { 469 | let (i, v) = alpha1(i)?; 470 | Ok((i, v.to_string())) 471 | } 472 | 473 | /// 真偽値、関数、ペア型をパース。 474 | fn parse_type(i: &str) -> IResult<&str, TypeExpr, VerboseError<&str>> { 475 | let (i, q) = parse_qual(i)?; // 修飾子 476 | let (i, _) = multispace1(i)?; 477 | let (i, val) = alt((tag("bool"), tag("(")))(i)?; 478 | if val == "bool" { 479 | // bool型 480 | Ok(( 481 | i, 482 | TypeExpr { 483 | qual: q, 484 | prim: PrimType::Bool, 485 | }, 486 | )) 487 | } else { 488 | // 関数型かペア型 489 | let (i, _) = multispace0(i)?; 490 | let (i, t1) = parse_type(i)?; // 一つめの型 491 | let (i, _) = multispace0(i)?; 492 | 493 | // ->か*をパース 494 | // ->の場合は関数型で、場合はペア型 495 | let (i, op) = alt((tag("*"), tag("->")))(i)?; 496 | 497 | let (i, _) = multispace0(i)?; 498 | let (i, t2) = parse_type(i)?; // 二つめの型 499 | let (i, _) = multispace0(i)?; 500 | 501 | let (i, _) = char(')')(i)?; 502 | 503 | Ok(( 504 | i, 505 | TypeExpr { 506 | qual: q, 507 | prim: if op == "*" { 508 | PrimType::Pair(Box::new(t1), Box::new(t2)) 509 | } else { 510 | PrimType::Arrow(Box::new(t1), Box::new(t2)) 511 | }, 512 | }, 513 | )) 514 | } 515 | } 516 | -------------------------------------------------------------------------------- /ch09/linz/src/typing.rs: -------------------------------------------------------------------------------- 1 | use crate::{helper::safe_add, parser}; 2 | use std::{borrow::Cow, cmp::Ordering, collections::BTreeMap, mem}; 3 | 4 | type VarToType = BTreeMap>; 5 | 6 | /// 型環境 7 | #[derive(Debug, Clone, Eq, PartialEq)] 8 | pub struct TypeEnv { 9 | env_lin: TypeEnvStack, // lin用 10 | env_un: TypeEnvStack, // un用 11 | } 12 | 13 | impl TypeEnv { 14 | pub fn new() -> TypeEnv { 15 | TypeEnv { 16 | env_lin: TypeEnvStack::new(), 17 | env_un: TypeEnvStack::new(), 18 | } 19 | } 20 | 21 | /// 型環境をpush 22 | fn push(&mut self, depth: usize) { 23 | self.env_lin.push(depth); 24 | self.env_un.push(depth); 25 | } 26 | 27 | /// 型環境をpop 28 | fn pop(&mut self, depth: usize) -> (Option, Option) { 29 | let t1 = self.env_lin.pop(depth); 30 | let t2 = self.env_un.pop(depth); 31 | (t1, t2) 32 | } 33 | 34 | /// 型環境へ変数と型をpush 35 | fn insert(&mut self, key: String, value: parser::TypeExpr) { 36 | if value.qual == parser::Qual::Lin { 37 | self.env_lin.insert(key, value); 38 | } else { 39 | self.env_un.insert(key, value); 40 | } 41 | } 42 | 43 | /// linとunの型環境からget_mutし、depthが大きい方を返す 44 | fn get_mut(&mut self, key: &str) -> Option<&mut Option> { 45 | match (self.env_lin.get_mut(key), self.env_un.get_mut(key)) { 46 | (Some((d1, t1)), Some((d2, t2))) => match d1.cmp(&d2) { 47 | Ordering::Less => Some(t2), 48 | Ordering::Greater => Some(t1), 49 | Ordering::Equal => panic!("invalid type environment"), 50 | }, 51 | (Some((_, t1)), None) => Some(t1), 52 | (None, Some((_, t2))) => Some(t2), 53 | _ => None, 54 | } 55 | } 56 | } 57 | 58 | /// 型環境のスタック 59 | #[derive(Debug, Clone, Eq, PartialEq, Default)] 60 | struct TypeEnvStack { 61 | vars: BTreeMap, 62 | } 63 | 64 | impl TypeEnvStack { 65 | fn new() -> TypeEnvStack { 66 | TypeEnvStack { 67 | vars: BTreeMap::new(), 68 | } 69 | } 70 | 71 | // 型環境をpush 72 | fn push(&mut self, depth: usize) { 73 | self.vars.insert(depth, BTreeMap::new()); 74 | } 75 | 76 | // 型環境をpop 77 | fn pop(&mut self, depth: usize) -> Option { 78 | self.vars.remove(&depth) 79 | } 80 | 81 | // スタックの最も上にある型環境に変数名と型を追加 82 | fn insert(&mut self, key: String, value: parser::TypeExpr) { 83 | if let Some(last) = self.vars.iter_mut().next_back() { 84 | last.1.insert(key, Some(value)); 85 | } 86 | } 87 | 88 | // スタックを上からたどっていき、はじめに見つかる変数の型を取得 89 | fn get_mut(&mut self, key: &str) -> Option<(usize, &mut Option)> { 90 | for (depth, elm) in self.vars.iter_mut().rev() { 91 | if let Some(e) = elm.get_mut(key) { 92 | return Some((*depth, e)); 93 | } 94 | } 95 | None 96 | } 97 | } 98 | 99 | type TResult<'a> = Result>; 100 | 101 | /// 型付け関数 102 | /// 式を受け取り、型を返す 103 | pub fn typing<'a>(expr: &parser::Expr, env: &mut TypeEnv, depth: usize) -> TResult<'a> { 104 | match expr { 105 | parser::Expr::App(e) => typing_app(e, env, depth), 106 | parser::Expr::QVal(e) => typing_qval(e, env, depth), 107 | parser::Expr::Free(e) => typing_free(e, env, depth), 108 | parser::Expr::If(e) => typing_if(e, env, depth), 109 | parser::Expr::Split(e) => typing_split(e, env, depth), 110 | parser::Expr::Var(e) => typing_var(e, env), 111 | parser::Expr::Let(e) => typing_let(e, env, depth), 112 | } 113 | } 114 | 115 | /// 関数適用の型付け 116 | fn typing_app<'a>(expr: &parser::AppExpr, env: &mut TypeEnv, depth: usize) -> TResult<'a> { 117 | // 関数部分 118 | let t1 = typing(&expr.expr1, env, depth)?; 119 | let t_arg; 120 | let t_ret; 121 | match t1.prim { 122 | parser::PrimType::Arrow(a, b) => { 123 | t_arg = a; // 引数の型 124 | t_ret = b; // 返り値の型 125 | } 126 | _ => return Err("関数型でない".into()), 127 | } 128 | 129 | // 引数部分 130 | let t2 = typing(&expr.expr2, env, depth)?; 131 | 132 | // 引数の型が一致しているかチェック 133 | if *t_arg == t2 { 134 | Ok(*t_ret) 135 | } else { 136 | Err("関数適用時における引数の型が異なる".into()) 137 | } 138 | } 139 | 140 | /// 修飾子付き値の型付け 141 | fn typing_qval<'a>(expr: &parser::QValExpr, env: &mut TypeEnv, depth: usize) -> TResult<'a> { 142 | // プリミティブ型を計算 143 | let p = match &expr.val { 144 | parser::ValExpr::Bool(_) => parser::PrimType::Bool, 145 | parser::ValExpr::Pair(e1, e2) => { 146 | // 式e1とe2をtypingにより型付け 147 | let t1 = typing(e1, env, depth)?; 148 | let t2 = typing(e2, env, depth)?; 149 | 150 | // expr.qualがUnであり、 151 | // e1か、e2の型にlinが含まれていた場合、型付けエラー 152 | if expr.qual == parser::Qual::Un 153 | && (t1.qual == parser::Qual::Lin || t2.qual == parser::Qual::Lin) 154 | { 155 | return Err("un型のペア内でlin型を利用している".into()); 156 | } 157 | 158 | // ペア型を返す 159 | parser::PrimType::Pair(Box::new(t1), Box::new(t2)) 160 | } 161 | parser::ValExpr::Fun(e) => { 162 | // 関数の型付け 163 | 164 | // un型の関数内では、lin型の自由変数をキャプチャできないため 165 | // lin用の型環境を置き換え 166 | let env_prev = if expr.qual == parser::Qual::Un { 167 | Some(mem::take(&mut env.env_lin)) 168 | } else { 169 | None 170 | }; 171 | 172 | // depthをインクリメントしてpush 173 | let mut depth = depth; 174 | safe_add(&mut depth, &1, || "変数スコープのネストが深すぎる")?; 175 | env.push(depth); 176 | env.insert(e.var.clone(), e.ty.clone()); 177 | 178 | // 関数中の式を型付け 179 | let t = typing(&e.expr, env, depth)?; 180 | 181 | // スタックをpopし、popした型環境の中にlin型が含まれていた場合、型付けエラー 182 | let (elin, _) = env.pop(depth); 183 | for (k, v) in elin.unwrap().iter() { 184 | if v.is_some() { 185 | return Err(format!("関数定義内でlin型の変数\"{k}\"を消費していない").into()); 186 | } 187 | } 188 | 189 | // lin用の型環境を復元 190 | if let Some(ep) = env_prev { 191 | env.env_lin = ep; 192 | } 193 | 194 | // 関数型を返す 195 | parser::PrimType::Arrow(Box::new(e.ty.clone()), Box::new(t)) 196 | } 197 | }; 198 | 199 | // 修飾子付き型を返す 200 | Ok(parser::TypeExpr { 201 | qual: expr.qual, 202 | prim: p, 203 | }) 204 | } 205 | 206 | /// free式の型付け 207 | fn typing_free<'a>(expr: &parser::FreeExpr, env: &mut TypeEnv, depth: usize) -> TResult<'a> { 208 | if let Some((_, t)) = env.env_lin.get_mut(&expr.var) { 209 | if t.is_some() { 210 | *t = None; 211 | return typing(&expr.expr, env, depth); 212 | } 213 | } 214 | Err(format!( 215 | "既にfreeしたか、lin型ではない変数\"{}\"をfreeしている", 216 | expr.var 217 | ) 218 | .into()) 219 | } 220 | 221 | /// if式の型付け 222 | fn typing_if<'a>(expr: &parser::IfExpr, env: &mut TypeEnv, depth: usize) -> TResult<'a> { 223 | let t1 = typing(&expr.cond_expr, env, depth)?; 224 | // 条件の式の型はbool 225 | if t1.prim != parser::PrimType::Bool { 226 | return Err("ifの条件式がboolでない".into()); 227 | } 228 | 229 | let mut e = env.clone(); 230 | let t2 = typing(&expr.then_expr, &mut e, depth)?; 231 | let t3 = typing(&expr.else_expr, env, depth)?; 232 | 233 | // thenとelse部の型は同じで、 234 | // thenとelse部評価後の型環境は同じかをチェック 235 | if t2 != t3 || e != *env { 236 | return Err("ifのthenとelseの式の型が異なる".into()); 237 | } 238 | 239 | Ok(t2) 240 | } 241 | 242 | /// split式の型付け 243 | fn typing_split<'a>(expr: &parser::SplitExpr, env: &mut TypeEnv, depth: usize) -> TResult<'a> { 244 | if expr.left == expr.right { 245 | return Err("splitの変数名が同じ".into()); 246 | } 247 | 248 | let t1 = typing(&expr.expr, env, depth)?; 249 | let mut depth = depth; 250 | safe_add(&mut depth, &1, || "変数スコープのネストが深すぎる")?; 251 | 252 | match t1.prim { 253 | parser::PrimType::Pair(p1, p2) => { 254 | env.push(depth); 255 | // ローカル変数の型を追加 256 | env.insert(expr.left.clone(), *p1); 257 | env.insert(expr.right.clone(), *p2); 258 | } 259 | _ => { 260 | return Err("splitの引数がペア型でない".into()); 261 | } 262 | } 263 | 264 | let ret = typing(&expr.body, env, depth); 265 | 266 | // ローカル変数を削除 267 | let (elin, _) = env.pop(depth); 268 | 269 | // lin型の変数を消費しているかチェック 270 | for (k, v) in elin.unwrap().iter() { 271 | if v.is_some() { 272 | return Err(format!("splitの式内でlin型の変数\"{k}\"を消費していない").into()); 273 | } 274 | } 275 | 276 | ret 277 | } 278 | 279 | /// 変数の型付け 280 | fn typing_var<'a>(expr: &str, env: &mut TypeEnv) -> TResult<'a> { 281 | let ret = env.get_mut(expr); 282 | if let Some(it) = ret { 283 | // 定義されている 284 | if let Some(t) = it { 285 | // 消費されていない 286 | if t.qual == parser::Qual::Lin { 287 | // lin型 288 | let eret = t.clone(); 289 | *it = None; // linを消費 290 | return Ok(eret); 291 | } else { 292 | return Ok(t.clone()); 293 | } 294 | } 295 | } 296 | 297 | Err(format!( 298 | "\"{}\"という変数は定義されていないか、利用済みか、キャプチャできない", 299 | expr 300 | ) 301 | .into()) 302 | } 303 | 304 | /// let式の型付け 305 | fn typing_let<'a>(expr: &parser::LetExpr, env: &mut TypeEnv, depth: usize) -> TResult<'a> { 306 | // 変数束縛 307 | let t1 = typing(&expr.expr1, env, depth)?; 308 | // 束縛変数の型をチェック 309 | if t1 != expr.ty { 310 | return Err(format!("変数\"{}\"の型が異なる", expr.var).into()); 311 | } 312 | 313 | // 関数内 314 | let mut depth = depth; 315 | safe_add(&mut depth, &1, || "変数スコープのネストが深すぎる")?; 316 | env.push(depth); 317 | env.insert(expr.var.clone(), t1); // 変数の型をinsert 318 | let t2 = typing(&expr.expr2, env, depth)?; 319 | 320 | // lin型の変数を消費しているかチェック 321 | let (elin, _) = env.pop(depth); 322 | for (k, v) in elin.unwrap().iter() { 323 | if v.is_some() { 324 | return Err(format!("let式内でlin型の変数\"{k}\"を消費していない").into()); 325 | } 326 | } 327 | 328 | Ok(t2) 329 | } 330 | -------------------------------------------------------------------------------- /ch09/parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "parser" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Yuuki Takano "] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | nom = "7.1.1" 12 | rustyline = "10.0" 13 | -------------------------------------------------------------------------------- /ch09/parser/src/main.rs: -------------------------------------------------------------------------------- 1 | //! # 逆ポーランド記法計算機 2 | //! 3 | //! パーサコンビネータのnomの説明のコード。 4 | //! 5 | //! 以下のようなBNFをパースし、実行する。 6 | //! 7 | //! ```text 8 | //! := | 9 | //! ``` 10 | use nom::{ 11 | branch::alt, 12 | character::complete::{char, one_of}, 13 | error::ErrorKind, 14 | multi::{many0, many1}, 15 | IResult, 16 | }; 17 | use rustyline::Editor; 18 | 19 | #[derive(Debug)] 20 | enum Expr { 21 | Num(u64), // 数値 22 | Add(Box, Box), // 加算 23 | Mul(Box, Box), // 乗算 24 | } 25 | 26 | fn main() { 27 | let mut rl = Editor::<()>::new().unwrap(); 28 | loop { 29 | // 1行読み込んでパースし成功すれば評価 30 | if let Ok(readline) = rl.readline(">> ") { 31 | if let Some(e) = parse(&readline) { 32 | println!("result: {}", eval(&e)); 33 | } 34 | } else { 35 | break; 36 | } 37 | } 38 | } 39 | 40 | fn parse(c: &str) -> Option { 41 | match parse_expr(c) { 42 | Ok((_, e)) => { 43 | println!("AST: {:?}", e); 44 | Some(e) 45 | } 46 | Err(e) => { 47 | println!("{e}"); 48 | None 49 | } 50 | } 51 | } 52 | 53 | fn parse_expr(c: &str) -> IResult<&str, Expr> { 54 | // 0個以上のホワイトスペースをスキップ 55 | let (c, _) = many0(char(' '))(c)?; 56 | 57 | // parse_numかparse_opをパース 58 | let result = alt((parse_num, parse_op))(c)?; 59 | Ok(result) 60 | } 61 | 62 | fn parse_num(c: &str) -> IResult<&str, Expr> { 63 | // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9のいずれかが、1個以上 64 | // 正規表現で[0..9]+ 65 | let (c1, v) = many1(one_of("0123456789"))(c)?; 66 | let var: String = v.into_iter().collect(); // VecをStringに変換 67 | 68 | // Stringをu64に変換 69 | if let Ok(n) = var.parse::() { 70 | Ok((c1, Expr::Num(n))) // Numを返す 71 | } else { 72 | let err = nom::error::Error::new(c, ErrorKind::Fail); 73 | Err(nom::Err::Failure(err)) 74 | } 75 | } 76 | 77 | fn parse_op(c: &str) -> IResult<&str, Expr> { 78 | // +か*のどちらか 79 | let (c, op) = one_of("+*")(c)?; 80 | let (c, e1) = parse_expr(c)?; // 一つめの式をパース 81 | let (c, e2) = parse_expr(c)?; // 二つめの式をパース 82 | 83 | if op == '+' { 84 | Ok((c, Expr::Add(Box::new(e1), Box::new(e2)))) // Addを返す 85 | } else { 86 | Ok((c, Expr::Mul(Box::new(e1), Box::new(e2)))) // Mulを返す 87 | } 88 | } 89 | 90 | fn eval(e: &Expr) -> u64 { 91 | match e { 92 | Expr::Num(n) => *n, 93 | Expr::Add(e1, e2) => eval(e1) + eval(e2), 94 | Expr::Mul(e1, e2) => eval(e1) * eval(e2), 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /errata.md: -------------------------------------------------------------------------------- 1 | # 『ゼロから学ぶRust』の正誤表 2 | 3 | 下記の誤りがありました。お詫びして訂正いたします。 4 | 5 | 本ページに掲載されていない誤植など間違いを見つけた方は、pull request、またはissueで報告いただけます。 6 | 7 | ## 第1刷 8 | 9 | |頁 | 誤 | 正 | 10 | | ---- | ----- | ---- | 11 | | 2.1.4項。P.18。下から2行目。 | 算術シフトは、最上位ビットが1の場合に右シフトすると、右側のビットに1が代入される。 | 算術シフトは、最上位ビットが1の場合に右シフトすると、左側のビットに1が代入される。 | 12 | | 2.1.6項。P.22。1行目。 | 4つの配列を持つ配列の値を定義できる | 4つの値を持つ配列を定義できる | 13 | | 2.1節。P.29。図 2.6。 | 中央の四角中のテキスト「`data: 40`」 | 「`data: 10`」 | 14 | | 2.2.8項。P.44。2段落目3行目。 | 変数fへの参照が保持され | 変数storageへの参照が保持され | 15 | | 2.2節。P.54。表 2.6。 `it.next()` | 次の要素へのイテレータを返す | 次の要素を返す | 16 | | 2.2節。P.54。表 2.6。 `it.last()` | 最後の要素へのイテレータを返す | 最後の要素を返す | 17 | | 2.2節。P.54。表 2.6。 | `it.nth(&n)` | `it.nth(n)` | 18 | | 2.2節。P.54。表 2.6。 | `it.fold(&init, &f)` | `it.fold(init, &f)` | 19 | | 2.2節。P.54。表 2.6。 `it.nth(&n)` | n番目の要素へのイテレータを返す | n番目の要素を返す | 20 | | 3.3節。P.68。ソースコードの2行目。 | `let b: &i32 = &b;` | `let b: &i32 = &a;` | 21 | | 3.4.2項。P.72。3段落目1行目。 | 可燃参照 | 可変参照 | 22 | | 3.4.2項。P.75。下から2行目。 | 分配束縛(destructive assignment) | 分配束縛(destructuring assignment) | 23 | | 3.4節。P.75。上のソースコードの下から2行目。 | `*elm * *elm` | `*elm + *elm` | 24 | | 3.5説。P.76。5行目。 | 排他制御と同じ制約を動的に | 排他制御と同じ制約を静的に | 25 | | 3.5節。P.76。下から2行目。 | 開放 | 解放 | 26 | | 3.5節。P.77。4行目。 | 開放 | 解放 | 27 | | 4.3節。P.88。図 4.3。 | `{"top": {"left": {"left": "left", "right": "leaf}, "right": {"left: "leaf", "right": "leaf"}}` | `{"top": {"left": {"left": "left", "right": "leaf"}, "right": {"left": "leaf", "right": "leaf"}}}` | 28 | | 4.4節。P.90。上のソースコードの6行目。 | `&js` | `&yml` | 29 | | 6.3.1項。P.136。上のソースコードの6行目。 | `espace` | `escape` | 30 | | 6.3節。P.141。ソースコード。 | 「`return Err(Box::new(..))`」となっている箇所 | このソースコード中でリターンしている箇所では、`Box::new()`は不要です | 31 | | 7.1節。P.167。下から5行目。 | Bourne Shell(Bash) | Bourne Again Shell(Bash) | 32 | | 7.3.4項。P.184。1行目。| `vec![("echo", vec!["hello"]), ("less", vec![])]` | `vec![("echo", vec!["echo", "hello"]), ("less", vec!["less"])]` | 33 | | 7.3.4項。P.196。ソースコード | 「`self.is_group_empty()`」を呼び出している箇所の`unwrap()` | `self.is_group_empty()`には、`unwrap()`は不要です | 34 | | 7.4節。P.201。演習問題3 | `nix::unistd::open` | `nix::fcntl::open` | 35 | | 9.1.4項。P.240~241。型に対するUn述語とLin述語の例。| `Un(un bool) = true`
`Un(lin ) = false`
`Lin(un bool) = true`
`Lin(un ) = true`
`Lin(lin ) = true` | `Un(un bool) = true`
`Un(lin (lin bool * lin bool)) = false`
`Lin(un bool) = true`
`Lin(un (un bool * un bool)) = true`
`Lin(lin (un bool * lin bool)) = true` | 36 | | 9.1.4項。P.241。型環境に対するUn述語とLin述語の例。| `Un(x:un , y:lin bool)`
`= Un(un ) ∧ Un(un bool)`
`= true`
`Un(x:un bool, y:un bool)`
`= Un(un bool) ∧ Un(lin bool)`
`= false` | `Un(x:un (un bool * un bool), y:un bool)`
`= Un(un (un bool * un bool)) ∧ Un(un bool)`
`= true`
`Un(x:un bool, y:lin bool)`
`= Un(un bool) ∧ Un(lin bool)`
`= false` | 37 | | 9.1.4項。P.244。下から1行目、および一番下の証明木の最終行。| `un (lin bool -> lin (un bool * un bool))` | `un (un bool -> lin (un bool * un bool))` | 38 | | 9.3.1項。P.255。`Expr`型 | | `#[derive(Debug)] `が必要です | 39 | 40 | ### 3.4.2項。P.75。構造体のフィールドを借用しコンパイルエラーとなる例 41 | 42 | このページあるコードとエラーの説明は誤りです。 43 | 正しい説明を以下に記載します。 44 | 45 | 構造体フィールドの値に対して何らかの定型処理を行いたい場合があります。 46 | たとえば、`XY`という構造体の`selector`の値に応じて、 47 | `XY::x`か`XY::y`を変更したいとします。 48 | 具体的には以下のようなコードとなります。 49 | 50 | ```rust 51 | #[derive(Debug)] 52 | struct XY { 53 | x: Vec, 54 | y: Vec, 55 | selector: bool, 56 | scaler: i32, 57 | } 58 | 59 | fn main() { 60 | let mut xy = XY { 61 | x: vec![1, 2, 3], 62 | y: vec![4, 5, 6], 63 | selector: true, 64 | scaler: 3, 65 | }; 66 | 67 | let v = xy.get_vec(); 68 | xy.update(v); // `xy`は借用されているためコンパイルエラー 69 | 70 | println!("{:?}", xy); 71 | } 72 | 73 | impl XY { 74 | /// `selector`の応じて、`x`か`y`を返す 75 | fn get_vec(&mut self) -> &mut [i32] { 76 | if self.selector { 77 | &mut self.x 78 | } else { 79 | &mut self.y 80 | } 81 | } 82 | 83 | /// `v`になんらかの定型処理を行う 84 | fn update(&mut self, v: &mut [i32]) { 85 | for elm in v.iter_mut() { 86 | *elm *= self.scaler; 87 | } 88 | } 89 | } 90 | ``` 91 | 92 | ここでは、`get_vec()`メソッドが`selector`の値に応じて`x`か`y`への可変参照を返しています。 93 | このコードは単純ですが、実際にはもっと複雑な処理の結果として`x`か`y`が返されると考えてください。 94 | やりたいことは、`get_vec()`で返された値に対して何らかの定型処理、ここでは`update()`メソッドを適用を行うとします。 95 | 96 | このような処理の場合、以下のように、`get_vec`メソッドと`update`メソッドを利用したくなります。 97 | 98 | ```rust 99 | let mut xy = XY { /* 省略 */ }; 100 | let v = xy.get_vec(); 101 | xy.update(v); // `xy`は借用されているためコンパイルエラー 102 | ``` 103 | 104 | しかし、このコードはコンパイルエラーとなります。 105 | なぜなら、`get_vec()`で返される可変参照が`v`に借用されているため、`update()`で必要な`&mut self`が借用できないからです。 106 | これを解決するために、分配束縛などが利用できます。 107 | 108 | ### 第6章。正規表現エンジンのVM実装について 109 | 110 | `(a*)*`のような正規表現でスタックオーバーフローとなるそうですが、`(a*)*`を`a*`と変換することでスタックオーバーフローを回避可能だそうです。詳細は、下記PRを御覧ください。 111 | 112 | - PRs 113 | - https://github.com/ytakano/rust_zero/pull/4 114 | - https://github.com/ytakano/rust_zero/pull/7 115 | 116 | -------------------------------------------------------------------------------- /fig/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ytakano/rust_zero/843eb4cf9b2dff3b24defc4ada1f04a58a8efe6c/fig/cover.jpg --------------------------------------------------------------------------------