技術書典3に同人誌を出す回
この広告は、90日以上更新していないブログに表示しています。を消したい。
こんにちは、yutoppです。イカ2ばかりしていたら前の記事が200日前などとなっていますね。
さて、10/22に行われる技術書典3に再びサークル進捗大陸で参加します。ので宣伝をします。
今回のサークルページです。詳細についてはこちらを参照していただけると嬉しいです。
場所はお16です。
今回出す本は進捗大陸02です。
500円です。!!!pixiv payと技術書典決済システムが利用可能です!!!
後日電子版をboothで頒布します。こちらは300円です。
今回の目次です。今回は目次を作りました。
自作言語 Rill と WebAssembly (@yutopp, 12ページ)
- 前回の進捗大陸 01 からの自作言語の進捗
- WebAssembly サポート
- シンプルに Hello world!
- ランタイム移植問題: リンクとlibc
- ランタイムビルド
- 全てリンクしたバイナリを作る
- 実行
- 問題点
- 今後の展望
- まとめ
抽象解釈による,再帰を持つ関数型言語の符号解析 (@youxkei, 22ページ)
- はじめに
- 再帰を持つトイ言語
- 型
- 構文
- 型付け規則
- 意味論
- 型システムの健全性
- 符号解析
- 符号解析
- 抽象値
- 抽象環境
- 符号解析の定義
- 符号解析の well-defined 性
- 符号解析の健全性
- 符号解析の例
- まとめ
証明付き分散システムを作ろう! (@amutake, 26ページ)
- イントロダクション
- 本稿の流れ
- Coq の概要
- Coq のインストール方法
- Coq を使ってみる
- コード抽出(extraction)
- Verdi の概要
- Verdi の使い方
- ネットワーク意味論
- VST
- まとめ
- Verdi を使って分散システムを作る
- Verdi のインストール
- ブール値保管システム BoolServ
- Verdi による Raft の証明
- Raft の概要
- Verdi チームが証明した定理
- Verdi による Raft の実装
- Verdi による Raft の証明の流れ
- まとめ
- イントロダクション
以上の本体60ページくらいの本になります。読み応えがあります。
よろしくお願いします!!!!
技術書典2に同人誌を出す回 / Aizu Advent Calendar 2016 24日目
おはようございます。Aizu Advent Calandar 24日目の @yutopp です。
今日は、2016/12/24なので、なんの問題もありませんね………。
今回は、2017/4/9にアキバ・スクエアで開催される技術書典2に、サークルで出るので宣伝します。
サークル名は進捗大陸です。スペースは う-17 です。
今回出す本は 進捗大陸01 です。
本の内容ですが、
となっています。
自作言語 Rill の実装とあれこれは、自分が書いた部分です。
自作言語Rillの紹介と実装の一部分について書きました。本当にふわっとしているので、ふわっとです。
抽象解釈による関数型言語の静的解析は、@youxkei氏が書いて下さいました。ありがとうございます。
この章では、ミニマルな言語を定義し、形式的にその言語の型付けと意味論をしたのち、符号解析を行う抽象解析を定義し、健全性を証明していきます。
抽象解析は、プログラムそのものの具体的な値を計算して解析するのは複雑すぎるので、値の性質のみを見るなどして抽象化した値を計算して解析するようなアプローチです(おそらく)。この章では、値の符号を計算する符号解析が例として用いられます。定義や健全性も証明付きで、1ステップずつ進んでいくので、とても理解しやすいです。オススメです。ぜひ最強の静的解析を目指しましょう。
静的に型がつく Erlang を目指しては、@amutake氏が書いて下さいました。ありがとうございます。
この章では、Erlangの静的型チェッカであるdialyzerが用いている型システムsuccess typingsの解説とその問題点、Erlangの型システムを設計する難しさ、問題解決の鍵になるかもしれない型システムMLsubの解説とその拡張について考察していきます。
日々Erlangで大きめのプログラムを書いていると、dialyzerの便利さに救われることも多いのですが、一方で型チェックやリソース食いすぎ問題に困ることも多々あり、もっと良い型チェッカはないものかと考えてしまいます。この章は、それらの問題に立ち向かったとある型プロの考察です。現行の型システムsuccess typingsや、新たな希望のMLsubの解説が詳しく載っています。オススメです。ぜひ最強の型システムを作りましょう。
以上の47ページくらいの本です。500円です。ぜひよろしくお願いします!!!エイプリルフールではないです。
K-frameworkの紹介 #1 - Aizu AdC 2015 / 20日目
おはようございます.Aizu Advent Calandar 20日目の @yutopp です.
前の人は @GUCC1_sp1ritu4l,次の人は @ishi_kuro です.
今回はK-frameworkというプログラミング言語の意味付けをサポートするフレームワークの紹介をしたいと思います.
日本語の資料がほとんど無い(英語もない)ので,どんどん使っていきましょう!!
間違えなど含んでいると思われます.ご指摘いただけると嬉しいです.
K-framework is 何
概要やチュートリアル,論文などは K-framework にて公開されています.
また,実装はGitHubの kframework/k 上で行われているようです.
何が出来るかというと,K-framework上で configuration
と computation
, rule
を用いて,実行可能なプログラミング言語や型システム,解析ツールを定義できます.
加えて読みやすいドキュメントの自動生成もしてくれます.便利ですね.
C言語やJava,Scheme,LLVMなどの意味論が既にK上で与えられており,実用的なように見えます.
Kで用いられている項書換えの形式的な定義などは解説出来る気がしない(ボ!w)ので,この記事では実際に簡単な言語を定義して使用例を示したいと思います.
実行可能というのは?
K-tools というものがあり,これを用いることでKで定義した意味論を実際に動かすことができます.
今回のサンプルを動かす場合は,GitHubの kframework/k から Version 3.6 のビルド済みパッケージ落としてくるか,私が作ったDocker imageを使うと楽に試せると思います.
ちなみにmasterはK-framework 4.0のSNAPSHOTになっており,挙動が不安定なため使用を見送りました.
言語はどう定義するのか??
冒頭で述べたように,意味論に関しては configuration
,computation
, rule
をK-framework上の記法に従って定義します.
加えて,syntax
として言語の構文も定義し,それらを組み合わせて言語の定義とします.
動く仕組みと部品の説明
形式的ではないざっくりした説明ですが,
まず,K-frameworkは定義したsyntax
に従って文字列の入力をパースして構造に変換します.
次に,定義したrule
に従ってconfiguration
のマッチした構造を書き換えていき,それの繰り返しで入力の実行を行います.
configuration
configuration
はcell
と呼ばれる値の集まりです.
ざっくり説明すると,プログラムのコードやシンボルテーブル,データのストレージなどを保持しておくような所です.
computation
computation
は計算の順序です. という記号で区切られたデータ列で,左から右へと評価されます.
Kでは構文などの構造をcomputation
に落として計算の順序のようなものを作ります.
rule
Kで定義したrule
はK rules
と呼ばれ,これは項書換え規則に変換された後,それに従ってconfiguration
が書き換えられていきます.
rules
は構造をcomputation
に書き換えるstructural rule
と,実際に計算を進めるcomputational rule
に区別されます.
その他
Sort
型みたいなやつ.
Attributes
syntax
の項で指定できるもの.結合規則や評価順序などの指示を与えられます.
KResult
計算の最終状態のSort
を指定します.
オンラインチュートリアル
Run K Online - kweb というものがあり,実際のK-framework上で用いる記法や,意味論の定義の仕方などをステップごとに試すことが出来ます.
面白いな,と思ったものは
- SIMPLE言語 (場所:
tutorial/2_languages/1_simple
)- 並列計算もできる手続き言語のSIMPLE(震え声)言語.Static semantics(Typedと書かれた型付け規則)と,Dynamic semantics(実行時)の両方の定義があるので,静的型付けの言語の意味付けの参考になります.
- 型システムチュートリアル (場所:
tutorial/1_k/5_types
)- 型システムの実装の部分のみを抜き出したチュートリアルです.
- Brainf*ck (場所:
samples/bf/bf.k
)- Bfの意味論が定義されていた… 単純なので読みやすいです.
実際に作成された定義など
- K frameworkのGitHub上のorganization
- ここからC言語,Java,WebAssembly(マジ?!),LLVM,JavaScriptなどのKでの意味論の定義のリポジトリにアクセスできます.やばい.
- K-MetaML
- ぼくの進捗です(大声).MetaMLのような言語を実装してみました.stagingやコード生成などのメタプログラミングもKで定義できてしまいました.
試してみる
プログラミング言語 Hammon Lisp の定義
複雑な言語はK-MetaMLで力尽きたので,ここではステップバイステップで簡単なLisp風言語を定義してみます!
この章を読んだ後に公式チュートリアルを読むとスラスラ理解できるようになっていれば嬉しいです.
K-frameworkにハマりすぎて研究室を破門されないように頑張っていきましょう!
全体のソースコードのリポジトリはここになります.動作確認はK-framework revision(a7673ca)で行いました.多分ver 3.6でも動くと思います.
全体の定義は上のリポジトリを見て頂いて,ここでは重要な点の説明をしていきます.
Step 1. 構文の定義
まず,構文の定義からいきます.ファイル名hammon-lisp.k
の中に,HAMMON-LISP-SYNTAX
というmodule
を作って,構文を定義していきます.
module HAMMON-LISP-SYNTAX syntax Expr ::= SId | Int | "nil" | "t" | "(" SList ")" syntax SList ::= List{Expr, ""} syntax SOp ::= "+" | "-" | "*" | "=" syntax SId ::= Id | SOp | "defun" | "progn" | "lambda" | "if" | "print" endmodule
アトムとS式の構文の定義です.
syntax
キーワードを使って,構文を定義します.まんまBNFですね.多少拡張されており,attribute
を加えたり,優先順位を変更できます.
Step 2. Configuration の定義と下準備
HAMMON-LISP
というmodule
を作って,その中に本体の定義を書いていきます.
module HAMMON-LISP imports HAMMON-LISP-SYNTAX syntax KItem ::= "%t" | "%nil" syntax KResult ::= Int | SId | "%t" | "%nil" syntax KItem ::= "%toplevel" configuration <k> %toplevel $PGM:SList </k> <venv> .Map </venv> <fenv> .Map </fenv> <store> .Map </store> <nextLoc> 0 </nextLoc> <results> .List </results> <out stream="stdout"> .List </out> // rewrite parentheses to << >> // it is needed to avoid confusion of K parsers... syntax Expr ::= "<<" SList ">>" rule ( E:SList ):Expr => << E >> [macro] endmodule
まずimports
を用いて構文の定義を読み込みます.
KItem
というのはKの構造の項みたいなもので,ここに加えた構文はどこでも使えるようになります.
KResult
は計算の最終状態の形です.
configuration
は configuration
キーワードを用いてXMLで定義します.今回の定義のCell
内訳は,
<k>
: プログラム本体.$PGM
の部分にパースされた構造が入ります.<venv>
: 変数とLocationの対応を保存します..Map
というのは空のマップ構造を指します.<fenv>
: 関数とLocationの対応を保存します.<store>
: Locationとデータの対応を保存します.<nextLoc>
: LocationのFreshIdを保存します.<results>
: おまけ.トップレベルのS式を評価したときの結果を保存します.デバッグ用.<out>
: 標準出力です.
となります.変数と関数の名前空間は分割しました.
最後に,S式のカッコを<< >>
に変換するマクロを定義しています.式の結合を変えるカッコとS式のカッコで,K-frameworkのパーサが混乱するようなので…
Step 3. Rules の定義
残るは書き換え規則をガンガン入れていくだけです!
Step 3.1 変数のルックアップ
// Variables rule <k> X:Id => Val ... </k> <venv> ... X |-> Loc ... </venv> <store> ... Loc |-> Val </store>
このルールは以下のように読むと意味を汲みやすいです.
<k>
の先頭にあるId
というSort
を持った項をX
と置いて,<venv>
にX
に対応するLoc
が存在して,<store>
にLoc
に対応するVal
が存在したときに,X
と置いた項をVal
に書き換える
また,=>
は左辺を右辺に書き換えることを示します.
...
は0個以上の何かしらの値がある,ということを示します.
Step 3.2 値
// special value rule <k> nil => %nil ... </k> rule <k> << .SList >> => %nil ... </k> rule <k> t => %t ... </k>
nil
リテラルや空リスト
,真偽値
の変換を定義します.
Step 3.3 defun
// defun syntax KItem ::= func(SId, SList, SList, Map, Map, Int) syntax KItem ::= midDefunCheckParam(SList) rule <k> << defun:SId Sym:SId << Params:SList >>:Expr Exprs:SList >> => midDefunCheckParam(Params) ~> Sym ... </k> <venv> VEnv </venv> <fenv> FEnv => FEnv[Sym <- N +Int 1] </fenv> <store> Sto => Sto[ N +Int 1 <- func(Sym, Params, Exprs, VEnv, FEnv, N +Int 1)] </store> <nextLoc> N => N +Int 1 </nextLoc> rule midDefunCheckParam(P:Id Ps:SList) => midDefunCheckParam(Ps) [structural] rule midDefunCheckParam(.SList) => .K [structural]
まず,func
という構造を定義しておきます.先頭から,関数名,パラメータ,関数本体,定義時の変数環境,定義時の関数環境,この関数のLocationを保持します.
このrule
はdefun
という構造が<k>
の先頭にあったとき,それをmidDefunCheckParam(Params) ~> Sym
という computation
に書き換えます.
で区切られるcomputation
は,K-frameworkの記法では~>
で区切られます.これで,midDefunCheckParam(Params)
が評価されてから Sym
が評価されることになります.
加えて,<fenv>
と<store>
,nextLoc
の値も書き換えます.
<fenv>
を例に説明すると,まず現時点の<fenv>
の内容をFEnv
と置き,それにSym
というキー を N +Int 1
という値に関連つけたものに書き換えます.
midDefunCheckParam
は再帰的に,パラメータが全てId
というSort
になっているかを確かめています.(パラメータに数字などを置かれると困りますからね…)
パラメータの名前の重複判定は省いてしまったので,気になる場合は追加してみてください.
Step 3.4 if
// if syntax KItem ::= midCond(Expr, Expr) rule <k> << if:SId Cond:Expr Then:Expr Else:Expr >> => Cond ~> midCond(Then, Else) ... </k> [structural] rule <k> CV:KResult ~> midCond(Then, Else) => Then ... </k> when CV =/=K %nil [structural] rule <k> %nil ~> midCond(_, Else) => Else ... </k> [structural]
みんな大好きif
です.ここでは,まずif
の条件式
を最初にcomputation
に乗せて評価させ,Then節
かElse節
は構造のまま保っておきます.
そして,条件式
の結果によってThen節
かElse節
の構造をcomputation
に乗せて評価させます.
これによって,Then節
かElse節
の片方のみが評価されます.
rule
にはwhen
節を付けることができて,ここで細かいruleの切り替えを行えます.
Step 3.5 呼び出し
大物になってしまいました…
// === // call // === syntax KItem ::= midFApply(SList) | midBeginApply(K, SList) rule <k> << E:Expr Args:SList >> => E ~> midFApply(Args) ... </k> when notBool isSpecialFormOrBuiltin(E) [structural] rule <k> Sym:Id ~> midFApply(Args) => midBeginApply(Func, Args) ... </k> <fenv> ... Sym |-> Loc ... </fenv> <store> ... Loc |-> Func ... </store> [structural] // (ref 1) syntax KItem ::= midConv(SList, SList, Map, Map) | midEndApply(Map, Map) rule <k> midBeginApply(func(Sym, Params, Body, FVEnv, FFEnv, N), Args) => midConv(Params, Args, FVEnv, FFEnv[Sym <- N]) ~> << progn Body >> ~> midEndApply(VEnv, FEnv) ... </k> <venv> VEnv </venv> <fenv> FEnv </fenv> [structural] // (ref 3) rule <k> V:KResult ~> midEndApply(VEnv, FEnv) => V ... </k> <venv> _ => VEnv </venv> <fenv> _ => FEnv </fenv> [structural] // (ref 2.1) syntax KItem ::= midBind(Id) rule <k> midConv(P:Id Ps:SList, A:Expr As:SList, VEnv, FEnv) => A ~> midBind(P) ~> midConv(Ps, As, VEnv, FEnv) ... </k> [structural] rule <k> Val:Expr ~> midBind(P) ~> midConv(Ps, As, VEnv, FEnv) => midConv(Ps, As, VEnv[P <- N +Int 1], FEnv) ... </k> <store> Sto => Sto[N +Int 1 <- Val] </store> <nextLoc> N => N +Int 1 </nextLoc> [structural] // (ref 2.2) rule <k> midConv(.SList, .SList, VEnv, FEnv) => .K ... </k> <venv> _ => VEnv </venv> <fenv> _ => FEnv </fenv> [structural]
最初の3つのルールは今までの説明で読み取れると思います.今度は<venv>
ではなく<fenv>
から値を取ってきています.
面倒なのが残りのルールで,これは変数環境と関数環境をstatic scopeかつ再帰呼出し可能に設定しているところです(より効率よくできるかもしれません…).
ざっくり説明すると,現在の変数/関数環境を保存した後(ref 1)に,呼び出す関数のパラメータと引数の対応を作りつつ(ref 2.1)新しい変数/関数環境に切り替えて(ref 2.2)関数本体をprognで包んで評価を行い(ref 1のcomputation),評価が終わったら呼び出す前の変数/関数環境に戻す(ref 3),という事をしています.
このあたりはK-frameworkのテクニックというよりは,この本の内容に近いです.
サンプルコード
これでコア部分の説明は終わりました.では,実際にどんなコードをHammon Lispで書けるのかというと,
(defun zoi (x y) 10) (zoi 10 20) (+ 10 20) ((lambda (x y) (+ x y)) 20 30) (defun zero () 0) (zero) (if () 114 514) (if nil 114 514) (if (+ 10) 114 514) (- 10 2) (- 10 2 3) (= 10 20) (= 10 10) (defun f1 (x) (print (= x 0)) (if (= x 0) 100 200)) (f1 0) (f1 1) (defun fact (x) (if (= x 0) 1 (* x (fact (- x 1))))) (print (fact 4))
このくらいなら実行することができます.
コレを実行すると,最終状態のconfiguration
の<results>
Cellから
<results> ListItem ( zoi ) ListItem ( 10 ) ListItem ( 30 ) ListItem ( 50 ) ListItem ( zero ) ListItem ( 0 ) ListItem ( 514 ) ListItem ( 514 ) ListItem ( 114 ) ListItem ( 8 ) ListItem ( 5 ) ListItem ( %nil ) ListItem ( %t ) ListItem ( f1 ) ListItem ( %t ) ListItem ( 100 ) ListItem ( %nil ) ListItem ( 200 ) ListItem ( fact ) ListItem ( 24 ) </results>
という結果が得られます.特に最後のfact
ではしっかり24
という値が得られていますね!
まとめ
高級な言語の意味論を定義でき,形式的な検証を行うことの出来るK-frameworkくんの紹介でした.
LLVM-IRくんの意味も定義されていることだし,Translatorの定義ができたらコンパイラになるんちゃうか?とか考えたりしています.
形式的な定義については教えて下さいお願いします,なんでもしますから!
〜おわり〜
鳥小屋のシンチョク#2
9月に入ってしまうので,鳥小屋のここまでのシンチョクのまとめ.
もう前のシンチョクから1年経ってしまったのか…
相変わらずWandboxがつよくてつらい.
鳥小屋 is 何
いわゆるオンラインコンパイラです.アドレスが変わりました.
ProcGarden(仮)
まだUIが未完成だけれど,ひとまず動く.
昔の方は,データを吸い出し次第止めようかな.
シンチョク
- 相変わらずオープンソースのまま.Githubに置いてあります.プルリク下さい.
- バックエンド(cage)からsandboxを切り離してC++で書き直した.感想 → 良い
- sandboxが独立して使えるようになった.べんり.
- ビルドサーバをgoで書き直した.内部でDockerを用いてパッケージをビルドするようになって管理が楽になった.
- http://factory.pg.yutopp.net:8000/
- webhookと毎時ビルドを実装しなおした.これで毎日最新のパッケージが手に入るぞ!!
- goで書きなおしたので静的に型が付くようになってメンテしやすくなった.最高.
- メモリ使用量がめちゃくちゃ減った.なんなんだ…
- フロントエンドを一から書き直した.
- https://pg.yutopp.net/
- HTTPSを使うようにした
- APIを重点的に作ることにした
- 設定ファイルとビルドスクリプトを統一した
- プロファイルはwebhookで勝手に更新されて配信される
- プルリクで簡単に言語を追加できるぞ!!
まとめ
Rubyでゴチャゴチャ書いていた部分をGoで書き直したら幸せになった.
Goでゴチャゴチャ書いていた部分をC++で書き直したら幸せになった.
ワンワンワンドボックス
おわり
つらい
Boost.Spirit.X3のご紹介 - C++ Advent Calendar 2014(16日目)
この記事はC++ Advent Calendar 2014 16日目の記事です.
おはようございます,@yutoppです.
去年のC++ Advent Calendar 2013ではBoost.Spirit.QiのTipsを書きました.今年は,Boost.Spirit.X3の紹介をしたいと思います!
環境は,Antergos Linux(x64) / Clang 3.5.0 / Boost 1.56.0 です.
また,Boost.Spirit.X3はexperimentalとのことなので,この記事の内容はすぐに古くなる可能性があります.
Boost.Spirit.X3とは?
C++11やC++14の機能を用いて再設計されたBoost.Spirit.Qi
の次のexperimentalな実装です.
Classic Spiritのようなシンプルさを取り戻すことを目標にしているとのことです.
spirit/include/boost/spirit/home/x3
がお目当てのX3の内容になります.
X3を使ったコードには以下のものがありました.
X3の利点
コンパイルが速い
試しにサンプルをビルドして計測してみました.
Qi Calc1 /
Qi Calc2 /
Qi Calc4
X3 Calc1 /
X3 Calc2 /
X3 Calc4
以下の表は,
clang++ -ftime-report -std=c++14 -I /usr/include/boost/ -c 各サンプル名
を10回繰り返し計測したClang front-end timer
のUser Time
の値(sec)の平均です.
calc1 | calc2 | calc4 | |
---|---|---|---|
Qi | 4.283 | 4.409 | 4.96335 |
X3 | 1.87333 | 1.89767 | 2.34934 |
大体今までの2倍の速さでコンパイル出来るようになっているようです.体感でわかる程度に速いので良いですね.
エラーがわかりやすい(Qi比)
内部実装がシンプルになったことによって,エラーも分かりやすくなった気がします.
宣言と定義を同時に書けるようになる(?)
X3からruleの内容を先に定義してから,rule自体を定義するようなスタイルになったので,謎マクロを噛ますことで同時に宣言できます(後述します).
C++14を使いまくれる
ライブラリ自体が最低でもC++14を要求するので,このライブラリをincludeした時点でC++14の機能を使わないともったいないという気持ちになれます.
X3の現状の問題点
undocumented
ドキュメントが無いのでつらいです…が,X3の実装がとても読みやすいので,コードを読みながら何とか使えます.
特に変わったのはruleの定義とセマンティックアクションの記述,attr
とval
の扱いかな,と思います.
定義済みパーサが少ない
仕方がないので自分で書きます.
Tips
X3では,以下のようにruleを定義します.(サンプルより抜粋)
x3::rule<class expression, ast::program> const expression("expression"); auto const expression_def = term >> *( ('+' >> term) | ('-' >> term) ) ; BOOST_SPIRIT_DEFINE( expression = expression_def );
しかし,個人的にはruleとdefを同時に書きたいわけです.
そこで,マクロを噛まします.
実際のコード
#define RULES_BEGIN( struct_name, entrypoint_name ) \ struct struct_name \ { \ using self_type = struct_name; \ \ static auto instance() \ -> struct_name const& \ { \ static struct_name self; \ return self; \ } \ \ static auto entrypoint() \ { \ return instance().entrypoint_name; \ } \ #define RULES_END \ }; #define ANNOTATOR_COL() : #define ANNOTAROR_BASE_SPEC( r, _unused, index, elem ) \ BOOST_PP_IIF( BOOST_PP_EQUAL(index, 0), \ ANNOTATOR_COL, \ BOOST_PP_COMMA \ )() public elem #define RULE( name, type, ... ) \ RULE_WITH_ANNOTATOR( name, type, BOOST_PP_SEQ_NIL, __VA_ARGS__ ) #define RULE_WITH_ANNOTATOR( name, type, bases, ... ) \ class name \ BOOST_PP_SEQ_FOR_EACH_I( ANNOTAROR_BASE_SPEC, _, bases ) \ {}; \ struct PP_ ## name { \ using rule_type = x3::rule<name, type>; \ static auto def( self_type const& t ) { \ return __VA_ARGS__; \ } \ auto operator()() const -> rule_type \ { \ return rule_type( #name ); \ } \ }; \ decltype(( std::declval<PP_ ## name>()) ()) name \ = PP_ ## name()(); \ template <typename Iterator, typename Context, typename Attribute> \ friend inline bool parse_rule( \ decltype(( std::declval<PP_ ## name>()) ()) rule_, \ Iterator& first, Iterator const& last, \ Context const& context, Attribute& attr \ ) \ { \ using boost::spirit::x3::unused; \ auto const& ri = self_type::instance(); \ auto const& raw_def = PP_ ## name :: def( ri ); \ auto const& def_ = ( ri.name = raw_def ); \ return def_.parse(first, last, context, unused, attr); \ }
classの中であれば前方参照できるので,ruleは全体的にclassの中で定義するようにします.
ruleの中身はstruct PP_ ## name
のdef
で定義します.引数のt
がキモで,ruleを定義する際は,t
のメンバ変数として参照するようにします.
また,parse_rule
が呼べればX3から使えるようなので,BOOST_SPIRIT_DEFINE
の定義から抜き出してfriendとして定義しておきます.
実際の使い方はこのような感じになります.
実際のコード
RULES_BEGIN( rules, program ) RULE_WITH_ANNOTATOR( program, ast::module_ptr, ( on_error_annotator_base ), t.module > ( x3::eol | x3::eoi ) ) RULES_END
RULES_BEGIN
では,rules
というクラスを定義して,program
から始まるrule群を定義しています.
RULE_WITH_ANNOTATOR
では,on_error_annotator_base
に従ってアノテーションを行い,ast::module_ptr
型の属性を持つ,program
というruleを定義しています.
t.
でmodule
というrule(ここでは定義していませんが…)を参照しています.残りはQiのときと同じですね.
あとはこのように使うだけです.
このマクロを用いると,ruleの定義を1箇所で行えるようになります.X3自体の効果でコンパイル時間も短くなりますし,良さがありますね.
実際に使ってみた
去年の記事にも書いた文鳥言語で,実際にX3を使ってみました.
(文鳥言語はC++14とBoost.Spirit.X3,LLVMを用いて,C++とDlangライクな言語を目指して開発している言語です)
Boost.Spirit.X3を使ってみて大きいのは,コンパイル時間が短くなったこと,それとコンパイル時間が短くなったこと,後はコンパイル時間が短くなったことが挙げられます.
エラーメッセージが分かりやすくなったこと,マクロで定義を1箇所に書けるようになったのも大きいかもしれません.
C++14のgeneric lambdaと組み合わせると,セマンティックアクションがとても書きやすいのも魅力でした.
Spirit.Qiでコンパイル時間がBoostするのがつらい皆様,ぜひX3を使ってみませんか.
そしてついでに文鳥言語も開発しませんか?とてもアットホームな鳥小屋です.
おわり
experimentalだけど現段階でも十分魅力的なBoost.Spirit.X3の紹介でした.
後日もう少し書き足します…
次
!2!文鳥言語にプルリクください!2! - Aizu Advent Calendar 2014(3日目)
おはようございます.Aizu Advent Calendar 2014,3日目の@yutoppです.
25日分埋まりましたありがとうございます!!!!
さて今日も,Rill(a.k.a. 文鳥言語)についてポエムを書きます.
この記事の要約をすると,"pullreqください" になります.
Rill
文鳥言語にプルリクください - Aizu Advent Calendar 2014(1日目)
今日は?
Rillの構文が変わり,一昨日書いたサンプルコードが早速動かなくなりました.ユーザが自分しか居ないので,言語機能の破壊的変更もなんのそのです.
テンプレートの構文が変わり,より短く記述できるようになりました.
ちなみに,「D言語パクった?」「劣化C++」「劣化D言語」「劣化Embedded C++」などというコメントは大歓迎です.
サンプルの一部
一昨日のやつ
template(T: type) def advent(_: u, c: T) { p("Advent"); return \() => p(c); }
今日のやつ
def advent!(T)(_: u, c: T) { p("Advent"); return \() => p(c); }
その他のサンプルはここにあります!
おわり
このように,Rillの構文や意味付けは数日単位で思いきり変わります.
Rillに関する何かがあれば,喜んで取り入れるのでissueやpullreqメッチャください.一緒に文鳥言語を作りませんか?とてもアットホームな鳥小屋です.
次は〜
(◔⊖◔)つ @i__yahoo
前は〜
@youxkei ⊂(◔⊖◔)
文鳥言語にプルリクください - Aizu Advent Calendar 2014(1日目)
おはようございます.Aizu Advent Calendar 2014一日目の@yutoppです.
現段階で9人枠が余ってるので各位頼む頼む頼む!!!!
さて今年は,Rill(a.k.a. 文鳥言語)についてポエムを書きます.
この記事の要約をすると,"pullreqください" になります.
Rill
Rillは趣味で作っているプログラミング言語です.
自分はC++をよく使うのですけど,構文に対して「こう書けたら良いのになあ」と思うことが多く,その感情を解決してみたかったということと,型理論やLLVMの勉強したさで実装を始めたのがこの処理系です.ア,C++は大好きですよ.
意味が直感的で落とし穴が少なく,ライブラリが作りやすい言語が目標です.[要出典]
C++,Ruby,Go,D,Scalaを主に参考にしています.
静的型付けです.また,この処理系が仕様です.
あれこれ
- 仮引数は,デフォルトでconst参照.
- 値と参照の扱いはC++に似ている.コピーとムーブがある.参照自体の値が欲しければポインタを使う.
- 型に生存期間の情報を付けて,リソースを適切に扱えるようにする.
- GCやスレッドサポートも入れたいところ.もちろんオプションで切り離し出来るようにする.
- コンパイル時処理はD以上に充実させたい.ライブラリを作りやすくするにはメタプログラミングの手厚いサポートが必要だという考えから.
現状の処理系でも,意味解析中にLLVM IRを生成し,IRをJITコンパイルして呼び出すような実装になっているので,Rillの言語機能の全体に近いサブセットをコンパイル時にも実行できるようになっているはず.既にintなどの型情報はコンパイル時はtype型の値として扱われていて,例えば,
const(int)
はただの関数呼び出しであり,コンパイル時にコンパイラ内部のconstという関数
にintの型を表す整数値
を渡して呼び出すLLVM IRを生成してJITコンパイル,実行され,const属性が付加されたint型を表す整数値
を生成する.今のところ上手くいっているけれど,コードが大きくなると重たそうでア! - それでいてC++並の自由さが欲しい.
- "C++の設計と進化"は最高.
つまり?
つまりRillは,文鳥が,小さいライブラリとランタイムを組み合わせて自由に楽しくコードを書けるプログラミング言語(になる|という)わけです.
ちなみに,「Rustと被ってない?」「劣化C++」「劣化D言語」「劣化Embedded C++」などというコメントは大歓迎です.
部分的に実装されている機能
- 基本型(整数,実数,文字列,ポインタ,型,配列)
- 型属性
- リテラル
- CTFE
- UFCS
- モジュール
- 標準ライブラリ
- クラス
- メソッド
- フィールド
- 関数
- 演算子オーバーロード
- テンプレート
- クラステンプレート
- 関数テンプレート
- メソッドテンプレート
- ラムダ式(コピーキャプチャのみのクロージャ)
サンプルコード
def main(): int { val i = "i"; ref calendar = "Calendar"; (Aizu() + i).f("u").advent(calendar)(); return 0; } class Aizu { def ctor() { p( "A" ); } def op +(v: ptr!int8) { p(v); return u("z"); } } class u { def ctor(v: ptr!int8) { this.f(v); } def f(v: ptr!int8): ref(u) { p(v); return this; } } template(T: type) def advent(_: u, c: T) { p("Advent"); return \() => p(c); }
これをコンパイルして実行すると
bytes => A bytes => i bytes => z bytes => u bytes => Advent bytes => Calendar
と出力されます.(bytes =>
はランタイムのデバッグログ)
p
は標準ライブラリに含まれる関数です.ptr
やint8
,type
も標準ライブラリで定義されるクラスです.(標準ライブラリ)
このサンプルではテンプレートや演算子オーバーロード,UFCS,ラムダ式を使ってみました.
その他のサンプルはここにあります!
おわり
プログラミング言語を作るのは楽しすぎてつらいですよね.
Rillは全体的に構文や意味付けも未完成ですので,issueやpullreqメッチャください.一緒に文鳥言語を作りませんか?とてもアットホームな鳥小屋です.
次は〜
(◔⊖◔)つ @youxkei