年賀状でふざけたい!
と言うことで、年賀状を難読プログラミング言語で書くことにしました
どの言語にするか
Brainfuck は王道すぎてつまらない
Ook! とかもいいけど、正月感がない
そうだ、作ってしまえ!
作ることにしました
色々考えた末、 Ook! の形式で、Happy
New
Year
で構成することにしました
名前は Happy
New
Year
の頭文字をとって hnyfuck
とします
リポジトリはこちらです
https://github.com/kimuti-tsukai/hnyfuck
作る過程
※初めての言語作成で、多少下手なところがあると思いますが、温かい目でお見守り下さい
命令はそれぞれ次のようにします
<
=> Happy New
>
=> New Year
+
=> Year Happy
-
=> Happy Year
.
=> Year New
,
=> New Happy
[
=> Happy Happy
]
=> New New
初めてとは言っても、 Rust の proc_macro はたまに作るので、 proc_macro, syn を真似て作ってみます
とりあえず命令をこんなふうに定義します
const SHIFT_LEFT: (&str, &str) = ("Happy", "New");
const SHIFT_RIGHT: (&str, &str) = ("New", "Year");
const INCREMENT: (&str, &str) = ("Year", "Happy");
const DECREMENT: (&str, &str) = ("Happy", "Year");
const OUTPUT: (&str, &str) = ("Year", "New");
const INPUT: (&str, &str) = ("New", "Happy");
const LOOP_START: (&str, &str) = ("Happy", "Happy");
const LOOP_END: (&str, &str) = ("New", "New");
TokenStream を実装します
Queue のように使いたいので VecDeque をラップします
#[derive(Debug, Clone)]
struct TokenStream {
tokens: VecDeque<String>,
}
impl TokenStream {
fn new() -> TokenStream {
TokenStream {
tokens: VecDeque::new(),
}
}
fn push(&mut self, token: String) {
self.tokens.push_back(token);
}
fn from_str(input: &str) -> TokenStream {
let mut stream = TokenStream::new();
for token in input.split_whitespace() {
stream.push(token.to_string());
}
stream
}
fn next(&mut self) -> Option<String> {
self.tokens.pop_front()
}
fn next2(&mut self) -> Option<(String, String)> {
let first = self.next();
let second = self.next();
match (first, second) {
(Some(f), Some(s)) => Some((f, s)),
_ => None,
}
}
fn peek(&self) -> Option<&String> {
self.tokens.front()
}
fn peekn(&self, n: usize) -> Option<&String> {
self.tokens.get(n)
}
}
標準入力を受け取るために InputStream を実装します
#[derive(Debug)]
struct InputStream {
stdin: io::Bytes<io::Stdin>,
}
impl InputStream {
fn new() -> InputStream {
InputStream {
stdin: io::stdin().bytes(),
}
}
fn next(&mut self) -> Option<u8> {
self.stdin.next().and_then(|result| result.ok())
}
}
状態を管理するために、 State と言う構造体を定義します
先頭への追加も高速に行いたいので VecDeque を使います
#[derive(Debug)]
struct State {
state: VecDeque<u8>,
index: usize,
input: InputStream,
}
命令を実装します
impl State {
fn new() -> State {
let mut state = VecDeque::new();
state.push_back(0);
State {
state,
index: 0,
input: InputStream::new(),
}
}
fn shift_left(&mut self) {
match self.index {
0 => self.state.push_front(0),
_ => self.index -= 1,
}
}
fn shiht_right(&mut self) {
match self.index {
i if i == self.state.len() - 1 => {
self.state.push_back(0);
self.index += 1;
}
_ => self.index += 1,
}
}
fn increment(&mut self) {
if let Some(cell) = self.state.get_mut(self.index) {
*cell += 1;
}
}
fn decrement(&mut self) {
if let Some(cell) = self.state.get_mut(self.index) {
*cell -= 1;
}
}
fn output(&mut self) {
if let Some(cell) = self.state.get(self.index) {
print!("{}", *cell as char);
}
}
fn input(&mut self) {
if let Some(cell) = self.state.get_mut(self.index) {
if let Some(byte) = self.input.next() {
*cell = byte;
}
}
}
fn cond(&self) -> bool {
self.state.get(self.index).map_or(false, |cell| *cell != 0)
}
}
プログラムの状態を初期化、実行するための HnyFuck と言う構造体を定義します
#[derive(Debug)]
struct HnyFuck {
stream: TokenStream,
state: State,
}
ループの実装の仕方はよく知らないので、今回はループ内のコードをコピーして現在の状態で実装します
impl HnyFuck {
fn new(stream: TokenStream) -> Self {
Self {
stream,
state: State::new(),
}
}
fn from_str(input: &str) -> Self {
Self::new(TokenStream::from_str(input))
}
fn run(&mut self) {
while let Some((first, second)) = self.stream.next2() {
match (first.as_str(), second.as_str()) {
SHIFT_LEFT => self.state.shift_left(),
SHIFT_RIGHT => self.state.shiht_right(),
INCREMENT => self.state.increment(),
DECREMENT => self.state.decrement(),
OUTPUT => self.state.output(),
INPUT => self.state.input(),
LOOP_START => {
let mut token_stream = TokenStream::new();
let mut depth = 1;
while let Some((token1, token2)) = self.stream.next2() {
match (token1.as_str(), token2.as_str()) {
LOOP_START => depth += 1,
LOOP_END => {
depth -= 1;
if depth == 0 {
break;
}
}
_ => (),
}
token_stream.push(token1);
token_stream.push(token2);
}
let state = std::mem::replace(&mut self.state, State::new());
let mut nest = Self {
stream: token_stream.clone(),
state,
};
while {
nest.run();
nest.stream = token_stream.clone();
nest.state.cond()
} {}
self.state = nest.state;
}
_ => panic!("Invalid token"),
}
}
}
}
これで完成です
Brainfuck から変換するやつも実装したのでどうぞ
fn from_brainfuck(code: &str) -> String {
code
.chars()
.map(|c| match c {
'>' => SHIFT_RIGHT,
'<' => SHIFT_LEFT,
'+' => INCREMENT,
'-' => DECREMENT,
'.' => OUTPUT,
',' => INPUT,
'[' => LOOP_START,
']' => LOOP_END,
_ => panic!("Invalid character"),
})
.map(|(a, b)| format!("{} {}", a, b))
.collect::<Vec<String>>()
.join(" ")
}
ほとんどコードの記事になってすみません