ドキュメント
mimiumに関する概要
mimium(MInimal-Musical-medIUM)は、音楽やサウンドを表現/生成するためのドメイン特化プログラミング言語です。
mimiumを使うと、LLVMに裏打ちされた高いパフォーマンスを享受しつつ、簡潔な記述で低レベルなオーディオ処理を書くことができます。
mimiumのソースコード例1
以下のmimiumのコードを見てみましょう。
fn lpf(input:float,fb:float){
return (1-fb)*input + fb*self
}
このコードはローパスフィルタ、すなわち入力信号 input
の高音域をカットし低音域のみを通すフィルタをmimiumで実装したものです。
mimiumのキーワード self
は関数内でのみ使うことができるキーワードで、その関数が最後に呼び出されたときの返り値が格納されています。このキーワードは音楽プログラミング言語Faustに影響を受けており、信号処理におけるフィードバック接続簡単かつ簡潔に記述できるものです。
mimiumのソースコード例2
また、mimiumでは「時間再帰」 (temporal recursion) を用いて音符単位の処理を書くことができます。以下のコードを見てみましょう。
fn noteloop()->void{
freq = (freq+1200)%4000
noteloop()@(now + 48000)
}
このコードは1秒おきに周波数 freq
の値を 1200
加算する関数の定義です。
@
を使うと、関数が実行される時間を指定して関数を呼び出すことができます。これを実現するイベントスケジューリング機構はオーディオドライバの要求によって駆動される、サンプル単位の精度を持ちます。
この機能は音楽プログラミング言語Extemporeから影響を受けました。
1 - ユーザーガイド
1.1 - Getting Started
Let’s start your mimium experiences
実行環境
mimiumは現在以下の環境で利用可能です。
- macOS(x86のみ)
- Linux(ALSAを利用します)
- Windows 10
Visual Studio Code拡張ではじめる
もっとも簡単にmimiumを利用する方法は無料のテキストエディタ/IDEであるVisual Studio Codeを利用する方法です。
- Visual Studio Code を公式Webサイトからインストールする。
- Visual Studio Codeを起動したら、拡張機能メニューを開く。 (Cmd+Shift+X).
- 検索欄から"mimium"と検索し、出てきた拡張をインストールする。
- `hello.mmm’という名前でファイルを作成し、以下のコードを貼り付けて保存し、再度開く。
- 最新版のmimiumバイナリをダウンロード、インストールするか聞くダイアログが現れるのでインストールする。
- Cmd+Shift+P でコマンドパレットを開き、“mimium"で検索する。 “Run currently opening file” を選択すると、ターミナルが新たに立ち上がり、現在開いているファイルを実行してくれる。
- Ctrl+Cを押すこと音を止めることができる。
// hello.mmm
fn dsp(){
output = sin(now*440*2*3.141595/48000)
return (output,output)
}
その他のインストール方法
GitHub Releaseより最新版のバイナリをダウンロードできます。bin/mimium
を適当な場所(macOS/Linuxであれば/usr/local/bin
など)にコピーしてください。
macOS,Linuxユーザーの場合はHomebrew/Linuxbrewを利用してインストールができます。
brew install mimium-org/mimium/mimium
手動ビルドなどより詳しい情報は インストールページを参照してください。
また、エディタにVisual Studio Codeを利用している場合は拡張機能の検索からmimium-languageをインストールすることでシンタックスハイライトが利用できます。これ以外の環境の場合はRustのシンタックスハイライトを利用すると概ね正しく表示してくれます。
実行
コマンドラインからmimium
を実行することでmimiumを利用できます。正しくインストールされていれば以下のコマンドからヘルプが参照できます。
現在の実行ディレクトリにhello.mmm
というファイルを作成し、上のコードをコピーして保存してください。
そして、ターミナルから以下のコマンドを実行してみましょう(スピーカーの音量に注意してください)。440Hzのサイン波が聞こえてくるはずです。
Making Soundのページからより詳しい文法の解説に進みましょう。
1.1.1 - インストール
mimiumをインストールしてみよう
Homebrewを使ったインストール
macOSとLinux向けのパッケージマネージャツール、Homebrewを利用すると、簡単にmimiumをインストールできます。
もしhomebrewをインストールしていない場合は、ターミナルアプリを開いて、以下の行をコピー&ペースト、Enterキーを押して実行してください。(より詳しい方法はhomebrewのドキュメントを参照してください。)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
brew
コマンドが使えるようになったら、以下のコマンドをコピー&ペースト、実行するとインストールされます。
brew tap mimium-org/mimium && brew install mimium
以下のコマンドを打ってバージョンが表示されたらインストール完了です!
mimium --version # will return mimium version:x.x.x
手動インストール
mimiumのビルド済みバイナリをGithub Release Pageからダウンロードすることが可能です。
mimium-vx.x.x-Darwin.zip がmacOS用、 mimium-vx.x.x-Linux.zip がLinux用、 mimium-vx.x.x-Windows.zip がWindows用です。
ダウンロードとzip展開が終わったら、bin
フォルダの中にあるmimium
プログラムを/usr/local/mimium
に、lib
フォルダにあるファイルすべてを/usr/local/lib
にコピー/移動/シンボリックリンクを貼るのいずれかを行ってください。GNU/Linuxにおいては/usr/local/lib
はデフォルトで動的リンクのライブラリパスに入っていないので、/usr/local/lib
を/etc/ld.so.conf.d/
配下のファイルに追記し、ldconfig
をroot権限で実行してください。Windowsでは適当な場所にダウンロードしたフォルダを配置して、環境設定からパスを通してください。
ソースからビルドする
mimiumの実行環境はC++で書かれていますので、ビルドにはC++のコンパイラーが必要です。
macOSの場合はxcode-select --install
を実行してインストールされるclang、Linuxの場合はGCC >= 9が推奨されています。(g++ on macやclang++ on Linuxでは標準ライブラリのリンクに競合を起こすことがあります)
Windowsの場合は、MSYS2のMinGW64環境を利用してビルドできます。
加えて、以下のツールやライブラリが必要になります。
macOSでは、xcode-select
でclangをインストールしたタイミングでcmakeとgitはインストールされています。
これらに加えて RtAudio(クロスプラットフォーム向けオーディオドライバライブラリ) にも依存しますが、RtAudioはcmakeが自動でダウンロード&ビルドするので手動でインストールする必要はありません。
これらのツールやライブラリを brew
やLinuxの場合は apt-get
、MSYS2ではpacman
を用いてインストールするのが簡単です。
Ubuntu 18.04(Bionic)では、
apt
でインストールできる
bison
は3.0.4と古いバージョンでこのプロジェクトに対応していません。手動でインストールする必要があります。
自動ビルド&テストのためのGithub Actions Workflow には依存パッケージのインストールや後述のビルド手順も含め、手動ビルドの手順が網羅されていますのでチェックしてみてください。
GitHubからソースコードをダウンロード
git clone https://github.com/mimium-org/mimium.git
cd mimium
# 'master' が安定版です。'dev'ブランチで開発版のビルドができます。他、v0.1.0などリリースバージョンのtagを用いてバージョンを変更することが可能です。
git checkout master
cmake -Bbuild . -DCMAKE_BUILD_TYPE=Debug
このステップでCMakeは自動でRtAudioをダウンロード&ビルドします。
cmake
コマンドには、-D
を先頭につけることで以下のようなオプションが渡せます。
-DCMAKE_INSTALL_PREFIX=/your/directory
後述のインストールステップのコピー先(標準では/usr/local)-DCMAKE_BUILD_TYPE=Debug
最適化のレベルの指定。 ‘Debug’, ‘Release’, ‘MinSizeRel’ , ‘RelWithDebinfo’から選べる-DCMAKE_CXX_COMPILER=/path/to/compiler
ビルドに使用するC++コンパイラの指定。-DBUILD_SHARED_LIBS=ON
ONにするとライブラリを動的リンクライブラリとしてビルドします(Linux、Windowsではうまく動かないケースがあります)。-DBUILD_TEST=ON
テストをビルド対象に含めます。-DENABLE_COVERAGE=ON
GCovを利用したカバレッジ計測のためのコンパイラオプションを有効化します。
ビルド
-j
はビルド時にCPUを並列で使用できる最大スレッド数を指定できます。-j8
なら最大8スレッド、番号なしの-j
は可能な限り多くのコア/スレッドを使用します。
インストール
cmake --build build target=install
アンインストール
cmake --build build --target uninstall
このアンインストールステップでは、先ほどのインストールステップで自動生成された build/install_manifest.txt
に書かれている情報を利用してファイルを削除します。もし失敗した場合はこのファイルの中身を確かめるか、もう一度ビルド、インストールを行ったあとアンインストールするを順番に行ってみてください。
バイナリでインストールした場合は/usr/local/bin/mimium
、/usr/local/lib/libmimium*
を削除してください。
ソースからビルドした場合は以下のコマンドでインストールできます。
Visual Studio Code向けシンタックスハイライト
シンタックスハイライトとして、クロスプラットフォームで動作するVisual Studio Code向けの拡張機能を用意しています。
vscode上でのextensionsパネルでmimium-language
と検索するか、以下のリンクよりインストールをしてください。
https://marketplace.visualstudio.com/items?itemName=mimium-org.mimium-language
より詳しい開発環境構築に関しては
環境構築を参照してください。
1.1.2 - サウンドを作る
ここではmimiumを使った基本的なサウンドの作り方を学びます。
440Hzのサイン波の音をつくろう
- 初めに「hello.mmm」というファイルを用意します。
- そのファイルに以下のスニペットを貼り付けて、ファイルを保存します。
fn dsp(){
output = sin(now*440*3.14*2/48000)
return (output,output)
}
- 最後にbashなどを使って、先程のプログラムを起動します。
1.1.3 - サンプル例
mimium初心者のための例を交えた簡単な機能紹介。
- dsp系
- selfを使用した例
- シンプルなエフェクト
- composition系
- ドレミ音
- リズムパターン
サンプル: ドレミ音
下の例では、システムのオーディオドライバーのサンプルレートが48000Hzの時、ドレミファソラシドを1秒おきに鳴らします。
notes = [260.7,293.3,330,347.7,391.1,440,495]
index = 0
fn updateIndex(){
index = (index+1)%7
updateIndex()@(now+48000)
}
updateIndex()@48000
fn dsp(){
vol = 0.2
octave = 1
sec = now/48000
freq = notes[index]
out = vol * sin(octave*freq*3.14*2*sec)
return (0,out)
}
チェックポイント1「配列」
notes = [260.7,293.3,330,347.7,391.1,440,495] // 1行目
mimiumでは、配列を定義することができます。配列の定義は[]
でおこないます。インデックスの先頭は0です。
この例のドレミ音を作る1行目の配列には、ドレミファソラシ音の周波数を格納し、その後の処理で使用しています。
- ド音: 260.7Hz
- レ音: 293.3Hz
- ミ音: 330Hz
- ファ音: 347.7Hz
- ソ音: 391.1Hz
- ラ音: 440Hz
- シ音: 495Hz
配列の利用の仕方は、1,2行目のように配列名[インデックス]
で表現します。
freq = notes[index] // 1,2行目
チェックポイント2 継時再帰(Temporal Recursion)
// 3~7行目
fn updateIndex(){
index = (index+1)%7
updateIndex()@(now+48000)
}
updateIndex()@48000
v0.3.0現在の仕様では、一般的な言語がもつforループ文は採用していませんが、再帰関数を定義して、再帰ループを3~6行目のように表現することができます。
ドレミ音の例では、7行目で48000サンプル時にupdateIndex()
を実行した後、関数内でnow
キーワードと@
キーワードを併用して、現時点から48000サンプル時点にupdateIndex()
を実行しています(5行目)。
チェックポイント3「nowキーワードと@キーワードの併用」
updateIndex()@(now+48000) // 5行目
mimiumでは、現在のサンプル数をnow
で取得することができます。ユーザーは、v0.3.0現在の仕様では、now
キーワードが、リアルタイム時間を表していないことに注意してください。単位はサンプルです。
また、mimiumには、@
キーワードという演算子があります。
これは 「@
キーワード以降の式で算出されたサンプル数のときに、@
前の関数を実行する」 という意味になります。
さらに、@
の時間は、オーディオドライバが起動してからの絶対時間を示しており、7行目のようにupdateIndex()@48000
と書いた場合は、必ず起動してから48000サンプル後に1度だけupdateIndex()
を実行する形になります。
例の5行目では、now
と48000
を+
演算子で結ぶことで、現時点から先のサンプル時点を定めることができ、さらに@
を使うことでそのサンプル時点で関数を実行することができます。
チェックポイント4「オクターブ」
// 8~15行目
fn dsp(){
vol = 0.2
octave = 1
sec = now/48000
freq = notes[index]
out = vol * sin(octave*freq*3.14*2*sec)
return (0,out)
}
音階は、振動数(Hz)が倍になれば1オクターブ上がり、反対に半分になれば1オクターブ下がる関係性があります。例の8〜15行目では、octave = 1
と固定なので260.7Hz〜495Hzのドレミファソラシド音を鳴らすのですが、例えばこの値を2と変更すれば1オクターブ上がった音階を表現することができます。
1.2.1 - 文法定義
mimium言語の文法定義に関する項目です。
mimium言語の文法規則に関する項目です。
コメントアウト
C++やJavaScriptと同様、行中の//
より右側はコメントとして扱われます。
また/* */
のように囲むと、複数行をまとめてコメントアウトできます。
変数宣言、代入
mimiumでは=
演算子で値を代入すると、左辺に指定した名前の変数がまだ存在していなければ新しく変数が作られます。
letのような変数宣言時のキーワードは必要ありません。
変数はすべて変更可能(mutable)です。すでに宣言されている変数に新たに値を代入もできます。
mynumber = 1000 // variable is declared, and 1000 is assigned
mynumber = 2000 // 2000 is newly assigned to mynumber
型
型とは変数などのデータを数値や文字列など目的に応じて区別するための概念です。
mimiumは静的型付け言語と呼ばれる、コンパイル時に(音を実際に鳴らす前)すべての型が決定される言語です。
静的型付け言語は一般的に、実行中に型をチェックする言語よりも実行速度の面で有利です。その一方、型の指定を手動で行う場合は記述が長くなりがちというデメリットも存在しますが、mimiumでは型推論と呼ばれる、文脈から型が自動的に決定できる場合は型注釈を省略できる機能が存在しているので、コードを簡潔に保つことが可能です。
型にはそれ以上分解できない最小単位であるプリミティブ型と、複数の型を組み合わせて作る合成型(aggregate type)が存在します。
型の明示的な注釈は変数の宣言と関数の宣言時に可能です。
変数および関数のパラメータでは名前に続けて:
(コロン)を挟み型名を書くことで指定可能です。
以下のように異なる型へ代入した場合はコンパイル時にエラーが発生します。
関数での型宣言では返り値をパラメータの括弧に続けて->
を挟んで書くことで指定できます。
fn add(x:float,y:float)->float{
return x + y
}
このadd関数の場合、文脈からxとyがfloatであることを予測できるので以下のように省略できます。
fn add(x,y){
return x+y
}
プリミティブ型
mimiumにおけるプリミティブ型はfloat
とstring
、void
のみです。
mimiumでは数値型はfloat
(内部的には64bit float)のみとなっています。
整数を利用するにはround
、ceil
、floor
関数などを利用します。
string
型の値は"hoge"
のようにダブルクオーテーションで囲った文字列リテラルから生成できます。
現在は文字列の切り出しや結合には対応しておらず、用途は基本的には
printstr
関数に渡してデバッグ用途に使うloadwav
関数に渡してオーディオファイルを読み込むinclude
に渡して他のソースファイルを読み込む
のいずれかに限られています。
void
は値を持たない型で、関数の返り値が存在しないことを明示するのに使用します。
合成型
配列
配列は、同じ型の値を複数個連続して格納できる型です。[]
(アングルブラケット)で囲んだカンマ区切りの値で生成できます。
myarr = [1,2,3,4,5,6,7,8,9,10]
配列型の値にmyarr[0]
のようにアングルブラケットで0基準のインデックスを指定することで配列の値を取り出すことができます。
arr_content = myarr[0] //arr_content should be 1
また左辺値に同様にアングルブラケットを使うことで配列の中身を書き換えることができます。
myarr[4] = 20 //myarr becomes [1,2,3,4,20,6,7,8,9,10]
配列のサイズは固定です。配列の後ろに値を追加していくような操作はできません。また境界チェックもないため範囲外へのアクセスはクラッシュを引き起こします。
自動内挿
インデックスは小数点以下の値でアクセスされた場合、自動で線形補完されて出力されます。
arr_content = myarr[1.5] //should be 2.5
自動で整数に丸められることはないので内挿を避けたい場合はround
関数などでインデックスを丸める必要があります。
タプル
タプルは、異なる型を1つにまとめた値です。変数を()
(丸括弧)で囲んでカンマ区切りの変数を入れることで生成できます。
タプルは配列とも似ていますが、各要素で異なる型を持つことができます。
左辺値にカンマ区切りの変数を置くことでタプルの値を取り出すことができます。この時には括弧で区切る必要がありません。
今後、左辺値で分解するだけではなく、mytup.1
のようにインデックスで取り出す記法も実装される予定です。
タプルはmimiumの中では典型的に信号処理でステレオやマルチチャンネルなどのオーディオ信号のチャンネルをまとめて扱うために利用されています。
型エイリアス
タプルは型注釈が長いので、以下のような構文でエイリアスを作ることができます。
type FilterCoeffs = (float,float,float,float,float)
構造体(レコード型)
構造体は、タプルと同じ機能を持ちますがフィールド名を持たせることが可能です。
構造体型の変数には変数.フィールド名
のようにドット演算子でアクセスすることができます。
構造体は無名型が作れません。 先に型エイリアスを宣言してから型名{変数1,変数2...}
という構文で初期化して使用する形になります。
type MyBigStruct = {"field1":float,"field2":FilterCoeffs,"field3":string}
mystr = MyBigStruct{100,(0.0,1.0,1.2,0.8,0.4),"test"}
println(mystr.field1)
printlnstr(mystr.field3)
mystr.fst = 111
のように構造体へ再代入する構文は現在実装中です。
関数
関数は、複数の値を取って新しい値を返すような、再利用可能な手続きをまとめたものです。
例として2つの値を足算して返すだけのadd関数を考えます。
mimiumで型を明示して変数として格納する場合は以下のように書きます。
fn add(x,y){
return x+y
}
mimiumでは関数が第一級の値として扱えます。これは、関数を変数に代入したり、関数のパラメータとして取ったりすることができるということです。
たとえば、先ほどのadd関数の型注釈は(float,float)->float
のようになっています。先ほどのadd関数を変数に代入する場合は以下のように書けます。関数を関数のパラメータとして代入する場合は高階関数の項を参照してください。
my_function:(float,float)->float = add
無名関数(ラムダ式)
実は先ほどの関数宣言は以下のような、無名関数を変数に格納する構文へのエイリアスです。
add = |x:float,y:float|->float{return x+y}
このような関数を変数に代入しないまま直接呼び出すことも可能です。
println(|x,y|{return x + y}(1,2)) //print "3"
またブロックの構文のところで解説しますが、ブロックの最後の1行はreturnの代わりに単に式を置くことで、returnの代わりにできます。つまり、add
関数は型推論も組み合わせると以下の例まで省略できます。
パイプ(|>
)演算子
mimiumではパイプ演算子|>
利用することでa(b(c(d)))
のようにネストした関数呼び出しをd |> c |> b |> a
のように書き換えることができます。
現在パイプ演算子はパラメータが1つの関数でのみ使用可能です。今後パラメータパックなどの機能でタプル型の値を自動展開することでパラメータが2つ以上の関数でも利用できるようになる予定です.
再帰によるループ
名前のついている関数は自分自身を呼び出すことも可能です。
階乗を計算するfact関数は以下のように定義できます。
fn fact(input:float){
if(input>0){
return 1
}else{
return input * fact(input-1)
}
}
再帰関数は無限ループを発生させる可能性があるので注意して使用してください。
self
関数の中では、self
という特別なキーワードが使用できます。
self
は関数が最後に返した値を参照できる変数です。たとえば、以下のような関数を作ると呼び出される度increment
ずつ増える値を返します。
fn counter(increment){
return self+increment
}
selfは基本的にdsp
関数を起点にして呼び出される関数でのみ使用できます。selfはオーディオエンジンが開始された時に0で初期化され、呼び出しコンテキストごとに別々の値が生成、管理されます。
たとえば以下の例ではcounter
関数にそれぞれ異なるincrementを与えていますが、この場合内部的にselfのためのメモリは2つ分確保され、lchは毎サンプル0.01ずつ増えて1を越えるたび0にリセットされ、rchは毎サンプル0.05ずつ増えます。
fn dsp()->(float,float){
lch = counter(0.01)%1
rch = counter(0.05)%1
return (lch,rch)
}
selfは現在常に0で初期化されます。この初期値を変更する方法は現在検討中です。
変数のスコープ
mimiumはレキシカルスコープと呼ばれる言語で、関数の外側で定義されている変数を参照することが可能です。
TBD
式(expression)、文(statement)、ブロック
関数などで使われていた中括弧{}
で囲まれた文の集まりはブロックと呼ばれる単位です。
文(statement)はほとんどの場合a = b
のような式の代入をする構文で構成されています。
**式(expression)**は1000
のような数字、mynumber
のような変数シンボル、1+2*3
のような演算式、add(x,y)
のような返り値を持つ関数呼び出しなどで構成される単位です。
ブロックは実は式の1つです。
ブロックには複数の文を置くことができ、最後の1行はreturn
を使って返却する値を指定できます。また最後の行のreturnは省略も可能です。
たとえば以下のような構文も文法上は正しいです。(v0.3.0現在この構文は実装が間違っていて動きません。)
//mynumber should be 6
mynumber = {
x = 2
y = 4
return x+y
}
条件分岐
mimiumの条件分岐はif (condition) then_expression else else_expression
という構文を持っています。
condition
、then_expression
、else_expression
はすべて式です。
conditionの値が0より大きい時then_expression
部分が、そうでなければelse_expression
が評価されます。
then/elseの部分をブロックとして表現すれば、以下のようにC言語風の書き方ができます。
fn fact(input:float){
if(input>0){
return 1
}else{
return input * fact(input-1)
}
}
一方でif文自体も式として扱えるので、同じ構文を以下のように書き換えることもできます。
条件部分の括弧は省略することができません。
fn fact(input:float){
return if (input>0) 1 else input * fact(input-1)
}
@
演算子による遅延実行
関数呼び出しに続けて@
と数値型の値を続けることで、関数の実行を遅らせることができます。
時間の単位はサンプルです。
たとえば以下の例ではオーディオドライバをスタートしてから0サンプル目と48000サンプル目に、100と200を続けて標準出力に書き込みます。
println(100)@0
println(200)@48000
現在@
演算子はvoid
型の(返り値を持たない)関数にのみ使用することが可能です。
再帰関数の実行を@
で遅延させることにより、一定間隔で特定の処理を繰り返すことも可能です。
たとえば以下の例では48000サンプル間隔で0から1ずつ数値を増やして標準出力に書き込みます。
fn loopprint(input)->void{
println(input)
loopprint(input+1)@(now+48000)
}
loopprint(0)@0
include
include("path/to/file.mmm")
という構文を用いると他のファイルをそのファイル内で読み込むことができます。
ファイルパスは絶対パスもしくはそのファイルからの相対パスで指定します。
現在は名前空間の分割などはなく、純粋にinclude文をそのファイルのテキストに置換するだけになっています(ただし、一度読み込まれたファイルが2回以上読み込まれることはありません)。
BNFによる文法定義、演算子の優先順位など
TBD
1.2.2 - 組み込み関数
mimiumでライブラリなしで利用できるプリミティブな関数
mimiumにおける組み込み関数について説明します。
入力をtime(単位:sample)だけ遅らせた値を返します。
timeの最大値は現在44100サンプルで固定されており、実際のディレイタイムにかかわらず44100サンプルを保存できるだけのメモリが必ず確保されています。これは将来的にコンパイル時定数の機能実装によって改善される予定です。
たとえばディレイはselfと組み合わせることで以下のようなフィードバックディレイを作ることが可能です。
fn fbdelay(input:float,time:float,feedback:float){
return delay(input*self*feedback,time)
}
random()->float
-1~1のランダムな値を返します。
C++言語上での実装は以下のようになっています。
(double)rand() / RAND_MAX) * 2 - 1
数学関数
C言語のmath.hの以下の関数を呼び出します。注記がない場合は1つのfloatを受け取り1つのfloatを返却します。
sin
cos
tan
asin
acos
atan
atan2
(x,y)sinh
cosh
tanh
log
log10
exp
pow
(x,y)sqrt
abs
ceil
floor
trunc
round
fmod
(x,y) %
演算子はこの関数へのエイリアスですremainder
(x,y)min
(x,y) fminへのエイリアスmax
(x,y) fmaxへのエイリアス
print / println / printstr
デバッグ用途などに利用される関数です。
標準出力に値を出力します。
print
、println
は数値型のみを受け付けます。println
は改行を入れて出力します。
printstr("hoge")
のようにすると文字列の出力が可能です
loadwav(path:string)->[float x 0] / loadwavsize(path:string)->float
LibSndFileを利用してオーディオファイルを読み込みます。
どちらもオーディオファイル(.wav、.aiff、.flacなど)のファイルパスをパラメータにとります。
パスは絶対パスでなければソースファイルの位置を基準とした相対パスとして解釈されます。
loadwavsize(path)
はオーディオファイルのサンプル数を返却します。
loadwav(path)
はオーディオファイルを読み込み配列として返却します。
ファイルサイズより大きいインデックスでアクセスした場合はクラッシュしますので、loadwavsizeの値を利用して値を制限して使用する必要があります。
ファイル読み込みは仮の実装となっているため、1chのオーディオファイルのみが利用できます。
今後構造体の導入により、1つの関数でサンプル数、チャンネル数、サンプルレート、各チャンネルへの配列などをまとめて取得できるような仕様に変更される予定です。
2 - Development
開発環境の構築、コンパイラ・ランタイムの構造やクラスなどの解説。
Development
2.1 - 開発環境の構築
依存ライブラリのインストール、IDEの設定、ビルド、デバッグ、テストの解説
依存ライブラリ、ツール
- cmake
- bison 3.3~
- flex
- llvm 11~
- libsndfile
- rtaudio(cmakeが自動でインストールします)
加えて、Ninjaをインストールするとビルドが早くなる可能性があります。
macOS
XCodeをインストールし、以下のコマンドでコマンドラインツールをインストールしてください。
[Homebrew]をインストールし、以下のコマンドで依存パッケージをインストールします。
brew tap mimium-org/mimium
brew install mimium -s --only-dependencies
Linux(Ubuntu)
ターミナルから以下のコマンドで依存パッケージをインストールします。
pushd /tmp && wget https://apt.llvm.org/llvm.sh && chmod +x llvm.sh && sudo ./llvm.sh && popd
sudo apt-get install libalsa-ocaml-dev libfl-dev libbison-dev libz-dev libvorbis-dev libsndfile-dev libopus-dev gcc-9 ninja-build
LinuxBrewを利用している場合はmacOSを同様のコマンドでも依存パッケージのインストールが可能です。
Windows
MSYS2(https://www.msys2.org/)のインストールを公式サイトの要領にしたがって行ってください。
スタートメニューからMinGW64コマンドプロンプトを開いてください。
MinGW64 Command Line Promptを開き、以下のコマンドで依存パッケージをインストールします。
pacman -Syu --noconfirm git flex bison mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc mingw64/mingw-w64-x86_64-libsndfile mingw64/mingw-w64-x86_64-opus mingw-w64-x86_64-ninja mingw-w64-x86_64-llvm
リポジトリのクローン
gitコマンドが使えることを確認し、次のコマンドでmimiumの開発リポジトリをクローンします。
git clone --recursive https://github.com/mimium-org/mimium.git
エディタ
mimiumの開発にはVisual Studio Codeを利用することでどのOSでも同じように開発できるようになっています。
mimiumディレクトリでmimium.code-workspace
をVS Codeで開いてください。
推奨拡張機能
ワークスペースを開くと、推奨される拡張機能がインストールされていなければポップアップメニューが現れインストールできます。
- cmake-tools
- clangd
- CodeLLDB
- Coverage Gutter
とくにCMake Toolsは必須の拡張です。
CMake Kitの設定
cmake-toolsがインストールされている状態でワークスペースを開くと、初回のみCMakeが使用するKit(コンパイラ)を選ぶメニューが出てきます。
macOSでは/usr/bin/clang
を選択してください。Linuxでは/usr/local/bin/g++
、MinGW64なら/mingw64/bin/g++
などそれぞれインストールしたC++コンパイラへのパスを指定します。
ビルドする
VS Code左側のメニューからCMake Toolsのタブを選択し、Configure Allを選択します。build/
以下にビルドディレクトリが構築されます(この時、RtAudioのダウンロードとビルドも自動的に行われます)。
Configure Allの右隣、Build Allを押すと全プロジェクトのビルドが始まります。
ターゲット一覧
src/mimium_exe
: メインのmimium
コマンドをビルドします。src/mimium
: ライブラリの本体、libmimium
をビルドします。test/Tests
: すべてのテスト(Fuzzingを除く)をビルドするためのターゲットです。Lcov
LcovResetCounter
: -DENABLE_COVERAGE
を有効化した時利用可能なターゲットです。1回以上テストなどをビルドした上で実行した後Lcov
ターゲットを実行するとカバレッジ情報を収集します。この状態でCmd+Shift+PのコマンドパレットからShow Coverage
を選択するとコードを実際に実行した部分がハイライトされます。ソースを編集して再ビルドした後はカバレッジ情報のコンフリクトが発生するので、LcovResetCounter
を実行する必要があります。
VS Codeでのデバッグ方法
VS Code左側のメニューからRunのタブを選択します。
Runの実行ボタンの隣からコンフィグが選択できます。ワークスペースにはCMakeのターゲットを起動するもの(“CMake Target Debug(Workspace)")と、それに加えてコマンドラインオプションを指定して実行するもの(“Launch with Arg(edit this config)(workspace)")の2つが存在します。
下メニューバーの"Select the target to launch"をクリックすることで、CMakeのどのターゲットを起動するかを選択できます。
コマンドラインにオプションを渡して起動したい場合、“Launch with Arg(edit this config)(workspace)“を選択してからさらに歯車マークでmimium.code-workspaceファイルを直接編集します。たとえば以下の例のように"args"にファイル名を指定するように使います。
...
{
"name": "Launch with arg(edit this config)",
"type": "lldb",
"request": "launch",
"program": "${command:cmake.launchTargetPath}",
"args": ["${workspaceFolder}/examples/gain.mmm"],
"cwd": "${workspaceFolder}/build",
},
...
他にも"stdio": ["${workspaceFolder}/stdin.txt",null,null],
のようにすると、標準入力から入力を受け付けることも可能です。
テスト
テストには主にGoogle Testを利用した、3種類のテストが存在します。
ユニットテスト
主にコンパイラの各セクションを単独で起動して正しく動作するかのテストです。
回帰テスト(Regression Test)
実際にビルドしたmimium
バイナリをstd::system
関数から起動して、新機能追加に伴って他の箇所にエラーが発生しないかチェックするためのテストです。
mimiumには現在テストを検証するための構文などが存在しないため、計算結果をprintln
関数で標準出力にパイプし、Google Testでそれをキャプチャして正しい答えが出るかを検証するという形をとっています。
また現在オーディオドライバを起動するテストは未実装です。
Fuzzingテスト
Clangのlibfuzzerを利用するファジングテストです。
ファジングテストとはランダムな入力(ここではソースコードの文字列のことです)を少しずつ変化させながら与え、正しい構文なら正しく処理され、エラーの場合はエラーとして処理され予期せぬクラッシュが発生しないかなどの検証に使用されています。
macOSでのみ検証されており、CIには含まれていません。
Ctestの実行
buildフォルダでctest
コマンドを実行するか、VS Codeの右下のメニューから"Run Ctest"をクリックするとユニットテストと回帰テストが実行されます。
2.2 - コーディング方針
C++のコーディング規約、方針について
基本的方針
- 言語仕様はC++17に準拠する。主な理由は
std::variant
やstd::optional
を積極的に利用するため、テンプレートの推論がif constexpr
などで可読性が向上するため。 - 可読性かわずかな実行速度の向上で迷ったら可読性を取る。 そもそもC++の時点である程度速いことが保証されているので多少は富豪的処理でも構わないし、その程度の迷いは大体最適化されるとほぼ同じになる。
- できるだけ外部ライブラリを用いない(とくにboostなど汎用的なものについては)。STLはなるべく積極的に活用する。
- コンパイラにはパーサーでbison(yacc)とflex(lex)に依存している。これはとくにbisonのソースのドキュメントとしての価値があるため今後も利用していくつもりだが、flexに関してはUnicodeが読み込めないなどの問題があるためREflexなどに移行するか手動実装に切り替えるかもしれない。要検討。
- ランタイムには現在オーディオファイル読み込みでlibsndfileなどに依存しているがこれは実行時にしか必要ないので後々プロジェクト構造として分離していきたい。
- 生ポインタは使わない。基本的に
std::shared_ptr<T>
を使用する。LLVMライブラリにおけるllvm::Type*
やllvm::Value*
は独自に参照カウントが実装されているためこの限りではない。 - ポインタ変数が空であることを
nullptr
を用いてifの条件式などで使用しない。多少手間がかかってもstd::optional<std::shared_ptr<T>>
の形をなるべく使うことでソースコードそのもののドキュメント的価値を向上させる。
動的多相 (Dynamic Polymorphism)
C++に置いて型ごとに個別の処理内容を分ける 多相(ポリモーフィズム) にはコンパイル時に型を確定させる静的多相と実行時に動作を確定する動的多相の2種類が存在します。静的多相は主にテンプレートによって実現され、動的多相は主に継承と仮想関数を用いて実現されます。
しかしmimiumの開発では動的多相に仮想関数を基本的に使用しません。代わりにC++17よりSTLに導入されたstd::variant
を用います。std::variant<T1,T2,T3...>
はT1~Tnの複数種類のどれかの型を持つ変数を代入できる型であり、std::get<T>
やstd::visit()
を用いることで動的に型に応じての処理を分けることが可能になります。これは関数型などでよく見られる 直和型 と呼ばれる型の代わりでもあり、std::visit
はテンプレートやconstexprを用いた処理分けと組み合わせるといわゆるパターンマッチングに近い処理を可能にします。内部実装的には取りうる型の最大値のメモリ分+現在どの型を保持しているのかのタグ(整数)を確保する形になっているので Tagged Unionとも呼ばれます。
mimiumにおける具体的な型でいうと抽象構文木であるmimium::ast::Expr
やmimium::ast::Statement
、中間表現であるmimium::mir::Instruction
、(mimium言語における)型を表すmimium::types::Value
などがstd::variant
へのエイリアスです。
仮想関数よりもvariant
を用いるメリットはなんでしょうか?1つは、実行コストがかかることです。仮想関数は実行時に関数へのテーブルを保持し仮想関数が呼び出されるたびにそれを参照する必要があるため、std::variant
を用いた多相の方が実行速度では一般的に有利だとされています。
もう1つはアップキャストの問題です。抽象構文木のような木構造のデータを渡りながら処理をする時、どうしても
- 継承した個別の型を基底クラスのポインターでダウンキャストして受け取る
- 仮想関数を用いて型に応じた処理をする
- 処理したあと帰ってきたポインターを元の型に戻し(アップキャスト)、さらに処理を続ける
といったパターンが発生します。このアップキャストは一般的なコーディングでは、間違った型へキャストして終えば予測不可能な挙動が起きるので御法度とされています。仮想関数で用いている実行時型情報(RTTI)を用いるdynamic_cast
を使って動的に型検査をして安全にアップキャストする方法もありますが、記述が長くなりがちなども問題もあります。一方でstd:variant
を使用するとこのようなダウン→アップキャストの必要はないので型情報が明確に取り扱えます。
またこうした(型に応じた処理を複数種類)x(複数の型)と組み合わせる方法はデザインパターンの中ではビジターパターンとも呼ばれ、 std::visit(function_object,variant_variable)
ではこの形が引数としてシンプルに表されています。一方仮想関数を使ってのビジターパターンはデータ側にacceptと呼ばれるメソッドを実装しておく必要があるので、データはデータ、関数は関数というように構造を分離することが難しくなります。
再帰的データ構造
std::variant
にも難しい点はあります。そのうち重要な点は再帰的データ構造がそのままでは扱えないことです。
たとえば、types::Value
はtypes::Float
やtypes::Function
など取りうる型すべてを含んでいますが、ここでtypes::Function
のメンバ変数にはたとえば返り値を表す型としてtypes::Value
が含まれてしまっています。
std::variant
は通常の数値型のデータなどと同じように、取りうる型の最大値分だけメモリをスタック確保し、ヒープアロケーションは行わない仕様となっており、再帰的なデータ構造の場合はデータサイズを静的に決定できなくなってしまうのでコンパイルできなくなります。
これを回避するためには、再帰的な部分を持つデータについては実体の代わりにポインタを格納するなどの方法が考えられるのですが、初期化やメンバアクセスなどが統一されないためややこしくなるなどの問題があるため、mimiumでは次の記事を参考にしたヘルパークラスを利用しています。
Breaking Circular Dependencies in Recursive Union Types With C++17 - Don’t Compute In Public(last view:2020-08-17)
具体的には内部のT
の実体を要素数1のstd::vector<T>
に確保し、キャスト演算子としてT& ()
を実装しimplicitに元の型から構築、キャストして参照を受け取ることができるようにしています。std::vector
はT
の中身が不完全なままでもスタック上のデータサイズを確定させることができるのでコンパイルが通るようになるのです。
記事中のrecursive_wrapper<T>
を、mimium内部ではRec_Wrap<T>
と名付け、さらにこの型をrT
(たとえばFunction
に対してrFunction
といった形で)エイリアスしています。
using Value = std::variant<None, Void, Float, String, rRef, rTypeVar, rPointer, rFunction,rClosure, rArray, rStruct, rTuple, rAlias>;
/*~~~~~*/
using rFunction = Rec_Wrap<Function>;
/*~~~~~*/
struct Function : Aggregate {
Value ret_type;
std::vector<Value> arg_types;
};
たとえばstd::visit
でパターンマッチングする時にはビジターの関数オブジェクトのoperator()
オーバーロードを以下のようにします。
types::Value operator()(types::Function& f){
return someProcess(f);
}
/*~~~~~*/
template<typename T>
types::Value operator()(Rec_Wrap<T>& t){
return (*this)(static_cast<T&>(t));
}
Rec_Wrap<T>
を一度キャストして剥がしてもう一度自分自身を適用するテンプレート関数を使います。operator()(Rec_Wrap<types::Function>& rf)
を直接オーバーロードしても構わないのですが、このrf
は直接メンバアクセスができないため一度ローカル変数でtypes::Function&
にキャストしてやらないといけなかったりする二度手間が発生します。
エラー処理、例外
try
、throw
、catch
と言ったC++標準のエラー処理を積極的に利用します。throw
を使ったエラー処理は実行コストが高いというデメリットがあるものの、正常系で処理をしている間はコストがかからず、言語組み込みの仕様なのでエラー処理部の可読性が高まります。
optionalなどを活用したエラー処理クラスの実装は型シグネチャが複雑になり可読性が下がるなどのデメリットもあります。ただ内部的に使用しているLLVMのライブラリでは実行速度を重視してExpected
クラスなどでこの形式でエラー処理をしているため、こちらとtry
、catch
のエラー処理を混ぜるのはあまり効率がよくはない、混乱するといった問題点も存在しています。
現状ではコンパイラもランタイムもtry/catchのエラー処理を使用していますが、ランタイム側では実行時間にセンシティブである、今後組み込み向けなどに移植される可能性もあると言った事情を考えるとoptional等を活用した処理に変更するかもしれません。
基本的にtry
,catch
はコンパイラでの処理のはじめに行い深くネストすることはしません。どの道、ほとんどのケースでエラーが起きれば最終的にコンパイルエラーの形に帰着するためです。必要に応じてエラークラスを継承して種類分けし、catchしたところでまとめてエラーの型ごとにメッセージ等を個別に処理します。( エラークラスの定義は今後の課題)
3 - Contributing
Code of Conduct
Contribution guide
Documentation guide
Community(gitter/twitter/etc)
3.1 - 行動規範(CoC)
コミュニティの目的と参加者としての期待される行動
目標
mimiumに関わるコミュニティ(開発グループ、ドキュメンテーション、財政的支援等のコントリビューター、ユーザーグループ、関連するイベントなど)の主な目標は、できるだけ多様な背景を持った貢献者を最大限に含むことです。
そのため、私たちは性別、性的指向、能力、国籍、言語、年齢、民族、社会経済的地位、宗教にかかわらず、友好的で安全かつ快適な環境を提供することに専念しています。
コミュニティとして努力すべきこと
- 多様性の尊重。あらゆる背景やアイデンティティを持つ人々を歓迎し、サポートするコミュニティであるように努めます。
- 思いやりを持つこと。あなたの作品や仕事の一部は他の人に利用され、あなたも他の人の作品や仕事に依存することになります。あなたの決定はユーザーや他のコントリビューターに影響を与えることを、決定を下す際に考慮しましょう。
- 他のコミュニティを尊重すること。mimiumは先行する様々な言語やプロジェクト、音楽作品の影響を受けて作られています。mimiumはそれらを凌駕するために作られたものではありません。様々なコミュニティとの交流を繰り返すことでmimiumや関わる他のコミュニティをより価値ある物に育てていくように心がけます。
参加者として期待される行動、振る舞い
- 誠実にかつ活動的に参加すること。そうすれば、本コミュニティの健全性と持続性に貢献することになります。
- 自分の言動を充分に考慮し尊重してください。
- 対立が起きる前に協調を試みて下さい。
- 自分の周囲と仲間の参加者に気を配ってください。 危険な状況、苦しんでいる人または本行動規範の違反に気づいた場合、たとえ取るに足らないように見えてもすぐにコミュニティのリーダーに知らせてください。
- mimiumコミュニティは国を超えて存在しています。相手が常に自分の第一言語でコミュニケーションを取るとは限らないことを忘れないでください。また、自身の使う言語がコミュニティで多く使われていなくても、その言語で発言することをためらう必要はありません。
宣伝について
- mimiumコミュニティでmimiumに直接関連する自分の制作した作品や、関わったプロジェクトについての宣伝は商業/非商業プロジェクトに関わらず歓迎します。
- mimiumに直接関連せずとも、関連するトピックの非商業的プロジェクトやイベント(学会やカンファレンス、コンペティションなど)の宣伝は歓迎します。
- mimiumに直接関係のない商業的なプロジェクトの広告(advertisement)やスパムを行うことは控えてください。
容認できない行動、振る舞い
容認できない行動には以下が含まれます。私たちのオンラインコミュニティ、すべての関連イベント、またコミュニティビジネスとの関連で行われる1対1のコミュニケーションに参加している人々による威圧的な、嫌がらせの、虐待的な、差別的な、軽蔑的なまたは品位を傷つけるような言動。コミュニティイベントの場は一般の人々と共有される可能性がありますので、すべての利用者を尊重してください。
ハラスメントには以下が含まれます。
- 性別、性的指向、人種、宗教、国籍、言語、年齢、障害に関する有害なまたは偏見に満ちた口頭および書面によるコメント
- ヌードや性的な画像の不適切な使用(プレゼンテーションスライド含む)
- 不適切な暴力的描写(プレゼンテーションスライド含む)
- 故意の脅迫、ストーカー行為または追跡
- 嫌がらせの写真または録音
- スピーチまたは他のイベントの継続的中断
- 不適切な身体的接触
- 不快な性的注目
容認できない行動の結果
スポンサーおよび意思決定権を持っている人を含め、コミュニティメンバーによる容認できない行動は許容されません。容認できない行動を止めるように求められた人は誰でもすぐに遵守することが期待されます。
コミュニティメンバーが容認できない行動をした場合、コミュニティ主催者は警告なし(有料イベントの場合は返金もなし)での一時的な禁止またはコミュニティからの永久追放も含め、妥当と判断される措置を講じることができる。
容認できない行動を目撃したり受けたりした場合
容認できない行動を受けたり目撃したりした場合、または他の懸念がある場合、すぐにコミュニティ主催者に知らせてください。
コミュニティの主催者は、コミュニティのメンバーが地元の法執行機関に関わること、または容認できない行動を経験した人が安心感を得ることを最大限支援します。主催者は行動規範に沿わないコメント、コード、issueやその他のコントリビューションを削除、編集、拒否する権利と義務を持っています。
苦情の対応について
本行動規範の違反のぬれぎぬを着せられたと感じる場合、苦情に関して簡潔に説明することによりコミュニティ主催者の一人に知らせるべきです。あなたの苦情は既存の管理ポリシーに従って処理されるでしょう。
範囲
私たちは、すべてのコミュニティ参加者(有償・無償は別として貢献者、スポンサー、他のゲスト)がこの行動規範をオンラインを含むすべての(1対1も含めた)コミュニケーションで遵守することを期待します。
ライセンスと帰属
この行動規範は「Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)」ライセンスを得て配信されています。
また、この行動規範は以下の前例を参考にして作成されています。
行動規範のアップデートについて
コミュニティの参加者は本行動規範が現在のコミュニティにとって妥当なものかどうかを考え、議論するように努めてください。必要に応じて本行動規範をコミュニティがよりよいものになるようにアップデートし続けてください。
更新履歴
- 2021/1/17 初版を主に松浦知也(@tomoyanonymous)が作成。
4 - Release Notes
Update/Deprecation infos on each update
v0.4.0(2021-04-03)
言語機能
構造体型と、それに伴って型エイリアス構文が導入されました。以下のサンプルのように使えます。
type stereo = (float,float)
type composite = {signal:stereo , id:float}
fn test2(){
ogya = composite{(100,200), 100}
ho,fu = ogya.signal
he = ogya.id
println(ho)
println(fu)
println(he)
}
test2()
構造体型にはまだいくつか不完全な部分もあります。
- 構造体やタプルのメンバとして関数を指定した場合は、クロージャ(引数以外の自由変数を含む関数)を代入することができません
hoge.a = 100
のような構造体への破壊的代入構文はパーサー部分のみ実装されていますが、コード生成部分が未実装です。
バグ修正
- CLIへ存在しないファイルの相対パスを渡した時の挙動がおかしかったのを修正しました。(#62,by @t-sin).
リファクタ
- 再帰バリアントなど内部的に使われているヘルパーテンプレートクラスをリファクタリングしました。
v0.3.1(2021-02-25)
Bugfixes
- if文がvoid型のブロックを持つときコード生成に失敗するバグを修正
==
と!=
演算子が実装されていなかったのを修正
Refactoring
Runtime
クラスから実際にJITコンパイルやGlobalコンテキストを実行する部分をExecutionEngine
クラスとして切り出しました。これは後々インタプリタバックエンドを作るときや、環境変数機能を実装することを見越した変更です。
New Function
パッチリリースではあるものの、新しい関数も追加されています。
- 1サンプルだけディレイする
mem
関数が追加されました。これを利用することでbiquadフィルタが現実的に実装しやすくなります。mimium-core/filter.mmm
にサンプルコードを追加しました。
Other updates
全コントリビューターをall-contributorsを用いてReadme.mdに表示するようにしました。
v0.3.0 (2021-02-03)
新しい言語機能
タプルと配列型
aggregate typeとしてタプル、配列が追加されました。
タプル型は括弧とカンマで構築できるようになりました。
現在のところ、タプル型の値を取得するには、C++のstructual bindingのような方法しかありません。
# タプル構造
triples = (100,200,300)//型シグネチャは (float,float,float)
one,two,three = triples
将来的には triples.0
のようなドットアクセス演算子が追加される予定です。
配列型は角括弧とカンマで構築することができます。
現在のところ、すべての配列は mutable , fixed size で、llvm モジュールの中で private global variable として宣言されています。
ユニークな機能として、オーディオファイルの読み込みと同様に、フローティングポインタインデックスによる補間がサポートされています。
# Array Construction
myarr = [100,200,300]
internalv = myarr[0]//zero-based index
myarr[2] = 400//the array is now [100,200,400]
interp = myarr[0.5]//the result will be 150(linear interpolation)
dsp機能のマルチチャンネル対応
ステレオ出力/チャンネルをサポートしました。。
入出力の数は dsp
関数の型によって決まります。
例えば、ステレオパンナーのコード以下のように書くことができます。
fn panner(input:float,pan:float) -> (float,float){
return (input*pan,input*(1-pan))
}
fn dsp(input:(float,float))->(float,float){
src = random()*0.2
res = panner(src,sin(5*now/48000)+1*0.5)
return res
}
これは以下の点で破壊的変更になります。
- v0.2以前は
dsp
の入力パラメータが time
であったこと (これは now
が実装される前の一時的な解決策でした)、 - 出力型が
float
であったが、現在は dsp
の型は (float のタプル)->(float のタプル)
とすべきであること、つまり、モノラル処理をしたい場合でも、1つの変数を float のタプルで受け取り、float のタプルを返す関数であること、ということです。
この言語仕様に合わせて、いくつかのexampleを書き換えました。
C++ライブラリとしてのmimium
ライブラリヘッダとfrontend(アプリケーションインスタンスとCLI)の依存関係を整理しました。
C++ライブラリとしてmimiumが使えるようになりました。CMakeを使えば簡単にインポートできます。
最低限の例は https://github.com/mimium-org/mimium-libimport-example にあります。
その他の変更点
- LLVMのサポートバージョンが11になりました。
- Code_Of_Conduct.mdを追加しました。
v0.2.0 (2020-12-24)
改善点
- Windowsビルド(MSYS2上)の準備ができました。ビルドしたい場合はGitHub Actionsのワークフローを確認してください。
- プリミティブ関数delay(input,delaytime)`を追加しました。
- 最大遅延時間は現在44100サンプルに固定されています。
- (実験的な)シンプルなマクロプリプロセッサが追加されました。
- グローバルコンテキストに
include "otherfile.mmm
と書くことで、他のソースファイルをインクルードできるようになりました。
- 中間レベルの内部表現をリファクタリングしました。
v0.1.5 (2020-09-28)
バグを修正しました。
- if文の中にfcallが1つしか入っていない場合のコンパイルエラーを修正した(Thanks @t-sin)。
改善点
- GitHub Actionsで、Changelogをリリースの中に自動的にふむめるようにしました。
その他
現在、v0.2.0に含まれる予定の遅延プリミティブ関数などの新機能を開発中です。
v0.1.4 (2020-09-11)
バグを修正しました。
- clangとlibstdc++の組み合わせでのコンパイルエラーを修正した。
- 関数内で if を式として返すとクラッシュする問題を修正した。
改善点
- 開発環境でのアドレスサニタイザと簡易ファジングテストを導入しました。
- ランタイムのデストラクタ呼び出し時にユーザコードのメモリ確保が解放されるようにした。
- 以前のバージョンでは、ユーザコードによるヒープ確保は解放されていなかったが、ランタイムのデストラクタが呼ばれるとプログラム自体が終了するので、大きな問題ではない。この改善は、同じプロセスで何度もコンパイルを繰り返すようなファジングテストでは、主に意味があります。
v0.1.3 (2020-09-09)
バグを修正しました。
- linuxでifステートメントを使用した場合の異常なクラッシュを修正
- 式として使用される場合のif文の演算子の優先順位を修正
// 数学演算子は if や else よりも優先度が高いです!
myvar = if(cond) 100+20 else 200*50
v0.1.2 (2020-09-07)
バグを修正しました。
- 以前のリリースでは、if文のためのllvm生成が含まれていませんでした。if文/式が正しく動作するようになった。
- 改行で終わるコメントアウトが正しく解析されないバグを修正した。
リファクタリング
MIR(Mid-Level Representation)関連のクラスの実装を簡素化しました。
v0.1.0でリファインされた ast クラスと同様に、データはメンバ関数を持たず、集約初期化で初期化されます。
v0.1.1 (2020-09-05)
バグを修正しました。
このリリースでは、主にパーサの修正を行いました。
括弧内のblock文の仕様はRustと同様です。
ブロック内の文の最後の行に単一の式を入れたり、return
文を入れたりすると、ブロックは関数呼び出しやインフィックス操作のように、単なる式として扱うことができます。その値はブロックの値そのものになります。
したがって、以下の例はすべて有効です。
fn test(x,y){
localvar = x*y+100
localvar //last line is expression.
}
fn test(x,y){
localvar = x*y+100
return localvar //last line is return statement.
}
testvar = { velylocal = 100
verylocal*2004} * 300 // this is also valid...
この変更により、if
文は式と文の両方を扱えるようにな離ました。then/else文の内容が戻り値を持たない場合は、void
型の関数と同じように扱われます。
v0.1.0 (2020-08-21)
今回のリリースは内部処理のリファクタリングが中心で、特に AST(Abstract Syntax Tree)関連のクラスは開発初期に書かれたもので、MIRやType関連のような新しく導入されたクラスとは全く異なる実装スタイルとなっています。今回のコンパイラ開発では、クラス継承ではなく、std::variant
を使った方法に動的多相性を統一しています。
今回のリリースでは、新機能の追加や変更点はありませんが、多くの不安定性、特に型推論の誤りを修正しました。
また、Ubuntu上でのビルドも準備しています。依存関係はgithub アクションワークフローページから確認できます。Windowsでテストできる方を募集しています。
v0.0.0 (2020-02-15)
最初のリリースです!配列、構造体、インクルード/モジュールはありません。これは mimium の「概念実証」バージョンです。
既知のバグ
- self` を使用する同じ関数を2回以上呼び出すと、selfのインスタンス化が正しく行われない。(関数のエイリアスを定義することでこの挙動を回避できます)
- 関数の中では、少なくとも1つのグローバル変数を使用しなければなりません。
制限事項
- if
は複数の文ではなく式のみを持つことができます(他の言語でいうところの条件演算子(
(cond)? then_expr : else_expr`)のようなものです)。 - 配列アクセス
[]
は,loadwav(path)を用いて読み込んだオーディオファイルに対してのみ利用できるようにした。配列アクセスには境界チェックはありません。配列のサイズは
loadwavsize(path)` で取得できます。