TL; DR
- playgroundの共有リンクでソースコードを共有
- 各命令をアルファベットに変換しクエリパラメータに格納
はじめに
今年のはじめに、Brainf*ck系のEsolang 「Cholc」を作成しました。
A A A A A A A A A |: F G E Am :| F Fm |: C C C C C C C C A A A B Em :| C Cm X |: Db Eb Eb Eb Eb Eb C C C Fm :| C C X |: C C#m :| Cm Cm X X C C C X Ebm X D Dm A A A A A X |: F G Cm :| Gm X F Fm X F F F X Gm Gm D F# Bm |: Gm D F# Bm :| Gm X |: Gm Dm :| G X A X
Hello, world!
Cholcでは、音楽のコード進行によって計算を行います("Chord" + "Calculate" でCholcです)。
| operator | role |
|---|---|
{メジャーコード} |
ポインタが指す値をインクリメント |
{マイナーコード} |
ポインタが指す値をデクリメント |
|: |
ポインタが指す値が0のとき、対応する :| の直後へ飛ぶ |
:| |
ポインタが指す値が0でないとき、対応する |: の直後へ飛ぶ |
v |
入力から1バイト読み込み(asciiとして)ポインタへ保存する |
X |
ポインタが指す値を(asciiとして)出力へ書き出す |
ポインタ移動はコードとコードの間で行われ、その距離は「完全5度いくつ分離れているか」によって決まります(詳しくは過去の記事をご覧ください)。
Playgroundでは実際に和音が鳴りながらプログラムが実行されるので、ぜひ試してみてください。
そして、せっかくPlaygroundを用意したので、書いたソースコードを他人に共有できるようにしたいです。
本記事では、共有リンクの実装方法について紹介します。
共有リンク
共有リンクとしてよくあるのがサーバー側にソースコードを保存するパターンです。
ただし今回はGitHub Pagesで完結させたかったため、UI1だけで共有リンクを作る方法を検討しました。
ソースコードをクエリパラメータに埋め込む
そこで、ソースコードをまるごとクエリパラメータに埋め込むことにしました。
https://syuparn.github.io/cholc
https://syuparn.github.io/cholc/?p=abc
Cholcには命令が28種類2しかありません。そのため、英数字[a-z0-9]の36種類あれば1文字で1命令を表すことができます。
C C# D
abc
function codeToQuery(code: ByteCode): string {
// 変換できないものは無視
if (code === byteCodes.Unknown) {
return ""
}
// 英数字(a, b, c, ..., z, 0, ..., 9)に変換
const alphabetOffset = "a".charCodeAt(0)
const numOffset = "0".charCodeAt(0)
if (code < 26) {
return String.fromCharCode(code + alphabetOffset)
}
if (code < 36) {
return String.fromCharCode(code - 26 + numOffset)
}
throw new Error(`code must not be ${code}`)
}
逆に、クエリパラメータが与えられた場合は命令にデコードすることでソースコードを復元します。
ただし、命令のバイトコードを使用している都合上以下の制約があり、完全に復元することはできません。
- 命令でない文字列(コメント等)はエンコードできず消えてしまう
- 表記の違う同じコード(異名同音、
C#とDb等)は処理系のデフォルトの表記に統一されてしまう3
コメントが消えてしまうのは少し不便なので、今後改修するかもしれません。
(余談)命令のバイトコード化
ここからは余談です。
先に述べたように、命令は内部的にバイトコードに変換されています。
音程の順に並べることで、コード進行によるポインタ移動量を計算しやすくしています。
(バイトコードの差を12で割ったあまりが「半音何個分離れているか」を表す)
| 和音 | バイトコード |
|---|---|
C |
0 |
C# |
1 |
D |
2 |
| ... | ... |
B |
11 |
Cm |
12 |
| ... | ... |
Bm |
23 |
| 特殊な命令 | バイトコード |
|---|---|
| ` | :` |
| `: | ` |
, |
26 |
X |
27 |
ポインタ移動は、2つの和音の音程が完全5度(=半音7個)何個分であるかで計算可能です。
export function keySignatureMove(pitch1: number, pitch2: number): number {
let pitch = pitch1
// move: ポインタ移動量
// 音程が同じになるまで完全5度1つ分ずつ音程を上げて比較
for (let move = 0; move < 12; move++) {
if (pitch == pitch2) {
// 完全5度5個分以上進む場合はマイナスとして扱う(前述の五度圏の図を参照)
return move > 5 ? move - 12 : move
}
// 完全5度1つ分音程を上げる
pitch = (pitch + 7) % 12
}
throw new Error(`must not be reacted: pitch1: "${pitch1}", pitch2: "${pitch2}"`)
}
おわりに
以上、playgroundのリンクでソースコードを共有する方法の紹介でした。
Esolangなら同様の方法が使えると思うので、皆さんも試してみてはいかがでしょうか?
