├── .gitignore ├── examples ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── sanim_proc_macro ├── .gitignore ├── Cargo.toml └── src │ └── lib.rs ├── Cargo.toml ├── Cargo.lock ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target/ -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /sanim_proc_macro/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "sanim_proc_macro", 4 | "examples", 5 | ] -------------------------------------------------------------------------------- /examples/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sanim_examples" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | sanim = { path = "../sanim_proc_macro/" } -------------------------------------------------------------------------------- /sanim_proc_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sanim" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [lib] 11 | proc-macro = true -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "sanim" 7 | version = "0.1.0" 8 | 9 | [[package]] 10 | name = "sanim_examples" 11 | version = "0.1.0" 12 | dependencies = [ 13 | "sanim", 14 | ] 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | do What The Fuck you want to Public License 2 | 3 | Version 1.0, March 2000 4 | Copyright (C) 2022 Cappy Ishihara 5 | 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Ok, the purpose of this license is simple 10 | and you just 11 | 12 | DO WHAT THE FUCK YOU WANT TO. 13 | 14 | === Thai Translation === 15 | 16 | สัญญาอนุญาตทำเหี้ยอะไรก็ได้ 17 | เวอร์ชัน 1.0 18 | ลิขสิทธิ์ (C) พ.ศ. 2565 แคปปี้ อิชิฮาระ 19 | 20 | ทุกคนสามารถทำสำเนาและกระจายสำเนาของเอกสารสัญญาอนุญาตนี้ได้ 21 | แต่ไม่อนุญาตให้เปลี่ยนแปลงเอกสารนี้ 22 | 23 | โอเค วัตถุประสงค์ของสัญญาอนุญาตนี้ง่ายๆ 24 | 25 | มึงทำเหี้ยอะไรก็ได้ เรื่องของมึง -------------------------------------------------------------------------------- /examples/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(uncommon_codepoints)] 2 | #![allow(confusable_idents)] 3 | sanim::sanim! { 4 | ใช้ มาตรฐาน::การจัดเก็บข้อมูล::แฮชแมพ; 5 | #[ย่อย(ดีบัก)] 6 | สาธารณะ แจกแจง เพศ { 7 | ชาย, 8 | หญิง, 9 | } 10 | #[ย่อย(ดีบัก)] 11 | สธณ ชุดข้อมูล บุคคล { 12 | สธณ ชื่อ: สตริง, 13 | สาธารณะ อายุ: จำนวนเต็ม, 14 | สาธารณะ เพศ: เพศ, 15 | } 16 | 17 | ลักษณะ มนุษย์ { 18 | ฟังก์ชัน เดิน(&ตน) { 19 | พิมพ์!("กำลังเดิน"); 20 | } 21 | ฟังก์ชัน พูด(&ตน) { 22 | พิมพ์!("สวัสดี"); 23 | } 24 | ฟังก์ชัน เล่น(&ตน); 25 | } 26 | 27 | วิธี มนุษย์ สำหรับ บุคคล { 28 | ฟังก์ชัน เล่น(&ตน) { 29 | พิมพ์!("เล่นเกมส์"); 30 | } 31 | } 32 | 33 | 34 | 35 | ฟังก์ชัน หลัก() { 36 | พิมพ์บรรทัด!("สวัสดี, โลก!"); 37 | 38 | ให้ นายเอ = บุคคล { 39 | ชื่อ: "สมชาย".เป็นสตริง(), 40 | อายุ: 18, 41 | เพศ: เพศ::ชาย, 42 | }; 43 | 44 | ให้ นางสาวบี = บุคคล { 45 | ชื่อ: "สมหญิง".นำเป็น(), 46 | อายุ: 20, 47 | เพศ: เพศ::หญิง, 48 | }; 49 | 50 | ให้ แปรผัน แมพ = แฮชแมพ::ใหม่(); 51 | 52 | แมพ.แทรก(0, นายเอ); 53 | แมพ.แทรก(1, นางสาวบี); 54 | 55 | พิมพ์บรรทัด!("แมพ: {:#?}", แมพ); 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sanim (สนิม) 2 | 3 | Aren't you เหนื่อย writing Rust programs in English? Do you like saying "เหี้ย" a lot? Would you like to try something different, in an exotic and funny-sounding language? Would you want to bring some Thai touch to your programs? 4 | 5 | **sanim** (Thai for Rust) is here to save your day! as it allows you to write Rust programs in Thai, using Thai keywords, Thai function names, and Thai idioms. Even the great leader Plaek Pibulsonggram himself would be proud of you for writing in such a civilized language! 6 | 7 | Here's an example on what can be achieved with Sanim: 8 | 9 | ## trait and impl (aka ลักษณะและวิธีการ) 10 | 11 | ```rust 12 | #[ย่อย(ดีบัก)] 13 | สาธารณะ แจกแจง เพศ { 14 | ชาย, 15 | หญิง, 16 | } 17 | #[ย่อย(ดีบัก)] 18 | สธณ ชุดข้อมูล บุคคล { 19 | สธณ ชื่อ: สตริง, // shorthand for pub 20 | สธณ อายุ: จำนวนเต็ม, 21 | สธณ เพศ: เพศ, 22 | } 23 | 24 | ลักษณะ มนุษย์ { 25 | ฟังก์ชัน เดิน(&ตน) { 26 | พิมพ์!("กำลังเดิน"); 27 | } 28 | ฟังก์ชัน พูด(&ตน) { 29 | พิมพ์!("สวัสดี"); 30 | } 31 | ฟังก์ชัน เล่น(&ตน); 32 | } 33 | 34 | วิธี มนุษย์ สำหรับ บุคคล { 35 | ฟังก์ชัน เล่น(&ตน) { 36 | พิมพ์!("เล่นเกมส์"); 37 | } 38 | } 39 | ``` 40 | 41 | ## Support for various Thai phrases 42 | (Thai does not have regional swear words) 43 | 44 | ```rust 45 | #[อนุญาต(โค้ดเข้าถึงไม่ได้)] 46 | ฟังก์ชัน บัก(&ตน) { 47 | ปัญหา!("หาสตริงไม่เจอ"); // the proper way to say "panic!" 48 | อ้าว!("งงดิ"); // the polite way 49 | ชิบหาย!("ไรวะ"); // when you're doing this for the 1000th time 50 | ไอ้เหี้ย!("ควย"); // when you're using unsafe code and it causes a memory leak 51 | กรรม!("ไม่น่าเลย"); // when you managed to create a footgun for yourself 52 | } 53 | ``` 54 | 55 | ## Other examples 56 | See the [examples](./examples/src/main.rs) to get a rough sense of the whole thing. 57 | 58 | ## การมีส่วนร่วม 59 | First of all, Thank you for participating in this one big joke. The Thai Government will probably promote this on national news as a "user friendly" way to learn programming. Feel free to throw in a few identifiers here and there, and make a PR against the `หลัก` (Thai for `main`) branch. 60 | 61 | ## But ทำไม? 62 | - The French did it, so why can't we? 63 | - proc macros are very fun 64 | 65 | ## Other languages 66 | - French: [rouille](https://github.com/bnjbvr/rouille) 67 | - Dutch: [roest](https://github.com/jeroenhd/roest) 68 | - German: [rost](https://github.com/michidk/rost) 69 | - Polish: [rdza](https://github.com/phaux/rdza) 70 | - Italian: [ruggine](https://github.com/DamianX/ruggine) 71 | - Russian: [ржавчина](https://github.com/FluxIndustries/rzhavchina) 72 | - Esperanto: [rustteksto](https://github.com/dscottboggs/rustteksto) 73 | - Hindi: [zung](https://github.com/rishit-khandelwal/zung) 74 | - Hungarian: [rozsda](https://github.com/jozsefsallai/rozsda) 75 | - Chinese: [xiu (锈)](https://github.com/lucifer1004/xiu) 76 | - Spanish: [rustico](https://github.com/UltiRequiem/rustico) 77 | - Korean: [Nok (녹)](https://github.com/Alfex4936/nok) 78 | - Finnish: [ruoste](https://github.com/vkoskiv/ruoste) 79 | - Arabic: [sada](https://github.com/LAYGATOR/sada) 80 | - Turkish: [pas](https://github.com/ekimb/pas) 81 | - Vietnamese: [gỉ](https://github.com/Huy-Ngo/gir) 82 | - Japanese: [sabi (錆)](https://github.com/yuk1ty/sabi) 83 | - Danish: [rust?](https://github.com/LunaTheFoxgirl/rust-dk) 84 | - Marathi: [gan̄ja](https://github.com/pranavgade20/ganja) 85 | - Romanian: [rugină](https://github.com/aionescu/rugina) 86 | - Czech: [rez](https://github.com/radekvit/rez) 87 | - Ukrainian: [irzha](https://github.com/brokeyourbike/irzha) 88 | - Bulgarian: [ryzhda](https://github.com/gavadinov/ryzhda) 89 | - Slovak: [hrdza](https://github.com/TheMessik/hrdza) 90 | - Catalan: [rovell](https://github.com/gborobio73/rovell) 91 | - Corsican: [rughjina](https://github.com/aldebaranzbradaradjan/rughjina) 92 | - Indonesian: [karat](https://github.com/annurdien/karat) 93 | - Lithuanian: [rūdys](https://github.com/TruncatedDinosour/rudys) 94 | - Greek: [skouriasmeno](https://github.com/devlocalhost/skouriasmeno) 95 | 96 | 97 | ## Acknowledgements 98 | Big thanks to [rouille](https://github.com/bnjbvr/rouille) for making me realize this was actually possible somehow. I remember seeing ruggine somewhere but I couldn't remember where the create is. 99 | 100 | ## สัญญาอนุญาต 101 | 102 | WTFPL 1.0, because the Author is Thai. Also translated into Thai. -------------------------------------------------------------------------------- /sanim_proc_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::{Group, Ident, TokenStream, TokenTree}; 2 | fn replace_ident(ident: Ident) -> Option { 3 | let ident_str = ident.to_string(); 4 | 5 | let new_str = match ident_str.as_str() { 6 | "พลาด" => "Err", 7 | "โอเค" => "Ok", 8 | "มาตรฐาน" => "std", 9 | "การจัดเก็บข้อมูล" => "collections", 10 | "สตริง" => "String", 11 | "สตริงระบบ" => "OsString", 12 | "เข้าออก" => "io", 13 | "โปรเจคต์" => "project", 14 | "กระบวนการ" => "process", 15 | "สตร" => "str", 16 | "แฮชแมพ" => "HashMap", 17 | "เวคเตอร์" => "Vec", 18 | "ปริยาย" => "Default", 19 | "ข้อผิดพลาด" => "Error", 20 | "เลือก" => "Option", 21 | "ดีบัก" => "Debug", 22 | "แสดง" => "Display", 23 | "โคลนนิ่ง" => "Clone", 24 | "โคลน" => "clone", 25 | "ค่า" => "Value", 26 | "ย่อย" => "derive", 27 | "มี" => "Some", 28 | "ว่าง" => "None", 29 | "ผลลัพธ์" => "Result", 30 | "คำสั่ง" => "Command", 31 | "ตนเอง" => "Self", 32 | "พิมพ์บรรทัด" => "println", 33 | "พิมพ์" => "print", 34 | "อ่าน" => "read", 35 | "เขียน" => "write", 36 | "เปิด" => "open", 37 | "ปิด" => "close", 38 | "อ่านเป็นสตริง" => "read_to_string", 39 | "เป็นสตริง" => "to_string", 40 | "เป็นสตร" => "as_str", 41 | "เก็บ" => "collect", 42 | "เก็บเป็น" => "collect", 43 | "ยกเลิก" => "break", 44 | "ไม่พร้อมกัน" => "async", 45 | "รอ" => "await", 46 | "วน" => "loop", 47 | "ย้าย" => "move", 48 | "ลัง" => "crate", 49 | "โค้ดเข้าถึงไม่ได้" => "unreachable_code", 50 | "ค่อยทำ" => "todo", 51 | "เป็น" => "as", 52 | "ค่าคงที่" => "const", 53 | "ลักษณะ" => "trait", 54 | "เสี่ยง" => "unsafe", 55 | "ใน" => "in", 56 | "จาก" => "from", 57 | "พลวัต" => "dyn", 58 | "แกะ" => "unwrap", 59 | "โดยปริยาย" => "default", 60 | "เป็นอ้างอิง" => "as_ref", 61 | "ภายนอก" => "extern", 62 | "เท็จ" => "false", 63 | "ฟังก์ชัน" => "fn", 64 | "เหนือ" => "super", 65 | "แทรก" => "insert", 66 | "นำ" => "get", 67 | "อนุญาต" => "allow", 68 | "ปัญหา" | "ชิบหาย" | "ไอ้เหี้ย" | "กรรม" | "อ้าว" => "panic", 69 | "โมดุล" => "mod", 70 | "แปรผัน" => "mut", 71 | "ใหม่" => "new", 72 | "สำหรับ" => "for", 73 | "เอาหรือใส่ด้วย" => "get_or_insert_with", 74 | "หลัก" => "main", 75 | "สาธารณะ" | "สธณ" => "pub", 76 | "ว่างเปล่า" => None?, 77 | "ให้ผล" => "return", 78 | "วิธี" => "impl", 79 | "อ้าง" => "ref", 80 | "จับคู่" => "match", 81 | "ถ้า" => "if", 82 | "ไม่งั้น" => "else", 83 | "ตน" => "self", 84 | "ให้" => "let", 85 | "สถิต" => "static", 86 | "ชุดข้อมูล" => "struct", 87 | "หวัง" => "expect", 88 | "เมื่อ" => "where", 89 | "ในเมื่อ" => "while", 90 | "ใช้" => "use", 91 | "นำเป็น" => "into", 92 | "จริง" => "true", 93 | "แจกแจง" => "enum", 94 | "เอา" => "take", 95 | "เอาออก" => "remove", 96 | "จำนวนเต็ม" => "i32", 97 | "จำนวนเต็มไม่ลบ" => "u32", 98 | "ทศนิยม" => "f32", 99 | 100 | _ => &ident_str, 101 | }; 102 | 103 | let new_ident = Ident::new(new_str, ident.span()); 104 | Some(TokenTree::Ident(new_ident)) 105 | } 106 | 107 | fn replace_tree(tok: TokenTree, out: &mut Vec) { 108 | match tok { 109 | TokenTree::Group(group) => { 110 | let mut group_elem = Vec::new(); 111 | replace_stream(group.stream(), &mut group_elem); 112 | let mut new_stream = TokenStream::new(); 113 | new_stream.extend(group_elem); 114 | out.push(TokenTree::Group(Group::new(group.delimiter(), new_stream))); 115 | } 116 | TokenTree::Ident(ident) => { 117 | if let Some(ident) = replace_ident(ident) { 118 | out.push(ident); 119 | } 120 | } 121 | TokenTree::Punct(..) | TokenTree::Literal(..) => { 122 | out.push(tok); 123 | } 124 | } 125 | } 126 | 127 | fn replace_stream(ts: TokenStream, out: &mut Vec) { 128 | for tok in ts { 129 | replace_tree(tok, out) 130 | } 131 | } 132 | 133 | #[proc_macro] 134 | pub fn sanim(item: TokenStream) -> TokenStream { 135 | let mut returned = Vec::new(); 136 | replace_stream(item, &mut returned); 137 | let mut out = TokenStream::new(); 138 | out.extend(returned); 139 | out 140 | } 141 | --------------------------------------------------------------------------------