自作言語の構文を考えるのがめんどくさいそこのアナタ、Rustのマクロで楽してもいいわよ?

最近 とっくんのYouTubeチャンネル - YouTube さんの再度ハマり言語野が支配されてます、id:Krouton です。

事の発端

つまり「言語を作りたいけど具象構文考えるのめんどくせ〜〜!!!!」「パーサジェネレータ使うにしてもそれすら書き下すのもめんどくせ〜〜!!!!!」って時ありますよね?(あってくれ)
そんなアナタのためにS式でASTを表現するのをオススメします。Rustが書きたかったのでそうしましたが、他のマクロがサポートされてる言語なら大体できそう?

TL;DR と まとめ

  • (1 + 2) + (3 + 4) を表現するのに
BinOP(
    Box::new(BinOP(Box::new(Number(1)), Add, Box::new(Number(2)))),
    Add,
    Box::new(BinOP(Box::new(Number(3)), Add, Box::new(Number(4)))),
)

から

ast!((+ (+ 1 2) (+ 3 4)))

という風にS式で表現できるようになる。Box::newを手書きするのから解放されるようになる。

  • 簡単にコンパイラのミドルエンド*1から手をつけられるようになる。
  • 言語処理系が書きたくなる。

本編

type AST = Expression;

#[derive(Debug, PartialEq)]
pub enum Expression {
    Number(usize),
    Ident(String),
}

use Expression::*;

impl From<usize> for Expression {
    fn from(n: usize) -> Self {
        Number(n)
    }
}

impl<'a> From<&'a str> for Expression {
    fn from(s: &'a str) -> Expression {
        Ident(s.to_string())
    }
}

macro_rules! ast {
    ($i:ident) => {
        Expression::from(stringify!($i))
    };
    ($e:expr) => {
        Expression::from($e)
    };
}

fn main() {
    dbg!(ast!(2)); // Number(2)
    dbg!(ast!(id)); // Ident("id")
}

ここはFromを使ってるだけで特に難しいところはありませんね。ASTとマクロを拡張して関数適用(Apply)を考えてみましょう。

#[derive(Debug, PartialEq)]
pub enum Expression {
    // 略
    Apply {
        fn_lit: Box<Expression>,
        args: Vec<Expression>,
    },
}

macro_rules! ast {
    (($fn:tt $( $arg:tt )*)) => {
        Apply {
            fn_lit: Box::new(ast!($fn)),
            args: vec![$( ast!($arg), )*]
        }
    };
    // 略
    ($e:expr) => {
        Expression::from($e)
    };
    // exprより後にしないと1 や 2が全てIdentになるので最後に置く
    ($t:tt) => {
        Expression::from(stringify!($t))
    }
}

ast!((print 1 2)); // Apply { fn_lit: Ident("print"), args: [Number(1), Number(2)] }
ast!((+ 1 2)); // Apply { fn_lit: Ident("+"), args: [Number(1), Number(2)] }

ミソとしては四則演算を関数適用として、stringify!で+などの演算子をIdentとして解釈する部分です。これのおかげで、四則演算用のマクロのルールを定義せずに表現できるようになりました。
あとは好きな風にASTを拡張して好きなS式で表現しましょう!

拡張したgist gist.github.com

Special Thanks

*1: コンパイラのミドルエンドって滅多に使わない言葉らしいってWikipedia先生が言ってた コンパイラ - Wikipedia

*2: 質問者は私です

Goの[]byte -> stringへのcastってshrinkするんですか?

珍しく技術系の投稿をします(ただし疑問、忘備録

tooEnoughBuf := make([]byte, tooEnoughSize)
Write1(tooEnoughBuf, src)
string(tooEnoughBuf) == string(src) // false

をしたい時のbufの長さをshrinkする方法を考えています。

なぜこの等価判定がfalseになるかというと

len(tooEnoughBuf) == len(src)

がfalseになるからですね。

func shrinkString(s string) string {
	var eos byte
	buf := make([]byte, 0)
	for _, b := range []byte(s) {
		if b == eos {
			break
		}
		buf = append(buf, b)
	}
	return string(buf)
}

のような実装を与えてあげればいいのだろうか・・・?わからない・・・

株式会社はてなに入社しました

id:Krouton です。株式会社はてなに新卒入社しました。
本日からWebアプリケーションエンジニアです。
株式会社はてなに入社しました - hitode909の日記


所属してる会社は隠そうと思いましたが、GitHubのOrganizationを見れば分かってしまうし隠すのもなんか嫌なのでエントリにすることにしました。試用期間中にリストラされないことを祈っています。
されたら誰か誘ってください。とりあえずがんばります。

www.amazon.jp

五カ年計画(後付)した電気通信大学を卒業しました

こんばんは。3/31で無事卒業できたので電通大卒業エントリを書こうと思います。今は無き情報理工学部の先端工学基礎課程(以下、K課程)に所属していました。

動機

親「国公立に入れ」
僕「家から近いから横国がいいな〜、勉強無理だ。電通大ってとこあるじゃん、前期受けよ!!」

センター試験

僕「緊張しまくって数学半分も解けなかった・・・、もう無理だ、ごちうさでも見てよう」
ごちうさ「線路へぽっぴんジャンプ♪ 恥決定♪」
僕「ア・・・、ア・・・・・」

前期試験後

僕「無理だな、浪人!!」
一緒に電通大受けたフォロワー「俺も無理だった〜、夜間主が2次募集してるんだけど受ける?」
僕「受ける!」

と言った流れで受験し、無事合格しました。フォロワーは落ちました。*1
K課程の2次募集は 3/29-30(午前) に募集、3/30(午後)に合格発表というなかなかアツいスケジュールでした。一人暮らしの人どうやって物件決めたんだ・・・。

1年前期(2015)

Twitterで気が合いそうなオタクを探しながら授業を受ける、バイトをしないといけなかったので授業中に探したりするも1ヶ月ぐらい連続で落ち続ける。*2
プログラミングに興味があったので学内のサークルに入るも、経験がなく何も分からないし、ノートPC持ってなかったので手を動かせずに消える。
Vim vs Emacsみたいなことをしていてたくさん会話してて面白そうだけど一切わからんしここは俺のためのところじゃないなぁと幽霊になる。
結局ファミマでバイトをしてました。初の落単を経験する。

1年後期

幽霊してたサークルの同学年のオタクが技術系バイトをしてることを知る。当人に「最低賃金で立ちっぱのバイトつらそう」と言われ、悔しくなったのでバイト代でMBPを買った。

苦しんで覚えるC言語

苦しんで覚えるC言語

  • 作者:MMGames
  • 発売日: 2011/06/24
  • メディア: 単行本
たのしいRuby 第6版 (Informatics&IDEA)

たのしいRuby 第6版 (Informatics&IDEA)

このへんの本 *3を読んで筋力をつけた。特に実装したいものもなかったが、コードを書きたかったので、基礎科学実験の計算結果を求めるためだったり、LaTeXの表のコードを吐かせるコードを書いたりしてた。実務未経験でバイトを探すのは本当に難しかったが、やる気を買っていただいた人たちと大学名に感謝。Pythonでクローラ書いたりしてた。

2年前期(2016)

SIerっぽいとこがバイト先になった。d3.jsでグラフ書いたり、出たばっかのSwift3を使ってiOSアプリのプロトタイプ版みたいなのを一人で作ったりしてた。JSもSwiftも何もわからなかった。

2年後期

学生ベンチャー企業と掛け持ちした。JSとAWSでサーバサイドをやってた。心理的安全性がありえん低く辛かったのに加え、年明けに高1から付き合ってた彼女に振られて心が壊れる。電車の中で涙が出てきたり、途中下車して号泣したりしてた日もあった。

3年前期(2017)

心が壊れてました。土曜1限起きれなくて落単、これが留年のきっかけになる。あとインターンシップが必修であったので、夏休みに2週間フルタイムで行ってた。そこではCircleCIとシェルをにらめっこしてた。

3年後期

初めて心療内科に行く。行くまでは自分の精神も管理できないどうしようもなくダメな人間だと思っていたけど、見方が変わった。SICPを少し読んだ。インターン先がバイト先になった。ScalaとTypeScriptを雰囲気で書いてた。それまで静的型付けがない言語を触っていたが、型があるといいな〜とか思い始める。この辺から言語に対する興味が強くなった。

4年前期(2018)

セキュキャンのCコンパイラゼミに通る。自分より圧倒的にすごい人達に圧倒される。謙虚になった。セルフコンパイルはできてないし未だにずっと「やろうやろう」考えている。本当に。
knium.hatenadiary.com

K課程は4年前期まで必修が誰でもあるカリキュラムになっているのだが、3年前期の落単科目が突然時間割変更して4年の必修科目に衝突する。留年確定。

4年後期

2科目ぐらいしかなかったのでほぼ引きこもりみたいになってた。わからん。記憶がない。科目はすべて必修だが、出席を取らなかったのでほとんど出た記憶がない。テスト前に教えてくれた友人達に圧倒的感謝。

5年前期

タスクを複数同時に持つと死ぬことを学ぶ。複数持ってたタスクは全部投げ出した。院試を受けたけど全然ダメだった。単位取りきった。就活もした。

5年後期

休学、インターンとバイト、卒業

振り返ってみて

授業の記憶ない・・・。通学に往復3時間かかるとそれだけで起きてる時間の1/5は持ってかれるのでできるだけ近くに住んだほうがいい、でも自分みたいな人間は一人暮らししたら破滅が見えるので難しいね。
4年前期に留年が確定してなかったらセキュキャンに行くことはなかったと思う。*4
GPAが低い自分が言うのもアレですが、情報やプログラミング、CS系は努力した分だけ報われるのがよく分かる分野だと思う。たぶん真の意味で技術は好きじゃないのかもしれないけど、生きる分にはそれでもいいかなってなってきた。何回も挫折したし、やめたくなったけど何とか辞めずに続けたし、これからも何とか続けて生きていたいなぁと思います。

*1: 翌年無事に合格したっぽい

*2: その時までにコンビニ2年半ぐらい経験してたのに・・・

*3: 結局制御構造まではわかったけどポインタはわからんかったぐらい、たのしいRubyは正直覚えてない

*4: 院試と時期被ってたし

Haskellやる

いちいちTokenizerやParserをライブラリを使わずに書くのはダルい。だからパーサジェネレータ(lex/yacc)を使ったりするものだが、Rustで書こうとするとパーサコンビネータである
github.com
が強いっぽい。nomを見てみたら何もわからん状態になったので影響を強く受けたっぽい
github.com
のtutorialから雰囲気でやってみようと思ったけどこれまた何もわからない。
なので、手元にすごいH本もあるしやってみようと思う。

Neovimに気持ち入門した

プログラミングを始めてからほとんどGUIエディタにしか触ったことがなかったしタイルマネージャを触り始めてからキーボードで全部アレコレしたいなという気持ちが強まったので試しにNeovimを突っ込んだ。
VSCodeにもVimのExtensionあったし気が向いたらそっちにも触ってみようかな、LSP周りをきっちり設定してTSでCDKを書けたらいいけど・・・。

とりあえずinit.vimを晒します

set number
syntax on
set fenc=utf-8
set nobackup
set noswapfile
set autoread
set showcmd
set cursorline
set cursorcolumn
set list listchars=tab:\▸\-
set showmatch

call plug#begin()
Plug 'scrooloose/nerdtree'
Plug '/usr/local/opt/fzf'
Plug 'junegunn/fzf.vim'
Plug 'jiangmiao/auto-pairs'
Plug 'prabirshrestha/async.vim'
Plug 'prabirshrestha/asyncomplete.vim'
Plug 'prabirshrestha/asyncomplete-lsp.vim'
Plug 'prabirshrestha/vim-lsp'
Plug 'mattn/vim-lsp-settings'
Plug 'ryanolsonx/vim-lsp-python'
Plug 'thinca/vim-quickrun'
Plug 'ajmwagar/vim-deus'
call plug#end()

if executable('pyls')
    au User lsp_setup call lsp#register_server({
        \ 'name': 'pyls',
        \ 'cmd': {server_info->['pyls']},
        \ 'whitelist': ['python'],
        \ })
endif

noremap <Space> <Nop>

let mapleader = "\<Space>"
noremap <Leader>r :source ~/.config/nvim/init.vim<CR>
noremap <Leader>w :w<CR>
noremap <Leader>wq :wq<CR>
noremap <Leader>a 0
noremap <Leader>e $
noremap <Leader>r <C-r>
noremap <Leader>t :NERDTreeToggle<CR>
inoremap <C-a> <ESC>0i
inoremap <C-e> <ESC>$a
nnoremap <BS> i<BS>
inoremap <C-w> <ESC>wa
inoremap <C-e> <ESC>ea
inoremap <C-b> <ESC>ba
inoremap <C-j> <ESC>ji
inoremap <C-k> <ESC>ki

set title
set smartindent
colors deus
filetype indent on

noremap s <Nop>
noremap <Leader>ss :split<CR>
noremap <Leader>sv :vsplit<CR>
noremap <Leader>h <C-w>h
noremap <Leader>j <C-w>j
noremap <Leader>k <C-w>k
noremap <Leader>l <C-w>l